3、这是因为,我们创建的类集成了CSprite类,但是却没有把这个类的声明文件include
进来。因此我们在新建类的头文件中,做如下操作: #include \4、编译程序,发现如下提示:
打开CommonClass.h文件,找到CSprite类的定义,可以发现该类没有缺省构造函数,只有一个带参数的构造函数。因此,作为它的子类,构造函数也必须带参数,并且将参数传递给父类。构造函数的声明和定义修改如下: CTankPlayer(const char* szName);
CTankPlayer::CTankPlayer(const char* szName):CSprite(szName) { }
5、为CTankPlayer类添加m_iDir,m_fSpeedX,m_fSpeedY,m_iHp四个成员变量(成员变
量。用来表示坦克在X轴和Y轴方向上的速度以及运行的方向,并且在构造函数中初始化为0。这里规定,m_iDir的值为0、1、2、3,分别表示上、右、下、左。 成员函数的声明和定义如何添加,访问权限是什么,可参考实验一,下文不再继续提示),分别表示运动方向、X轴、Y轴速度以及血量值。本文档的命名采用匈牙利命名法,m_表示类成员变量,i表示整型,f表示float型,sz表示字符指针,g_表示全局变量等。
同时需要在public权限下定义获取和设置这些变量的Get和Set方法,可在LessonX.h文件中完成:
//set方法
void SetHp(int hp) {m_iHp = hp;} void SetDir(int dir) {m_iDir = dir;} void SetSpeedX(float speedX) {m_fSpeedX = speedX;} void SetSpeedY(float speedY) {m_fSpeedY = speedY;} //get方法
8
上海锐格软件有限公司 int GetHp() {return m_iHp;} int GetDir() {return m_iDir;} float GetSpeedX() {return m_fSpeedX;} float GetSpeedY() {return m_fSpeedY;}
6、在CTankPlayer类的构造函数完成上面四个成员变量的初始化。
CTankPlayer::CTankPlayer(const char* szName):CSprite(szName) //对构造函数进行实现 {
m_iDir=0; m_fSpeedX=0.f; m_fSpeedY=0.f;
m_iHp=2;
}
子类对象创建时,要先调用父类的构造函数完成父类部分的构造。如果父类没有默认构造函数,子类的构造函数必须显示调用父类的构造函数。CTankPlayer构造函数调用CSprite类构造函数,并将参数szName的值传递给它,从而将名称为szName的精灵图片与CTankPlayer对象绑定起来。
7、为CTankPlayer类添加Init函数,该函数主要用来完成该类的初始化工作。这里,先
调用setHp方法设置血量。然后调用父类的SetSpritePosition函数将坦克精灵设置在屏幕中央(0,0)处。接下来给坦克精灵设置时间边界的大小,与世界边界的碰撞模式为WORLD_LIMIT_NULL,表示精灵与世界边界碰撞的响应由代码完成,同时设置坦克碰撞模式为发送碰撞和接收碰撞。
void CTankPlayer::Init() {
SetHp(2);
SetSpritePosition(0.f,0.f);
SetSpriteWorldLimit(WORLD_LIMIT_NULL, -26, -22, 26, 22);
SetSpriteCollisionActive(1,1);//设置为可以接受和发生碰撞
SetSpriteVisible(true); }
8、完成CTankPlayer类相关定义后,在CGameMain类中增加一个私有成员变量
m_pTankplayer,类型为CTankPlayer*。 注意在LessonX.h添加头文件:
#include\
9、按下空格键后,程序会调用键盘按下事件响应函数,将m_iGameState的值改为1,
游戏进入开始状态(见实验一)。程序再次执行CGameMain::GameMainLoop函数时,根据m_iGameState的值调用并执行CGameMain::GameInit函数。因此,可以在该函数中创建我方坦克,并调用CTankPlayer::Init函数来完成该类对象的初始化工作。
m_pTankPlayer=new CTankPlayer(\新建一个名字是myPlayer的我方坦克对象
m_pTankPlayer->CloneSprite(\我方坦克克隆在funcode模板中存在的名字为player的坦克,表示新建的坦克对象有现在精灵的所有属性 m_pTankPlayer->Init();
10、接下来,我们为CTankPlayer类添加OnMove函数,参数iKey、bPress分别表示按
下的是哪个按键和按键是否按下。首先声明该函数,访问权限为public:
9
void OnMove(int iKey, bool bPress);
11、接着,完成OnMove方法。
void CTankPlayer::OnMove(int iKey, bool bPress) {
if(bPress) {
switch (iKey) {
case KEY_W: SetDir(0); SetSpeedX(0); SetSpeedY(-8); break; case KEY_D: SetDir(1); SetSpeedX(8); SetSpeedY(0); break; case KEY_S: SetDir(2); SetSpeedX(0); SetSpeedY(8); break; case KEY_A: SetDir(3); SetSpeedX(-8); SetSpeedY(0); break; }
SetSpriteRotation(float(90*GetDir())); //用方向值乘于90得到精灵旋转度数 SetSpriteLinearVelocity(GetSpeedX(),GetSpeedY()); } else {
if(iKey == KEY_W || iKey == KEY_D || iKey == KEY_S || iKey == KEY_A) {
SetSpeedX(0); SetSpeedY(0);
SetSpriteLinearVelocity(GetSpeedX(),GetSpeedY()); } } }
用参数bPress来判断键盘是按下还是弹起。如果bPress为false,表示键盘弹起,且弹起的键是WASD键时,设置坦克的X轴和Y轴移动速度都为0。如果bPress为
10
上海锐格软件有限公司
true,表示键盘按下,根据按下的键,为m_iDir,m_fSpeedX,m_fSpeedY赋予相应的值。SetSpriteRotation和SetSpriteLinearVelocity函数用来设置精灵的角度和线性速度,具体含义参考CommonClass.h文件。 12、在CGameMain类的OnKeyDown函数中,通过调用CTankPlayer类的OnMove方法,
根据按下的键,控制坦克朝指定方向运动。只有游戏进入开始状态,按键才有效。注意:OnMove参数bPress的值为true。在OnKeyDown函数中添加代码如下:
if(m_iGameState == 2) {
m_pTankPlayer->OnMove(iKey, true); }
13、编译并运行程序。按空格键开始,按WASD键,坦克会向规定的方向运动。但是松
开按键后,坦克继续前进,不会停止下来。
14、在CGameMain::OnKeyUp函数中调用CTankPlayer的OnMove方法,参数bPress的
值为false。
void CGameMain::OnKeyUp(const int iKey) {
if(m_iGameState == 2) {
m_pTankPlayer->OnMove(iKey, false); } }
15、编译并运行程序,按下WASD键,坦克运行;松开按键,坦克停止。接下来,我们
来实现坦克运行到黑色区域边界停止运动的效果。
16、当坦克精灵碰到边界时,将精灵的速度都设为0,精灵停止运动。首先,我们需要
给该精灵设置世界边界的碰撞模式。可以在CTankPlayer::GameInit()中完成。 17、用字符串处理函数strstr()来判断碰到世界边界的精灵是否为我方坦克。当碰到世界
边界的精灵名字中含有”player”的时候,strstr会返回一个非空值。因此代码如下:
void CGameMain::OnSpriteColWorldLimit( const char *szName, const int iColSide ) {
if(strstr(szName,\判断碰到世界边界的坦克是否为我方坦克 {
m_pTankPlayer->SetSpriteLinearVelocity(0,0); } }
实验三 坦克开炮
【实验内容】
1、创建子弹类CBullet;
2、通过按键J控制坦克开炮; 【实验运行结果】
11