找回密码
 立即注册
查看: 429|回复: 0

slua unreal分析(四)LuaArray&LuaMap

[复制链接]
发表于 2020-11-25 09:03 | 显示全部楼层 |阅读模式
相关文章:
之前介绍的UObject,UStruct传递都是单个元素传递,slua也支持array和map的容器传递。
LuaArray

UE中数组类型为TArray,把TArray从UE传到lua,会用一个LuaArray类把TArray包装起来,LuaArray也提供了迭代接口供lua使用数组常用操作。
TArray传到Lua时有两种创建LuaArray方式,一种会把TArray复制一份,Lua中对LuaArray操作不会影响C++TArray,另一种是把TArray引用传到Lua。Lua对LuaArray的添加和删除会影响C++的TArray。


复制一份
当TArray作为函数的返回值时,会复制一份,相关代码如下:
构造函数接受两个参数,分别为数组元素的UProperty,和源array数据。
  1. LuaArray::LuaArray(UProperty* p, FScriptArray* buf)
  2.     : inner(p)
  3.     , prop(nullptr)
  4.     , propObj(nullptr)
  5. {
  6.     array = new FScriptArray();
  7.     clone(array, p, buf);
  8. }
复制代码
  1. void LuaArray::clone(FScriptArray* destArray, UProperty* p, const FScriptArray* srcArray) {
  2.     // blueprint stack will destroy the TArray
  3.     // so deep-copy construct FScriptArray
  4.     // it's very expensive
  5.     if(!srcArray || srcArray->Num()==0)
  6.         return;
  7.         
  8.     FScriptArrayHelper helper = FScriptArrayHelper::CreateHelperFormInnerProperty(p,destArray);
  9.     helper.AddValues(srcArray->Num());
  10.     uint8* dest = helper.GetRawPtr(;
  11.     uint8* src = (uint8*)srcArray->GetData();
  12.     for(int n=0;n<srcArray->Num();n++) {
  13.         p->CopySingleValue(dest,src);
  14.         dest+=p->ElementSize;
  15.         src+=p->ElementSize;
  16.     }
  17. }
复制代码
源array类型为FScriptArray,可以认为是一个数组,通过遍历FScriptArray可以把元素逐个复制到luaarray的array属性中。因为我们已经知道了数组内元素的UProperty类型,因此可用对应的CopySingleValue方法利用反射机制来拷贝元素。


传递引用
把UObject上的TArray属性进行传递时,会使用传递引用的方式。LuaArray构造函数中,并没有创建新的FScriptArray,而是把Tarray的指针赋给了array,因此Lua中操作的就是TArray本身,对LuaArray的add和remove操作都会影响到实际的TArray。
  1. LuaArray::LuaArray(UArrayProperty* p, UObject* obj)
  2.     : inner(p->Inner)
  3.     , prop(p)
  4.     , propObj(obj)
  5. {
  6.     array = prop->ContainerPtrToValuePtr<FScriptArray>(obj);
  7. }
复制代码

数组迭代
LuaArray实现了基本的add,remove,迭代等功能,前面的相对简单,主要介绍下迭代功能。
Lua中,可以通过for in pairs语法遍历LuaArray中的元素:
  1. for i,v in pairs(arr) do
  2.     print("arr item",i,v)
  3. end
复制代码
slua提供了LuaArray::Enumerator类作为迭代器,LuaArray::Enumerable作为迭代函数,来实现pairs功能,迭代函数内容如下:
  1. int LuaArray::Enumerable(lua_State* L) {
  2.     CheckUD(LuaArray::Enumerator, L, 1);
  3.     auto arr = UD->arr;
  4.     if (arr->isValidIndex(UD->index)) {
  5.         auto element = arr->inner;
  6.         auto es = element->ElementSize;
  7.         auto parms = ((uint8*)arr->array->GetData()) + UD->index * es;
  8.         LuaObject::push(L, UD->index);
  9.         LuaObject::push(L, element, parms);
  10.         UD->index += 1;
  11.         return 2;
  12.     }
  13.     return 0;
  14. }
复制代码
UD就是Enumerator,index为当前遍历到的下标,根据下标和数组,直接去ScriptArray对应的内存偏移取元素指针,返回给lua。
LuaArray与GC
LuaArray会对内部的元素产生引用,UEGC会看到这些引用,当LuaArray销毁,这些引用也会消失。因为LuaArray继承自FGCObject,内部实现了AddReferencedObjects()函数,每次UE执行GC时,都会调用这个函数。函数内部通过之前介绍的LuaReference方法,模拟一遍UE的引用添加逻辑。


LuaMap

LuaMap与LuaArray的实现思想基本相同,也实现了Map的add,remove等常用功能,最大的区别可能是LuaMap底层数据为ScriptMap散列表,这也使LuaMap在一些处理上要小心对待。
如下图所示,ScriptArray和ScriptMap都存储了3个元素,容器中也都留有空位。对于ScriptArray,做clone、迭代等操作时只要遍历前三个位置即可,也不需要判断前三个位置元素是否有效,因为它们肯定是有效的。而ScriptMap由于是散列表,因此clone、迭代时必须把整个容器都遍历一遍,还有判断当前元素是否有效,过滤掉空的槽位。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-9-19 11:47 , Processed in 0.254575 second(s), 27 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表