C语言用面向对象的思想写贪吃蛇
大概一年前这时候,接触C语言一个月,那时候知之甚少,对面向对象只觉”可远观而不可亵玩“,而且会看到很多言论说C语言就是面向过程的语言,C++就是面向对象的语言。不过,不记得什么时候在网上看到过一篇博文,大概是说如何优雅的写C语言。其中颇有印象的就是通过结构的函数指针模拟C++中的类。
今天粗略尝试了一下,写的是之前写过的贪吃蛇。的确,用面向对象的思维写让我的思维变的更加清晰,因为这个游戏(以及大多数显示事物)的天然属性就是对象。
此外,这次从最关键最核心的写起,如此写起来真是越写越轻松。因为如果关键部分写不出来,其他写的再好也没用。所以这次的顺序大概是:能移动的蛇--->能吃食物并变长--->能判断自己是否死亡--->其他修饰功能。
最后,调试无误后。在发布代码前写了写注释。发现自己现在命名编码规范了不少,所以注释也并不多。曾经看到一种观点就是,注释关键部分就足够了,最好的注释就是良好的命名习惯、编码风格和清晰的逻辑。
不过,由于没有太多的规划,都是自己在路上零散构思的,代码还是不够紧凑,逻辑还可以优化。
头文件
#ifndef _HEAD_H_
#define _HEAD_H_
#include <stdio.h>
#include <windows.h>//
int Score = -;
typedef struct node1
{
int x;
int y;
struct node1 *next;
}SnakeBody;
typedef struct node2
{
int x;
int y;//location
char Status;
char Direction;
void (*MoveUp)(struct node2 *);
void (*MoveDown)(struct node2 *);
void (*MoveRight)(struct node2 *);
void (*MoveLeft)(struct node2 *);
void (*GrowUp)(struct node2 *);
int (*IsDie)(struct node2 *);
SnakeBody *next;
}SnakeHead;
void HideCursor()
{
CONSOLE_CURSOR_INFO cursor_info = { , };
SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info);
}
void SetPosition(int x, int y)
{
COORD pos;
HANDLE hOutput;
pos.X = x;
pos.Y = y;
hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleCursorPosition(hOutput, pos);
}
void Print(SnakeHead *H)
{
SetPosition(H->x, H->y);
printf("@");//mean snake head
SnakeBody *temp = H->next;
while(NULL != temp)
{
SetPosition(temp->x, temp->y);
printf("+");//means snake body
temp = temp->next;
}
}
void Move(SnakeHead *H)
{
SnakeBody *temp = H->next;
while (NULL != temp->next->next)
{
temp = temp->next;
}
temp->next->next = H->next;
H->next = temp->next;
SetPosition(temp->next->x, temp->next->y);
printf(" ");
temp->next = NULL;
H->next->x = H->x;
H->next->y = H->y;
}
void _MoveUp(SnakeHead *H)
{
Move(H);
H->y--;
}
void _MoveDown(SnakeHead *H)
{
Move(H);
H->y++;
}
void _MoveRight(SnakeHead *H)
{
Move(H);
H->x++;
}
void _MoveLeft(SnakeHead *H)
{
Move(H);
H->x--;
}
void _GrowUp(SnakeHead *H)
{
SnakeBody *temp = (SnakeBody *)malloc(sizeof(SnakeBody));
temp->x = H->x;
temp->y = H->y;
temp->next = H->next;
H->next = temp;
switch (H->Direction)
{
case 'U':
{
H->y--;
break;
}
case 'D':
{
H->y++;
break;
}
case 'L':
{
H->x--;
break;
}
case 'R':
{
H->x++;
break;
}
default:
{
break;
}
}
Print(H);
}
const int Left = ;
const int Right = ;
const int Top = ;
const int Bottom = ;
void BuildWall()
{
for (int x = Left; x <= Right; x++)
{
SetPosition(x, Top);
printf("-");
SetPosition(x, Bottom);
printf("-");
}
for (int y = Top; y <= Bottom; y++)
{
SetPosition(Left, y);
printf("|");
SetPosition(Right, y);
printf("|");
}
//some hint
SetPosition(, );
printf("Snake");
SetPosition(, );
printf("zhaoyu");
SetPosition(, );
printf("F1:Slow Down");
SetPosition(, );
printf("F2:Speed Up");
SetPosition(, );
printf("F3:Speed Up++");
SetPosition(, );
printf("Score:");
}
void CreateFood(int *x, int *y, SnakeHead *H)
{
int flag = ;
int i = , j = ;
//make sure in the wall && not on snake
SnakeBody *temp = H->next;
while ( == flag)
{
i = rand()%;
i += ;
j = rand()%;
j += ;
flag = ;
if (i == H->x && j == H->y)
{
flag = ;
continue;
}
temp = H->next;
while (NULL != temp)
{
if (i == temp->x && j == temp->y)
{
flag = ;
break;
}
temp = temp->next;
}
} *x = i;
*y = j;
SetPosition(i, j);
printf("$");//dollar mead food!
SetPosition(, );
//once food created ,score plus 1
printf("%d", ++Score);
}
int Die(SnakeHead *H)
{
//out the wall will die
if (H->x <= Left || H->x >= Right || H->y >= Bottom || H->y <= Top)
{
return ;//die
}
// crush into itself will die
SnakeBody *temp = H->next;
while (NULL !=temp)
{
if (H->x == temp->x && H->y == temp->y)
{
return ;
}
temp = temp->next;
}
// 0 mean not die
return ;
}
#endif
源文件
#include "head.h" void StartGame()
{
//Create a snake
SnakeBody *temp = (SnakeBody *)malloc(sizeof(SnakeBody));
SnakeHead *Head = (SnakeHead *)malloc(sizeof(SnakeHead));
Head->Direction = 'L';
Head->Status = ;
Head->x = ;
Head->y = ;
//assign some functions to snake
Head->MoveUp = _MoveUp;
Head->MoveDown = _MoveDown;
Head->MoveRight = _MoveRight;
Head->MoveLeft = _MoveLeft;
Head->GrowUp = _GrowUp;
Head->IsDie = Die;
temp->x = ;
temp->y = ;
temp->next = NULL;
Head->next = temp;
// create snake body
for (int i = ; i < ; i++)
{
SnakeBody *move = (SnakeBody *)malloc(sizeof(SnakeBody));
move->x = temp->x + ;
move->y = temp->y;
temp->next = move;
temp = move;
move->next = NULL;
}
//Init food
int Foodx = , Foody = ;
CreateFood(&Foodx, &Foody, Head);
int Speed = ;
while ( == Head->Status)
{
//print the snake
Print(Head);
//judge is or not die
if((*Head->IsDie)(Head))
{
break;
}
//get direction
if(GetAsyncKeyState(VK_UP) && 'D' != Head->Direction)
{
Head->Direction = 'U';
}
else if (GetAsyncKeyState(VK_DOWN) && 'U' != Head->Direction)
{
Head->Direction = 'D';
}
else if (GetAsyncKeyState(VK_RIGHT) && 'L' != Head->Direction)
{
Head->Direction = 'R';
}
else if (GetAsyncKeyState(VK_LEFT) && 'R' != Head->Direction)
{
Head->Direction = 'L';
}
//move
switch (Head->Direction)
{
case 'U':
{
//judge is there a food
if (Head->x == Foodx && Head->y - == Foody)
{
(*Head->GrowUp)(Head);
CreateFood(&Foodx, &Foody, Head);
}
else//Just move
{
(*Head->MoveUp)(Head);
}
break;
}
case 'D':
{
if (Head->x == Foodx && Head->y + == Foody)
{
(*Head->GrowUp)(Head);
CreateFood(&Foodx, &Foody, Head);
}
else
{
(*Head->MoveDown)(Head);
}
break;
}
case 'R':
{
if (Head->x + == Foodx && Head->y == Foody)
{
(*Head->GrowUp)(Head);
CreateFood(&Foodx, &Foody, Head);
}
else
{
(*Head->MoveRight)(Head);
} break;
}
case 'L':
{
if (Head->x - == Foodx && Head->y == Foody)
{
(*Head->GrowUp)(Head);
CreateFood(&Foodx, &Foody, Head);
}
else
{
(*Head->MoveLeft)(Head);
}
break;
}
default:
{
break;
}
}
//control
if (GetAsyncKeyState(VK_F1) && Speed > )
{
Speed -= ;
}
else if (GetAsyncKeyState(VK_F2) && Speed < )
{
Speed += ;
}
else if (GetAsyncKeyState(VK_F3))
{
Speed += ;
}
if ('L' == Head->Direction ||'R' == Head->Direction)
{
Sleep(/Speed);
}
else
{
Sleep(/Speed);
}
}
}
int main(int argc, char const *argv[])
{
//init
system("cls");
system("mode con cols=50 lines=30");//no space around equal sign
BuildWall();
HideCursor();
StartGame();
SetPosition(, );
printf("Enter to exit!");
getchar();
return ;
}
运行:
后记:
1、函数指针与函数名
函数名与函数指针都是一样的,即都是函数指针。函数名是一个函数指针常量,而函数指针是一个函数数指针变量。
一般的C语言书籍都不会谈及这些近乎奇淫技巧的东东。
2、GetAsyncKeyState函数
这个函数按下之后其状态会一直存在,我在写俄罗斯方块就大受其害。以下面代码为例。你只需要按一下F1就可以跳出循环了,而不是1000下。因为按下一次之后其状态就始终为真了,除非你按下别的键。
#include <stdio.h>
#include <windows.h>
int main(int argc, char const *argv[])
{
int i = ;
while ()
{
if (GetAsyncKeyState(VK_F1))
{
i++;
}
if (i >= )
{
break;
}
}
printf("%d\n", i);
return ;
}
C语言用面向对象的思想写贪吃蛇的更多相关文章
- JS高级---面向对象的编程思想(贪吃蛇梳理)
面向对象的编程思想(贪吃蛇梳理) 模拟贪吃蛇游戏,做的项目 地图: 宽,高,背景颜色,因为小蛇和食物都是相对于地图显示的, 这里小蛇和食物都是地图的子元素, 随机位置显示, 脱离文档流的, 地图也需要 ...
- JavaScript面向对象编程小游戏---贪吃蛇
1 面向对象编程思想在程序项目中有着非常明显的优势: 1- 1 代码可读性高.由于继承的存在,即使改变需求,那么维护也只是在局部模块 1- 2 维护非常方便并且成本较低. 2 这个demo是采用了 ...
- 用python+pygame写贪吃蛇小游戏
因为python语法简单好上手,前两天在想能不能用python写个小游戏出来,就上网搜了一下发现了pygame这个写2D游戏的库.了解了两天再参考了一些资料就开始写贪吃蛇这个小游戏. 毕竟最开始的练手 ...
- 【Java并发基础】利用面向对象的思想写好并发程序
前言 下面简单总结学习Java并发的笔记,关于如何利用面向对象思想写好并发程序的建议.面向对象的思想和并发编程属于两个领域,但是在Java中这两个领域却可以融合到一起.在Java语言中,面向对象编程的 ...
- pygame写贪吃蛇
python小白尝试写游戏.. 学了点pygame不知道那什么练手好,先拿贪吃蛇开刀吧. 一个游戏可以粗略的分为两个部分: 数据(变量) 处理数据(函数,方法) 设计变量 首先预想下,画面的那些部分需 ...
- 用原生Canvas写贪吃蛇及问题解决
为了学习Canvas,写了这个小游戏贪吃蛇供自己和大家学习 Github: https://github.com/zhiyishou/Gsnake Play On: http://zhiyishou. ...
- 用Js写贪吃蛇
使用Javascript做贪吃蛇小游戏, 1.自定义地图宽高,蛇的初始速度 2.食物随机出现 3.蛇的样式属性 4.贪吃蛇玩法(吃食物,碰到边界,吃食物后加速,计分,) <!DOCTYPE ht ...
- 一步步教你怎么用python写贪吃蛇游戏
目录 0 引言 1 环境 2 需求分析 3 代码实现 4 后记 0 引言 前几天,星球有人提到贪吃蛇,一下子就勾起了我的兴趣,毕竟在那个Nokia称霸的年代,这款游戏可是经典中的经典啊!而用Pytho ...
- javascript写贪吃蛇游戏(20行代码!)
<!doctype html> <html> <body> <canvas id="can" width="400" ...
随机推荐
- 前端见微知著JavaScript基础篇:this or that ?
上节,我们提到了this关键字的问题,并且追加了一句很有意义的话:谁调用我,我指向谁.的确,在javascript中,在默认情况下,this会指向一个已经初始化的window对象.所以你不论有多少全局 ...
- shell读取文件内容
Shell脚本,执行解释速度快.代码简单易于理解.在shell代码编写过程中,经常会用到读取文件内容. 写法一: ------------------------------------ ...
- Go--避免SQL注入
避免SQL注入 什么是SQL注入 SQL注入攻击(SQL Injection),简称注入攻击,是Web开发中最常见的一种安全漏洞.可以用它来从数据库获取敏感信息,或者利用数据库的特性执行添加用户,导出 ...
- 68 id -显示用户的id
Linux id命令用于显示用户的ID,以及所属群组的ID. id会显示用户以及所属群组的实际与有效ID.若两个ID相同,则仅显示实际ID.若仅指定用户名称,则显示目前用户的ID. 语法 id [-g ...
- 63-w 简明笔记
显示关于系统用户的信息 w [options] [username] w用于显示当前登录系统的用户的名字以及他们的终端设备编号.登录时间.正在运行的命令和其他一些信息 参数 username 限定仅显 ...
- 【The Expendables】团队博客目录
站立式会议: •[alpha版本]第一次站立式会议 •[beta版本]冲刺计划 •[beta版本]冲刺总结 •[alpha版本]第二次站立式会议 •[beta版本]第 ...
- java 上传图片 并压缩图片大小
Thumbnailator 是一个优秀的图片处理的Google开源Java类库.处理效果远比Java API的好.从API提供现有的图像文件和图像对象的类中简化了处理过程,两三行代码就能够从现有图片生 ...
- Shell命令_case
语法: case "变量" in "变量1") ... ;; #输出两个分号 "变量2") ...
- Kernel Methods - An conclusion
Kernel Methods理论的几个要点: 隐藏的特征映射函数\(\Phi\) 核函数\(\kappa\): 条件: 对称, 正半定; 合法的每个kernel function都能找到对应的\(\P ...
- js-jquery-将table的td转化成可编辑的文本
1.使用插件mindmup-editabletable.js $('#table').editableTableWidget({editor: $('<textarea>')}); 2.j ...