C语言课程设计黄金矿工(提高篇)

发布时间:2018-12-16 06:15:28

C语言课程设计--黄金矿工

一、实验内容

玩家通过键盘的按键控制矿工抓取金块,将钩子碰触到的金块抓取过来。

要求如下:

1. 游戏的初始界面如下图(一),单机键盘上的空格键进入游戏,进入后界面如图(二),金块的总数是20,大小位置是随机的。

2. 在没有抓取状态下,钩子左右摆动,此时矿工的是静止的。当钩子摆动到一定角度,玩家可以单击键盘上的上下左右键中的下方向键控制矿工伸出长钩,抓取金子,此时矿工是向下摇动转轴。获取到金子往回拉后,矿工是不断转动转轴,知道金子拉动到钩子初始处,矿工恢复静止,钩子继续左右摇摆,直到玩家再次单击向下方向键。

3. 伸出的钩子如果碰触到金子,则钩子和金子一起往回拉,回收的速度根据抓取到的金子的大小变化而变化,金子越大,回拉的速度越慢,反之亦然。如果钩子没有碰触到金子,而是碰触到左右和下的边界,则钩子保持原来的速度往回收。

4. 抓取到的金子拉回到转轴处消失,此时金子数目减少一个。如果玩家将所有金子抓取完,游戏返回到如图(一)的初始界面。

图(一)

图(二)

二、实验指南

实验一 开始实验

【实验任务】

步骤一、打开FunCode,创建一个的C语言项目;

步骤二、导入Goldman模板。

【实验思路】

按实验指导完成。

【实验指导】

1、 打开FunCode,点击“项目”菜单,选择“创建C语言工程”

注意:工程名名称要求字母开头,只能包含字母和数字,且名字中间不能有空格

2、 点击菜单“项目”中的“导入地图模块”,如图一。跳出一个对话框,选中“Goldman”模板,点击“导入到工程”按钮,如图二。

3、 导入成功后的,界面如下图所示:

实验二 单击空格键,开始游戏

【实验内容】

步骤、启动游戏显示“空格开始”,单击空格键进入游戏初始界面。

【实验思路】

系统会自动响应dOnKeyDown函数来响应键盘按下消息,这部分代码实现在main.cpp里。我们要做的就是通过在main.cppdOnKeyDown函数里实现我们的代码。当用户单击键盘上的空格键之后,设置GameBegin即“空格开始”精灵不可见。

【实验指导】

1、 首先游戏会有不同的游戏状态,我们设置全局变量g_iGameState,分别用游戏状0 表示游戏结束等待开始状态;游戏状态1表示按下空格键开始,初始化游戏;游戏状态2表示游戏进行中;

2、 有了以上游戏状态的定义,我们就可以将游戏的主流程写出来;由于游戏是一直进行的,所以一下要写在while循环当中:

switch( g_iGameState )

{

// 初始化游戏,清空上一局相关数据

case 1:

{

GameInit();

g_iGameState = 2; // 初始化之后,将游戏状态设置为进行中

}

break;

// 游戏进行中,处理各种游戏逻辑

case 2:

{

// 金子数量大于0的时候,继续游戏

if( g_iGoldCount > 0 )

{

GameRun( fTimeDelta );

}

else

{

// 游戏结束。调用游戏结数函数并把游戏状态修改为结束状态

g_iGameState = 0;

GameEnd();

}

}

break;

// 游戏结束/等待按空格键开始

case 0:

default:

break;

};

这是游戏的总体框架,那么我们就来完成这个游戏;

3、 我们已经知道,系统会自动检测键盘的按下消息,即main.cpp里面的dOnKeyDown函数。系统会捕获按下什么键,是否同时按下Alt键或Shift键或Ctrl键,通过dOnKeyDown的参数传递进来,供编程者使用。

