C++语言课程设计一黄金矿工
一、实验内容
玩家通过键盘的按键控制矿工抓取金块,将钩子碰触到的金块抓取过来。
要求如下:
1. 单机键盘上的空格键进入游戏,金块的总数是20,大小位置是随机的。
2. 在没有抓取状态下,钩子左右摆动,此时矿工的是静止的。当钩子摆动到一定角度,玩家可以单击键盘上的上下左右键中的下方向键控制矿工伸出长钩,抓取金子,此时矿工是向下摇动转轴。获取到金子往回拉后,矿工是不断转动转轴,知道金子拉动到钩子初始处,矿工恢复静止,钩子继续左右摇摆,直到玩家再次单击向下方向键。
3. 伸出的钩子如果碰触到金子,则钩子和金子一起往回拉,回收的速度根据抓取到的金子的大小变化而变化,金子越大,回拉的速度越慢,反之亦然。如果钩子没有碰触到金子,而是碰触到左右和下的边界,则钩子保持原来的速度往回收。
抓取到的金子拉回到转轴处消失,此时金子数目减少一个。如果玩家将所有金子抓取完,游戏返回到初始界面。
4.附加:可抓取自己名字的金块,名字金块仅有一个。
二、实验代码
LessonX.h
/////////////////////////////////////////////////////////////////////////////////
//
//
//
//
/////////////////////////////////////////////////////////////////////////////////
#ifndef _LESSON_X_H_
#define _LESSON_X_H_
#include<vector>
using namespace std;
#include <Windows.h>
/////////////////////////////////////////////////////////////////////////////////
//
// 游戏总管类。负责处理游戏主循环、游戏初始化、结束等工作
class CGameMain
{
private:
int m_iGameState; // 游戏状态,0:结束或者等待开始;1:初始化;2:游戏进行中
CSprite* m_pGameBegin; //GameBegin精灵实例
CSprite* m_pGoldenHook; //钩子精灵实例
CAnimateSprite* m_pGoldenMan; //挖金人实例
int m_iGetGoldState ; //抓取金子的状态,0 -- 等待按键开始抓金子;1 -- 钩子伸出去;2 -- 钩子往回伸,未抓到东西;3 -- 钩子往回伸,带着金子
float m_fHookRotation; // 钩子当前转动的朝向
int m_iHookRotToLeft; // 钩子当前是往左摆动还是往右摆动( 1 右-左, 0 左-右 )
float m_fHookStartPosX; // 钩子初始位置。在刚开始的时候初始化一次,用于钩子复位
float m_fHookStartPosY;
int m_iInitedHookPos;
int sum;
int m_iGoldCount ;// 本局生成的金子数量,每抓取一个金子,数量递减一个,为0时本局结束
//以下四个变量为金子能出现在地图上的上下左右边界最大值和最小值
int m_iGoldBornMinX ;
int m_iGoldBornMaxX ;
int m_iGoldBornMinY ;
int m_iGoldBornMaxY ;
CSprite* m_pGoldenTemplate; //默认绑定的金子精灵,用来创建其他金子
vector<CSprite> m_vGoldenBlock; //金子精灵数组
char m_szCurGetGold[64]; // 当前抓取到的金子名字
float m_fEmptyHookSpeed ; // 空的钩子的移动速度
CSprite* mcp; //我的名字精灵
vector<CSprite> m_vmyname; //名字精灵数组
char m_szCurGetname[1]; // 当前抓取到的金子名字
public:
CGameMain(); //构造函数
~CGameMain(); //析构函数
// Get方法
int GetGameState()
{
return m_iGameState;
}
// Set方法
void SetGameState( const int iState )
{
m_iGameState = iState;
}
// 游戏主循环等
void GameMainLoop( float fDeltaTime );
void GameInit();
void GameRun( float fDeltaTime );
void GameEnd();
void OnKeyDown( const int iKey, const int iAltPress, const int iShiftPress, const int iCtrlPress );
void DrawHookLine();
void OnSpriteColSprite( const char szSrcName[], const char szTarName[] );
CSprite GetCurGoldenSprite(const char* goldenname);
void OnSpriteColWorldLimit( const char szName[], const int iColSide );
void OnKeyUp( const int iKey );
void OnSpriteColSprite1( const char szSrcName[], const char szTarName []);
CSprite GetCurGoldenSprite1(const char* goldenname);
};
/////////////////////////////////////////////////////////////////////////////////
//
extern CGameMain g_GameMain;
it( const char szName[], const int iColSide );
LessonX.cpp
///////////////////////////////////////////////////////////////////////////////// // // // // ///////////////////////////////////////////////////////////////////////////////// #include <Stdio.h> #include "CommonClass.h" #include "LessonX.h" //////////////////////////////////////////////////////////////////////////////// // // CGameMain g_GameMain; //============================================================================== // // 大体的程序流程为:GameMainLoop函数为主循环函数,在引擎每帧刷新屏幕图像之后,都会被调用一次。 //============================================================================== // // 构造函数 CGameMain::CGameMain() { m_iGameState = 0; m_pGameBegin = new CSprite("GameBegin"); m_iGetGoldState = 0; m_fHookRotation = 0.f; m_iHookRotToLeft = 1; m_fHookStartPosX = 0.f; m_fHookStartPosY = 0.f; m_iInitedHookPos = 0; m_pGoldenHook = new CSprite("goldHook"); m_pGoldenMan = new CAnimateSprite("goldMan"); mcp =new CSprite("mcp "); m_iGoldCount = 0; sum=0; m_iGoldBornMinX = 0; m_iGoldBornMaxX = 0; m_iGoldBornMinY = 0; m_iGoldBornMaxY = 0; m_pGoldenTemplate = new CSprite("goldTemplate"); m_fEmptyHookSpeed = 15.f; } //============================================================================== // // 析构函数 CGameMain::~CGameMain() { } //============================================================================== // // 游戏主循环,此函数将被不停的调用,引擎每刷新一次屏幕,此函数即被调用一次 // 用以处理游戏的开始、进行中、结束等各种状态. // 函数参数fDeltaTime : 上次调用本函数到此次调用本函数的时间间隔,单位:秒 void CGameMain::GameMainLoop( float fDeltaTime ) { switch( GetGameState() ) { // 初始化游戏,清空上一局相关数据 case 1: { GameInit(); SetGameState(2); // 初始化之后,将游戏状态设置为进行中 } break; // 游戏进行中,处理各种游戏逻辑 case 2: { // TODO 修改此处游戏循环条件,完成正确游戏逻辑 if( m_iGoldCount>0) { GameRun( fDeltaTime ); } else // 游戏结束。调用游戏结算函数,并把游戏状态修改为结束状态 { SetGameState(0); GameEnd(); } } break; // 游戏结束/等待按空格键开始 case 0: default: break; }; DrawHookLine(); } //============================================================================= // // 每局开始前进行初始化,清空上一局相关数据 void CGameMain::GameInit() { // 重设钩子从0度、由右往左自动旋转 m_fHookRotation = 0.f; m_iHookRotToLeft = 1; // 初始化抓取状态 m_iGetGoldState = 0; // 以下变量只需要初始化一次。 m_iInitedHookPos = 0; m_iGoldCount=20; sum=1; if( 0 == m_iInitedHookPos ) { m_iInitedHookPos = 1; // 钩子初始位置值初始化 m_fHookStartPosX = m_pGoldenHook->GetSpritePositionX(); m_fHookStartPosY = m_pGoldenHook->GetSpritePositionY(); //钩子的世界边界初始化 m_pGoldenHook->SetSpriteWorldLimit(WORLD_LIMIT_NULL,CSystem::GetScreenLeft(),CSystem::GetScreenTop(),CSystem::GetScreenRight(),CSystem::GetScreenBottom()); m_iGoldBornMinX = CSystem::GetScreenLeft() + 5; m_iGoldBornMaxX = CSystem::GetScreenRight() - 5; m_iGoldBornMinY = CSystem::GetScreenTop() + 20; m_iGoldBornMaxY = CSystem::GetScreenBottom() - 5; //钩子可以发出碰撞 m_pGoldenHook->SetSpriteCollisionSend(true); //金子可以接收碰撞 m_pGoldenTemplate->SetSpriteCollisionReceive(true); mcp->SetSpriteCollisionReceive(true); } int iLoop = 0; int iSize = 4, iPosX = 0, iPosY = 0; for( iLoop = 0; iLoop < m_iGoldCount; iLoop++ ) { if( iLoop < 10 ) { iSize = 4; } else if( iLoop >= 10 && iLoop < 16 ) { iSize = 6; } else { iSize = 8; } // 初始化金子精灵实例 char* tmpBlock; tmpBlock=CSystem::MakeSpriteName("GoldBlock",iLoop); CSprite *tmpGlodenBlock=new CSprite(tmpBlock); tmpGlodenBlock->CloneSprite("goldTemplate"); tmpGlodenBlock->SetSpriteWidth((float)iSize ); tmpGlodenBlock->SetSpriteHeight((float)iSize); // 随机一个位置 iPosX = CSystem::RandomRange(m_iGoldBornMinX, m_iGoldBornMaxX); iPosY = CSystem::RandomRange(m_iGoldBornMinY, m_iGoldBornMaxY); tmpGlodenBlock->SetSpritePosition((float)iPosX, (float)iPosY); m_vGoldenBlock.push_back(*tmpGlodenBlock); } //设置自己的名字 char* tmpBlock1; tmpBlock1=CSystem::MakeSpriteName("mcp1",1); CSprite *tmpGlodenBlock1=new CSprite(tmpBlock1); tmpGlodenBlock1->CloneSprite("mcp"); tmpGlodenBlock1->SetSpriteWidth(10); tmpGlodenBlock1->SetSpriteHeight(8); // 随机一个位置 tmpGlodenBlock1->SetSpritePosition(-5, -10); m_vmyname.push_back(*tmpGlodenBlock1); // 播放挖金者的动作(恢复初始守候动作) m_pGoldenMan->AnimateSpritePlayAnimation("GolderManAnimation2", 0); // 隐藏游戏开始的提示 m_pGameBegin->SetSpriteVisible(0); } //============================================================================= // // 每局游戏进行中 void CGameMain::GameRun( float fDeltaTime ) { // 如果当前为等待抓取状态,那么,左右摇摆钩子 if( 0 == m_iGetGoldState) { const float fRotateSpeed = 45.f; // 摇摆速度,单位度/秒 float fThisRotate = fRotateSpeed * fDeltaTime; // 本次旋转的度数 if( m_iHookRotToLeft ) { m_fHookRotation += fThisRotate; if( m_fHookRotation >= 180.f ) { m_fHookRotation = 180.f; m_iHookRotToLeft = 0; } } else { m_fHookRotation -= fThisRotate; if( m_fHookRotation <= 0.f ) { m_fHookRotation = 0.f; m_iHookRotToLeft = 1; } } m_pGoldenHook->SetSpriteRotation(m_fHookRotation); } else if( 2 == m_iGetGoldState || 3 == m_iGetGoldState ) { // 判断是否移动到终点(判断的依据是XY方向的移动速度为0,即金子是否已经停止移动) float fSpeedX = m_pGoldenHook->GetSpriteLinearVelocityX(); float fSpeedY = m_pGoldenHook->GetSpriteLinearVelocityY(); // 当前速度不为0,还在运动中 // 浮点数是否为0,不能直接判断 == != 0 if( fSpeedX > 0.00001f || fSpeedX < -0.00001f || fSpeedY > 0.00001f || fSpeedY < -0.00001f ) return; // 速度为0,钩子回到初始点,开始下一轮的抓取 // 当前抓取到金子,将抓取到的金子释放并删除之。然后将金子数量减一 if(3 == m_iGetGoldState&&stricmp(m_szCurGetGold,"GoldBlock")==1) { m_iGoldCount--; GetCurGoldenSprite( m_szCurGetGold ).SpriteDismount(); GetCurGoldenSprite( m_szCurGetGold ).DeleteSprite(); } if(3 == m_iGetGoldState&&stricmp(m_szCurGetname,"mcp1")==1) { sum--; GetCurGoldenSprite1( m_szCurGetname ).SpriteDismount(); GetCurGoldenSprite1( m_szCurGetname ).DeleteSprite(); } m_iGetGoldState=0; m_pGoldenMan->AnimateSpritePlayAnimation("GolderManAnimation2", 0); } } //============================================================================= // // 本局游戏结束 void CGameMain::GameEnd() { m_pGameBegin->SetSpriteVisible(1); } void CGameMain::OnKeyDown( const int iKey, const int iAltPress, const int iShiftPress, const int iCtrlPress ) { if( KEY_SPACE == iKey && 0 == m_iGameState ) { m_iGameState = 1; m_pGameBegin->SetSpriteVisible(0); } else if( KEY_DOWN == iKey && 2 == m_iGameState && m_iGetGoldState==0 ) { // 设置抓取状态为:钩子往外伸 m_iGetGoldState =1; // 以当前朝向给钩子一个向前的速度 m_pGoldenHook->SetSpriteLinearVelocityPolar(m_fEmptyHookSpeed, m_fHookRotation); // 播放挖金者的动作(一个胳膊往下压的动作) m_pGoldenMan->AnimateSpritePlayAnimation("GolderManAnimation1", 0); } } void CGameMain::DrawHookLine() { float fStartX = m_pGoldenMan->GetSpriteLinkPointPosX(1); float fStartY = m_pGoldenMan->GetSpriteLinkPointPosY(1); // 绳子终点在钩子精灵上获取(该链接点在编辑器里编辑好) float fEndX = m_pGoldenHook->GetSpriteLinkPointPosX(1); float fEndY = m_pGoldenHook->GetSpriteLinkPointPosY(1); // 在这两点之间划线.线的颜色红绿蓝值都为50,即灰色 CSystem::DrawLine( fStartX, fStartY, fEndX, fEndY, 2.f, 0, 50, 50, 50, 255 ); } void CGameMain::OnSpriteColSprite( const char szSrcName[], const char szTarName[] ) { if( 2 != m_iGameState || 1 != m_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; CSprite tmpSprite= GetCurGoldenSprite(szGoldName); tmpSprite.SpriteMountToSpriteLinkPoint("goldHook",2); m_iGetGoldState = 3; strcpy( m_szCurGetGold, szGoldName ); float fWidth; fWidth= GetCurGoldenSprite( szGoldName ).GetSpriteWidth(); float fSpeed = ((10.f - fWidth) / 10.f) * m_fEmptyHookSpeed; // 钩子往初始位置移动 m_pGoldenHook->SpriteMoveTo(m_fHookStartPosX, m_fHookStartPosY, fSpeed, 1); // 播放挖金者的动作(胳膊来回动的动作) m_pGoldenMan->AnimateSpritePlayAnimation("GolderManAnimation3", 0 ); } void CGameMain::OnSpriteColSprite1( const char szSrcName[], const char szTarName[] ) { if( 2 != m_iGameState || 1 != m_iGetGoldState ) return; if( stricmp( "goldHook", szSrcName ) != 0 && stricmp( "goldHook", szTarName ) != 0) return; // 找到哪个是金子的名字 const char *myName = NULL; if( strstr( szTarName, "mcp1" ) ) myName = szTarName; else if( strstr( szSrcName, "mcp1" ) ) myName = szSrcName; else return; CSprite tmpSprite1= GetCurGoldenSprite1(myName); tmpSprite1.SpriteMountToSpriteLinkPoint("goldHook",2); m_iGetGoldState = 3; strcpy( m_szCurGetname, myName ); float fSpeed = 10; // 钩子往初始位置移动 m_pGoldenHook->SpriteMoveTo(m_fHookStartPosX, m_fHookStartPosY, fSpeed, 1); // 播放挖金者的动作(胳膊来回动的动作) m_pGoldenMan->AnimateSpritePlayAnimation("GolderManAnimation3", 0 ); } ///*****金子数组 CSprite CGameMain::GetCurGoldenSprite(const char* goldenname) { for(int i=0; i<m_vGoldenBlock.size(); i++) if(strcmp(goldenname,m_vGoldenBlock[i].GetName())==0) return m_vGoldenBlock[i]; return NULL; } ///****名字数组 CSprite CGameMain::GetCurGoldenSprite1(const char* goldenname) { for(int i=0; i<10; i++) if(strcmp(goldenname,m_vmyname[i].GetName())==0) return m_vmyname[i]; return NULL; } void CGameMain::OnSpriteColWorldLimit( const char szName[], const int iColSide ) { if( stricmp( szName, "goldHook" ) == 0 ) { // 设置抓取状态为:钩子往回收,未抓取到东西 m_iGetGoldState = 2; // 钩子往初始位置移动 m_pGoldenHook->SpriteMoveTo(m_fHookStartPosX, m_fHookStartPosY, m_fEmptyHookSpeed, 1); // 播放挖金者的动作(胳膊来回动的动作) m_pGoldenMan->AnimateSpritePlayAnimation("GolderManAnimation3", 0); } }
Main.cpp
//----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- #include "CommonClass.h" #include "LessonX.h" /////////////////////////////////////////////////////////////////////////////////////////// // // 主函数入口 // ////////////////////////////////////////////////////////////////////////////////////////// int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { // 初始化游戏引擎 if( !CSystem::InitGameEngine( hInstance, lpCmdLine ) ) return 0; // To do : 在此使用API更改窗口标题 CSystem::SetWindowTitle("LessonX"); // 引擎主循环,处理屏幕图像刷新等工作 while( CSystem::EngineMainLoop()) { // 获取两次调用之间的时间差,传递给游戏逻辑处理 float fTimeDelta = CSystem::GetTimeDelta(); // 执行游戏主循环 g_GameMain.GameMainLoop( fTimeDelta ); // 画钩子的缆绳线。不管游戏是什么状态,这根缆绳线都要画出来 }; // 关闭游戏引擎 CSystem::ShutdownGameEngine(); return 0; } //========================================================================== // // 引擎捕捉鼠标移动消息后,将调用到本函数 void CSystem::OnMouseMove( const float fMouseX, const float fMouseY ) { // 可以在此添加游戏需要的响应函数 } //========================================================================== // // 引擎捕捉鼠标点击消息后,将调用到本函数 void CSystem::OnMouseClick( const int iMouseType, const float fMouseX, const float fMouseY ) { // 可以在此添加游戏需要的响应函数 } //========================================================================== // // 引擎捕捉鼠标弹起消息后,将调用到本函数 void CSystem::OnMouseUp( const int iMouseType, const float fMouseX, const float fMouseY ) { // 可以在此添加游戏需要的响应函数 } //========================================================================== // // 引擎捕捉键盘按下消息后,将调用到本函数 // bAltPress bShiftPress bCtrlPress 分别为判断Shift,Alt,Ctrl当前是否也处于按下状态。比如可以判断Ctrl+E组合键 void CSystem::OnKeyDown( const int iKey, const bool bAltPress, const bool bShiftPress, const bool bCtrlPress ) { // 可以在此添加游戏需要的响应函数 g_GameMain.OnKeyDown(iKey, bAltPress, bShiftPress, bCtrlPress); } //========================================================================== // // 引擎捕捉键盘弹起消息后,将调用到本函数 void CSystem::OnKeyUp( const int iKey ) { // 可以在此添加游戏需要的响应函数 // g_GameMain. } //=========================================================================== // // 引擎捕捉到精灵与精灵碰撞之后,调用此函数 void CSystem::OnSpriteColSprite( const char *szSrcName, const char *szTarName ) { g_GameMain.OnSpriteColSprite(szSrcName,szTarName); g_GameMain.OnSpriteColSprite1(szSrcName,szTarName); } //=========================================================================== // // 引擎捕捉到精灵与世界边界碰撞之后,调用此函数. // iColSide : 0 左边,1 右边,2 上边,3 下边 void CSystem::OnSpriteColWorldLimit( const char *szName, const int iColSide ) { g_GameMain.OnSpriteColWorldLimit(szName,iColSide); }