GMap.h

#pragma once //保证头文件只被编译一次

#include "stdafx.h"

#define MAP_LEN    19    //逻辑地图大小 (逻辑地图由行、列各为19的方块组成)

#define P_ROW 10 //大嘴出生地的横逻辑坐标
#define P_COLUMN 9 //大嘴出生地的列逻辑坐标
#define E_ROW 8 //敌人出生地的横逻辑坐标
#define E_COLUMN 9 //敌人出生地的列逻辑坐标 /*
地图中应该存在障碍物和豆子,所以要有豆子和障碍物的尺寸和逻辑地图点阵,以及绘制地图(包括障碍物)和豆子的绘制函数
以及颜色变量和设置敌我出生位置不能有豆子存在,以及地图类要能被物体类和大嘴类访问,故设置物体类和大嘴类为地图类的友元类
下面为地图类,为关卡1、关卡2、关卡3的父类
*/
class GMap
{
protected:
static int LD; //障碍物的尺寸大小,设置为静态变量,因为要所有GMap对象共用这个变量
static int PD; //豆子的半径,设置为静态变量,因为要所有GMap对象共用这个变量
bool mapData[MAP_LEN][MAP_LEN]; //障碍物逻辑地图点阵
bool peaMapData[MAP_LEN][MAP_LEN]; //豆子逻辑地图点阵
COLORREF color; //COLORREF是DWORD的宏,而DWORD是unsigned long
//----
void InitOP(); //使敌我双方的出生位置没有豆子存在 public:
void DrawMap(HDC &hdc); //绘制地图
void DrawPeas(HDC &hdc); //绘制豆子 friend class GObject; //设置物体类为地图类的友元类,使物体类对象能访问地图类对象
friend class PacMan; //设置大嘴类为地图类的友元类,使大嘴类对象能访问地图类对象
}; //关卡1类,为地图类的子类
class Stage_1:public GMap //c++默认的是private继承,无法进行转换,所以继承后面都要有一个public
{
private:
bool static initData[MAP_LEN][MAP_LEN]; //关卡逻辑地图点阵 public:
Stage_1(); //构造函数
}; class Stage_2:public GMap
{
private:
bool static initData[MAP_LEN][MAP_LEN];
public:
Stage_2();
};
class Stage_3:public GMap
{
private:
bool static initData[MAP_LEN][MAP_LEN];
public:
Stage_3();
};

GMap.cpp 