4、 键盘的键值我们通过枚举类型给出,其中空格对应的键值为KEY_SPACE,所以当ikey参数的值为KEY_SPACE且游戏的状态为0,即g_iGameState的值为0,说明用户单击了空格键,之后将g_iGameState的值改为1,说明游戏开始,并且把“游戏开始”精灵隐藏掉。

5、 如果是空格键按下,则说明现在游戏开始,说明游戏状态进入1,我们在GameInit中调用dSetSpriteVisible函数将“空格开始”隐藏;这里我们就不在添加代码。

至此,实验二完成,单机空格键,“空格开始”消失,游戏进入初始化状态。

实验三 钩子左右摇摆

【实验内容】

步骤、钩子在等待抓取状态下左右摇摆

【实验思路】

钩子的摆动实际是动画精灵以某个点为中心位置,在某个角度范围来回旋转。在FunCode中,游戏不断刷新屏幕,每次时间为几微秒,每次刷新相应改变精灵的角度,就能达到旋转的效果。

游戏每刷新一次屏幕,while循环执行一次,并且通过参数fTimeDelta把刷新屏幕的时间传递进去。因此,在该函数中,通过精灵的旋转速度、旋转时间就可以获得每个时刻,精灵的旋转角度。最后,利用FunCode提供的API接口dSetSpriteRotation函数,设置精灵每个时刻对应的角度,从而获得精灵旋转的动画效果。

具体到钩子左右摆动,要考虑的因素有四个:摆动方向(从左往右、从右往左)、摆动速度、摆动时间、摆动的角度范围。

定义两个变量g_iHookRotToLeft表示钩子当前是往左摆动还是往右摆动( 1 -左, 0 - )g_fHookRotation表示钩子当前转动的朝向。判断当 g_iHookRotToLeft 不为0的时候(钩子往左摆),将g_fHookRotation加上上面计算的本次旋转的度数。如果g_fHookRotation大于180度,则将其改为180度,并且将g_iHookRotToLeft设置为0(往右摆)。往左到头后,开始往右摆。往右摆与往左摆相反,但是算法是一致的定义变量g_iHookRotToLeft表示钩子当前是往左摆动还是往右摆动( 1 -左, 0 - )。定义变量g_fHookRotation表示钩子当前角度。当前角度=摆动速度×摆动时间。

规定钩子摆动弧度,从左往右或从右往左正好都是摆动到水平位置,因此钩子摆动角度的范围正好是[0, 180]。从右往左摆时,摆动大于180度时,设置当前角度为180度,钩子变为从左往右摆。从左往右摆时,摆动小于0度时,设置当前就得为0度,钩子变为从右向左摆。

【实验指导】

1、 根据实验思路,为了实现钩子左右摇摆,我们需要定义获得金子的状态、钩子的角度、是否再往左方向摆动、以及钩子的坐标点等全局变量,并且在初始化函数GameInit中进行初始化

2、 Main.cpp文件中,定义GameRun函数,要实现钩子左右摇摆,则判断当g_iHookRotToLeft 不为0的时候(钩子往左摆),将g_fHookRotation加上上面计算的本次旋转的度数。如果g_fHookRotation大于180度,则将其改为180度,并且将g_iHookRotToLeft设置为0(往右摆)。往左到头后,开始往右摆。往右摆与往左摆相反,但是算法是一致的。思路如下:

1) 如果当前为等待抓取状态,那么钩子以一定的角速度左右摇摆,如果钩子左摆的角度大于了180度,就给角度变量赋值为180度,并改变是否往左摇摆的变量为否,这样钩子就可以往右摇摆了;

2) 如果往右摇摆的角度小于了0度,那么就给角度变量赋值为0度,并且改变是否往左摇摆的变量,这样钩子就可以不停的左右摇摆。

到此实验三完成,游戏最开始钩子左右摇摆动作的实现了。

实验四 显示金子

【实验内容】

步骤一、初始化金子实例

步骤二、随机显示金子

步骤三、保存初始化的每一个金子

【实验思路】

