# Random monsters move in-sync instead of individually

I am making a game in which monsters spawn randomly and begin to move around randomly. I am fairly new at programming but not at game development. Through using a few tutorials I was able to make 1 creature spawn randomly and move randomly (with animations!) in a way that seemed organic instead of robotic.

So, I moved on to the next step of doing this to several creatures. However, when I executed this, I found that instead of executing their own instructions, the rest of the monsters simply mimic the relative movements of 1 central guy. Everyone moves in sync like some North Korean military parade!

I am using foreach loops and random numbers to choose their directions. Since my game is a top down 2d isometric, I am using a dictionary to store 4 directions. I have made some changes based on the suggestions here, and now all the creatures animate! But they still don't change directions or do things on their own.

How do I make them each have their own unique set of instructions instead of everyone just following the dear leader?

Below is a walkthrough of my code:

Monster Initialization:

public void AddCreatures()
{
NewMonster = new Monster(Texture);
NewMonster.AddAnimation("northeast", 0, 96, 48, 48, 8, 0.2f);
NewMonster.AddAnimation("northwest", 0, 144, 48, 48, 8, 0.2f);
NewMonster.AddAnimation("southeast", 0, 240, 48, 48, 8, 0.2f);
NewMonster.AddAnimation("southwest", 0, 288, 48, 48, 8, 0.2f);
NewMonster.AddAnimation("hoon", 144, 336, 48, 48, 1, 1.0f);
NewMonster.Position = RandomPos();
NewMonster.DrawOffset = new Vector2(49, 80);
NewMonster.CurrentAnimation = "southwest";
NewMonster.IsAnimating = false;
}


Spawn:

for (int v = 0; v < 12; v++)
{
}


Randomize spawn Position:

 private Vector2 RandomPos()
{
Vector2 location = Vector2.Zero;
location.X = rng.Next(300,900);
location.Y = rng.Next(200, 700);
return location;
}


Movements:

 public void Update(GameTime gameTime)
{
timenow += (float)gameTime.ElapsedGameTime.TotalSeconds;
foreach (Monster mon in creatures)
{
if (mon.IsAnimating)
{

if (timenow - (float)gameTime.ElapsedGameTime.TotalSeconds > 0.5f)
{
mon.Velocity = directions[rng.Next(1, 5)];
ChooseAnim(mon);
mon.Velocity.Normalize();
speed = (float)(rng.Next(0, 3));
diff = new Vector2(mon.Position.X - mon.GetLastPosition.X, mon.Position.Y - mon.GetLastPosition.Y);
if (Math.Abs(diff.X) >= 4 && Math.Abs(diff.Y) >= 4)
{
mon.Velocity = directions[rng.Next(1, 5)];
ChooseAnim(mon);
mon.Velocity.Normalize();
}
mon.Velocity.X *= speed;
mon.Velocity.Y *= speed;

timenow = 0f;
}
mon.MoveBy(mon.Velocity);

}
DoNotExit();
mon.Update(gameTime);
}
}


Animation choosing method:

    private void ChooseAnim(Monster mon)
{
if (mon.Velocity == directions[1])
mon.CurrentAnimation = "northeast";
if (mon.Velocity == directions[2])
mon.CurrentAnimation = "northwest";
if (mon.Velocity == directions[3])
mon.CurrentAnimation = "southeast";
if (mon.Velocity == directions[4])
mon.CurrentAnimation = "southwest";

}

• Could you add where you initialize rng? Apr 18 '12 at 20:26
• Change foreach (Monster NewMonster in creatures) to foreach (Monster monster in creatures) Then rename all the instances of NewMonster inside that loop to monster Apr 18 '12 at 20:39
• I simply initialize rng at the beginning of the Enemy Manager class where all this code is contained. Random rng = new Random(); Apr 18 '12 at 20:54

There is a very quick way to fix your code. Each monster should have its own timenow. The rest of the code can be improved, but is not responsible for the bug you are experiencing.

Once it is done, instead of this:

    timenow += (float)gameTime.ElapsedGameTime.TotalSeconds;
foreach (Monster mon in creatures)
{
...


You do this:

    foreach (Monster mon in creatures)
{
mon.timenow += (float)gameTime.ElapsedGameTime.TotalSeconds;
...


Otherwise when you reset timenow = 0f the operation synchronises all monsters.

• Thanks. I changed both timenow and speed into properties of the monster and it works now! Thanks a bunch! Apr 22 '12 at 18:00

I don't know if that's everything, but here are some things that look wrong too me:

• Looks like your oldLoc variable is common to all your monsters. It should probably be a member variable of your Monster class. Otherwise you can't keep a separate record of each monster location.
• You also have a global thisMonster and another global NewMonster, they seem useless to me, if they're not bringing this bug now, they'll sure bring one later.
• In ChooseDirection, you set another global variable chosen, and use it afterwards. It's probably not breaking anything for the moment, but simply returning the chosen direction would be a bit cleaner. Otherwise you could be tempted to use this before calling ChooseDirection and you'd end up with some more weird issues.
• Less problematic, but still: you're re-initializing your directions array at each update, this could be done in an init function, this should event be a static variable.

Moral of the story: global variables are bad. Unless you're a fan of unexpected military parades.

• Thanks. This seems like some very solid advice. I'll see what I can do and report back later. Apr 18 '12 at 20:57
• 1) done 2) I have removed thisMonster but I need NewMonster in order to initialize i think. 3) I think this method is the key to the problem. I have made some changes including the one you suggested and now the creatures all animate instead of standing still. They still dont change directions though. 4) done Apr 18 '12 at 21:59
• Weird... I can't see anything else. Do you mean you still have all your monsters changing direction at the same time? Post a Youtube video maybe? Apr 19 '12 at 7:46

Well, where are you deciding the random movement? This is a symptom of creating multiple Random(the class) instances at the same time. They'll all have the same seed time (probably some differing in just a little).
You can usually fix this by instead of creating a new Random() for every class that needs random moves, you can just create one Random object in your "management class(or something like that) and pass by reference to the classes that will use it. And I think your flaw is in the RandomPos() method or in the Monster constructor.

Something like this:

Vector2 RandomPos()
{
Random rand = new Random();
return new Vector2(rand.nextInt(x1, x2), rand.nextInt(y1, y2) );
}


or this:

Monster(Texture2D texture)
{
this.rand = new Random();
///blablabla define movement with rand object.
}


you should use:

Vector2 RandomPos(Random rand)
{
return new Vector2(rand.nextInt(x1, x2), rand.nextInt(y1, y2) );
}


or this:

Monster(Texture2D texture, Random rand)
{
this.rand = rand;
///blablabla define movement with rand object.
}


With this example, rand should be created in AddCreatures() and shared by all calls of the RandomPos function or Monster constructor.

• I am only initializing random once at the beginning of my Enemymanager class. Random rng = new Random(); I also added randompos to the original post. Apr 18 '12 at 20:51