C/C++ snake 貪食蛇遊戲範例

本篇 ShengYu 介紹 C/C++ 用 SFML 函式庫寫 snake 貪食蛇遊戲,使用 SFML 函式庫寫遊戲的好處就是能夠讓你的遊戲跨平台運行,例如在 Windows / macOS / Linux 上執行,本篇的貪食蛇遊戲圖片是在 Ubuntu 執行,但也可以運行在 Windows / macOS 上,接下來就開始介紹怎麼寫 snake 貪食蛇遊戲。

首先先渲染一個 30x20 圖片的遊戲背景,這背景就是貪食蛇的背景,如果不熟悉 SFML 可以看上一篇 SFML 介紹

snake1.cpp
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
#include <SFML/Graphics.hpp>
#include <time.h>
using namespace sf;

int N = 30, M = 20;
int size = 16;
int w = size * N;
int h = size * M;

int main()
{
RenderWindow window(VideoMode(w, h), "Snake Game!");

Texture t1, t2;
t1.loadFromFile("images/white.png");
t2.loadFromFile("images/red.png");

Sprite sprite1(t1);
Sprite sprite2(t2);

while (window.isOpen()) {
Event e;
while (window.pollEvent(e)) {
if (e.type == Event::Closed)
window.close();
}

////// draw ///////
window.clear();

for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
sprite1.setPosition(i * size, j * size);
window.draw(sprite1);
}
}

window.display();
}

return 0;
}

程式執行起來會是這樣,已經完成貪食蛇的背景了!

完成貪食蛇的背景後要開始加入些主要邏輯了,

Tick() 函式主要處理繪製貪食蛇的身體,貪食蛇的碰撞判斷兩大邏輯,

貪食蛇的身體分為蛇身與蛇頭,蛇頭 s[0] 會根據上次的移動方向來繪製,

貪食蛇的碰撞分為碰到果實,碰到邊界與碰到自己蛇身。碰到果實 f 則產生下一個新的果實 f 位置。碰到邊界就要從另外一個邊界出現,碰到自己蛇身某一節的話,則將長度設為碰到的那節。

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
void Tick()
{
// 更新 s[1]~s[num] 的位置
for (int i = num; i > 0; --i) {
s[i].x = s[i - 1].x;
s[i].y = s[i - 1].y;
}

// 更新 s[0] 的位置
if (dir == 0)
s[0].y += 1;
if (dir == 1)
s[0].x -= 1;
if (dir == 2)
s[0].x += 1;
if (dir == 3)
s[0].y -= 1;

// 如果 s[0] 碰到 f 的位置,則 num 長度加 1,並產生新的隨機 f 位置
if ((s[0].x == f.x) && (s[0].y == f.y)) {
num++;
f.x = rand() % N;
f.y = rand() % M;
}

// 超出邊界的處理
if (s[0].x > N)
s[0].x = 0;
if (s[0].x < 0)
s[0].x = N;
if (s[0].y > M)
s[0].y = 0;
if (s[0].y < 0)
s[0].y = M;

// 如果頭碰到身體某一節的話,則將長度設為碰到的那節
for (int i = 1; i < num; i++) {
if (s[0].x == s[i].x && s[0].y == s[i].y)
num = i;
}
}

以下就是整個完整的貪食蛇遊戲範例,
這個貪食蛇功能還算陽春,但已經包含了最主要的貪食蛇基本邏輯,剩下就是計分功能,跟額外的玩法變化了。

snake2.cpp
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
#include <SFML/Graphics.hpp>
#include <time.h>
using namespace sf;

int N = 30, M = 20;
int size = 16;
int w = size * N;
int h = size * M;

int dir, num = 4;

struct Snake
{
int x, y;
} s[100];

struct Fruit
{
int x, y;
} f;

void Tick()
{
for (int i = num; i > 0; --i) {
s[i].x = s[i - 1].x;
s[i].y = s[i - 1].y;
}

if (dir == 0)
s[0].y += 1;
if (dir == 1)
s[0].x -= 1;
if (dir == 2)
s[0].x += 1;
if (dir == 3)
s[0].y -= 1;

if ((s[0].x == f.x) && (s[0].y == f.y)) {
num++;
f.x = rand() % N;
f.y = rand() % M;
}

if (s[0].x > N)
s[0].x = 0;
if (s[0].x < 0)
s[0].x = N;
if (s[0].y > M)
s[0].y = 0;
if (s[0].y < 0)
s[0].y = M;

for (int i = 1; i < num; i++) {
if (s[0].x == s[i].x && s[0].y == s[i].y)
num = i;
}
}

int main()
{
srand(time(0));

RenderWindow window(VideoMode(w, h), "Snake Game!");

Texture t1, t2;
t1.loadFromFile("images/white.png");
t2.loadFromFile("images/red.png");

Sprite sprite1(t1);
Sprite sprite2(t2);

Clock clock;
float timer = 0, delay = 0.1;

f.x = 10;
f.y = 10;

while (window.isOpen()) {
float time = clock.getElapsedTime().asSeconds();
clock.restart();
timer += time;

Event e;
while (window.pollEvent(e)) {
if (e.type == Event::Closed)
window.close();
}

if (Keyboard::isKeyPressed(Keyboard::Left))
dir = 1;
if (Keyboard::isKeyPressed(Keyboard::Right))
dir = 2;
if (Keyboard::isKeyPressed(Keyboard::Up))
dir = 3;
if (Keyboard::isKeyPressed(Keyboard::Down))
dir = 0;

if (timer > delay) {
timer = 0;
Tick();
}

////// draw ///////
window.clear();

for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
sprite1.setPosition(i * size, j * size);
window.draw(sprite1);
}
}

for (int i = 0; i < num; i++) {
sprite2.setPosition(s[i].x * size, s[i].y * size);
window.draw(sprite2);
}

sprite2.setPosition(f.x * size, f.y * size);
window.draw(sprite2);

window.display();
}

return 0;
}

程式執行起來會是這樣,這樣就完成簡單的貪食蛇遊戲囉!

以上就是 C/C++ snake 貪食蛇遊戲範例介紹,
如果你覺得我的文章寫得不錯、對你有幫助的話記得 Facebook 按讚支持一下!

其它相關文章推薦
如果你想學習 C++ 相關技術,可以參考看看下面的文章,
C/C++ 新手入門教學懶人包
C/C++ SFML 基本範例教學