This is somewhat more complicated problem than when snake can move in any direction
I have a problem calculating position of tail elements and how they should move and follow
the head.
Any help is appreciated!
This is somewhat more complicated problem than when snake can move in any direction
I have a problem calculating position of tail elements and how they should move and follow
the head.
Any help is appreciated!
How are you moving - are you aiming for a fluent motion or do you want choppy “jumps”, like it was with original snake on old phones/handhelds?
Also - how are you storing the playable area - a 2d array?
These are just to make sure that proposed solutions will fit your needs.
Hi! Thanks for looking into this…I need smooth movement no choppy jumps. I have a problem figuring out how tail follows the head which can only move left/right/up/down but it’s not on a grid (it can move any time you change direction immediately, no need to wait to be in position where movement is allowed - center position in the grid)
If you have any ideas please share it with me!
Assuming each block of your snake is a separate object you game is going to have to have an internal timer based on how long it takes one block to move its full length. Lets say you want a block to move completely left/right/up/down in 0.5 seconds. You will start to move all your blocks, and while this is going on each Update you skip doing anything while the blocks move. (You could store the last user input so when your game ticks it will use that input). Then when the blocks have all moved you process the next direction, or keep going in the same one if user didn’t input anything, and start the process all over again. Now will the blocks are moving they will necessarily overlap each other, but that is just how it has to be (at first) if you want to use large blocks for pieces of the snake. You can get fancy later and figure out ways to turn the blocks and move them diagonally at corners.
So to actually implement the above, I would create SnakeBlock script and attach it to a gameObject. You make this a prefab. The SnakeBlock script will contain lastPosition variable. It should also have a Coroutine to move it to the next destination its told:
public class Snakeblock : Monobehaviour
{
Vector3 lastPosition;
bool isMoving;
public Vector3 LastPosition
{
get { return lastPosition;}
}
public bool IsMoving
{
get { return isMoving;}
}
void Awake()
{
lastPosition = transform.position;
isMoving=false;
}
public void StartMoving(Vector3 newPosition, float speed)
{
lastPosition = transform.position;
StartCoroutine(MoveToPosition(newPosition,speed));
}
IEnumerator MoveToPosition(newPosition,speed)
{
currentTime = 0f;
isMoving = true;
while (currentTime < speed)
{
yield return null;
currentTime+=Time.deltaTime;
transform.position = Mathf.Lerp(lastPosition,newPosition,currentTime/speed);
}
isMoving=false;
}
}
Then you create a Snake Object. It will handle user input, and tell the snakes to move to the next Spot. It will move the head Snake to the next spot based on the current direction. It will move the rest of the snakes based on the last position of the snake in front of them
This code is missing a lot of stuff, handling making new blocks when the snake gets bigger, checking for user input, etc. I just put in the code for how to move the snakes
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class Snake : Monobehaviour
{
// how long it takes the snake to move one block
// probably somewhere between 0.25f and .75f
public float snakeSpeed;
// these two variables hold the current Direction of the Snake
// and the next Direction if the user pressed up/down/left/right
// probaby should be an enum for readability
int currentDirection;
int nextDirection;
List<SnakeBlockt> snakeBlocks = new List<SnakeBlock>();
void Awake()
{
// Code goes here to make X snake blocks from Prefabs
// You need to use GetComponent from Instantiate call to fill up the snakeBlocks list with the script references
currentDirection = 0; // up
}
void Update
{
// Code goes here to get user Input
// and set the next direction
// snakeBlocks[0] is always our head
// if its not moving time to move it
if (!snakeBlocks[0].IsMoving)
{
if (currentDirection != nextDirection)
currentDirection = nextDirection;
// write this function to return the new positon of the snakeHead
// based on current position and direction
Vector3 newHeadPosition = GetPositionFromDirection(snakeBlocks[0].LastPosition,currentDirection);
snakeBlocks[0].StartMoving(newHeadPosition,snakeSpeed);
for (int i=0;i<snakeBlocks.Count-1;i++)
{
Vector3 newPosition = snakeBlocks[i].LastPosition;
snakeBlocks[i+1].StartMoving(newPosition,snakeSpeed);
}
}
}
}
The idea is you only have to figure out where the head of the snake needs to go next. All the rest of them just get told to move where the guy in front of them was. Using a Coroutine and Mathf.Lerp the movement should be pretty smooth.
First thing that comes to mind would be to use a Breadcrumb Pathfinding solution.
The head drops markers with direction (for performance, it could drop it only on direction change)
Each part/block of the snake moves “on its own” when called from the container, in its current direction. Direction changes when a block encounters a new direction marker.
Last part/block has a bool flag that if true = remove the breadcrumb.
So the movement is not particularly complicated and is handled by each block.
I’m not sure though how do you add a block to a block-based snake with fluent movement, this would need a good solution too, depending on how you want to add length.
Another way to move the snake is to not move it at all. Note: The idea below would best be used with an Object Pool. Create a lot of snake blocks ahead of time. Whenever I say instantiate below, grab it from the pool. When i say destroy below, give it back to the pool.
Keep a List<> of all the gameObjects of the current blocks. When the snake needs to move, Instantiate a new block in front of the snake in the correct direction. Destroy the block that is at the tail. Now this would look jumpy, but what you could do is set the X, or Z scale of the block to very tiny (depending on the direction it got added from), and then slowly lerp it to full scale. This would make it appear to grow out of the front of the snake. Do the same for the destroyed tail but in reverse.
Then your snake appears to be moving around the board, but your not actually moving any objects at all… just sticking new ones in front and growing them there.
thanks for ideas guys, it seems that the easiest way is breadcrumb solution. i will have modification of that solution with queue that will hold positions that tail need to follow. so each element will have stack of positions that will be remembered when tail/head changes direction. if queue is empty (nothing to dequeue) it should translate in the direction of previous tail…or something like that, i will work on this and post the solution…
or i should use breadcrumb solution, i will test and use whatever is easier