C语言课程设计桌球.docx
《C语言课程设计桌球.docx》由会员分享,可在线阅读,更多相关《C语言课程设计桌球.docx(15页珍藏版)》请在冰豆网上搜索。
![C语言课程设计桌球.docx](https://file1.bdocx.com/fileroot1/2023-1/26/10a9c4e5-9c79-4abe-99fb-3c71327b18e6/10a9c4e5-9c79-4abe-99fb-3c71327b18e61.gif)
C语言课程设计桌球
C语言课程设计--桌球
一、实验内容
玩家通过移动球杆到单一的桌球处碰撞桌球运动,当桌球于一定速度通过桌面右方的同样颜色的球洞后,则该球进洞,在桌面左边重新分配新球开始游戏。
要求如下:
1.游戏的初始界面如下,其中左边有一个体积较小的桌球,其x方向位置固定,y方向位置随机。
右边有八个颜色不一样,体积较大的球洞,游戏的目的是使用左边的桌球以一定小的速度穿过右边同样颜色的球洞就算桌球进洞,然后重新分配新球。
2.球杆随鼠标运动而运动,单击球杆,桌球获取一个前进的速度同时往前有运动。
桌球的前进方向有一段指示球运动方向的虚线。
3.右侧的球洞以移动的频率不断更换位置。
球洞的x左边不变,更换的是y坐标的位置,即每一轮中每一个球洞移动到下一个球洞的位置,最下边的球洞则移动到第一个球洞位置。
所以的这些动作都要在一个频率的时间里面完成。
因此当游戏一某一个频率进行的时候,右侧的球洞就能实现循环变化的效果。
4.球碰撞到桌面边沿的时候以发射角的角度弹走。
5.球进洞的判断是球以一定的速度通过同样颜色的球洞时,桌球消失,表明球进洞了,此时左侧随机生成新球。
球运动到球洞的速度不能太大,太大则球直接运动过球洞,如果速度太小,则球同样不进洞。
二、实验指南
实验一开始实验
【实验任务】
步骤一、打开FunCode,创建一个的C++语言项目;
步骤二、导入snooker模板。
【实验思路】
按实验指导完成。
【实验指导】
1、打开FunCode,点击“项目”菜单,选择“创建C语言工程”
注意:
工程名名称要求字母开头,只能包含字母和数字,且名字中间不能有空格。
2、点击菜单“项目”中的“导入地图模块”,如图一。
跳出一个对话框,选中“snooker”模板,点击“导入到工程”按钮,如图二。
图一图二
3、导入成功后的,界面如下图所示:
实验二初始化桌球和球杆同时在球的前进方向画虚线
【实验内容】
步骤一、设置球的初始位置,球杆的初始朝向
步骤二、设置球杆随鼠标移动
步骤三、在球的前进方向上画虚线
【实验思路】
在设置初始球的时候要考虑随机本局的球数字,如果和上一局的不一致,则将上局的球拉到屏幕外,将本局的球拉到屏幕内,在Y向上随机摆放球的出生位置,只随机Y方向坐标,不动X方向。
对于球杆则通过系统的dOnMouseMove函数获取到鼠标移动时的坐标位置,然后使用dSetSpritePosition函数同步设置桌球精灵的位置。
通过获取球的X和Y坐标,并求两个坐标平方和的平方根来得到球运动方向向量的大小,利用循环调用系统画线函数画虚线。
【实验指导】
1.在Main.cpp中,初始化几个全局变量,代码如下:
//游戏玩法设计:
上方有8个球洞,分别是数字0-7,球洞间的数字按一定速率滚//动,每局球都随机一个0-7的数字,对应的数字打入对应的球洞才胜利,开始打球//状态
g_iPlayState=0;
g_fRotateTime=0.f;
g_fHoleRollTime=0.f;
2.初始化精灵:
//使用循环,给数组赋值:
//将数组的8个值分别赋值0-7即可,无需随机。
名字使用dMakeSpriteName分//别产生,为BallHole0-BallHole7
//球洞精灵预先摆放在场景中,因此不需要创建
intiLoop=0;
for(iLoop=0;iLoop<8;iLoop++)
{
g_iHoleNumber[iLoop]=iLoop;
strcpy(g_szHoleName[iLoop],dMakeSpriteName("BallHole",iLoop));
}
3.在Main.cpp中填写球和球杆的初始化代码。
//随机本局的球数字,如果和上一局的不一致,则将上局的球拉到屏幕外,将本//局的球拉到屏幕内
intiNewBallNumber=dRandomRange(0,7);
if(iNewBallNumber!
=g_iBallNumber)
{
dSetSpritePositionY(g_szBallName,50.f);
//新球,移动到屏幕中
g_iBallNumber=iNewBallNumber;
strcpy(g_szBallName,dMakeSpriteName("Ball",g_iBallNumber));
dSetSpritePositionX(g_szBallName,-30.f);
}
//在Y向上随机摆放球的出生位置,只随机Y方向坐标,不动X方向
intiPosY=dRandomRange(-25,25);
dSetSpritePositionY(g_szBallName,(float)iPosY);
//获取球拍的初始朝向,只做一次
staticintiInited=0;
if(!
iInited)
{
iInited=1;
g_fOldRotation=dGetSpriteRotation("BallCue");
}
4.设置球杆随鼠标移动而移动。
首先通过系统的dOnMouseMove函数实时获取到鼠标移动的坐标位置参数,然后将该参数传给球杆精灵的dSetSpritePosition函数,这样就能让球杆随鼠标移动了。
由于鼠标在地图中有自己的图标,因此我们需要调用系统的隐藏鼠标的函数ShowCursor将鼠标隐藏掉。
实现步骤如下:
1)在Main.cpp中添加我们自己定义的OnMouseMove函数的定义:
voidCGameMain:
:
OnMouseMove(constfloatfMouseX,constfloatfMouseY)
{
}
2)调用球杆精灵的设置位置函数dSetSpritePosition将鼠标的位置设置为球杆精灵的位置。
在上面的函数里面填入:
dSetSpritePosition("BallCue",fMouseX,fMouseY);
3)使用FunCode提供的API函数dShowCursor将鼠标在地图的图标隐藏掉。
这一步可以在程序初始化的时候就完成。
在Main.cpp的初始化里面的if(!
iInited)判断里面填入一行代码:
dShowCursor(0);
5.在球运动的方向画虚线比较简单。
主要是获取球和球杆连线的向量,然后等到该向量的单位向量大小,调用DrawLine函数,利用for循环,判断循环因子为偶数的时候不画线,奇数的时候才画线。
1)在Main.cpp中,在while循环里面,先判断是否处理可以打球的状态,即球目前还处于静止状态,是的话就进行画线操作。
填入如下代码:
if(0==g_iPlayState)
{
//下面的代码,用于画出球前进方向的一段虚线
//之前的案例里,有段画抛物线弹道轨迹的代码,比较复杂。
这里画虚线比较//简单,可以自己动手实现
//获取球杆和球的位置
floatfBallPosX=dGetSpritePositionX(g_szBallName);
floatfBallPosY=dGetSpritePositionY(g_szBallName);
floatfCuePosX=dGetSpritePositionX("BallCue");
floatfCuePosY=dGetSpritePositionY("BallCue");
}
m_spBall[m_iBallNumber]是指当前桌面上的球。
2)通过上面得到的坐标计算球和球杆之间的向量(fVectorX,fVectorY),在上面的if判断里面添加如下代码。
//有A,B两点坐标,求两点间的向量==>B减去A得到一条由A指向B的向量。
//下面的计算,得到一条球杆坐标指向球坐标的向量
floatfVectorX=fBallPosX-fCuePosX;
floatfVectorY=fBallPosY-fCuePosY;
3)判断球和球杆不重合的时候就执行画线操作。
所以还需要在上面的代码后面添加一个if的判断,同时计算向量(fVectorX,fVectorY)的大小,以及将该向量单位化:
if(fVectorX>0.001f||fVectorX<-0.001f||fVectorY>0.001f||fVectorY<-0.001f)
{
//计算该向量的大小,以及将该向量单位化(sqrt函数是开平方函数,回想下//数学里如何求单位向量)
floatfVectorSize=sqrt(fVectorX*fVectorX+fVectorY*fVectorY);
floatfDirX=fVectorX/fVectorSize;
floatfDirY=fVectorY/fVectorSize;
}
4)用循环,与上一个if判断平行,画出球前进方向的虚线:
//我们用循环画12段首尾相连的线段,如果全部画出来就是一条实线,如果循环//变量为偶数就画,为奇数就不画,那么就得到一条虚线
//画一条线需要前后两个点:
一个点使用LastPos,一个点使用NewPos。
循环12//次,按照下面步骤写代码:
//NewPos等于LastPos加上Dir乘以PosStep(单位向量乘以长度值等于该向量上//的一个坐标点)
//当循环变量为偶数的时候,使用dDrawLine画一根线
//在画了线之后,将NewPos赋值给LastPos
intiLoop=0;
floatfLastPosX=fBallPosX,fLastPosY=fBallPosY;
floatfNewPosX=0.f,fNewPosY=0.f;
floatfPosStep=fVectorSize/10.f;
for(iLoop=0;iLoop<12;iLoop++)
{
//新坐标等于前一坐标加上向量方向上的一个距离值
fNewPosX=fLastPosX+fDirX*fPosStep;
fNewPosY=fLastPosY+fDirY*fPosStep;
//偶数段就画
if(iLoop%2==0)
dDrawLine(fLastPosX,fLastPosY,fNewPosX,fNewPosY,2.f,0,0,255,0,255);
//坐标往前移动(NewPos赋值给LastPos)
fLastPosX=fNewPosX;
fLastPosY=fNewPosY;
}
实验三球洞来回循环滚动
【实验内容】
步骤、循环改变球洞的位置实现来回滚动
【实验思路】
使用循环,将数组值分别往后移动一位,最后一位移动到第一位。
同时移动精灵名字数组,移动对应的精灵Y坐标(移动精灵。
使用dGetSpritePositionY获取精灵的位置,交换一下,用dSetSpritePositionY赋值回去)
思路:
先取出最后一个的值、名字、Y坐标,由后往前遍历数组,依次将前一个的值覆盖当前的值,最后将取出来的值赋值给第一个(这一步是在循环结束后做)
【实验指导】
1.在Main.cpp中,添加球洞精灵的声明:
//8个球洞当前的数字值,0-7来回滚动。
对应的球数字打入对应的球洞才胜利
intg_iHoleNumber[8];
charg_szHoleName[8][64];//8个球洞的精灵名字
floatg_fHoleRollTime=0.f;//球洞循环滚动间隔时间
2.在Main.cp的while循环中初始化球洞精灵变量:
//使用循环,给数组赋值:
//将数组的8个值分别赋值0-7即可,无需随机。
名字使用dMakeSpriteName分//别产生,为BallHole0-BallHole7
//球洞精灵预先摆放在场景中,因此不需要创建
intiLoop=0;
for(iLoop=0;iLoop<8;iLoop++)
{
g_iHoleNumber[iLoop]=iLoop;
strcpy(g_szHoleName[iLoop],dMakeSpriteName("BallHole",iLoop));
}
3.在while循环的if后面添加球洞循环更换的实现代码。
循环算法如下:
先将最后一个球洞的Y坐标保存下来,然后使用一个for循环对第一个至第七个球洞所在的数组进行从后往前的遍历。
每一次循环都先将当前球洞的Y坐标保存下来,然后将下一个球洞的Y坐标赋给当前球洞。
第一次循环的时候将第七个球洞的Y坐标保存下来,然后将最后一个球洞的Y坐标赋给第七个球洞。
第二次循环的时候将第六个球洞的Y坐标保存下来,然后将第七个球洞的坐标赋给第六个,依次类推,共进行六次循环。
最后循环结束,将第一个球洞的Y坐标赋给最后一个球洞。
初始化变量:
intiLoop=0,;
IntiLastData=0;
floatfTempPosY=0.f,;
floatfLastPosY=0.f;
charszLastName[64];
//iLoop是循环因子,iListData记录最后一个球洞的在m_iHoleNumber中的数值。
//隔一定时间滚动一次
g_fHoleRollTime+=fDeltaTime;
if(g_fHoleRollTime>1.f)
{
g_fHoleRollTime-=1.f;
}
4.在上面的if判断里面添加下面代码:
//取出最后一个值
iLastData=g_iHoleNumber[7];
fLastPosY=dGetSpritePositionY(g_szHoleName[7]);
strcpy(szLastName,g_szHoleName[7]);
//从后往前遍历数组。
注意只需要做6次循环(iLoop由7递减至1)
for(iLoop=7;iLoop>0;iLoop--)
{
g_iHoleNumber[iLoop]=g_iHoleNumber[iLoop-1];
//先保存需要更改坐标的精灵的Y坐标作为下一个精灵的坐标,然后再给它//赋值
fTempPosY=dGetSpritePositionY(g_szHoleName[iLoop-1]);
dSetSpritePositionY(g_szHoleName[iLoop-1],fLastPosY);
fLastPosY=fTempPosY;
strcpy(g_szHoleName[iLoop],g_szHoleName[iLoop-1]);
}
//将取出来的最后一个值赋值给第一个
g_iHoleNumber[0]=iLastData;
strcpy(g_szHoleName[0],szLastName);
dSetSpritePositionY(g_szHoleName[0],fLastPosY);
5.至此,球洞循环来回滚动的实验结束。
实验四击打桌球
【实验内容】
步骤一、处理鼠标单击事件,球杆角度变化
步骤二、球杆击打之后,桌球获取一个向前运动的速度
【实验思路】
获取系统鼠标点击的消息,判断游戏是否在进行以及球是否可以击打(即g_iPlayState不等于0),然后球杆旋转,模拟打球的操作。
之后获取球和球杆之间的向量用来计算速度,最后利用dSetSpriteLinearVelocity函数给该球一个速度,同时用dSetSpriteDamping函数给球一个减速系数。
【实验指导】
1.在Main.cpp中添加如下变量声明:
floatg_fRotateTime;//球杆拍下时,做个角度旋转,持续一小段时间,模拟拍下的动作
2.进入Main.cpp中对变量g_fRotateTime进行初始化:
g_fRotateTime=0.f;
3.在dOnMouseClick中填入如下代码:
1)只响应游戏中可以点击打球的状态
if(2!
=g_iGameState||0!
=g_iPlayState)
return;
2)改变球的状态,同时给球杆一个旋转,模拟打球动作,这个过程持续一定的时间
g_iPlayState=1;
g_fRotateTime=0.2f;
dSetSpriteRotation("BallCue",g_fOldRotation+10.f);
3)获取球和球杆之间的变量(通过获取坐标得到)
//球的位置
floatfPosX=dGetSpritePositionX(g_szBallName);
floatfPosY=dGetSpritePositionY(g_szBallName);
//有A,B两点坐标,求两点间向量==>B减去A得到一条由A指向B的向量。
//下面的计算,得到一条鼠标坐标指向球坐标的向量
floatfVectorX=fPosX-fMouseX;
floatfVectorY=fPosY-fMouseY;
4)通过上面的变量计算球的速度,同时让球运动已经添加一个减速系数使其慢慢减速到静止。
//将该向量放大,得到我们想要的速度
fVectorX*=12.f;
fVectorY*=12.f;
//给球设置该速度,以及设置一个速度衰减系数,使其逐渐停止
dSetSpriteLinearVelocity(g_szBallName,fVectorX,fVectorY);
dSetSpriteDamping(g_szBallName,1.6f);
4.球杆按下之后做了旋转,需要对其进行复位。
在while循环的最后面添加下面的代码:
//球杆按下之后,做了个旋转。
时间到了,将球杆复位
if(g_fRotateTime>0.f)
{
g_fRotateTime-=fDeltaTime;
if(g_fRotateTime<=0.f)
{
//球杆旋转复位
dSetSpriteRotation("BallCue",g_fOldRotation);
}
}
至此,本实验结束。
实验五球和桌球台边沿的碰撞
【实验内容】
步骤、判断球和桌球台的判断,改变球的运动方向
【实验思路】
两个精灵之间的碰撞我们已经做过了很多了,系统会自动检测到两个精灵的碰撞,然后就能得到它们的名字,因此我们可以自定义一个函数来接收这两个名字并进行相应的操作即行,额外要考虑的只是球碰撞之后的运动方向问题,这里我们一理想世界作参考,球碰了台沿之后会以物理中反射角的角度运动,速度不变。
【实验指导】
1.打开FunCode,依次在地图上点击下面几个桌球精灵,然后在右侧的“编辑”->“碰撞”里选中“发送碰撞”
2.同样,依次在地图中点击上图中的四个桌球台边沿精灵,分别给左右上下的边起名字为:
VerSide1、VerSide0、HorSide0、HorSide1进入精灵与精灵碰撞函数,碰撞的算法是这样的:
碰到垂直的边的时候,将当前球的X方向速度取反即可得到反弹效果。
水平方向则是对Y取反.如下图:
水平方向上:
3.垂直方向上:
1)判断是否是我们控制的球碰撞到边界
if(strcmp(szSrcName,g_szBallName)==0||strcmp(szTarName,g_szBallName)==0
{
}
2)在上面的if里面再进行判断,如果球碰到水平边(即上下边沿)则按下面代码处理:
3)判断碰撞到垂直还是水平边
if(strstr(szSrcName,"VerSide")||strstr(szTarName,"VerSide"))
{
floatfVelX=dGetSpriteLinearVelocityX(g_szBallName);
dSetSpriteLinearVelocityX(g_szBallName,fVelX*-1.f);
}
4)如果球碰到垂直边(即左右边沿)则按下面代码处理:
elseif(strstr(szSrcName,"HorSide")||strstr(szTarName,"HorSide"))
{
floatfVelY=dGetSpriteLinearVelocityY(g_szBallName);
dSetSpriteLinearVelocityY(g_szBallName,fVelY*-1.f);
}
至此,本实验结束。
实验六桌球进洞
【实验内容】
步骤、桌球以一定速度移动到球洞中,则判为进洞,将球移出屏幕,生成新球
【实验思路】
判断球是否正在运动,是的话则获取该球的速度,同时判断该球的速度是否符合进洞速度要求以及该球的坐标是否在同款颜色的球洞精灵中,是的话则认为球进洞,将球移出屏幕,左侧生成新球。
【实验指导】
1.在Main.cpp的while循环当中,前面以及有过判断球是否运动的if(0==g_iPlayState)操作了,只需要在后面添加一个else语句,判断球进洞的操作在else里面执行。
2.在球洞精灵初始化的后面,填入如下代码:
//判断球是否位于球洞内。
floatfVelX=dGetSpriteLinearVelocityX(g_szBallName);
floatfVelY=dGetSpriteLinearVelocityY(g_szBallName);
//计算速度向量的大小.sqrt是开平方函数
floatfSpeedValue=sqrt(fVelX*fVelX+fVelY*fVelY);
3.判断球的速度,小于30.f时获取球的坐标,g_spBallHole[g_iBallNumber]为当前同款颜色球洞精灵,通过dIsPointInSprite判断球的坐标是否在该球洞精灵里面,是的话重新开局,写在2的后边,代码如下,
//速度判断,球的速度必须低于某个值,速度太大将直接滚过球洞
if(fSpeedValue<30.f)
{
floatfPosX=dGetSpritePositionX(g_szBallName);
floatfPosY=dGetSpriteP