问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

Piccolo游戏引擎源码解析与Lua脚本扩展实践

创作时间:
作者:
@小白创作中心

Piccolo游戏引擎源码解析与Lua脚本扩展实践

引用
CSDN
1.
https://blog.csdn.net/qq_58220428/article/details/136949662

本文将带领读者深入解析Piccolo游戏引擎的源码结构和核心功能。通过详细讲解引擎的文件组织、编译过程以及关键代码逻辑,帮助读者理解游戏引擎的工作原理。此外,本文还将演示如何在引擎中添加Lua脚本支持,为引擎开发提供实用的实践指导。

一、下载源码压缩包

项目地址:Releases · BoomingTech/Piccolo (github.com)

对于不熟悉Git的用户,可以按照以下方式进行下载:

二、文件和文件夹介绍

  1. .开头的文件或文件夹:这些文件通常是各个平台或工具的配置文件。在某些操作系统中,这些文件可能会默认隐藏,如果下载后发现没有这些文件,无需担心。

  2. CMake相关脚本:Piccolo引擎使用CMake进行构建,因此包含了一些CMake相关的脚本文件。

  3. 系统批处理文件:这些文件是为了方便构建Piccolo而编写的一些小脚本。

  4. LICENSE、README和ReleaseNotes:这些文件可以在阅读代码之前简单浏览,了解项目的基本信息和使用说明。

除了上述四类文件或文件夹,剩下的主要是engine文件夹,接下来将详细讲解其内容:

  • 3rdparty:存放引擎所需的第三方库源码或二进制库。
  • asset:存放引擎启动时默认关卡所需的资源。
  • bin:在构建过程中生成的文件夹。
  • configs:存放游戏启动时需要的配置文件。
  • jolt-asset:存放物理库所需的着色器。
  • shader:存放引擎启动时必须的着色器。
  • source:引擎的核心源代码。
  • template:PiccoloParser生成代码所需的模板文件。
  • .gitignore:指明需要忽略的文件夹。

三、Source文件夹解析

  • _generated:存放PiccoloParser生成的功能性代码。
  • editor:存放编辑器的源代码。
  • meta_parser:PiccoloParser的源代码。
  • precompile:包含CMake脚本和一个文件,用于在构建工程前解析源代码,生成所需的功能性代码。
  • runtime:引擎最核心的代码所在,体现了课程中提到的分层结构。
  • test:目前为空,用于以后编写单元测试或其他测试。

四、runtime核心代码关注点

  • CMakeLists.txt:指明了runtime工程的构建方式。
  • engine.cpp 和 engine.h:整个引擎的入口文件。

五、编译运行Piccolo

  1. 在终端输入cmake -B build生成工程,这行命令的意思是以当前文件夹为工程根目录,生成工程在build文件夹里。
  2. 再次输入cmake --build build命令,去构建build文件夹里面的工程。

完成上述步骤后,引擎就已经编译完成了。接下来,运行引擎:

在终端输入命令.\bin\PiccoloEditor,引擎将启动。通过右键可以旋转视角,点击Editor Mode可以切换为游戏模式。

六、代码逻辑解读

engine.h中,我们发现只有PiccoloEngine一个类,这个类中只有以下几个public的函数:

  1. startEngine函数:首先注册了一些类型的元信息,然后调用g_runtime_global_contextstartSystems函数,最后打印了一个LOG。
  2. shutdownEngine函数:与startEngine相对应,操作刚好相反。

  1. run函数:首先取得窗口关闭信息,再决定是否在每个循环中去tickOneFrametickOneFrame正是我们要说的第四个函数。

所以,PiccoloEngine做的最重要的两件事情就是启动和关闭g_runtime_global_context里面的system以及每帧的tick。

  1. tickOneFrame函数:Piccolo引擎采用逻辑和渲染分离的架构。它首先去Tick逻辑,接下来计算FPS,然后交换逻辑和渲染的数据,再Tick渲染,剩余的代码是关于窗口标题等的处理。

逻辑Tick的主体是World的Tick,一个World由多个level组成,而level最活跃的组成部分是GObject,每个Object本身没有功能,不需要更新,其功能由它身上拥有的不同component实现,所以只需要更新它的component。

七、g_runtime_global_context解析

g_runtime_global_context是一个全局变量,我们找到了RuntimeGlobalContext类,这个类包含一堆system和manager的成员变量。

startSystem函数的主要工作是实例化和初始化这些system和manager。其中,system更多是物理上需要的支持系统,比如窗口、输入、渲染等;而manager更偏向于引擎逻辑上,比如物理系统、粒子系统等。

八、总结

Piccolo引擎以engine.h中的PiccoloEngine类作为入口,由RuntimeGlobalContext类管理的system和manager进行启动和管理,同时负责每帧的逻辑和渲染更新。逻辑tick的主体是World的Tick,一个World由多个level组成,而level最活跃的组成部分是GObject,每个Object的功能由它身上的component实现。

九、动手实践:添加Lua脚本支持

