码迷,mamicode.com
首页 > 编程语言 > 详细

unity3D 知识点随手记

时间:2018-04-09 21:08:09      阅读:157      评论:0      收藏:0      [点我收藏+]

标签:stop   index   性能   ring   工作   clear   www   ++   基本   

  最近闲来无事,记记unity3D相关的一些知识点吧,也当作笔记存储。转载请标明出处:http://www.cnblogs.com/zblade/

1、unity是如何调用Start/Awake等相关函数的?

  在unity中,一个常见的问题是awake, start, update等相关函数的执行顺序,这个就不在这儿赘述了,一个比较深入的问题,是如何调用这些函数的。如果是虚函数的重载,那么我们为什么没有override关键字?我查阅了一下,知乎上有一个相关问题,大概是2个方向的意见。一个是源代码显示,unity的mono支持对函数名进行字符串获取,在获取到这些函数名后,再进行反射调用。其实说的更浅白一点,就是继承自mono的类,unity都会对其中的函数进行引用统计,在每帧调用的时候,对于非空的函数,都会执行一次遍历反射回调。很多人觉得反射对于性能影响比较大,其实可以用缓存的方式,在第一次反射执行后,缓存下来,下一次执行的之后可以直接从缓存中获取,直接执行,所以很多人测试发现反射的性能接近于函数调用,就是这个原理。

 

2、图集的原理及使用图集的原因?

  图集的本质,其实就是一张大图,将各个图集中的小图合并到一张大图,然后还有一份保存各个小图的尺寸、位置、偏移等信息的数据文件,所以一般一个图集会对应2个文件,当然如果把数据文件也打包进去,就会只有一个数据文件。

  使用图集的原因,有这么几点:首先,使用图集可以方便的管理图片资源;其次,在每次绘制一张图的时候,都会在GPU阶段送入一张图片进去,这样的一次操作就会触发一次DrawCall,如果有几十上百个图,那么每次在转换的时候,都会触发多次DrawCall,使用图集,可以只触发一次DrawCall,把所有相关的图片都塞入进去,从而大大降低DrawCall;最后使用图集也方便图片资源的加载和卸载。

 

3、lua相关的一些问题

  1)lua中table实现的原理

  lua对于table的设计,是基于数组和hash共同兼容的,对于数组,其主要存储连续的同类型数据,hash则通过key-value的方式存储。对于hash和数组,默认大小都是0,然后是1,2,4等等基于2的幂次递增,由于每次递增的时候,都会进行一次rehash,所以性能都消耗在rehash上,所以在创建table的时候,尽量避免这样rehash的操作,比如:

  local t1 = {}  t["x"] =1, t["y" ] =2 , t["z"] = 3, 这样三次操作,就会触发三次rehash,想想原理即可明白

       local t2 = {"x" = 1, "y" = 2, "z" = 3}, 这样只会触发一次rehash,对比节省3次性能。

       2)请回答lua中对于key值的查找过程

  lua对于key值的查找,首先会去数组和hash中查找对应的key值,如果存在,则返回;如果不存在,则查找该table是否有元表metatable,如果没有,则返回nil;如果有,则查看metatable中是否有__index方法,如果没有,则返回nil;如果有,则执行__index[key]查找,返回对应的值。

       3)lua如何执行GC,以及对应的原理和API设置

   lua GC的原理,推荐大家阅读一下云风的blog中对于GC的详细过程的阐述,结合源代码,有较为详细的讲解过程:Lua GC的源码剖析

  这儿做一个读后笔记记录吧:

          (1) Lua GC对象

          lua中一共有9种数据类型,分别为nil, boolean, lightuserdata, number, string, table, function, userdata 和 thread。其中, string, table, function, thread会被GC处理,此外还有proto和upvalue需要被GC处理。

          (2) Lua 数据定义方式 union + type

//Union of all Lua values
typedef union
{
    GCObject* gc;  
    void* p;  
    lua_Number n;
    int b;
}Value;

#define TValuefields Value value;
int tt
typedef struct lua_TValue
{
    TValuefields;
}TValue

  所有的GCObject都有一个相同的数据头,CommonHeader,其定义为:

