Search code examples
xnarotation2dline

Drawing 2D lines in XNA using rotations


I'm trying to draw a line from the player to a targets asteroid as a "Grapple" hook, i've found some examples for doing this in 2D in xna but my "Position" and "origin" vector2 seems to change depending on who and when they are used. In the case of the lines they appear to draw up and right of the position (roughly 100 pixels) in the opposite direction of the target and rotate about an origin somewhere to the left of the target as the player moves.

here is the player code including the grapple code and target assignment

namespace Sparatius.Sprites
{
    public class PlayerSprite : BaseSprite
    {
        ControlInput controlInput;

        Texture2D Grapple;

        SpriteFont font;

        bool LeftGrapple = false, RightGrapple = false;

        int GrappleRange = 300;

        Sprites.AsteroidSprite LeftTarget, RightTarget;

        public BoundingSphere grappleHitBox
        {
            get { return new BoundingSphere(new Vector3(Position.X + Origin.X, Position.Y + Origin.Y, 0), GrappleRange); }
        }

        public float speed
        {
            get { return Speed; }
        }

        public PlayerSprite(Vector2 spriteLocal)
            :base(spriteLocal)
        {
            this.Rotation = 0;
            this.Speed = 0;
            this.FrameCount = new Point(4, 2);
            this.ColorTint = Color.White;
            this.controlInput = new ControlInput();
        }

        public void LoadContent(ContentManager content)
        {
            Texture = content.Load<Texture2D>("Sprites/PinballSpin");
            font = content.Load<SpriteFont>("Fonts/Font1");
            Grapple = content.Load<Texture2D>("Sprites/Grapple");
            FrameSize = new Point((int)Texture.Width / FrameCount.X, (int)Texture.Height / FrameCount.Y);
            Origin = new Vector2(FrameSize.X / 2, FrameSize.Y / 2);

            Animation = new Animation(Texture, FrameSize);
        }

        public override void Update(GameTime gameTime)
        {
            base.Update(gameTime);

            controlInput.GetControlStates();

            if (controlInput.JustPressed(Keys.W))
                Speed += 2;
            else if (controlInput.JustPressed(Keys.S))
                Speed -= 2;

            if (controlInput.IsHeld(Keys.A))
                Rotation -= 0.05f;
            if (controlInput.IsHeld(Keys.D))
                Rotation += 0.05f;

            if (LeftTarget != null)
            {
                LeftTarget.Distance = Vector2.Distance(Position, LeftTarget.Position);
                if (LeftTarget.Distance > GrappleRange)
                {
                    LeftTarget.isTarget = false;
                    LeftTarget = null;
                }
                if (controlInput.IsHeld(Keys.Q))
                {
                    LeftGrapple = true;
                }
                else
                    LeftGrapple = false;
            }
            if (RightTarget != null)
            {
                RightTarget.Distance = Vector2.Distance(Position, RightTarget.Position);
                if (RightTarget.Distance > GrappleRange)
                {
                    RightTarget.isTarget = false;
                    RightTarget = null;
                }
                if (controlInput.IsHeld(Keys.E))
                {
                    RightGrapple = true;
                }
                else
                    RightGrapple = false;
            }
        }

        public override void Draw(SpriteBatch spriteBatch)
        {
            base.Draw(spriteBatch);

            if (LeftGrapple)
            {
                float leftRotation = (float)Math.Atan2(LeftTarget.Position.Y - Position.Y, LeftTarget.Position.X - Position.X);
                //spriteBatch.Draw(Texture, Position, null, ColorTint, leftRotation, Position, 1f, SpriteEffects.None, 0f);
                spriteBatch.Draw(Grapple,
                    new Rectangle((int)Position.X, (int)Position.Y, 2, (int)LeftTarget.Distance),
                    null, Color.Blue, leftRotation, Position, SpriteEffects.None, 0f);
            }
            if (RightGrapple)
            {
                float rightRotation = (float)Math.Atan2(RightTarget.Position.Y - Position.Y, RightTarget.Position.X - Position.X);
                //spriteBatch.Draw(Texture, Position, null, ColorTint, rightRotation, Position, 1f, SpriteEffects.None, 0f);
                spriteBatch.Draw(Grapple,
                    new Rectangle((int)Position.X, (int)Position.Y, 2, (int)RightTarget.Distance),
                    null, Color.Blue, rightRotation, Position, SpriteEffects.None, 0f);
            }

            spriteBatch.DrawString(font, "Player Rotation: " + Rotation, Position, Color.Red);
            spriteBatch.DrawString(font, "Player RoationDegree: " + (int)MathHelper.ToDegrees(Rotation), origin, Color.Blue);
        }

        public void GrappleCheck(AsteroidSprite target)
        {
            float targetTragectory = (float)Math.Atan2(Position.Y - target.Position.Y, Position.X - target.Position.X);

            if ((targetTragectory < (rotation - (float)MathHelper.PiOver4)) && ((targetTragectory > (rotation - (float)MathHelper.Pi + (float)MathHelper.PiOver4))))
            {
                target.Distance = Vector2.Distance(Position, target.Position);
                if (LeftTarget != null)
                {
                    if (LeftTarget.Distance > target.Distance)
                    {
                        LeftTarget.isTarget = false;
                        LeftTarget = target;
                        LeftTarget.isTarget = true;
                    }
                }
                else
                {
                    LeftTarget = target;
                    LeftTarget.isTarget = true;
                }
            }

            if ((targetTragectory > (rotation + (float)MathHelper.PiOver4)) && ((targetTragectory < (rotation + (float)MathHelper.Pi - (float)MathHelper.PiOver4))))
            {
                target.Distance = Vector2.Distance(Position, target.Position);
                if (RightTarget != null)
                {
                    if (RightTarget.Distance > target.Distance)
                    {
                        RightTarget.isTarget = false;
                        RightTarget = target;
                        RightTarget.isTarget = true;
                    }
                }
                else
                {
                    RightTarget = target;
                    RightTarget.isTarget = true;
                }
            }
        }
    }
}

any idea whats going wrong? cheers


Solution

  • public static void DrawLine(SpriteBatch spriteBatch, Vector2 begin, Vector2 end, Color color, int width = 1)
        {
            Rectangle r = new Rectangle((int)begin.X, (int)begin.Y, (int)(end - begin).Length()+width, width);
            Vector2 v = Vector2.Normalize(begin - end);
            float angle = (float)Math.Acos(Vector2.Dot(v, -Vector2.UnitX));
            if (begin.Y > end.Y) angle = MathHelper.TwoPi - angle;
            spriteBatch.Draw(Pixel, r, null, color, angle, Vector2.Zero, SpriteEffects.None, 0);
        }
    

    Pixel is just a 1x1 sprite

    You can also use the this keyword to make a handy extension method.