#include "stdafx.h"
#include "GMap.h" int GMap::LD =; //初始化静态变量 障碍物的尺寸大小
int GMap::PD =; //豆子的半径 //使敌我双方的出生位置没有豆子存在
void GMap::InitOP()
{
peaMapData[P_ROW][P_COLUMN] = false; //让大嘴出生地的豆子消失 true,有豆子 false,无豆子
peaMapData[E_ROW][E_COLUMN] = false; //让敌人出生地的豆子消失 true,有豆子 false,无豆子
} //绘制地图
void GMap::DrawMap(HDC &hdc)
{
for(int i=;i<MAP_LEN;i++)
{
for(int j=;j<MAP_LEN;j++)
{
if(!mapData[i][j]) //元素为B,即false,为墙壁/障碍物,则进入if内绘制 是A的位置不绘制,即为空白处
{
RECT rect; //矩形结构体对象
rect.left = j*LD; //设置矩形4个方向启动的坐标
rect.top = i*LD;
rect.right = (j+)*LD;
rect.bottom = (i+)*LD; FillRect(hdc,&rect,CreateSolidBrush(color)); //绘制矩形(墙壁/障碍物)
}
}
}
} //绘制豆子
void GMap::DrawPeas(HDC &hdc)
{
for(int i=;i<MAP_LEN;i++)
{
for(int j=;j<MAP_LEN;j++)
{
if(peaMapData[i][j]) //元素为A,即true,为豆子,则进入if内绘制 是B的位置则不绘制豆子
{
Ellipse(hdc,(LD/-PD)+j*LD,(LD/-PD)+i*LD,(LD/+PD)+j*LD,(LD/+PD)+i*LD); //绘制圆弧(豆子)
}
}
}
} #define A true
#define B false //初始化static数组InitData[MAP_LEN][MAP_LEN,即关卡1的原始数据 B表示障碍物/墙壁,A表示空白处(除出生地外的空白处有豆子)
bool Stage_1::initData[MAP_LEN][MAP_LEN] =
{
B,B,B,B,B,B,B,B,B,A,B,B,B,B,B,B,B,B,B,//
B,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,//
B,A,A,B,A,A,B,B,B,A,B,B,B,A,A,B,A,A,B,//
B,A,B,B,A,A,A,A,A,A,A,A,A,A,A,B,B,A,B,//
B,A,B,A,A,A,B,B,B,A,B,B,B,A,A,A,B,A,B,//
B,A,B,A,A,A,A,A,A,A,A,A,A,A,A,A,B,A,B,//
B,A,A,A,A,A,B,B,A,A,A,B,B,A,A,A,A,A,B,//
B,A,B,A,A,A,A,A,A,A,A,A,A,A,A,A,B,A,B,//
B,A,B,A,A,A,A,A,B,A,B,A,A,A,A,A,B,A,B,//
A,A,A,A,A,A,A,A,B,B,B,A,A,A,A,A,A,A,A,//
B,A,B,A,A,A,A,A,A,A,A,A,A,A,A,A,B,A,B,//
B,A,B,A,A,B,A,A,A,A,A,A,A,B,A,A,B,A,B,//
B,A,B,A,B,B,B,A,A,A,A,A,B,B,B,A,B,A,B,//
B,A,A,A,A,B,A,A,A,A,A,A,A,B,A,A,A,A,B,//
B,A,B,B,A,A,A,A,A,A,A,A,A,A,A,B,B,A,B,//
B,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,//
B,A,A,A,A,B,B,B,A,B,A,B,B,B,A,A,A,A,B,//
B,A,A,A,A,B,A,A,A,A,A,A,A,B,A,A,A,A,B,//17
B,B,B,B,B,B,B,B,B,A,B,B,B,B,B,B,B,B,B,//
};
#undef A
#undef B Stage_1::Stage_1()
{
color =RGB(,,); //设置关卡1的地图颜色 for(int i=;i<MAP_LEN;i++)
{
for(int j=;j<MAP_LEN;j++)
{
this->mapData[i][j] = this->initData[i][j]; //初始化关卡1的地图数据
this->peaMapData[i][j] = this->initData[i][j]; //初始化关卡1的豆子数据
}
} //敌我双方出现位置没有豆子出现
this->InitOP();
} //Stage_2成员定义
#define A true
#define B false
bool Stage_2::initData[MAP_LEN][MAP_LEN]=
{
B,B,B,B,B,B,B,B,B,A,B,B,B,A,B,B,B,B,B,//
A,A,A,A,A,A,A,B,A,A,B,A,A,A,B,A,B,A,A,//
B,A,A,A,B,A,A,B,A,A,B,A,B,A,B,A,B,A,B,//
B,B,B,A,B,A,A,B,B,A,B,A,B,A,B,A,B,B,B,//
B,A,A,A,A,A,A,A,A,A,A,A,B,B,B,A,A,A,B,//
B,A,A,B,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,//
B,A,A,B,A,A,A,B,B,B,B,B,B,A,A,B,A,A,B,//
B,A,A,B,A,B,A,A,A,A,A,A,A,A,A,B,A,A,B,//
B,A,A,B,A,B,A,A,B,A,B,A,A,B,A,B,A,A,B,//
A,A,A,B,A,B,A,A,B,B,B,A,A,B,A,B,A,A,A,//
B,A,A,B,A,B,A,A,A,A,A,A,A,B,A,A,A,A,B,//
B,A,A,B,A,A,A,B,B,B,B,B,A,B,A,A,A,A,B,//
B,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,//
B,A,A,A,B,B,B,B,B,B,B,A,A,A,A,A,A,A,B,//
B,A,A,A,A,A,A,A,A,A,A,A,A,B,A,A,A,A,B,//
B,B,B,B,B,A,A,A,A,B,B,B,A,B,A,A,A,A,B,//
B,A,A,A,B,B,B,A,A,A,A,B,A,B,B,B,A,A,B,//
A,A,A,A,B,A,A,A,A,A,A,B,A,A,A,B,A,A,A,//17
B,B,B,B,B,B,B,B,B,A,B,B,B,A,B,B,B,B,B,//
};
#undef A
#undef B Stage_2::Stage_2()
{
color = RGB(,,);
for(int i= ;i<MAP_LEN;i++)
{
for(int j =;j<MAP_LEN;j++)
{
this->mapData[i][j] = this->initData[i][j];
this->peaMapData[i][j] = this->initData[i][j];
}
}
//敌我双方出现位置没有豆子出现
this->InitOP();
} //Stage_3成员定义
#define A true
#define B false
bool Stage_3::initData[MAP_LEN][MAP_LEN]=
{
B,B,B,B,B,B,B,B,B,A,B,B,B,B,B,B,B,B,B,//
A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,//
B,A,A,B,A,A,B,B,B,B,B,B,B,A,A,A,B,A,B,//
B,A,B,B,A,A,A,A,A,A,A,A,B,A,A,A,B,A,B,//
B,A,B,A,A,A,B,B,B,B,B,B,B,A,A,A,B,A,B,//
B,A,B,A,B,B,B,A,A,A,A,A,B,B,B,A,B,A,B,//
B,A,A,A,B,A,B,A,A,A,A,A,A,A,A,A,B,A,B,//
B,A,B,A,B,A,A,A,A,A,A,A,A,B,A,A,B,A,B,//
B,A,B,A,B,B,A,A,B,A,B,A,A,B,A,A,B,A,B,//
B,A,A,A,A,B,A,A,B,B,B,A,A,B,A,A,B,A,B,//
B,A,B,A,A,B,A,A,A,A,A,A,A,B,A,A,A,A,B,//
B,A,B,A,A,B,A,A,A,A,A,A,B,B,B,A,B,A,B,//
B,A,B,A,A,B,A,B,B,B,B,B,B,A,B,A,B,A,B,//
B,A,B,A,A,B,A,A,A,A,A,A,A,A,B,A,B,A,B,//
B,A,B,B,A,B,B,B,B,B,B,A,B,A,B,A,B,A,B,//
B,A,A,A,A,B,A,A,A,A,A,A,B,A,B,A,B,A,B,//
B,B,B,B,B,B,A,A,B,B,B,A,B,A,B,A,B,A,B,//
A,A,A,A,A,A,A,A,B,A,A,A,A,A,B,A,A,A,A,//17
B,B,B,B,B,B,B,B,B,A,B,B,B,B,B,B,B,B,B,//
};
#undef A
#undef B Stage_3::Stage_3()
{
color = RGB(,,);
for(int i= ;i<MAP_LEN;i++)
{
for(int j =;j<MAP_LEN;j++)
{
this->mapData[i][j] = this->initData[i][j];
this->peaMapData[i][j] = this->initData[i][j];
}
}
//敌我双方出现位置没有豆子出现
this->InitOP();
}

GObject.h

