前篇:游戏开发设计模式之命令模式(unity3d示例实现)

前篇:游戏开发设计模式之命令模式(unity3d示例实现)

博主还年轻,难免会犯错误,尤其是设计模式,很有禅意游戏开发设计模式,需要大量的经验。 如有书写错误或遗漏,请指正。

原理:重用固定池中的对象来提高性能和内存使用率,而不是一一分配内存然后释放。

当需要创建大量重复对象并频繁使用这些对象时,应该考虑使用对象池,因为重复创建和销毁就是重复分配和释放内存的过程,很容易造成内存碎片。

与 PC 相比,游戏机和移动设备上的内存匮乏。 我们都希望游戏能够更加稳定,但是内存却无法得到有效的管理。 这时候大量的内存碎片是致命的。

内存碎片是指内存被一一分割成小块,而不是整个大块。 所有小内存块的大小可能很大但无法使用。 比如你要分配16bytes的内存,那么如果你有20bytes的空间,你就可以分配成功,但是如果这20个字节是内存碎片材质材料,那么两个10个字节的分配就会失败。 因此,如果存在大量内存碎片,理论上会有足够的可用内存,分配就会失败。

许多游戏公司对他们的游戏进行浸泡测试,让游戏运行几天,看看是否崩溃,以检测内存泄漏等,因为内存碎片是一个缓慢的过程,可能会产生毁灭性的结果。

游戏开发设计模式_游戏开发设计模式_开发模式设计游戏案例

内存池的原理是预先分配一大块内存,生成需要经常使用的对象,然后将其全部释放,直到不再使用为止。 内存池可以看作是可重用对象的集合。 它可以在一定程度上避免大量的内存碎片。

在创建内存池的时候,我们会生成指定数量的所有对象,然后对这些对象进行标记,以区分它们是否正在使用,所以当我们想要一个对象时,只需要从池中获取标记为“未使用”的对象即可,然后只需将其标记为“正在使用”,然后在使用完毕后将其标记为“未使用”。 这就像一个出租办公室。 需要时借出,用完后归还。 使用此方法可以重用对象。 注意:使用对象时记得初始化,不再需要对象池时立即释放。

对象池常用于粒子系统中,用来生成粒子、子弹、敌人等,或者需要播放的声音。

当您需要时:

1.频繁创建和销毁对象

2. 需要大致相同大小空间的对象

3、可能出现内存碎片

4. 可重用且创建和销毁成本高昂的对象

请毫不犹豫地使用对象池。

对象池也有一些缺点。 它可能开始生成很多对象,但其中大部分不会被使用,这会浪费大量内存。 因此,需要根据情况控制最初生成的对象数量,或者创建第二个池。 接下来我们要讲的是动态扩展对象池。 将解决这个问题。 另一种可能是世代不够游戏评测,这是一个悲剧。 一种解决办法是强行“不使用”一件或几件东西,拆东墙补西墙。 那些不会被玩家发现的最好拆掉。 另一种方式就是如上面我们讲的动态扩展对象池来解决这个问题。 非常小的对象池可能没有用,并且仍然可能导致内存碎片。 而且,初始化时瞬间分配大量内存可能会导致问题。

我们需要将生成的对象存储在池中,以便将它们取出并增加回收。 有多种选项可供选择:

1.数组,这里要么是ArrayList,要么是普通的Object[]数组。 它在内存中连续存储,索引速度很快,比较好用。 但这种数组不能动态扩展,即生成的对象数量保持不变。 如果一不小心超出了这个范围,就会导致数据溢出,而且只能存储一种类型的对象。

2、ArrayList,动态数组,可以动态扩展,也可以存储不同类型的对象。 但操作不同类型的数据时,需要装箱和拆箱(需要强制转换),带来性能损失,且类型不安全。

3.列表,通用,可以动态扩展,但不能存储不同类型的数据。 需要指定存储数据类型T。 安全型,无需装箱或拆箱。

