I'm pretty new to XNA|C# and what I want to do is simply move a sprite between two points and have it move back and forth continuously.
Let's say I want the sprite to move between the y-coordinate 100 and 0.
How would I accomplish this?
Your question has nothing to do with XNA itself. You are asking how to move an object in a straight line and that is something that is usually learnt in first year geometry.
I'll assume you are drawing your texture like this:
SpriteTexture sprite;
Vector2 position;
...
protected override void Draw(GameTime gameTime)
{
graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin();
spriteBatch.Draw(sprite, position, Color.White);
spriteBatch.End();
base.Draw(gameTime);
}
A straight line is an extremely simple path - it is easily defined by two points. Let the points be P1 and P2. The straight line is then defined as the function (1 - t) * P1 + t * P2 where 0 <= t <= 1. To move the sprite, start from t = 0 and increment t in each update cycle. Computing the function with the given t gives you the position of the sprite. When t >= 1 you've reached P2, this means you should start decrementing t back to 0 and so on and so forth. Here's how to use that fact to move the sprite:
SpriteTexture sprite;
Vector2 position;
Vector2 p1 = new Vector2(0, 100),
p2 = new Vector2(0, 0);
double currentTime = 0, timestep = 0.01;
...
protected override void Update(GameTime gameTime)
{
position = currentTime * p1 + (1 - currentTime) * p2;
currentTime += timestep;
if (currentTime >= 1 || currentTime <= 0)
{
timestep *= -1;
}
}
Here is how XNA works, every frame it calls the methods update and draw in your game's main page. We need to keep track of which direction your sprite is moving and its position so lets add to your main game file:
public Vector2 rectanglePosition = new Vector2(0,0);
public bool moveRight = true;
Now what you want to do is every frame update the position, and use it to draw an object. So in the update method you would have something like
if (moveRight)
rectanglePosition.Y += 10;
else
rectanglePosition.Y -= 10;
if(rectanglePosition.Y>100 || rectanglePosition.Y<0)
moveRight = !moveright;
Then in the draw method just draw the sprite based on the position (you can start by just drawing a rectangle), which you can look up how to do easily.
I can further help you if you don't get the code.
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 trying to make a small game for my programming class at college and I am having a problem trying to create a rectangle for collision.
When I try and use the width and height from my texture. I get an error telling me that I can't convert from a float to an int. But the pixel size of the image is not a float value?
Here is the code I have in my game objects class (there is a lot of comments to help direct where things are meant to go):
class ButtonSprite
{
public Texture2D Art;
public Vector2 Position;
public ButtonSprite(Vector2 pos, Texture2D tex)
{
// Copy the texture "tex" into the "Art" class variable
Art = tex;
// Copy the vector "pos" into the "Position" class variable
Position = pos;
}
public void DrawMe(SpriteBatch sb, Color col)
{
// use the spritebatch "sb" to draw the sprite at "Position" using the texture "Art" with the tint from "col"
sb.Draw(Art, Position, col);
}
}
class PlayerSprite
{
public Texture2D Art;
public Vector2 Position;
public Rectangle CollisionRect;
public PlayerSprite(Vector2 pos, Texture2D tex)
{
// Copy the texture "tex" into the "Art" class variable
Art = tex;
// Copy the vector "pos" into the "Position" class variable
Position = pos;
// create a new CollisionRect Rectangle using the X and Y from Position and the Width and Height from Art
CollisionRect = new Rectangle(Position.X, Position.Y, Art.Width, Art.Height);
}
public void UpdateMe(ButtonState leftB, ButtonState rightB, ButtonState downB, ButtonState upB)
{
// if leftB is pressed
if (leftB == ButtonState.Pressed)
{
// subtract 1 from the X that belongs to Position
Position.X -= 1;
}
// endif
// if rightB is pressed
if (rightB == ButtonState.Pressed)
{
// add 1 to the X that belongs to Position
Position.X += 1;
}
// endif
// if downB is pressed
if (downB == ButtonState.Pressed)
{
// add 1 to the Y that belongs to Position
Position.Y += 1;
}
// endif
// if upB is pressed
if (upB == ButtonState.Pressed)
{
// subtract 1 from the Y that belongs to Position
Position.Y -= 1;
}
// endif
// set the X that belongs to CollisionRect to equal the integer version of the X that belongs to Position
// set the Y that belongs to CollisionRect to equal the integer version of the Y that belongs to Position
}
public void DrawMe(SpriteBatch sb)
{
// use the spritebatch "sb" to draw the sprite at "Position" using the texture "Art" with a white tint
sb.Draw(Art, Position, Color.White);
}
}
}
Right off the bat, I would suggest you calculate your collisions using the movement too. This will prevent more glitches (e.g. passing through walls).
A good way to do this is to inflate the target rectangle (temporarily) by the size of the moving rectangle. Then, you perform a line to bounding box intersection. This will give you an intersection point, while being safer against glitches.
Try this line when building your collision rect. It will convert the height & width floats to integers. The + 1 is optional.
CollisionRect = new Rectangle(Position.X, Position.Y, (int)Art.Width + 1, (int)Art.Height + 1);
Hello there wonderful people!
Let's just cut to the chase.
I've made a tile engine that draws my map relative to my camera (The tiles that are drawn are the ones visible in the camera window), and i've made a sprite (my character) that is centered within the camera.
Whenever i move the camera my character follows accordingly, the only problem is when i reach the borders of my map. Programing the way i did has constrained my camera from moving beyond the borders, resulting in limiting my character from moving closer to the borders(since it's always centered within the camera).
How do i free my camera from the restraints of my evil map?
p.s. I've made a picture to illustrate what i want to do.
And here is the guide i followed.
Here's my code where i draw the tiles and place the camera:
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Black);
spriteBatch.Begin();
Vector2 firstSquare = new Vector2(Camera.Location.X / Tile.TileWidth, Camera.Location.Y / Tile.TileHeight);
int firstX = (int)firstSquare.X;
int firstY = (int)firstSquare.Y;
Vector2 squareOffset = new Vector2(Camera.Location.X % Tile.TileWidth, Camera.Location.Y % Tile.TileHeight);
int offsetX = (int)squareOffset.X;
int offsetY = (int)squareOffset.Y;
for (int y = 0; y < squaresDown; y++)
{
for (int x = 0; x < squaresAcross; x++)
{
foreach (int tileID in myMap.Rows[y + firstY].Columns[x + firstX].BaseTiles)
{
spriteBatch.Draw(
Tile.TileSetTexture,
new Rectangle(
(x * Tile.TileWidth) - offsetX, (y * Tile.TileHeight) - offsetY,
Tile.TileWidth, Tile.TileHeight),
Tile.GetSourceRectangle(tileID),
Color.White);
}
}
}
spriteBatch.End();
// TODO: Add your drawing code here
base.Draw(gameTime);
}
And here is the code where i move the camera:
protected override void Update(GameTime gameTime)
{
// Allows the game to exit
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
IsMouseVisible = true;
KeyboardState ks = Keyboard.GetState();
if (ks.IsKeyDown(Keys.A))
{
Camera.Location.X = MathHelper.Clamp(Camera.Location.X - 2, 0, (myMap.MapWidth - squaresAcross) * Tile.TileWidth);
}
if (ks.IsKeyDown(Keys.D))
{
Camera.Location.X = MathHelper.Clamp(Camera.Location.X + 2, 0, (myMap.MapWidth - squaresAcross) * Tile.TileWidth);
}
if (ks.IsKeyDown(Keys.W))
{
Camera.Location.Y = MathHelper.Clamp(Camera.Location.Y - 2, 0, (myMap.MapHeight - squaresDown) * Tile.TileHeight);
}
if (ks.IsKeyDown(Keys.S))
{
Camera.Location.Y = MathHelper.Clamp(Camera.Location.Y + 2, 0, (myMap.MapHeight - squaresDown) * Tile.TileHeight);
}
// TODO: Add your update logic here
base.Update(gameTime);
}
Instead of making your player follow the camera, you need to make your camera follow the player. That way you can set restrictions on the camera, instead of attempting to hack the camera system to make the character do things.
On every update, you would have something like:
Player.Update(gametime);
Camera.Update(Player.Position);
I am using a translation matrix to move the screen but when the player collides with an object the player will jitter as if it wants to be in 2 places at once. It looks like velocity wants to keep going down while the block pushes it up, how would I go by fixing this?
Video: Here
Camera class:
class Camera
{
public Vector2 Position;
Viewport viewPort;
public Vector2 cameraBounds;
public float wasGround;
public Matrix Transform()
{
var translationMatrix = Matrix.CreateTranslation(new Vector3(-Position.X, -Position.Y, 0));
return translationMatrix;
}
public Player thing(Player player)
{
cameraBounds.X = player.Position.X - Game1.offset.X;
if (cameraBounds.X > 0)
Position.X = player.Position.X - Game1.offset.X;
else
Position.X = 0;
//Problem
cameraBounds.Y = player.Position.Y - Game1.offset.Y;
if (cameraBounds.Y > 0)
{
Position.Y = player.Position.Y - Game1.offset.Y;
if (player.goingUp == false && (wasGround != player.ground))
Position.Y = player.ground - Game1.offset.Y;
wasGround = player.ground;
}
else
Position.Y = 0;
return player;
}
public Camera(Viewport viewport)
{
viewPort = viewport;
}
}
I tried to fix the problem by adding in player goingUp and ground if statements but that did not help.
I solved it. It is about sequence of operations. Just move method camera.thing() as shown below:
// TODO: Add your update logic here
HandleInput(Keyboard.GetState());
player.Update(gameTime);
// delete from here
Time += (float)gameTime.ElapsedGameTime.TotalSeconds;
foreach (Block b in Blocks)
{
player = b.BlockCollision(player);
}
// place here
camera.thing(player);
Explanation: You have to set camera position after all collisions have done.
what i can see while you stand still on some object, velocity changes. so try to convert camera position to integer. or make velocity exact 0 if is near 0.
cameraBounds.X = cInt(cameraBounds.X)
cameraBounds.y = cInt(cameraBounds.y)
I must apologies as this seems such a simple question but I have been trying to get it work for ages.
I have a sprite and two Vector2 variables and I want a sprite to move from one vector2 to another.
Any help would be great. Thanks.
this code works for me but it does a small move every click
//CurPos is the sprite current Position and DestPos is the Destination Position
Vector2 StepAnm = (CurPos - DestPos) / 60;
and in the update function
if (currentMouseState.LeftButton ==Microsoft.Xna.Framework.Input.ButtonState.Pressed&&lastMouseState.LeftButton ==Microsoft.Xna.Framework.Input.ButtonState.Released)
{
if ((int)CurPos.X != (int)DestPos.X)
{
CurPos.X -= StepAnm.X;
}
}
Here is an example using the Update method.
The following will need to be set outside this code
Vector2 destPos;
Vector2 stepAnm; //Needs to be set when destPos is updated
float duration; //Could be dependent on distance or not
protected override void Update(GameTime gameTime)
{
float elapsedSeconds = (float)gameTime.ElapsedGameTime.TotalSeconds;
float moveAmount = elapsedSeconds / duration;
Vector2 movement = stepAnm * moveAmount;
//This should fix going too far
if (movement.LengthSquared() > destPos.DistanceSquared(curPos))
{
curPos = destPos;
}
else
{
curPos += stepAnm * moveAmount; //+= or -= depending on how you calculate stepAnm
}
}
You will probably want to change it a little if you are using a constant speed system. I included the more general duration based system.