#include "stdafx.h"
#include <time.h>
#include "GMap.h" #define DISTANCE 10//图型范围
#define D_OFFSET 2//绘图误差
#define RD (DISTANCE + D_OFFSET)//绘图范围 12
//----
#define PLAYER_SPEED 6//玩家速度
#define ENERMY_SPEED 4//敌人速度
//----
#define LEGCOUNTS 5//敌人腿的数量
#define BLUE_ALERT 8//蓝色警戒范围 //方向枚举
enum TWARDS{UP,DOWN,LEFT,RIGHT,OVER}; //物体类 (含有纯虚函数,为抽象类,不能被实例化)
class GObject
{
protected:
int dRow; //逻辑横坐标
int dColumn; //逻辑纵坐标
POINT point; //中心坐标
int mX; //物体的中心横坐标
int mY; //物体的中心纵坐标
//----
TWARDS twCommand;//朝向指令缓存
TWARDS tw;//朝向
//----
int speed;//速度
int frame;//帧数 //子程序
bool Achive(); //判断物体是否到达逻辑坐标位置
int PtTransform(int k);//将实际坐标转换为逻辑坐标
virtual void AchiveCtrl();//到达逻辑点后更新数据
bool Collision() ;//逻辑碰撞检测,将物体摆放到合理的位置 public:
static GMap* pStage; //指向地图类的指针,设置为静态,使所有自类对象都能够使用相同的地图 GObject(int Row,int Column); //有参构造函数 int GetRow(); //提供共有接口,获取保护成员变量dRow
int GetArray(); //提供共有接口,获取保护成员变量dColumn
virtual void action() = ;//数据变更的表现
//----
void SetPosition(int Row,int Column); //设置自身中心位置
void DrawBlank(HDC &hdc);
virtual void Draw(HDC &hdc) = ;//绘制对象 }; //大嘴,玩家控制的对象
class PacMan:public GObject //PacMan是GObject的子类
{
protected:
virtual void AchiveCtrl();//重写虚函数 (到达逻辑点后更新数据) public:
PacMan(int Row,int Column); POINT GetPos(); //提供共有接口,获取保护成员变量point
TWARDS GetTw(); //提供共有接口,获取保护成员变量twCommand
//----
bool Win(); //轮询peaMapData[][],判断是否存在任意一个豆子,来判断是否获胜
void SetTwCommand(TWARDS command); //设置大嘴的移动方向指令
void Over(); //设置大嘴的方向为OVER
//----
void Draw(HDC &hdc); //重写虚函数: 根据移动方向指令,绘制不同朝向的大嘴4帧动画
void action();//数据变更的表现 (直接调用父类GObject的逻辑碰撞检测函数)
}; //追捕大嘴的敌人
class Enermy:public GObject //Enermy是GObject的子类
{
protected:
void Catch(); //检测敌人是否抓捕到大嘴
void virtual MakeDecision(bool b) = ;//AI实现
COLORREF color; public:
static PacMan* player; //在敌人类中,定义一个玩家(大嘴) 设置为静态变量,全部敌人追捕一个公共的玩家 void virtual Draw(HDC& hdc); //绘制敌人的图像
Enermy(int x,int y); void virtual action();
}; //松散型的敌人,随机移动
class RedOne:public Enermy
{
protected:
void virtual MakeDecision(bool b);
public:
void Draw(HDC& hdc);
RedOne(int x,int y):Enermy(x,y)
{
color = RGB(,,); //松散型的敌人,颜色为红色
}
}; //守卫者
class BlueOne:public RedOne
{
protected:
void virtual MakeDecision(bool b);
public:
void Draw(HDC& hdc);
BlueOne(int x,int y):RedOne(x,y) //守卫者,颜色为蓝色
{ color = RGB(,,);
} }; //扰乱者
class YellowOne:public RedOne
{
protected:
void virtual MakeDecision(bool b);
public:
void Draw(HDC& hdc);
YellowOne(int x,int y):RedOne(x,y)
{
color = RGB(,,); //扰乱者,颜色为黄色
}
};

GObject.cpp

#include "stdafx.h"
#include "GObject.h" //-----------------------------------------------------以下为GOject成员定义:------------------------------------------------
GMap* GObject::pStage =NULL; //静态变量使用前,必须先初始化 //有参构造函数 参数为物体出生地的逻辑坐标
GObject::GObject(int Row,int Column)
{
this->dRow = Row; //逻辑横坐标
this->dColumn = Column; //逻辑纵坐标
this->point.y = dRow*pStage->LD + pStage->LD/; //根据逻辑坐标换算获得中心坐标 【注意y方向对应的是行dRow,x方向对应的是列dColumn】
this->point.x = dColumn*pStage->LD + pStage->LD/;
this->mX = point.x; //物体的中心横坐标
this->mY = point.y ; //物体的中心纵坐标 frame = ; //初始帧数为1
pStage = NULL; //初始化静态GMap指针
} //提供共有接口,获取保护成员变量dRow
int GObject::GetRow()
{
return dRow;
} //提供共有接口,获取保护成员变量dColumn
int GObject::GetArray()
{
return dColumn;
} //判断物体是否到达逻辑坐标位置
bool GObject::Achive()
{
//若物体在逻辑坐标位置,则point.x - pStage->LD/2 为pStage->LD的整数倍,取余为0
if( (point.x - pStage->LD/)%pStage->LD== && (point.y - pStage->LD/)%pStage->LD== ) //到达逻辑坐标位置,返回true
{
return true; //到达逻辑坐标位置,返回true
} return false; //未到达,返回false
} //将实际坐标转换为逻辑坐标 k为point.x或者point.y
int GObject::PtTransform(int k)
{
return (k - pStage->LD/)/pStage->LD;
} //到达逻辑坐标位置后更新数据
void GObject::AchiveCtrl() //声明时,为虚函数,定义加virtual会报错
{
if(Achive()) //先判断是否到达逻辑坐标位置
{
dRow = PtTransform(point.y); //更新活动逻辑横坐标,由实际横坐标转换而来 【注意y方向对应的是行dRow,x方向对应的是列dColumn】
dColumn = PtTransform(point.x); //更新活动逻辑纵坐标,由实际纵坐标转换而来
}
} //逻辑碰撞检测,将物体摆放到合理的位置 返回指令有效与否的标志位
bool GObject::Collision()
{
bool cmdValid_flag = true; //指令有效与否的标志位 ==true,指令有效 ==false,指令无效 //检测逻辑碰撞前,先更新数据,直到到达逻辑坐标位置,进行逻辑碰撞检测
AchiveCtrl(); //更新逻辑坐标数据,若物体是大嘴,则会执行PacMan重写的AchiveCtrl()消除豆子 //判断指令的有效性
if(dRow< || dColumn< || dRow>MAP_LEN- || dColumn>MAP_LEN-) //逻辑横、纵坐标超出允许范围,即在屏幕外,则不可以改变指令(新指令无效)
cmdValid_flag = false; //(新)指令无效
else if (Achive()) //到达逻辑坐标位置,才能进行逻辑碰撞检测
{
switch(twCommand) //判断前进的方向
{
case LEFT:
{
if(dColumn> && !pStage->mapData[dRow][dColumn-]) //判断下一个格子是否是墙/障碍物 是墙,则!B=!false=true
cmdValid_flag = false; //下一个格子是墙/障碍物,(新)指令无效
}
break;
case RIGHT:
{
if(dColumn<MAP_LEN- && !pStage->mapData[dRow][dColumn+])
cmdValid_flag = false;
}
break;
case UP:
{
if(dRow> && !pStage->mapData[dRow-][dColumn])
cmdValid_flag = false;
}
break;
case DOWN:
{
if(dRow<MAP_LEN- && !pStage->mapData[dRow+][dColumn])
cmdValid_flag = false;
}
break;
} if(cmdValid_flag)
tw = twCommand; //前进方向上的下一方格不是墙壁/障碍物,则指令成功,改变方向有效
} //依照真实的方向移动
mX = point.x;
mY = point.y;
int MAX = pStage->LD*MAP_LEN + pStage->LD/; //超出地图右、下边界一个方格的位置(注意是"中心"坐标)
int MIN = pStage->LD/; //超出地图左、上边界一个方格的位置 switch(tw)//判断行进的方向
{
case LEFT:
if(dColumn> && !pStage->mapData[dRow][dColumn-])//判断下一个格子是否能够通行
{
cmdValid_flag= false;
break;//"撞墙了"
}
point.x -= speed; //改变物体在地图上的中心坐标,相当于物体在地图中移动(这里向左移动)
if(point.x<MIN) //向左移动到了地图左边界外半个方格外,则从另一边出现
{
point.x = MAX;
} break;
//以下方向的判断原理相同
case RIGHT:
if(dColumn<MAP_LEN-&&!pStage->mapData[dRow][dColumn+])
{
cmdValid_flag= false;
break;//"撞墙了"
}
point.x += speed;
if(point.x>MAX)
{
point.x = MIN;
} break;
case UP:
if(dRow>&&!pStage->mapData[dRow-][dColumn])
{
cmdValid_flag= false;
break;//"撞墙了"
}
point.y -=speed;
if(point.y<MIN)
{
point.y = MAX;
}
break;
case DOWN:
if(dRow<MAP_LEN-&&!pStage->mapData[dRow+][dColumn])
{
cmdValid_flag= false;
break;//"撞墙了"
}
point.y +=speed;
if(point.y>MAX)
{
point.y = MIN;
}
break;
} return !cmdValid_flag; //返回指令有效与否的标志位
} //设置自身中心位置
void GObject::SetPosition(int Row,int Column)
{
dRow = Row;
dColumn = Column; point.y = dRow*pStage->LD + pStage->LD/; //根据逻辑坐标换算获得中心坐标 【注意y方向对应的是行dRow,x方向对应的是列dColumn】
point.x = dColumn*pStage->LD + pStage->LD/;
} //将这一帧时物体所在的方格处绘制的图像擦除,移动后再绘制下一方格的图像,使物体看起来像在移动一样
void GObject::DrawBlank(HDC &hdc)
{
RECT rect; rect.left = mX - RD; //这一帧时物体所在的方格(矩形),4个方向边缘坐标
rect.top = mY - RD;
rect.right = mX + RD;
rect.bottom = mY + RD; FillRect(hdc,&rect,CreateSolidBrush(RGB(,,)));
} //-----------------------------------------------------以下为PacMan(大嘴)成员定义:------------------------------------------------ PacMan::PacMan(int Row,int Column):GObject(Row,Column) //使用初始化列表,同时初始化了PacMan::dROW、dColumn、point.x、point.y等等
{
this->speed = PLAYER_SPEED; //设置大嘴速度
twCommand = tw = LEFT; //设置开局时大嘴的移动方向
} //提供共有接口,获取保护成员变量point
POINT PacMan::GetPos()
{
return point;
} //设置大嘴的方向为OVER
void PacMan::Over()
{
tw = OVER;
} //提供共有接口,获取保护成员变量tw
TWARDS PacMan::GetTw()
{
return tw;
} //设置大嘴的移动方向指令
void PacMan::SetTwCommand(TWARDS command)
{
twCommand = command;
} //轮询peaMapData[][],判断是否存在任意一个豆子,来判断是否获胜
bool PacMan::Win()
{
for(int i=;i<=MAP_LEN;i++)
{
for(int j=;j<=MAP_LEN;j++)
{
if(pStage->peaMapData[i][j]==true)
{
return false; //存在任意一个豆子,没取得胜利
}
}
}
return true;//没有豆子,胜利
} //重写虚函数 (到达逻辑点后更新数据)
void PacMan::AchiveCtrl()
{
GObject::AchiveCtrl(); //直接调用GObject类的功能函数: 到达逻辑坐标位置后更新数据 //重写虚函数,为实现大嘴的吃豆子功能
if(Achive()) //必须到达逻辑坐标位置,才能进行下一步
{
if(dRow>= && dRow<MAP_LEN && dColumn>= && dColumn<MAP_LEN)//防止数组越界
{
//peaMapData[dRow][dColumn]: ==true,表示有豆子 ==false,表示没有豆子
if(pStage->peaMapData[dRow][dColumn]) //豆子到达的逻辑坐标位置,如果有豆子
{
pStage->peaMapData[dRow][dColumn] = false; //则将豆子"吃掉"
}
}
}
} //行为函数,直接调用父类GObject的逻辑碰撞检测函数
void PacMan::action()
{
Collision(); //直接调用父类GObject的逻辑碰撞检测函数
} //重写虚函数: 根据移动方向指令,绘制不同朝向的大嘴4帧动画
void PacMan::Draw( HDC& hdc)
{
if(tw == OVER) //移动指令为OVER,无效,不执行如何程序
{ }
else if(frame% == ) //绘制第2帧动画和第4帧动画 (都是V形开口的圆弧)
{
int x1=,x2=,y1=,y2=;
int offsetX = DISTANCE/+D_OFFSET;//弧弦交点 10/2+2 = 7
int offsetY = DISTANCE/+D_OFFSET;//弧弦交点
switch(tw) //移动方向不同,(大嘴)圆弧的V形开口不同
{
case UP:
x1 = point.x - offsetX;
x2 = point.x + offsetX;
y2 = y1 = point.y-offsetY;
break;
case DOWN:
x1 = point.x + offsetX;
x2 = point.x - offsetX;
y2 = y1 = point.y+offsetY;
break;
case LEFT:
x2 = x1 = point.x-offsetX;
y1 = point.y + offsetY;
y2 = point.y - offsetY;
break;
case RIGHT: //V形开口向右
x2 = x1 =point.x + offsetX;
y1 = point.y - offsetY;
y2 = point.y + offsetY;
break;
} //绘制圆弧 RIGHT朝向情况下,x1,y1和x2,y2各为右上角、右下角的弧弦交点
Arc(hdc,point.x-DISTANCE,point.y-DISTANCE,
point.x+DISTANCE,point.y+DISTANCE,
x1,y1,
x2,y2);
MoveToEx(hdc,x1,y1,NULL); //RIGHT朝向情况下,移动坐标原点到右上角的弧弦交点
LineTo(hdc,point.x,point.y); //绘制右上角的弧弦交点到大嘴中心坐标点的直线
LineTo(hdc,x2,y2); //绘制大嘴中心坐标点到右下角的弧弦交点的直线
}
else if(frame% ==) //绘制第3帧动画 (椭圆)
{
Ellipse(hdc,point.x-DISTANCE,point.y-DISTANCE,
point.x+DISTANCE,point.y+DISTANCE);
}
else //绘制第1帧动画 半圆
{
int x1=,x2=,y1=,y2=;
switch(tw)
{
case UP:
x1 = point.x - DISTANCE;
x2 = point.x + DISTANCE;
y2 = y1 = point.y;
break;
case DOWN:
x1 = point.x + DISTANCE;
x2 = point.x - DISTANCE;
y2 = y1 = point.y;
break;
case LEFT:
x2 = x1 = point.x;
y1 = point.y + DISTANCE;
y2 = point.y - DISTANCE;
break;
case RIGHT:
x2 = x1 =point.x ;
y1 = point.y - DISTANCE;
y2 = point.y + DISTANCE;
break;
} Arc(hdc,point.x-DISTANCE,point.y-DISTANCE,
point.x+DISTANCE,point.y+DISTANCE,
x1,y1,
x2,y2);
MoveToEx(hdc,x1,y1,NULL);
LineTo(hdc,point.x,point.y);
LineTo(hdc,x2,y2);
} frame++;//绘制下一祯
} //-----------------------------------------------------以下为Enermy(敌人)成员定义:------------------------------------------------ PacMan* Enermy::player = NULL; //在敌人类中,定义一个玩家(大嘴) 设置为静态变量,全部敌人追捕一个公共的玩家 //Enermy(敌人)的构造函数
Enermy::Enermy(int x,int y):GObject(x,y)
{
this->speed = ENERMY_SPEED; //设置敌人的速度
twCommand = tw = LEFT; //设置开局时,敌人的移动方向
//twCommand = UP;
} //检测敌人是否抓捕到大嘴
void Enermy::Catch()
{
int DX =point.x - player->GetPos().x; //敌人与玩家的中心坐标,在x方向上的距离
int DY =point.y - player->GetPos().y; //敌人与玩家的中心坐标,在y方向上的距离 //敌人与玩家的中心坐标,在x、y方向上的距离小于绘图距离RD,则说明敌人和玩家接触了一半身体 敌人和玩家的直径接近2*RD,实际是2*DISTANCE
if((-RD<DX && DX<RD) && (-RD<DY && DY<RD))
{
player->Over(); //敌人抓到了玩家,玩家方向设置为OVER,不能再移动了
}
} //绘制敌人的图像
void Enermy::Draw(HDC& hdc)
{
HPEN pen =::CreatePen(,,color); //创建画笔
HPEN oldPen = (HPEN)SelectObject(hdc,pen); //应用创建的画笔 Arc(hdc,point.x-DISTANCE,point.y-DISTANCE,
point.x+DISTANCE,point.y+DISTANCE,
point.x+DISTANCE,point.y,
point.x-DISTANCE,point.y);//先绘制半圆型的头 int const LEGLENTH = (DISTANCE)/(LEGCOUNTS); //LEGLENTH为“腿部圆弧”的半径 //根据祯数来绘制身体和“腿部”
if(frame% == ) //绘制第2帧和第4帧图像
{
//绘制2根直线,作为敌人的身子
MoveToEx(hdc,point.x-DISTANCE,point.y,NULL);//矩形的身子
LineTo(hdc,point.x-DISTANCE,point.y +DISTANCE - LEGLENTH);
MoveToEx(hdc,point.x+DISTANCE,point.y,NULL);
LineTo(hdc,point.x+DISTANCE,point.y +DISTANCE - LEGLENTH); for(int i = ;i<LEGCOUNTS;i++)//从左往右绘制“腿部”
{
//绘制腿部,为多个圆弧
Arc(hdc,point.x-DISTANCE+i**LEGLENTH,point.y+DISTANCE-*LEGLENTH,
point.x-DISTANCE+(i+)**LEGLENTH,point.y+DISTANCE,
point.x-DISTANCE+i**LEGLENTH,point.y+DISTANCE-LEGLENTH,
point.x-DISTANCE+(i+)**LEGLENTH,point.y+DISTANCE-LEGLENTH
);
}
}
else //绘制第1帧和第3帧图像
{
MoveToEx(hdc,point.x-DISTANCE,point.y,NULL);//绘制身体
LineTo(hdc,point.x-DISTANCE,point.y +DISTANCE);
MoveToEx(hdc,point.x+DISTANCE,point.y,NULL);
LineTo(hdc,point.x+DISTANCE,point.y +DISTANCE);
//从左往右绘制“腿部” MoveToEx(hdc,point.x-DISTANCE,point.y+DISTANCE,NULL);
LineTo(hdc,point.x-DISTANCE+LEGLENTH,point.y+DISTANCE-LEGLENTH); for(int i = ;i<LEGCOUNTS-;i++)
{
Arc(hdc,point.x-DISTANCE+(+i*)*LEGLENTH,point.y+DISTANCE-*LEGLENTH,
point.x-DISTANCE+(+i*)*LEGLENTH,point.y+DISTANCE,
point.x-DISTANCE+(+i*)*LEGLENTH,point.y+DISTANCE-LEGLENTH,
point.x-DISTANCE+(+i*)*LEGLENTH,point.y+DISTANCE-LEGLENTH
);
} MoveToEx(hdc,point.x+DISTANCE,point.y+DISTANCE,NULL);
LineTo(hdc,point.x+DISTANCE-LEGLENTH,point.y+DISTANCE-LEGLENTH);
} //根据方向绘制眼睛
int R = DISTANCE/; //眼睛的半径
switch(tw)
{
case UP:
Ellipse(hdc,point.x-*R,point.y-*R,point.x,point.y);
Ellipse(hdc,point.x,point.y-*R,point.x+*R,point.y);
break;
case DOWN:
Ellipse(hdc,point.x-*R,point.y,point.x,point.y+*R);
Ellipse(hdc,point.x,point.y,point.x+*R,point.y+*R);
break;
case LEFT:
Ellipse(hdc,point.x-*R,point.y-R,point.x-R,point.y +R);
Ellipse(hdc,point.x-R,point.y-R,point.x+R,point.y +R);
break;
case RIGHT:
Ellipse(hdc,point.x-R,point.y-R,point.x+R,point.y +R);
Ellipse(hdc,point.x+R,point.y-R,point.x+*R,point.y+R);
break;
} frame++; //准备绘制下一祯 SelectObject(hdc,oldPen);
DeleteObject(pen); //销毁画笔 return;
} //敌人:数据变更的表现
void Enermy::action()
{
bool b = Collision();
MakeDecision(b);
Catch();
} //-----------------------------------------------------以下为RedOne(敌人)成员定义:------------------------------------------------ //绘制RedOne(敌人)的图像
void RedOne::Draw(HDC& hdc)
{
Enermy::Draw(hdc);
} //AI-人工智能函数: 松散型
void RedOne::MakeDecision(bool b)
{
//srand(time(0));
int i = rand();
if(b)//撞到墙壁,改变方向
{
//逆时针转向
if(i%==)
{
tw == UP?twCommand = LEFT:twCommand=UP;
}
else if(i%==)
{
tw == DOWN?twCommand =RIGHT:twCommand=DOWN;
}
else if(i%==)
{
tw == RIGHT?twCommand = UP:twCommand=RIGHT;
}
else
{
tw == LEFT?twCommand = DOWN:twCommand=LEFT;
}
return;
} if(i%==)
{
twCommand!=UP?tw==DOWN:twCommand ==UP;
}
else if(i%==)
{
tw != DOWN?twCommand = UP:twCommand=DOWN;
}
else if(i%==)
{
tw != RIGHT?twCommand = LEFT:twCommand=RIGHT;
}
else
{
tw != LEFT?twCommand = RIGHT:twCommand=LEFT;
} } //-----------------------------------------------------以下为BlueOne(敌人)成员定义:------------------------------------------------ //绘制BlueOne(敌人)的图像
void BlueOne::Draw( HDC& hdc)
{
Enermy::Draw(hdc);
} //AI-人工智能函数: 守卫者
void BlueOne::MakeDecision(bool b)
{ const int DR = this->dRow-player->GetRow();
const int DA = this->dColumn-player->GetArray();
if(!b&&DR==)
{
if(DA<=BLUE_ALERT&&DA>)//玩家在左侧边警戒范围s
{
twCommand = LEFT; //向左移动
return;
}
if(DA<&&DA>=-BLUE_ALERT)//右侧警戒范围
{
twCommand = RIGHT;//向右移动
return;
}
}
if(!b&&DA==)
{
if(DR<=BLUE_ALERT&&DR>)//下方警戒范围
{
twCommand = UP;
return;
}
if(DR<&&DR>=-BLUE_ALERT)//上方警戒范围
{
twCommand = DOWN;
return;
}
} RedOne::MakeDecision(b);//不在追踪模式时RED行为相同
} //-----------------------------------------------------以下为YellowOne(敌人)成员定义:------------------------------------------------ //绘制YellowOne(敌人)的图像
void YellowOne::Draw(HDC& hdc)
{
Enermy::Draw(hdc);
} //AI-人工智能函数:扰乱者
void YellowOne::MakeDecision(bool b)
{
const int DR = this->dRow-player->GetRow();
const int DA = this->dColumn-player->GetArray();
if(!b)
{
if(DR*DR>DA*DA)
{
if(DA>)//玩家在左侧边警戒范围
{
twCommand = LEFT; //向左移动
return;
}
else if(DA<)//右侧警戒范围
{
twCommand = RIGHT;//向右移动
return;
}
}
else
{
if(DR>)//下方警戒范围
{
twCommand = UP;
return;
}
if(DR<)//上方警戒范围
{
twCommand = DOWN;
return;
}
}
}
RedOne::MakeDecision(b);
}