以我们在实验一中添加的一个金子精灵作为模板,定义好金子精灵数量,调用dCloneSprite函数,使用一个for循环来复制金子。这里可以在for循环里面嵌入if判断来实现复制不同大小的金子精灵,最后在地图上随机显示金子精灵。

【实验指导】

1、 Main.cpp中添加要赋值金子的个数和金子分布的世界边界的坐标等全局变量;并在GameInit函数中添加代码,初始化全局变量。

2、 接着在使用if语句,以限定金子最终在地图上的显示范围不能超过世界边界,数字部分只是一个参考值,自己也可以定义在一个合理的范围内。

// 以下变量只需要初始化一次

static int iInitedHookPos = 0;

if( 0 == iInitedHookPos )

{

iInitedHookPos = 1;

// 钩子初始位置值初始化

g_fHookStartPosX = dGetSpritePositionX( "GoldHook" );

g_fHookStartPosY = dGetSpritePositionY( "GoldHook" );

// 金子可以出现的边界范围初始化

g_iGoldBornMinX = dGetWorldLeft() + 5;

g_iGoldBornMaxX = dGetWorldRight() - 5;

g_iGoldBornMinY = dGetWorldTop() + 20;

g_iGoldBornMaxY = dGetWorldBottom() - 5;

}

3、 if后面,我们需要用一个for循环来创建所有的金子。我们规定金子的大小为三种,用iSize来记录,分别是iSize=4iSize=6iSize=8。数量的比例是10个,6个,4个(这个是在金子总数为20的前提下,如果总数不是20,根据比例创建)。我们可以再for循环里面通过if判断循环变量iLoop的大小范围为创建不同大小的金子。设置金子大小的函数分别为SetSpriteWidth(设置宽度)dSetSpriteHeight(设置高度),最后使用FunCode提供的随机函数随机生成每个金子的(x,y)坐标放入地图:

其中(float)iSize是类型转换,因为iSizeint型,而SetSpriteWidth函数的参数是float型,如果直接传进去可能会丢失数据,所以需要先将int型转换为float型。

4、 最后添加下面代码,恢复挖金者的动作。

// 播放挖金者的动作(恢复初始守候动作)

dAnimateSpritePlayAnimation( "GoldMan", "GolderManAnimation2", 0 );

至此,实验四显示金子的部分就完成了。

实验五 抓取金子

【实验内容】

步骤一、从缆绳器那里画根线到钩子上

步骤二、检测钩子和金子的碰撞

步骤三、实现金子绑定在钩子上并往回拖

步骤四、回拖之后使金子消失,金子数目减一

【实验思路】

从缆绳器画线到钩子上:

在地图上创建精灵的链接点,然后获取他们的坐标位置,调用FunCodedDrawLine函数即可。

钩子与金子的碰撞:

系统检测到两者的碰撞之后会调用dOnSpriteColSprite函数,所以我们只要在此函数中实现碰撞之后钩子抓取金子的代码就可以了。这里需要注意的是钩子抓取金子之后的速度处理,抓的金子越大,钩子回收的速度就会越慢。

【实验指导】

1、 打开funcode,单击地图上的矿工精灵图像,在显示框的上面五个选择左数第二个“编辑次精灵的链接点”,按如图所示依次单击这位置显示0,保存即可。

2、 同上,点击地图上的钩子精灵,选择“编辑此精灵的链接点”,依次单击这三个位置显示0,1,2,最后保存即可。

3、 首先在Main.cpp添加全局的字符数组变量,用它来存放抓到的金子的名字并定义空钩子在抓取金子的时候移向钩子的速度变量,定义完成之后在GameInit中初始化;

4、 精灵的碰撞方式为:当A移动中碰上B时,如果A是可以产生碰撞的,B是可以接受碰撞的,则这2个物体会产生碰撞,精灵碰撞的API将被调用。否则无碰撞发生

通过调用库函数dSetSpriteCollisionSend设置钩子可以发送碰撞,通过dSetSpriteCollisionReceive函数设置金子可以接收碰撞,这样,当在地图上钩子和金子相碰时,系统就能检测到。

5、 首先实现从缆绳器画线到钩子。首先在Main.cpp进行声明

void DrawHookLine();

然后,添加函数的定义。画线的原理是先获取缆绳器的坐标和钩子的坐标,然后调用DrawLine函数即可。

函数定义如下

void DrawHookLine()

{

// 首先,从矿工精灵上获取一个缆绳链接点作为绳子的起始点(该链接点在编 // 辑器里编辑好)

float fStartX = dGetSpriteLinkPointPosX( "GoldMan", 1 );

float fStartY = dGetSpriteLinkPointPosY( "GoldMan", 1 );

// 绳子终点在钩子精灵上获取(该链接点在编辑器里编辑好)

float fEndX = dGetSpriteLinkPointPosX( "GoldHook", 1 );

float fEndY = dGetSpriteLinkPointPosY( "GoldHook", 1 );

// 在这两点之间划线.线的颜色红绿蓝值都为50,即灰色

dDrawLine( fStartX, fStartY, fEndX, fEndY, 2.f, 0, 50, 50, 50, 255 );

}

dDrawLine函数参数参见文档前面部分的介绍或者是CommonAPI.h中关于 dDrawLine的介绍。

6、 main.cpp文件里面的主函数中的while循环每次调用GameMainLoop函数时能保证调用到DrawHookLine函数,保证了钩子与缆绳器之间的线是一直连着的。

while循环的最后面添加如下代码:

// 画钩子的缆绳线。不管游戏是什么状态,这根缆绳线都要画出来

DrawHookLine();

7、 接下来要实现单击键盘上的下方向键伸出钩子。如实验二所说,系统检测到键盘按下,会响应main.cpp里面的dOnKeyDown函数,向下方向键的宏定义是KEY_DOWN,因此我们只要判断dOnKeyDown获取的键值ikey等于它,并且游戏是正在进行的(g_iGameState等于2),钩子没有抓到金子(g_iGetGoldState等于0)即可。之后利用系统提供的函数给钩子一个向前的速度,在播放挖金者手臂往下压的动作即可。

main.cpp里面dOnKeyDown函数if后面添加如下代码:

// 当前处于游戏进行中,按下向下的方向键,钩子伸出抓取金子

else if( KEY_DOWN == iKey && 2 == g_iGameState && 0 == g_iGetGoldState )

{

// 设置抓取状态为:钩子往外伸

g_iGetGoldState = 1;

// 以当前朝向给钩子一个向前的速度

dSetSpriteLinearVelocityPolar("GoldHook",g_fEmptyHookSpeed, g_fHookRotation );

// 播放挖金者的动作(一个胳膊往下压的动作)

dAnimateSpritePlayAnimation( "GoldMan", "GolderManAnimation1", 0 );

}

8、 实现钩子抓金子的精灵与精灵碰撞事件。

// 首先判断钩子是否伸出,已经游戏是否真在进行,否则推出函数:

if( 2 != g_iGameState || 1 != g_iGetGoldState )

return;

//判断是否是钩子与金子进行碰撞(即这两个名字字符串里必须一个是金子名,一 //个是钩子名)

if( stricmp("GoldHook",szSrcName)!=0&&stricmp("GoldHook",szTarName)!=0)

return;;

// 找到哪个是金子的名字

const char *szGoldName = NULL;

if( strstr( szSrcName, "GoldBlock" ) )

szGoldName = szSrcName;

else if( strstr( szTarName, "GoldBlock" ) )

szGoldName = szTarName;

else

return;

9、 得到金子实例之后,使用FunCode给的绑定函数SpriteMountToSpriteLinkPoint()将金子绑定在钩子上随钩子移动:

// 将金子挂接到钩子的2号挂接点上

dSpriteMountToSpriteLinkPoint( szGoldName, "GoldHook", 2 );

// 设置抓取状态为:钩子往回收,抓取到东西。记录金子名字

g_iGetGoldState = 3;

strcpy( g_szCurGetGold, szGoldName );

// 根据金子大小,计算钩子的往回收的速度,越大的越慢。

// 算法:之前设置的金子大小为4,6,8,用10减去该大小再除以10,得到3个小// 数:0.6,0.4,0.2.该小数乘以空钩子的速度即得到挂接不同金子后的实际速度

float fWidth = dGetSpriteWidth( szGoldName );

float fSpeed = ((10.f - fWidth) / 10.f) * g_fEmptyHookSpeed;

// 钩子往初始位置移动

dSpriteMoveTo( "GoldHook", g_fHookStartPosX, g_fHookStartPosY, fSpeed, 1);

// 播放挖金者的动作(胳膊来回动的动作)

dAnimateSpritePlayAnimation( "GoldMan", "GolderManAnimation3", 0 );

10、 金子抓取之后回收到缆绳器之后,系统判断钩子的是否往回运动,是的话判断钩子是否回到终点,若是则调用dSpriteDismount函数取消金子与钩子的绑定,同时调用dDeleteSprite函数删除该金子精灵,再将金子精灵的总数减一。在GameRun函数后面添加如下代码:

// 如果当前为钩子往回归位,则判断是否已经运动到终点。到了终点,则又开始摇// 摆、等待

else if( 2 == g_iGetGoldState || 3 == g_iGetGoldState )

{

// 判断是否移动到终点(判断的依据是XY方向的移动速度为0,即金子是否 // 已经停止移动)

float fSpeedX = dGetSpriteLinearVelocityX( "GoldHook" );

float fSpeedY = dGetSpriteLinearVelocityY( "GoldHook" );

// 当前速度不为0,还在运动中

// 浮点数是否为0,不能直接判断 == != 0

if( fSpeedX > 0.00001f || fSpeedX < -0.00001f || fSpeedY > 0.00001f || fSpeedY < -0.00001f )

return;

// 速度为0,钩子回到初始点,开始下一轮的抓取

// 当前抓取到金子,将抓取到的金子释放并删除之。然后将金子数量减一

if( 3 == g_iGetGoldState )

{

g_iGoldCount--;

dSpriteDismount( g_szCurGetGold );

dDeleteSprite( g_szCurGetGold );

}

// 恢复等待状态与播放等待动作

g_iGetGoldState = 0;

dAnimateSpritePlayAnimation( "GoldMan", "GolderManAnimation2", 0 );

}

至此,抓取金子的实验完成。

11、 要令游戏更加完整,在抓取完全部金子之后设置游戏回到初始界面,即显示“空格开始”界面。只需要在Main.cpp中定义GameEnd函数中将“空格开始”显示即可。

至此,抓金子试验完成了。

实验六 钩子碰到边界处理

【实验内容】

步骤、处理钩子碰到世界边界的动作

【实验思路】

通过dSetSpriteWorldLimit函数设置钩子碰到世界边界是需要进一步处理,因而当系统检测到钩子与世界边界碰触后会响应dOnSpriteColWorldLimit函数,从而我们获取参数进行操作。

【实验指导】

1、 Main.cpp中对函数OnSpriteColWorldLimit进行定义,第一个参数是钩子的名称,第二个参数是碰撞到的边界代号,通过main.cpp中的OnSpriteColWorldLimitiColSide参数传入,当其为0时是指左边的世界边界,1为右边边界,2为上边边界,3为下边边界

2、 首先判断钩子是否碰到边界,即传进来的字符串参数szName的值为钩子的名称“GoldHook”,是的话设置钩子的抓取状态为2,即未取到东西,然后钩子按原路径返回,调用dSpriteMoveTo函数,最后播放挖金者胳膊来回动。

至此,本实验结束。

C语言课程设计黄金矿工(提高篇)

相关推荐