简体   繁体   中英

XNA Direction based on Rotation

I have an arrow in my XNA Project, which I have successfully made to rotate towards the position of the mouse with this 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 Test
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        Texture2D arrowSprite;
        float rotation;
        Vector2 direction;
        Vector2 position;
        float speed;
        Vector2 mousePos;
        SpriteFont font1;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here
            position = new Vector2(GraphicsDevice.Viewport.Width / 2, GraphicsDevice.Viewport.Height / 5);
            speed = 1000.0f;
            this.IsMouseVisible = true;
            MouseState mouse = Mouse.GetState();
            mousePos.X = mouse.X;
            mousePos.Y = mouse.Y;
            rotation = Angle(position, mousePos);
            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);
            arrowSprite = Content.Load<Texture2D>("Sprites\\Arrow");
            font1 = Content.Load<SpriteFont>("Sprites\\Test");
            // TODO: use this.Content to load your game content here
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        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
            MouseState mouse = Mouse.GetState();
            mousePos.X = mouse.X;
            mousePos.Y = mouse.Y;
            rotation = Angle(position, mousePos);
            direction = new Vector2((float)Math.Cos(rotation), (float)Math.Sin(rotation));
            position += direction * 10.0f * (float)gameTime.ElapsedGameTime.TotalSeconds;
            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            // TODO: Add your drawing code here
            spriteBatch.Begin();
            spriteBatch.Draw(arrowSprite, position, null, Color.White, rotation, new Vector2(arrowSprite.Width / 2, arrowSprite.Height / 2), 1.0f, SpriteEffects.None, 1.0f);
            spriteBatch.DrawString(font1, mousePos.X.ToString()+ " " + mousePos.Y.ToString(), new Vector2(GraphicsDevice.Viewport.Width / 2, 0), Color.White);
            //spriteBatch.DrawString(font1, "Rotation:" + rotation.ToString(), Vector2.Zero, Color.White);
            spriteBatch.DrawString(font1, "Direction" + direction.ToString(), Vector2.Zero, Color.White);
            spriteBatch.End();
            base.Draw(gameTime);
        }

        public static float Angle(Vector2 from, Vector2 to)
        {
            return (float)Math.Atan2(from.X - to.X, to.Y - from.Y);
        }
    }
}

However, when I run the code, the arrow simply rotates AROUND the mouse, and the radius at which it does so expands slightly over time. I wish for the arrow to rotate TO the mouse and then it to move towards the area it's rotated to. Thanks, any help is appreciated.

Your Angle() calculation implies that the arrow sprite points upwards in its original state. But then, your calculation of direction is wrong. Calculating the direction and angles can be done as follows:

private void CalculateAngleAndDirection(Vector2 from, Vector2 to, out Vector2 direction, out float angle)
{
    direction = to - from; //get the direction vector
    direction.Normalize(); //make it a unit vector
    angle = (float)Math.Atan2(-direction.X, direction.Y);
}

protected override void Update(GameTime gameTime)
{
    //...
    MouseState mouse = Mouse.GetState();
    mousePos.X = mouse.X;
    mousePos.Y = mouse.Y;
    CalculateAngleAndDirection(position, mousePos, out direction, out angle); 
    position += direction * 10.0f * (float)gameTime.ElapsedGameTime.TotalSeconds;
    base.Update(gameTime);
}

I noticed you said

and the radius at which it does so expands slightly over time

and spotted this

position += direction * 10.0f * (float)gameTime.ElapsedGameTime.TotalSeconds;

In xna update is called 60 times a second in theory and gameTime.ElapsedGameTime.TotalSeconds constantly increases each second. If you are adding the value of this equation to the variable position it will gradually change 'orbit' due to this increment. Also, from what I can gather the way you are working out rotations can return a negative (Supplying backwards 'orbit') which will return unusual numbers when multiplied by a positive float.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM