1
0
forked from 0ad/0ad

Use a predictable RNG seed for random actor variations, so they are consistent between runs of the game.

This was SVN commit r9143.
This commit is contained in:
Ykkrosh 2011-04-02 12:51:42 +00:00
parent 97c934ad1c
commit 1a072a3f37
8 changed files with 41 additions and 18 deletions

View File

@ -27,9 +27,9 @@
#include "ps/Filesystem.h"
#include "ps/CLogger.h"
#include "lib/timer.h"
#include "lib/rand.h"
#include "maths/MathUtil.h"
#include <boost/random/uniform_int.hpp>
CObjectBase::CObjectBase(CObjectManager& objectManager)
: m_ObjectManager(objectManager)
@ -411,7 +411,14 @@ const CObjectBase::Variation CObjectBase::BuildVariation(const std::vector<u8>&
return variation;
}
std::set<CStr> CObjectBase::CalculateRandomVariation(const std::set<CStr>& initialSelections)
std::set<CStr> CObjectBase::CalculateRandomVariation(uint32_t seed, const std::set<CStr>& initialSelections)
{
rng_t rng;
rng.seed(seed);
return CalculateRandomVariation(rng, initialSelections);
}
std::set<CStr> CObjectBase::CalculateRandomVariation(rng_t& rng, const std::set<CStr>& initialSelections)
{
std::set<CStr> selections = initialSelections;
@ -471,10 +478,8 @@ std::set<CStr> CObjectBase::CalculateRandomVariation(const std::set<CStr>& initi
bool allZero = (totalFreq == 0);
if (allZero) totalFreq = (int)grp->size();
// Choose a random number in the interval [0..totalFreq).
// (It shouldn't be necessary to use a network-synchronised RNG,
// since actors are meant to have purely visual manifestations.)
int randNum = (int)rand(0, (size_t)totalFreq);
// Choose a random number in the interval [0..totalFreq)
int randNum = boost::uniform_int<>(0, totalFreq-1)(rng);
// and use that to choose one of the variants
for (size_t i = 0; i < grp->size(); ++i)
@ -518,7 +523,7 @@ std::set<CStr> CObjectBase::CalculateRandomVariation(const std::set<CStr>& initi
CObjectBase* prop = m_ObjectManager.FindObjectBase(it->second);
if (prop)
{
std::set<CStr> propSelections = prop->CalculateRandomVariation(selections);
std::set<CStr> propSelections = prop->CalculateRandomVariation(rng, selections);
// selections = union(propSelections, selections)
std::set<CStr> newSelections;
std::set_union(propSelections.begin(), propSelections.end(),

View File

@ -28,6 +28,8 @@ class CObjectManager;
#include "lib/file/vfs/vfs_path.h"
#include "ps/CStr.h"
#include <boost/random/mersenne_twister.hpp>
class CObjectBase
{
NONCOPYABLE(CObjectBase);
@ -106,7 +108,7 @@ public:
// Get a set of selection strings that are complete enough to specify an
// exact variation of the actor, using the initial selections wherever possible
// and choosing randomly where a choice is necessary.
std::set<CStr> CalculateRandomVariation(const std::set<CStr>& initialSelections);
std::set<CStr> CalculateRandomVariation(uint32_t seed, const std::set<CStr>& initialSelections);
// Get a list of variant groups for this object, plus for all possible
// props. Duplicated groups are removed, if several props share the same
@ -144,6 +146,13 @@ public:
VfsPath m_Material;
private:
// A low-quality RNG like rand48 causes visible non-random patterns (particularly
// in large grids of the same actor with consecutive seeds, e.g. forests),
// so use a better one that appears to avoid those patterns
typedef boost::mt19937 rng_t;
std::set<CStr> CalculateRandomVariation(rng_t& rng, const std::set<CStr>& initialSelections);
std::vector< std::vector<Variant> > m_VariantGroups;
CObjectManager& m_ObjectManager;
};

View File

@ -44,14 +44,14 @@ CUnit::~CUnit()
delete m_Model;
}
CUnit* CUnit::Create(const CStrW& actorName, const std::set<CStr>& selections, CObjectManager& objectManager)
CUnit* CUnit::Create(const CStrW& actorName, uint32_t seed, const std::set<CStr>& selections, CObjectManager& objectManager)
{
CObjectBase* base = objectManager.FindObjectBase(actorName);
if (! base)
return NULL;
std::set<CStr> actorSelections = base->CalculateRandomVariation(selections);
std::set<CStr> actorSelections = base->CalculateRandomVariation(seed, selections);
std::vector<std::set<CStr> > selectionsVec;
selectionsVec.push_back(actorSelections);

View File

@ -42,9 +42,10 @@ private:
public:
// Attempt to create a unit with the given actor, with a set of
// suggested selections (with the rest being randomised).
// suggested selections (with the rest being randomised using the
// given random seed).
// Returns NULL on failure.
static CUnit* Create(const CStrW& actorName, const std::set<CStr>& selections, CObjectManager& objectManager);
static CUnit* Create(const CStrW& actorName, uint32_t seed, const std::set<CStr>& selections, CObjectManager& objectManager);
// destructor
~CUnit();

View File

@ -125,12 +125,12 @@ CUnit* CUnitManager::PickUnit(const CVector3D& origin, const CVector3D& dir) con
///////////////////////////////////////////////////////////////////////////////
// CreateUnit: create a new unit and add it to the world
CUnit* CUnitManager::CreateUnit(const CStrW& actorName, const std::set<CStr8>& selections)
CUnit* CUnitManager::CreateUnit(const CStrW& actorName, uint32_t seed, const std::set<CStr8>& selections)
{
if (! m_ObjectManager)
return NULL;
CUnit* unit = CUnit::Create(actorName, selections, *m_ObjectManager);
CUnit* unit = CUnit::Create(actorName, seed, selections, *m_ObjectManager);
if (unit)
AddUnit(unit);
return unit;

View File

@ -50,7 +50,7 @@ public:
void DeleteAll();
// creates a new unit and adds it to the world
CUnit* CreateUnit(const CStrW& actorName, const std::set<CStr8>& selections);
CUnit* CreateUnit(const CStrW& actorName, uint32_t seed, const std::set<CStr8>& selections);
// return the units
const std::vector<CUnit*>& GetUnits() const { return m_Units; }

View File

@ -57,6 +57,7 @@ public:
virtual void Init(const CParamNode& UNUSED(paramNode))
{
m_ActorSeed = 0;
}
virtual void Deinit()
@ -119,6 +120,8 @@ private:
std::vector<Projectile> m_Projectiles;
uint32_t m_ActorSeed;
void LaunchProjectile(entity_id_t source, CFixedVector3D targetPoint, entity_id_t targetEnt, fixed speed, fixed gravity);
void AdvanceProjectile(Projectile& projectile, float dt, float frameOffset);
@ -151,7 +154,7 @@ void CCmpProjectileManager::LaunchProjectile(entity_id_t source, CFixedVector3D
std::set<CStr> selections;
Projectile projectile;
projectile.unit = GetSimContext().GetUnitManager().CreateUnit(name, selections);
projectile.unit = GetSimContext().GetUnitManager().CreateUnit(name, m_ActorSeed++, selections);
if (!projectile.unit)
{
// The error will have already been logged

View File

@ -117,7 +117,7 @@ public:
m_R = m_G = m_B = fixed::FromInt(1);
std::set<CStr> selections;
m_Unit = GetSimContext().GetUnitManager().CreateUnit(m_ActorName, selections);
m_Unit = GetSimContext().GetUnitManager().CreateUnit(m_ActorName, GetActorSeed(), selections);
if (!m_Unit)
{
// The error will have already been logged
@ -336,7 +336,7 @@ public:
return;
std::set<CStr> selections;
CUnit* newUnit = GetSimContext().GetUnitManager().CreateUnit(m_ActorName, selections);
CUnit* newUnit = GetSimContext().GetUnitManager().CreateUnit(m_ActorName, GetActorSeed(), selections);
if (!newUnit)
return;
@ -368,6 +368,11 @@ public:
}
private:
int32_t GetActorSeed()
{
return GetEntityId();
}
void Update(fixed turnLength);
void Interpolate(float frameTime, float frameOffset);
void RenderSubmit(SceneCollector& collector, const CFrustum& frustum, bool culling);