XNA Simple NPC movement is wonky

Started by
0 comments, last by l0k0 12 years, 9 months ago
Hello Everyone! I'm embarking on a big group project as a programmer sometime in august and in preparation am making several simple games in XNA. However, this one I'm having a little trouble with. An NPC is supposed to move in a predictable pattern by way of waypoints, represented by 2 Vector2 variables. In short, I want my character to move from her starting position to point 1, then to point 2, and then back to the point 1 and repeat. However, when she gets to point 1, she gets 'stuck' there and jiggles around. Here is my source code so far:



using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace LetSee
{

public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Vector2 backPosition = new Vector2(0,0);
Texture2D back;
Vector2 mousepos;
Vector2 Scoutp = new Vector2(0, 0);
Texture2D Scouti;
Vector2 ScoutSpeed = new Vector2(5, 5);
bool eaten= false;
Texture2D Carriei;
Vector2 Carriep = new Vector2(300, 300);
int CarrieSpeed = 2;
Vector2 idlePoint = new Vector2(152, 124);
Vector2 idlePoint2 = new Vector2(111, 400);
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}


protected override void Initialize()
{

graphics.PreferredBackBufferHeight=500;
graphics.PreferredBackBufferWidth = 500;
graphics.IsFullScreen = false;
graphics.ApplyChanges();
Window.Title = "Prototype";
base.Initialize();

}


protected override void LoadContent()
{

spriteBatch = new SpriteBatch(GraphicsDevice);
back = this.Content.Load<Texture2D>("BackGroundEngi");
Scouti = this.Content.Load<Texture2D>("Scout");
Carriei = this.Content.Load<Texture2D>("car");


}


protected override void UnloadContent()
{

}


protected override void Update(GameTime gameTime)
{

if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();


bool idle = true;
bool idle2 = false;
Rectangle ScoutRec = new Rectangle((int)Scoutp.X, (int)Scoutp.Y, Scouti.Width, Scouti.Height);
Rectangle CarrieRec = new Rectangle((int)Carriep.X, (int)Carriep.Y, Carriei.Width, Carriei.Height);
MouseState ms = Mouse.GetState();
#if WINDOWS
mousepos.X=ms.X;
mousepos.Y=ms.Y;
#endif
KeyboardState moveme = Keyboard.GetState();
if (moveme.IsKeyDown(Keys.Up))
{
Scoutp.Y -= ScoutSpeed.X;
}
else if (moveme.IsKeyDown(Keys.Down))
{
Scoutp.Y += ScoutSpeed.X;
}
else if (moveme.IsKeyDown(Keys.Right))
{
Scoutp.X += ScoutSpeed.Y;
}
else if (moveme.IsKeyDown(Keys.Left))
{
Scoutp.X -= ScoutSpeed.Y;
}
if (idle == true && idle2==false)
{
if (Carriep == idlePoint)
{

idle = false;
idle2 = true;
Carriep.X = 22;
Carriep.Y = 200;
}
if (Carriep.Y >= idlePoint.Y)
{
Carriep.Y -= CarrieSpeed;
}
else if (Carriep.Y <= idlePoint.Y)
{
Carriep.Y += CarrieSpeed;
}
if (Carriep.X >= idlePoint.X)
{
Carriep.X -= CarrieSpeed;
}
else if (Carriep.X <= idlePoint.X)
{
Carriep.X += CarrieSpeed;
}

}
if (idle2 == true && idle==false)
{
if (Carriep == idlePoint2)
{
idle = true;
idle2 = false;
Carriep.X = 222;
Carriep.Y = 66;
}
if (Carriep.Y >= idlePoint2.Y)
{
Carriep.Y -= CarrieSpeed;
}
else if (Carriep.Y <= idlePoint2.Y)
{
Carriep.Y += CarrieSpeed;
}
if (Carriep.X >= idlePoint2.X)
{
Carriep.X -= CarrieSpeed;
}
else if (Carriep.X <= idlePoint2.X)
{
Carriep.X += CarrieSpeed;
}

}
if (CarrieRec.Intersects(ScoutRec))
{
eaten = true;
}

base.Update(gameTime);

}


protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin();
spriteBatch.Draw(back, backPosition, Color.White);
spriteBatch.Draw(Scouti, Scoutp, Color.Pink);
spriteBatch.End();
if (eaten == false)
{
spriteBatch.Begin();
spriteBatch.Draw(Carriei, Carriep, Color.White);
spriteBatch.End();
}
base.Draw(gameTime);
}
}
}


I tried to make it so that she 'teleports' away from the waypoint once she reaches it, but that didn't work.
Advertisement
I see a number of problems that may be causing this issue. One, is that there are no tolerances at all.


if (Carriep == idlePoint)
{
idle = false;
idle2 = true;
Carriep.X = 22;
Carriep.Y = 200;
}


Floating point error will inevitably accumulate, and if the value is off by just one bit, this section of code will never be hit. Try using the distance formula to see if the point is within an appropriate tolerance. Using squared values is better for performance, but for now, try using:

if (Vector2.Distance(Carriep, idlePoint) <= 2.0f)

You could of course replace this with a more appropriate tolerance value.

Secondly, your code will seek even if it is at the correct coordinates on a given axis because you are using less than/greater than OR EQUAL comparisons.


if (Carriep == idlePoint)
{

idle = false;
idle2 = true;
Carriep.X = 22;
Carriep.Y = 200;
} // consider putting an else here {
if (Carriep.Y >= idlePoint.Y)
{
Carriep.Y -= CarrieSpeed;
}
else if (Carriep.Y <= idlePoint.Y)
{
Carriep.Y += CarrieSpeed;
}
if (Carriep.X >= idlePoint.X)
{
Carriep.X -= CarrieSpeed;
}
else if (Carriep.X <= idlePoint.X)
{
Carriep.X += CarrieSpeed;
}
// end else here


Think about this logically for a second. If the target.X is at 400 and you are positioned at 400 on the X-axis, do you want to move by 2 units on that axis? Change your <= to < comparisons, and your >= to > comparisons.

My guess is that the main culprit is the first tolerance issue I discussed above. Since you are using absolute comparisons, I think your code never registers as reaching "idlePoint" and continuously seeks it. It's easy to test for yourself though. Inside the comparison, put a write line statement like so:

if (Carriep == idlePoint)
{
idle = false;
idle2 = true;
Carriep.X = 22;
Carriep.Y = 200;
Console.WriteLine("Idle Point Reached!");
}

I suspect this will never be printed in your console window. Note: if you don't have a console at all, you'll need to change your project settings from a WindowsApplication to a ConsoleApplication. If you're looking for something more challenging you could try Googling Steering Behaviors, specifically Seek, Arrival, and Path Following.
<shameless blog plug>
A Floating Point
</shameless blog plug>

This topic is closed to new replies.

Advertisement