0%

Unity 做贪吃蛇

用unity做了个贪吃蛇

游戏界面

游戏试玩

如果没失效的话

http://andersonfeng.asuscomm.com:8081/Snake/index.html

难点

贪吃蛇body跟随

刚开始用Vector2.MoveTowards ,不过经过测试每次head转弯body都会跟得更近一些

想要body完全跟随,还是需要一个list或者array存放head的路径,然后body再一个个切换head的路径

贪吃蛇吃到食物后body变长

将body设置成prefab 每次head吃到food

1
2
var newBody = Instantiate(Instance.bodyPrefab, head.position, Quaternion.identity);
newBody.transform.SetParent(Instance.transform);

贪吃蛇与border和body的碰撞

一开始想到的是用int 坐标判断或者用collider判断

觉得用坐标每一帧要遍历body次数判断可能消耗会高?

不知道collider和遍历哪个性能更好

不过都能实现,这里用得是用OnTriggerEnter2D判断

玩法

地图上会随机生成一个food

head通过左右上下4个方向移动去吃food

吃到food,body长1格 food再随机生成一个

body长到50胜利

head碰到body body少1格

head碰到border 游戏结束

倒计时8秒 没吃到food body少1格

body为-1 游戏结束

上代码

SnakeController 控制蛇移动

GameManager 控制游戏

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SnakeController : MonoBehaviour
{
[Header("移动速度")] public float speed;
public KeyCode up;
public KeyCode down;
public KeyCode left;
public KeyCode right;
[Header("蛇身")] public GameObject bodyPrefab;

[Header("路径间隔")] public int pathInterval = 10;
public int successFoodCount = 50;
[Header("移动时间间隔")] public float moveDuration = 0.1f;

private Vector2 _movement;
private static SnakeController Instance;
private List<Vector2> pathList = new List<Vector2>();
private bool eat;
private float _moveTimer;
private bool _releaseMove = true;

private void Awake()
{
Instance = this;
}

// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(up) && _movement != Vector2.down && _releaseMove)
{
_movement = Vector2.up;
_releaseMove = false;
}

if (Input.GetKeyDown(down) && _movement != Vector2.up && _releaseMove)
{
_movement = Vector2.down;
_releaseMove = false;
}

if (Input.GetKeyDown(left) && _movement != Vector2.right && _releaseMove)
{
_movement = Vector2.left;
_releaseMove = false;
}

if (Input.GetKeyDown(right) && _movement != Vector2.left && _releaseMove)
{
_movement = Vector2.right;
_releaseMove = false;
}
}

private void FixedUpdate()
{
/*
* 移动蛇头
* 并记录蛇头[pathInterval]个节点位置
* 每有1个蛇身 蛇头记录节点就多[pathInterval]个
* 每个蛇身按照 [pathInterval*蛇身数] 的蛇头节点进行移动
*/
if (_movement == Vector2.zero)
return;
_moveTimer -= Time.fixedDeltaTime;
if (_moveTimer > 0)
return;
_moveTimer = moveDuration;
_releaseMove = true;

var head = transform.GetChild(0);

pathList.Add(head.position);
if (pathList.Count > transform.childCount * pathInterval)
pathList.RemoveAt(0);

var targetPosition = (Vector2) head.position + _movement * head.localScale.x;
head.position = targetPosition;

for (int i = 1; i < transform.childCount; i++)
{
if (pathList.Count < i * pathInterval + 1)
return;

var currentBody = transform.GetChild(i);
currentBody.position = pathList[pathInterval * i];
}

if (eat)
{
eat = false;
GenerateBody();
}
}

public static void EatSelf()
{
SoundManager.PlayEatSelf();
Destroy(Instance.transform.GetChild(Instance.transform.childCount - 1).gameObject);
if (Instance.transform.childCount <= 1)
GameManager.GameOver();
}

public static void GenerateBody()
{
var head = Instance.transform.GetChild(0);
var newBody = Instantiate(Instance.bodyPrefab, head.position, Quaternion.identity);
newBody.transform.SetParent(Instance.transform);
if (Instance.transform.childCount > Instance.successFoodCount)
GameManager.Success();
}

public static int BodyLength()
{
return Instance.transform.childCount - 1;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

public class GameManager : MonoBehaviour
{
public SpriteRenderer background;
public Text lengthText;
public Text timerText;
public Text resultText;
public Text messageText;
public Text foodCountText;
public float countDown;
public GameObject gameBoard;


public static GameManager Instance;
private bool pauseGame = false;
private bool dead;
private float currentTimeScale;
private float timer;
private float foodCount;


private void Awake()
{
Instance = this;
}

private void Start()
{
Time.timeScale = 0.5f;
currentTimeScale = Time.timeScale;
ResetTimer();
}

private void Update()
{
if (pauseGame)
Time.timeScale = 0;
else
Time.timeScale = currentTimeScale;

if (dead || pauseGame)
return;

Timer();

currentTimeScale = Mathf.Clamp(currentTimeScale, 0, 20f);

if (Input.GetKeyDown(KeyCode.Escape))
{
gameBoard.SetActive(true);
resultText.text = "Pause";
PauseGame();
}

lengthText.text = SnakeController.BodyLength().ToString();
}

private void Timer()
{
timer -= Time.deltaTime / Time.timeScale;
timerText.text = ((int) timer).ToString();
if (timer < 0)
{
SnakeController.EatSelf();
ResetTimer();
}
}

public static void GameOver()
{
SoundManager.PlayFail();
Instance.gameBoard.SetActive(true);
Instance.resultText.text = "GAME OVER";
Instance.dead = true;
PauseGame();
}

public static void PauseGame()
{
Instance.pauseGame = true;
}

public static void SpeedUp()
{
Instance.currentTimeScale += 0.02f;
}

public static void RamdomBackground()
{
float r = Random.Range(0f, 1f);
float g = Random.Range(0f, 1f);
float b = Random.Range(0f, 1f);
Instance.background.color = new Color(r, g, b);
}

public static void ResetTimer()
{
Instance.timer = Instance.countDown;
}

public void Restart()
{
Instance.gameBoard.SetActive(false);
SceneManager.LoadScene(SceneManager.GetActiveScene().name);
}

public void Resume()
{
Instance.gameBoard.SetActive(false);
Instance.pauseGame = false;
if (dead)
Restart();
}

public static void Success()
{
SoundManager.PlaySuccess();
Instance.gameBoard.SetActive(true);
Instance.resultText.text = "MISSION COMPLETE!";
PauseGame();
}

public static void SetMessage(string message)
{
Instance.messageText.gameObject.SetActive(true);
Instance.messageText.text = message;
Instance.Invoke("DisableMessageText", 2f);
}

private void DisableMessageText()
{
Instance.messageText.gameObject.SetActive(false);
}

public static void SpeedDown()
{
Instance.currentTimeScale -= 0.01f;
}

public static void EatFood()
{
Instance.foodCount++;
Instance.foodCountText.text = Instance.foodCount.ToString();
}
}