Sprite Facing Mouse

Started by
8 comments, last by Khatharr 11 years, 5 months ago
Hey all,

So I'm trying to have a sprite face my mouse cursor, but with my current code, the sprite is -close-, but it's not fully rotating to face my cursor. I've pasted my Player.cs class below. Right now, I initialize the direction and rotation variables within my Update(GameTime gameTime) class. I've already set the origin to be in the center of the sprite, so I don't think that's causing the issue. Thanks!

[source lang="csharp"]using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;

namespace MyTerribleGame
{
class Player
{
#region Data Fields
private Texture2D texture;

private Rectangle position;

private int health;
private int armor;
private int lives;
private int velocity;

private Vector2 direction;
private float rotation;
#endregion

#region Properties
public Texture2D Texture
{
get { return texture; }
set { texture = value; }
}

public Rectangle Position
{
get { return position; }
set { position = value; }
}

public int Health
{
get { return health; }
set { health = value; }
}

public int Armor
{
get { return armor; }
set { armor = value; }
}

public int Lives
{
get { return lives; }
set { lives = value; }
}

public int Velocity
{
get { return velocity; }
set { velocity = value; }
}
#endregion

#region Constructors
// Default Constructor. Will be removed when there's more implementation.
public Player()
{
health = 100;
armor = 100;
lives = 100;

velocity = 10;
}

// Overloaded Constructor. Possibly will become the new default.
public Player(Rectangle position, int health, int armor, int lives, int velocity)
{
this.position = position;
this.health = health;
this.armor = armor;
this.lives = lives;
this.velocity = velocity;
}
#endregion

#region Methods
// Load the content of the player class.
public void LoadContent(ContentManager Content)
{
texture = Content.Load<Texture2D>("Player/playerRed");
}

// Update the player, such as input, collision detection, etc.
public void Update(GameTime gameTime)
{
direction.X = Input.MousePos.X - position.X;
direction.Y = Input.MousePos.Y - position.Y;

direction.Normalize();

if (Input.IsKeyDown(Keys.W))
{
position.Y -= velocity;
}

if (Input.IsKeyDown(Keys.S))
{
position.Y += velocity;
}

if (Input.IsKeyDown(Keys.D))
{
position.X += velocity;
}

if (Input.IsKeyDown(Keys.A))
{
position.X -= velocity;
}

rotation = (float)Math.Atan2(direction.Y, direction.X);
}

// Draw the player to the screen.
public void Draw(SpriteBatch spriteBatch)
{
spriteBatch.Draw(texture, position, null, Color.White, rotation, new Vector2(position.Width / 2, position.Height / 2), SpriteEffects.None, 0);
}

// This version of CheckCollision is used to check for collision between the screen and the player.
public void CheckCollision(GraphicsDeviceManager graphics)
{
if (position.X <= 0)
position.X += velocity;

if (this.position.Y <= 0)
position.Y += velocity;

if (this.position.X + this.position.Width >= graphics.PreferredBackBufferWidth)
position.X -= velocity;

if (this.position.Y + this.position.Height >= graphics.PreferredBackBufferHeight)
position.Y -= velocity;
}

// 2D Bounding Box collision b/w the player and an object.
public void CheckCollision(Rectangle obj)
{
// Player -> Object

// Right -> Left
if (position.Right > obj.Left &amp;amp;&amp;amp;
position.Left < obj.Left &amp;amp;&amp;amp;
position.Bottom > obj.Top &amp;amp;&amp;amp;
position.Top < obj.Bottom)
{
position.X -= velocity;
}

// Left -> Right
if (position.Left < obj.Right &amp;amp;&amp;amp;
position.Right > obj.Right &amp;amp;&amp;amp;
position.Bottom > obj.Top &amp;amp;&amp;amp;
position.Top < obj.Bottom)
{
position.X += velocity;
}

// Top -> Bottom
if (position.Top < obj.Bottom &amp;amp;&amp;amp;
position.Bottom > obj.Bottom &amp;amp;&amp;amp;
position.Right > obj.Left &amp;amp;&amp;amp;
position.Left < obj.Right)
{
position.Y += velocity;
}

// Bottom -> Top
if (position.Bottom > obj.Top &amp;amp;&amp;amp;
position.Top < obj.Top &amp;amp;&amp;amp;
position.Right > obj.Left &amp;amp;&amp;amp;
position.Left < obj.Right)
{
position.Y -= velocity;
}
}
#endregion
}
}
[/source]
Advertisement
This is how I do it in most of my games:

float dx = Player.Position.X - MouseLocation.X;
float dy = Player.Position.Y - MouseLocation.Y;

