您的位置:首页 > 编程语言 > C语言/C++

【贪吃蛇,C++实现】,由简单游戏开始学习OOP,的编程新手

2015-05-07 16:07 986 查看
{我又来写黑历史了}....

以前学习C语言的时候,老是感觉只能做点数学题什么的(事实程序就是由许多道数学题构成)。

然后最近两个月开始学习C++,接触到OOP,然后感觉视界扩大了(误)特别是最近突然发现好像可以写些小游戏了(大误)。

前天埋头几个小时写了个贪吃蛇,而且成功运行了,但是回头看看代码,虽然使用了类,但还是C语言那套路....类被我当做一个单纯的函数集来使用。于是花了点时间重新构思了下,写下了这篇文章。

第一个类:坐标类

这个类要求

1,有两个变量分别代表X轴和Y轴,这样我们能把光标移动到任意一点。

2,蛇是否会撞到墙壁,就是坐标是否会重叠,所以这个类的对象要能相互比较。

3,构造函数和赋值也是必须的。

头文件:Coordinate.h

#ifndef _COORDINATE_H
#define _COORDINATE_H

class Coordinate
{
public:
Coordinate(int X, int Y)
{
x = X;
y = Y;
}
Coordinate() { }

bool operator == ( const Coordinate &a )
{
return a.x == x&&a.y == y;
}
bool operator !=( const Coordinate &a )
{
return !(a.x == x&&a.y == y);
}

Coordinate &operator=( const Coordinate &a )
{
x = a.x;
y = a.y;
return *this;
}

int x, y;
};

#endif

然后就能用windows的API函数来构建一个函数,这个函数接受一个坐标类的对象,并且把光标移动到该坐标。

头文件:GotoXY.h

#ifndef _GOTOXY_H
#define _GOTOXY_H

#include "Coordinate.h"
#include <Windows.h>

void gotoxy(Coordinate a)
{
COORD local;
local.X = a.x;
local.Y = a.y;
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), local);
}

#endif
第二个类:蛇类

用一种数据结构来储存坐标类的对象,作为蛇的身体。
这里用了STL模板库的<list>

原因,能迭代访问元素,增加删除头尾元素方便。

类要求:

1,用<list>创建舍得身体

2,要有一个坐标类的对象,存储这条蛇前进到下一个位置的坐标

3,一个在下一个位置增加蛇头的函数(打印字符),一个删除蛇尾的函数(打印空格字符)。

(蛇向前一步,增加蛇头一次,删除蛇尾一次。蛇迟到食物身体变长,只增加蛇头,不删蛇尾)

4,构造函数初始化蛇的身体为坐标(0,0)到(3,0)四格

头文件:Snake.h

#include <iostream>
#include <list>
#include "GotoXY.h"

using namespace std;

class Snake
{
public:
Snake();
void AddHead( );
void SubtractTail( );

Coordinate next;
list <Coordinate> snakebody;
};

Snake::Snake()
{
next = Coordinate(3, 0);

for (int n = 0, m = 0; n < 4; n++)
{
snakebody.push_front(Coordinate(n, m));
}
}

void Snake::AddHead( )
{
gotoxy(next);
cout << "#";
snakebody.push_front(next);
}

void Snake::SubtractTail( )
{
gotoxy(snakebody.back( ));
cout << " ";
snakebody.pop_back( );
}

第三个类:裁判类
功能:

1,用GetAsyncKeyState接受键盘输入信息,改变蛇的方向。

2,根据舍得方向改变蛇下一个位置的坐标。

3,根据下一个位置的坐标,检测下一个位置是否越界或撞上自己的身体,判断是否死亡。

4,能改变分数(吃到食物),并且根据分数多少增加等级(加快速度),然后打印分数和等级。

5,除分数,等级外,还有四个变量,分别为上下左右方向,要符合GetAsyncKeyState的虚拟键码

头文件:Judgement.h

#include <iomanip>
#include "Snake.h"
/*边界*/
const int confineX = 20;
const int confineY = 10;
/*分数和等级坐标*/
const Coordinate _score(confineX + 5, 4);
const Coordinate _level(confineX + 5, 7);

const int Level[6] = { 0, 400, 300, 200, 100, 50 };//每个等级对应前进时间间隔(毫秒)
const int Score[6] = { 0, 10, 30, 60, 100, 150 };//每个等级对应的分数数值

class Judge
{
public:
Judge(int l, int u, int r, int d) :level(1), score(0),
left(l), up(u), right(r), down(d)
{
direction = r;
}
bool Death(Snake &snake);//判断是否死亡
bool ChangeScore( );//改变分数
void ChangeDriection(Snake &snake);//改变蛇的前进方向
void ChangeNext(Snake &snake);//改变蛇下一个位置坐标

int direction;
int level;
int score;
int left, up, right, down;
};

