目录
一、知道点归纳
第三章 数组在游戏的使用
第四章 easyX总结
二、代码实现
第三章
3.2消砖块
3.3空战游戏
3.4贪吃蛇
第四章
4.2多球反弹
4.3实时钟表
4.4Easy实现消砖块
一、知识点归纳
第三章 数组在游戏的使用
1 数组的使用 2 3.1生命游戏 3 将画面当二维数组,每次循环更新一遍数组的值 4 int cells[High][Width]; // 所有位置细胞生1或死0 5 6 3.2消砖块 7 将整个画面当作二维数组,填充要素:砖块、球、挡板。 8 需要用到大量判断语句,每次循环都要重新执行,对时间复杂度上不够优秀,可以单独维护砖块的二维数组,减少show()的判断次数 9 int canvas[High][Width] = {0}; // 二维数组存储游戏画布中对应的元素 10 void startup() // 数据初始化 11 { 12 canvas[ball_x][ball_y] = 1; 13 int k,i; 14 for (k=left;k<=right;k++) // 挡板 15 canvas[position_x][k] = 2; 16 for (k=0;k<Width;k++) // 加几排砖块 17 for (i=0;i<High/4;i++) 18 canvas[i][k] = 3; 19 } 20 void updateWithoutInput() // 与用户输入无关的更新 21 { 22 if (canvas[ball_x-1][ball_y]==3)//有砖块消砖块 23 { 24 ball_vx = -ball_vx; 25 canvas[ball_x-1][ball_y] = 0; 26 printf("\a"); 27 } 28 } 29 30 3.3空战游戏 31 用数组存储多台敌机,enemy_x[i],enemy_y[i],i号敌机的xy坐标。 32 这里可以运用结构体数组,会让结构更紧凑。 33 typedef struct 34 { 35 int x,y; 36 } enemy[10]; 37 38 用二维数组,方便对画面进行大改动例如发射散弹,积分增加,散弹半径增加,触发敌机变快。同时可以做到连续发射子弹。 39 对画面遍历一遍,在有子弹的位置,遍历一遍敌机数组,判断是否击中,没有则向上移动。 40 for (k=0;k<EnemyNum;k++) 41 { 42 if ((i==enemy_x[k]) && (j==enemy_y[k])) // 子弹击中敌机 43 { 44 score++; // 分数加1 45 if (score%5==0 && EnemyMoveSpeed>3) // 达到一定积分后,敌机变快 46 EnemyMoveSpeed--; 47 if (score%5==0) // 达到一定积分后,子弹变厉害 48 BulletWidth++; 49 canvas[enemy_x[k]][enemy_y[k]] = 0; 50 enemy_x[k] = rand()%2; // 产生新的飞机 51 enemy_y[k] = rand()%Width; 52 canvas[enemy_x[k]][enemy_y[k]] = 3; 53 canvas[i][j] = 0; // 子弹消失 54 } 55 } 56 57 多台敌机,统一的逻辑,都自动下落,当任一台跑出都要重新生成。 58 EnemyMoveSpeed用于更改敌机移动速度,当得分到一定值,该值减小,敌机移动速度变快 59 if (enemy_x[k]>High) // 敌机跑出显示屏幕 60 { 61 canvas[enemy_x[k]][enemy_y[k]] = 0; 62 enemy_x[k] = rand()%2; // 产生新的飞机 63 enemy_y[k] = rand()%Width; 64 canvas[enemy_x[k]][enemy_y[k]] = 3; 65 score--; // 减分 66 } 67 68 if (speed == EnemyMoveSpeed) 69 { 70 // 敌机下落 71 for (k=0;k<EnemyNum;k++) 72 { 73 canvas[enemy_x[k]][enemy_y[k]] = 0; 74 enemy_x[k]++; 75 speed = 0; 76 canvas[enemy_x[k]][enemy_y[k]] = 3; 77 } 78 } 79 怎么实现发射散弹 80 else if (input == ' ') // 发射子弹 81 { 82 int left = position_y-BulletWidth;//发射一排子弹 83 int right = position_y+BulletWidth; 84 if (left<0) left = 0; //这防止子弹溢出 85 if (right>Width-1) 86 right = Width-1; 87 int k; 88 for (k=left;k<=right;k++) // 发射闪弹 89 canvas[position_x-1][k] = 2; // 发射子弹的初始位置在飞机的正上方 90 } 91 92 当得分到5 10 15 时子弹宽带增加,但是,我觉得这里可能有一点不会逻辑,因为存在敌机跑出屏幕减分的情况,单纯判断mod5,会出现5 10 5 的情况,按原文代码还是会使得子弹宽度增加。 93 原文: 94 if (score%5==0) // 达到一定积分后,子弹变厉害 95 BulletWidth++; 96 97 我的想法: 98 方法一:用一个全局变量记录得分最大值 99 int scoremax=0; 100 if(score%5==0&&scoremax<score) 101 { 102 BulletWidth++; 103 scoremax=score; 104 } 105 方法二:BulletWidth = score/5,使得直接关联。 106 107 3.4贪吃蛇 108 用数组实现蛇: 109 canvas[High/2][Width/2] = 1;//定蛇头 110 for (i=1;i<=4;i++) 111 canvas[High/2][Width/2-i] = i+1; 112 蛇移动:逻辑是>0的值加一,将最大值改为0,对应移动方向(上下左右)0改为1。这里不能直接判断蛇尾为6(吃食物后,最值会改变) 113 void moveSnakeByDirection() 114 { 115 int i,j; 116 for (i=1;i<High-1;i++) 117 for (j=1;j<Width-1;j++) 118 if (canvas[i][j]>0) 119 canvas[i][j]++; //全加一 120 121 int oldTail_i,oldTail_j,oldHead_i,oldHead_j; 122 int max = 0; 123 124 for (i=1;i<High-1;i++) 125 for (j=1;j<Width-1;j++) 126 if (canvas[i][j]>0) 127 { 128 if (max<canvas[i][j]) 129 { 130 max = canvas[i][j];//找最值,更新蛇尾 131 oldTail_i = i; 132 oldTail_j = j; 133 } 134 if (canvas[i][j]==2)//记录旧蛇头 135 { 136 oldHead_i = i; 137 oldHead_j = j; 138 } 139 } 140 141 int newHead_i,newHead_j; 142 143 if (moveDirection==1) // 向上移动 144 { 145 newHead_i = oldHead_i-1; 146 newHead_j = oldHead_j; 147 } 148 if (moveDirection==2) // 向下移动 149 { 150 newHead_i = oldHead_i+1; 151 newHead_j = oldHead_j; 152 } 153 if (moveDirection==3) // 向左移动 154 { 155 newHead_i = oldHead_i; 156 newHead_j = oldHead_j-1; 157 } 158 if (moveDirection==4) // 向右移动 159 { 160 newHead_i = oldHead_i; 161 newHead_j = oldHead_j+1; 162 } 163 } 164 判断游戏结束:只看新蛇头的位置,碰到自身(>0),或者碰到墙壁(-1) 165 if (canvas[newHead_i][newHead_j]>0 || canvas[newHead_i][newHead_j]==-1) 166 { 167 printf("游戏失败!\n"); 168 Sleep(2000); 169 system("pause"); 170 exit(0); 171 } 172 吃食物后,蛇身变长,出现新食物 173 if (canvas[newHead_i][newHead_j]==-2) 174 { 175 canvas[food_x][food_y] = 0; 176 // 产生一个新的食物 177 food_x = rand()%(High-5) + 2; 178 food_y = rand()%(Width-5) + 2; 179 canvas[food_x][food_y] = -2; 180 // 原来的旧蛇尾留着,长度自动+1 181 } 182 183
第四章 easyX总结
1 EasyX常用函数 2 3 cleardevice();//用背景色清空屏幕 4 initgraph(width,height) ;//初始化绘图窗口 5 closegraph();//关窗口 6 7 RGB(byRed,byGreen,byBlue);//RGB三原色的值设颜色 8 getbkcolor();//获取当前背景色 9 setbkcolor();//设置背景颜色 10 getbkmode();//获取当前文字背景样式 11 setbkmode();//设置文字背景 12 getfillcolor();//获取填充颜色 13 setfillcolor();//设置填充颜色,可以填写BALCK,WHITE,BLUE,GREEN,RED,BROWN,YELLOW 14 setlinecolor();//设置线条颜色,填写内容同上 15 setcolor();//设置前景色 16 setfillstyle();//设置填充样式 17 void setfillstyle( 18 int style, 19 long hatch = NULL, 20 IMAGE* ppattern = NULL 21 ); 22 style可以填写 23 Macro Value Description 24 BS_SOLID 0 全填 25 BS_NULL 1 不填充 26 BS_HATCHED 2 图案填充 27 BS_PATTERN 3 自定义填充样式hatch 28 BS_DIBPATTERN 5 自定义图像填充 29 30 setlinestyle();//设置线条样式 31 Value Description 32 PS_SOLID The line is solid. 33 PS_DASH Line is:------------ 34 PS_DOT Line is:············ 35 PS_DASHDOT Line is:-·-·-·-·-·-· 36 PS_DASHDOTDOT Line is:-··-··-··-·· 37 38 circle( x,y, radius);//画一个坐标(x,y)半径为radius的圆 39 line(x1,y1,x2,y2);//画直线(x1,y1),(x2,y2)为直线的两端点的线 40 putpixel(x,y,c);//画点(x,y),像素的颜色为c 41 solidrectangle(x1,y1,x2,y2);//画填充矩形,(x1,y1)为左上角,(x2,y2)右下角 42 43 对文字的操作 44 gettextcolor();//获取文字颜色 45 settextcolor(COLORREF color);//设置文字颜色,BLUE等 46 47 对图片的操作 48 { 49 initgraph(640, 480); 50 IMAGE img(200, 200);//创建200*200的图片对象 51 SetWorkingImage(&img);//用于设置当前绘图设备 52 line(0, 100, 200, 100); 53 line(100, 0, 100, 200); 54 circle(100, 100, 50);//绘制一个图案 55 SetWorkingImage();//设置绘图对象到窗口 56 putimage(220, 140, &img);//显示图片对象到窗口 57 _getch(); 58 closegraph();}
1 Sleep有明显画面闪烁 2 用 BeginBatchDraw();FlushBatchDraw();EndBatchDraw(); 3 BeginBatchDraw();//开始批量绘图 4 while (1) 5 { 6 FlushBatchDraw();//执行批量绘制 7 8 // 延时 9 Sleep(3); 10 } 11 EndBatchDraw();//结束批量绘制
调用outtextxy()函数会报错
原因:Unicode编码,不论中文还是英文都是用2个字节表示;
解决方法:项目属性——字符集———使用多字节字符集
二、代码实现
第三章
3.2消砖块
1 #pragma warning(disable:4996); 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <conio.h> 5 #include <windows.h> 6 7 #define High 15 // 游戏画面尺寸 8 #define Width 20 9 10 // 全局变量 11 int ball_x, ball_y; // 小球的坐标 12 int ball_vx, ball_vy; // 小球的速度 13 int position_x, position_y; // 挡板中心坐标 14 int ridus; // 挡板半径大小 15 int left, right; // 挡板左右位置 16 int canvas[High][Width] = { 0 }; // 二维数组存储游戏画布中对应的元素 17 // 0为空格,1为小球O,2为挡板*,3为方块# 18 19 void gotoxy(int x, int y) //光标移动到(x,y)位置 20 { 21 HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); 22 COORD pos; 23 pos.X = x; 24 pos.Y = y; 25 SetConsoleCursorPosition(handle, pos); 26 } 27 28 void startup() // 数据初始化 29 { 30 ridus = 5; 31 position_x = High - 1; 32 position_y = Width / 2; 33 left = position_y - ridus; 34 right = position_y + ridus; 35 36 ball_x = position_x - 1; 37 ball_y = position_y; 38 ball_vx = -1; 39 ball_vy = 1; 40 canvas[ball_x][ball_y] = 1; 41 42 int k, i; 43 for (k = left;k <= right;k++) // 挡板 44 canvas[position_x][k] = 2; 45 46 for (k = 0;k < Width;k++) // 加几排砖块 47 for (i = 0;i < High / 4;i++) 48 canvas[i][k] = 3; 49 } 50 51 void show() // 显示画面 52 { 53 gotoxy(0, 0); // 光标移动到原点位置,以下重画清屏 54 int i, j; 55 for (i = 0;i < High;i++) 56 { 57 for (j = 0;j < Width;j++) 58 { 59 if (canvas[i][j] == 0) 60 printf(" "); // 输出空格 61 else if (canvas[i][j] == 1) 62 printf("0"); // 输出小球0 63 else if (canvas[i][j] == 2) 64 printf("*"); // 输出挡板* 65 else if (canvas[i][j] == 3) 66 printf("#"); // 输出砖块# 67 } 68 printf("|\n"); // 显示右边界 69 } 70 for (j = 0;j < Width;j++) 71 printf("-"); // 显示下边界 72 printf("\n"); 73 } 74 75 void updateWithoutInput() // 与用户输入无关的更新 76 { 77 if (ball_x == High - 2) 78 { 79 if ((ball_y >= left) && (ball_y <= right)) // 被挡板挡住 80 { 81 } 82 else // 没有被挡板挡住 83 { 84 printf("游戏失败\n"); 85 system("pause"); 86 exit(0); 87 } 88 } 89 90 static int speed = 0; 91 if (speed < 7) 92 speed++; 93 if (speed == 7) 94 { 95 speed = 0; 96 97 canvas[ball_x][ball_y] = 0; 98 // 更新小球坐标 99 ball_x = ball_x + ball_vx; 100 ball_y = ball_y + ball_vy; 101 canvas[ball_x][ball_y] = 1; 102 103 // 碰到边界后反弹 104 if ((ball_x == 0) || (ball_x == High - 2)) 105 ball_vx = -ball_vx; 106 if ((ball_y == 0) || (ball_y == Width - 1)) 107 ball_vy = -ball_vy; 108 109 // 碰到砖块后反弹 110 if (canvas[ball_x - 1][ball_y] == 3) 111 { 112 ball_vx = -ball_vx; 113 canvas[ball_x - 1][ball_y] = 0; 114 printf("\a"); 115 } 116 } 117 } 118 119 void updateWithInput() // 与用户输入有关的更新 120 { 121 char input; 122 if (kbhit()) // 判断是否有输入 123 { 124 input = getch(); // 根据用户的不同输入来移动,不必输入回车 125 if (input == 'a' && left > 0) 126 { 127 canvas[position_x][right] = 0; 128 position_y--; // 位置左移 129 left = position_y - ridus; 130 right = position_y + ridus; 131 canvas[position_x][left] = 2; 132 } 133 if (input == 'd' && right < Width - 1) 134 { 135 canvas[position_x][left] = 0; 136 position_y++; // 位置右移 137 left = position_y - ridus; 138 right = position_y + ridus; 139 canvas[position_x][right] = 2; 140 } 141 } 142 } 143 144 int main() 145 { 146 startup(); // 数据初始化 147 while (1) // 游戏循环执行 148 { 149 show(); // 显示画面 150 updateWithoutInput(); // 与用户输入无关的更新 151 updateWithInput(); // 与用户输入有关的更新 152 } 153 return 0; 154 }
3.3空战游戏
1 #pragma warning(disable:4996); 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <conio.h> 5 #include <windows.h> 6 7 #define High 15 // 游戏画面尺寸 8 #define Width 25 9 #define EnemyNum 5 // 敌机个数 10 11 // 全局变量 12 int position_x, position_y; // 飞机位置 13 int enemy_x[EnemyNum], enemy_y[EnemyNum]; // 敌机位置 14 int canvas[High][Width] = { 0 }; // 二维数组存储游戏画布中对应的元素 15 // 0为空格,1为飞机*,2为子弹|,3为敌机@ 16 int score; // 得分 17 int BulletWidth; // 子弹宽度 18 int EnemyMoveSpeed; // 敌机移动速度 19 20 void gotoxy(int x, int y) //光标移动到(x,y)位置 21 { 22 HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); 23 COORD pos; 24 pos.X = x; 25 pos.Y = y; 26 SetConsoleCursorPosition(handle, pos); 27 } 28 29 void startup() // 数据初始化 30 { 31 position_x = High - 1; 32 position_y = Width / 2; 33 canvas[position_x][position_y] = 1; 34 int k; 35 for (k = 0;k < EnemyNum;k++) 36 { 37 enemy_x[k] = rand() % 2; 38 enemy_y[k] = rand() % Width; 39 canvas[enemy_x[k]][enemy_y[k]] = 3; 40 } 41 score = 0; 42 BulletWidth = 0; 43 EnemyMoveSpeed = 20; 44 } 45 46 void show() // 显示画面 47 { 48 gotoxy(0, 0); // 光标移动到原点位置,以下重画清屏 49 int i, j; 50 for (i = 0;i < High;i++) 51 { 52 for (j = 0;j < Width;j++) 53 { 54 if (canvas[i][j] == 0) 55 printf(" "); // 输出空格 56 else if (canvas[i][j] == 1) 57 printf("*"); // 输出飞机* 58 else if (canvas[i][j] == 2) 59 printf("|"); // 输出子弹| 60 else if (canvas[i][j] == 3) 61 printf("@"); // 输出飞机@ 62 } 63 printf("\n"); 64 } 65 printf("得分:%d\n", score); 66 Sleep(20); 67 } 68 69 void updateWithoutInput() // 与用户输入无关的更新 70 { 71 int i, j, k; 72 for (i = 0;i < High;i++) 73 { 74 for (j = 0;j < Width;j++) 75 { 76 if (canvas[i][j] == 2) 77 { 78 for (k = 0;k < EnemyNum;k++) 79 { 80 if ((i == enemy_x[k]) && (j == enemy_y[k])) // 子弹击中敌机 81 { 82 score++; // 分数加1 83 if (score % 5 == 0 && EnemyMoveSpeed > 3) // 达到一定积分后,敌机变快 84 EnemyMoveSpeed--; 85 if (score % 5 == 0) // 达到一定积分后,子弹变厉害 86 BulletWidth++; 87 canvas[enemy_x[k]][enemy_y[k]] = 0; 88 enemy_x[k] = rand() % 2; // 产生新的飞机 89 enemy_y[k] = rand() % Width; 90 canvas[enemy_x[k]][enemy_y[k]] = 3; 91 canvas[i][j] = 0; // 子弹消失 92 } 93 } 94 // 子弹向上移动 95 canvas[i][j] = 0; 96 if (i > 0) 97 canvas[i - 1][j] = 2; 98 } 99 } 100 } 101 102 static int speed = 0; 103 if (speed < EnemyMoveSpeed) 104 speed++; 105 106 for (k = 0;k < EnemyNum;k++) 107 { 108 if ((position_x == enemy_x[k]) && (position_y == enemy_y[k])) // 敌机撞到我机 109 { 110 printf("失败!\n"); 111 Sleep(3000); 112 system("pause"); 113 exit(0); 114 } 115 116 if (enemy_x[k] > High) // 敌机跑出显示屏幕 117 { 118 canvas[enemy_x[k]][enemy_y[k]] = 0; 119 enemy_x[k] = rand() % 2; // 产生新的飞机 120 enemy_y[k] = rand() % Width; 121 canvas[enemy_x[k]][enemy_y[k]] = 3; 122 score--; // 减分 123 } 124 125 if (speed == EnemyMoveSpeed) 126 { 127 // 敌机下落 128 for (k = 0;k < EnemyNum;k++) 129 { 130 canvas[enemy_x[k]][enemy_y[k]] = 0; 131 enemy_x[k]++; 132 speed = 0; 133 canvas[enemy_x[k]][enemy_y[k]] = 3; 134 } 135 } 136 } 137 } 138 139 void updateWithInput() // 与用户输入有关的更新 140 { 141 char input; 142 if (kbhit()) // 判断是否有输入 143 { 144 input = getch(); // 根据用户的不同输入来移动,不必输入回车 145 if (input == 'a' && position_y > 0) 146 { 147 canvas[position_x][position_y] = 0; 148 position_y--; // 位置左移 149 canvas[position_x][position_y] = 1; 150 } 151 else if (input == 'd' && position_y < Width - 1) 152 { 153 canvas[position_x][position_y] = 0; 154 position_y++; // 位置右移 155 canvas[position_x][position_y] = 1; 156 } 157 else if (input == 'w') 158 { 159 canvas[position_x][position_y] = 0; 160 position_x--; // 位置上移 161 canvas[position_x][position_y] = 1; 162 } 163 else if (input == 's') 164 { 165 canvas[position_x][position_y] = 0; 166 position_x++; // 位置下移 167 canvas[position_x][position_y] = 1; 168 } 169 else if (input == ' ') // 发射子弹 170 { 171 int left = position_y - BulletWidth; 172 int right = position_y + BulletWidth; 173 if (left < 0) 174 left = 0; 175 if (right > Width - 1) 176 right = Width - 1; 177 int k; 178 for (k = left;k <= right;k++) // 发射闪弹 179 canvas[position_x - 1][k] = 2; // 发射子弹的初始位置在飞机的正上方 180 } 181 } 182 } 183 184 int main() 185 { 186 startup(); // 数据初始化 187 while (1) // 游戏循环执行 188 { 189 show(); // 显示画面 190 updateWithoutInput(); // 与用户输入无关的更新 191 updateWithInput(); // 与用户输入有关的更新 192 } 193 return 0; 194 }
3.4贪吃蛇
1 #pragma warning(disable:4996); 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <conio.h> 5 #include <windows.h> 6 7 #define High 20 // 游戏画面尺寸 8 #define Width 30 9 10 // 全局变量 11 int moveDirection; // 小蛇移动位置,上下左右分别用1,2,3,4表示 12 int food_x, food_y; // 食物的位置 13 int canvas[High][Width] = { 0 }; // 二维数组存储游戏画布中对应的元素 14 // 0为空格0,-1为边框#,-2为食物F,1为蛇头@,大于1的正数为蛇身* 15 16 void gotoxy(int x, int y) //光标移动到(x,y)位置 17 { 18 HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); 19 COORD pos; 20 pos.X = x; 21 pos.Y = y; 22 SetConsoleCursorPosition(handle, pos); 23 } 24 25 // 移动小蛇 26 // 第一步扫描数组canvas所有元素,找到正数元素都+1 27 // 找到最大元素(即蛇尾巴),把其变为0 28 // 找到=2的元素(即蛇头),再根据输出的上下左右方向,把对应的另一个像素值设为1(新蛇头) 29 void moveSnakeByDirection() 30 { 31 int i, j; 32 for (i = 1;i < High - 1;i++) 33 for (j = 1;j < Width - 1;j++) 34 if (canvas[i][j] > 0) 35 canvas[i][j]++; 36 37 int oldTail_i, oldTail_j, oldHead_i, oldHead_j; 38 int max = 0; 39 40 for (i = 1;i < High - 1;i++) 41 for (j = 1;j < Width - 1;j++) 42 if (canvas[i][j] > 0) 43 { 44 if (max < canvas[i][j]) 45 { 46 max = canvas[i][j]; 47 oldTail_i = i; 48 oldTail_j = j; 49 } 50 if (canvas[i][j] == 2) 51 { 52 oldHead_i = i; 53 oldHead_j = j; 54 } 55 } 56 57 int newHead_i, newHead_j; 58 59 if (moveDirection == 1) // 向上移动 60 { 61 newHead_i = oldHead_i - 1; 62 newHead_j = oldHead_j; 63 } 64 if (moveDirection == 2) // 向下移动 65 { 66 newHead_i = oldHead_i + 1; 67 newHead_j = oldHead_j; 68 } 69 if (moveDirection == 3) // 向左移动 70 { 71 newHead_i = oldHead_i; 72 newHead_j = oldHead_j - 1; 73 } 74 if (moveDirection == 4) // 向右移动 75 { 76 newHead_i = oldHead_i; 77 newHead_j = oldHead_j + 1; 78 } 79 80 // 新蛇头如果吃到食物 81 if (canvas[newHead_i][newHead_j] == -2) 82 { 83 canvas[food_x][food_y] = 0; 84 // 产生一个新的食物 85 food_x = rand() % (High - 5) + 2; 86 food_y = rand() % (Width - 5) + 2; 87 canvas[food_x][food_y] = -2; 88 89 // 原来的旧蛇尾留着,长度自动+1 90 } 91 else // 否则,原来的旧蛇尾减掉,保持长度不变 92 canvas[oldTail_i][oldTail_j] = 0; 93 94 // 是否小蛇和自身撞,或者和边框撞,游戏失败 95 if (canvas[newHead_i][newHead_j] > 0 || canvas[newHead_i][newHead_j] == -1) 96 { 97 printf("游戏失败!\n"); 98 Sleep(2000); 99 system("pause"); 100 exit(0); 101 } 102 else 103 canvas[newHead_i][newHead_j] = 1; 104 } 105 106 void startup() // 数据初始化 107 { 108 int i, j; 109 110 // 初始化边框 111 for (i = 0;i < High;i++) 112 { 113 canvas[i][0] = -1; 114 canvas[i][Width - 1] = -1; 115 } 116 for (j = 0;j < Width;j++) 117 { 118 canvas[0][j] = -1; 119 canvas[High - 1][j] = -1; 120 } 121 122 // 初始化蛇头位置 123 canvas[High / 2][Width / 2] = 1; 124 // 初始化蛇身,画布中元素值分别为2,3,4,5.... 125 for (i = 1;i <= 4;i++) 126 canvas[High / 2][Width / 2 - i] = i + 1; 127 128 // 初始小蛇向右移动 129 moveDirection = 4; 130 131 food_x = rand() % (High - 5) + 2; 132 food_y = rand() % (Width - 5) + 2; 133 canvas[food_x][food_y] = -2; 134 } 135 136 void show() // 显示画面 137 { 138 gotoxy(0, 0); // 光标移动到原点位置,以下重画清屏 139 int i, j; 140 for (i = 0;i < High;i++) 141 { 142 for (j = 0;j < Width;j++) 143 { 144 if (canvas[i][j] == 0) 145 printf(" "); // 输出空格 146 else if (canvas[i][j] == -1) 147 printf("#"); // 输出边框# 148 else if (canvas[i][j] == 1) 149 printf("@"); // 输出蛇头@ 150 else if (canvas[i][j] > 1) 151 printf("*"); // 输出蛇身* 152 else if (canvas[i][j] == -2) 153 printf("F"); // 输出食物F 154 } 155 printf("\n"); 156 } 157 Sleep(100); 158 } 159 160 void updateWithoutInput() // 与用户输入无关的更新 161 { 162 moveSnakeByDirection(); 163 } 164 165 void updateWithInput() // 与用户输入有关的更新 166 { 167 char input; 168 if (kbhit()) // 判断是否有输入 169 { 170 input = getch(); // 根据用户的不同输入来移动,不必输入回车 171 if (input == 'a') 172 { 173 moveDirection = 3; // 位置左移 174 moveSnakeByDirection(); 175 } 176 else if (input == 'd') 177 { 178 moveDirection = 4; // 位置右移 179 moveSnakeByDirection(); 180 } 181 else if (input == 'w') 182 { 183 moveDirection = 1; // 位置上移 184 moveSnakeByDirection(); 185 } 186 else if (input == 's') 187 { 188 moveDirection = 2; // 位置下移 189 moveSnakeByDirection(); 190 } 191 } 192 } 193 194 int main() 195 { 196 startup(); // 数据初始化 197 while (1) // 游戏循环执行 198 { 199 show(); // 显示画面 200 updateWithoutInput(); // 与用户输入无关的更新 201 updateWithInput(); // 与用户输入有关的更新 202 } 203 return 0; 204 }
第四章
4.2多球反弹
1 #pragma warning(disable:4996); 2 3 #include <graphics.h> 4 #include <conio.h> 5 #include <math.h> 6 #define High 480 // 游戏画面尺寸 7 #define Width 640 8 #define BallNum 15 // 小球个数 9 10 int main() 11 { 12 float ball_x[BallNum], ball_y[BallNum]; // 小球的坐标 13 float ball_vx[BallNum], ball_vy[BallNum]; // 小球的速度 14 float radius; // 小球的半径 15 int i, j; 16 17 radius = 20; 18 19 for (i = 0;i < BallNum;i++) // 随机小球的位置与速度 20 { 21 ball_x[i] = rand() % int(Width - 4 * radius) + 2 * radius; 22 ball_y[i] = rand() % int(High - 4 * radius) + 2 * radius; 23 ball_vx[i] = (rand() % 2) * 2 - 1; 24 ball_vy[i] = (rand() % 2) * 2 - 1; 25 } 26 27 initgraph(Width, High); 28 BeginBatchDraw(); 29 30 while (1) 31 { 32 // 绘制黑线、黑色填充的圆 33 setcolor(BLACK); 34 setfillcolor(BLACK); 35 for (i = 0;i < BallNum;i++) 36 fillcircle(ball_x[i], ball_y[i], radius); 37 38 // 更新小圆坐标 39 for (i = 0;i < BallNum;i++) 40 { 41 ball_x[i] = ball_x[i] + ball_vx[i]; 42 ball_y[i] = ball_y[i] + ball_vy[i]; 43 44 // 把超出边界的小球拉回来 45 if (ball_x[i] < radius) 46 ball_x[i] = radius; 47 if (ball_y[i] < radius) 48 ball_y[i] = radius; 49 if (ball_x[i] > Width - radius) 50 ball_x[i] = Width - radius; 51 if (ball_y[i] > High - radius) 52 ball_y[i] = High - radius; 53 } 54 55 // 判断是否和墙壁碰撞 56 for (i = 0;i < BallNum;i++) 57 { 58 if ((ball_x[i] <= radius) || (ball_x[i] >= Width - radius)) 59 ball_vx[i] = -ball_vx[i]; 60 if ((ball_y[i] <= radius) || (ball_y[i] >= High - radius)) 61 ball_vy[i] = -ball_vy[i]; 62 } 63 64 float minDistances2[BallNum][2]; // 记录某个小球,距离它最近的小球的距离,这个小球的下标 65 for (i = 0;i < BallNum;i++) 66 { 67 minDistances2[i][0] = 9999999; 68 minDistances2[i][1] = -1; 69 } 70 71 // 求解所有小球两两之间的距离平方 72 for (i = 0;i < BallNum;i++) 73 { 74 for (j = 0;j < BallNum;j++) 75 { 76 if (i != j) // 自己和自己不需要比 77 { 78 float dist2; 79 dist2 = (ball_x[i] - ball_x[j]) * (ball_x[i] - ball_x[j]) 80 + (ball_y[i] - ball_y[j]) * (ball_y[i] - ball_y[j]); 81 if (dist2 < minDistances2[i][0]) 82 { 83 minDistances2[i][0] = dist2; 84 minDistances2[i][1] = j; 85 } 86 } 87 } 88 } 89 90 // 判断球之间是否碰撞 91 for (i = 0;i < BallNum;i++) 92 { 93 if (minDistances2[i][0] <= 4 * radius * radius) // 最小距离小于阈值,发生碰撞 94 { 95 j = minDistances2[i][1]; 96 // 交换速度 97 int temp; 98 temp = ball_vx[i]; ball_vx[i] = ball_vx[j]; ball_vx[j] = temp; 99 temp = ball_vy[i]; ball_vy[i] = ball_vy[j]; ball_vy[j] = temp; 100 101 minDistances2[j][0] = 999999999; // 避免交换两次速度,又回去了 102 minDistances2[j][1] = -1; 103 } 104 } 105 106 // 绘制黄线、绿色填充的圆 107 setcolor(YELLOW); 108 setfillcolor(GREEN); 109 for (i = 0;i < BallNum;i++) 110 fillcircle(ball_x[i], ball_y[i], radius); 111 112 FlushBatchDraw(); 113 114 // 延时 115 Sleep(3); 116 } 117 EndBatchDraw(); 118 closegraph(); 119 return 0; 120 }
4.3实时钟表
1 #pragma warning(disable:4996); 2 3 #include <graphics.h> 4 #include <conio.h> 5 #include <math.h> 6 7 #define High 480 // 游戏画面尺寸 8 #define Width 640 9 #define PI 3.14159 10 11 int main() 12 { 13 initgraph(Width, High); // 初始化 640 x 480 的绘图窗口 14 int center_x, center_y; // 中心点的坐标,也是表的中心 15 center_x = Width / 2; 16 center_y = High / 2; 17 int secondLength = Width / 5; // 秒针的长度 18 int minuteLength = Width / 6; // 分针的长度 19 int hourLength = Width / 7; // 时针的长度 20 21 int secondEnd_x, secondEnd_y; // 秒针的终点 22 int minuteEnd_x, minuteEnd_y; // 分针的终点 23 int hourEnd_x, hourEnd_y; // 时针的终点 24 float secondAngle; // 秒钟对应的角度 25 float minuteAngle; // 分钟对应的角度 26 float hourAngle; // 时钟对应的角度 27 28 SYSTEMTIME ti; // 定义变量保存当前时间 29 30 BeginBatchDraw(); 31 while (1) 32 { 33 // 绘制一个简单的表盘 34 setlinestyle(PS_SOLID, 1); 35 setcolor(WHITE); 36 circle(center_x, center_y, Width / 4); 37 38 // 画刻度 39 int x, y, i; 40 for (i = 0; i < 60; i++) 41 { 42 x = center_x + int(Width / 4.3 * sin(PI * 2 * i / 60)); 43 y = center_y + int(Width / 4.3 * cos(PI * 2 * i / 60)); 44 45 if (i % 15 == 0) 46 bar(x - 5, y - 5, x + 5, y + 5); 47 else if (i % 5 == 0) 48 circle(x, y, 3); 49 else 50 putpixel(x, y, WHITE); 51 } 52 53 outtextxy(center_x - 25, center_y + Width / 6, "我的时钟"); 54 55 GetLocalTime(&ti); // 获取当前时间 56 // 秒钟角度变化 57 secondAngle = ti.wSecond * 2 * PI / 60; // 一圈一共2*PI,一圈60秒,一秒钟秒钟走过的角度为2*PI/60 58 // 分钟角度变化 59 minuteAngle = ti.wMinute * 2 * PI / 60 + secondAngle / 60; // 一圈一共2*PI,一圈60分,一分钟分钟走过的角度为2*PI/60 60 // 时钟角度变化 61 hourAngle = ti.wHour * 2 * PI / 12 + minuteAngle / 12; // 一圈一共2*PI,一圈12小时,一小时时钟走过的角度为2*PI/12 62 // 由角度决定的秒针端点坐标 63 secondEnd_x = center_x + secondLength * sin(secondAngle); 64 secondEnd_y = center_y - secondLength * cos(secondAngle); 65 66 // 由角度决定的分针端点坐标 67 minuteEnd_x = center_x + minuteLength * sin(minuteAngle); 68 minuteEnd_y = center_y - minuteLength * cos(minuteAngle); 69 70 // 由角度决定的时针端点坐标 71 hourEnd_x = center_x + hourLength * sin(hourAngle); 72 hourEnd_y = center_y - hourLength * cos(hourAngle); 73 74 setlinestyle(PS_SOLID, 2); 75 setcolor(YELLOW); 76 line(center_x, center_y, secondEnd_x, secondEnd_y); // 画秒针 77 78 setlinestyle(PS_SOLID, 5); 79 setcolor(BLUE); 80 line(center_x, center_y, minuteEnd_x, minuteEnd_y); // 画分针 81 82 setlinestyle(PS_SOLID, 10); 83 setcolor(RED); 84 line(center_x, center_y, hourEnd_x, hourEnd_y); // 画时针 85 86 FlushBatchDraw(); 87 Sleep(10); 88 89 setcolor(BLACK); 90 setlinestyle(PS_SOLID, 2); 91 line(center_x, center_y, secondEnd_x, secondEnd_y); // 隐藏前一帧的秒针 92 setlinestyle(PS_SOLID, 5); 93 line(center_x, center_y, minuteEnd_x, minuteEnd_y); // 隐藏前一帧的分针 94 setlinestyle(PS_SOLID, 10); 95 line(center_x, center_y, hourEnd_x, hourEnd_y); // 隐藏前一帧的时针 96 } 97 98 EndBatchDraw(); 99 getch(); // 按任意键继续 100 closegraph(); // 关闭绘图窗口 101 return 0; 102 }
4.4Easy实现消砖块
1 #pragma warning(disable:4996); 2 3 #include <conio.h> 4 #include <graphics.h> 5 6 #define High 480 // 游戏画面尺寸 7 #define Width 640 8 #define Brick_num 10 // 砖块个数 9 10 // 全局变量 11 int ball_x, ball_y; // 小球的坐标 12 int ball_vx, ball_vy; // 小球的速度 13 int radius; // 小球的半径 14 int bar_x, bar_y; // 挡板中心坐标 15 int bar_high, bar_width; // 挡板的高度和宽度 16 int bar_left, bar_right, bar_top, bar_bottom; // 挡板的上下左右位置坐标 17 18 int isBrickExisted[Brick_num]; // 每个砖块是否存在,1为存在,0为没有了 19 int brick_high, brick_width; // 每个砖块的高度和宽度 20 21 void startup() // 数据初始化 22 { 23 ball_x = Width / 2; 24 ball_y = High / 2; 25 ball_vx = 1; 26 ball_vy = 1; 27 radius = 20; 28 29 bar_high = High / 20; 30 bar_width = Width / 2; 31 bar_x = Width / 2; 32 bar_y = High - bar_high / 2; 33 bar_left = bar_x - bar_width / 2; 34 bar_right = bar_x + bar_width / 2; 35 bar_top = bar_y - bar_high / 2; 36 bar_bottom = bar_y + bar_high / 2; 37 38 brick_width = Width / Brick_num; 39 brick_high = High / Brick_num; 40 41 int i; 42 for (i = 0;i < Brick_num;i++) 43 isBrickExisted[i] = 1; 44 45 initgraph(Width, High); 46 BeginBatchDraw(); 47 } 48 49 void clean() // 消除画面 50 { 51 setcolor(BLACK); 52 setfillcolor(BLACK); 53 fillcircle(ball_x, ball_y, radius); // 绘制黑线、黑色填充的圆 54 bar(bar_left, bar_top, bar_right, bar_bottom); // 绘制黑线、黑色填充的挡板 55 56 int i, brick_left, brick_right, brick_top, brick_bottom; 57 for (i = 0;i < Brick_num;i++) 58 { 59 brick_left = i * brick_width; 60 brick_right = brick_left + brick_width; 61 brick_top = 0; 62 brick_bottom = brick_high; 63 if (!isBrickExisted[i]) // 砖块没有了,绘制黑色 64 fillrectangle(brick_left, brick_top, brick_right, brick_bottom); 65 } 66 } 67 68 void show() // 显示画面 69 { 70 setcolor(YELLOW); 71 setfillcolor(GREEN); 72 fillcircle(ball_x, ball_y, radius); // 绘制黄线、绿色填充的圆 73 bar(bar_left, bar_top, bar_right, bar_bottom); // 绘制黄线、绿色填充的挡板 74 75 int i, brick_left, brick_right, brick_top, brick_bottom; 76 77 for (i = 0;i < Brick_num;i++) 78 { 79 brick_left = i * brick_width; 80 brick_right = brick_left + brick_width; 81 brick_top = 0; 82 brick_bottom = brick_high; 83 84 if (isBrickExisted[i]) // 砖块存在,绘制砖块 85 { 86 setcolor(WHITE); 87 setfillcolor(RED); 88 fillrectangle(brick_left, brick_top, brick_right, brick_bottom); // 绘制砖块 89 } 90 } 91 92 FlushBatchDraw(); 93 // 延时 94 Sleep(3); 95 } 96 97 void updateWithoutInput() // 与用户输入无关的更新 98 { 99 // 挡板和小圆碰撞,小圆反弹 100 if (((ball_y + radius >= bar_top) && (ball_y + radius < bar_bottom - bar_high / 3)) 101 || ((ball_y - radius <= bar_bottom) && (ball_y - radius > bar_top - bar_high / 3))) 102 if ((ball_x >= bar_left) && (ball_x <= bar_right)) 103 ball_vy = -ball_vy; 104 105 // 更新小圆坐标 106 ball_x = ball_x + ball_vx; 107 ball_y = ball_y + ball_vy; 108 109 // 小圆和边界碰撞 110 if ((ball_x == radius) || (ball_x == Width - radius)) 111 ball_vx = -ball_vx; 112 if ((ball_y == radius) || (ball_y == High - radius)) 113 ball_vy = -ball_vy; 114 115 // 判断小圆是否和某个砖块碰撞 116 int i, brick_left, brick_right, brick_bottom; 117 for (i = 0;i < Brick_num;i++) 118 { 119 if (isBrickExisted[i]) // 砖块存在,才判断 120 { 121 brick_left = i * brick_width; 122 brick_right = brick_left + brick_width; 123 brick_bottom = brick_high; 124 if ((ball_y == brick_bottom + radius) && (ball_x >= brick_left) && (ball_x <= brick_right)) 125 { 126 isBrickExisted[i] = 0; 127 ball_vy = -ball_vy; 128 } 129 } 130 } 131 } 132 133 void updateWithInput() // 与用户输入有关的更新 134 { 135 char input; 136 if (kbhit()) // 判断是否有输入 137 { 138 input = getch(); // 根据用户的不同输入来移动,不必输入回车 139 if (input == 'a' && bar_left > 0) 140 { 141 bar_x = bar_x - 15; // 位置左移 142 bar_left = bar_x - bar_width / 2; 143 bar_right = bar_x + bar_width / 2; 144 } 145 if (input == 'd' && bar_right < Width) 146 { 147 bar_x = bar_x + 15; // 位置右移 148 bar_left = bar_x - bar_width / 2; 149 bar_right = bar_x + bar_width / 2; 150 } 151 if (input == 'w' && bar_top > 0) 152 { 153 bar_y = bar_y - 15; // 位置左移 154 bar_top = bar_y - bar_high / 2; 155 bar_bottom = bar_y + bar_high / 2; 156 } 157 if (input == 's' && bar_bottom < High) 158 { 159 bar_y = bar_y + 15; // 位置右移 160 bar_top = bar_y - bar_high / 2; 161 bar_bottom = bar_y + bar_high / 2; 162 } 163 } 164 } 165 166 void gameover() 167 { 168 EndBatchDraw(); 169 closegraph(); 170 } 171 172 int main() 173 { 174 startup(); // 数据初始化 175 while (1) // 游戏循环执行 176 { 177 clean(); // 把之前绘制的内容取消 178 updateWithoutInput(); // 与用户输入无关的更新 179 updateWithInput(); // 与用户输入有关的更新 180 show(); // 显示新画面 181 } 182 gameover(); // 游戏结束、后续处理 183 return 0; 184 }
4.5鼠标交互的反弹器
1 #pragma warning(disable:4996); 2 #include <conio.h> 3 #include <graphics.h> 4 5 #define High 480 // 游戏画面尺寸 6 #define Width 640 7 #define Brick_num 10 // 砖块个数 8 9 // 全局变量 10 int ball_x, ball_y; // 小球的坐标 11 int ball_vx, ball_vy; // 小球的速度 12 int radius; // 小球的半径 13 int bar_x, bar_y; // 挡板中心坐标 14 int bar_high, bar_width; // 挡板的高度和宽度 15 int bar_left, bar_right, bar_top, bar_bottom; // 挡板的上下左右位置坐标 16 17 int isBrickExisted[Brick_num]; // 每个砖块是否存在,1为存在,0为没有了 18 int brick_high, brick_width; // 每个砖块的高度和宽度 19 20 void startup() // 数据初始化 21 { 22 ball_x = Width / 2; 23 ball_y = High / 2; 24 ball_vx = 1; 25 ball_vy = 1; 26 radius = 20; 27 28 bar_high = High / 20; 29 bar_width = Width / 2; 30 bar_x = Width / 2; 31 bar_y = High - bar_high / 2; 32 bar_left = bar_x - bar_width / 2; 33 bar_right = bar_x + bar_width / 2; 34 bar_top = bar_y - bar_high / 2; 35 bar_bottom = bar_y + bar_high / 2; 36 37 brick_width = Width / Brick_num; 38 brick_high = High / Brick_num; 39 40 int i; 41 for (i = 0;i < Brick_num;i++) 42 isBrickExisted[i] = 1; 43 44 initgraph(Width, High); 45 BeginBatchDraw(); 46 } 47 48 void clean() // 消除画面 49 { 50 setcolor(BLACK); 51 setfillcolor(BLACK); 52 fillcircle(ball_x, ball_y, radius); // 绘制黑线、黑色填充的圆 53 bar(bar_left, bar_top, bar_right, bar_bottom); // 绘制黑线、黑色填充的挡板 54 55 int i, brick_left, brick_right, brick_top, brick_bottom; 56 for (i = 0;i < Brick_num;i++) 57 { 58 brick_left = i * brick_width; 59 brick_right = brick_left + brick_width; 60 brick_top = 0; 61 brick_bottom = brick_high; 62 if (!isBrickExisted[i]) // 砖块没有了,绘制黑色 63 fillrectangle(brick_left, brick_top, brick_right, brick_bottom); 64 } 65 } 66 67 void show() // 显示画面 68 { 69 setcolor(YELLOW); 70 setfillcolor(GREEN); 71 fillcircle(ball_x, ball_y, radius); // 绘制黄线、绿色填充的圆 72 bar(bar_left, bar_top, bar_right, bar_bottom); // 绘制黄线、绿色填充的挡板 73 74 int i, brick_left, brick_right, brick_top, brick_bottom; 75 76 for (i = 0;i < Brick_num;i++) 77 { 78 brick_left = i * brick_width; 79 brick_right = brick_left + brick_width; 80 brick_top = 0; 81 brick_bottom = brick_high; 82 83 if (isBrickExisted[i]) // 砖块存在,绘制砖块 84 { 85 setcolor(WHITE); 86 setfillcolor(RED); 87 fillrectangle(brick_left, brick_top, brick_right, brick_bottom); // 绘制砖块 88 } 89 } 90 91 FlushBatchDraw(); 92 // 延时 93 Sleep(3); 94 } 95 96 void updateWithoutInput() // 与用户输入无关的更新 97 { 98 // 挡板和小圆碰撞,小圆反弹 99 if (((ball_y + radius >= bar_top) && (ball_y + radius < bar_bottom - bar_high / 3)) 100 || ((ball_y - radius <= bar_bottom) && (ball_y - radius > bar_top - bar_high / 3))) 101 if ((ball_x >= bar_left) && (ball_x <= bar_right)) 102 ball_vy = -ball_vy; 103 104 // 更新小圆坐标 105 ball_x = ball_x + ball_vx; 106 ball_y = ball_y + ball_vy; 107 108 // 小圆和边界碰撞 109 if ((ball_x == radius) || (ball_x == Width - radius)) 110 ball_vx = -ball_vx; 111 if ((ball_y == radius) || (ball_y == High - radius)) 112 ball_vy = -ball_vy; 113 114 // 判断小圆是否和某个砖块碰撞 115 int i, brick_left, brick_right, brick_bottom; 116 for (i = 0;i < Brick_num;i++) 117 { 118 if (isBrickExisted[i]) // 砖块存在,才判断 119 { 120 brick_left = i * brick_width; 121 brick_right = brick_left + brick_width; 122 brick_bottom = brick_high; 123 if ((ball_y == brick_bottom + radius) && (ball_x >= brick_left) && (ball_x <= brick_right)) 124 { 125 isBrickExisted[i] = 0; 126 ball_vy = -ball_vy; 127 } 128 } 129 } 130 } 131 132 void updateWithInput() // 与用户输入有关的更新 133 { 134 MOUSEMSG m; // 定义鼠标消息 135 if (MouseHit()) //这个函数用于检测当前是否有鼠标消息 136 { 137 m = GetMouseMsg(); // 获取一条鼠标消息 138 if (m.uMsg == WM_MOUSEMOVE) 139 { 140 // 鼠标移动时,挡板的位置等于鼠标所在的位置 141 bar_x = m.x; 142 bar_y = m.y; 143 bar_left = bar_x - bar_width / 2; 144 bar_right = bar_x + bar_width / 2; 145 bar_top = bar_y - bar_high / 2; 146 bar_bottom = bar_y + bar_high / 2; 147 } 148 else if (m.uMsg == WM_LBUTTONDOWN) 149 { 150 // 按下鼠标左键,初始化小球的位置为挡板上面中心 151 ball_x = bar_x; 152 ball_y = bar_top - radius - 3; 153 } 154 } 155 } 156 157 void gameover() 158 { 159 EndBatchDraw(); 160 closegraph(); 161 } 162 163 int main() 164 { 165 startup(); // 数据初始化 166 while (1) // 游戏循环执行 167 { 168 clean(); // 把之前绘制的内容取消 169 updateWithoutInput(); // 与用户输入无关的更新 170 updateWithInput(); // 与用户输入有关的更新 171 show(); // 显示新画面 172 } 173 gameover(); // 游戏结束、后续处理 174 return 0; 175 }