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

View File

@ -28,6 +28,8 @@ class CObjectManager;
#include "lib/file/vfs/vfs_path.h" #include "lib/file/vfs/vfs_path.h"
#include "ps/CStr.h" #include "ps/CStr.h"
#include <boost/random/mersenne_twister.hpp>
class CObjectBase class CObjectBase
{ {
NONCOPYABLE(CObjectBase); NONCOPYABLE(CObjectBase);
@ -106,7 +108,7 @@ public:
// Get a set of selection strings that are complete enough to specify an // Get a set of selection strings that are complete enough to specify an
// exact variation of the actor, using the initial selections wherever possible // exact variation of the actor, using the initial selections wherever possible
// and choosing randomly where a choice is necessary. // 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 // Get a list of variant groups for this object, plus for all possible
// props. Duplicated groups are removed, if several props share the same // props. Duplicated groups are removed, if several props share the same
@ -144,6 +146,13 @@ public:
VfsPath m_Material; VfsPath m_Material;
private: 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; std::vector< std::vector<Variant> > m_VariantGroups;
CObjectManager& m_ObjectManager; CObjectManager& m_ObjectManager;
}; };

View File

@ -44,14 +44,14 @@ CUnit::~CUnit()
delete m_Model; 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); CObjectBase* base = objectManager.FindObjectBase(actorName);
if (! base) if (! base)
return NULL; return NULL;
std::set<CStr> actorSelections = base->CalculateRandomVariation(selections); std::set<CStr> actorSelections = base->CalculateRandomVariation(seed, selections);
std::vector<std::set<CStr> > selectionsVec; std::vector<std::set<CStr> > selectionsVec;
selectionsVec.push_back(actorSelections); selectionsVec.push_back(actorSelections);

View File

@ -42,9 +42,10 @@ private:
public: public:
// Attempt to create a unit with the given actor, with a set of // 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. // 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 // destructor
~CUnit(); ~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 // 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) if (! m_ObjectManager)
return NULL; return NULL;
CUnit* unit = CUnit::Create(actorName, selections, *m_ObjectManager); CUnit* unit = CUnit::Create(actorName, seed, selections, *m_ObjectManager);
if (unit) if (unit)
AddUnit(unit); AddUnit(unit);
return unit; return unit;

View File

@ -50,7 +50,7 @@ public:
void DeleteAll(); void DeleteAll();
// creates a new unit and adds it to the world // 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 // return the units
const std::vector<CUnit*>& GetUnits() const { return m_Units; } const std::vector<CUnit*>& GetUnits() const { return m_Units; }

View File

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

View File

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