前言:
1、相较于文档给出的,优化了单身二十年手速所造成的问题(包括中心点问题)
2、优化了连续点击拍子的效果不佳的问题
效果图:
精灵总览
背景图层数设置为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;
}
//==========================================================================
//
// 引擎捕捉鼠标移动消息后,将调用到本函数
// 参数 fMouseX, fMouseY:为鼠标当前坐标
//
void CSystem::OnMouseMove( const float fMouseX, const float fMouseY )
{
// 可以在此添加游戏需要的响应函数
g_GameMain.OnMouseMove(fMouseX, fMouseY);
}
//==========================================================================
//
// 引擎捕捉鼠标点击消息后,将调用到本函数
// 参数 iMouseType:鼠标按键值,见 enum MouseTypes 定义
// 参数 fMouseX, fMouseY:为鼠标当前坐标
//
void CSystem::OnMouseClick( const int iMouseType, const float fMouseX, const float fMouseY )
{
// 可以在此添加游戏需要的响应函数
g_GameMain.OnMouseClick(iMouseType, fMouseX, fMouseY);
}
//==========================================================================
//
// 引擎捕捉鼠标弹起消息后,将调用到本函数
// 参数 iMouseType:鼠标按键值,见 enum MouseTypes 定义
// 参数 fMouseX, fMouseY:为鼠标当前坐标
//
void CSystem::OnMouseUp( const int iMouseType, const float fMouseX, const float fMouseY )
{
// 可以在此添加游戏需要的响应函数
g_GameMain.OnMouseUp(iMouseType, fMouseX, fMouseY);
}
//==========================================================================
//
// 引擎捕捉键盘按下消息后,将调用到本函数
// 参数 iKey:被按下的键,值见 enum KeyCodes 宏定义
// 参数 iAltPress, iShiftPress,iCtrlPress:键盘上的功能键Alt,Ctrl,Shift当前是否也处于按下状态(0未按下,1按下)
//
void CSystem::OnKeyDown( const int iKey, const bool bAltPress, const bool bShiftPress, const bool bCtrlPress )
{
// 可以在此添加游戏需要的响应函数
g_GameMain.OnKeyDown(iKey, bAltPress, bShiftPress, bCtrlPress);
}
//==========================================================================
//
// 引擎捕捉键盘弹起消息后,将调用到本函数
// 参数 iKey:弹起的键,值见 enum KeyCodes 宏定义
//
void CSystem::OnKeyUp( const int iKey )
{
// 可以在此添加游戏需要的响应函数
g_GameMain.OnKeyUp(iKey);
}
//===========================================================================
//
// 引擎捕捉到精灵与精灵碰撞之后,调用此函数
// 精灵之间要产生碰撞,必须在编辑器或者代码里设置精灵发送及接受碰撞
// 参数 szSrcName:发起碰撞的精灵名字
// 参数 szTarName:被碰撞的精灵名字
//
void CSystem::OnSpriteColSprite( const char szSrcName, const char szTarName )
{
// 可以在此添加游戏需要的响应函数
g_GameMain.OnSpriteColSprite(szSrcName, szTarName);
}
//===========================================================================
//
// 引擎捕捉到精灵与世界边界碰撞之后,调用此函数.
// 精灵之间要产生碰撞,必须在编辑器或者代码里设置精灵的世界边界限制
// 参数 szName:碰撞到边界的精灵名字
// 参数 iColSide:碰撞到的边界 0 左边,1 右边,2 上边,3 下边
//
void CSystem::OnSpriteColWorldLimit( const char *szName, const int iColSide )
{
// 可以在此添加游戏需要的响应函数
g_GameMain.OnSpriteColWorldLimit(szName, iColSide);
}
LessonX.h
ifndef LESSON_X_H
define LESSON_X_H
//
include <Windows.h>
include
using namespace std;
/////////////////////////////////////////////////////////////////////////////////
//
// 游戏总管类。负责处理游戏主循环、游戏初始化、结束等工作
class CGameMain
{
private:
int m_iGameState; // 游戏状态,0:结束或者等待开始;1:初始化;2表示未开始(此时拍子不随鼠标移动),3 表示倒计时阶段, 4表示开始拍子可以随鼠标移动并拍打
public:
CGameMain(); //构造函数
~CGameMain(); //析构函数
float m_fRotateTime; // 拍子拍下后距离复位还差多少时间
float m_fOldRotation; // 拍子的初始角度
CSprite *paizi;
// int m_iGameState;
int countdownTime ; //倒计时时间
float countPassedTime; //倒计时经过的时间
CTextSprite *countdown; //倒计时的文字精灵
CSprite *kaishi; //“空格开始”精灵
vector< CSprite *> m_mosquitos;
vector< CSprite *> m_names;
int m_iMosquitoCount;
int m_iGameScore; //当前得分
CTextSprite *score; //score精灵
float m_fGameTime; //游戏时间
CTextSprite *gameTime; //游戏时间精灵
int index = 1;
// 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 OnMouseMove( const float fMouseX, const float fMouseY );
void OnMouseClick( const int iMouseType, const float fMouseX, const float fMouseY );
void OnMouseUp( const int iMouseType, const float fMouseX, const float fMouseY );
void OnKeyDown( const int iKey, const bool bAltPress, const bool bShiftPress, const bool bCtrlPress );
void OnKeyUp( const int iKey );
void OnSpriteColSprite( const char *szSrcName, const char *szTarName );
void OnSpriteColWorldLimit( const char *szName, const int iColSide );
};
/////////////////////////////////////////////////////////////////////////////////
//
extern CGameMain g_GameMain;
endif // LESSON_X_H
LessonX.Cpp
include <Stdio.h>
include "CommonClass.h"
include "LessonX.h"
include
using namespace std;
////////////////////////////////////////////////////////////////////////////////
//
//
CGameMain g_GameMain;
//==============================================================================
//
// 大体的程序流程为:GameMainLoop函数为主循环函数,在引擎每帧刷新屏幕图像之后,都会被调用一次。
//==============================================================================
//
// 构造函数
CGameMain::CGameMain()
{
m_iGameState = 1;
paizi = new CSprite("paizi");
countdown = new CTextSprite("countdown");
kaishi = new CSprite("kaishi");
score = new CTextSprite("score");//实例化得分
gameTime = new CTextSprite("gameTime");
}
//==============================================================================
//
// 析构函数
CGameMain::~CGameMain()
{
}
//==============================================================================
//
// 游戏主循环,此函数将被不停的调用,引擎每刷新一次屏幕,此函数即被调用一次
// 用以处理游戏的开始、进行中、结束等各种状态.
// 函数参数fDeltaTime : 上次调用本函数到此次调用本函数的时间间隔,单位:秒
void CGameMain::GameMainLoop( float fDeltaTime )
{
switch( GetGameState() )
{
// 初始化游戏,清空上一局相关数据
case 1:
{
GameInit();
SetGameState(2); // 初始化之后,将游戏状态设置为进行中
}
break;
// 游戏进行中,处理各种游戏逻辑
case 2:
{
// TODO 修改此处游戏循环条件,完成正确游戏逻辑
if( true )
{
GameRun( fDeltaTime );
}
else // 游戏结束。调用游戏结算函数,并把游戏状态修改为结束状态
{
SetGameState(0);
GameEnd();
}
}
break;
case 3:
case 4:
{// TODO 修改此处游戏循环条件,完成正确游戏逻辑
if( true ){
GameRun( fDeltaTime );
}
else // 游戏结束。调用游戏结算函数,并把游戏状态修改为结束状态
{
SetGameState(0);
GameEnd();
}
}
// 游戏结束/等待按空格键开始
case 0:
default:
break;
};
}
//=============================================================================
//
// 每局开始前进行初始化,清空上一局相关数据
void CGameMain::GameInit()
{
CSystem::ShowCursor(false); //隐藏鼠标
m_fRotateTime = 0.f;//旋转时间
m_fOldRotation = paizi->GetSpriteRotation(); //获取拍子的初始角度
countdownTime = 5; //倒计时时间为5s
countPassedTime = 0.f;
countdown->SetSpriteVisible(false); //设置倒计时不可见
m_iMosquitoCount = 15;//蚊子的数量
m_iGameScore = 0;//将得分设置为0
}
//=============================================================================
//
// 每局游戏进行中
void CGameMain::GameRun( float fDeltaTime )
{
/*if(m_fRotateTime > 0) {
m_fRotateTime -= fDeltaTime; //每经过fDeltaTime,拍子旋转的时间也少了fDeltaTime
if(m_fRotateTime <= 0){ //经过了0.2s以后
paizi->SetSpriteRotation(m_fOldRotation); //恢复拍子的初始旋转角
}
}*/
if(m_iGameState==3){ //m_iGameState 为3时才执行
countPassedTime += fDeltaTime; //统计经过的时间,并存储到countPassedTime
//思考:countPassedTime 是浮点数,那么为什么countdown -countPassedTime可以显示整数的时间?
countdown->SetTextValue(countdownTime -countPassedTime);
if(countPassedTime >= countdownTime){ //经过的时间超过countdownTime
countdown->SetSpriteVisible(false); //隐藏countdown
countPassedTime = 0; //重置经过的时间,为下一轮游戏做准备
m_iGameState = 4; //切换g_iGameState 3->4
//蚊子的生成是在此处
//获取最大边界
int minX = CSystem::GetScreenLeft() + 5;
int maxX = CSystem::GetScreenRight() - 5;
int minY = CSystem::GetScreenBottom() - 5;
int maxY = CSystem::GetScreenTop() + 5;
int iPosX = 0, iPosY = 0; //随机产生的横纵坐标位置
int iLoop; //循环控制变量
for( iLoop = 0; iLoop < m_iMosquitoCount; iLoop++ )
{
//利用iLoop产生不同的蚊子名
char *szName = CSystem::MakeSpriteName("wenzi",iLoop);
CSprite *wenzi = new CSprite(szName);
wenzi->CloneSprite("wenziTemplate"); //复制wenziTemplate
iPosX = CSystem::RandomRange(minX, maxX); //随机产生X坐标
iPosY = CSystem::RandomRange(minY, maxY); //随机产生Y坐标
wenzi->SetSpritePosition(iPosX,iPosY); //设置产生蚊子的位置
if(iLoop%3==0)
{
wenzi->SetSpriteWidth(3.7f);
wenzi->SetSpriteHeight(3.7f);
}else if(iLoop%3==1)
{
wenzi->SetSpriteWidth(5.7f);
wenzi->SetSpriteHeight(5.7f);
}else{
wenzi->SetSpriteWidth(7.7f);
wenzi->SetSpriteHeight(7.7f);
}
m_mosquitos.push_back(wenzi); //压入vector中集中管理
}
}
} else if(m_iGameState==4){
m_fGameTime -= fDeltaTime;
if(m_fGameTime > 0){
gameTime->SetTextValue(m_fGameTime); //若游戏时间未结束,则显示剩余的游戏时间
}else{
m_iGameState = 2; //若游戏时间结束,完成g_iGameState 2->0的转换
kaishi->SetSpriteVisible(true); //显示“空格开始”
m_fGameTime = 0;
paizi->SetSpritePositionX(-0.852f);
paizi->SetSpritePositionY(-1.000f);
gameTime->SetTextValue(m_fGameTime);
}
}
}
//=============================================================================
//
// 本局游戏结束
void CGameMain::GameEnd()
{
}
//==========================================================================
//
// 鼠标移动
// 参数 fMouseX, fMouseY:为鼠标当前坐标
void CGameMain::OnMouseMove( const float fMouseX, const float fMouseY )
{
if(m_iGameState==4)
{
paizi->SetSpritePosition(fMouseX, fMouseY); //使拍子的位置和鼠标一致
}
}
//==========================================================================
//
// 鼠标点击
// 参数 iMouseType:鼠标按键值,见 enum MouseTypes 定义
// 参数 fMouseX, fMouseY:为鼠标当前坐标
void CGameMain::OnMouseClick( const int iMouseType, const float fMouseX, const float fMouseY )
{
if(iMouseType == MOUSE_LEFT && m_iGameState == 4) //鼠标左键按下
{
paizi->SetSpriteRotation(m_fOldRotation - 10); //设置拍子的旋转角,为初始值-10
m_fRotateTime = 0.2f; //拍打的时间间隔 0.2s,即0.2s后恢复拍子初始角度
//遍历判定蚊子是否被击中
float fX,fY;int flag = -1;
for(int i=0;i<m_mosquitos.size();i++){ //遍历vector中的蚊子
fX = m_mosquitos[i]->GetSpritePositionX(); //获取蚊子所的纵坐标
fY = m_mosquitos[i]->GetSpritePositionY(); //获取蚊子所在
if(paizi->IsPointInSprite(fX,fY)) //判断蚊子是否在拍子的范围内
{
int h = m_mosquitos[i]->GetSpriteHeight();
int w = m_mosquitos[i]->GetSpriteWidth();
char *myName = CSystem::MakeSpriteName("names",i);
CSprite *myNames = new CSprite(myName);
myNames->CloneSprite("myName");
myNames->SetSpritePosition(fX,fY);
myNames->SetSpriteWidth(w+2);
myNames->SetSpriteHeight(h);
m_names.push_back(myNames);
m_mosquitos[i]->DeleteSprite(); //删除该蚊子
vector<CSprite*>::iterator it = m_mosquitos.erase(m_mosquitos.begin()+i,m_mosquitos.begin()+i+1);
m_iGameScore++; //每打一只蚊子加一分
score->SetTextValue(m_iGameScore);
break;
}
}
}
}
//==========================================================================
//
// 鼠标弹起
// 参数 iMouseType:鼠标按键值,见 enum MouseTypes 定义
// 参数 fMouseX, fMouseY:为鼠标当前坐标
void CGameMain::OnMouseUp( const int iMouseType, const float fMouseX, const float fMouseY )
{
paizi->SetSpriteRotation(m_fOldRotation);
}
//==========================================================================
//
// 键盘按下
// 参数 iKey:被按下的键,值见 enum KeyCodes 宏定义
// 参数 iAltPress, iShiftPress,iCtrlPress:键盘上的功能键Alt,Ctrl,Shift当前是否也处于按下状态(0未按下,1按下)
void CGameMain::OnKeyDown( const int iKey, const bool bAltPress, const bool bShiftPress, const bool bCtrlPress ){
if( KEY_SPACE == iKey && 2 == m_iGameState ) //按下空格且游戏状态为2时
{
m_iGameState = 3; //设置游戏状态进入倒计时,即3
countdown->SetSpriteVisible(true); //显示倒计时文本框
kaishi->SetSpriteVisible(false); //隐藏“空格开始”
m_iGameScore = 0.f; //初始化新一轮游戏分数
m_fGameTime = 10.f; //初始化新一轮游戏时间
score->SetTextValue(m_iGameScore);
gameTime->SetTextValue(m_fGameTime);
for(int i=0;i<m_mosquitos.size();i++){ //删除上一轮游戏中剩余的蚊子
m_mosquitos[i]->DeleteSprite();
}
for(int i=0;i<m_names.size();i++)
{
m_names[i]->DeleteSprite();
}
m_mosquitos.clear(); //清空vector
m_names.clear();
}
}
//==========================================================================
//
// 键盘弹起
// 参数 iKey:弹起的键,值见 enum KeyCodes 宏定义
void CGameMain::OnKeyUp( const int iKey )
{
}
//===========================================================================
//
// 精灵与精灵碰撞
// 参数 szSrcName:发起碰撞的精灵名字
// 参数 szTarName:被碰撞的精灵名字
void CGameMain::OnSpriteColSprite( const char szSrcName, const char szTarName )
{
}
//===========================================================================
//
// 精灵与世界边界碰撞
// 参数 szName:碰撞到边界的精灵名字
// 参数 iColSide:碰撞到的边界 0 左边,1 右边,2 上边,3 下边
void CGameMain::OnSpriteColWorldLimit( const char *szName, const int iColSide )
{
}