皇竹草与甜象草的区别是什么?

小说:皇竹草与甜象草的区别是什么?作者:辛北更新时间:2019-05-26字数:65020

  很久没有更新了,今天给大家写一篇游戏资源池的相关文章,就当作2017年的最后一篇文章吧。 转载请标明出处:http://www.cnblogs.com/zblade/

一、游戏项目中的资源池

  在一款游戏中,随着游戏的进行,我们会不断的创建和销毁一些角色,比如我们玩一款射击游戏,我们需要不断的发射子弹,一般的情况下,我们会不断的创建子弹,然后发射出去,在击中物体后销毁。分析整个设计的过程,我们会不断的创建子弹,然后发射出去,最后销毁它。这儿,其实就可以引入资源池的概念来解决子弹的反复创建和销毁。

  如果只是反复的创建和销毁子弹,那么每次的性能点主要在子弹的创建上,在场景中有较多角色频繁操作射击的时候,不断执行创建,会带来性能的较大消耗,从而让游戏卡帧。如果我们把这些子弹预先创建出来,塞入到一个弹夹,然后每次在射击发射子弹的时候,从预先创建的弹夹中取出来发射,每次在销毁的时候,又将其还原到弹夹中,这样循环反复的利用,可以避免每次射击子弹的时候的创建操作带来的性能消耗。将弹夹拓展一步,就是资源池的概念了。

 

二、不同设计下的资源池

  1、简单lua版本的资源池

  基于前文子弹的阐述,我就写一个简单版本的资源池的lua版本,首先,是资源池的定义:

function BulletPool:initialize()
    --缓存子弹的table
    self.mBulletPool = {}
end

  是不是觉得很简单,是的,lua版本的资源池可以只需要用一个table就简单的表示,接下来,我们只需要维护好这个table即可。首先是取子弹的接口:

