这一次我们应用printf输出实现一个经典的小游戏—贪吃蛇,主要难点是小蛇数据如何存储、如何实现转弯的效果、吃到食物后如何增加长度。
1 构造小蛇
首先,在画面中显示一条静止的小蛇。二维数组canvas[High][Width]的对应元素,值为0输出空格,-1输出边框#,1输出蛇头@,大于1的正数输出蛇身*。startup()函数中初始化蛇头在画布中间位置(canvas[High/2][Width/2] = 1;),蛇头向左依次生成4个蛇身(for (i=1;i<=4;i++) canvas[High/2][Width/2-i] = i+1;),元素值分别为2、3、4、5。
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <conio.h> 4 #include <windows.h> 5 //C语言自学网 6 #define High 20 // 游戏画面尺寸 7 #define Width 30 8 9 // 全局变量 10 int canvas[High][Width] = {0}; // 二维数组存储游戏画布中对应的元素 11 // 0为空格,-1为边框#,1为蛇头@,大于1的正数为蛇身* 12 13 void gotoxy(int x,int y) //光标移动到(x,y)位置 14 { 15 HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); 16 COORD pos; 17 pos.X = x; 18 pos.Y = y; 19 SetConsoleCursorPosition(handle,pos); 20 } 21 22 void startup() // 数据初始化 23 { 24 int i,j; 25 26 // 初始化边框 27 for (i=0;i<High;i++) 28 { 29 canvas[i][0] = -1; 30 canvas[i][Width-1] = -1; 31 } 32 for (j=0;j<Width;j++) 33 { 34 canvas[0][j] = -1; 35 canvas[High-1][j] = -1; 36 } 37 38 // 初始化蛇头位置 39 canvas[High/2][Width/2] = 1; 40 // 初始化蛇身,画布中元素值分别为2,3,4,5.... 41 for (i=1;i<=4;i++) 42 canvas[High/2][Width/2-i] = i+1; 43 } 44 45 void show() // 显示画面 46 { 47 gotoxy(0,0); // 光标移动到原点位置,以下重画清屏 48 int i,j; 49 for (i=0;i<High;i++) 50 { 51 for (j=0;j<Width;j++) 52 { 53 if (canvas[i][j]==0) 54 printf(" "); // 输出空格 55 else if (canvas[i][j]==-1) 56 printf("#"); // 输出边框# 57 else if (canvas[i][j]==1) 58 printf("@"); // 输出蛇头@ 59 else if (canvas[i][j]>1) 60 printf("*"); // 输出蛇身* 61 } 62 printf(" "); 63 } 64 } 65 66 void updateWithoutInput() // 与用户输入无关的更新 67 { 68 } 69 70 void updateWithInput() // 与用户输入有关的更新 71 { 72 } 73 74 int main() 75 { 76 startup(); // 数据初始化 77 while (1) // 游戏循环执行 78 { 79 show(); // 显示画面 80 updateWithoutInput(); // 与用户输入无关的更新 81 updateWithInput(); // 与用户输入有关的更新 82 } 83 return 0; 84 }
2 小蛇自动移动
实现小蛇的移动是贪吃蛇游戏的难点,下图列出了小蛇分别向右、向上运动后,对应二维数组元素值的变化,从中我们可以得出实现思路。
假设小蛇元素为54321,其中1为蛇头、5432为蛇身、最大值5为蛇尾。首先将所有大于0的元素加1,得到65432;将最大值6变为0,即去除了原来的蛇尾;再根据对应的移动方向,将2对应方向的元素由0变成1;如此即实现了小蛇的移动。小蛇向上移动的对应流程如图所示。
定义变量int moveDirection表示小蛇的移动方向,值1、2、3、4分别表示小蛇向上、下、左、右方向移动,小蛇移动实现在moveSnakeByDirection()函数中。
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <conio.h> 4 #include <windows.h> 5 //C语言自学网 6 #define High 20 // 游戏画面尺寸 7 #define Width 30 8 9 // 全局变量 10 int moveDirection; // 小蛇移动方向,上下左右分别用1,2,3,4表示 11 int canvas[High][Width] = {0}; // 二维数组存储游戏画布中对应的元素 12 // 0为空格0,-1为边框#,1为蛇头@,大于1的正数为蛇身* 13 14 void gotoxy(int x,int y) //光标移动到(x,y)位置 15 { 16 HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); 17 COORD pos; 18 pos.X = x; 19 pos.Y = y; 20 SetConsoleCursorPosition(handle,pos); 21 } 22 23 // 移动小蛇 24 // 第一步扫描数组canvas所有元素,找到正数元素都+1 25 // 找到最大元素(即蛇尾巴),把其变为0 26 // 找到=2的元素(即蛇头),再根据输出的上下左右方向,把对应的另一个像素值设为1(新蛇头) 27 void moveSnakeByDirection() 28 { 29 int i,j; 30 for (i=1;i<High-1;i++) 31 for (j=1;j<Width-1;j++) 32 if (canvas[i][j]>0) 33 canvas[i][j]++; 34 35 int oldTail_i,oldTail_j,oldHead_i,oldHead_j; 36 int max = 0; 37 38 for (i=1;i<High-1;i++) 39 for (j=1;j<Width-1;j++) 40 if (canvas[i][j]>0) 41 { 42 if (max<canvas[i][j]) 43 { 44 max = canvas[i][j]; 45 oldTail_i = i; 46 oldTail_j = j; 47 } 48 if (canvas[i][j]==2) 49 { 50 oldHead_i = i; 51 oldHead_j = j; 52 } 53 } 54 55 canvas[oldTail_i][oldTail_j] = 0; 56 57 if (moveDirection==1) // 向上移动 58 canvas[oldHead_i-1][oldHead_j] = 1; 59 if (moveDirection==2) // 向下移动 60 canvas[oldHead_i+1][oldHead_j] = 1; 61 if (moveDirection==3) // 向左移动 62 canvas[oldHead_i][oldHead_j-1] = 1; 63 if (moveDirection==4) // 向右移动 64 canvas[oldHead_i][oldHead_j+1] = 1; 65 } 66 67 void startup() // 数据初始化 68 { 69 int i,j; 70 71 // 初始化边框 72 for (i=0;i<High;i++) 73 { 74 canvas[i][0] = -1; 75 canvas[i][Width-1] = -1; 76 } 77 for (j=0;j<Width;j++) 78 { 79 canvas[0][j] = -1; 80 canvas[High-1][j] = -1; 81 } 82 83 // 初始化蛇头位置 84 canvas[High/2][Width/2] = 1; 85 // 初始化蛇身,画布中元素值分别为2,3,4,5.... 86 for (i=1;i<=4;i++) 87 canvas[High/2][Width/2-i] = i+1; 88 89 // 初始小蛇向右移动 90 moveDirection = 4; 91 } 92 93 void show() // 显示画面 94 { 95 gotoxy(0,0); // 光标移动到原点位置,以下重画清屏 96 int i,j; 97 for (i=0;i<High;i++) 98 { 99 for (j=0;j<Width;j++) 100 { 101 if (canvas[i][j]==0) 102 printf(" "); // 输出空格 103 else if (canvas[i][j]==-1) 104 printf("#"); // 输出边框# 105 else if (canvas[i][j]==1) 106 printf("@"); // 输出蛇头@ 107 else if (canvas[i][j]>1) 108 printf("*"); // 输出蛇身* 109 } 110 printf(" "); 111 } 112 Sleep(100); 113 } 114 115 void updateWithoutInput() // 与用户输入无关的更新 116 { 117 moveSnakeByDirection(); 118 } 119 120 void updateWithInput() // 与用户输入有关的更新 121 { 122 } 123 124 int main() 125 { 126 startup(); // 数据初始化 127 while (1) // 游戏循环执行 128 { 129 show(); // 显示画面 130 updateWithoutInput(); // 与用户输入无关的更新 131 updateWithInput(); // 与用户输入有关的更新 132 } 133 return 0; 134 }
3 玩家控制小蛇移动
这一步的实现比较简单,在updateWithInput()函数中按asdw键改变moveDirection的值,然后调用moveSnakeByDirection()实现小蛇向不同方向的移动,如图所示。
1 void updateWithInput() // 与用户输入有关的更新 2 //C语言自学网 3 { 4 char input; 5 if(kbhit()) // 判断是否有输入 6 { 7 input = getch(); // 根据用户的不同输入来移动,不必输入回车 8 if (input == 'a') 9 { 10 moveDirection = 3; // 位置左移 11 moveSnakeByDirection(); 12 } 13 else if (input == 'd') 14 { 15 moveDirection = 4; // 位置右移 16 moveSnakeByDirection(); 17 } 18 else if (input == 'w') 19 { 20 moveDirection = 1; // 位置上移 21 moveSnakeByDirection(); 22 } 23 else if (input == 's') 24 { 25 moveDirection = 2; // 位置下移 26 moveSnakeByDirection(); 27 } 28 } 29 }
4 判断游戏失败
当小蛇和边框或自身发生碰撞时,游戏失败,如图所示。
1 void moveSnakeByDirection() 2 //C语言自学网 3 { 4 int i,j; 5 for (i=1;i<High-1;i++) 6 for (j=1;j<Width-1;j++) 7 if (canvas[i][j]>0) 8 canvas[i][j]++; 9 int oldTail_i,oldTail_j,oldHead_i,oldHead_j; 10 int max = 0; 11 for (i=1;i<High-1;i++) 12 for (j=1;j<Width-1;j++) 13 if (canvas[i][j]>0) 14 { 15 if (max<canvas[i][j]) 16 { 17 max = canvas[i][j]; 18 oldTail_i = i; 19 oldTail_j = j; 20 } 21 if (canvas[i][j]==2) 22 { 23 oldHead_i = i; 24 oldHead_j = j; 25 } 26 } 27 canvas[oldTail_i][oldTail_j] = 0; 28 int newHead_i,newHead_j; 29 if (moveDirection==1) // 向上移动 30 { 31 newHead_i = oldHead_i-1; 32 newHead_j = oldHead_j; 33 } 34 if (moveDirection==2) // 向下移动 35 { 36 newHead_i = oldHead_i+1; 37 newHead_j = oldHead_j; 38 } 39 if (moveDirection==3) // 向左移动 40 { 41 newHead_i = oldHead_i; 42 newHead_j = oldHead_j-1; 43 } 44 if (moveDirection==4) // 向右移动 45 { 46 newHead_i = oldHead_i; 47 newHead_j = oldHead_j+1; 48 } 49 50 // 是否小蛇和自身撞,或者和边框撞,游戏失败 51 if (canvas[newHead_i][newHead_j]>0 || canvas[newHead_i][newHead_j]==-1) 52 { 53 printf("游戏失败! "); 54 exit(0); 55 } 56 else 57 canvas[newHead_i][newHead_j] = 1; 58 }
5 吃食物增加长度
增加食物,二维数组canvas[High][Width]元素值为-2时,输出食物数值’F’,如图所示。当蛇头碰到食物时,长度加一。
实现思路和2中小蛇移动类似,只需保持原蛇尾,不将最大值变为0即可,下图为小蛇向上移动吃到食物的对应流程。
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <conio.h> 4 #include <windows.h> 5 //C语言自学网 6 #define High 20 // 游戏画面尺寸 7 #define Width 30 8 9 // 全局变量 10 int moveDirection; // 小蛇移动位置,上下左右分别用1,2,3,4表示 11 int food_x,food_y; // 食物的位置 12 int canvas[High][Width] = {0}; // 二维数组存储游戏画布中对应的元素 13 // 0为空格0,-1为边框#,-2为食物F,1为蛇头@,大于1的正数为蛇身* 14 15 void gotoxy(int x,int y) //光标移动到(x,y)位置 16 { 17 HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); 18 COORD pos; 19 pos.X = x; 20 pos.Y = y; 21 SetConsoleCursorPosition(handle,pos); 22 } 23 24 // 移动小蛇 25 // 第一步扫描数组canvas所有元素,找到正数元素都+1 26 // 找到最大元素(即蛇尾巴),把其变为0 27 // 找到=2的元素(即蛇头),再根据输出的上下左右方向,把对应的另一个像素值设为1(新蛇头) 28 void moveSnakeByDirection() 29 { 30 int i,j; 31 for (i=1;i<High-1;i++) 32 for (j=1;j<Width-1;j++) 33 if (canvas[i][j]>0) 34 canvas[i][j]++; 35 36 int oldTail_i,oldTail_j,oldHead_i,oldHead_j; 37 int max = 0; 38 39 for (i=1;i<High-1;i++) 40 for (j=1;j<Width-1;j++) 41 if (canvas[i][j]>0) 42 { 43 if (max<canvas[i][j]) 44 { 45 max = canvas[i][j]; 46 oldTail_i = i; 47 oldTail_j = j; 48 } 49 if (canvas[i][j]==2) 50 { 51 oldHead_i = i; 52 oldHead_j = j; 53 } 54 } 55 56 int newHead_i,newHead_j; 57 58 if (moveDirection==1) // 向上移动 59 { 60 newHead_i = oldHead_i-1; 61 newHead_j = oldHead_j; 62 } 63 if (moveDirection==2) // 向下移动 64 { 65 newHead_i = oldHead_i+1; 66 newHead_j = oldHead_j; 67 } 68 if (moveDirection==3) // 向左移动 69 { 70 newHead_i = oldHead_i; 71 newHead_j = oldHead_j-1; 72 } 73 if (moveDirection==4) // 向右移动 74 { 75 newHead_i = oldHead_i; 76 newHead_j = oldHead_j+1; 77 } 78 79 // 新蛇头如果吃到食物 80 if (canvas[newHead_i][newHead_j]==-2) 81 { 82 canvas[food_x][food_y] = 0; 83 // 产生一个新的食物 84 food_x = rand()%(High-5) + 2; 85 food_y = rand()%(Width-5) + 2; 86 canvas[food_x][food_y] = -2; 87 88 // 原来的旧蛇尾留着,长度自动+1 89 } 90 else // 否则,原来的旧蛇尾减掉,保持长度不变 91 canvas[oldTail_i][oldTail_j] = 0; 92 93 // 是否小蛇和自身撞,或者和边框撞,游戏失败 94 if (canvas[newHead_i][newHead_j]>0 || canvas[newHead_i][newHead_j]==-1) 95 { 96 printf("游戏失败! "); 97 Sleep(2000); 98 system("pause"); 99 exit(0); 100 } 101 else 102 canvas[newHead_i][newHead_j] = 1; 103 } 104 105 void startup() // 数据初始化 106 { 107 int i,j; 108 109 // 初始化边框 110 for (i=0;i<High;i++) 111 { 112 canvas[i][0] = -1; 113 canvas[i][Width-1] = -1; 114 } 115 for (j=0;j<Width;j++) 116 { 117 canvas[0][j] = -1; 118 canvas[High-1][j] = -1; 119 } 120 121 // 初始化蛇头位置 122 canvas[High/2][Width/2] = 1; 123 // 初始化蛇身,画布中元素值分别为2,3,4,5.... 124 for (i=1;i<=4;i++) 125 canvas[High/2][Width/2-i] = i+1; 126 127 // 初始小蛇向右移动 128 moveDirection = 4; 129 130 food_x = rand()%(High-5) + 2; 131 food_y = rand()%(Width-5) + 2; 132 canvas[food_x][food_y] = -2; 133 } 134 135 void show() // 显示画面 136 { 137 gotoxy(0,0); // 光标移动到原点位置,以下重画清屏 138 int i,j; 139 for (i=0;i<High;i++) 140 { 141 for (j=0;j<Width;j++) 142 { 143 if (canvas[i][j]==0) 144 printf(" "); // 输出空格 145 else if (canvas[i][j]==-1) 146 printf("#"); // 输出边框# 147 else if (canvas[i][j]==1) 148 printf("@"); // 输出蛇头@ 149 else if (canvas[i][j]>1) 150 printf("*"); // 输出蛇身* 151 else if (canvas[i][j]==-2) 152 printf("F"); // 输出食物F 153 } 154 printf(" "); 155 } 156 Sleep(100); 157 } 158 159 void updateWithoutInput() // 与用户输入无关的更新 160 { 161 moveSnakeByDirection(); 162 } 163 164 void updateWithInput() // 与用户输入有关的更新 165 { 166 char input; 167 if(kbhit()) // 判断是否有输入 168 { 169 input = getch(); // 根据用户的不同输入来移动,不必输入回车 170 if (input == 'a') 171 { 172 moveDirection = 3; // 位置左移 173 moveSnakeByDirection(); 174 } 175 else if (input == 'd') 176 { 177 moveDirection = 4; // 位置右移 178 moveSnakeByDirection(); 179 } 180 else if (input == 'w') 181 { 182 moveDirection = 1; // 位置上移 183 moveSnakeByDirection(); 184 } 185 else if (input == 's') 186 { 187 moveDirection = 2; // 位置下移 188 moveSnakeByDirection(); 189 } 190 } 191 } 192 193 int main() 194 { 195 startup(); // 数据初始化 196 while (1) // 游戏循环执行 197 { 198 show(); // 显示画面 199 updateWithoutInput(); // 与用户输入无关的更新 200 updateWithInput(); // 与用户输入有关的更新 201 } 202 return 0; 203 }
6 思考题
1. 增加道具,吃完可以加命或减速;
2. 尝试实现双人版贪吃蛇
感谢你的阅读,请用心感悟!希望可以帮到爱学习的你!!分享也是一种快乐!!!请接力。。。