Skip to content

Get Started

lilith-avatar-sharifma edited this page Oct 11, 2021 · 12 revisions

本手册旨在让大家快速上手本框架,更多的作为范例供大家查阅,若有不足或者遗漏,请随时指出。

目录

适用范围

  • 适用人群:本手册适用于对编辑器、Lua以及面向对象有一定了解,想快速上手框架的同学。
  • 适用框架版本:v 3.x.

层级结构

Hierarchy

  • World 世界根节点
    • Global 全局作用域,客户端和服务器都会执行 编辑器默认
      • LuaFunctionScript Lua常用函数库
      • ModuleRequireScript Ava框架模块脚本载入脚本,用于自定义模块载入
      • Utility 工具模块目录,客户端服务器可通用
      • Framework Ava框架目录,用于启动游戏主循环,与游戏业务无关
      • Plugin 插件模块目录,用于存放独立的自定义插件
      • Define 定义模块目录,用于存储数据
      • Module 游戏逻辑模块目录,游戏启动后,服务器/客户端会分别载入对应的模块目录

详细框架结构参考Hierarchy

层级引用关系

  • 游戏逻辑层:DefineS_ModuleC_Module
  • 框架层:Framework
  • 基础层:LuaFunctionScriptModuleRequireScriptUtility

引用关系:自上而下引用,禁止反向引用。

  • 逻辑层可以引用框架层和基础层的模块脚本
  • 框架层可以引用基础层,但是不允许引用游戏逻辑层的
  • 基础层不引用任何其他模块

(Plugin插件内容独立于框架,稍后会更新)

框架启动流程

  1. 运行LuaFunctionScript,在_G下扩展Lua标准库
  2. 运行ModuleRequireScript,载入Global下的所有脚本,载入顺序自上而下
    • 客户端/服务器在各自的Lua虚拟机中载入基础层、框架层、游戏逻辑层代码
  3. 启动框架
    • 服务器:运行World.S_Code.ServerMainScript,会执行ServerModule中的Run函数
    • 客户端:运行Player.C_Code.ClientMainScript,会执行ClientModule中的Run函数
  4. 框架层运行:在ServerModuleClientModule中启动服务器/客户端框架
    1. 初始化全局随机种子,用于Lua的math.random()函数
    2. 初始化心跳
    3. 根据EventsModule中定义的事件,生成对应的CustomEvent节点
    4. 预加载所有的CSV表格,转换为Lua Table,加载的CSV在ConfigModule里定义
    5. 启动心跳
    6. 初始化游戏逻辑层模块:顺序执行运行游戏逻辑层模块的InitDefault()Awake()Start()函数
    7. 启动更新函数:顺序执行运行游戏逻辑层每个模块中的Update()LateUpdate()FixedUpdate()更新函数

Script

框架相对于原本编辑器新增了一些脚本,本部分将讲解这些脚本的作用

LuaFunction

打开World->Global下的LuaFunctionScript,可以看到非常多的函数定义,这些是对Lua标准库的扩展,具体的用法可以查看注释上的使用说明

ModuleRequire

框架的模块引用脚本,所有模块都在这里引用,按照正确的框架目录进行模块的存放,即可不需要在这里手动进行模块的引用。

ServerMain

服务器代码的入口,原则上不必更改ServerMain中的内容

--- 服务器代码入口
-- @script Server Main Function
-- @copyright Lilith Games, Avatar Team
-- @author Yuancheng Zhang
Server:Run()

ClientMain

客户端代码的入口,不用更改里面的内容

--- 客户端代码入口
-- @script Client Main Function
-- @copyright Lilith Games, Avatar Team
Client:Run()

Module

框架中所有的模块都存放在World->Global->Module下,打开编辑器中的目录可以看到S_Module和C_Module两个文件夹,分别存放服务器模块和客户端模块。此部分主要介绍框架中自带的模块,以及如何着手编写模块。

Define

框架的World->Define目录下自带了四个模块,在模块下可以进行一些全局的定义。

Defualt

全局变量在GlobalDef下定义

Const

全局常量在ConstDef下定义

Config

用于载入Xls文件夹的模块

Events

CustomEvent的定义模块,用于事件动态生成

编写一个服务器模块

本部分具体讲解如何自己编写一个服务器模块。

在World->Module->S_Module中新建一个ModuleScript,命名为PrintAModule,代码如下:

---这是一个只会打印A的模块
-- @module PrintAModule
local PrintA, this = ModuleUtil.New('PrintA', ServerBase)

--- 初始化
function PrintA:Init()
    print('[信息]PrintAModule:初始化数据')
end

--- Update函数
-- @param dt delta time 每帧时间
function PrintA:Update(dt)
    print('我是一个每帧都会打印的A')
end

return PrintA

运行编辑器会在Output中得到以下结果:

