C语言用面向对象的思想写贪吃蛇
大概一年前这时候,接触C语言一个月,那时候知之甚少,对面向对象只觉”可远观而不可亵玩“,而且会看到很多言论说C语言就是面向过程的语言,C++就是面向对象的语言。不过,不记得什么时候在网上看到过一篇博文,大概是说如何优雅的写C语言。其中颇有印象的就是通过结构的函数指针模拟C++中的类。
今天粗略尝试了一下,写的是之前写过的贪吃蛇。的确,用面向对象的思维写让我的思维变的更加清晰,因为这个游戏(以及大多数显示事物)的天然属性就是对象。
此外,这次从最关键最核心的写起,如此写起来真是越写越轻松。因为如果关键部分写不出来,其他写的再好也没用。所以这次的顺序大概是:能移动的蛇--->能吃食物并变长--->能判断自己是否死亡--->其他修饰功能。
最后,调试无误后。在发布代码前写了写注释。发现自己现在命名编码规范了不少,所以注释也并不多。曾经看到一种观点就是,注释关键部分就足够了,最好的注释就是良好的命名习惯、编码风格和清晰的逻辑。
不过,由于没有太多的规划,都是自己在路上零散构思的,代码还是不够紧凑,逻辑还可以优化。
头文件
1 #ifndef _HEAD_H_ 2 #define _HEAD_H_ 3 #include <stdio.h> 4 #include <windows.h>// 5 int Score = -1; 6 typedef struct node1 7 { 8 int x; 9 int y; 10 struct node1 *next; 11 }SnakeBody; 12 typedef struct node2 13 { 14 int x; 15 int y;//location 16 char Status; 17 char Direction; 18 void (*MoveUp)(struct node2 *); 19 void (*MoveDown)(struct node2 *); 20 void (*MoveRight)(struct node2 *); 21 void (*MoveLeft)(struct node2 *); 22 void (*GrowUp)(struct node2 *); 23 int (*IsDie)(struct node2 *); 24 SnakeBody *next; 25 }SnakeHead; 26 void HideCursor() 27 { 28 CONSOLE_CURSOR_INFO cursor_info = { 1, 0 }; 29 SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info); 30 } 31 void SetPosition(int x, int y) 32 { 33 COORD pos; 34 HANDLE hOutput; 35 pos.X = x; 36 pos.Y = y; 37 hOutput = GetStdHandle(STD_OUTPUT_HANDLE); 38 SetConsoleCursorPosition(hOutput, pos); 39 } 40 void Print(SnakeHead *H) 41 { 42 SetPosition(H->x, H->y); 43 printf("@");//mean snake head 44 SnakeBody *temp = H->next; 45 while(NULL != temp) 46 { 47 SetPosition(temp->x, temp->y); 48 printf("+");//means snake body 49 temp = temp->next; 50 } 51 } 52 void Move(SnakeHead *H) 53 { 54 SnakeBody *temp = H->next; 55 while (NULL != temp->next->next) 56 { 57 temp = temp->next; 58 } 59 temp->next->next = H->next; 60 H->next = temp->next; 61 SetPosition(temp->next->x, temp->next->y); 62 printf(" "); 63 temp->next = NULL; 64 H->next->x = H->x; 65 H->next->y = H->y; 66 } 67 void _MoveUp(SnakeHead *H) 68 { 69 Move(H); 70 H->y--; 71 } 72 void _MoveDown(SnakeHead *H) 73 { 74 Move(H); 75 H->y++; 76 } 77 void _MoveRight(SnakeHead *H) 78 { 79 Move(H); 80 H->x++; 81 } 82 void _MoveLeft(SnakeHead *H) 83 { 84 Move(H); 85 H->x--; 86 } 87 void _GrowUp(SnakeHead *H) 88 { 89 SnakeBody *temp = (SnakeBody *)malloc(sizeof(SnakeBody)); 90 temp->x = H->x; 91 temp->y = H->y; 92 temp->next = H->next; 93 H->next = temp; 94 switch (H->Direction) 95 { 96 case 'U': 97 { 98 H->y--; 99 break; 100 } 101 case 'D': 102 { 103 H->y++; 104 break; 105 } 106 case 'L': 107 { 108 H->x--; 109 break; 110 } 111 case 'R': 112 { 113 H->x++; 114 break; 115 } 116 default: 117 { 118 break; 119 } 120 } 121 Print(H); 122 } 123 const int Left = 3; 124 const int Right = 34; 125 const int Top = 3; 126 const int Bottom = 27; 127 void BuildWall() 128 { 129 for (int x = Left; x <= Right; x++) 130 { 131 SetPosition(x, Top); 132 printf("-"); 133 SetPosition(x, Bottom); 134 printf("-"); 135 } 136 for (int y = Top; y <= Bottom; y++) 137 { 138 SetPosition(Left, y); 139 printf("|"); 140 SetPosition(Right, y); 141 printf("|"); 142 } 143 //some hint 144 SetPosition(15, 1); 145 printf("Snake"); 146 SetPosition(20, 2); 147 printf("zhaoyu"); 148 SetPosition(37, 7); 149 printf("F1:Slow Down"); 150 SetPosition(37, 9); 151 printf("F2:Speed Up"); 152 SetPosition(37, 11); 153 printf("F3:Speed Up++"); 154 SetPosition(37, 15); 155 printf("Score:"); 156 } 157 void CreateFood(int *x, int *y, SnakeHead *H) 158 { 159 int flag = 1; 160 int i = 0, j = 0; 161 //make sure in the wall && not on snake 162 SnakeBody *temp = H->next; 163 while (1 == flag) 164 { 165 i = rand()%30; 166 i += 4; 167 j = rand()%24; 168 j += 4; 169 flag = 0; 170 if (i == H->x && j == H->y) 171 { 172 flag = 1; 173 continue; 174 } 175 temp = H->next; 176 while (NULL != temp) 177 { 178 if (i == temp->x && j == temp->y) 179 { 180 flag = 1; 181 break; 182 } 183 temp = temp->next; 184 } 185 } 186 187 *x = i; 188 *y = j; 189 SetPosition(i, j); 190 printf("$");//dollar mead food! 191 SetPosition(45, 15); 192 //once food created ,score plus 1 193 printf("%d", ++Score); 194 } 195 int Die(SnakeHead *H) 196 { 197 //out the wall will die 198 if (H->x <= Left || H->x >= Right || H->y >= Bottom || H->y <= Top) 199 { 200 return 1;//die 201 } 202 // crush into itself will die 203 SnakeBody *temp = H->next; 204 while (NULL !=temp) 205 { 206 if (H->x == temp->x && H->y == temp->y) 207 { 208 return 1; 209 } 210 temp = temp->next; 211 } 212 // 0 mean not die 213 return 0; 214 } 215 #endif
源文件
1 #include "head.h" 2 3 4 void StartGame() 5 { 6 //Create a snake 7 SnakeBody *temp = (SnakeBody *)malloc(sizeof(SnakeBody)); 8 SnakeHead *Head = (SnakeHead *)malloc(sizeof(SnakeHead)); 9 Head->Direction = 'L'; 10 Head->Status = 1; 11 Head->x = 10; 12 Head->y = 10; 13 //assign some functions to snake 14 Head->MoveUp = _MoveUp; 15 Head->MoveDown = _MoveDown; 16 Head->MoveRight = _MoveRight; 17 Head->MoveLeft = _MoveLeft; 18 Head->GrowUp = _GrowUp; 19 Head->IsDie = Die; 20 temp->x = 11; 21 temp->y = 10; 22 temp->next = NULL; 23 Head->next = temp; 24 // create snake body 25 for (int i = 0; i < 5; i++) 26 { 27 SnakeBody *move = (SnakeBody *)malloc(sizeof(SnakeBody)); 28 move->x = temp->x + 1; 29 move->y = temp->y; 30 temp->next = move; 31 temp = move; 32 move->next = NULL; 33 } 34 //Init food 35 int Foodx = 0, Foody = 0; 36 CreateFood(&Foodx, &Foody, Head); 37 int Speed = 10; 38 while (1 == Head->Status) 39 { 40 //print the snake 41 Print(Head); 42 //judge is or not die 43 if((*Head->IsDie)(Head)) 44 { 45 break; 46 } 47 //get direction 48 if(GetAsyncKeyState(VK_UP) && 'D' != Head->Direction) 49 { 50 Head->Direction = 'U'; 51 } 52 else if (GetAsyncKeyState(VK_DOWN) && 'U' != Head->Direction) 53 { 54 Head->Direction = 'D'; 55 } 56 else if (GetAsyncKeyState(VK_RIGHT) && 'L' != Head->Direction) 57 { 58 Head->Direction = 'R'; 59 } 60 else if (GetAsyncKeyState(VK_LEFT) && 'R' != Head->Direction) 61 { 62 Head->Direction = 'L'; 63 } 64 //move 65 switch (Head->Direction) 66 { 67 case 'U': 68 { 69 //judge is there a food 70 if (Head->x == Foodx && Head->y - 1 == Foody) 71 { 72 (*Head->GrowUp)(Head); 73 CreateFood(&Foodx, &Foody, Head); 74 } 75 else//Just move 76 { 77 (*Head->MoveUp)(Head); 78 } 79 break; 80 } 81 case 'D': 82 { 83 if (Head->x == Foodx && Head->y + 1 == Foody) 84 { 85 (*Head->GrowUp)(Head); 86 CreateFood(&Foodx, &Foody, Head); 87 } 88 else 89 { 90 (*Head->MoveDown)(Head); 91 } 92 break; 93 } 94 case 'R': 95 { 96 if (Head->x + 1 == Foodx && Head->y == Foody) 97 { 98 (*Head->GrowUp)(Head); 99 CreateFood(&Foodx, &Foody, Head); 100 } 101 else 102 { 103 (*Head->MoveRight)(Head); 104 } 105 106 break; 107 } 108 case 'L': 109 { 110 if (Head->x - 1 == Foodx && Head->y == Foody) 111 { 112 (*Head->GrowUp)(Head); 113 CreateFood(&Foodx, &Foody, Head); 114 } 115 else 116 { 117 (*Head->MoveLeft)(Head); 118 } 119 break; 120 } 121 default: 122 { 123 break; 124 } 125 } 126 //control 127 if (GetAsyncKeyState(VK_F1) && Speed > 6) 128 { 129 Speed -= 5; 130 } 131 else if (GetAsyncKeyState(VK_F2) && Speed < 30) 132 { 133 Speed += 5; 134 } 135 else if (GetAsyncKeyState(VK_F3)) 136 { 137 Speed += 10; 138 } 139 if ('L' == Head->Direction ||'R' == Head->Direction) 140 { 141 Sleep(1000/Speed); 142 } 143 else 144 { 145 Sleep(2000/Speed); 146 } 147 } 148 } 149 int main(int argc, char const *argv[]) 150 { 151 //init 152 system("cls"); 153 system("mode con cols=50 lines=30");//no space around equal sign 154 BuildWall(); 155 HideCursor(); 156 StartGame(); 157 SetPosition(36, 20); 158 printf("Enter to exit!"); 159 getchar(); 160 return 0; 161 }
运行:
后记:
1、函数指针与函数名
函数名与函数指针都是一样的,即都是函数指针。函数名是一个函数指针常量,而函数指针是一个函数数指针变量。
一般的C语言书籍都不会谈及这些近乎奇淫技巧的东东。
2、GetAsyncKeyState函数
这个函数按下之后其状态会一直存在,我在写俄罗斯方块就大受其害。以下面代码为例。你只需要按一下F1就可以跳出循环了,而不是1000下。因为按下一次之后其状态就始终为真了,除非你按下别的键。
1 #include <stdio.h> 2 #include <windows.h> 3 int main(int argc, char const *argv[]) 4 { 5 int i = 0; 6 while (1) 7 { 8 if (GetAsyncKeyState(VK_F1)) 9 { 10 i++; 11 } 12 if (i >= 1000) 13 { 14 break; 15 } 16 } 17 printf("%d\n", i); 18 return 0; 19 }