HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

Snake game in Unity

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
gameunitysnake

Problem

I've made a simple snake game in Unity and it has 3 different types of foods.

Note - 5 blocks = 1 point

This is a screenshot from the game here I'm colliding with myself and the game is restarted after that. As you can see the snake has a highlighted head (the light green block) and it consists of quite a lot smaller blocks and there are those outlines on the body of the snake they are created on each 10 blocks you get;

The snake is controlled by the SnakeHead script

```
public class SnakeHead : MonoBehaviour
{
public bool IsMoving { get; set; }

private const float Speed = .2f;
public ObjectPooler snakePartsObjectPooler;
public Text ScoreText;

private Vector3 moveSize;
private Vector3 moveDirection;

private float invulnerabilityTime;

private readonly List snake = new List();

private void Start()
{
moveSize = GetComponent().bounds.size;
snake.Add(gameObject);
StartCoroutine(Move());
}

private void Update()
{
if (Input.anyKeyDown)
{
SetDirection(Vector3.left, Vector3.right, KeyCode.LeftArrow, KeyCode.A);
SetDirection(Vector3.right, Vector3.left, KeyCode.RightArrow, KeyCode.D);
SetDirection(Vector3.up, Vector3.down, KeyCode.UpArrow, KeyCode.W);
SetDirection(Vector3.down, Vector3.up, KeyCode.DownArrow, KeyCode.S);
}
}

private void SetDirection(Vector3 direction, Vector3 oppositeDirection, params KeyCode[] triggers)
{
if (triggers.Any(Input.GetKeyDown))
{
if (snake.Count == 1 || moveDirection != oppositeDirection)
{
moveDirection = direction;
IsMoving = true;
}
}
}

private IEnumerator Move()
{
do
{
for (int i = snake.Count - 1; i > 0; i--)
{
snake[i].transform.position = snake[i - 1].transform.position;
}
transform.posi

Solution

private void Update()
{
    if (Input.anyKeyDown)
    {
        SetDirection(Vector3.left, Vector3.right, KeyCode.LeftArrow, KeyCode.A);
        SetDirection(Vector3.right, Vector3.left, KeyCode.RightArrow, KeyCode.D);
        SetDirection(Vector3.up, Vector3.down, KeyCode.UpArrow, KeyCode.W);
        SetDirection(Vector3.down, Vector3.up, KeyCode.DownArrow, KeyCode.S);
    }
}

private void SetDirection(Vector3 direction, Vector3 oppositeDirection, params KeyCode[] triggers)
{
    if (triggers.Any(Input.GetKeyDown))
    {
         if (snake.Count == 1 || moveDirection != oppositeDirection)
         {
             moveDirection = direction;
             IsMoving = true;
         }
    }
}


Not sure if I'm missing something, but I don't see the point of calling SetDirection and iterating through an array four times to set the direction - you only expect one of these directions to be set, right?

Also it might be more efficient to do this check if (snake.Count == 1 || moveDirection != oppositeDirection) before if (triggers.Any(Input.GetKeyDown)) - technically they could all be in the same if statement, but better to shortcut with a couple of simple comparisons before going through an iterator. (Unless Vector3 has overloaded the operators to do some complex logic...)

Overall I think it would be more efficient to create a dictionary with the various keys of interest, look up Input.GetKeyDown (it returns a KeyCode, right?) in the dictionary, and if found use the corresponding value. Although since you only want to move in the direction if it's not the "opposite" direction, you could either have another dictionary for storing opposites or create a class, e.g.

class Direction
{
    public Vector3 Desired { get; }
    public Vector3 Opposite { get; }
}


Then setup a dictionary, e.g.

private readonly Dictionary keycodeToDirection = new Dictionary()
{
    { KeyCode.LeftArrow, new Direction() { Desired = Vector3.left, Opposite = Vector3.right } },
    { KeyCode.A, new Direction() { Desired = Vector3.left, Opposite = Vector3.right } }
    // all the rest
    // edit: Just realised this wouldn't compile due to not being able to set the property values here, but that's not the point of the example... you get the idea.
};


Then:

private void Update()
{
    if (Input.anyKeyDown)
    {
        var direction = GetDirectionForKeyCode(Input.GetKeyDown);
        if (direction != null)
            SetDirection(direction);
    }
}

private Direction GetDirectionForKeyCode(KeyCode keyCode)
{
    Direction d;
    if (keycodeToDirection.TryGetValue(keyCode, out d)
    {
        if (snake.Count == 1 || moveDirection != d.Opposite)
            return d;
    }
    return null;
}

private void SetDirection(Direction direction)
{
    moveDirection = direction.Desired;
    IsMoving = true;
}


private void OnTriggerEnter2D(Collider2D other)
{
    if (other.tag == "Wall")
    {
        Loss();
    }
    if (other.tag == "SnakePart" && invulnerabilityTime <= Time.time)
    {
        Loss();
    }
}


Could be written with a single if statement:

private void OnTriggerEnter2D(Collider2D other)
{
    if (other.tag == "Wall" || (other.tag == "SnakePart" && invulnerabilityTime <= Time.time))
    {
        Loss();
    }
}

Code Snippets

private void Update()
{
    if (Input.anyKeyDown)
    {
        SetDirection(Vector3.left, Vector3.right, KeyCode.LeftArrow, KeyCode.A);
        SetDirection(Vector3.right, Vector3.left, KeyCode.RightArrow, KeyCode.D);
        SetDirection(Vector3.up, Vector3.down, KeyCode.UpArrow, KeyCode.W);
        SetDirection(Vector3.down, Vector3.up, KeyCode.DownArrow, KeyCode.S);
    }
}

private void SetDirection(Vector3 direction, Vector3 oppositeDirection, params KeyCode[] triggers)
{
    if (triggers.Any(Input.GetKeyDown))
    {
         if (snake.Count == 1 || moveDirection != oppositeDirection)
         {
             moveDirection = direction;
             IsMoving = true;
         }
    }
}
class Direction
{
    public Vector3 Desired { get; }
    public Vector3 Opposite { get; }
}
private readonly Dictionary<KeyCode, Direction> keycodeToDirection = new Dictionary<KeyCode, Direction>()
{
    { KeyCode.LeftArrow, new Direction() { Desired = Vector3.left, Opposite = Vector3.right } },
    { KeyCode.A, new Direction() { Desired = Vector3.left, Opposite = Vector3.right } }
    // all the rest
    // edit: Just realised this wouldn't compile due to not being able to set the property values here, but that's not the point of the example... you get the idea.
};
private void Update()
{
    if (Input.anyKeyDown)
    {
        var direction = GetDirectionForKeyCode(Input.GetKeyDown);
        if (direction != null)
            SetDirection(direction);
    }
}

private Direction GetDirectionForKeyCode(KeyCode keyCode)
{
    Direction d;
    if (keycodeToDirection.TryGetValue(keyCode, out d)
    {
        if (snake.Count == 1 || moveDirection != d.Opposite)
            return d;
    }
    return null;
}

private void SetDirection(Direction direction)
{
    moveDirection = direction.Desired;
    IsMoving = true;
}
private void OnTriggerEnter2D(Collider2D other)
{
    if (other.tag == "Wall")
    {
        Loss();
    }
    if (other.tag == "SnakePart" && invulnerabilityTime <= Time.time)
    {
        Loss();
    }
}

Context

StackExchange Code Review Q#150862, answer score: 4

Revisions (0)

No revisions yet.