除了这三个基本的之外,还可以使用堆、栈、哈希、字典。 如果需要根据key查找特定对象,可以使用hash或者Dictionary。 下面博主的代码使用List来演示。

生成敌人的简单代码

首先看一下敌人的类别:

using UnityEngine;
using System.Collections;
public class Enemy : MonoBehaviour {
    public AnimationClip hurtAnimation;
    public AudioClip hertSound;
    public AudioClip dieSound;
    AudioSource audiosouce;
    int Max_HP = 3;
    int Now_HP = 3;
    bool isUsed = false;
    float moveSpeed = 0;
    float act = 0;
    // Use this for initialization
    public void init(bool _isUsed, float _moveSpeed, float _act, Vector3 _scale, Vector3 _pos, Quaternion _rot,int _Max_HP)
    {
        isUsed = _isUsed;
        moveSpeed = _moveSpeed;
        act = _act;
        this.transform.localScale = _scale;
        this.transform.position = _pos;
        this.transform.rotation = _rot;
        Max_HP = _Max_HP;
        Now_HP = _Max_HP;
    }
    public bool getUsed()
    {
        return isUsed;
    }
    void hert()
    {
…受伤处理…
    }
    void Die()
    {
        audiosouce.PlayOneShot(dieSound);
        isUsed = false;
     this.GetComponent().useGravity = false;
        this.transform.position = Vector3.zero;
   //     Destroy(this.gameObject);不需要destroy对象了
    }
    void Start () {
        audiosouce = this.GetComponent();
    }
    void OnTriggerEnter(Collider other)
    {
          …hert()..
    }
    // Update is called once per frame
    void Update () {
    …逻辑…..
    }
}

init函数负责初始化敌人。 每次从池中生成时都需要对其进行初始化。 最初,isUsed 被标记为 false 表示不使用游戏开发设计模式,然后在池中调用 init() 来分配参数。 getUsed方法是池获取对象状态的方法。

一个生成敌人的简单对象池:

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class EnemyPool : MonoBehaviour
{
    public GameObject Hero;
    public GameObject perfab;
    List enemy = new List();
    int Max_Amount = 10;
    // Use this for initialization
    void Start()
    {
        InvokeRepeating("setEnemy", 1, 10);
        for (int i = 0; i < Max_Amount; i++)
        {
            enemy.Add(Instantiate(perfab, Vector3.left*i*2, Quaternion.identity) as GameObject);
        }
    }
    void setEnemy()
    {
        for (int i = 0; i < Max_Amount; i++)
        {
            if (!enemy[i].GetComponent().getUsed())
            {
  enemy[i].GetComponent().init(true,2,enemy[i].transform.localScale,_pos,Quaternion.identity, 3);
                enemy[i].GetComponent().useGravity = true;
                return;
            }
        }
           print("enemy all busy! create new!");
        addEnemy();
    }
    void addEnemy()
    {
        enemy.Add(Instantiate(perfab, Vector3.zero, Quaternion.identity) as GameObject);
        ++Max_Amount;
    }
}

博主这里设置每10秒生成一个敌人,可以自动扩大对象池。

setEnemy() 生成一个敌人。 在setEnemy()方法中,我们遍历所有敌人对象,找到未使用的敌人,将其标记为已使用,然后初始化敌人属性,成功“生成”了一个敌人。 有一个办法可以处理遍历产生的消耗,就是分成两张表:used和unused。 创建时,从未使用的表中取出,放入已使用的表中。

addEnemy()方法是扩展一个敌人,使用List的add方法。

取得成果:

开发模式设计游戏案例_游戏开发设计模式_游戏开发设计模式

游戏开发设计模式_开发模式设计游戏案例_游戏开发设计模式

博主近期效果图:最近用unity5做的一些效果图

---- 作者:wolf96

文章来源:https://blog.csdn.net/wolf96/article/details/49123627