function BulletPool:GetBullet()
      --如果没有定义,则执行一次兜底定义
    if not self.mBulletPool then 
           self.mBulletPool = {}
    end
    -- 如果池子里面没有可以取的了,则返回nil
    if next(self.mBulletPool) == nil then
           return nil
    end

    local bulletObj = self.mBulletPool[#self.mBulletPool]
    table.remove(self.mBulletPool, #self.mBulletPool)
    return bulletObj
end

  有了设计的接口,接下来,我们可以继续设计归还的接口,所谓有借有还再借不难,不能只从池子里面取,不归还,那池子早晚会干涸的 :D

function BulletPool:InsertBullet(bullet)
    if not bullet then return end
    if not self.mBulletPool then
        self.mBulletPool = {}
    end
    --子弹归还前的释放操作,可以不在意这一步操作
    bullet:Release()
    table.insert(self.mBulletPool, bullet)
end

  好了,有了整体的获取和归还的操作,池子的基本接口就有了,有的同学会说,如果我们想重置一遍池子怎么办?那就再写一个清除池子的操作吧 :b

function BulletPool:Release()
    for k, v in pairs(self.mBulletPool) do
        v:Release()  
    end
    
    for k, v in pairs(self.mBulletPool) do
        self.mBulletPool[k] = nil
   end
end

  这下接口都有了,让我们来应用这些接口吧 :D

      首先给角色挂载一个子弹的资源池的获取接口吧:

--获取接口
function Character:GetBullet()
    return BulletPool:GetBullet()
end
--塞入接口
function Character:RemoveBullet(bullet)
    BulletPool:InsertBullet(bullet)
end

  因为每个角色都会射出一堆的子弹,所以我们是直接挂在角色身上,就不在整个场景管理器中去管理子弹了,可以通过场景管理器的更新来执行角色的更新,从而执行所有子弹的更新,这样每个角色的子弹更新和角色更新一致。这种设计模式下,不会出现先更新角色,然后再更新子弹的带来的一些问题。

  有了这两个接口,下面就是让角色调用这2个接口:

...
--获取子弹
local bullet = mChar:GetBullet()
--没有则新建,有则重新初始化相关参数
if bullet == nil then 
    bullet = Bullet:new(...)
else
    bullet:initialize(...)
end
--塞入到角色身上的一个table中维护
mChar:AddBullet()


...
--移除子弹,比较简单
if bullet:Update() then 
    mChar:RemoveBullet(bullet)
end

  到这儿,我们完成了一个简单的lua版本的资源池的设计和实现,通过这几个接口,对资源池有一个简单的入门理解了。接下来,我们进一步编写一个c#版本的资源池吧。

  2、C#版本的资源池

     在有了lua版本的资源池入门之后,接下来我们可以进一步的设计一个c#版本的资源池了。在unity的c#中,会有各种各样的资源需要资源池来进行管理,所以我们不能单独的做某个类的资源池了,我们需要引入泛型来指代各种类型的资源池。

  先写一个简单的资源池,就实现一个获取和归还接口吧: 

using System.Collections;
using System.Collections.Generic;

public class ObjectPool<T> where T:class
{
    //用一个列表来代替lua中的table,用作资源池
    LinkedList<T> objs = new LinkedList<T>();
    
    public T GetObject()
    {
       if(objs.Count > 0)
       {
           T obj = objs.Last.Value;
           objs.RemoveLast();
           return obj;
        }
        return null;
     }

     public void ReturnObj(T obj)
     {
        if(obj != null)
        {
           obj.AddLast(obj);
        }
     }  
}    

  有了简化版本的资源池,我们可以进一步的拓展这个池子的设计。首先,我们可以将链表改为堆栈,用一个栈来代替链表,相对会比较容易控制,只需要管理入栈和出栈即可。其次,在池子已经被榨干,取完的时候,前面是直接返回一个null,我们可以继续拓展,在没有的时候,就进行一次创建操作,这个可以通过委托来实现,在池子的初始化的时候就注册相关的委托。同理,进一步的拓展出取完后的操作和归还释放时的操作委托,这样就把我们前面lua中归还池子时候释放子弹的操作封装为一个事件。说完思路,下面让我们开始吧:

using System;
using System.Collections.Generic;

public class ObjectPool<T> where T:class
{
   //堆栈
   private readonly StackT> m_stack;
   //事件
   private readonly Func<T>   m_ActionOnCreate;
   private readonly Action<T> m_ActionOnGet;
   private readonly Action<T> m_ActionOnRelease;
   //构造函数
  public ObjectPool(Func<T> actionOnCreate, Action<T> actionOnGet, Action<T> actionOnRelease)
    {
            m_stack                 = new Stack<T>();
            m_ActionOnCreate  = actionOnCreate;
            m_ActionOnGet       = actionOnGet;
            m_ActionOnRelease = actionOnRelease;
     }
    //获取接口
    public T Get()
    {
          T obj;
          if(m_stack.Count == 0)
          {
             //执行构建操作
              obj = m_ActionOnCreate();
          }
          else 
           {
              obj = m_stack.Pop();
           }
           //执行回调
           if(m_AcitonOnGet != null)
           {
              m_ActionOnGet(obj);
            }
            return obj;
     }
     //释放接口
    public void Release(T obj)
    {
       if(m_ActionOnRelease != null)
       {
           m_ActionOnRelease(obj);
       }
       m_stack.Push(obj);
    }
    //clear接口
    public void Clear()
    {
       m_stack.Clear();
    }
}    

   写到这儿,一个基本的资源池的构建算是完成了,大家可以在这个版本的基础上进一步的衍生出资源池的使用,比如给资源池的对象添加一个计时的功能,当资源计时超过一定的时间后,就将其从资源池中去除,避免资源池不断扩大。诸如此类种种,都是后续可以操作的,好了,这篇文章就写到这儿,也祝提前祝大家2018年新年快乐!

当前文章:http://www.cnsdbtzg.com/news/201904/18/content_65567.html

发布时间:2019-05-26 16:51:57

2公分日本红枫价格,苗圃最低价批发 最便宜的蔷薇批发基地 地径12公分紫叶李哪里有卖的? 河南丛生紫薇多吗? 6公分紫薇装车价格哪里最低? 地柏生长速度快吗? 大型紫藤下山桩的价格很贵吗? 美国凌霄适合北方种植吗? 紫藤苗哪里有卖的? 紫藤冬天开花吗?

89694 65006 58353 22401 26381 14082 38407 92648 54959 38659 75286 37908 34269 77831 40627 83253 83650 19635 16073 81711 20351 94621 25017

我要说两句: (0人参与)

发布