PacMan.cpp(包含主函数)

// pacman.cpp : 定义应用程序的入口点。
// #include "stdafx.h"
#include "pacman.h"
#include "GObject.h" //-----start-----
#define WLENTH 700
#define WHIGHT 740
#define STAGE_COUNT 3 //关卡数 //游戏物体
PacMan* p ;
GObject* e1;
GObject* e2 ;
GObject* e3 ;
GObject* e4 ; //释放动态内存函数模板
template<class T>
void Realese(T t)
{
if(t!=NULL)
delete t;
} //进入下一关卡的时候,还原所有物体的位置
void ResetGObjects()
{
p->SetPosition(P_ROW,P_COLUMN);
e1->SetPosition(E_ROW,E_COLUMN);
e2->SetPosition(E_ROW,E_COLUMN);
e3->SetPosition(E_ROW,E_COLUMN);
e4->SetPosition(E_ROW,E_COLUMN);
} //------end------ #define MAX_LOADSTRING 100 // 全局变量:
HINSTANCE hInst; // 当前实例
TCHAR szTitle[MAX_LOADSTRING]; // 标题栏文本
TCHAR szWindowClass[MAX_LOADSTRING]; // 主窗口类名s // 此代码模块中包含的函数的前向声明:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE,int,HWND&);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM); int APIENTRY _tWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{ UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine); // TODO: 在此放置代码。
int s_n = ;//进行到的关卡数
p = new PacMan(P_ROW,P_COLUMN);
e1 =new RedOne(E_ROW,E_COLUMN);
e2 =new RedOne(E_ROW,E_COLUMN);
e3 = new BlueOne(E_ROW,E_COLUMN);
e4 = new YellowOne(E_ROW,E_COLUMN);
GMap* MapArray[STAGE_COUNT] = {new Stage_1(),new Stage_2(),new Stage_3()};
GObject::pStage =MapArray[s_n];//初始化为第一关地图
Enermy::player = p;
//-----end----- MSG msg;
HACCEL hAccelTable; // 初始化全局字符串
LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadString(hInstance, IDC_PACMAN, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance); // 执行应用程序初始化: 【改写了】
HWND hWnd;
if (!InitInstance (hInstance, nCmdShow,hWnd))
{
return FALSE;
}
hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_PACMAN)); DWORD t =;
// 主消息循环:
while(p->GetTw()!=OVER&&s_n<)
{
if(p->Win()) //赢了,p->Win()返回true
{
HDC hdc = GetDC(hWnd);
s_n++;
ResetGObjects(); //进入下一关卡的时候,还原所有物体的位置
if(s_n <)
{
MessageBoxA(hWnd,"恭喜您过关","吃豆子提示",MB_OK);
GObject::pStage = MapArray[s_n];
RECT screenRect;
screenRect.top = ;
screenRect.left = ;
screenRect.right = WLENTH;
screenRect.bottom = WHIGHT;
::FillRect(hdc,&screenRect,CreateSolidBrush(RGB(,,)));
GObject::pStage->DrawMap(hdc);
}
continue;
} if(PeekMessage(&msg, NULL, , ,PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
} if(GetAsyncKeyState(VK_DOWN)&0x8000)
{
p->SetTwCommand(DOWN);
} if(GetAsyncKeyState(VK_LEFT)&0x8000)
{
p->SetTwCommand(LEFT);
} if(GetAsyncKeyState(VK_RIGHT)&0x8000)
{
p->SetTwCommand(RIGHT);
} if(GetAsyncKeyState(VK_UP)&0x8000)
{
p->SetTwCommand(UP);
} else
{
if(GetTickCount()-t>) //每58ms,游戏的数据和画面会更新一次
{
HDC hdc = GetDC(hWnd);
e1->action();
e2->action();
e3->action();
e4->action();
p->action(); GObject::pStage->DrawPeas(hdc);
e1->DrawBlank(hdc);
e2->DrawBlank(hdc);
e3->DrawBlank(hdc);
e4->DrawBlank(hdc); p->DrawBlank(hdc);
e1->Draw(hdc);
e2->Draw(hdc);
e3->Draw(hdc);
e4->Draw(hdc);
p->Draw(hdc); DeleteDC(hdc); //销毁上下文设备 t = GetTickCount(); //GetTickCount()函数获得的是从开机到当前时刻机器运行的毫秒数
}
}
} Realese(e1);
Realese(e2);
Realese(e3);
Realese(e4); for(int i = ;i<STAGE_COUNT;i++)
{
Realese(MapArray[i]);
} if(p->GetTw()==OVER)
{
MessageBoxA(hWnd,"出师未捷","吃豆子提示",MB_OK);
}
else
{
MessageBoxA(hWnd,"恭喜您赢得了胜利","吃豆子提示",MB_OK);
} Realese(p); return (int) msg.wParam;
} //
// 函数: MyRegisterClass()
//
// 目的: 注册窗口类。
//
// 注释:
//
// 仅当希望
// 此代码与添加到 Windows 95 中的“RegisterClassEx”
// 函数之前的 Win32 系统兼容时,才需要此函数及其用法。调用此函数十分重要,
// 这样应用程序就可以获得关联的
// “格式正确的”小图标。
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEX wcex; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = ;
wcex.cbWndExtra = ;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_PACMAN));
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+);
wcex.lpszMenuName = MAKEINTRESOURCE(IDC_PACMAN);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL)); return RegisterClassEx(&wcex);
} //
// 函数: InitInstance(HINSTANCE, int)
//
// 目的: 保存实例句柄并创建主窗口
//
// 注释:
//
// 在此函数中,我们在全局变量中保存实例句柄并
// 创建和显示主程序窗口。
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow,HWND& hWnd) //【改写了,增加了一个参数HWND& hWnd】
{ hInst = hInstance; // 将实例句柄存储在全局变量中 hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
, , WLENTH, WHIGHT, NULL, NULL, hInstance, NULL); if (!hWnd)
{
return FALSE;
} ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd); return TRUE;
} //
// 函数: WndProc(HWND, UINT, WPARAM, LPARAM)
//
// 目的: 处理主窗口的消息。
//
// WM_COMMAND - 处理应用程序菜单
// WM_PAINT - 绘制主窗口
// WM_DESTROY - 发送退出消息并返回
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc; switch (message)
{
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// 分析菜单选择:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_START: case IDM_EXIT:
DestroyWindow(hWnd);
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
// TODO: 在此添加任意绘图代码...
GObject::pStage->DrawMap(hdc);
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage();
::exit();
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return ;
} // “关于”框的消息处理程序。
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE; case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}

