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);
}