【V0.1B16】从零开始的2D游戏引擎开发之路

TEnGinE Version-0.1 Build-16 更新日志

发布日期: 2022-05-30 01:18

内部版本号: Version-0.1 Build-16

更新类型: 接口规范化与地图数据接口扩展


变更概要

类别 内容
接口精简 GameState模块10个接口统一为下划线命名规范
功能新增 GameState模块新增地图数据返回接口2个
功能拆分 CompileState拆分至CompileStateUnits与CompileStateEvent
功能新增 InsertAction模块新增地图行列跨度设置与移动数据接入
架构重构 LocationBase模块功能并入InsertAction模块
文件新增 新增Tanxl_HitBox_Check.h碰撞模块占位
文件清理 移除Tanxl_LocationBase.h

一、GameState 模块(接口规范化)

1.1 接口命名统一

Build-15 中 GameStateBase 与 StateUnit 的接口命名同时存在驼峰式与下划线式两种写法,Build-16 将该模块全部对外接口统一为下划线分隔风格。

Tanxl_GameState.h - GameStateBase

cpp 复制代码
// Build-15
void SetState(int Width, int Height);
void CompileState(std::string Infor);
void SetAdjust(float Adjust);
size_t GetStateSize();
StateUnit* GetStateUnit(int Pos);
static GameStateBase& GetStateBase(int Height = 0, int Width = 0);
void SetExacHeight(float& Current);
void SetExacWidth(float& Current);
int GetStateHeight()const;
int GetStateWidth()const;

// Build-16
void Set_State(int Width, int Height);
void CompileStateUnits(std::string Infor);
void CompileStateEvent(std::string Infor);
void Set_Adjust(float Adjust);
size_t Get_StateSize();
StateUnit* Get_StateUnit(int Pos);
static GameStateBase& Get_StateBase(int Height = 0, int Width = 0);
void Set_ExacHeight(float& Current);
void Set_ExacWidth(float& Current);
int Get_StateHeight()const;
int Get_StateWidth()const;

Tanxl_GameState.h - StateUnit

cpp 复制代码
bool GetMoveT();      // Build-15
bool GetMoveAble();   // Build-16

接口重命名对照如下。

Build-15 Build-16
SetState() Set_State()
SetAdjust() Set_Adjust()
SetExacHeight() Set_ExacHeight()
SetExacWidth() Set_ExacWidth()
GetStateSize() Get_StateSize()
GetStateUnit() Get_StateUnit()
GetStateBase() Get_StateBase()
GetStateHeight() Get_StateHeight()
GetStateWidth() Get_StateWidth()
StateUnit::GetMoveT() StateUnit::GetMoveAble()

私有构造函数处补充了单例初始化说明注释。

cpp 复制代码
private:
	//地图初始化默认构造函数 采用单例模式进行第一次初始化
	GameStateBase(int Height = 0, int Width = 0);
1.2 地图编译接口拆分

CompileState() 同时承担地图格子编译与事件绑定两项工作。Build-16 将其拆分为两个独立接口:CompileStateUnits() 负责从字符串编译出 StateUnit 网格,CompileStateEvent() 保留原有的事件绑定逻辑,注释同步迁移。

cpp 复制代码
// Build-15
//CompileState : 使用一个字符串来完成整个地图状态的设计 以英文逗号(,)为间断 以英文句号(.)为结尾
void CompileState(std::string Infor);

// Build-16
void CompileStateUnits(std::string Infor);
//CompileStateEvent : 使用一个字符串来完成整个地图状态的设计 以英文逗号(,)为间断 以英文句号(.)为结尾
void CompileStateEvent(std::string Infor);

CompileStateUnits() 的解析规则为逐字符读取,, 之前的内容解析为 State_Id,- 之前的内容解析为 State_Move,遇到 - 时按 Status_Id - State_Move 构造并压入一个 StateUnit。

Tanxl_GameState.cpp - CompileStateUnits()

cpp 复制代码
void GameStateBase::CompileStateUnits(std::string Infor)
{
	std::string Text_Reader{};
	int Status_Id{}, State_Move{};
	for (int i = 0, SetCount = 0; i < Infor.size(); i++)
	{
		if (Infor.at(i) != ',' && Infor.at(i) != '-')
			Text_Reader += Infor.at(i);
		else if (Infor.at(i) != '-')
		{
			Status_Id = std::stoi(Text_Reader);
			Text_Reader = "";
		}
		else
		{
			State_Move = std::stoi(Text_Reader);
			Text_Reader = "";
			this->GameState.push_back(new StateUnit(NULL, Status_Id, State_Move));
			Status_Id = 0;
			State_Move = 0;
		}
	}
}

