5
\$\begingroup\$

Noobie here... I've started learning monogame development and currently working on a simple snake game. I'm having trouble with the snake's movement, I figured I could make it tile-based movement but eventually decided to create turning points everytime the head changes direction, insert those into a list and remove a point when the last body unit have passed through it.

The method that gets keyboard input and creates the turning points:

 protected override void Update(GameTime gameTime)
    {
        if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
            Exit();
        player.update();
        if (Keyboard.GetState().IsKeyDown(Keys.Up) || Keyboard.GetState().IsKeyDown(Keys.Down))
        {
            if (Keyboard.GetState().IsKeyDown(Keys.Up))
                player.direction.Y = -3f;
            else
                player.direction.Y = 3f;
            player.direction.X = 0;
            /**
             * Creating a turnPoint that will save the change in movement 
             * and where it occured.
             * **/
            player.turnPoint.Add(new Player.turningPoint(player.position, player.direction));
            return;
        }
        if (Keyboard.GetState().IsKeyDown(Keys.Left) || Keyboard.GetState().IsKeyDown(Keys.Right))
        {
            if (Keyboard.GetState().IsKeyDown(Keys.Left))
                player.direction.X = -3f;
            else
                player.direction.X = 3f;
            player.direction.Y = 0;

            player.turnPoint.Add(new Player.turningPoint(player.position, player.direction));
        }

        base.Update(gameTime);
    }

GameObject which is used as the snake's tail basically consists of 2 Vectors, one for position, the other for movement direction.

 public struct turningPoint // Used to save the point and specific change in movement
    {
        public Vector2 location { get; }
        public Vector2 velocityChange { get; }

        public turningPoint(Vector2 location, Vector2 velocityChange)
        {
            this.location = location;
            this.velocityChange = velocityChange;
        }
    }
    public List<turningPoint> turnPoint = new List<turningPoint>();
    private List<GameObject> bodyUnits = new List<GameObject>(); 

And finally, the method in whic I update the body units:

        private void updateBodyUnits()
    {
        for(int i = 0; i < bodyUnits.Count; i++)
        {
            //Update the unit's position using it's current movement direction
            bodyUnits[i].position.X += bodyUnits[i].direction.X;
            bodyUnits[i].position.Y += bodyUnits[i].direction.Y;

            foreach(turningPoint point in turnPoint)
            {
                if(bodyUnits[i].position.X == point.location.X && bodyUnits[i].position.Y == point.location.Y)
                {   //If the unit is currently on a specific turning point, change it's direction
                    bodyUnits[i].direction.X = point.velocityChange.X;
                    bodyUnits[i].direction.Y = point.velocityChange.Y;

                    if (i == bodyUnits.Count - 1) //Remove turn point if the last 
                                                  //unit passed through
                        turnPoint.Remove(point);
                    break;
                }
            }
        }
    }

Basically it kinda works, if the movement the snake is low. If I set direction. X\Y to 1, or 2 even it kinda works, but above that it seems as if the units just speed through the turning point, ignoring it (and currently there just the head with 3 units of tail). So, is my code ridiculously inefficient? Or the method I am using is wrong altogether?

\$\endgroup\$

2 Answers 2

1
\$\begingroup\$

The problem is using equality with floats.

if(bodyUnits[i].position.X == point.location.X &&
   bodyUnits[i].position.Y == point.location.Y)

While your turning point might be at

x:1.43, y:5.23

Your body unit might be at

x:1.430000001, y:5.23

Those are not equal, therefore, the point will not be triggered. Using your current strategy, you can alter your code to instead look for something that's close enough. Like defining the maximum tolerance like so:

float maxDelta = .01f;

if(Math.Abs(bodyUnits[i].position.X - point.location.X) < maxDelta &&
   Math.Abs(bodyUnits[i].position.Y - point.location.Y) < maxDelta)
\$\endgroup\$
6
  • \$\begingroup\$ That doesn't work unfortunately, there may be a different problem... \$\endgroup\$ Mar 1, 2017 at 21:10
  • 1
    \$\begingroup\$ You'll need to describe it more. What do you mean "doesn't work"? What is it doing? What did you try? \$\endgroup\$
    – House
    Mar 1, 2017 at 21:11
  • 1
    \$\begingroup\$ You have to help people help you. You already told us that in your original post. How does the above solution not solve the problem? What did you try (did you implement this code? Did you try different maxDelta values? Did you do anything else?). Come on man, try a little harder to convey useful information. Look at my questions, answer each one. Try to think of what else you can add to make your situation clearer. \$\endgroup\$
    – House
    Mar 1, 2017 at 21:42
  • \$\begingroup\$ Hmm, yeah, sorry about that, I implemented the code and tried different values of maxDelta, was going to edit my reply but suddenly I realized what could be the problem and just deleted it in order to check first, \$\endgroup\$ Mar 1, 2017 at 22:11
  • \$\begingroup\$ Well the problem is basically that my snake sprite (a basic rectangle) is built by units of rectangle the size of 10x10, so if I update the movement speed in anything that isn't the divisor of 10 it just skips the point because it never actually get there. Sorry for the trouble. \$\endgroup\$ Mar 1, 2017 at 22:16
0
\$\begingroup\$

Why use turning points? It's much easier to use a queue based approach to this instead. Like this:

#include "lib/renderer.h";

void main() {
    int head_x=0;
    int head_y=0;
    int dir_x=1;
    int dir_y=0;
    struct segmentList segments;
    bool running=true;
    while (running == true) {
        head_x+=dir_x;
        head_y+=dir_y;
        segments.push([head_x,head_y]);
        segments.remove(0);
        for (int i=0;i<segments.length;i++) {
          render(segments.get(i));
        }
    }
}
\$\endgroup\$
3
  • \$\begingroup\$ Sorry about the horrible looking pseudo-c it's the closest thing I know to C#. \$\endgroup\$ Aug 4, 2017 at 19:40
  • \$\begingroup\$ This seems like more of a queue-based approach than a stack-based approach. It's first-in-first-out. You add to one end of the queue, as well as one end to the snake, to account for where it moves next. Then you remove from the other end of the queue and the snake to show where it slithers away from. \$\endgroup\$
    – tyjkenn
    Aug 4, 2017 at 21:12
  • \$\begingroup\$ @tyjkenn You are right! I really messed up there! \$\endgroup\$ Aug 5, 2017 at 4:21

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .