[关闭]
@leviyuan 2017-08-04T09:48:20.000000Z 字数 14850 阅读 326

SuperMobs.lua

有逼格的logo

utils

time 时间工具

event 事件工具

string

table

orderdict 自动排序的字典

字典中的key是要排序的数据
value是排序的依据,类型为table数组,每项值为number类型

  1. -- 创建一个排序器,排序依据的table有最多4个值
  2. -- 依次按照升序、升序、降序、降序的方式排列
  3. local compare = utils.table.compare.makearr(true, true, false, false)
  4. -- 使用这个排序器创建一个orderdict
  5. local dict = utils.table.createorderdict(compare)
  6. -- 添加一些数据
  7. dict["chiuan"] = {28, 15, 236, 0}
  8. dict["levi"] = {27, 0, 0, 0}
  9. dict["test"] = {28, 15, 0, 0}
  10. -- 调用接口测试一下
  11. -- 数据的数量
  12. print(dict:getlen()) -- 3
  13. -- 获取排序在第2位的数据
  14. print(dict:getat(2)) -- test
  15. -- 完整遍历一下
  16. for k,v in dict:ipairs() do
  17. print(k, v) -- 1,levi 2,test 3,chiuan
  18. end
  19. -- 删除一项
  20. dict["test"] = nil
  21. -- 下面这样会导致error orderdict do not support same order value
  22. dict["test_error"] = {27, 0, 0, 0}

sortdict 有序的字典

与原生table基本一样,唯一差别就是保证pairs的遍历顺序永远与添加进去的顺序一致

  1. -- 创建一个有序字段
  2. local dict = utils.table.createsortdict()
  3. -- 添加一些数据
  4. dict["levi"] = 27
  5. dict["chiuan"] = {}
  6. dict["test"] = true
  7. -- 使用接口测试一下
  8. print(dict:getfirst()) -- 27
  9. print(dict.count) -- 3
  10. -- 完整遍历
  11. for k,v in dict:pairs() do
  12. print(k, v) -- levi,27 chiuan,table0x0000 test,true
  13. end
  14. -- 删除或更新会让这一项移动到最末位
  15. dict["levi"] = 1
  16. print(dict:getfirst()) -- table0x0000
  17. dict["chiuan"] = {}
  18. print(dict:getfirst()) -- true

ecs

QQ图片20170803185521.jpg-15.4kB

名词解释

另类方式讲述ecs

有一个流水线,要做一种罐头

创建一个pool,名字叫罐头制作流水线

罐头由瓶子、盖子、水、糖浆、水果组成

我们最终要创建出的entity包含瓶子、盖子、水、糖浆、水果这五种component

流水线上有5道工序

需要5个system来实现这个过程

第一道工序,在合理的情况下(下游没有累积过多、总需求量没超),向流水线上放空瓶子

第一个system,在合适的时候,创建空entity,并添加瓶子组件

第二、三、四道工序,分别对应水、糖浆、水果,检查瓶子里没有自己要加的东西时,添加进去

第二、三、四个system,在entity上没有自己对应组件时,添加给entity

第五道工序,检查其他的都已经齐全,把盖子盖上

第五个system,当其他四个component都已经添加时,为entity添加盖子component

流水线把产品制作分成不同模块,方便分别改进优化
流水线为每个工人提供工作环境,单个工人不需要在意整体流程
产品出现问题时,可以在流水线上查找产品定位问题到单一工人
承担相同工作的工人可以在流水线上随意替换

ecs把流水线的好处带给coding

设计思路

使用ecs最大的难点是要转变设计思路,它与面向对象的程序设计思路相差甚远
下面从一个实际的需求开始讲述如何使用ecs的思路来设计解决方案

需求:服务器列表功能
1、服务器有 编号、名称、分组、排序、状态属性需要显示
2、需要自动选择玩家上次登录的服务器
3、玩家可以手动刷新服务器状态
4、服务器列表中要显示玩家在该服务器拥有的角色基本信息

一、基础数据结构设计

这一步需要设计出功能需要数据的最精简结构
这部分数据将是功能的核心数据,其他的数据都可以通过计算或者数据查询得到

在这个示例需求中,最核心的数据就是
    服务器固定信息:{编号、名称、分组、序号}
    服务器动态信息:{编号、状态}
    玩家信息:{上次登录的服务器编号}
    角色信息:{编号、名称、等级、所属服务器}
这样一个数据结构

PS:设计基础数据时要保证不同时变化的数据需要分开,示例中的服务器固定信息和动态信息
分开的好处是方便做数据匹配,可以让系统设计更加容易

二、完整的数据结构

在完整的数据结构里,将会数据的 整合、分组 设计
然后得出模块的Component设计

