目 录CONTENT

文章目录

【2D割草类游戏 - 03】游戏架构

千年的霜雪
2024-08-31 / 1 评论 / 0 点赞 / 36 阅读 / 0 字 / 正在检测是否收录...

【2D割草类游戏 - 03】游戏架构

由于只是一个轻量的练手小游戏,所以没有进行头文件分离,自然也就没有过于追求扩展性
游戏架构也比较简单

类定义

总共定义了以下类:

  • 图集类
    用于实现单例模式
  • 动画类
    用于渲染动画
  • 玩家类
  • 子弹类
  • 敌人类
  • 按钮类
    • 开始游戏按钮类
    • 退出游戏按钮类

数据结构

将敌人存储在vector中为敌人指针列表
将子弹存储在vector中为子弹列表

主循环

主循环内的逻辑为:输入处理->数据处理->渲染画面

输入处理

在整个游戏中,需要接收数据输入的只有玩家角色和按钮

输入处理并没有将信息过滤后直接传递至数据处理部分,而是分别在对应的需要接收数据的类中定义了处理方法,并直接将原始数据交给对应的对象进行输入

主循环中:

// 输入处理
while (peekmessage(&msg))
{
	if(is_game_started)
		player.ProcessEvent(msg);
	else
	{
		btn_start_game.ProcessEvent(msg);
		btn_quit_game.ProcessEvent(msg);
	}
}

按钮类中的数据输入:

// 事件响应
void ProcessEvent(const ExMessage& msg)
{
	switch (msg.message)
	{
	case WM_MOUSEMOVE:
		if (status == Status::Idle && CheckCursorHit(msg.x, msg.y))
			status = Status::Hovered;
		else if (status == Status::Hovered && CheckCursorHit(msg.x, msg.y))
			status = Status::Idle;
		break;
	case WM_LBUTTONDOWN:
		if (CheckCursorHit(msg.x, msg.y))
			status = Status::Pushed;
		break;
	case WM_LBUTTONUP:
		if (status == Status::Pushed)
			OnClick();
		break;
	default:
		break;
	}
}

玩家类中的数据输入:

// 处理玩家操作消息
void ProcessEvent(const ExMessage& msg)
{
    switch (msg.message)
    {
        case WM_KEYDOWN:
            switch (msg.vkcode)
            {
            case VK_UP:
                is_move_up = true;
                break;
            case VK_DOWN:
                is_move_down = true;
                break;
            case VK_LEFT:
                is_move_left = true;
                break;
            case VK_RIGHT:
                is_move_right = true;
                break;
            }
            break;

        case WM_KEYUP:
            switch (msg.vkcode)
            {
            case VK_UP:
                is_move_up = false;
                break;
            case VK_DOWN:
                is_move_down = false;
                break;
            case VK_LEFT:
                is_move_left = false;
                break;
            case VK_RIGHT:
                is_move_right = false;
                break;
            }
           break;
    }
}

数据处理

首先判断了游戏是否开始,配合开始游戏按钮使用

处理顺序:

  1. 调用了玩家类中的移动方法,处理玩家移动
  2. 调用全局子弹更新函数,更新子弹位置
  3. 调用全局敌人生成函数,尝试生成敌人
  4. 调用敌人类中的移动方法,更新敌人位置
  5. 碰撞检测
  6. 遍历敌人数组,移除生命值归零的敌人

碰撞检测

首先检测敌人与玩家碰撞:
在敌人类中定义了检测与玩家碰撞方法
遍历敌人列表,逐个调用该对象的检测与玩家碰撞方法,传入玩家对象

// 检测敌人与玩家碰撞
for (Enemy* enemy : enemy_list)
{
	if (enemy->CheckPlayerCollision(player))
	{
		static TCHAR text[128];
		_stprintf_s(text, _T("最终得分:%d !"), score);
		MessageBox(GetHWnd(), text, _T("游戏结束"), MB_OK);
		running = false;
		break;
	}
}

然后检测子弹与敌人的碰撞:
在敌人类中定义了检测与子弹碰撞方法
遍历敌人列表,对于每个敌人遍历子弹列表,逐个调用该对象的检测与子弹碰撞方法,传入子弹对象

// 检测子弹和敌人的碰撞
for (Enemy* enemy : enemy_list)
{
	for (const Bullet& bullet : bullet_list)
	{
		if (enemy->CheckBulletCollision(bullet))
		{
			mciSendString(_T("play hit from 0"), NULL, 0, NULL);
			enemy->Hurt();
			score++;
		}
	}
}

渲染画面

  1. 清空画面
  2. 调用各个背景绘制画面
  3. 调用玩家类中的绘制方法
  4. 遍历子弹与敌人列表,调用对应的绘制方法
  5. 刷新画面缓冲区

稳定帧率

使用GetTickCount方法配合Sleep函数稳定帧率

0

评论区