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);
Related
I'm making a game in which I draw sprites from a sprite sheet. My Sprite class has an integer property called spriteDirection, and when this is equal to +1 the sprite faces right, and when it is equal to -1 the sprite is flipped to face the left. I have been using this Draw method successfully:
public void Draw(SpriteBatch spriteBatch)
{
if (draw)
{
int drawX = (int)(spritePosition.X - spriteOffset.X);
int drawY = (int)(spritePosition.Y - spriteOffset.Y);
if (texture != null)
{
spriteBatch.Draw(texture, new Rectangle(drawX, drawY, frameWidth, frameHeight), rSpriteSourceRectangle, Color.White);
}
}
}
The rSpriteSourceRectangle is calculated like this:
private Rectangle rSpriteSourceRectangle
{
get
{
if (spriteDirection == -1)
{
return new Rectangle(frameOffsetX + (frameWidth * (currentFrame + 1)), frameOffsetY, -frameWidth, frameHeight);
}
else
{
return new Rectangle(frameOffsetX + (frameWidth * currentFrame), frameOffsetY, frameWidth, frameHeight);
}
}
}
This all works absolutely fine. However I want to be able to switch to using this new Draw method:
public void Draw(SpriteBatch spriteBatch)
{
if (draw)
{
Vector2 positionDraw = spritePosition - spriteOffset;
if (texture != null)
{
spriteBatch.Draw(texture, positionDraw, rSpriteSourceRectangle, Color.White);
}
}
}
This works, provided my spriteDirection property is equal to 1 (so the sprite faces right). If I make it equal to -1, the sprite disappears from the screen. I can't work out why this is happening. I'm sure it's something simple, so hopefully some nice person can point me in the right direction!
Thanks!
I have found a way around the problem by using a different Draw overload:
Draw(Texture2D texture, Vector2 position, Rectangle? sourceRectangle, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effects, float layerDepth);
I am trying to instantiate a number of planes to use as my terrain and give them OnMouseOver() to change them to some color and OnMouseExit() to change them back to the original color.
I attached a plane instantiation script to the main camera to generate the plane prefab and a mouse event script to the plane prefab. I get them all instantiated and the events pass to them, but in-game the mouse events are being applied to either long strips of planes, an entire quadrant, or a random single plane not at the location of the mouse cursor.
I made a new material and applied it to the place prior to turning it into a prefab to replace the standard material set upon creation of the plane.
I have started attempting to use the mouse position to apply the color change to the plane at the current mouse position with Physics.CheckSphere, but I don't fully understand how to tell specifically what gameObject is at a specific position.
public class TerrainGeneration : MonoBehaviour {
[SerializeField]
private Transform groundTile;
private Vector3 row;
private int max = 10;
// Use this for initialization
void Start () {
for ( int i = 0; i <= max; i++)
{
for (int x = 0; x <= max; x++) {
row = new Vector3(i, 0, x);
Instantiate(groundTile, row, Quaternion.identity);
}
}
}
}
public class MouseEvents : MonoBehaviour {
private Color isTargeted;
private Color notTargeted;
private MeshRenderer groundTileMeshRenderer;
private Vector3 mousePosition;
private float mouseX;
private float mouseY;
void Start () {
groundTileMeshRenderer = gameObject.GetComponent<MeshRenderer>();
isTargeted = Color.cyan;
notTargeted = groundTileMeshRenderer.material.color;
}
void Update()
{
mouseX = Mathf.RoundToInt(Input.GetAxis("Mouse X"));
mouseY = Mathf.RoundToInt(Input.GetAxis("Mouse Y"));
mousePosition = new Vector3(mouseX, 0, mouseY);
if (Physics.CheckSphere(mousePosition, 1))
{
**//Get the specific gameObject located at the current mouse position
//Set the gameObject as the target for the color change**
}
}
void OnMouseOver()
{
groundTileMeshRenderer.material.color = isTargeted;
}
void OnMouseExit()
{
groundTileMeshRenderer.material.color = notTargeted;
}
}
So, I found out what the answer is thanks to the Unity forums. My error was in the instantiation transform script. Because I was using the plane primitive 3d object I was not accounting for the size of the plane correctly. The following code snippet for instantiation of each row removes the overlapping that was occurring as the planes are too large to simply place at (0,0,0), (0,0,1), (0,0,2) for example.
row = new Vector3(i * 10, 0, x * 10);
Instantiate(groundTile, row, Quaternion.identity);
tag your gameObjects as necessary and then you can identify them.
If you do, then you can use them like so (purely for example):
public virtual void OnTriggerEnter(Collider other)
{
if (other.gameObject.tag == "planeA")
{
//do something
}
}
I'm new to this so not completely sure whether I am posting this correctly, but I have been having a few issues when creating my game. My main goal is to create a topdown shooter styled game, using movement where the 'player' rotates based on the current position of the mouse and can press 'w' to move towards it at a set speed.
The main issue is, when the game loads, the movement works exactly how I want it to, but the texture itself is not moving, but only the drawRectangle.
Game1.cs:
player = new Player(Content, #"graphics\Player1", 500, 500, spritePosition);
spritePosition = new Vector2(player.CollisionRectangle.X, player.CollisionRectangle.Y);
protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
Exit();
KeyboardState keyboard = Keyboard.GetState();
MouseState mouse = Mouse.GetState();
IsMouseVisible = true;
distance.X = mouse.X - spritePosition.X;
distance.Y = mouse.Y - spritePosition.Y;
//Works out the rotation depending on how far away mouse is from sprite
rotation = (float)Math.Atan2(distance.Y, distance.X);
// TODO: Add your update logic here
spritePosition = spriteVelocity + spritePosition;
spriteOrigin = new Vector2(player.DrawRectangle.X / 2, player.DrawRectangle.Y / 2);
if (Keyboard.GetState().IsKeyDown(Keys.W))
{
spriteVelocity.X = (float)Math.Cos(rotation) * tangentialVelocity;
spriteVelocity.Y = (float)Math.Sin(rotation) * tangentialVelocity;
}
else if(spriteVelocity != Vector2.Zero)
{
Vector2 i = spriteVelocity;
spriteVelocity = i -= friction * i;
}
This is the main movement code from the Update function as well as where the new player has been created.
Player.cs:
class Player
{
Texture2D sprite;
Rectangle drawRectangle;
int health = 100;
public Player(ContentManager contentManager, string spriteName, int x , int y, Vector2 velocity)
{
LoadContent(contentManager, spriteName, x, y, velocity);
}
public Rectangle CollisionRectangle
{
get { return drawRectangle; }
}
public Rectangle DrawRectangle
{
get { return drawRectangle; }
set { drawRectangle = value; }
}
public int Health
{
get { return health; }
set {
health = value;
if (health <= 0)
health = 0;
if (health > 100)
health = 100;
}
}
public Vector2 Velocity
{
get { return Velocity; }
set { Velocity = value; }
}
public void Update(GameTime gameTime, KeyboardState keyboard, MouseState mouse)
{
}
public void Draw(SpriteBatch spriteBatch)
{
spriteBatch.Draw(sprite, drawRectangle, Color.White);
}
private void LoadContent(ContentManager contentManager, string spriteName, int x, int y, Vector2 velocity)
{
sprite = contentManager.Load<Texture2D>(spriteName);
drawRectangle = new Rectangle(x - sprite.Width / 2, y - sprite.Height / 2, sprite.Width, sprite.Height);
}
}
I didn't know what to include in the Update function of the player.cs, whether the code for movement should go in there or the main Game1.cs.
Hopefully this is enough code for you guys to be able to help. Sorry for there being quite a lot of code, but I'm just unsure where the error is occurring.
Thanks in advance
For the rotation you would want to use 'another' kind of spriteBatch.Draw, because the draw function can have more parameters than your currently using.
The full draw function is as followed:
SpriteBatch.Draw(Texture2D texture, Vector2 position, Rectangle sourceRectangle, Color color, float rotation, Vector2 origin, float scale, SpriteEffects effects, float layerdepht)
As you can see, now we have a parameter for rotation that can be used.
So your code will look something like this:
spriteBatch.Draw(sprite, drawRectangle, null, Color.White, rotation, Vector2.Zero, 1, SpriteEffects.None, 0);
As for your question for where your code should go. It is good to learn yourself to put the code where it belongs, in this case the player. Because when your programs become bigger, it would be very messy to do it this way.
To call the update function in your player class, you can just simply call player.Update(gameTime, keyboard, mouse) in the Update of Game1.
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.
This is my code for the movement of my sprite for my game. I looked at tutorials and lessons on line to help me with this. I need to know how to make the character sprite change every time a certain key is pressed. (eg. left key: left facing sprite, right key: right facing sprite) I have looked on line but I;m not sure how to integrate it into my code. I believe I can do something with the enumerated list in my "MageChar" class to change the sprite but i'm not sure how to begin. Any help is appreciated.
Game Class
namespace RPG
{
public class MageChar : charMovement
{
//variable where sprite file name is stored
const string MageAssetName = "MageChar";
//starting x position
const int StartPositionX = 0;
//starting y position
const int StartPositionY = 0;
//speed that the sprite will move on screen
const int MageSpeed = 160;
//move sprite 1 up/down when the arrow key is pressed
const int MoveUp = 1;
const int MoveDown = -1;
const int MoveLeft = -1;
const int MoveRight = 1;
//used to store the current state of the sprite
enum State
{
Walking
}
//set to current state of the sprite. initally set to walking
State CurrentState = State.Walking;
//stores direction of sprite
Vector2 Direction = Vector2.Zero;
//stores speed of sprite
Vector2 Speed = Vector2.Zero;
//stores previous state of keyboard
KeyboardState PreviousKeyboardState;
public void LoadContent(ContentManager theContentManager)
{
//sets position to the top left corner of the screen
Position = new Vector2(StartPositionX, StartPositionY);
base.LoadContent(theContentManager, MageAssetName);
}
//checks state of the keyboard
private void UpdateMovement(KeyboardState aCurrentKeyboardState)
{
//run if the sprite is walking
if (CurrentState == State.Walking)
{
//sets direction and speed to zero
Speed = Vector2.Zero;
Direction = Vector2.Zero;
//if left key is pressed, move left
if (aCurrentKeyboardState.IsKeyDown(Keys.Left) == true)
{
//speed of sprite movement
Speed.X = MageSpeed;
//moves the sprite left
Direction.X = MoveLeft;
}
//if right key is pressed, move right
else if (aCurrentKeyboardState.IsKeyDown(Keys.Right) == true)
{
//speed of sprite movement
Speed.X = MageSpeed;
//moves the sprite right
Direction.X = MoveRight;
}
//if up key is pressed, move up
if (aCurrentKeyboardState.IsKeyDown(Keys.Up) == true)
{
//speed of sprite movement
Speed.Y = MageSpeed;
//moves sprite up
Direction.Y = MoveUp;
}
//if down key is pressed, move down
else if (aCurrentKeyboardState.IsKeyDown(Keys.Down) == true)
{
//speed of sprite movement
Speed.Y = MageSpeed;
//moves sprite down
Direction.Y = MoveDown;
}
}
}
public void Update(GameTime theGameTime)
{
//obtains current state of the keyboard
KeyboardState aCurrentKeyboardState = Keyboard.GetState();
//calls in UpdateMovement and passes in current keyboard state
UpdateMovement(aCurrentKeyboardState);
//set previous state to current state
PreviousKeyboardState = aCurrentKeyboardState;
//call update method of the charMovement class
base.Update(theGameTime, Speed, Direction);
}
}
}
CharMovement
namespace RPG
{
public class charMovement
{
//The asset name for the Sprite's Texture
public string charSprite;
//The Size of the Sprite (with scale applied)
public Rectangle Size;
//The amount to increase/decrease the size of the original sprite.
private float mScale = 1.0f;
//The current position of the Sprite
public Vector2 Position = new Vector2(0,0);
//The texture object used when drawing the sprite
private Texture2D charTexture;
//Load the texture for the sprite using the Content Pipeline
public void LoadContent(ContentManager theContentManager, string theCharSprite)
{
//loads the image of the sprite
charTexture = theContentManager.Load<Texture2D>("charSprite");
charSprite = theCharSprite;
//creates a new rectangle the size of the sprite
Size = new Rectangle(0, 0, (int)(charTexture.Width * mScale), (int)(charTexture.Height * mScale));
}
//Update the Sprite and change it's position based on the set speed, direction and elapsed time.
public void Update(GameTime theGameTime, Vector2 theSpeed, Vector2 theDirection)
{
Position += theDirection * theSpeed * (float)theGameTime.ElapsedGameTime.TotalSeconds;
}
//Draw the sprite to the screen
public void Draw(SpriteBatch theSpriteBatch)
{
//draw the sprite to the screen inside a rectangle
theSpriteBatch.Draw(charTexture, Position,
new Rectangle(0, 0, charTexture.Width, charTexture.Height),
Color.White, 0.0f, Vector2.Zero, mScale, SpriteEffects.None, 0);
}
}
}
MageChar
namespace RPG
{
public class MageChar : charMovement
{
//variable where sprite file name is stored
const string MageAssetName = "MageChar";
//starting x position
const int StartPositionX = 0;
//starting y position
const int StartPositionY = 0;
//speed that the sprite will move on screen
const int MageSpeed = 160;
//move sprite 1 up/down when the arrow key is pressed
const int MoveUp = 1;
const int MoveDown = -1;
const int MoveLeft = -1;
const int MoveRight = 1;
//used to store the current state of the sprite
enum State
{
Walking
}
//set to current state of the sprite. initally set to walking
State CurrentState = State.Walking;
//stores direction of sprite
Vector2 Direction = Vector2.Zero;
//stores speed of sprite
Vector2 Speed = Vector2.Zero;
//stores previous state of keyboard
KeyboardState PreviousKeyboardState;
public void LoadContent(ContentManager theContentManager)
{
//sets position to the top left corner of the screen
Position = new Vector2(StartPositionX, StartPositionY);
base.LoadContent(theContentManager, MageAssetName);
}
//checks state of the keyboard
private void UpdateMovement(KeyboardState aCurrentKeyboardState)
{
//run if the sprite is walking
if (CurrentState == State.Walking)
{
//sets direction and speed to zero
Speed = Vector2.Zero;
Direction = Vector2.Zero;
//if left key is pressed, move left
if (aCurrentKeyboardState.IsKeyDown(Keys.Left) == true)
{
//speed of sprite movement
Speed.X = MageSpeed;
//moves the sprite left
Direction.X = MoveLeft;
}
//if right key is pressed, move right
else if (aCurrentKeyboardState.IsKeyDown(Keys.Right) == true)
{
//speed of sprite movement
Speed.X = MageSpeed;
//moves the sprite right
Direction.X = MoveRight;
}
//if up key is pressed, move up
if (aCurrentKeyboardState.IsKeyDown(Keys.Up) == true)
{
//speed of sprite movement
Speed.Y = MageSpeed;
//moves sprite up
Direction.Y = MoveUp;
}
//if down key is pressed, move down
else if (aCurrentKeyboardState.IsKeyDown(Keys.Down) == true)
{
//speed of sprite movement
Speed.Y = MageSpeed;
//moves sprite down
Direction.Y = MoveDown;
}
}
}
public void Update(GameTime theGameTime)
{
//obtains current state of the keyboard
KeyboardState aCurrentKeyboardState = Keyboard.GetState();
//calls in UpdateMovement and passes in current keyboard state
UpdateMovement(aCurrentKeyboardState);
//set previous state to current state
PreviousKeyboardState = aCurrentKeyboardState;
//call update method of the charMovement class
base.Update(theGameTime, Speed, Direction);
}
}
}
First you need to create sprite texture: put all images of your character in one texture (like this: https://encrypted-tbn1.gstatic.com/images?q=tbn:ANd9GcTl7XCFrVrnkzcNtpokcpJf3SKxcALw-IpbuSgVP4xNRBgtsnN4). Lets say that for now your character have two different states - facing left and facing right and both textures is 64x64. So we have something like this:
-----------------
| | |
| <-- | --> |
| | |
-----------------
Total texture size: 128x64
You need to add more values to state enum. Lets say your state enum will have two values Left and Right instead of just Walking.
Now you can switch frames in your draw method using third parameter:
if (CurrentState == State.Left)
theSpriteBatch.Draw(charTexture, Position,
new Rectangle(0, 0, 64, 64), // first frame
Color.White, 0.0f, Vector2.Zero, mScale, SpriteEffects.None, 0);
else if (CurrentState == State.Right)
theSpriteBatch.Draw(charTexture, Position,
new Rectangle(64, 0, 64, 64), // second frame
Color.White, 0.0f, Vector2.Zero, mScale, SpriteEffects.None, 0);
You also can use direction or anything you want instead of state. In the future you would want to have different textures for whole walking animation and other actions for single character.
More about sprite animations: http://thundernoodle.net/notblog/2011/10/17/how-to-animate-a-sprite-using-a-sprite-sheet-in-xna/
You can also switch textures, as suggested, for different states. To do so just load two different textures, for example charTexture_Left and charTexture_Right and use them in draw method depending on CurrentState.
To make CurrentState change along with a user arrow input add CurrentState to UpdateMovement method:
if (aCurrentKeyboardState.IsKeyDown(Keys.Left) == true)
{
//speed of sprite movement
Speed.X = MageSpeed;
//moves the sprite left
Direction.X = MoveLeft;
CurrentState = State.Left; // set current state to left
}
else if (aCurrentKeyboardState.IsKeyDown(Keys.Right) == true)
{
//speed of sprite movement
Speed.X = MageSpeed;
//moves the sprite right
Direction.X = MoveRight;
CurrentState = State.Right; // set current state to right
}