三、数据处理系统设计

这一步将会得到一些System的设计结论,从以下两个方向来思考

四、具体功能实现

汇总一下前面的结论,所有的Component如下:

lua版本API说明

Component

Matcher

pool

System

Group

  1. ecs.component.regist("A", "a")
  2. ecs.component.regist("B", "b")
  3. local p = ecs.pool.create("test")
  4. local group = p:getgroup(ecs.matcher.A)
  5. -- B组件的属性来做索引计算,但是group的声明中并没有涉及到B
  6. -- ecs内部更新时,不会认为B组件的变化与这个group有任何关系
  7. -- 所以索引值完全不正确,没法办法去使用
  8. group:addindex("b", function(entity)
  9. return entity.hasB and entity.B.b or 0
  10. end)
  11. -- 实际的逻辑中,如果用A+B作为group的特征描述也ok,那么可以这样声明group
  12. group = p:getgroup(ecs.matcher.A, eca.matcher.B)
  13. -- 如果只有A可以作为特征描述,B不与特征有直接关系,那么可以这样做
  14. -- 本质与getgroup(ecs.matcher.A)相同,但是向ecs系统说明了这个groupB有关
  15. group = p:getgroup(ecs.matcher.A, eca.matcher.A.any, eca.matcher.B.any)
  1. for i, entity in group:ipairs("ordername") do
  2. print(i, entity)
  3. end

Entity

Module

  1. {
  2. -- using UnityEngine;
  3. "A.B.*",
  4. "C.*",
  5. -- using C = UnityEngine.Camera;
  6. c = "D.EE",
  7. f = "D.XXXX"
  8. }

没有声明shorttable的情况下,ecs系统会在全局搜索以'.componentname'结尾的组件,如果只有一个,那就当做是这个组件来处理,如果不止一个,将会抛出error,"componet name XXXXX is not explicited"
以示例的shorttable来说明组件搜索过程:
1、查找“c = "D.EE"”这样的格式,如果匹配,那么就是等号右边就是组件的fullname
2、依次把componentname代入"A.B.*"这样格式,查找是否存在这样fullname的组件,如果有,查找结束
3、无视shorttable,去全局查找,有可能出现"componet name XXXXX is not explicited"的报错

编辑器的使用

延伸阅读


ui

使用FairyGUI引擎 http://www.fairygui.com/

资源包管理

  1. -- 定义游戏中界面的层级,数值越小越靠底部
  2. local layer = {
  3. scene = 3,
  4. hud = 4,
  5. default = 5,
  6. guide = 10,
  7. tips = 11,
  8. loading = 12
  9. }
  10. -- uilayer设置默认值
  11. ui.defaultlayer = layer.default
  12. -- 添加资源包
  13. ui.addpackage("主界面") -- 声明ui资源中有一个包叫"主界面"
  14. :dep("通用按钮") -- "主界面"资源包引用了"通用按钮"资源包,被依赖的资源包不必在引用之前声明
  15. :add("home", -- 必填字段,ui的名称
  16. "主面板", -- 可选字段,ui素材在资源包中的名称;默认值为空
  17. --使用默认值则不能创建显示对象,只能使用ui.apply接口打开ui
  18. "pkm.ui.home.main", -- 可选字段,ui脚本的加载路径,默认值为空
  19. true, -- 可选字段,界面是否是单例的,默认值为true
  20. layer.default, -- 可选字段,界面的层级,默认值为ui.defaultlayer
  21. {"home.*"} -- 可选字段,ui脚本中的ecs接口缩写说明,默认为空
  22. )

UI脚本

界面脚本被闭包括起来,可以读取全局环境,但不可以直接修改全局环境,所有的global写操作都会卸载ui逻辑对象上

  1. -- 初始化界面
  2. function initcom()
  3. herobtn = getchild("pannel/herobtn") --mainview:GetChild("pannel"):GetChild("herobtn")
  4. herobtn.onClick:Add(function() -- 为按钮添加监听
  5. ui.open("hero", 1) -- 打开hero界面,并且传一个参数 1
  6. end)
  7. pvebtn = mainview:GetChild("pvebtn")
  8. end
  9. -- 界面显示出来时候调用,会接到透穿参数
  10. function show(p1, p2)
  11. print("open params", p1, p2)
  12. end
  13. -- 被关闭时调用,可选,mainview已经被释放
  14. function onclose()
  15. print("home ui closed")
  16. end
  17. -- 自定义方法
  18. function customfunc(...)
  19. end

UI管理接口

UI脚本的一些内置方法


log

使用开源日志工具 TTConsole https://github.com/chiuan/TTConsole

LUAAPI

一些使用的tips

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注