2
votes

I'm working on a snake game and I'm running into a problem where if I hit my inputs (up, down, left, right) too fast, my snake runs into itself. I've tried to create a timer under Update() to regulate my movements but I find my snake to still be running into itself from time to time.

I use InvokeRepeating() to move my snake every 0.05 seconds and I have 2 methods of inputs: 1) Update to listen for keyboard inputs (up, down, left, right arrows) and 2) a Gamepad UI (up, down, left, right buttons). Any suggestions to how I could regulate my directional changes?

    void Start () {
        Screen.orientation = ScreenOrientation.Portrait;
        isPaused = false;
        InvokeRepeating("TimerInvoke", 0, deltaTimer);
    }

    void TimerInvoke() {
    Movement();
    StartCoroutine(checkVisible());
    if (currentSize == maxSize)
        TailFunction();
    else
        currentSize++;
    }

    void Update () {
         CompDirectionControl();
         PauseCMD();
    }

    void CompDirectionControl() {
    if (direction != Direction.South && Input.GetKeyDown(KeyCode.UpArrow))
        direction = Direction.North;
    if (direction != Direction.West && Input.GetKeyDown(KeyCode.RightArrow))
        direction = Direction.East;
    if (direction != Direction.North && Input.GetKeyDown(KeyCode.DownArrow))
        direction = Direction.South;
    if (direction != Direction.East && Input.GetKeyDown(KeyCode.LeftArrow))
        direction = Direction.West;
    }

    public void  MPadDirectionControl(int numDirection)
    {
    if (direction != Direction.South && numDirection == 0)
        direction = Direction.North;
    if (direction != Direction.West && numDirection == 1)
        direction = Direction.East;
    if (direction != Direction.North && numDirection == 2)
        direction = Direction.South;
    if (direction != Direction.East && numDirection == 3)
        direction = Direction.West;
    }

I tried creating something like this but the delay is very inconsistent

    if (Time.time >= timeStamp)
    {
        CompDirectionControl();
        timeStamp = Time.time + moveInterval;
    }

UPDATE I was able to add a delay to my keyboard inputs by using a coroutine. However, I have not figured out how to do the same with my button UI inputs. If I create a public IEnumerator GamePadDirControl(int direction), how do I pass my button's parameter to my IEnumator? Even though I set my GamePadDirControl to public, I don't see it in my Inspector so I can't link my button to it

IEnumerator DirControl() {
    if (direction != Direction.South && Input.GetKeyDown(KeyCode.UpArrow))
    {
        yield return new WaitForSeconds(deltaTimer);
        direction = Direction.North;
    }
    else if (direction != Direction.West && Input.GetKeyDown(KeyCode.RightArrow))
    {
        yield return new WaitForSeconds(deltaTimer);
        direction = Direction.East;
    }
    else if (direction != Direction.North && Input.GetKeyDown(KeyCode.DownArrow))
    {
        yield return new WaitForSeconds(deltaTimer);
        direction = Direction.South;
    }
    else if (direction != Direction.East && Input.GetKeyDown(KeyCode.LeftArrow))
    {
        yield return new WaitForSeconds(deltaTimer);
        direction = Direction.West;
    }
}

UPDATE 2 For my buttons UI - Since it isn't possible for my button to link up with IEnumator functions, I decided to create individual coroutines for each direction and call on them from my public void MPadDirectionControl(int numDirection) function.

updated code on button ui input

public void MPadDirectionControl(int numDirection)
{
    if (direction != Direction.South && numDirection == 0)
    {
        StartCoroutine(IntervalN());
        //direction = Direction.North;
    }
    else if (direction != Direction.West && numDirection == 1)
    {
        StartCoroutine(IntervalE());
        //direction = Direction.East;
    }
    else if (direction != Direction.North && numDirection == 2)
    {
        StartCoroutine(IntervalS());
        //direction = Direction.South;
    }
    else if (direction != Direction.East && numDirection == 3)
    {
        StartCoroutine(IntervalW());
        //direction = Direction.West;
    }
}

IEnumerator IntervalN() {
    yield return new WaitForSeconds(deltaTimer);
    direction = Direction.North;
}

IEnumerator IntervalE()
{
    yield return new WaitForSeconds(deltaTimer);
    direction = Direction.East;
}

IEnumerator IntervalS()
{
    yield return new WaitForSeconds(deltaTimer);
    direction = Direction.South;
}

IEnumerator IntervalW()
{
    yield return new WaitForSeconds(deltaTimer);
    direction = Direction.West;
}
2

2 Answers

0
votes

Not sure i fully understand your question ...

Your trying to change the direction before a delay ?

If so, I will try:

StartCoroutine(ChangeDirectionTo(Direction.North, 0.05f));
IEnumarator ChangeDirectionTo(Direction direction, float delay) {
   yield return new WaitForSeconds(delay);
   this.direction = direction;
}
0
votes

I think I see the problem. You need to distinguish between the current direction of the snake, and the user's commanded direction. Try something like this:

if (direction != Direction.South && Input.GetKeyDown(KeyCode.UpArrow))
    nextDirection = Direction.North;
if (direction != Direction.West && Input.GetKeyDown(KeyCode.RightArrow))
    nextDirection = Direction.East;
if (direction != Direction.North && Input.GetKeyDown(KeyCode.DownArrow))
    nextDirection = Direction.South;
if (direction != Direction.East && Input.GetKeyDown(KeyCode.LeftArrow))
    nextDirection = Direction.West;
}

Then in the code where you actually create the cube, just before spawning it, use nextDirection to tell you where the player wants to go:

direction = nextDirection;

Thus the player simply doesn't have the chance to change the direction before the cube is spawned.

This should work, but it has the disadvantage that you won't get the nice "input buffering" effect that some snake games have, where you can tap Right, Down while the snake is going north to do a U-turn. If you want to do that, you'll have to implement some kind of smart buffering system that detects if the player puts in a very specific kind of "invalid" input (north/south when direction is north and nextDirection is east) and sets the snake up for a future direction change. Kind of like buffering "special move" inputs for a fighting game.