该接口传入的字符串格式为 Id-Move,Id-Move,Id-Move,单个单元以英文逗号分隔,Id 与移动标记之间以英文连字符分隔。

1.3 地图数据返回接口

Build-16 新增两个用于导出地图状态的接口,分别返回格子对象数组与格子可移动标记数组,供 InsertAction 模块的移动判定使用。

Tanxl_GameState.cpp - 新增实现:

cpp 复制代码
std::vector<StateUnit*>* GameStateBase::Get_GameState()
{
	return &this->GameState;
}

std::vector<bool>* GameStateBase::Get_GameState_MoveAble()
{
	std::vector<bool> MAB;
	for (int i = 0; i < GameState.size(); i++)
	{
		if (GameState.at(i)->GetMoveAble())
			MAB.push_back(true);
		else
			MAB.push_back(false);
	}
	return &MAB;
}

Get_GameState() 直接返回内部容器地址,Get_GameState_MoveAble() 按 GameState 顺序遍历并逐个读取 StateUnit::GetMoveAble(),将结果展开为同序的 bool 数组。


二、InsertAction 模块(编译地图场景与碰撞判定预留)

2.1 新增行列跨度成员

地图碰撞判定需要知道一行有多少格,Build-16 在 InsertEventBase 中加入 _Max_Line 成员与对应设置接口。

Tanxl_InsertAction.h

cpp 复制代码
	void Set_MaxFloat(float Max_float);
	void Set_MaxLine(int Max_Line);              // ← 新增
	void Get_MoveData(std::vector<bool>* PVB);   // ← 新增
private:
	void AutoCheck(float* MoveX, float* MoveY);
	std::vector<Key_Unit> KeyEventS;
	std::vector<bool>* _PTB;                     // ← 新增
	float _Max_float;
	int _Max_Line;                               // ← 新增

Tanxl_InsertAction.cpp

cpp 复制代码
void InsertEventBase::Set_MaxLine(int Max_Line)
{
	this->_Max_Line = Max_Line;
}

void InsertEventBase::Get_MoveData(std::vector<bool>* PVB)
{
	this->_PTB = PVB;
}

Get_MoveData() 用于将 GameState 模块产出的可移动标记数组接入 InsertEventBase,指针直接保存不做复制。

2.2 GetInsert 中的移动合法性判定

GetInsert() 中新增了基于 _PTB 的相邻格判定逻辑,判定当前格在移动方向上是否越界或不可通行。该段逻辑在 Build-16 中以注释形式保留,尚未启用,按键处理仍走原有的直接累加分支。

Tanxl_InsertAction.cpp - GetInsert()

cpp 复制代码
		if (KeyEventS.at(i).MoveToX/* &&
			((*MoveX >= 0 && i + 1 < static_cast<int>(_PTB->size()) && _PTB->at(static_cast<std::vector<bool, std::allocator<bool>>::size_type>(i) + 1))
				|| (*MoveX < 0 && i - 1 >= 0 && _PTB->at(static_cast<std::vector<bool, std::allocator<bool>>::size_type>(i) - 1)))*/)
			*MoveX += KeyEventS.at(i).MoveLen;
		if (KeyEventS.at(i).MoveToY/* &&
			(*MoveY >= 0 && i + _Max_Line < static_cast<int>(_PTB->size()) && _PTB->at(static_cast<std::vector<bool, std::allocator<bool>>::size_type>(i) + _Max_Line))
			|| (*MoveY < 0 && i - _Max_Line >= 0 && _PTB->at(static_cast<std::vector<bool, std::allocator<bool>>::size_type>(i) - _Max_Line))*/)
			*MoveY += KeyEventS.at(i).MoveLen;

X 轴方向判定按 i ± 1 取相邻格,Y 轴方向判定按 i ± _Max_Line 取上下行同列格,两个方向均先行做下标边界检查再访问 _PTB

2.3 构造函数初始化同步

新增成员在两个构造函数中同步补入初始化列表,避免出现未初始化指针。

cpp 复制代码
// Build-15
InsertEventBase::InsertEventBase() :KeyEventS(NULL), _Max_float(1.0f) {}
InsertEventBase::InsertEventBase(const InsertEventBase&) :KeyEventS(NULL), _Max_float(1.0f) {}