步骤1:下载第三方库

需要下载两个第三方库:

  • Lua库:GitHub - lua/lua at v5.4.6
  • sol2库:GitHub - ThePhD/sol2 at v3.3.0

将它们解压缩并放置在Piccolo项目的3rdparty目录中。

步骤2:构建第三方库

sol2库已经包含CMakeLists,可以直接在3rdparty目录下的CMakeLists中添加相应内容。

对于Lua库,需要为其编写CMakeLists脚本,可以参考imgui.cmake的格式,新建一个lua.cmake文件,内容如下:

set(lua_SOURCE_DIR_ ${CMAKE_CURRENT_SOURCE_DIR}/lua-5.4.6)
add_library(lua_static STATIC
${lua_SOURCE_DIR_}/lapi.c
${lua_SOURCE_DIR_}/lauxlib.c
${lua_SOURCE_DIR_}/lbaselib.c
${lua_SOURCE_DIR_}/lcode.c
${lua_SOURCE_DIR_}/lcorolib.c
${lua_SOURCE_DIR_}/lctype.c
${lua_SOURCE_DIR_}/ldblib.c
${lua_SOURCE_DIR_}/ldebug.c
${lua_SOURCE_DIR_}/ldo.c
${lua_SOURCE_DIR_}/ldump.c
${lua_SOURCE_DIR_}/lfunc.c
${lua_SOURCE_DIR_}/lgc.c
${lua_SOURCE_DIR_}/linit.c
${lua_SOURCE_DIR_}/liolib.c
${lua_SOURCE_DIR_}/llex.c
${lua_SOURCE_DIR_}/lmathlib.c
${lua_SOURCE_DIR_}/lmem.c
${lua_SOURCE_DIR_}/loadlib.c
${lua_SOURCE_DIR_}/lobject.c
${lua_SOURCE_DIR_}/lopcodes.c
${lua_SOURCE_DIR_}/loslib.c
${lua_SOURCE_DIR_}/lparser.c
${lua_SOURCE_DIR_}/lstate.c
${lua_SOURCE_DIR_}/lstring.c
${lua_SOURCE_DIR_}/lstrlib.c
${lua_SOURCE_DIR_}/ltable.c
${lua_SOURCE_DIR_}/ltablib.c
#${lua_SOURCE_DIR_}/ltests.c
${lua_SOURCE_DIR_}/ltm.c
#${lua_SOURCE_DIR_}/lua.c
${lua_SOURCE_DIR_}/lundump.c
${lua_SOURCE_DIR_}/lutf8lib.c
${lua_SOURCE_DIR_}/lvm.c
${lua_SOURCE_DIR_}/lzio.c
#${lua_SOURCE_DIR_}/onelua.c
)
target_include_directories(lua_static PUBLIC ${lua_SOURCE_DIR_})

在3rdparty目录下的CMakeLists中添加相应内容,并在runtime的CMakeLists中链接这两个库。

步骤3:编写Lua Component代码

将Lua Component代码放在runtime的function的framework的component下,新建一个lua文件夹。

lua_component.h

#pragma once
#include "sol/sol.hpp"
#include "runtime/function/framework/component/component.h"
namespace Piccolo
{
    REFLECTION_TYPE(LuaComponent)
    CLASS(LuaComponent : public Component, WhiteListFields)
    {
        REFLECTION_BODY(LuaComponent)
    public:
        LuaComponent() = default;
        void postLoadResource(std::weak_ptr<GObject> parent_object) override;
        void tick(float delta_time) override;
    protected:
        sol::state m_lua_state;
        META(Enable)
        std::string m_lua_script;
    };
} // namespace Piccolo

lua_component.cpp

#include "runtime/function/framework/object/object.h"
#include "runtime/function/framework/component/lua/lua_component.h"
#include "runtime/core/base/macro.h"
namespace Piccolo
{
    void LuaComponent::postLoadResource(std::weak_ptr<GObject> parent_object)
    {
        m_parent_object = parent_object;
        //为m_lua_state添加一些基础的函数
        m_lua_state.open_libraries(sol::lib::base);
    }
    void LuaComponent::tick(float delta_time)
    {
        // LOG_INFO(m_lua_script);
        m_lua_state.script(m_lua_script);
    }
}

步骤4:配置Lua Component到场景中

默认场景位于engine\asset\world\hello.world.json,其中设置了关卡"default_level_url": "asset/level/1-1.level.json"

在关卡的JSON文件中,找到名为Player的物体,仿照其定义添加Lua Component:

{
    "typeName": "LuaComponent",
    "context": {
        "lua_script": "print('hello world')"
    }
}

步骤5:编译运行

在终端依次输入以下命令:

cmake -Bbuild
cmake --build build
.\bin\PiccoloEditor

切换到Game Mode模式,命令行将开始输出"hello world",这表明Lua脚本已经成功运行。

通过以上步骤,我们成功在Piccolo引擎中添加了Lua脚本支持,实现了动态脚本执行的功能。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号