目录
游戏开发基本框架
int main()
{
GameInit(); // 游戏初始化
while (1)
{
Show(); // 显示游戏画面
UpdateWithoutInput(); // 和输入无关的更新
UpdateWithInput(); // 和输入有关的更新
}
return 0;
}
一、游戏初始化
#include <graphics.h>
#include <stdlib.h>
#include <time.h>
#define ROW 30
#define COL 40
#define BLOCK_SIZE 20
int board[ROW][COL] = { 0 }; // 创建一个二维数组用来存储游戏画面上每个格子的信息
char move_direction; // 小蛇的移动方向
int food_i, food_j; // 食物的位置
int is_failure; // 判断游戏是否失败
void GameInit()
{
srand((unsigned int)time(NULL)); // 设置随机数种子
initgraph(COL * BLOCK_SIZE, ROW * BLOCK_SIZE); // 初始化绘画窗口
setlinecolor(RGB(200, 200, 200)); // 设置每个格子边框的画线颜色
int i = 0;
int j = 0;
// 1. 当游戏重新开始时,需要将每个格子的信息重新初始化为 0
for (i = 0; i < ROW; i++)
{
for (j = 0; j < COL; j++)
{
board[i][j] = 0;
}
}
// 2. 分别生成蛇头和蛇身
board[ROW / 2][COL / 2] = 1; // 1 表示蛇头
for (i = 1; i <= 4; i++)
{
board[ROW / 2][COL / 2 - i] = i + 1; // 蛇头后的蛇身依次为 2、3、4、5 等正整数
}
// 3. 对几个全局变量进行初始化
move_direction = 'D'; // 向右移动
food_i = rand() % ROW;
food_j = rand() % COL;
is_failure = 0;
// 4. 开始批量绘图
BeginBatchDraw();
}
1. 每个格子的边框颜色:
2. 0 表示空,即绘制灰色的方格;1 表示蛇头,蛇头后的蛇身依次为 2、3、4、5 等正整数,即绘制彩色的方格:
二、显示游戏画面
void Show()
{
int i = 0;
int j = 0;
// 1. 显示
for (i = 0; i < ROW; i++)
{
for (j = 0; j < COL; j++)
{
if (board[i][j] > 0)
{
setfillcolor(HSVtoRGB(board[i][j] * 10, 1, 1)); // 绘制彩色方格
}
else
{
setfillcolor(RGB(150, 150, 150)); // 绘制灰色方格
}
fillrectangle(j * BLOCK_SIZE, i * BLOCK_SIZE, (j + 1) * BLOCK_SIZE, (i + 1) * BLOCK_SIZE);
}
}
setfillcolor(RGB(0, 255, 0)); // 绘制绿色食物
fillrectangle(food_j * BLOCK_SIZE, food_i * BLOCK_SIZE, (food_j + 1) * BLOCK_SIZE, (food_i + 1) * BLOCK_SIZE);
// 2. 判断是否失败
if (is_failure)
{
int ret = MessageBox(NULL, L"Game Over! Again?", L"提示", MB_OKCANCEL | MB_SYSTEMMODAL);
if (ret == IDOK)
{
GameInit(); // 游戏重新初始化
}
else if (ret == IDCANCEL)
{
exit(0);
}
}
// 3. 执行未完成的绘制任务
FlushBatchDraw();
}
1. 灰色方格的填充颜色:
2. fillrectangle 函数:
这个函数使用当前线形和当前填充样式绘制有外框的填充矩形。
void fillrectangle(
int left,
int top,
int right,
int bottom
);
参数:
-
left:矩形左部 x 坐标。
-
top:矩形顶部 y 坐标。
-
right:矩形右部 x 坐标。
-
bottom:矩形底部 y 坐标。
3. 游戏画面:
三、 与输入无关的更新
3.1 - 小蛇移动
void SnakeMove()
{
int i = 0;
int j = 0;
// 1. 让所有大于 0 的元素加 1
for (i = 0; i < ROW; i++)
{
for (j = 0; j < COL; j++)
{
if (board[i][j] > 0)
{
board[i][j] += 1;
}
}
}
// 2. 找到旧蛇尾和旧蛇头的位置
int old_tail_i, old_tail_j; // 旧蛇尾的位置
int old_head_i, old_head_j; // 旧蛇头的位置
int max = 0;
for (i = 0; i < ROW; i++)
{
for (j = 0; j < COL; j++)
{
if (board[i][j] > max)
{
max = board[i][j];
old_tail_i = i;
old_tail_j = j;
}
if (board[i][j] == 2)
{
old_head_i = i;
old_head_j = j;
}
}
}
// 3. 根据移动方向找到新蛇头的位置
int new_head_i = old_head_i;
int new_head_j = old_head_j;
if (move_direction == 'A') // 向左移动
{
new_head_j = old_head_j - 1;
}
else if (move_direction == 'D') // 向右移动
{
new_head_j = old_head_j + 1;
}
else if (move_direction == 'W') // 向上移动
{
new_head_i = old_head_i - 1;
}
else if (move_direction == 'S') // 向下移动
{
new_head_i = old_head_i + 1;
}
// 4. 判断游戏是否失败,即越过边界或者蛇头与蛇身发生碰撞
if (new_head_i < 0 || new_head_i >= ROW ||
new_head_j < 0 || new_head_j >= COL ||
board[new_head_i][new_head_j] > 0)
{
is_failure = 1;
return;
}
// 5. 设置新蛇头以及处理旧蛇尾
board[new_head_i][new_head_j] = 1;
if (new_head_i == food_i && new_head_j == food_j) // 此时不对旧蛇尾进行处理
{
food_i = rand() % ROW;
food_j = rand() % COL;
}
else
{
board[old_tail_i][old_tail_j] = 0; // 将旧蛇尾设置为灰方格
}
}
1. 小蛇移动(例如向右移动):
2. 小蛇吃食物:
3.2 - 控制小蛇的移动速度
void UpdateWithoutInput()
{
if (is_failure)
{
return;
}
static int wait = 1; // 控制小蛇的移动速度
wait++;
if (wait == 10)
{
SnakeMove(); // 小蛇移动
wait = 1;
}
}
Sleep 函数虽然也可以控制小蛇的移动速度,但是 Sleep 函数会让整个程序暂停,因此使用一个静态变量 wait 来控制小蛇的移动速度。
四、 与输入有关的更新
void UpdateWithInput()
{
// 控制小蛇移动的方向
ExMessage msg;
if (peekmessage(&msg, EM_KEY) && is_failure == 0)
{
switch (msg.vkcode) // vscode,即 virtual key code,虚拟键码
{
case VK_UP: // 上
case 0x57: // W 键
move_direction = 'W';
break;
case VK_DOWN: // 下
case 0x53: // S 键
move_direction = 'S';
break;
case VK_LEFT: // 左
case 0x41: // A 键
move_direction = 'A';
break;
case VK_RIGHT: // 右
case 0x44: // D 键
move_direction = 'D';
break;
}
SnakeMove();
}
}
五、源代码
#define _CRT_SECURE_NO_WARNINGS 1
#include <graphics.h>
#include <stdlib.h>
#include <time.h>
#define ROW 30
#define COL 40
#define BLOCK_SIZE 20
int board[ROW][COL] = { 0 }; // 创建一个二维数组用来存储游戏画面上每个格子的信息
char move_direction; // 小蛇的移动方向
int food_i, food_j; // 食物的位置
int is_failure; // 判断游戏是否失败
// 一、游戏初始化(仅执行一次)
void GameInit()
{
srand((unsigned int)time(NULL)); // 设置随机数种子
initgraph(COL * BLOCK_SIZE, ROW * BLOCK_SIZE); // 初始化绘画窗口
setlinecolor(RGB(200, 200, 200)); // 设置每个格子边框的画线颜色
int i = 0;
int j = 0;
// 1. 当游戏重新开始时,需要将每个格子的信息重新初始化为 0
for (i = 0; i < ROW; i++)
{
for (j = 0; j < COL; j++)
{
board[i][j] = 0;
}
}
// 2. 分别生成蛇头和蛇身
board[ROW / 2][COL / 2] = 1; // 1 表示蛇头
for (i = 1; i <= 4; i++)
{
board[ROW / 2][COL / 2 - i] = i + 1; // 蛇头后的蛇身依次为 2、3、4、5 等正整数
}
// 3. 对几个全局变量进行初始化
move_direction = 'D'; // 向右移动
food_i = rand() % ROW;
food_j = rand() % COL;
is_failure = 0;
// 4. 开始批量绘图
BeginBatchDraw();
}
// 二、显示游戏画面
void Show()
{
int i = 0;
int j = 0;
// 1. 显示
for (i = 0; i < ROW; i++)
{
for (j = 0; j < COL; j++)
{
if (board[i][j] > 0)
{
setfillcolor(HSVtoRGB(board[i][j] * 10, 1, 1)); // 绘制彩色方格
}
else
{
setfillcolor(RGB(150, 150, 150)); // 绘制灰色方格
}
fillrectangle(j * BLOCK_SIZE, i * BLOCK_SIZE, (j + 1) * BLOCK_SIZE, (i + 1) * BLOCK_SIZE);
}
}
setfillcolor(RGB(0, 255, 0)); // 绘制绿色食物
fillrectangle(food_j * BLOCK_SIZE, food_i * BLOCK_SIZE,
(food_j + 1) * BLOCK_SIZE, (food_i + 1) * BLOCK_SIZE);
// 2. 判断是否失败
if (is_failure)
{
int ret = MessageBox(NULL, L"Game Over! Again?", L"提示", MB_OKCANCEL | MB_SYSTEMMODAL);
if (ret == IDOK)
{
GameInit(); // 游戏重新初始化
}
else if (ret == IDCANCEL)
{
exit(0);
}
}
// 3. 执行未完成的绘制任务
FlushBatchDraw();
}
// 三、和输入无关的更新
void SnakeMove()
{
int i = 0;
int j = 0;
// 1. 让所有大于 0 的元素加 1
for (i = 0; i < ROW; i++)
{
for (j = 0; j < COL; j++)
{
if (board[i][j] > 0)
{
board[i][j] += 1;
}
}
}
// 2. 找到旧蛇尾和旧蛇头的位置
int old_tail_i, old_tail_j; // 旧蛇尾的位置
int old_head_i, old_head_j; // 旧蛇头的位置
int max = 0;
for (i = 0; i < ROW; i++)
{
for (j = 0; j < COL; j++)
{
if (board[i][j] > max)
{
max = board[i][j];
old_tail_i = i;
old_tail_j = j;
}
if (board[i][j] == 2)
{
old_head_i = i;
old_head_j = j;
}
}
}
// 3. 根据移动方向找到新蛇头的位置
int new_head_i = old_head_i;
int new_head_j = old_head_j;
if (move_direction == 'A') // 向左移动
{
new_head_j = old_head_j - 1;
}
else if (move_direction == 'D') // 向右移动
{
new_head_j = old_head_j + 1;
}
else if (move_direction == 'W') // 向上移动
{
new_head_i = old_head_i - 1;
}
else if (move_direction == 'S') // 向下移动
{
new_head_i = old_head_i + 1;
}
// 4. 判断游戏是否失败,即越过边界或者蛇头与蛇身发生碰撞
if (new_head_i < 0 || new_head_i >= ROW || new_head_j < 0 || new_head_j >= COL
|| board[new_head_i][new_head_j] > 0)
{
is_failure = 1;
return;
}
// 5. 设置新蛇头以及处理旧蛇尾
board[new_head_i][new_head_j] = 1;
if (new_head_i == food_i && new_head_j == food_j) // 此时不对旧蛇尾进行处理
{
food_i = rand() % ROW;
food_j = rand() % COL;
}
else
{
board[old_tail_i][old_tail_j] = 0; // 将旧蛇尾设置为灰方格
}
}
void UpdateWithoutInput()
{
if (is_failure)
{
return;
}
static int wait = 1; // 控制小蛇的移动速度
wait++;
if (wait == 12)
{
SnakeMove(); // 小蛇移动
wait = 1;
}
}
// 四、和输入有关的更新
void UpdateWithInput()
{
// 控制小蛇移动的方向
ExMessage msg;
if (peekmessage(&msg, EM_KEY) && is_failure == 0)
{
switch (msg.vkcode) // vscode,即 virtual key code,虚拟键码
{
case VK_UP: // 上
case 0x57: // W 键
move_direction = 'W';
break;
case VK_DOWN: // 下
case 0x53: // S 键
move_direction = 'S';
break;
case VK_LEFT: // 左
case 0x41: // A 键
move_direction = 'A';
break;
case VK_RIGHT: // 右
case 0x44: // D 键
move_direction = 'D';
break;
}
SnakeMove();
}
}
// 主函数
int main()
{
GameInit(); // 游戏初始化
while (1)
{
Show(); // 显示游戏画面
UpdateWithoutInput(); // 和输入无关的更新
UpdateWithInput(); // 和输入有关的更新
}
return 0;
}