// Build-16
InsertEventBase::InsertEventBase() :KeyEventS(NULL), _Max_float(1.0f), _PTB(NULL), _Max_Line(0) {}
InsertEventBase::InsertEventBase(const InsertEventBase&) :KeyEventS(NULL), _Max_float(1.0f), _PTB(NULL), _Max_Line(0) {}

_PTB 初始化为 NULL,_Max_Line 初始化为 0,在未调用 Get_MoveData()Set_MaxLine() 之前不参与判定。

2.4 模块版本头更新

Tanxl_InsertAction.h 文件头:

cpp 复制代码
// Build-15
//_VERSION_0_3_ UPDATE LOG
// LAST_UPDATE 2022-05-04 21:40
// 整理至引擎核心内容
// 提供对可操纵方块的最大移动位置限制的功能
// 修复单例功能错误的问题

// Build-16
//_VERSION_0_4_ UPDATE LOG
// LAST_UPDATE 2022-05-30 01:18
// 整合原LocationBase模块的基本功能
// 提供编译地图场景的功能

三、模块整合与新增(工程整理)

3.1 LocationBase 模块并入 InsertAction

Tanxl_LocationBase.h 内容仅为一个 17 行的头文件类,含构造函数外的 Set_LocationBase(float, float) 内联实现与两个坐标成员,全工程无任何调用点。Build-16 将该文件整体移除,其功能定位并入 InsertAction 模块。

Tanxl_LocationBase.h(Build-15,已删除):

cpp 复制代码
//_VERSION_0_1_ UPDATE LOG
// LAST_UPDATE 2022-05-15 22:43
// 基础版本

class LocationBase
{
public:
	void Set_LocationBase(float LocX, float LocY);
private:
	float Location_x;
	float Location_y;
};

void LocationBase::Set_LocationBase(float LocX, float LocY)
{
	Location_x = LocX;
	Location_y = LocY;
}
3.2 碰撞模块占位新增

Build-16 新增 Tanxl_HitBox_Check.h,该文件内容仅为文件头注释与 #pragma once 预编译指令,作为后续碰撞判定功能的落点预先加入工程。

Tanxl_HitBox_Check.h(新增):

cpp 复制代码
//_VERSION_0_1_ UPDATE LOG
// LAST_UPDATE 2022-05-30 01:11
// 初始版本

#pragma once
3.3 工程文件同步

TANXL_ENGINE.vcxprojTANXL_ENGINE.vcxproj.filters 中的 ClInclude 条目同步替换。

xml 复制代码
<!-- Build-15 -->
<ClInclude Include="Tanxl_LocationBase.h" />

<!-- Build-16 -->
<ClInclude Include="Tanxl_HitBox_Check.h" />

Tanxl_HitBox_Check.hTanxl_LocationBase.h 同属 源文件 筛选器,过滤器归属保持不变。


四、调用点与外围同步(代码修复)

4.1 main.cpp 调用点同步
cpp 复制代码
// Build-15
GameStateBase* GSB{ &GameStateBase::GetStateBase(10, 10) };

// Build-16
GameStateBase* GSB{ &GameStateBase::Get_StateBase(10, 10) };
4.2 OpenGL_Draw 调用点同步

Tanxl_OpenGL_Draw.cpp - init()

cpp 复制代码
// Build-15
HeightInt = State->GetStateHeight();
WidthInt = State->GetStateWidth();

// Build-16
HeightInt = State->Get_StateHeight();
WidthInt = State->Get_StateWidth();
4.3 DataBase 模块注释补充

Tanxl_DataBase.hPrint_Data() 的接口注释补充了空内存时的行为说明,代码实现未作调整。

cpp 复制代码
// Build-15
//↓输出当前内存中的链表的所有内容 仅支持输出Id_Vector和Data_Vector中的内容
void Print_Data();

// Build-16
//↓输出当前内存中的链表的所有内容 仅支持输出Id_Vector和Data_Vector中的内容 当前内存为空时会抛出错误
void Print_Data();

影响范围总览

文件 旧版行数 新版行数 变更类型
Tanxl_GameState.cpp 215 257 修改(接口重命名×9、拆分CompileState、新增地图数据接口×2)
Tanxl_GameState.h 86 92 修改(接口声明同步、新增接口声明×2、补充注释)
Tanxl_InsertAction.cpp 70 84 修改(新增Set_MaxLine/Get_MoveData、注释保留判定逻辑、构造函数初始化)
Tanxl_InsertAction.h 59 62 修改(新增成员×2与接口声明×2、版本头更新)
Tanxl_HitBox_Check.h - 4 新增(碰撞模块占位)
Tanxl_LocationBase.h 17 - 删除(功能并入InsertAction模块)
main.cpp 63 63 修改(调用点同步×1)
Tanxl_OpenGL_Draw.cpp 96 96 修改(调用点同步×2)
Tanxl_DataBase.h 152 152 修改(接口注释补充)
TANXL_ENGINE.vcxproj 197 197 修改(ClInclude条目替换)
TANXL_ENGINE.vcxproj.filters 84 84 修改(ClInclude条目替换)
Tanxl_DataBase.usd 36 135 修改(测试运行产生的Item数据)