bool Judge::Death(Snake &snake)
{
ChangeNext(snake);

if (snake.next.x >= confineX || snake.next.x < 0)
{
return true;
}
else if (snake.next.y >= confineY || snake.next.y < 0)
{
return true;
}
else
{
for (list<Coordinate>::iterator i = snake.snakebody.begin( );
i != snake.snakebody.cend( ); i++)
{
if (( *i ) == snake.next)
{
return true;
}
}
}

return false;
}

bool Judge::ChangeScore( )
{
score += level;
gotoxy(_score);
cout << setw(5) << score;

if (score >= Score[level])
{
level++;

if (level > 5)
{
return true;
}

gotoxy(_level);
cout << setw(5) << level;
}
return false;
}

void Judge::ChangeDriection(Snake &snake)
{
if (direction == left || direction == right)
{
if (GetAsyncKeyState(up))
{
direction = up;
}
else if (GetAsyncKeyState(down))
{
direction = down;
}
}
else
{
if (GetAsyncKeyState(left))
{
direction = left;
}
else if (GetAsyncKeyState(right))
{
direction = right;
}
}
}

void Judge::ChangeNext(Snake &snake)
{
ChangeDriection(snake);

if (direction == up)
{
snake.next.y--;
}
else if (direction == left)
{
snake.next.x--;
}
else if (direction == down)
{
snake.next.y++;
}
else if (direction == right)
{
snake.next.x++;
}
}

有人说,为什么这么麻烦,非要分开蛇类和裁判类,集合为一个类不就行了吗。
我是这样想的,这样能方便弄出双人游戏,在屏幕上打印两个框,然后弄两条蛇和两个裁判,就能和小伙伴一起玩耍了www。不知是否可行呢.....

最后一个类:控制整个游戏

1,有一个蛇类的对象,和一个裁判类的对象。

2,能制造食物。

3,打印游戏开始前界面。

4,执行游戏流程(游戏开始,循环(判断是否死亡,判断是否吃到食物,蛇前进一步))

头文件:Manage.h

#include "Judgement.h"
#include <ctime>

enum Direction
{
LEFT = 37,
UP = 38,
RIGHT = 39,
DOWN = 40
};

class Manage
{
public:
Manage() :food(confineX / 2, confineY / 2),
B(LEFT,UP,RIGHT,DOWN){ }
void DrawConfine( );
void Begin( );
void MakeFood( );

private:
Snake A;
Judge B;
Coordinate food;
};

void Manage::DrawConfine( )
{
int x, y;

for (y = confineY, x = 0; x <= confineX; x++)
{
gotoxy(Coordinate(x, y));
cout << "-";
}
for (x = confineX, y = 0; y <= confineY; y++)
{
gotoxy(Coordinate(x, y));
cout << "|";
}

for (int n = 0, m = 0; n < 4; n++)
{
gotoxy(Coordinate(n, m));
cout << "#";
}

gotoxy(Coordinate(confineX + 5, 3));
cout << "Score:";
gotoxy(_score);
cout << setw(5) << B.score;

gotoxy(Coordinate(confineX + 5, 6));
cout << "Level:";
gotoxy(_level);
cout << setw(5) << B.level;

gotoxy(Coordinate(confineX / 2, confineY / 2));
cout << "*";
}

void Manage::Begin( )
{
while (!B.Death(A))
{
if (food == A.next)
{
if (B.ChangeScore( ))
{
break;
}

A.AddHead( );
MakeFood( );
}
else
{
A.AddHead( );
A.SubtractTail( );
}

Sleep(Level[B.level]);
}
}

void Manage::MakeFood( )
{
while (1)
{
bool flag = true;
Coordinate f = Coordinate(rand( ) % confineX, rand( ) % confineY);

for (list<Coordinate>::iterator i = A.snakebody.begin( );
i != A.snakebody.cend( ); i++)
{
if (( *i ) == f)
{
flag = false;
break;
}
}

if (flag)
{
food = f;
gotoxy(f);
cout << "*";
break;
}
}
}

然后最后就是源文件了
Snake!!.cpp

#include "Management.h"

int main( )
{
srand(time(NULL));
Manage game;
game.DrawConfine( );
game.Begin( );
}

ctrl+F5,开始游戏吧~~~~

PS:

最后:

其实写这篇文章是为了,能在以后(或许一年,或许几个月)回头看的时候,能感受到自己的进步。

祝大家生活愉快!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  c++ 游戏 面向对象