#define CommonHeader GCObject* next; lu_byte tt; lu_byte marked;

  这样所有的GCObject都会被同一个单向链表串接起来,每个对象基于tt识别,marked用来标记清除的工作

        (3) Lua 对不同类型的清除操作分类

         Lua在每次GC清除的的时候,分为多种类型:

         对于GCObject,通过若干根节点开始,逐个直接或者间接的将其上的所有节点左上标记,完成标记后,遍历链表,对未被标记的节点执行删除操作;

         对于string类型,由于所有的string都放在一张大的hash表中,这样是为了确保整个lua中同一个string不会被创建两份,所以其是被单独管理的,不会被串在GCObject的链表中

         对于upvalue类型数据,也是一个特殊处理过程,这是由于GC可能分布扫描,由于upvalue是对已有的对象的间接引用,在创建的时候不属于创建新数据,在mark的过程中需要添加luaC_barrier

         对于userdata, 由于userdata都有gc方法,所以会在最后单独处理逐一遍历所有的userdata来执行其中的gc方法,会有一些特殊的处理

         (4) Lua执行GC的几个流程

         Lua执行GC的几个流程,可以分为5步: GCSpause\ GCSpropagate\GCSsweepstring\GCSsweep\GCSfinalize,从lua5.1 开始就执行分布GC,每次执行可能会有多个状态切换

         GCSpause 为GC阶段的启动流程,标记系统的根节点即可

         GCSpropagate 这是标记流程,对尚未标记的对象(灰色链表)迭代标记(反复调用propagatemark),否则在atomic函数中执行一次标记

         GCSsweepstring 这就是前面提起的对string类型的数据,进行特殊的处理,在这个状态中,每步都会清除string的hash表中的一列

         GCSsweep  和上一个状态类是,不过这步操作的对象是GCObject

         GCSfinalize 在这儿主要对userdata执行,如果需要调用其gc,则执行gc操作,由于userdata的对象和关联数据不会在之前的清除阶段被清除,所以其实际清除会在下一次的GC清除中执行或者在lua_close中被清除:lua_close的工作就是简单的处理所有userdata的gc元方法,以及释放其所用到的内存。

         (5) Lua GC的标记流程

     Lua对于所有的GCObject都设置一个颜色,最开始是白色,新建的节点也是白色,然后在标记阶段,可见的节点被设置为黑色,如果某些节点关联其他节点,在没有处理完其关联节点前,都被标记为灰色,对于颜色的标记,其存储在CommonHeader的8位的marked域中,对于白色有两个白色的标记位,采用一种乒乓开关,避免在标记完成后,清理没有完成前,对象间关系发生变化的时候,某些不需要被清理的节点,就可以从一种类型的白色转换到另一种类型的白色中,比如当前删除0型白色,那么转换到1型白色,这样1型白色就会被保护起来不会被删除,反之亦然。具体对于8个位的定义和使用,可以看云风的原文,有一定讲解。

          (6) Lua GC的操作

         常用的几个API: luaC_fullgc\ luaC_step\luaC_checkGC

        luaC_fullgc: 执行一次完整的gc动作,对于可能执行一般的流程,在走完一次流程后,会阻塞状态再次执行一遍gc,对于已经执行的前半程gc,其实不需要做清除操作,只需要做状态回复

        luaC_step: 其核心在与调用singlestep函数,通过设置gcstepmul值,可以设置步长,从而影响gcthreshold,其实步进量的设置,是一个经验值

        luaC_checkGC: 自动GC的接口,在大部分导致内存增长的api中会调用该方法,自动GC,可能会在某一个周期性中将众多临时对象也mark了,造成系统的峰值内存占用比实际需求大,可以在这种周期性调用中采用gcstep的方法,同时设置较大的data量,使得有限周期做一个完整的gc。

     (7) Lua GC的mark操作

         对于Lua的mark操作,主要操作的API: markroot\ reallymarkobject\remarkupvals\atomic\iscleared

         (8) Lua GC的write barrier操作

         主要的API: luaC_barrier\luaC_barriert\luaC_objbarrier\luaC_objbarriert

         (9) Lua GC的剩余操作 sweep/finalize

         sweep的操作分为 GCSsweepstring 和GCSseep, 贴2个源码:

    case GCSsweepstring: {
      lu_mem old = g->totalbytes;
      sweepwholelist(L, &g->strt.hash[g->sweepstrgc++]);
      if (g->sweepstrgc >= g->strt.size)  /* nothing more to sweep? */
        g->gcstate = GCSsweep;  /* end sweep-string phase */
      lua_assert(old >= g->totalbytes);
      g->estimate -= old - g->totalbytes;
      return GCSWEEPCOST;
    }
    case GCSsweep: {
      lu_mem old = g->totalbytes;
      g->sweepgc = sweeplist(L, g->sweepgc, GCSWEEPMAX);
      if (*g->sweepgc == NULL) {  /* nothing more to sweep? */
        checkSizes(L);
        g->gcstate = GCSfinalize;  /* end sweep phase */
      }
      lua_assert(old >= g->totalbytes);
      g->estimate -= old - g->totalbytes;
      return GCSWEEPMAX*GCSWEEPCOST;
    }

  对于seeplist,其源代码为:

