Unity elementary tutorial Snake implementation (Snake)

Posted by rahulephp on Tue, 01 Feb 2022 20:31:18 +0100

Play games after learning Unity

It is hereby declared that this material is made by sikicollege Obtained from, without commercial use, only for learning and use.
This time, the default full screen is 1920 * 1080; Moreover, the connection between the head and body of the snake is not handled properly. At the beginning, there are two snake bodies by default and there is no collision body. Therefore, in the beginning, you will not die unless you hit the wall... You will die after eating a food; You can hit yourself with a fast button... Not if it's slow. I'll solve these bugs slowly. This time, I'll do it first. Those with strong hands-on ability can also be solved. Then don't talk more nonsense and start the tutorial.
First, there are two scenes, a start interface and a game scene. The start interface has a start game Button. Two Toggle groups store the mode and skin respectively. Two Text groups store the highest score and maximum length. Just throw other backgrounds (I'm destined not to be a UI). The skin was first displayed with Sprite Render. Suddenly, I found that setting Pivot didn't work, So first set the Order in Layer to show... But then find out why it's useless to set Pivot. Just set the sorting mode as perspective in edit = > project settings = > graphics = > camera settings = > if it's customized, x, y and z can't be set. It feels like this is the only way, Oh, by the way, to set the radio selection of Toggle, you need to set an empty parent object on two Toggle, and then add a component called Toggle Group, and then set the groups of two Toggle as the parent object to realize the radio selection. To start the game, the Button needs to be bound with an event. It can be bound manually or automatically, and it can be bound with a function; Automatically, add a delegate to the onclick event, and you can do it directly with a lambda expression, like this: Button onClick. AddListener(() => SceneManager. LoadScene(1)); The storage of other player options and score control will be discussed later.

Then the game scene starts the long process of counting grids... What I decided here is that food and snakes occupy two grids, but they can move one grid by one grid, so first set the snake head to 2 * 2 grid, start to find the boundary of four corners, record it, and then move two grids to find the length of two steps. At first, the boundary I set was one grid away from the real boundary, and the step size was two grids. Later, I found that the endless mode could not be well transmitted to the other side, so I changed it one grid at a time, and the boundary was changed to what it is now. At the beginning, I looked for the step length for most of the day... I found the least common multiple, and calculated the number of cells one by one. As a result, I found that I took a detour and didn't need to look at it at all... I finished it in one time according to the method I said above.
After recording, you can write your own moving method to test. First write a global variable moveVec2 to store the direction after each key press, time it by timer, and then the moving length is one grid step after the timing is over.

if (h != 0 && moveVec2.x != -h)//If the horizontal axis is offset and the moving direction is different from the offset direction, it is forbidden to turn 180 ° and start to stop as soon as the logic is involved o_o IQ is obviously not online
{
    moveVec2.x = h;//Change the moving direction
    moveVec2.y = 0;
    if (h > 0)//Set the rotation angle to turn the head
        transform.eulerAngles = new Vector3(0, 0, -90);
    else
        transform.eulerAngles = new Vector3(0, 0, 90);
}
else if (v != 0 && moveVec2.y != -v)//The same as above, no comments
{
    moveVec2.y = v;
    moveVec2.x = 0;
    if (v > 0)
        transform.eulerAngles = new Vector3(0, 0, 0);
    else
        transform.eulerAngles = new Vector3(0, 0, 180);
}

The following is the more important logic - moving the snake body, it's really difficult to think of it at once if you can't write in your mind. After thinking about it for a long time, you draw a picture, and your thinking is clear. This picture I drew

Cough, explain. First of all, the snake head is i-1, that is, No. 0, followed by the snake body. Snake head movement has nothing to do with the body, so the connection between snake head and body is handled separately. First, record the snake head position tpos, the ith snake body position pos, move the ith snake body to the tpos position, then store the value of pos in tpos, then store the original position of the ith + 1st snake body pos, and transform the ith + 1st snake body position into tpos, and so on. In short, tpos is the position to be changed, and pos stores the previous position. (ah, hey, it's so casually named) that's what the code is like.

if (timer < 0)//When it's time to move, the difficulty comes. The snake body moves with the snake head. The implementation method is that the following moves with the front, and the connection part of the snake head and snake body is handled separately
{
    for (int i = 1; i < snakeList.Count; i++)//Start from the first snake
    {
        Vector3 pos = snakeList[i].transform.position;//Store the ith snake position
        if (i == 1)//The first snake is treated separately
        {
            tempPos = snakeList[i - 1].transform.position;//tpos stores the snakehead location tpos=tempPos, which I abbreviated
            snakeList[i].transform.position = tempPos;//Move snake body to snake head position
            tempPos = pos;//The position of tpos storage becomes the position of the first snake body
            continue;
        }
        snakeList[i].transform.position = tempPos;//The position of the ith snake body becomes the position of tpos, that is, the position of the second snake body becomes the position of the first snake body
        tempPos = pos;//The location of tpos storage becomes the location of the ith snake body
    }
    transform.position += new Vector3(moveVec2.x * runSpeed * footStep / 2, moveVec2.y * runSpeed * footStep / 2);//Moving snake head
    timer = 0.3f;//Re timing
}//It's not difficult to say. The most difficult thing is the process from scratch, which needs to be conceived again and again

In addition to these two difficulties, the later is the judgment of the generation position of food. At the beginning, only one detection with inexplicable bug s was written. Later, I simply wrote more ray detection. The logic of this part is chaotic and has not been greatly modified, so this part is only for reference. At least there is no problem. I will modify it when I have time. The code is posted directly, and the comments are completely written, so they are no longer explained.

public void OnEnable()
{
    short y;//Food x location
    short x;//Food y location
    while (true)
    {
        x = (short)Random.Range(0, Mathf.Round((bound[3] - bound[2]) / footStep * 2) + 1);//Randomly generated x, y positions
        y = (short)Random.Range(0, Mathf.Round((bound[0] - bound[1]) / footStep * 2) + 1);
        if (!Equals(new Vector2(Mathf.Round(bound[2] + x * footStep), Mathf.Round(bound[1] + y * footStep)), new Vector2(Mathf.Round(SnakeHead.transform.position.x), Mathf.Round(SnakeHead.transform.position.y))))//The generated position is different from the position of the snake head and jumps out of the loop
            break;
    }
    RaycastHit2D ray = Physics2D.BoxCast(new Vector2(bound[2] + x * footStep / 2, bound[1] + y * footStep / 2), new Vector2(transform.localScale.x, transform.localScale.y), 0, new Vector2(0, 0), LayerMask.GetMask("PlayLayer"));//Create a block ray to detect whether there are other objects in the area
    if (ray.collider == null)//Nothing detected
    {
        GetComponent<SpriteRenderer>().sprite = foodSprite[Random.Range(0, foodSprite.Length)];//Random replacement food picture
        transform.position = new Vector3(bound[2] + x * footStep / 2, bound[1] + y * footStep / 2);//Generate new location
        GetComponent<SpriteRenderer>().enabled = true;//Picture visible
    }
}
private void OnTriggerStay2D(Collider2D collision)
{
    if (collision.gameObject.layer == LayerMask.NameToLayer("PlayLayer") && !collision.CompareTag("SnakeHead"))//If you encounter an object in the playlayer that is not a snake head, regenerate the food
    {
        GetComponent<SpriteRenderer>().enabled = false;//Food picture not visible
        OnEnable();
    }
}

There is nothing to pay attention to in other codes and operations here. Oh, and there is a special prop in the material, which I haven't realized. If you want to do it, you can try it. It's OK to add two to the length or score. This time, we use something related to data storage, In fact, unity has defined a class PlayerPrefs to store some small data. If it is large, it is better to store it honestly in a file. This is too performance-consuming, but the advantage is that it can be read directly from memory. Generally, it uses the get and set methods in it, but there is no bool type, If string or int type storage is required, it is recommended to use int. if string is a reference, frequent modification requires frequent opening up new space on the heap and continuous use of GC recycling, which is very performance-consuming. There are no other places to pay attention to.
The more you write, the more you feel that you are low-level. You may also see less code from others. if you don't make a decision in case of trouble... When it comes to large-scale projects, this road must be impassable. Don't learn from me. I recommend you to read the big talk data structure after you get started to a certain extent, Don't study algorithms, but you must understand the data structure as long as you write code. Only after learning data structure can you not "learn in vain", otherwise you will be ridiculed by the project manager when you enter the company. Well, that's all for this time. See my code and settings for specific things. Post links directly.
       
       
       
Link: https://pan.baidu.com/s/159vq3liZU6Kkztpz_We5wQ
Extraction code: gvmk

Topics: Unity