C++小项目-吃豆子游戏的更多相关文章

  1. IOS开发小项目—找色块游戏

    1.项目代码: @interface NextViewController () { int r ;//色块层数的全局变量 int m;//后面用于tag值的变化 UIView *view;//色块 ...

  2. 吃豆子(Packman)

    ZLYD团队总结--吃豆子(Packman) 一.设计主要内容 玩家可以通过键盘或鼠标控制游戏区.游戏采取分数和血量制,当血量用尽时游戏结束,游戏以最终玩家获得的分数来判断玩家水平的高低.使用IEDA ...

  3. 小项目特供 贪吃蛇游戏(基于C语言)

    C语言写贪吃蛇本来是打算去年暑假写的,结果因为ACM集训给耽搁了,因此借寒假的两天功夫写了这个贪吃蛇小项目,顺带把C语言重温了一次. 是发表博客的前一天开始写的,一共写了三个版本,第一天写了第一版,第 ...

  4. JAVA课程设计——“小羊吃蓝莓”小游戏

    JAVA课程设计--"小羊吃蓝莓"小游戏 1. 团队课程设计博客链接 http://www.cnblogs.com/HXY071/p/7061216.html 2. 个人负责模块或 ...

  5. java小项目之:扫雷,这游戏没有你想的那么简单!

    扫雷 我之前分享的小项目和小游戏,电影购票.坦克大战.捕鱼达人.贪吃蛇等,虽然已经是耳熟能详人尽皆知的项目和游戏,但是保不齐真的有人没接触过. 今天分享的这个项目,我不相信没人接触过(仅限80后-00 ...

  6. 【源码项目+解析】C语言/C++开发,打造一个小项目扫雷小游戏!

    一直说写个几百行的小项目,于是我写了一个控制台的扫雷,没有想到精简完了代码才200行左右,不过考虑到这是我精简过后的,浓缩才是精华嘛,我就发出来大家一起学习啦,看到程序跑起来能玩,感觉还是蛮有成就感的 ...

  7. Java小项目--坦克大战(version1.0)

    Java小项目--坦克大战<TankWar1.0> 这个小项目主要是练习j2se的基础内容和面向对象的思想.项目实现了基本的简单功能,我方一辆坦克,用上下左右键控制移动方向,按F键为发射炮 ...

  8. 3D位置语音,引领吃鸡游戏体验升级

    欢迎大家前往云加社区,获取更多腾讯海量技术实践干货哦~ 作者:腾讯游戏云 导语:在刚刚结束的首届腾讯用户开放日上,腾讯音视频实验室带着3D位置音效解决方案,向所有用户亮相,为用户提供360度立体空间的 ...

  9. person小项目

    所用的IDE为idea,数据库用的是Navicat for MySQL. 好了,开始正题,今天要做person小项目,有以下几个步骤: Navicat for MySQL数据库的构建,以及如何建per ...

随机推荐

  1. 【ZJOI2017 Round1练习&BZOJ4774】D3T2 road(斯坦纳树,状压DP)

    题意: 对于边带权的无向图 G = (V, E),请选择一些边, 使得1<=i<=d,i号节点和 n − i + 1 号节点可以通过选中的边连通, 最小化选中的所有边的权值和. d< ...

  2. 原 linux添加虚拟ip(手动vip和keepalived方式)

    https://blog.csdn.net/dear_snowing/article/details/68066544 https://www.cnblogs.com/liuyisai/p/59906 ...

  3. Layui栅格系统与后台框架布局

    一.栅格布局规则: 1. 采用 layui-row 来定义行,如:<div class="layui-row"></div> 2. 采用类似 layui-c ...

  4. I/O---BufferedInputStream及相关类介绍

    关于BufferedInputStream 是java提供的具有缓存作用的字节输入流.与之对应的还有BufferedOutStream 和 BufferedRead 和BufferedWriter 这 ...

  5. CSS3(各UI元素状态伪类选择器受浏览器的支持情况)

    选择器 Firefox Safari Opera IE Chrome E:hover Y Y Y Y Y E:active Y Y Y N Y E:focus Y Y Y Y Y E:enabled ...

  6. 我的arcgis培训照片14

    来自:http://www.cioiot.com/successview-562-1.html

  7. 【剑指Offer面试题】 九度OJ1385:重建二叉树

    题目链接地址: pid=1385">http://ac.jobdu.com/problem.php?pid=1385 题目1385:重建二叉树 时间限制:1 秒内存限制:32 兆特殊判 ...

  8. Centos samba install

    Ready Change Root Password passwd root 在提示下建立新密码 静态IP vi /etc/sysconfig/network-scripts/ifcfg-eth0  ...

  9. 【c语言】统计一个数字在排序数组中出现的次数

    // 题目:统计一个数字在排序数组中出现的次数. //  比如:排序数组{1.2,3,3,3,3,4.5}和数字3,因为3出现了4次.因此输出4 有一种最简单的算法,遍历.可是有比它效率更高的 先看遍 ...

  10. MySQL Study之--MySQL压力測试工具mysqlslap

    MySQL Study之--MySQL压力測试工具mysqlslap 一.Mysqlslap介绍     mysqlslap是MySQL5.1之后自带的benchmark基准測试工具,类似Apache ...