float angle = (float)Math.Atan2(dy, dx);

The only thing you really need to change, is switching your :
direction.X = Input.MousePos.X - position.X;
direction.Y = Input.MousePos.Y - position.Y;
to
direction.X = position.X - Input.MousePos.X;
direction.Y = position.Y - InputMousePos.X;

Hope that helps!
Have you tried to debug it?

This is how I do it in most of my games:
float dx = Player.Position.X - MouseLocation.X;
float dy = Player.Position.Y - MouseLocation.Y;
float angle = (float)Math.Atan2(dy, dx);
The only thing you really need to change, is switching your :
direction.X = Input.MousePos.X - position.X;
direction.Y = Input.MousePos.Y - position.Y;
to
direction.X = position.X - Input.MousePos.X;
direction.Y = position.Y - InputMousePos.X;
Hope that helps!



Thanks for the suggestion! I've switched them around as recommended, but unfortunately, it's not giving me the correct results :(. Anything else come to mind?
Did someone seriously downvote Aliii for asking the most sane question in the circumstance? OP did not specify that debugging had happened, and debugging will reveal the point at which the data is becoming incorrect.

...

There's no reason to normalize the vector. This may be causing slight inaccuracies in the angle since all it's really doing is changing the granularity of the arc-tangent calculation. Also, try calculating the vector after the motion is processed. Is this a consistent problem or does it only occur while you're moving?

I'm curious as to why you separate the atan2 from the vector calculation. It seems confusing to do part of a calculation, then do some different calculation and then finish the first one.
void hurrrrrrrr() {__asm sub [ebp+4],5;}

There are ten kinds of people in this world: those who understand binary and those who don't.

Did someone seriously downvote Aliii for asking the most sane question in the circumstance? OP did not specify that debugging had happened, and debugging will reveal the point at which the data is becoming incorrect.

...

There's no reason to normalize the vector. This may be causing slight inaccuracies in the angle since all it's really doing is changing the granularity of the arc-tangent calculation. Also, try calculating the vector after the motion is processed. Is this a consistent problem or does it only occur while you're moving?

I'm curious as to why you separate the atan2 from the vector calculation. It seems confusing to do part of a calculation, then do some different calculation and then finish the first one.


I've removed the normalization and it's still not working. Essentially, if my mouse cursor is at the top right, the sprite is looking at the top left. When I rotate the sprite using my mouse, it slowly catches up to the cursor, but then slows down again. I can provide more source code if needed? As for debugging - I've tried watching the rotation and direction.x and direction.y, but I'm not sure where they should be at. Finally, I moved the rotation calculation under the direction to avoid ambiguity. I'm still pretty new to this, so this is just for a learning experience.
Is the mouse position translated to the world coordinate system or is it just the cursor position on the screen?
Currently it's the cursor position on the screen. Input.MousePos is a read-only property that returns a new Vector2 of Mouse.GetState().X and Mouse.GetState().Y. Thanks for all the help so far!
If your character is always in the middle of the screen, then determine the cursor distance from the centre of the screen and call atan2 on that.
Vector2 cursor_direction;
cursor_direction.x = mousePos.x - screen_width / 2.0
cursor_direction.y = screen_height / 2.0 - mousePos.y

I guess mousePos is in pixels and is measured from the top left corner.
If it's a problem of being on the wrong side of the x axis then you need to invert your vector's x magnitude. (direction.X = position.X - Input.MousePos.X;)

That's quite different than "-close-, but it's not fully rotating to face".

Of course it will be correct in certain positions because, as they say, even a stopped clock is right twice a day. If your x is inverted from what it needs to be then when its magnitude approaches zero you'll have more 'correct-looking' results, but as it approaches extremes from your rotation point you'll get increasingly bad results.

For future reference, familiarize yourself with the direction of rotation and zero position in your render calls whenever you use a new API. atan2 is going to give you results based on a 4 quadrant graph like the ones from high-school geometry class. If your zero position is straight upward and your direction of rotation is clockwise then you should rotate through the xy quadrants in the order ++, +-, --, -+. Make sure that the coordinates you're feeding to atan2 match signage appropriately for the quadrant your cursor is in.

In other words, for upward zero with clockwise rotation, the vector's x should be negative to the left of the sprite and positive to the right. The y should be positive above and negative below. Order your subtractions to make that happen and you should get the correct result.

Also, please ensure that both your render call and atan2 are using the same unit - either radians or degrees. (I believe they both use radians in this case, but idk for sure.)
void hurrrrrrrr() {__asm sub [ebp+4],5;}

There are ten kinds of people in this world: those who understand binary and those who don't.

This topic is closed to new replies.

Advertisement