My enemy sprite vibrates all the time after it has reached the variable endposition. What is wrong? Why is it vibrating?
In addition, the variable next_position is never true. But why? I want that the enemy sprite moves to a new random endposition after it reached the current endposition.
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Texture2D enemy;
Vector2 position, endposition;
bool next_position = false;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
graphics.PreferredBackBufferWidth = 1280;
graphics.PreferredBackBufferHeight = 720;
}
protected override void Initialize()
{
base.Initialize();
}
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
enemy = Content.Load<Texture2D>("zombie");
Random randomstart = new Random();
position = new Vector2(randomstart.Next(100, 200), randomstart.Next(100, 200));
endposition = new Vector2(randomstart.Next(100, 600), randomstart.Next(100, 400));
}
protected override void Update(GameTime gameTime)
{
float delta = (float)gameTime.ElapsedGameTime.TotalSeconds;
Random random = new Random();
position.X += 5 * delta;
position.Y += 3 * delta;
if (next_position == true)
{
endposition = new Vector2(random.Next(100, 600), random.Next(100, 400));
next_position = false;
}
if (Vector2.Dot(endposition - position, endposition - position) > 0 || Vector2.Dot(endposition - position, endposition - position) < 0)
{
Vector2 enemyDirection = Vector2.Normalize(endposition - position) * 100f;
position += enemyDirection * delta;
}
else
{
next_position = true;
}
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin();
spriteBatch.Draw(enemy, position, Color.White);
spriteBatch.End();
base.Draw(gameTime);
}
}
I am slightly confused by what the line
if (Vector2.Dot(endposition - position, endposition - position) > 0 || Vector2.Dot(endposition - position, endposition - position) < 0)
is supposed to do exactly.
From the rest of the code what you are trying to achieve with it, is checking whether the sprite's position is the enemy's position.
What the line actually does is calculating dot products of vectors in opposite directions, which will never be zero, unless the vectors are zero. So in theory this might actually work, but only if the position equals the enemy's position precisely.
However, you are moving the sprite a fixed distance per unit of time, and so it actually does not reach the enemy but overshoots it. Finding itself then on the other side it moves back, where it came from, and overshoots it again. This continues and results in the vibration you describe.
Now, the way the behaviour you want to implement is usually done is calculating the distance between the two objects(sprite and enemy), using Pythagoras' theorem, and then accepting the arrival of the object when the distance is either below a certain threshold(easy but prone to error), or shorter than the distance the object could travel this particular update(in your case, since you move by a normalized vector times 100f * delta, this distance is equal to 100f * delta).
I hope my explanation is helpful.
Of course, feel free to ask for clarifications.
Edit(response to comment):
The limit vector you are creating is relatively meaningless, as it is just the position offset by max_distance in both x and y direction. There is no need for anything like that.
However, the other if() includes pretty much the code you want, check this out:
// first you calculate the distance we can move this update
float max_distance = 100f * delta;
// now, we compare the actual distance to the end position to that maximum
// if the actual distance is larger, then we are not there yet
// otherwise, (if the actual distance is smaller) we will overshoot the target,
// thus we are in fact there! (=close enough to continue)
// (note: we compare the distances squared to avoid a square root,
// which makes this check much faster, which is good practice,
// in case we ever scale this up to more objects)
if ((position - endposition).LengthSquared() > max_distance * max_distance)
{
/* we are not yet there, continue moving! */
}
else
{
/* we are there! */
}
It really is this simple and no other check is needed. Let me know if this works out for you!
It’s better now, but not perfect. The enemy sprite doesn’t vibrate anymore, but it always moves some pixels down right if it has reached the end position. Why does it always move some pixels down right?
The rest works fine.
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Texture2D enemy;
Vector2 position, endposition;
bool next_position = false;
int count_nextposition;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
graphics.PreferredBackBufferWidth = 1280;
graphics.PreferredBackBufferHeight = 720;
}
protected override void Initialize()
{
base.Initialize();
}
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
enemy = Content.Load<Texture2D>("zombie");
Random randomstart = new Random();
position = new Vector2(randomstart.Next(100, 200), randomstart.Next(100, 200));
endposition = new Vector2(randomstart.Next(100, 600), randomstart.Next(100, 400));
count_nextposition = randomstart.Next(90, 121);
}
protected override void Update(GameTime gameTime)
{
float delta = (float)gameTime.ElapsedGameTime.TotalSeconds;
count_nextposition -= 1;
Random random = new Random();
position.X += 5 * delta;
position.Y += 3 * delta;
if (count_nextposition <= 0)
{
if (next_position == true)
{
endposition = new Vector2(random.Next(100, 600), random.Next(100, 400));
next_position = false;
}
float max_distance = 100f * delta;
if ((position - endposition).LengthSquared() > max_distance * max_distance)
{
Vector2 enemyDirection = Vector2.Normalize(endposition - position) * 100f;
position += enemyDirection * delta;
}
else
{
next_position = true;
count_nextposition = random.Next(90, 121);
}
}
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin();
spriteBatch.Draw(enemy, position, Color.White);
spriteBatch.End();
base.Draw(gameTime);
}
}
Related
Im trying to write some simple 2d physics in monogame.
I release a ball from a given start position with a given velocity and I want it to bounce back up when it is colliding with the floor.
My problem is that I seem to give the ball more energi for each bounce i.e. it bounces higher and higher for each collision with the floor. It should be the other way around.
I have:
float get_VelocityX(float _speed, double _angle)
{
return velocity_x = velocity_x +_speed * (float)Math.Cos(_angle);
}
public float get_VelocityY(float _speed, double _angle, float _t, float gravity)
{
return velocity_y = velocity_y + _speed * (float)Math.Cos(_angle); // - (float)(-gravity * _t);
}
And in my Update function I have this:
if (speed > 0)
{
timeCount += (float)gameTime.ElapsedGameTime.TotalSeconds;
t += timeCount;
}
else
{
return;
}
Vx = ball.get_VelocityX(speed, angle);
Vy = ball.get_VelocityY(speed, angle, t, gravity);
if (posX >= windowMAX)
{
posX = posX + -Vx * friction * t;
}
if (posY > windowMIN)
{
posY = posY + -Vy * friction * t;
}
else
{
posY += gravity;
}
ballRect.X = (int)posX;
ballRect.Y = (int)posY;
Where posX, posY and speed are user inputs for start position and velocity.
Gravity is just a float = 9.82f;
Right now Im not doing anything with the posX except setting the balls starting position. Next step will be to implement a throwing motion.
EDIT:
Friction = 0.001f;
t is deltatime.
I went through your logic and have prepared a sample code. Please read the following before you go through it.
In order to simulate real-life motion, you need to implement the physics accurately. Although your implemented velocity and position seems mostly correct, the gravity needs to be treated as acceleration, and therefore adding its value to the position (as done in your code) is incorrect. I assume that this is the reason why you aren't getting your expected result since the value of increment on the Y-component of position is far greater than it should be.
Instead of keeping PosX, PosY for the position, Velocity_X..(), Velocity_Y..() for velocity, I would advise you to use struct Vector2 as shown below in my code, which is included in the Monogame framework and has a lot more helping functions built-in. This will help in making your code shorter and cleaner.
I did not understand why you used the Cosine of the given angle in your implementation of Velocity for both its X and Y components. My code below is ignoring this.
You can see my code below. Here the bouncing object Box is of the type struct PhyObj with all the needed Physics of motion implemented within it.
public class Game1 : Game
{
private SpriteBatch _batch;
internal Texture2D Texture;
public static Vector2 GravityAcceleration => new Vector2(0, 9.8f);//Constant accerleration along Y-Axis
internal Rectangle Ground;
internal PhyObj Box;
public Game1()
{
_ = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
IsMouseVisible = true;
}
protected override void LoadContent()
{
Point center = Window.ClientBounds.Center;
Box = new PhyObj(new Vector2(center.X, 0), Vector2.Zero, 30, 30);
Ground = new Rectangle(0, center.Y, center.X * 2, 10);
_batch = new SpriteBatch(GraphicsDevice);
Texture = new Texture2D(GraphicsDevice, 1, 1);
Texture.SetData(new[] { Color.White });
}
protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
Exit();
Box.Accelerate(GravityAcceleration, gameTime);
if (Box.Pos.Y > Ground.Top - Box.Dest.Height)//Check if bounce needed
{
Box.Pos.Y = Ground.Top - Box.Dest.Height;//Clipping
Box.Vel.Y *= -1; //Bouncing
}
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
_batch.Begin();
_batch.Draw(Texture, Ground, Color.Black);
_batch.Draw(Texture, Box.Dest, Color.White);
_batch.End();
base.Draw(gameTime);
}
}
public struct PhyObj
{
internal static float friction => 0.005f;
public PhyObj(Vector2 x, Vector2 v, int width, int height)
{
Pos = x;
Vel = v;
Dest = new Rectangle(0, 0, width, height);
(Dest.X, Dest.Y) = ((int)Pos.X, (int)Pos.Y);
}
internal Vector2 Pos, Vel;
internal Rectangle Dest;
public void Accelerate(Vector2 acc, GameTime time)
{
Vel += acc - Vel * friction;
Pos += Vel * (float)time.ElapsedGameTime.TotalSeconds;
(Dest.X, Dest.Y) = ((int)Pos.X, (int)Pos.Y);
}
}
As shown in the Update() function, the PhyObj Box is being accelerated externally (In this case gravity, but you can add your custom external force), and the velocity/position it needs to attain is calculated internally.
The bouncing logic is simple: The Y-component of velocity is inverted.
The "Clipping" process here makes sure that the Box does not cross the Ground object even when the downward acceleration is acting upon it.
The next subsequent bounces have their height reduced due to the friction value (Done internally by the struct PhyObj).
I am drawing a line in XNA which I want to never end. I also have a tool that moves forward in X-direction and a camera which is centered at this tool. However, when I reach the end of the viewport the lines are not drawn anymore. Here are some pictures to illustrate my problem:
At the start the line goes across the whole screen, but as my tool moves forward, we reach the end of the line.
Here are the method which draws the lines:
private void DrawEvenlySpacedSprites (Texture2D texture, Vector2 point1, Vector2 point2, float increment)
{
var distance = Vector2.Distance (point1, point2); // the distance between two points
var iterations = (int)(distance / increment); // how many sprites with be drawn
var normalizedIncrement = 1.0f / iterations; // the Lerp method needs values between 0.0 and 1.0
var amount = 0.0f;
if (iterations == 0)
iterations = 1;
for (int i = 0; i < iterations; i++) {
var drawPoint = Vector2.Lerp (point1, point2, amount);
spriteBatch.Draw (texture, drawPoint, Color.White);
amount += normalizedIncrement;
}
}
Here are the draw method in Game. The dots are my lines:
protected override void Draw (GameTime gameTime)
{
graphics.GraphicsDevice.Clear(Color.Black);
nyVector = nextVector (gammelVector);
GraphicsDevice.SetRenderTarget (renderTarget);
spriteBatch.Begin ();
DrawEvenlySpacedSprites (dot, gammelVector, nyVector, 0.9F);
spriteBatch.End ();
GraphicsDevice.SetRenderTarget (null);
spriteBatch.Begin (SpriteSortMode.Deferred, BlendState.AlphaBlend, null, null, null, null, camera.transform);
spriteBatch.Draw (renderTarget, new Vector2 (), Color.White);
spriteBatch.Draw (tool, new Vector2(toolPos.X - (tool.Width/2), toolPos.Y - (tool.Height/2)), Color.White);
spriteBatch.End ();
gammelVector = new Vector2 (nyVector.X, nyVector.Y);
base.Draw (gameTime);
}
Can anyone point me in the right direction here? I'm guessing it has to do with the viewport.width, but I'm not quite sure how to solve it. Thank you for reading!
I read this and thought it would be a fun exercise this morning, so I decided to write this for fun.
The implementation is pretty simple, keep adding lines at the end of each other until the last line is outside of the viewable area.
The following code will draw a line infinitely going right. As an added optimization, the lines on the left side of the screen are deleted as you pass them. You could easily make it retain the lines that were there previously, or also create lines going left as well. I will leave these exercises to you.
Take a look at the following Line class, which will define a single line on screen:
public class Line
{
Texture2D Texture;
Color Color;
public Vector2 PointA;
public Vector2 PointB;
float Width;
public Line(Vector2 pointA, Vector2 pointB, float width, Color color, Texture2D texture)
{
Texture = texture;
PointA = pointA;
PointB = pointB;
Width = width;
Color = color;
}
public void Draw(SpriteBatch spritebatch)
{
float angle = (float)Math.Atan2(PointB.Y - PointA.Y, PointB.X - PointA.X);
float length = Vector2.Distance(PointA, PointB);
spritebatch.Draw(Texture, PointA, null, Color, angle, Vector2.Zero, new Vector2(length, Width), SpriteEffects.None, 0);
}
}
I wrote the implementation inside of a game class, since I was speedcoding. You can see the Game class below:
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Camera Camera;
Texture2D LineTexture;
List<Line> Lines;
Random Random;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
protected override void Initialize()
{
base.Initialize();
}
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
Camera = new Camera(GraphicsDevice.Viewport, 1f);
LineTexture = new Texture2D(GraphicsDevice, 1, 1);
LineTexture.SetData<Color>(new Color[] { Color.White });
Random = new Random();
Lines = new List<Line>();
}
protected override void UnloadContent()
{
}
protected override void Update(GameTime gameTime)
{
//handle input
KeyboardState kbState = Keyboard.GetState();
if (kbState.IsKeyDown(Keys.Escape))
this.Exit();
if (kbState.IsKeyDown(Keys.OemMinus))
Camera.Zoom -= 0.015f;
else if (kbState.IsKeyDown(Keys.OemPlus))
Camera.Zoom += 0.015f;
if (kbState.IsKeyDown(Keys.Up))
Camera.Move(new Vector2(0, -30));
else if (kbState.IsKeyDown(Keys.Down))
Camera.Move(new Vector2(0, 30));
if (kbState.IsKeyDown(Keys.Left))
Camera.Move(new Vector2(-30, 0));
else if (kbState.IsKeyDown(Keys.Right))
Camera.Move(new Vector2(30, 0));
//check if line is still in viewport - if not remove it
for (int i = 0; i < Lines.Count; i++)
{
if (Lines[i].PointB.X < Camera.Viewport.X)
{
Lines.RemoveAt(i);
i--;
}
}
//if there are no lines, create one to get started
if (Lines.Count == 0)
{
Vector2 p1 = new Vector2(Camera.Viewport.X, Random.Next(Camera.Viewport.Y + 50, Camera.Viewport.Height - 100));
Vector2 p2 = new Vector2(p1.X + Random.Next(5, 20), p1.Y + Random.Next(-5, 5));
Line line = new Line(p1, p2, 1, Color.Black, LineTexture);
Lines.Add(line);
}
//Check if we need to add some lines to the right of our last list item
while (Lines[Lines.Count - 1].PointB.X < Camera.Viewport.X + Camera.Viewport.Width)
{
Vector2 p1 = new Vector2(Lines[Lines.Count - 1].PointB.X, Lines[Lines.Count - 1].PointB.Y); ;
Vector2 p2 = new Vector2(p1.X + Random.Next(5, 20), p1.Y + Random.Next(-5, 5));
Line line = new Line(p1, p2, 1, Color.Black, LineTexture);
Lines.Add(line);
}
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(new Color(30, 90, 150));
spriteBatch.Begin(SpriteSortMode.Deferred, null, null, null, null, null, Camera.View);
foreach (Line line in Lines)
line.Draw(spriteBatch);
spriteBatch.End();
base.Draw(gameTime);
}
}
I've also included the Camera class for your convenience:
public class Camera
{
private const float zoomUpperLimit = 1.5f;
private const float zoomLowerLimit = 0.1f;
private float _zoom;
private Vector2 _pos;
private int ViewportWidth, ViewportHeight;
#region Properties
public float Zoom
{
get { return _zoom; }
set
{
_zoom = value;
if (_zoom < zoomLowerLimit)
_zoom = zoomLowerLimit;
if (_zoom > zoomUpperLimit)
_zoom = zoomUpperLimit;
}
}
public Rectangle Viewport
{
get
{
int width = (int)((ViewportWidth / _zoom));
int height = (int)((ViewportHeight / _zoom));
return new Rectangle((int)(_pos.X - width / 2), (int)(_pos.Y - height / 2), width, height);
}
}
public void Move(Vector2 amount)
{
_pos += amount;
}
public Vector2 Position
{
get { return _pos; }
set { _pos = value; }
}
public Matrix View
{
get
{
return Matrix.CreateTranslation(new Vector3(-_pos.X, -_pos.Y, 0)) *
Matrix.CreateScale(new Vector3(Zoom, Zoom, 1)) *
Matrix.CreateTranslation(new Vector3(ViewportWidth * 0.5f, ViewportHeight * 0.5f, 0));
}
}
#endregion
public Camera(Viewport viewport, float initialZoom)
{
_zoom = initialZoom;
_pos = Vector2.Zero;
ViewportWidth = viewport.Width;
ViewportHeight = viewport.Height;
}
}
So I just finished up doing a class for my wandering & pursuing enemies yet once I've gone through and declared the class and done the necessary coding when I run the game I get a NullReferenceException pop up which then tells me that the field Virtual_Alien is never assigned and will always have the default value of null.
What am I doing wrong here?
main game code:
private Roaming_Aliens roamingAlien;
private Virtual_Aliens virtualAlien;
public Vector2 playerPosition;
public Player mainPlayer = new Player();
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
mainPlayer.position = playerPostion;
// now assign that image to each of the characters we have
roamingAlien.LoadContent(Content);
virtualAlien.LoadContent(Content);
mainPlayer.LoadContent(Content);
score.LoadContent(Content);
// mainBackground.LoadContent(Content);
}
protected override void Update(GameTime gameTime)
{
currentKey = Keyboard.GetState();
if (currentKey.IsKeyDown(Keys.Escape))
{
this.Exit();
}
mainPlayer.Update(gameTime);
score.Update(gameTime);
virtualAlien.Update(gameTime);
roamingAlien.Wander();
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin();
//mainBackground.Draw(spriteBatch);
mainPlayer.Draw(spriteBatch);
virtualAlien.Draw(spriteBatch);
roamingAlien.Draw(spriteBatch);
spriteBatch.End();
}
Virtual alien code:
public class Virtual_Aliens
{
private enum TankAiState
{
Chasing,
Wander
}
private float maxSpeed;
private float maxRotation;
private float chaseDistance;
private float hysteresis;
private Texture2D texture;
private Vector2 drawingOrigin;
private Vector2 position;
private TankAiState tankState = TankAiState.Wander;
private float orientation;
private Random random = new Random();
private Rectangle viewportbounds;
public Rectangle boundingBox;
public Vector2 playerPosition;
private Vector2 heading;
public Virtual_Aliens(Rectangle pos, Rectangle b)
{
position = new Vector2 (100, 100);
boundingBox = new Rectangle(pos.X, pos.Y, pos.Width, pos.Height);
viewportbounds = new Rectangle(b.X, b.Y, b.Width, b.Height);
orientation = 0.0f;
heading = new Vector2(0, 0);
maxSpeed = 2.0f;
maxRotation = 0.20f;
random = new Random();
hysteresis = 15.0f;
chaseDistance = 250.0f;
}
public void LoadContent(ContentManager Content)
{
texture = Content.Load<Texture2D>("images/asteroid");
}
private Vector2 OrientationAsVector(float orien)
{
Vector2 orienAsVect;
orienAsVect.X = (float)Math.Cos(orien);
orienAsVect.Y = (float)Math.Sin(orien);
return orienAsVect;
}
Vector2 wanderPosition = new Vector2();
public void Wander()
{
// The wander effect is accomplished by having the character aim in a random
// direction. Every frame, this random direction is slightly modified.
// the max +/- the agent will wander from its current position
float wanderLimits = 0.5f;
// this defines what proportion of its maxRotation speed the agent will turn
float turnFactor = 0.15f;
// randomly define a new position
wanderPosition.X += MathHelper.Lerp(-wanderLimits, wanderLimits, (float)random.NextDouble());
wanderPosition.Y += MathHelper.Lerp(-wanderLimits, wanderLimits, (float)random.NextDouble());
// normalize the wander position, ...
if (wanderPosition != Vector2.Zero)
wanderPosition.Normalize();
// now find the new orientation based on the wanderPosition
orientation = TurnToFace(wanderPosition, orientation, turnFactor * maxRotation);
// determine the heading vector based on orientation
heading = OrientationAsVector(orientation);
// finally update the agents position based upon the new heading and its speed
// assume a wandering agent only moves at 0.5 of maxSpeed
position += heading * 0.5f * maxSpeed;
WrapForViewport();
}
private void WrapForViewport()
{
if (position.X < 0)
{
position.X = viewportbounds.Width;
}
else if (position.X > viewportbounds.Width)
{
position.X = 0;
}
if (position.Y < 0)
{
position.Y = viewportbounds.Height;
}
else if (position.Y > viewportbounds.Height)
{
position.Y = 0;
}
}
private float WrapAngle(float radian)
{
while (radian < -MathHelper.Pi)
{
radian += MathHelper.TwoPi;
}
while (radian > MathHelper.Pi)
{
radian -= MathHelper.TwoPi;
}
return radian;
}
private float TurnToFace(Vector2 steering, float currentOrientation, float turnSpeed)
{
float newOrientation;
float desiredOrientation;
float orientationDifference;
float x = steering.X;
float y = steering.Y;
// the desiredOrientation is given by the steering vector
desiredOrientation = (float)Math.Atan2(y, x);
// find the difference between the orientation we need to be
// and our current Orientation
orientationDifference = desiredOrientation - currentOrientation;
// now using WrapAngle to get result from -Pi to Pi
// ( -180 degrees to 180 degrees )
orientationDifference = WrapAngle(orientationDifference);
// clamp that between -turnSpeed and turnSpeed.
orientationDifference = MathHelper.Clamp(orientationDifference, -turnSpeed, turnSpeed);
// the closest we can get to our target is currentAngle + orientationDifference.
// return that, using WrapAngle again.
newOrientation = WrapAngle(currentOrientation + orientationDifference);
return newOrientation;
}
public void Update(GameTime gameTime)
{
if (tankState == TankAiState.Wander)
{
chaseDistance -= hysteresis / 2;
}
else if (tankState == TankAiState.Chasing)
{
chaseDistance += hysteresis / 2;
}
// Second, now that we know what the thresholds are, we compare the tank's
// distance from the cat against the thresholds to decide what the tank's
// current state is.
float distanceFromPlayer = Vector2.Distance(position, playerPosition);
if (distanceFromPlayer > chaseDistance)
{
// just like the mouse, if the tank is far away from the cat, it should
// idle.
tankState = TankAiState.Wander;
}
else
{
tankState = TankAiState.Chasing;
}
// Third, once we know what state we're in, act on that state.
float currentTankSpeed;
if (tankState == TankAiState.Chasing)
{
// the tank wants to chase the cat, so it will just use the TurnToFace
// function to turn towards the cat's position. Then, when the tank
// moves forward, he will chase the cat.
orientation = TurnToFace(playerPosition, orientation, maxRotation);
currentTankSpeed = maxSpeed;
}
else if (tankState == TankAiState.Wander)
{
Wander();
}
}
public void Draw(SpriteBatch spriteBatch)
{
drawingOrigin = new Vector2(texture.Width / 2, texture.Height / 2);
spriteBatch.Draw(texture, position, null, Color.White, orientation, drawingOrigin, 1.0f, SpriteEffects.None, 0.0f);
}
public Vector2 PlayerPosition
{
set
{
playerPosition = value;
}
get
{
return playerPosition;
}
}
}
You declare (twice actually) a variable called virtualAlien (of type Virtual_Aliens). However, you never assign it anything (while using it all over the place.
You declaration needs to look like:
Virtual_Alien virtualAlien = new Virtual_Alien();
This is assigns it to a new instance of the Virtual_Alien class, allowing you to use it later on. If you need parameters that aren't available until later, put the instantiation at the point when all needed information is available, and add null checks around any use of the variable that could happen before the instantiating code is called.
The romaingAlien member appears to have a similar problem.
I am making a little game in c# using XNA 4.0. I have successfully made my sprite move in all directions and make it look like it is actually walking using an array. So far I have only tested this with the "Up" key. The problem is that when I push the up key, the sprite moves, but it goes through all the elements in the array so fast that it looks like he is running way to fast for the amount of space he is going. Is there any way to slow down the speed at which the textures change between each other, such as a pause method or something. Any bit of help is appreciated, Thanks.
namespace RandomGame
{
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Color backColor = Color.FromNonPremultiplied(190, 230, 248, 250);
int i = 0;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
graphics.IsFullScreen = false;
graphics.PreferredBackBufferHeight = 500;
graphics.PreferredBackBufferWidth = 800;
Content.RootDirectory = "Content";
}
protected override void Initialize()
{
base.Initialize();
}
Texture2D[] UpTextures = new Texture2D[6];
Texture2D startTexture;
Texture2D leftTexture;
Texture2D rightTexture;
Vector2 position = new Vector2(380, 230);
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
startTexture = Content.Load<Texture2D>("BlueLinkStart");
leftTexture = Content.Load<Texture2D>("BlueLinkLeft");
rightTexture = Content.Load<Texture2D>("BlueLinkRight");
UpTextures[0] = Content.Load<Texture2D>("BlueLinkUp");
UpTextures[1] = Content.Load<Texture2D>("BlueLinkUp2");
UpTextures[2] = Content.Load<Texture2D>("BlueLinkUp3");
UpTextures[3] = Content.Load<Texture2D>("BlueLinkUp4");
UpTextures[4] = Content.Load<Texture2D>("BlueLinkUp5");
UpTextures[5] = Content.Load<Texture2D>("BlueLinkUp6");
}
protected override void UnloadContent()
{
}
protected override void Update(GameTime gameTime)
{
// Allows the game to exit
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.Escape))
{
this.Exit();
}
if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.Left) && position.X > -3)
{
position.X -= 2;
}
if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.Right) && position.X < 772)
{
position.X += 2;
}
if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.Up) && position.Y > -3)
{
position.Y -= 2;
}
if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.Down) && position.Y < 472)
{
position.Y += 2;
}
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.FromNonPremultiplied(188, 231, 241, 255));
if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.Left))
{
spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.AlphaBlend);
spriteBatch.Draw(leftTexture, position, Color.White);
spriteBatch.End();
base.Draw(gameTime);
}
else if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.Right))
{
spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.AlphaBlend);
spriteBatch.Draw(rightTexture, position, Color.White);
spriteBatch.End();
base.Draw(gameTime);
}
else if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.Up))
{
spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.AlphaBlend);
spriteBatch.Draw(UpTextures[i], position, Color.White);
spriteBatch.End();
i++;
if (i == 6) { i = 0; }
base.Draw(gameTime);
}
else
{
spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.AlphaBlend);
spriteBatch.Draw(startTexture, position, Color.White);
spriteBatch.End();
base.Draw(gameTime);
}
}
}
}
Yes, there is.
First of all, you need a variable that holds the animation state. Your i is probably supposed to be that. However, you should rename it to e.g. animationState to reflect its purpose. Furthermore, it is easier to make it a float or double variable.
Then updating the animation is a task for the Update() method. You obviously rely on the 60 Hz update frequency. This is ok for a small game, but you should take possible slow downs into consideration for bigger ones. If you have n sprites, and you want to change the sprite every m ms, then you update the animationState as follows:
animationState += updateDuration * m;
if(animationState >= n) animationState -= n;
updateDuration is the time since the last update. So for 60 Hz this is 1000.0 / 60.
And then you need to draw the correct sprite in the Draw() method:
spriteBatch.Draw(UpTextures[(int)animationState], position, Color.White);
I want that my character makes a 40 pixel height jump if I press the A button once. But I don't know if 40f is equal to 40 pixel?
What do you think about my code? What is wrong?
In addition, I want that my program runs with the same speed on every computer.
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Texture2D image, water;
float Gravity = 5.0F;
float Acceleration = 20.0F;
Vector2 Position = new Vector2(1200,720);
Vector2 Velocity;
float rotation = 0;
SpriteEffects flip;
Vector2 Speed = new Vector2(0, 0);
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
graphics.PreferredBackBufferWidth = 1280;
graphics.PreferredBackBufferHeight = 720;
}
protected override void Initialize()
{
base.Initialize();
}
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
image = Content.Load<Texture2D>("cartoondolphin");
water = Content.Load<Texture2D>("background");
flip = SpriteEffects.None;
}
protected override void Update(GameTime gameTime)
{
float VelocityX = 0f;
float VelocityY = 0f;
float time = (float)gameTime.ElapsedGameTime.TotalSeconds;
KeyboardState kbState = Keyboard.GetState();
if(kbState.IsKeyDown(Keys.Left))
{
rotation = 0;
flip = SpriteEffects.None;
VelocityX += -5f;
}
if(kbState.IsKeyDown(Keys.Right))
{
rotation = 0;
flip = SpriteEffects.FlipHorizontally;
VelocityX += 5f;
}
// jump if the dolphin is under water
if(Position.Y >= 670)
{
if (kbState.IsKeyDown(Keys.A))
{
if (flip == SpriteEffects.None)
{
VelocityY += 40f;
}
else
{
VelocityY += 40f;
}
}
}
else
{
VelocityY += -10f;
}
float deltaY = 0;
float deltaX = 0;
deltaY = Gravity * (float)gameTime.ElapsedGameTime.TotalSeconds;
deltaX += VelocityX * (float)gameTime.ElapsedGameTime.TotalSeconds * Acceleration;
deltaY += -VelocityY * (float)gameTime.ElapsedGameTime.TotalSeconds * Acceleration;
Speed = new Vector2(Speed.X + deltaX, Speed.Y + deltaY);
Position += Speed * (float)gameTime.ElapsedGameTime.TotalSeconds;
Velocity.X = 0;
if (Position.Y + image.Height/2 > graphics.PreferredBackBufferHeight)
Position.Y = graphics.PreferredBackBufferHeight - image.Height/2;
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin();
spriteBatch.Draw(water, new Rectangle(0, graphics.PreferredBackBufferHeight -100, graphics.PreferredBackBufferWidth, 100), Color.White);
spriteBatch.Draw(image, Position, null, Color.White, MathHelper.ToRadians(rotation), new Vector2(image.Width / 2, image.Height / 2), 1, flip, 1);
spriteBatch.End();
base.Draw(gameTime);
}
}
The problem is that you're not taking the gameTime into account. If a computer with a fast CPU executes that code twice as fast as a slower PC, the value of velocity will increase twice as fast (relative to the slower PC).
When you have code like:
VelocityX += 5f;
You need to keep track of the last gameTime, and get the difference between the frames, and multiply that by your constant. (Some code grabbed from Xna adding gravity to a 2d sprite)
int updateTime = gt.ElapsedGameTime.TotalMilliseconds - oldgt.ElapsedGameTime.TotalMilliseconds;
float timeScalar = updateTime / AVG_FRAME_TIME;
VelocityX += 5f * timeScalar;