static GCObject **sweeplist (lua_State *L, GCObject **p, lu_mem count) {
  GCObject *curr;
  global_State *g = G(L);
  int deadmask = otherwhite(g);
  while ((curr = *p) != NULL && count-- > 0) {
    if (curr->gch.tt == LUA_TTHREAD)  /* sweep open upvalues of each thread */
      sweepwholelist(L, &gco2th(curr)->openupval);
    if ((curr->gch.marked ^ WHITEBITS) & deadmask) {  /* not dead? */
      lua_assert(!isdead(g, curr) || testbit(curr->gch.marked, FIXEDBIT));
      makewhite(g, curr);  /* make it white (for next cycle) */
      p = &curr->gch.next;
    }
    else {  /* must erase `curr‘ */
      lua_assert(isdead(g, curr) || deadmask == bitmask(SFIXEDBIT));
      *p = curr->gch.next;
      if (curr == g->rootgc)  /* is the first element of the list? */
        g->rootgc = curr->gch.next;  /* adjust first */
      freeobj(L, curr);
    }
  }
  return p;
}

  基本看源代码就可以理解,对于dead的freeobj,没有dead的则执行makewhite,最后一个流程就是GCS finalize,通过GCTM函数执行,每次调用一个需要回收的userdata的gc元方法:

static void GCTM (lua_State *L) {
  global_State *g = G(L);
  GCObject *o = g->tmudata->gch.next;  /* get first element */
  Udata *udata = rawgco2u(o);
  const TValue *tm;
  /* remove udata from `tmudata‘ */
  if (o == g->tmudata)  /* last element? */
    g->tmudata = NULL;
  else
    g->tmudata->gch.next = udata->uv.next;
  udata->uv.next = g->mainthread->next;  /* return it to `root‘ list */
  g->mainthread->next = o;
  makewhite(g, o);
  tm = fasttm(L, udata->uv.metatable, TM_GC);
  if (tm != NULL) {
    lu_byte oldah = L->allowhook;
    lu_mem oldt = g->GCthreshold;
    L->allowhook = 0;  /* stop debug hooks during GC tag method */
    g->GCthreshold = 2*g->totalbytes;  /* avoid GC steps */
    setobj2s(L, L->top, tm);
    setuvalue(L, L->top+1, udata);
    L->top += 2;
    luaD_call(L, L->top - 2, 0);
    L->allowhook = oldah;  /* restore hooks */
    g->GCthreshold = oldt;  /* restore threshold */
  }
}

  在回收的时候,设置较大的GCthreshold来避免GC的重入

4、unity中协程的理解

  协程的本质是一个分部执行函数,在unity的mainThread中执行,unity在每帧的更新中,都会执行各个协程调用,分别在FixedUpdate和LateUpdate之后的一些协程调用上,其本质就是一个迭代器,当遇到条件不满足的时候会被挂起,条件满足的时候,会被唤醒来继续执行。举个在其他地方看到的例子吧,这样便于讲解过程:

 

void Start()
{
     StartCoroutine(Test1());
}

IEnumerator Test1()
{
      LogWrapper.Error("a1");
      yield return Test2();  
      LogWrapper.Error("a2");
}

IEnumerator Test2()
{
       LogWrapper.Error("b1");
       yield  return null;
       LogWrapper.Error("b2");
}

 

  会输出什么的顺序:a1, b1, b2, a2

      执行的顺序是先输出a1,然后执行Test2,输出b1,这时候遇到yield return null ,被挂起,在下一帧,被唤醒,继续执行,输出b2, 接着执行输出a2

       如果对这个过程理解了,那么协程基本就没问题了

 

5、unity中meta文件的作用

      有两个作用,第一是包含了当前资源(代码或者prefab, 图片等)在当前工程中唯一的guid, unity获取资源是依据guid来获取的,所以每个资源都会附带生成一份meta文件;

       第二个,包含了当前资源的导入信息,比如图片资源,会包含一下bump等相关的信息

 

 未完待续,持续更新ing

 

           

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  

 

unity3D 知识点随手记

标签:stop   index   性能   ring   工作   clear   www   ++   基本   

原文地址:https://www.cnblogs.com/zblade/p/8760029.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!