致谢
感谢博客园用户@Ghost_zhao
因为是应对学校课程设计的要求,所以在他的代码基础上进行了解读
十分感谢! 传送门
项目预览
项目与课程设计实验报告
点击下载,密码ynf1
实现思路
界面数据
在游戏数据操作上使用0和1表示方块情况,0表示没有方块的位置,1表示有方块的位置
游戏主窗口对应15*20的方格,下一个方格窗口对应4*4的方格,在内存中的表示情况
定时机制
围绕计时器为核心的俄罗斯方块,所以将单步下落执行操作写在OnTimer()中覆盖父类的方法,每次触发计时器则进行单步下落。
暂停与游戏结束的区别在于是否重置得分/等级/方块信息,在触发暂停按钮时通过杀死计时器但不重置数据来实现暂停,再次点击暂停按钮时重新建立计时器
开始游戏并建立计时器
void CBoxDlg::OnBnClickedButtonStart()
{
game->Start();
CString sData; // 显示级别和得分
sData.Format(_T("%d"), game->Level);
GetDlgItem(IDC_STATIC_LEVEL)->SetWindowText(sData);
sData.Format(_T("%d"), game->Score);
GetDlgItem(IDC_STATIC_Score)->SetWindowText(sData);
SetTimer(1,game->TIME_STEP,NULL);
}
暂停/继续操作
void CBoxDlg::OnBnClickedButtonHalt()
{
game->HaltOrContinue();
if(game->GetState()==HALT)
KillTimer(1);
if(game->GetState()==GO)
SetTimer(1,game->TIME_STEP,NULL);
}
计时器触发操作
void CBoxDlg::OnTimer(UINT_PTR nIDEvent)
{
if(!game->Go())
{
KillTimer(1);
TCHAR *msg = _T("Game Over!");
MessageBox(msg);
}
CString sLevel,sScore; // 显示级别和得分
sLevel.Format(_T("%d"), game->Level);
GetDlgItem(IDC_STATIC_LEVEL)->SetWindowText(sLevel);
sScore.Format(_T("%d"), game->Score);
GetDlgItem(IDC_STATIC_Score)->SetWindowText(sScore);
Invalidate(true);// 重绘画面
}
定时处理
MFC官方提供了OnTimer方法的处理操作,在这里自定义OnTimer方法覆盖父类方法
在OnTimer方法中操作Go()方块单步下落,并判定是否到达底端等
单步下落后判定是否死亡,死亡则杀死计时器并弹框提示Game Over
未死亡则更新分数和等级并重新绘制画面进入新的循环
定时处理操作
void CBoxDlg::OnTimer(UINT_PTR nIDEvent)
{
if(!game->Go()) // 执行处理函数
{
KillTimer(1);
TCHAR *msg = _T("Game Over!");
MessageBox(msg);
}
CString sLevel,sScore; // 显示级别和得分
sLevel.Format(_T("%d"), game->Level);
GetDlgItem(IDC_STATIC_LEVEL)->SetWindowText(sLevel);
sScore.Format(_T("%d"), game->Score);
GetDlgItem(IDC_STATIC_Score)->SetWindowText(sScore);
Invalidate(true);// 重绘画面
}
单步下落操作
bool Game::Go()
{
if(CanMoveDown())
{
MoveDown();
return true;
}
else
{
AddTool(this->BigNet);
RemoveLines();
if(IsDead())
{
state = STOP;
return false;
}
else
{
NextTool();
return true;
}
}
}
旋转操作
因为方块定义的时4*4的二维数组,在进行旋转操作时需要先进行转置操作,再进行水平对置,能够保证所有的形状不会变形且稳定旋转。
一字型旋转操作
田字形旋转操作
L型旋转操作
反L型旋转操作
Z型旋转操作
反Z型旋转操作
边界检测
下落方块的边界检测是游戏最核心的功能之一,由于时间原因没有将图详细画出来
思路是在边界检测函数中模拟该操作执行后的情况
例如:判断是否可以继续下落CanMoveDown()
先统计当前情况下大窗口内1的个数(正在下落的方块4+已落到底部成型的块数)
获得大窗口数据的拷贝副本
对副本进行模拟下落操作,如果下落后会产生碰撞(方块重合或越界)则1的总数发生变化,此时则不能继续执行