净代码量变化: +52行(GameState接口扩展 +48,InsertAction碰撞预留 +17,HitBox_Check新增 +4),-17行(LocationBase模块删除)


技术要点总结

地图数据流向
复制代码
GameStateBase::Get_GameState()
        │
        │  返回 std::vector<StateUnit*>*
        ▼
  StateUnit 网格  ──►  _State_Id / Is_Move_Target
        │
        │  逐个读取 GetMoveAble()
        ▼
GameStateBase::Get_GameState_MoveAble()
        │
        │  返回 std::vector<bool>*
        ▼
InsertEventBase::Get_MoveData(std::vector<bool>*)
        │
        │  保存至 _PTB
        ▼
InsertEventBase::GetInsert()  移动合法性判定(Build-16 注释保留)
        ├─ X轴:_PTB[i + 1] / _PTB[i - 1]
        └─ Y轴:_PTB[i + _Max_Line] / _PTB[i - _Max_Line]
单元格索引与行列换算
复制代码
_Max_Line 为地图一行的格子数,用于 Y 轴方向取上下相邻格

索引 i 的上下相邻格:
┌─────┬─────┬─────┐
│ i-L │     │     │   ← 上一行同列
├─────┼─────┼─────┤
│     │  i  │     │   ← 当前格
├─────┼─────┼─────┤
│ i+L │     │     │   ← 下一行同列
└─────┴─────┴─────┘
        L = _Max_Line

索引 i 的左右相邻格:i - 1 / i + 1
核心 API 说明
API/配置项 所属模块 说明
Set_State(int, int) GameState 设置地图宽高并重建 StateUnit 网格
CompileStateUnits(std::string) GameState Id-Move,Id-Move 格式编译地图格子
CompileStateEvent(std::string) GameState Id,Id,Id 格式编译格子事件绑定
Get_GameState() GameState 返回内部 StateUnit 容器地址
Get_GameState_MoveAble() GameState 返回与 StateUnit 同序的可移动标记数组地址
StateUnit::GetMoveAble() GameState 读取单个格子的可移动标记
Set_MaxLine(int) InsertAction 设置地图一行的格子数
Get_MoveData(std::vector<bool>*) InsertAction 接入地图可移动标记数组
待处理项
  • Get_GameState_MoveAble() 返回的是函数内局部 std::vector<bool> MAB 的地址,该容器在函数返回后即被析构,调用方通过该指针访问属于悬垂访问,需改为返回对象或改用成员容器承载。
  • CompileStateUnits() 循环初始化中的 SetCount 变量当前未被使用。
  • InsertEventBase::_PTB 为裸指针成员,Get_MoveData() 不做生命周期管理,接入时需保证传入数组的生命周期覆盖 InsertEventBase 的使用周期。

Wild_Chicken_Programing /// 2022-05-30 01:18

相关推荐
番茄巴士2 小时前
手写一个 mini HashMap,彻底搞懂哈希表原理
算法
孙启超2 小时前
【AI开发之Rust】第 8 课:泛型、trait 与 trait 对象
开发语言·后端·rust
宣宣猪的小花园.2 小时前
【机器学习】过拟合与泛化:模型为什么会“刷题很强、实战失灵”
人工智能·算法·机器学习
INGNIGHT2 小时前
624.数组列表中的最大距离(maximum)
算法·散列表
2501_933670792 小时前
经营财务岗技能栈拆解:Excel、SQL、BI、预算分析怎么准备
开发语言
Niuguangshuo2 小时前
论文解读:w2v-BERT,把 wav2vec 2.0 和 BERT 合成一根管子的语音 SSL
算法·音视频·语音识别
科技小E2 小时前
国标视频分析平台EasyGBS×自动化AI算法训练服务器DLTM,把通用AI炼成你的现场AI
算法·自动化·音视频
zander2582 小时前
LeetCode 15. 三数之和
算法
AiNightVision2 小时前
NMC存算一体与AI ISP
人工智能·算法·车载系统·自动驾驶·无人机·视频·智能硬件