游戏开发设计模式之对象池模式(unity3d 示例实现)
来源:互联网 发布:Linux中find的exec 编辑:程序博客网 时间:2024/05/22 17:51
前篇:游戏开发设计模式之命令模式(unity3d 示例实现)
博主才学尚浅,难免会有错误,尤其是设计模式这种极富禅意且需要大量经验的东西,如果哪里书写错误或有遗漏,还请各位前辈指正。
当你需要创造大量重复的对象,而且经常使用这些对象,你就要考虑使用对象池了,因为反复创建销毁就是一个内存反复分配与释放的过程,很容易产生内存碎片。
在主机和移动端与PC相比内存稀缺,我们都希望游戏能够更加稳定,而不能有效的管理内存,此时大量的内存碎片是致命的。
内存碎片的意思是内存被分成一个一个的小块而不是整个大块,所有内存小块的大小可能很大但并不能使用,比如你想分配16byte的内存,此时如果有20byte的空间就可以分配成功,但是如果这20byte是内存碎片,为两个10byte就会分配失败。所以,如果存在大量内存碎片,理论上有足够的可用内存,也会分配失败。
很多游戏公司的游戏都会进行浸泡测试,让一个游戏跑好几天,查看是否崩溃来检测内存泄露等等,因为内存碎片产生毁灭性的结果是一个缓慢的过程。
内存池的原理就是预先分配一大块内存,生成满需要经常用的对象,然后直到不使用再全部释,可以把内存池看做是一堆可重用对象的集合。可以一定程度上避免产生大量内存碎片。
创建内存池时我们生成指定数量的所有对象,然后把这些对象做标记区分来是否正在使用,所以当我们想要一个对象时只需要从池中获取一个“未使用”标记的对象就可以,再把它标记为“使用中”,用完了再标记回“未使用”即可。就像一个租借处,需要的时候借出去,用完了再还回来。用这种方法来重用对象。注意:使用时切记要初始化对象,对象池不需要了要立即释放。
对象池经常用在粒子系统生成粒子,和子弹,还有生成敌人等等,或者是需要播放的声音。
当你需要:
1. 频繁的创建和销毁一种对象
2. 需要空间大小差不多的对象
3. 可能产生内存碎片
4. 可重用,而且创建和销毁都很消耗的对象
请毫不犹豫的使用对象池。
对象池也有一些缺点,可能开始生成很多对象但是大多用不上,浪费了大量内存,所以需要根据情况控制好开始生成对象的数量,或者创建第二个池,接下来会讲动态扩充对象池,会解决这个问题。另一种可能是生成的不够用,悲剧,一种解决办法是强行“不使用“一个或几个对象来拆东墙补西墙,最好拆掉的不会被玩家发现,还有一种就是同上接下来会讲动态扩充对象池会解决这个问题。一个很小的对象池也可能并无卵用,还是会产生内存碎片。而且在初始化时一瞬间分配大量内存,可能产生问题。
我们需要把生成的对象储存在池中,来取出增加回收,有下面几种可以选择
1.数组,此处不是ArrayList就是普通的Object[]这样的数组,在内存中是连续存储的,索引速度非常快,使用起来比较。但是这种数组不能动态扩充,也就是生成对象的数量是不变的,不小心超出了这个范围还会产造成数据溢出,而且只能存储一种类型的对象。
2.ArrayList,动态数组,可以动态扩充,也可以存储不同类型对象,但是在操作不同类型数据时需要装箱拆箱(要做一个强制转换),带来性能耗损,并且不是类型安全。
3.List<T>,泛型,可动态扩充,但是不能存储不同类型数据,需要制定存储数据类型T。安全类型,不存在装箱拆箱。
除此三种基本的之外还可以用堆,栈,哈希,Dictionary,如果需要根据key来查找具体的对象可以用哈希或Dictionary。博主下面代码用List<T>来示范。
简单的生成敌人的代码
首先看看敌人类:
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<Rigidbody>().useGravity = false; this.transform.position = Vector3.zero; // Destroy(this.gameObject);不需要destroy对象了 } void Start () { audiosouce = this.GetComponent<AudioSource>(); } 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<GameObject> enemy = new List<GameObject>(); 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<Enemy>().getUsed()) { enemy[i].GetComponent<Enemy>().init(true,2,enemy[i].transform.localScale,_pos,Quaternion.identity, 3); enemy[i].GetComponent<Rigidbody>().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()方法中我们遍历所有敌人对象来寻找未使用的敌人,再标记为标为使用,再初始化敌人属性,就成功“生成“了一个敌人。对于遍历所产生的消耗有一种办法,把分为使用和未使用两个表,create时就从未使用的表中获取,再放入使用中的表中。
addEnemy()方法为扩充一个敌人,使用List<T>的add方法。
实现结果:
博主近期渲染:最近用unity5弄的一些渲染
2 0
- 游戏开发设计模式之对象池模式(unity3d 示例实现)
- 游戏开发设计模式之原型模式 & unity3d JSON的使用(unity3d 示例实现)
- 游戏开发设计模式之命令模式(unity3d 示例实现)
- 游戏开发设计模式之状态模式 & 有限状态机 & c#委托事件(unity3d 示例实现)
- 游戏开发设计模式之状态模式 & 有限状态机 & c#委托事件(unity3d 示例实现)
- 游戏开发设计模式之子类沙盒模式(unity3d 示例实现)
- Unity设计模式之-Unity3d游戏开发设计模式之子类沙盒模式
- 【Unity3D 打斗游戏开发】之三 简单实现对象池
- 游戏开发 设计模式
- Unity3D 游戏引擎之利用C#实现代理模式
- 设计模式在游戏开发中的应用之命令模式
- 设计模式在游戏开发中的应用之观察者模式
- python设计模式之对象池模式
- Unity游戏设计模式之状态模式实现
- Unity3D之设计模式MVC
- 设计模式之对象池模式(Object Pool)
- Unity3D设计模式之观察者模式(16)(一)
- Unity3D设计模式之观察者模式(16)(二)
- myEclipse搭建SSH(Struts2+Spring3+Hibernate3)框架项目教程
- Servlet & JSP 基础4(JSTL)
- 在Linux下使用vim配合xxd查看并编辑二进制文件
- cocos2dx 3.2在android下断点调试
- 数据库初始化
- 游戏开发设计模式之对象池模式(unity3d 示例实现)
- MyEclipse安装插件的几种方法
- 如何在github上fork一个项目来贡献代码以及同步原作者的修改
- <LeetCode><Easy>292 Nim Game
- C语言回顾 一 数据类型 进制
- 剑指offer第七题【斐波那契数列】c++实现
- AngularJs + REST API 文件下载
- ZOJ 3903 Ant (公式推导)
- Android 开发中的日常积累