I have a problem that my camera starts to stutter when moving it after a random amount of time. This is not the case for the movement itself. That runs really smoothly. It is just hte camera that starts to stutter. Small notice I am working in monogame
Here is the code of the main
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
namespace Cube_chaser
{
public class CubeChaserGame : Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Camera camera;
Map map;
BasicEffect effect;
private Vector2 mouseRotationBuffer;
private MouseState currentMouseState, previousMouseState;
public Vector3 moveVector = Vector3.Zero;
public CubeChaserGame()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
protected override void Initialize()
{
camera = new Camera(this, new Vector3(0.5f, 0.5f, 0.5f), Vector3.Zero, 5f);
Components.Add(camera);
effect = new BasicEffect(GraphicsDevice);
map = new Map(GraphicsDevice);
IsMouseVisible = false;
graphics.IsFullScreen = true;
previousMouseState = Mouse.GetState();
base.Initialize();
}
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
}
protected override void UnloadContent()
{
// TODO: Unload any non ContentManager content here
}
protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
Exit();
float dt = (float)gameTime.ElapsedGameTime.TotalSeconds;
KeyboardState keyState = Keyboard.GetState();
currentMouseState = Mouse.GetState();
moveVector = Vector3.Zero;
//Handle the mouse movement for rotation
float deltaX, deltaY;
if (currentMouseState != previousMouseState)
{
//We need to save the mouse location for further use
deltaX = (currentMouseState.X - (GraphicsDevice.Viewport.Width / 2));
deltaY = (currentMouseState.Y - (GraphicsDevice.Viewport.Height / 2));
mouseRotationBuffer.X -= (0.09f * deltaX * dt);
mouseRotationBuffer.Y -= (0.09f * deltaY * dt);
if (mouseRotationBuffer.Y < MathHelper.ToRadians(-75.0f))
{
mouseRotationBuffer.Y = mouseRotationBuffer.Y - (mouseRotationBuffer.Y - MathHelper.ToRadians(-75.0f));
}
if (mouseRotationBuffer.Y > MathHelper.ToRadians(75.0f))
{
mouseRotationBuffer.Y = mouseRotationBuffer.Y - (mouseRotationBuffer.Y - MathHelper.ToRadians(75.0f));
}
camera.Rotation = new Vector3(-MathHelper.Clamp(mouseRotationBuffer.Y, MathHelper.ToRadians(-75.0f), MathHelper.ToRadians(75.0f)), MathHelper.WrapAngle(mouseRotationBuffer.X), 0);
deltaX = 0;
deltaY = 0;
}
try { Mouse.SetPosition(GraphicsDevice.Viewport.Width / 2, GraphicsDevice.Viewport.Height / 2); }
catch { }
if (keyState.IsKeyDown(Keys.W)) moveVector.Z = 1;
if (keyState.IsKeyDown(Keys.S)) moveVector.Z = -1;
if (keyState.IsKeyDown(Keys.A)) moveVector.X = 1;
if (keyState.IsKeyDown(Keys.D)) moveVector.X = -1;
if (keyState.IsKeyDown(Keys.Space) && camera.Position.Y >= 0.5f && camera.Position.Y <= 0.8f) moveVector.Y = 0.05f;
// if (camera.Position.Y == 0.8f&&camera.Position.Y>=0.5f)camera.moveVector.Y=-0.01f;
if (moveVector != Vector3.Zero)
{
//We don't want to make the player move faster when it is going diagonally.
moveVector.Normalize();
//Now we add the smoothing factor and speed factor
moveVector *= (dt * camera.cameraSpeed);
Vector3 newPosition = camera.PreviewMove(moveVector);
bool moveTrue = true;
if (newPosition.X < 0 || newPosition.X > Map.mazeWidth) moveTrue = false;
if (newPosition.Z < 0 || newPosition.Z > Map.mazeHeight) moveTrue = false;
foreach (BoundingBox boxes in map.GetBoundsForCell((int)newPosition.X, (int)newPosition.Z))
{
if (boxes.Contains(newPosition) == ContainmentType.Contains)
{
moveTrue = false;
}
}
if (moveTrue) camera.Move(moveVector);
previousMouseState = currentMouseState;
camera.Update(gameTime);
base.Update(gameTime);
}
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Black);
map.Draw(camera, effect);
// TODO: Add your drawing code here
base.Draw(gameTime);
}
}
}
This is the code for the camera:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Graphics;
namespace Cube_chaser
{
public class Camera: GameComponent
{
#region Fields
public Vector3 cameraPosition;
public Vector3 cameraRotation;
public float cameraSpeed;
private Vector3 cameraLookAt;
#endregion
#region Properties
public Matrix Projection
{
get;
protected set;
}
public Matrix View
{
get
{
return Matrix.CreateLookAt(cameraPosition, cameraLookAt, Vector3.Up);
}
}
public Vector3 Position
{
get { return cameraPosition; }
set
{
cameraPosition = value;
UpdateLookAt();
}
}
public Vector3 Rotation
{
get { return cameraRotation; }
set
{
cameraRotation = value;
UpdateLookAt();
}
}
#endregion
#region Constructor
public Camera (Game game, Vector3 position, Vector3 rotation, float speed):base(game)
{
cameraSpeed = speed;
//Setup the projection Matrix
Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, game.GraphicsDevice.Viewport.AspectRatio, 0.05f, 100.0f);
MoveTo(position, rotation);
}
#endregion
#region Helper Methods
//Set the camera its position and rotation
public void MoveTo(Vector3 pos, Vector3 rot)
{
Position = pos;
Rotation = rot;
}
//Updating the look at vector
public void UpdateLookAt()
{
//Built a rotation matrix to rotate the direction we are looking
Matrix rotationMatrix = Matrix.CreateRotationX(cameraRotation.X) * Matrix.CreateRotationY(cameraRotation.Y);
// Build a look at offset vector
Vector3 lookAtOffset = Vector3.Transform(Vector3.UnitZ, rotationMatrix);
//Update our camera's look at the vector
cameraLookAt = (cameraPosition + lookAtOffset);
}
//Method to create movement and to check if it can move:)
public Vector3 PreviewMove(Vector3 amount)
{
//Create a rotation matrix to move the camera
Matrix rotate = Matrix.CreateRotationY(cameraRotation.Y);
//Create the vector for movement
Vector3 movement = new Vector3(amount.X, amount.Y, amount.Z);
movement = Vector3.Transform(movement, rotate);
// Give the value of the camera position +ze movement
return (cameraPosition+movement);
}
//Method that moves the camera when it hasnt'collided with anything
public void Move(Vector3 scale)
{
//Moveto the location
MoveTo(PreviewMove(scale), Rotation);
}
#endregion
/*public void Update(GameTime gameTime)
{
}*/
}
}
Any idea why the camera stutters after a while?
Thanks,
Jeromer
Forgot to post the answer to my problem for people having the same issue.
The following changes were made to make the problem go away:
in the constructor of the game1 class
IsFixedTimeStep = true;
TargetElapsedTime = TimeSpan.FromMilliseconds(16);
This results in fixed timesteps and thus a smooth calculation
Related
enter code hereWhen im drawing it to a string it just stays at 300,300 . My mouse always updates its Vector2 position.X, position.Y. I need to be able to update my players position or my enemy wont follow my player. It just goes to that certain player position i set for it. PLEASE HELP AND THANK YOU!
class Enemy
{
Player p = new Player();
public Vector2 direction, velocity,position;
public float speed;
public Texture2D texture;
public Enemy()
{
speed = 1;
texture = null;
position = new Vector2(600, 500);
}
public void LoadContent(ContentManager Content)
{
texture = Content.Load<Texture2D>("circle");
}
public void Update(GameTime gameTime)
{
MouseState mouse = Mouse.GetState();
direction = p.position - position;
direction.Normalize();
velocity = direction * speed;
position += velocity;
}
public void Draw(SpriteBatch spriteBatch)
{
spriteBatch.Draw(texture, position, Color.Red);
}
/*
direction = p.position - position;
direction.Normalize();
velocity = direction * speed;
position += velocity;
*/
}
class Player
{
public float rotation, bulletDelay;
public Vector2 position,velocity,origin;
public string spriteName;
public Texture2D texture,bulletTexture;
List<Bullets> bullets = new List<Bullets>();
public float speed = 10;
public float health = 100;
public Player()
{
texture = null;
spriteName = "playerover";
position = new Vector2(300, 300);
bulletDelay = 20;
}
public void LoadContent(ContentManager Content)
{
texture = Content.Load<Texture2D>(spriteName);
bulletTexture = Content.Load<Texture2D>("playerbullet");
}
public void Draw(SpriteBatch spriteBatch)
{
origin = new Vector2(texture.Width / 2, texture.Height / 2);
spriteBatch.Draw(texture, new Rectangle((int)position.X, (int)position.Y, texture.Width, texture.Height), null, Color.White, rotation,new Vector2(texture.Width / 2, texture.Height / 2), SpriteEffects.None, 0);
foreach (Bullets bullet in bullets)
{
bullet.Draw(spriteBatch);
}
}
public void Update(GameTime gameTime)
{
MouseState curMouse = Mouse.GetState();
KeyboardState keyState = Keyboard.GetState();
Vector2 mouseLoc = new Vector2(curMouse.X, curMouse.Y);
Vector2 direction = mouseLoc - position;
rotation = (float)(Math.Atan2(direction.Y, direction.X));
if (keyState.IsKeyDown(Keys.W))
{
position.Y -= speed;
}
if (keyState.IsKeyDown(Keys.S))
{
position.Y += speed;
}
if (keyState.IsKeyDown(Keys.A))
{
position.X -= speed;
}
if (keyState.IsKeyDown(Keys.D))
{
position.X += speed;
}
if (curMouse.LeftButton == ButtonState.Pressed)
{
Shoot();
}
UpdateBullets();
}
public void UpdateBullets()
{
foreach (Bullets bullet in bullets)
{
bullet.position += bullet.velocity;
if (bullet.position.Y <= 5)
{
bullet.isVisible = false;
}
if (bullet.position.X <= 5)
{
bullet.isVisible = false;
}
if (bullet.position.X >= 785)
{
bullet.isVisible = false;
}
if (bullet.position.Y >= 575)
{
bullet.isVisible = false;
}
}
for (int i = 0; i < bullets.Count; i++)
{
if (!bullets[i].isVisible)
{
bullets.RemoveAt(i);
i--;
}
}
}
public void Shoot()
{
if (bulletDelay >= 0)
bulletDelay--;
if (bulletDelay <= 0)
{
Bullets newBullet = new Bullets(bulletTexture);
newBullet.velocity = new Vector2((float)Math.Cos(rotation), (float)Math.Sin(rotation)) * 5f + velocity;
newBullet.position = position + newBullet.velocity * 5;
newBullet.isVisible = true;
if (bullets.Count < 20)
{
bullets.Add(newBullet);
}
}
if (bulletDelay == 0)
{
bulletDelay = 20;
}
}
}
class Hud
{
public SpriteFont font;
public bool showHud;
Player p = new Player();
Enemy e = new Enemy();
public Hud()
{
showHud = true;
}
public void LoadContent(ContentManager Content)
{
font = Content.Load<SpriteFont>("font");
}
public void Draw(SpriteBatch spriteBatch)
{
MouseState mouse = Mouse.GetState();
if (showHud)
{
spriteBatch.DrawString(font, "Mouse.X = " + mouse.X, new Vector2(10, 0), Color.White);
spriteBatch.DrawString(font, "Mouse.Y = " + mouse.Y, new Vector2(10, 20), Color.White);
spriteBatch.DrawString(font, "Health = " + p.health, new Vector2(10, 40), Color.White);
spriteBatch.DrawString(font, "Pos.Y = " + p.position.Y, new Vector2(10, 60), Color.White);
spriteBatch.DrawString(font, "Pos.X = " + p.position.X, new Vector2(10, 80), Color.White);
}
}
}
I didn't read all the code you posted, but it seems that you created a new separate instance for the player in the Enemy class which is never updated.
class Enemy
{
Player p = new Player();
//...
}
You should either take a Vector2 for the players position into your enemies Update method or handle the enemy movement somewhere else. Perhaps you could have a method in your Enemy class named Follow taking a Player object as a parameter, then you could do something like the following in the place where you handle all entity movement. It would look roughly like this:
public void UpdateEntities(GameTime gameTime)
{
player.Update(gameTime);
enemy.Update(gameTime);
if(PlayerDistanceFromEnemy() < 50)
enemy.Follow(player);
}
That is a very rough guideline and probably something you'd want to rewrite later on, but it will work.
as for the Follow method in your enemy class:
public void Follow(Player player)
{
this.p = player;
}
public void Update(GameTime gameTime)
{
if(p != null)
{
//do stuff
}
}
This will work, but you have to work on the structure of your code if you want to expand. I might update this answer later with a better solution if someone doesn't do it before me.
i am new in XNA .i am trying to build a small 2D game where enemy(ball) are falling from the top of the screen randomly.the player can move inside the screen. what i want to do is when the player(spaceShip) collide with the ball the ball will remove from the screen.And i don't know how to do that. can anyone help me with this?
here's my code-
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 RandomSprite
{
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
TimeSpan timeSpan = TimeSpan.FromMilliseconds(2000);
Sprite spaceShip;
Texture2D ballTexture;
Texture2D backgroundtexture;
Vector2 ballPos = new Vector2(100f, 100f);
List<Sprite> ballList = new List<Sprite>();
float timer = 0f;
float dropInterval = .50f;
float speed = 4f;
Random random;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
protected override void Initialize()
{
// TODO: Add your initialization logic here
random = new Random();
base.Initialize();
}
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
backgroundtexture = Content.Load<Texture2D>("dungeon600x400");
spriteBatch = new SpriteBatch(GraphicsDevice);
ballTexture = Content.Load<Texture2D>("ball");
spaceShip = new Sprite();
spaceShip.Texture = Content.Load<Texture2D>("gugu");
// retrieve the height of the screen
int screenHeight = GraphicsDevice.Viewport.Height;
// find the center point of the screen along the x-axis
int screenCenterX = GraphicsDevice.Viewport.Width / 2;
spaceShip.Position = new Vector2(
screenCenterX - (spaceShip.Texture.Width / 2),
screenHeight - spaceShip.Texture.Height - 20);
}
private void HandleInput()
{
// Retrieve the current state of the keyboard
KeyboardState keyboardState = Keyboard.GetState();
Vector2 playerVelocity = Vector2.Zero;
// Check if the Left arrow key is pressed and change the velocity of the character accordingly
if (keyboardState.IsKeyDown(Keys.Left))
{
playerVelocity += new Vector2(-speed, 0);
}
// Check if the Right arrow key is pressed and change the velocity of the character accordingly
if (keyboardState.IsKeyDown(Keys.Right))
{
playerVelocity += new Vector2(speed, 0);
}
if (keyboardState.IsKeyDown(Keys.Up))
{
playerVelocity += new Vector2(0, -speed);
}
if (keyboardState.IsKeyDown(Keys.Down))
{
playerVelocity += new Vector2(0, speed);
}
// Apply the velocity to the character's position
spaceShip.Position += playerVelocity;
}
public void HandleFallingCake()
{
List<Sprite> toRemove = new List<Sprite>();
foreach (Sprite ball in ballList)
{
if (ball.Position.Y > (GraphicsDevice.Viewport.Height - 100))
toRemove.Add(ball);
else
ball.Position += new Vector2(0, speed);
}
if (toRemove.Count==1 )
{
foreach (Sprite cake in toRemove)
{
ballList.Remove(cake);
}
}
}
protected override void Update(GameTime gameTime)
{
// Allows the game to exit
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
// TODO: Add your update logic here
timer += (float)gameTime.ElapsedGameTime.TotalSeconds;
if (timer >= dropInterval)
{
int xPos = random.Next(GraphicsDevice.Viewport.Width-50);
ballList.Add(new Sprite(ballTexture, new Vector2(xPos, -100)));
timer = 0f;
}
HandleFallingCake();
HandleInput();
timeSpan -= gameTime.ElapsedGameTime;
if (timeSpan <= TimeSpan.Zero)
{
timeSpan = TimeSpan.FromMilliseconds(2000);
}
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
// TODO: Add your drawing code here
spriteBatch.Begin();
//spriteBatch.Draw(backgroundtexture, new Rectangle(0,0,GraphicsDevice.Viewport.Width, GraphicsDevice.Viewport.Height), Color.White);
spaceShip.Draw(spriteBatch);
foreach (Sprite ball in ballList)
{
ball.Draw(spriteBatch);
}
spriteBatch.End();
base.Draw(gameTime);
}
}
}
and i have a subclass sprite.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace RandomSprite
{
class Sprite
{
public Texture2D Texture;
public Vector2 Position ;
public Sprite() { }
public Sprite(Texture2D texture, Vector2 position)
{
Texture = texture;
Position = position;
}
public void Draw(SpriteBatch spriteBatch)
{
if (Texture != null)
spriteBatch.Draw(Texture, Position, Color.White);
}
}
}
You describe the goal, but not ask questions.
If you want to know how to check the intersection of two objects, then you need to define for them bounds (class Rectangle) and check their intersection method Rectangle.Intersect. Anyway better adjust your question.
I built a camera based on the class from http://www.david-amador.com/2009/10/xna-camera-2d-with-zoom-and-rotation/ . I'm currently making a kinda space-shooter game (but with story and this stuff) and want to have the camera working fine first. But the Zoom doesn't work as I want. I don't really know why, but I hope you can help me ^^
The Issue is, that the sprite moves around for like 30 pixels in some direction. Propbably i didn't set the Position of the main-spaceship properly or the camera is just bugging.
The game is running in full-screen.
Game1 Method:
InputHandler.Update(gameTime, graphics.GraphicsDevice);
World.Camera.Position = new Vector2(Player.Ship.Position.X + Player.Ship.Texture.Width/2, Player.Ship.Position.Y+Player.Ship.Texture.Height/2);
World.Camera.Zoom = InputHandler.ZoomValue;
I add half the size of the texture of the main-spaceship to the cameraposition so that the middle of the texture should be in the middle. It doesn't work either without it..
How I set fullscreen (I know... It's not the way I should have done it but it doesn't work with the normal code.)
graphics.PreferredBackBufferHeight = 10000;
graphics.PreferredBackBufferWidth = 10000;
graphics.ToggleFullScreen();
graphics.PreferMultiSampling = true;
graphics.IsFullScreen = true;
graphics.ApplyChanges();
The InputHandler Class:
public static void Update(GameTime gameTime, GraphicsDevice graphics)
{
_oldKeyboardState = keyboardState;
_oldMouseState = mouseState;
keyboardState = Keyboard.GetState();
mouseState = Mouse.GetState();
MousePosition = new Vector2(mouseState.X, mouseState.Y);
MatrixMousePosition = Vector2.Transform(MousePosition, Matrix.Invert(World.Camera.GetTransformation(graphics)));
OldScrollWheelValue = ScrollWheelValue;
ScrollWheelValue = mouseState.ScrollWheelValue;
if (ScrollWheelValue > OldScrollWheelValue) ZoomValue += 0.1f;
if (ScrollWheelValue < OldScrollWheelValue) ZoomValue -= 0.1f;
ZoomValue = MathHelper.Clamp(ZoomValue, 0.5f, 1.5f);
}
And finally the camera class:
protected float _zoom; // Camera Zoom
public Matrix _transform; // Matrix Transform
public Vector2 _pos; // Camera Position
protected float _rotation; // Camera Rotation
public float Zoom
{
get { return _zoom; }
set { _zoom = value; if (_zoom < 0.1f) _zoom = 0.1f; }
}
public float Rotation
{
get { return _rotation; }
set { _rotation = value; }
}
public Vector2 Position
{
get { return _pos; }
set { _pos = value; }
}
public Camera()
{
_zoom = 1.0f;
_rotation = 0.0f;
_pos = Vector2.Zero;
}
public Matrix GetTransformation(GraphicsDevice graphicsDevice)
{
var viewport = graphicsDevice.Viewport;
_transform =
Matrix.CreateTranslation(new Vector3(-_pos.X*Zoom, -_pos.Y*Zoom, 0)) *
Matrix.CreateRotationZ(Rotation) *
Matrix.CreateScale(new Vector3(Zoom, Zoom, 1)) *
Matrix.CreateTranslation(new Vector3(viewport.Width * 0.5f, viewport.Height * 0.5f, 0));
return _transform;
}
I see what you did wrong, this line:
transform = Matrix.CreateTranslation(new Vector3(-_pos.X*Zoom, -_pos.Y*Zoom, 0)) * ....
should be:
transform = Matrix.CreateTranslation(new Vector3(-_pos.X, -_pos.Y, 0)) * ....
Because right now when you zoom you also translate the image, which is causing the moving sprites.
Put together an enemy AI system for an enemy in which it should wander around idly when the player is not within distance to the enemy and when player is within enemy distance it should initiate chase behaviour and chase after player until player has managed to exit out of the enemy's chase radius.
Currently the enemy is able to wonder freely yet when the player comes within proximity of the enemy the enemy will carry on wandering instead of chasing player.
Anyone help me fix this problem?
Code is as follows.
public enum AIState
{
Chasing,
Wander
}
private float maxSpeed;
private float maxRotation;
private float chaseDistance;
private float hysteresis;
private Texture2D texture;
private Vector2 drawingOrigin;
private Vector2 position;
public AIState aiState = AIState.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(300, 400);
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;
hysteresis = 15.0f;
chaseDistance = 250.0f;
Thread.Sleep(200);
random = new Random();
}
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 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());
if (wanderPosition != Vector2.Zero)
{
wanderPosition.Normalize();
}
orientation = TurnToFace(wanderPosition, orientation, turnFactor * maxRotation);
heading = OrientationAsVector(orientation);
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 (aiState == AIState.Wander)
{
chaseDistance -= hysteresis / 2;
}
else if (aiState == AIState.Chasing)
{
chaseDistance += hysteresis / 2;
}
float distanceFromPlayer = Vector2.Distance(position, playerPosition);
if (distanceFromPlayer > chaseDistance)
{
aiState = AIState.Wander;
}
else
{
aiState = AIState.Chasing;
}
float currentSpeed;
if (aiState == AIState.Chasing)
{
orientation = TurnToFace(playerPosition, orientation, maxRotation);
currentSpeed = maxSpeed;
}
else if (aiState == AIState.Wander)
{
Wander();
}
}
public void Draw(SpriteBatch spriteBatch)
{
boundingBox.X = (int)position.X;
boundingBox.Y = (int)position.Y;
drawingOrigin = new Vector2(texture.Width / 2, texture.Height / 2);
spriteBatch.Draw(texture, boundingBox, null, Color.White, orientation, drawingOrigin, SpriteEffects.None, 0.0f);
}
public Vector2 PlayerPosition
{
set
{
playerPosition = value;
}
get
{
return playerPosition;
}
}
You get the distance from the player using:
float distanceFromPlayer = Vector2.Distance(position, playerPosition);
But you never actually set the variable playerPosition in your class. So effectively if the enemy is within the chase radius from the point (0,0) they will chase your player, but otherwise will they will just wander around.
I would recommend doing one of two things to solve this issue.
First off you could change the parameters of your Update method to take in the Vector2 of the players position.
A second approach (the one I would personally choose) would be to add a new field (class variable) that is of type Player and then in your Virtual_Aliens' constructor pass in an instance of the player class. That way any time you reference playerPosition you would be able to just say player.Position (or however you have your position field named).
I'm trying to make the ball bounce off of the top and bottom 'Walls' of my UI when creating a 2D Pong Clone.
This is my Game.cs
public void CheckBallPosition()
{
if (ball.Position.Y == 0 || ball.Position.Y >= graphics.PreferredBackBufferHeight)
ball.Move(true);
else
ball.Move(false);
if (ball.Position.X < 0 || ball.Position.X >= graphics.PreferredBackBufferWidth)
ball.Reset();
}
At the moment I'm using this in my Ball.cs
public void Move(bool IsCollidingWithWall)
{
if (IsCollidingWithWall)
{
Vector2 normal = new Vector2(0, 1);
Direction = Vector2.Reflect(Direction,normal);
this.Position += Direction;
Console.WriteLine("WALL COLLISION");
}
else
this.Position += Direction;
}
It works, but I'm using a manually typed Normal and I want to know how to calculate the normal of the top and bottom parts of the screen?
Well, this is how I would handle it
public void CheckBallPositionAndMove()
{
if (ball.Position.Y <= 0 || ball.Position.Y >= graphics.PreferredBackBufferHeight)
ball.HandleWallCollision();
ball.Move();
if (ball.Position.X < 0 || ball.Position.X >= graphics.PreferredBackBufferWidth)
ball.Reset();
}
//In Ball.cs:
private void HandleWallCollision(Vector2 normal)
{
Direction.Y *= -1; //Reflection about either normal is the same as multiplying y-vector by -1
}
private void Move()
{
this.Position += Direction;
}
Note however that using this "discrete" collision detection, you wait until after the ball has moved past the top/bottom of the screen to detect a collision; collisions that occur "between" frames may be noticably off, especially if the ball is moving fast. This is especially a problem if you are using this collision-detection method to detect collision with a paddle, since, if the ball is moving fast enough, it is possible for the ball to move right through the paddle!
The solution to this problem is to use what is known as Continuous Collision Detection. CCD is usually significantly more complex than discrete collision detection; fortunately, pong is simple enough that doing CCD would only be slightly more complex. However, you'd still need a solid grasp of high-school algebra to solve the equations.
If you are still interested, there is a good explaination of CCD in this lecture, and this GameDev article goes a bit more in-depth. There are also many questions relating to it on SO.
You could change boolean IsCollidingWithWall with some enum like:
enum CollideType
{
None,
Vertical,
Horizontal
}
and check this type when creating normal.
Each of the boundaries in your world is a line. One side of the line is solid and the other is not. The normal you are trying to compute is one part of the equation for that line. It points toward the non-solid side of the line. The other part of the line equation is the distance from the line to the origin. The equation for the line can be found from two points on that line. You can define these two points based on the coordinates in your game space where you want a wall.
The normal is computed by rotating the line segment defined by the two points 90 degrees and then Normalizing.
public static Vector2 ComputeNormal(Vector2 point1, Vector2 point2)
{
Vector2 normal = new Vector2();
normal.X = point2.Y - point1.Y;
normal.Y = point1.X - point2.X;
normal.Normalize();
return normal;
}
You are using the preferred back buffer width and height to define your world space so your would use these to define the points used to compute the normals.
float left = 0.0f;
float right = graphics.PreferredBackBufferWidth;
float top = 0.0f;
float bottom = graphics.PreferredBackBufferHeight;
Vector2 topNormal = ComputeNormal(new Vector2(left, top), new Vector2(right, top));
Vector2 bottomNormal = ComputeNormal(new Vector2(right, bottom), new Vector2(left, bottom));
Note that the points must be given in clockwise order so that the normal points in the correct direction.
The following XNA 4.0 program demonstrates these concepts in use:
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
namespace WindowsGame
{
public class Ball
{
const int DIAMETER = 40;
const float RADIUS = DIAMETER * 0.5f;
const float MASS = 0.25f;
const int PIXELS = DIAMETER * DIAMETER;
static readonly uint WHITE = Color.White.PackedValue;
static readonly uint BLACK = new Color(0, 0, 0, 0).PackedValue;
Texture2D m_texture;
Vector2 m_position;
Vector2 m_velocity;
public Ball(GraphicsDevice graphicsDevice)
{
m_texture = new Texture2D(graphicsDevice, DIAMETER, DIAMETER);
uint[] data = new uint[PIXELS];
for (int i = 0; i < DIAMETER; i++)
{
float iPosition = i - RADIUS;
for (int j = 0; j < DIAMETER; j++)
{
data[i * DIAMETER + j] = new Vector2(iPosition, j - RADIUS).Length() <= RADIUS ? WHITE : BLACK;
}
}
m_texture.SetData<uint>(data);
}
public float Radius
{
get
{
return RADIUS;
}
}
public Vector2 Position
{
get
{
return m_position;
}
}
public Vector2 Velocity
{
get
{
return m_velocity;
}
set
{
m_velocity = value;
}
}
public void ApplyImpulse(Vector2 impulse)
{
Vector2 acceleration = impulse / MASS;
m_velocity += acceleration;
}
public void Update(float dt)
{
m_position += m_velocity; // Euler integration - innaccurate and unstable but it will do for this simulation
}
public void Draw(SpriteBatch spriteBatch)
{
spriteBatch.Draw(m_texture, DrawRectangle, Color.White);
}
private Rectangle DrawRectangle
{
get
{
int x = (int)Math.Round(m_position.X - RADIUS);
int y = (int)Math.Round(m_position.Y - RADIUS);
return new Rectangle(x, y, DIAMETER, DIAMETER);
}
}
}
public class Boundary
{
private Vector2 m_point1;
private Vector2 m_point2;
private Vector2 m_normal;
private float m_distance;
public Boundary(Vector2 point1, Vector2 point2)
{
m_point1 = point1;
m_point2 = point2;
m_normal = new Vector2();
m_normal.X = point2.Y - point1.Y;
m_normal.Y = point1.X - point2.X;
m_distance = point2.X * point1.Y - point1.X * point2.Y;
float invLength = 1.0f / m_normal.Length();
m_normal *= invLength;
m_distance *= invLength;
}
public Vector2 Normal
{
get
{
return m_normal;
}
}
public void PerformCollision(Ball ball)
{
float distanceToBallCenter = DistanceToPoint(ball.Position);
if (distanceToBallCenter <= ball.Radius)
{
ResolveCollision(ball);
}
}
public void ResolveCollision(Ball ball)
{
ball.Velocity = Vector2.Reflect(ball.Velocity, m_normal);
}
private float DistanceToPoint(Vector2 point)
{
return
m_normal.X * point.X +
m_normal.Y * point.Y +
m_distance;
}
}
public class World
{
Boundary m_left;
Boundary m_right;
Boundary m_top;
Boundary m_bottom;
public World(float left, float right, float top, float bottom)
{
m_top = new Boundary(new Vector2(right, top), new Vector2(left, top));
m_right = new Boundary(new Vector2(right, bottom), new Vector2(right, top));
m_bottom = new Boundary(new Vector2(left, bottom), new Vector2(right, bottom));
m_left = new Boundary(new Vector2(left, top), new Vector2(left, bottom));
}
public void PerformCollision(Ball ball)
{
m_top.PerformCollision(ball);
m_right.PerformCollision(ball);
m_bottom.PerformCollision(ball);
m_left.PerformCollision(ball);
}
}
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Matrix viewMatrix;
Matrix inverseViewMatrix;
Ball ball;
World world;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
IsMouseVisible = true;
}
protected override void Initialize()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
ball = new Ball(GraphicsDevice);
float right = Window.ClientBounds.Width * 0.5f;
float left = -right;
float bottom = Window.ClientBounds.Height * 0.5f;
float top = -bottom;
world = new World(left, right, top, bottom);
viewMatrix = Matrix.CreateTranslation(Window.ClientBounds.Width * 0.5f, Window.ClientBounds.Height * 0.5f, 0.0f);
inverseViewMatrix = Matrix.Invert(viewMatrix);
base.Initialize();
}
private void ProcessUserInput()
{
MouseState mouseState = Mouse.GetState();
Vector2 mousePositionClient = new Vector2((float)mouseState.X, (float)mouseState.Y);
Vector2 mousePositionWorld = Vector2.Transform(mousePositionClient, inverseViewMatrix);
if (mousePositionWorld != ball.Position)
{
Vector2 impulse = mousePositionWorld - ball.Position;
impulse *= 1.0f / impulse.LengthSquared();
ball.ApplyImpulse(-impulse);
}
}
protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
float dt = (float)gameTime.ElapsedGameTime.TotalSeconds;
ProcessUserInput();
ball.Update(dt);
world.PerformCollision(ball);
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin(SpriteSortMode.Deferred, null, null, null, null, null, viewMatrix);
ball.Draw(spriteBatch);
spriteBatch.End();
base.Draw(gameTime);
}
}
}
Wouldn't you just take the position of the ball minus the position of the wall and then normalize that vector to get what you needed without hardcoding it?
Vector2 normal = Position - WallPosition;
normal.Normalize();
The rest of your code should just work the same.