10c语言课程设计坦克大战提高篇 1Word格式.docx
- 文档编号:20004442
- 上传时间:2023-01-14
- 格式:DOCX
- 页数:23
- 大小:91.62KB
10c语言课程设计坦克大战提高篇 1Word格式.docx
《10c语言课程设计坦克大战提高篇 1Word格式.docx》由会员分享,可在线阅读,更多相关《10c语言课程设计坦克大战提高篇 1Word格式.docx(23页珍藏版)》请在冰豆网上搜索。
计算y/x的反正切值。
以弧度表示并介于-pi到pi之间(不包括-pi)。
如需使用角度,需要转换。
floatftan=atan2((y1-y0),(x1-x0));
计算通过点(x1,y1)到点(x0,y0)的连成的直线与X轴之间的夹角。
String.h
externchar*strstr(char*str1,char*str2);
找出str2字符串在str1字符串中第一次出现的位置(不包括str2的串结束符)。
返回该位置的指针,如找不到,返回空指针。
strstr(szName,“feichong”)!
=NULL
说明szName中包含feichong
externintstrcmp(constchar*s1,constchar*s2);
比较字符串s1和s2。
当s1<
s2时,返回值<
0
当s1=s2时,返回值=0
当s1>
s2时,返回值>
strcmp(szName,“feichong_0”)==0
说明szName与feichong_0相等
FunCodeAPI
floatdGetScreenLeft();
获取屏幕左边界值
floatdGetScreenRight();
获取屏幕右边界值
floatdGetScreenTop();
获取屏幕上边界值
floatdGetScreenBottom();
获取屏幕下边界值
floatdGetSpritePositionX(constchar*szName);
获取精灵中心点的X坐标值
szName–精灵名称。
所有API均相同。
游戏中的精灵的名称不能相同。
floatdGetSpritePositionY(constchar*szName);
获取精灵中心点的Y坐标值
floatdSetSpritePositionX(constchar*szName);
设置精灵中心点的X坐标值
floatdSetSpritePositionY(constchar*szName);
设置精灵中心点的Y坐标值
voiddSetSpritePosition(constchar*szName,constfloatfPosX,constfloatfPosY);
设置精灵中心点的X和Y坐标值,用来将精灵放置在某个指定位置。
dSetSpritePosition(“feichong_0”,0,0);
将名称为”feichong_0”的精灵的中心点设置在坐标(0,0)上
voiddSetSpriteLinearVelocityX(constchar*szName,constfloatfVelX);
设置精灵X轴方向速度
voiddSetSpriteLinearVelocityY(constchar*szName,constfloatfVelY);
设置精灵Y轴方向速度
voiddSetSpriteLinearVelocity(constchar*szName,constfloatfVelX,constfloatfVelY);
设置精灵X轴和Y轴方向速度
floatdGetSpriteRotation(constchar*szName);
获取精灵的旋转角度
原图的角度
调整后的角度
获得的旋转角度即为两张图片的角度差
floatdSetSpriteRotation(constchar*szName,floatfRot);
设置图片的旋转角度
fRot>
0,图片顺时针旋转;
fRot<
0,图片逆时针旋转。
voiddSetTextValue(constchar*szName,intiVal);
设置文字精灵的整数数值
dSetTextValue(“score”,100);
名称为score的文字精灵显示100
voiddSetSpriteVisible(constchar*szName,boolbVisible);
设置精灵可见或不可见
bVisible为true,可见;
为false,不可见。
voiddShowCursor(constboolbShow);
设置鼠标可见或不可见
bShow为true,可见;
voiddDeleteSprite(constchar*szName);
删除精灵
booldIsPointInSprite(constchar*szName,constfloatfPosX,constfloatfPosY);
判断某个坐标点(fPosX,fPosY)是否在精灵内部
常用于判断一个物体是不是碰到另外一个物体
booldCloneSprite(constchar*szSrcName,constchar*szTarName);
复制一个精灵。
1–复制成功;
0–复制失败。
地图中没有找到对应名称的精灵用于复制。
做法:
一般在地图上摆放一个精灵作为模板,并设置好各种属性。
不仅复制图片,还复制属性。
szSrcName–作为模板的精灵
szTarName–新的精灵名称
voiddSetSpriteWorldLimit(constchar*szName,constEWorldLimitLimit,constfloatfLeft,constfloatfTop,constfloatfRight,constfloatfBottom)
设置精灵的世界边界,精灵碰到边界时,会激发精灵与边界的碰撞事件。
因此,设置精灵位置时,考虑到精灵自身大小,最好离开边界一段距离。
fLeft-左边界值
fTop-上边界值
fRight-右边界值
fBottom-下边界值
Limit-统一使用
WORLD_LIMIT_NULL
voiddSpriteMoveTo(constchar*szName,constfloatfPosX,constfloatfPosY,constfloatfSpeed,constbooliAutoStop);
让精灵从当前位置飞向另外一点
fPosX:
目标点的X坐标值
fPosY:
目标点的Y坐标值
fSpeed:
移动速度
iAutoStop:
移动到终点之后是否自动停止,true停止false不停止
intdRandomRange(constintiMin,constintiMax);
获取一个位于[iMin,iMax]之间的随机整数
intd=dRandomRange[0,3]
d值可能为0,1,2或3
五、程序初步设计
如果程序规模比较小的时候,我们习惯一上手就写代码,边写边调整。
但是当程序越来越大,代码越来越多的时候,如果我们还用这种方式编程,程序写到一半的时候,你可能会恨不得重写一遍。
此,我们在写代码之前,先把程序功能细化一下,并初步设计好程序结构,包括数据结构和自定义函数。
有了比较清晰的思路以后,再开始开发程序。
在本项目中,我们要操作的对象有6个:
玩家坦克、敌方坦克、玩家子弹、敌方子弹、墙、玩家指挥部。
其中,墙和指挥部都比较简单,主要是前4种,而且它们有共通性:
名称、速度、位置,因此,可以考虑用一个结构体来表示。
此外,我们需要用一种数据结构来管理它们。
由于敌方坦克、子弹的数量都无法事先确定,所以我们选择链表而不是数组来管理它们。
structWeapon{
charszName[128];
//精灵名称
floatfPosX,fPosY;
//精灵坐标
floatfSpeedX,fSpeedY;
//X和Y方向上速度
floatfFireTime;
//敌方坦克距下一次开炮的剩余时间
intiHp;
//生命值
intiDir;
//朝向:
0-上方;
1-右方;
2-下方;
3-左方
intiType;
//类型:
0-我方坦克;
1-敌方坦克;
2-我方
//子弹;
3-敌方子弹
Weapon*pNext;
//指向下一个节点的指针
};
其中,iDir和iType用不同整数表示不同含义。
如果在小程序中,我们可以在代码里直接调用这些整数,但是想象一下下面情况:
如果你连续写了三小时代码,你还能清晰记得1表示什么含义吗?
你时不时需要找到Weapon结构体定义查看这些数字的含义,然后再引用,出错的概率有多大?
如果你一不小心,在某处搞错了,比如要处理的是敌方坦克,你却引用2,需要多少时间才能把错误找出来?
因此,在做一个比较大的程序时,我们强烈建议用定义一个枚举类型,用我们熟悉的单词来表示这种数字的含义。
enumDirection{
UP=0,//上方
RIGHT=1,//右方
DOWN=2,//下方
LEFT=3//左方
enumRole
{
MYTANK=0,//我方坦克
ENEMYTANK=1,//敌方坦克
MYBULLET=2,//我方子弹
ENEMYBULLET=3//敌方子弹
除此之外,我们还需要定义一些全局变量来控制程序,根据程序需求,我们目前能考虑到表示游戏状态的变量、表示游戏得分的变量、游戏剩余时间变量以及距离下一辆坦克产生的时间等变量;
//游戏地图,0表示此处为空,1表示此处有墙。
根据游戏空间大小、墙以及坦克大小,
//我们把地图分成11行,13列,每格大小刚好放一块墙。
intiMap[11][13];
正如前面所示,我们用枚举类型来表示一些数字的含义。
出于同样的原因,我们也定义一些全局常量来存储某些数值。
constfloatGAME_TIME=30.f;
//一局游戏时间
constfloatCREATE_TANK_TIME=5.f;
//每批次生成坦克的时间间隔
constfloatTANK_SPEED=5.f;
//坦克速度
constfloatBULLET_SPEED=8.f;
//子弹速度
constfloatFIRE_TIME=2.f;
//坦克开炮时间间隔
constfloatWORLD_LEFT=-26.f;
//游戏场景边界左值
constfloatWORLD_TOP=-22.f;
constfloatWORLD_RIGHT=26.f;
//游戏场景边界左值
constfloatWORLD_BOTTOM=22.f;
好处有两点:
第一、跟枚举类型一样,用有具体含义的单词,在具体调用时容易记住,不会搞错;
第二、如果我们需要调整这些数值,只需在全局常量初始化这里调整就可以了。
比如我们要调整坦克速度,没有定义全局常量的话,我们就要找到各处代码用到坦克速度赋值的地方修改。
这样,既麻烦又容易出错。
程序本身由一个main.cpp文件组成,包含7个函数,一个主函数WinMain和6个事件函数(键盘按下、键盘弹起、鼠标移动、鼠标点击、精灵与精灵的碰撞、精灵与世界边界的碰撞)。
我们增加两个文件,List.h和List.cpp,主要用来声明和定义结构体以及链表操作的函数。
链表操作至少包括下面四个操作:
AddToList//往链表里添加一个节点;
DeleteNode//从链表里删除一个节点;
FindeNode//从链表里查找一个节点;
DeleteList//删除整个链表。
一局游戏结束,我们需要把本局游戏中还没删除的精灵全部删除,从而保持下一局游戏的“干净”,所以往往需要删除整个链表。
载入地图、玩家坦克运动、敌方坦克的生成、坦克发射子弹,我们也可以考虑定义单独的函数来完成:
LoadMap//载入地图
MoveMyTank//玩家坦克运动
CreateEnemyTanks//敌方坦克生成
OnFire//坦克发射炮弹
本游戏,大部分功能都是通过碰撞来实现的,比如玩家坦克子弹击中敌方坦克,就是玩家子弹与敌方坦克的碰撞。
子弹到了游戏界面外,就是子弹与世界边界的碰撞。
我们通过下面表格,把整个游戏中各种碰撞整理出来,表格中的响应是反应方的响应。
“无”表示不可能发生碰撞。
我们知道,要发生碰撞,碰在一起的两个精灵,必须一方具有“发送碰撞”的属性,另外一方具有“接受碰撞”的属性。
由于敌方坦克与敌方坦克、敌方子弹与敌方子弹也可能发生碰撞,所以需要同时设置“发送碰撞”和“接受碰撞”的属性
参与方方
反应
玩家坦克
发送
敌方坦克
接受
玩家子弹
敌方子弹
墙
玩家指挥部
世界边界
无
游戏结束
停止
后面一辆调头;
对撞,都调头
删除
不处理
顺时针调转一个方向
加分
接受
均删除
无
根据上面表格,我们可以定义5个碰撞函数
OnMyTankColOther//玩家坦克与其他精灵碰撞
OnEnemyTankColOther//敌方坦克与其他精灵碰撞
OnBulletColOther//子弹与其他精灵碰撞。
子弹碰上其他精灵,本身都是被
//删除,比较简单,因此两种子弹合并起来
OnWallColOther//墙与其他精灵碰撞
OnGoalColOther//玩家指挥部与其他精灵碰撞
其中,col是collision(碰撞)的缩写。
精灵与世界边界的碰撞,比较简单,我们直接在dOnSpriteColWorldLimit函数中完成。
我们现在对整个程序架构有了一定了解。
现在可以开始编程了,在编程的过程,我们还会根据细节进一步完善。
六、实验指南
实验一游戏开始和结束
【实验内容】
步骤一、按空格键,游戏开始,“空格开始”字样消失,设置初始时间为30。
步骤二、按WASD键,控制坦克上下左右运动。
步骤三、游戏开始后,右上角实时显示剩余时间。
步骤四、当超过30秒,游戏结束,重新显示“空格开始“字样,游戏时间设为0,坦克回到初始位置。
【实验思路】
在List.h中定义Weapon结构体和两个枚举类型。
定义全局变量和全局常量。
定义MoveMyTank函数,控制玩家坦克上下左右移动。
【实验指导】
1、创建“TankWar”工程,并且添加List.h文件,并在List.h中定义Weapon结构体和Role、Direction两个枚举类型。
参考“程序初步设计”;
2、接着我们要定义一个游戏状态的变量,依次变量来判断游戏是否开始,并且还要定义我们控制的坦克精灵的对象指针;
3、dOnKeyDown函数是处理键盘按下事件的。
一局游戏还未开始,按下的空格键,游戏开始。
此时应该“空格开始”消失,显示本局的游戏时间以及得分等信息,
4、运行程序,按下空格键,看看游戏是否按要求运行。
5、游戏开始后,开始计时。
游戏时间到,一局游戏结束,游戏恢复到初始界面。
要判断游戏时间,我们需要在WinMain的主循环中进行处理。
//游戏主循环
while(dEngineMainLoop()){
//获取游戏屏幕刷新一次的时间间距
floatfTimeDelta=dGetTimeDelta();
if(g_bStart)
{
g_fGameTime-=fTimeDelta;
if(g_fGameTime>
0.f)//一局游戏进行中
{
}
else//一局游戏结束
g_bStart=false;
}
else//游戏结束后处理
}
6、一局游戏开始后,在WinMain函数中的主循环里,通过dSetTextValue把(int)g_fGameTime的数值显示在名为“time”的文字精灵里,从而实时显示剩余时间(显示整秒的时间)。
7、一局游戏进行中,每循环一次,g_fGameTime减去一次刷屏的时间。
当g_fGameTime≤0,说明一局游戏时间已完,游戏停止,重新显示“空格开始“字样。
注意:
g_iStart要改为0,否则下一局就不能正确判断游戏开始或结束。
8、我们定义一个MoveMyTank函数来处理玩家坦克的运动。
因为玩家坦克是用键盘控制的,所以需要传递两个参数,一个用来表示按下或松开了哪个键盘,一个用来表示是按下还是松开。
按下W键,设置玩家坦克向上的速度;
按下A键,设置向左速度;
按下S键,设置向下的速度,按下D键,设置向右速度。
松开键盘,坦克速度为0。
此外,按下键盘时,还需要相应设置坦克朝向iDir。
如果函数定义写在函数调用之后,需要在函数调用前面进行声明。
voidMoveMyTank(intiKey,intiPress)
9、完成函数,我们在dOnKeyDown和dOnKeyUp函数中进行调用。
//按下键盘
if(g_bStart)//一局游戏进行中
if(iKey==KEY_W||iKey==KEY_A||iKey==KEY_S||iKey==KEY_D)
{
MoveMyTank(iKey,1);
//松开键盘
XX文库-让每个人平等地提升自我if(g_bStart)
if(iKey==KEY_W||iKey==KEY_A||iKey==KEY_S||iKey==KEY_D)
{
MoveMyTank(iKey,0);
10、玩家坦克开到边界时,让它停止运动。
我们在dOnSpriteColWorldLimit函数中,设设置玩家坦克速度为0。
if(strcmp(szName,g_pMyTank->
szName)==0)//玩家坦克
dSetSpriteLinearVelocity(g_pMyTank->
szName,0.f,0.f);
11、因为增加了新功能,我们在一局游戏结束时,就必须考虑到新情况:
游戏结束时,坦克已经不在原来位置了;
坦克朝向发生变化;
游戏结束的时候,玩家正好按下某个控制键,给坦克设置了速度。
我们该如何处理这三种情况?
实验二玩家坦克在街道中运行
步骤一、一局游戏开始时载入地图。
步骤二、一局游戏结束后删除地图。
步骤三、玩家坦克碰到墙的话,不能继续前行。
利用数组的值来载入地图,值为0,说明此处为空;
值为1,说明此处有墙。
游戏开始后载入地图,但只能载入一次。
如果不停载入,会占有大量资源。
游戏结束时,需要把地图中墙都删除。
创建一个新的精灵,首先需要给它命名。
有规则地给精灵命名,有利于我们对精灵进行处理。
我们按照墙生成的先后顺序,分别给精灵命名为:
wall0,wall1,wall2…
设置玩家坦克的碰撞属性,并专门定义一个函数处理玩家坦克发生碰撞后,玩家坦克的响应。
1、增加新的全局变量定义
intg_iWallCount=0;
//记录墙的数量
intg_iMap[11][13];
//地图数组
一局游戏结束后,我们需要卸载地图,也就是把墙都删除了。
因此,我们需要知道墙的数量。
2、定义LoadMap函数,用来载入地图。
定义一个局部数组并初始化,然后依次赋值给全局数组。
局部数组初始化如下:
intiMap[11][13]=
{0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,1,0,1,0,1,0,1,0,1,0,1,0},
{0,1,0,1,0,1,1,1,0,1,0,1,0},
{0,0,0,0,0,1,1,1,0,0,0,0,0},
{0,1,0,1,0,0,0,0,0,1
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 10c语言课程设计坦克大战提高篇 10 语言 课程设计 坦克 大战 提高