[06:02:33.351][信息]PrintAModule:初始化数据
[06:02:33.495]我是一个每帧都会打印的A
[06:02:33.543]我是一个每帧都会打印的A
[06:02:33.577]我是一个每帧都会打印的A
[06:02:33.611]我是一个每帧都会打印的A
[06:02:33.645]我是一个每帧都会打印的A
[06:02:33.679]我是一个每帧都会打印的A
[06:02:33.712]我是一个每帧都会打印的A
[06:02:33.746]我是一个每帧都会打印的A
[06:02:33.780]我是一个每帧都会打印的A
[06:02:33.814]我是一个每帧都会打印的A
[06:02:33.848]我是一个每帧都会打印的A

至此,我们就在框架中添加了一个会每帧输出字母'A'的模块。

编写一个客户端模块

首先在Archetype->Player->Local->ControlGui下新建一个Button,命名为PrintBtn,然后在World->Module->C_Module中新建一个ModuleScript,命名为ButtonPrintModule,代码如下:

---这是一个玩家点击对应按钮就会输出的模块
-- @module ButtonPrintModule
local ButtonPrint, this = ModuleUtil.New('ButtonPrint', ClientBase)

--- 初始化
function ButtonPrint:Init()
	print('[信息]ButtonPrintModule:初始化')
	--绑定按钮事件
	localPlayer.Local.ControlGui.PrintBtn.OnClick:Connect(PrintButton)
end

function PrintButton()
	print('[客户端]你点击了按钮')
end

return ButtonPrint

在这里要注意,应该将客户端模块放在指定的地方

然后运行编辑器,可以看到Output中有以下输出:

	[2020-04-27 00:26:41.935] [信息]ButtonPrintModule:初始化

点击界面中的按钮,Output中会增加一行输出:

	[2020-04-27 00:26:48.410] [客户端]你点击了按钮

至此,我们就编写了一个简单的客户端模块.

双端通信

框架中提供了NetUtilModule模块来进行双端通信

这一部分主要讲解如何进行事件绑定、事件监听以及NetUtil模块。

NetUtil

打开World->Global->Utility下的NetUtilModule,其中内置了以下三种函数:

	NetUtil.Fire_C(_eventName, _player, ...args)

	NetUtil.Fire_S(_eventName, ...args)

	NetUtil.Broadcast(_eventName, ...args)

其中Fire_C是向客户端通信,Fire_S是向服务端通信,Broadcast是向客户端广播。

例如:

	NetUtil.Fire_C('PlayerRebornEvent',_player,1)

会触发客户端下名为PlayerRebornEvent的事件,传输的参数为1

事件枚举

CustomEvent节点在框架中可以通过更改World->Global->Define下的EventsModule模块来添加或删除,你在该模块中所列举的事件都会在框架运行时被创建。

事件绑定

对于CustomEvent的绑定,我们会使用事件名+'Handler'进行绑定,但在绑定之前,应该先在EventsModule中先枚举你需要的事件名。

编写一个客户端模块的基础上,在World->S_Event下新建一个Custom Event,命名为BtnPrintEvent

然后在S_Module下新建一个ModuleScript,命名为BtnModule,代码如下:

	---这是接收玩家点击按钮信息的模块
	-- @module BtnModule
	local Btn, this = ModuleUtil.New('Btn', ServerBase)
		
	--- 初始化
	function Btn:Init()
		print('[信息]BtnModule:初始化')
	end

	function Btn:BtnPrintEventHandler()
		print('[服务器]BtnModule知道了你点击了按钮')
	end

	return Btn

在C_Module下ButtonPrintModule的PrintButton函数中加入以下代码:

	NetUtil.Fire_S('BtnPrintEvent')

运行编辑器并点击按钮后会得到以下输出:

	[2020-04-27 01:10:08.253] [客户端]你点击了按钮 
	[2020-04-27 01:10:08.255] [信息] 服务器事件: BtnPrintEvent 
	[2020-04-27 01:10:08.270] [服务器]BtnModule知道了你点击了按钮 

对于编辑器内置的事件(可以查看API文档),我们仍然需要使用Connect方法绑定事件,如:

	--- 初始化
	function ButtonPrint:Init()
		print('[信息]ButtonPrintModule:初始化')
		
		--绑定按钮事件
		localPlayer.Local.ControlGui.BtnPrint.OnClick:Connect(PrintButton)
	end

中的按钮绑定事件:

	localPlayer.Local.ControlGui.BtnPrint.OnClick:Connect(PrintButton)

这个代码可以在C_Module下的ButtonPrintModule中找到

至此,我们就进行了一次服务器端模块的绑定

跨模块引用

原则上,在框架中最好不要通过跨模块引用进行其他模块方法的调用,这样会增加耦合度。

推荐的方法是使用事件通信来调用。

Plugin

本框架鼓励同学们自己开发插件,在World->Global->Plugin下自带了UI动效编辑的插件

参考手册

若想快速查阅框架中自带的一些方法,可以查阅参考手册