unity3d中 内存池和unity 3d 子弹 对象池池的区别

Unity 内存优化 和 内存池使用实践10 months ago有一些脚本会需要在游戏流程中访问对象池,因此用public static instance令各个脚本可以访问它而不需要从GameObject上获取Component的操作。在脚本顶端,增加以下using声明:using System.Collections.G
你将会使用(using)一个泛型(generic)列表来存储入池了的对象。这一声明允许你访问泛型数据结构,这样就可以在脚本里用到List类。注意:普通(泛型的原文Generic也有普通的意思)?没人想要普通!每个人都希望独特!在程序语言,比如C#里,泛型(普通)能让你的代码可以被很多不同的类型所使用,同时保持类型安全(type safety)。一个典型的应用就是在使用集合(collection)的时候使用泛型。这个方法限定了在数组(array)中存放对象的类型,以防比如在猫数组里放了个狗,尽管这会显得很神奇。:]说到列表(list),添加对象池列表以及两个新的公共变量:public GameObject objectToP
public int amountToP
这些变量的命名是很自明的(self-explanatory)。通过Unity的Inspector,你将能够指定GameObject入池,并且设置预实例化的数量。一会儿会做这个。然后,把这些代码加入Start():pooledObjects = new List&GameObject&();
for (int i = 0; i
amountToP i++) {
GameObject obj = (GameObject)Instantiate(objectToPool);
obj.SetActive(false);
pooledObjects.Add(obj);
for循环会实例化numberToPool所指定的数量的objectToPoolGameObject。然后这些GameObject会被设置为inactive状态,之后再加入到pooledObjects列表中。回到Unity然后在Inspector中添加Player Bullet Prefab到objectToPool变量上。在numberToPool字段上填入20。再次运行游戏。现在场景(视图)里应该有20个预实例化好但无家可归的子弹了。干得好!你现在拥有一个对象池了 :]深入对象池回到ObjectPooler脚本然后添加以下代码:public GameObject GetPooledObject() {
for (int i = 0; i
pooledObjects.C i++) {
if (!pooledObjects[i].activeInHierarchy) {
return pooledObjects[i];
首先要注意的是这个方法现在的返回值类型是GameObject而非void。这是指别的脚本可以向GetPooledObject请求一个池中的对象,而他会返回一个GameObject作为回应。其他会发生的事情如下:这个方法使用for循环遍历你的pooledObjects列表。检查其中一个对象是否并非激活(active)状态。如果是激活的,循环到下一个。如果是非激活的,退出方法并把这个对象提交给对GetPooledObject的调用者。如果目前没有非激活状态的对象,退出方法并且不返回任何东西。现在,你可以向池子请求对象了,这需要替换掉原有的子弹的实例化和销毁代码,以对象池替代。玩家的子弹会在ShipController脚本里的两个方法中(被)实例化。Shoot()ActivateScatterShotTurret()在MonoDevelop里打开ShipController脚本(并找到下列代码):Instantiate(playerBullet, turret.transform.position, turret.transform.rotation);
把两处实例都用以下代码替换:GameObject bullet = ObjectPooler.SharedInstance.GetPooledObject();
if (bullet != null) {
bullet.transform.position = turret.transform.
bullet.transform.rotation = turret.transform.
bullet.SetActive(true);
注意: 在继续之前,确保在Shoot()和ActivateScatterShotTurret()方法中也做好了这样的替换。在之前,这些方法遍历玩家飞船上的激活着的子弹列表((基于加成道具)),然后在枪口位置与角度上实例化子弹。而现在改成了询问ObjectPooler脚本获取一个池中的对象。如果拿到了,将它设置到枪口的位置与角度,然后设为active状态来向敌人倾泻下你的弹火吧:]归还对象池当你不再需要使用子弹的时候,将它们归还到池中,而不是将对象销毁。有两个方法可以销毁不再需要的玩家子弹:net当子弹移出屏幕时,调用DestroyByBoundary中的OnTriggerExit2D() 方法移除它们。OnTriggerEnter2D() in the EnemyDroneController script removes the bullet when it collides and destroys an enemy.*当子弹碰撞并摧毁敌人时,调用EnemyDroneController中的OnTriggerEnter2D()方法移除它们。在MonoDevelop中打开DestroyByBoundary,并将OnTriggerExit2D方法替换成如下代码:if (other.gameObject.tag == "Boundary") {
if (gameObject.tag == "Player Bullet") {
gameObject.SetActive(false);
Destroy(gameObject);
那些离开屏幕范围时需要被移除的对象可以使用这一段通用脚本加以控制。这段代码可以判断这些触发了碰撞的对象是否拥有Player Bullet标签——如果有,则将其设置为非活动状态,而不是直接销毁。类似地,打开EnemyDroneController并找到OnTriggerEnter2D()方法,将Destroy(other.gameObject);替换成这一行代码:other.gameObject.SetActive(false);
等等,刀下留人!没错,我几乎都能看见你的鼠标指在了运行按钮上。在进行了这么一大段编程之后,你肯定对于测试对象池跃跃欲试了吧。先别忙!我们还要再改一个脚本——别担心,只是一点小改动。^_^在MonoDevelop中打开BasicMovement脚本,并将Start方法重命名为OnEnable。这里有个坑:使用对象池模式时,注意池中对象的生命周期会和之前有所不同。好了,你可以点击运行了。^_^当你射击时,在Hierarchy中的玩家子弹自动从未激活状态变为激活状态。它们会在移出屏幕、或击中敌机的时候,以优雅代码应有的方式变成回未激活的状态。干的漂亮!但假如你收集了所有的加成道具,会怎样?“啊呀,子弹不够用了”?至高无上的游戏设计者当然可以控制一切!比如可以控制玩家的火力,玩家就会更加专注于射击目标,也可以反过来鼓励玩家随时随地射个痛快。若要实现这样的控制,只要修改最初的池中设定过的子弹数量就可以了。相反的,你也可以走另一条路线,给池中设置一大堆的子弹,直接满足收集了全部加成道具的场景。但这会产生一个问题:若你在百分之九十的情况下只需要50发弹药,为什么要为这难得一见的终极力量而给池中设计100一百发的容量呢?毕竟这样做,内存中就会有50发子弹几乎派不上用场了。超级扩展对象池现在你将学习如何修改对象池,以在运行时按需增加池中的对象数量。在MonoDevelop中打开ObjectPooler脚本,并将以下代码加入到公有变量中:public bool shouldExpand =
这个代码会在Inspector中创建一个复选框,用来决定是否可以增加池中对象的数量。在GetPooledObject()中,将修改为如下代码:if (shouldExpand) {
GameObject obj = (GameObject)Instantiate(objectToPool);
obj.SetActive(false);
pooledObjects.Add(obj);
当向池中请求玩家子弹时,若找不到未激活的子弹可以使用,这段代码就会设法扩展对象池,而不是简单的退出。如果这样做,你将实例化一个新子弹,设置其为未激活状态,将其加入到池中,并作为返回值交给请求者。在Unity里点击运行,然后试试看吧。搞点儿加成道具,射个痛快!你的20发子弹的对象池是可以根据需要自行扩展的!对象池群Invariably, lots of bullets mean lots of enemies, lots of explosions, lots of enemy bullets and so on.通常情况下,大量的子弹就意味着大量的敌人,不停的爆炸效果,敌人们大量发射的子弹,以及其他方方面面开销。要为这场疯狂屠戮做准备,你需要扩展你的对象池以便于处理复杂的对象类型。接下来的一步,将实现在Inspector中的相同位置调节 不同类型自带的参数。在ObjectPooler类的前面加入以下代码:[System.Serializable]
public class ObjectPoolItem {
[System.Serializable]允许你在Inspector里实例化这个类的对象。接下来将objectToPool,amountToPool 和 shouldExpand这三个变量移动到新的ObjectPoolItem类里。注意:在移动过程中会遇到报错,不过我们稍后会搞定它。更新之后的ObjectPoolItem类应该是这样的:[System.Serializable]
public class ObjectPoolItem {
public int amountToP
public GameObject objectToP
public bool shouldE
现在一些ObjectPoolItem的实例就能修改自己成员数据与行为了。当你添加了上面的共有变量(public variables),你需要确保从ObjectPooler中删除它们。将下列代码加入ObjectPooler中:public List&ObjectPoolItem& itemsToP
新的变量列表可以让你控制ObjectPooler的实例接下来你需要调整ObjectPooler里的Start()以保证ObjectPooler的所有实例都在你的池列表中。将Start()的代码修改为如下:pooledObjects = [new](/search?q=new+)
List&GameObject&();
foreach (ObjectPoolItem item in itemsToPool) {
for (int i = 0; i item.amountToP i++) {
GameObject obj = (GameObject)Instantiate(item.objectToPool);
obj.SetActive(false);
pooledObjects.Add(obj);
在这里,通过一个foreach循环来遍历ObjectPoolItem的所有实例,并把合适的对象添加到对象池中。你可能会想知道怎样从对象池里请求一个特定的对象——有时候你需要一枚子弹,有时候你需要多来几颗海王星,诸如此类。将GetPooledObject里的代码调整为如下:for (int i = 0; i pooledObjects.C i++) {
if (!pooledObjects[i].activeInHierarchy && pooledObjects[i].tag == tag) {
return pooledObjects[i];
foreach (ObjectPoolItem item in itemsToPool) {
if (item.objectToPool.tag == tag) {
if (item.shouldExpand) {
GameObject obj = (GameObject)Instantiate(item.objectToPool);
obj.SetActive(false);
pooledObjects.Add(obj);
现在你的GetPooledObject多了一个string参数,这样游戏就可以按照相应的tag标签来进行索引。这个方法会在对象池里检索一个具有此标签的非活动对象,然后将其返回。此外,在找不到相应的对象的情况下,它会根据标签来检索相关的ObjectPoolItem实例并检查是否可以将其扩展。增加Tag标签这里先拿子弹来举例,之后你可以加入其他的对象。在MonoDevelop里打开ShipController脚本。在Shoot()和ActivateScatterShotTurret()里找到如下代码:GameObject bullet = ObjectPooler.SharedInstance.GetPooledObject();
在代码里添加Player Bullet这一参数作为Tag标签。GameObject bullet = ObjectPooler.SharedInstance.GetPooledObject(“Player Bullet”);
回到Unity里点击GameController ,在Inspector打开它。在ItemsToPool 中增加一个新的道具,然后在其里面加入20发玩家的子弹。点击Play来确保别的东西没有被乱动过。:]好!现在你已经学会怎么向对象池里添加新的对象了。将ItemsToPool 的大小改为3,然后新加入两类敌人的舰船。按照如下内容来调整ItemsToPool 的实例参数。Element 1:Object to Pool: EnemyDroneType1Amount To Pool: 6Should Expand: UncheckedElement 2Object to Pool: EnemyDroneType2Amount to Pool: 6Should Expand: Unchecked如处理子弹时一样,你需要改变这两类敌舰instantiate和destroy的方法。敌舰在执行GameController脚本时进行实例化,并在EnemyDroneController这一脚本中销毁。之前已经做过一次了,所以这里我们会快一点。-w-打开GameController脚本。在SpawnEnemyWaves()中找到enemyType1 instantiation code:Instantiate(enemyType1, spawnPosition, spawnRotation);
将其替换为如下代码:GameObject enemy1 = ObjectPooler.SharedInstance.GetPooledObject("Enemy Ship 1");
if (enemy1 != null) {
enemy1.transform.position = spawnP
enemy1.transform.rotation = spawnR
enemy1.SetActive(true);
找到enemyType2 instantiation这段代码:Instantiate(enemyType2, spawnPosition, spawnRotation);
将其替换为:GameObject enemy2 = ObjectPooler.SharedInstance.GetPooledObject("Enemy Ship 2");
if (enemy2 != null) {
enemy2.transform.position = spawnP
enemy2.transform.rotation = spawnR
enemy2.SetActive(true);
最后,打开EnemyDroneController 脚本,目前OnTriggerExit2D()在敌舰离开屏幕之后就会销毁这个敌舰对象,这可不太行。(这太浪费了!)找到此行代码:Destroy(gameObject);
将其替换为如下代码以确保敌人还会回到对象池:gameObject.SetActive(false);
与此类似,在OnTriggerEnter2D()中敌人碰到玩家子弹也会被销毁。找到Destroy():Destroy(gameObject);
替换为如下:gameObject.SetActive(false);
按play按钮,观察所有子弹和敌人的实例在活动与非活动状态之间的切换,以及他们在屏幕上出现的时机。你的可再生船很棒棒哦~何去何从感谢你跑完这段教程。这里是完整工程的。在这个教程里,你了改写一个现有的游戏,通过给其加入对象池的方式,从可以预期的过载中拯救玩家的CPU,并解决了因此而会产生的跳帧和电池过热问题。你在脚本的切换和内容整合上熟练度+5。如果想了解更多教程,可以检阅所有。希望这份教程对你有用!我想知道这份教程是如何帮助你做出一个屌屌的东西,或者将一个现有的app变得屌屌的。可以在评论里附上你的作品的链接,同样欢迎问题与关于改进的讨论!我很期待与你聊聊关于对象池,Unity以及干掉外星人的事情。72收藏分享举报文章被以下专栏收录泰然网游戏玄学翻译社出品。推荐阅读{&debug&:false,&apiRoot&:&&,&paySDK&:&https:\u002F\\u002Fapi\u002Fjs&,&wechatConfigAPI&:&\u002Fapi\u002Fwechat\u002Fjssdkconfig&,&name&:&production&,&instance&:&column&,&tokens&:{&X-XSRF-TOKEN&:null,&X-UDID&:null,&Authorization&:&oauth c3cef7c66aa9e6a1e3160e20&}}{&database&:{&Post&:{&&:{&isPending&:false,&contributes&:[{&sourceColumn&:{&lastUpdated&:,&description&:&泰然网旗下翻译社,专注游戏视频翻译,游戏文章翻译,搬运海外优质视频,欢迎加入!&,&permission&:&COLUMN_PUBLIC&,&memberId&:1188994,&contributePermission&:&COLUMN_PUBLIC&,&translatedCommentPermission&:&all&,&canManage&:true,&intro&:&泰然网游戏玄学翻译社出品。&,&urlToken&:&games&,&id&:8712,&imagePath&:&v2-0ffdc129eedc309c025c.jpg&,&slug&:&games&,&applyReason&:&&,&name&:&游戏玄学社&,&title&:&游戏玄学社&,&url&:&https:\u002F\\u002Fgames&,&commentPermission&:&COLUMN_ALL_CAN_COMMENT&,&canPost&:true,&created&:,&state&:&COLUMN_NORMAL&,&followers&:153,&avatar&:{&id&:&v2-0ffdc129eedc309c025c&,&template&:&https:\u002F\\u002F{id}_{size}.jpg&},&activateAuthorRequested&:false,&following&:false,&imageUrl&:&https:\u002F\\u002Fv2-0ffdc129eedc309c025c_l.jpg&,&articlesCount&:4},&state&:&accepted&,&targetPost&:{&titleImage&:&https:\u002F\\u002Fv2-8bc06c9bae545dac44e17cafdfc39cbc_r.jpg&,&lastUpdated&:,&imagePath&:&v2-8bc06c9bae545dac44e17cafdfc39cbc.jpg&,&permission&:&ARTICLE_PUBLIC&,&topics&:[,1045],&summary&:&本文由游戏玄学翻译社制作发布,首发于\u003Ca href=\&http:\u002F\\u002Farchives\u002F1F\& data-title=\&泰然网\& class=\&\&\u003E泰然网\u003C\u002Fa\u003E! 翻译:太昊,兔角鹿,小木曾;Review: 风雨时;统筹润色:杨雍 想象一下:你正在打飞机!!哦,不,测试你最新的和最棒的一个射击游戏。敌人在以你能掌握的最快速度来回飞行,然后,砰!卡了一帧之后,你就…&,&copyPermission&:&ARTICLE_COPYABLE&,&translatedCommentPermission&:&all&,&likes&:0,&origAuthorId&:0,&publishedTime&:&T14:55:26+08:00&,&sourceUrl&:&&,&urlToken&:,&id&:2013508,&withContent&:false,&slug&:,&bigTitleImage&:false,&title&:&Unity 内存优化 和 内存池使用实践&,&url&:&\u002Fp\u002F&,&commentPermission&:&ARTICLE_ALL_CAN_COMMENT&,&snapshotUrl&:&&,&created&:,&comments&:0,&columnId&:8712,&content&:&&,&parentId&:0,&state&:&ARTICLE_PUBLISHED&,&imageUrl&:&https:\u002F\\u002Fv2-8bc06c9bae545dac44e17cafdfc39cbc_r.jpg&,&author&:{&bio&:&?游戏人 ?《Cocos2d-x 之Lua 核心编程 》作者 ?@泰然网()创始人&,&isFollowing&:false,&hash&:&054facfefc74f42a5b00&,&uid&:76,&isOrg&:false,&slug&:&ivenyang&,&isFollowed&:false,&description&:&在逆袭的邪路上,越滚越远。。。。&,&name&:&杨雍&,&profileUrl&:&https:\u002F\\u002Fpeople\u002Fivenyang&,&avatar&:{&id&:&4f209fef6bcfc345bf66&,&template&:&https:\u002F\\u002F50\u002F{id}_{size}.jpg&},&isOrgWhiteList&:false,&isBanned&:false},&memberId&:1188994,&excerptTitle&:&&,&voteType&:&ARTICLE_VOTE_CLEAR&},&id&:506056}],&title&:&Unity 内存优化 和 内存池使用实践&,&author&:&ivenyang&,&content&:&\u003Cblockquote\u003E本文由游戏玄学翻译社制作发布,首发于\u003Ca href=\&http:\u002F\\u002F?target=http%3A\u002F\\u002Farchives\u002F1F\& class=\& wrap external\& target=\&_blank\& rel=\&nofollow noreferrer\&\u003E泰然网\u003Ci class=\&icon-external\&\u003E\u003C\u002Fi\u003E\u003C\u002Fa\u003E!\u003Cbr\u003E翻译:太昊,兔角鹿,小木曾;Review: \u003Cbr\u003E风雨时;统筹润色:杨雍\u003C\u002Fblockquote\u003E\u003Cbr\u003E\u003Cnoscript\u003E\u003Cimg src=\&https:\u002F\\u002Fv2-49bef758b355f7b046280b_b.png\& data-rawwidth=\&250\& data-rawheight=\&250\& class=\&content_image\& width=\&250\&\u003E\u003C\u002Fnoscript\u003E\u003Cimg src=\&data:image\u002Fsvg+utf8,&svg%20xmlns='http:\u002F\u002Fwww.w3.org\u002FFsvg'%20width='250'%20height='250'&&\u002Fsvg&\& data-rawwidth=\&250\& data-rawheight=\&250\& class=\&content_image lazy\& width=\&250\& data-actualsrc=\&https:\u002F\\u002Fv2-49bef758b355f7b046280b_b.png\&\u003E\u003Cbr\u003E\u003Cblockquote\u003E\u003Cp\u003E想象一下:你正在打飞机!!哦,不,测试你最新的和最棒的一个射击游戏。敌人在以你能掌握的最快速度来回飞行,然后,砰!卡了一帧之后,你就被凶神恶煞的外星人手打成了翔。\u003C\u002Fp\u003E\u003C\u002Fblockquote\u003E\u003Cp\u003E这可是场横扫千军的战斗,不应该由于莫名其妙的内存尖峰左右战斗的结果。你是不是也曾经因为这个问题输掉?来来来,搬个小马扎,听我来扒一扒 \u003Cem\u003E对象池技术\u003C\u002Fem\u003E 吧。\u003C\u002Fp\u003E\u003Cp\u003E在这篇Unity教程中,你将学到:\u003C\u002Fp\u003E\u003Cul\u003E\u003Cli\u003E\u003Cp\u003E所有关于对象池技术的内容\u003C\u002Fp\u003E\u003C\u002Fli\u003E\u003Cli\u003E\u003Cp\u003E如何将一个game object入池\u003C\u002Fp\u003E\u003C\u002Fli\u003E\u003Cli\u003E\u003Cp\u003E如何在运行时按需扩展对象池\u003C\u002Fp\u003E\u003C\u002Fli\u003E\u003Cli\u003E\u003Cp\u003E如何扩展对象池以适应不同的对象\u003C\u002Fp\u003E\u003C\u002Fli\u003E\u003C\u002Ful\u003E\u003Cp\u003E在教程的最后,你会得到一个可以得到新游戏的全部代码。而且,你会懂得如何为现有的游戏改进这个代码。\u003C\u002Fp\u003E\u003Cp\u003E\u003Cem\u003E预备知识:\u003C\u002Fem\u003E你需要熟悉C#基础并且知道如何操作Unity的开发环境。如果你需要帮助,查看这里\u003Ca href=\&http:\u002F\\u002F?target=https%3A\u002F\\u002Funity-tutorials\& class=\& wrap external\& target=\&_blank\& rel=\&nofollow noreferrer\&\u003EUnity教程(暂未翻译)\u003Ci class=\&icon-external\&\u003E\u003C\u002Fi\u003E\u003C\u002Fa\u003E\u003C\u002Fp\u003E\u003Ch2\u003E什么是对象池技术?\u003C\u002Fh2\u003E\u003Cp\u003EInstantiate() 和 Destroy() 是在游戏流程中好用而必备的方法。(通常情况下,单独调用这两个方法只占用CPU相当微小的时间。)\u003C\u002Fp\u003E\u003Cp\u003E然而,对于在游戏流程中生命周期短暂而且每秒大量摧毁的对象群而言,CPU进行内存分配的时间占用十分显著。\u003C\u002Fp\u003E\u003Cnoscript\u003E\u003Cimg src=\&https:\u002F\\u002Fv2-d13e1e76ee53d1f38ba9ad0cc9196f27_b.png\& data-rawwidth=\&650\& data-rawheight=\&480\& class=\&origin_image zh-lightbox-thumb\& width=\&650\& data-original=\&https:\u002F\\u002Fv2-d13e1e76ee53d1f38ba9ad0cc9196f27_r.png\&\u003E\u003C\u002Fnoscript\u003E\u003Cimg src=\&data:image\u002Fsvg+utf8,&svg%20xmlns='http:\u002F\u002Fwww.w3.org\u002FFsvg'%20width='650'%20height='480'&&\u002Fsvg&\& data-rawwidth=\&650\& data-rawheight=\&480\& class=\&origin_image zh-lightbox-thumb lazy\& width=\&650\& data-original=\&https:\u002F\\u002Fv2-d13e1e76ee53d1f38ba9ad0cc9196f27_r.png\& data-actualsrc=\&https:\u002F\\u002Fv2-d13e1e76ee53d1f38ba9ad0cc9196f27_b.png\&\u003E\u003Cbr\u003E\u003Cbr\u003E\u003Cp\u003E子弹就是一个适合入池的GameObject的好例子。\u003C\u002Fp\u003E\u003Cp\u003E并且,Unity使用\u003Cem\u003E垃圾收集(Garbage Collection)\u003C\u002Fem\u003E技术来释放不需要继续使用的内存。不断调用Destroy()会频繁的激活收集,而这会拖慢CPU导致游戏流程的卡顿。\u003C\u002Fp\u003E\u003Cp\u003E这一行为在移动设备和网页等资源受限的环境下中尤为致命。\u003C\u002Fp\u003E\u003Cp\u003E\u003Cem\u003E对象池技术\u003C\u002Fem\u003E将尚未用到的对象放在游戏流程之前——比如在读取界面的时候——进行预先实例化。这样游戏可以从『池子』中重用对象,而不需在游戏流程中不断地创建和销毁。\u003C\u002Fp\u003E\u003Cnoscript\u003E\u003Cimg src=\&https:\u002F\\u002Fv2-d2fe4fb3e3e47df7dd29f3_b.png\& data-rawwidth=\&500\& data-rawheight=\&700\& class=\&origin_image zh-lightbox-thumb\& width=\&500\& data-original=\&https:\u002F\\u002Fv2-d2fe4fb3e3e47df7dd29f3_r.png\&\u003E\u003C\u002Fnoscript\u003E\u003Cimg src=\&data:image\u002Fsvg+utf8,&svg%20xmlns='http:\u002F\u002Fwww.w3.org\u002FFsvg'%20width='500'%20height='700'&&\u002Fsvg&\& data-rawwidth=\&500\& data-rawheight=\&700\& class=\&origin_image zh-lightbox-thumb lazy\& width=\&500\& data-original=\&https:\u002F\\u002Fv2-d2fe4fb3e3e47df7dd29f3_r.png\& data-actualsrc=\&https:\u002F\\u002Fv2-d2fe4fb3e3e47df7dd29f3_b.png\&\u003E\u003Cbr\u003E\u003Cbr\u003E\u003Ch2\u003E开始\u003C\u002Fh2\u003E\u003Cp\u003E如果还没有Unity5或者更新版本,从 \u003Ca href=\&http:\u002F\\u002F?target=https%3A\\u002Fget-unity\u002Fdownload\& class=\& wrap external\& target=\&_blank\& rel=\&nofollow noreferrer\&\u003EUnity官网\u003Ci class=\&icon-external\&\u003E\u003C\u002Fi\u003E\u003C\u002Fa\u003E下载。\u003C\u002Fp\u003E\u003Cp\u003E然后下载 \u003Ca href=\&http:\u002F\\u002F?target=https%3A\u002F\u002Fkoenig-\u002Fuploads\u002F\u002FSuperRetroShooter_Starter.zip\& class=\& wrap external\& target=\&_blank\& rel=\&nofollow noreferrer\&\u003E起始项目\u003Ci class=\&icon-external\&\u003E\u003C\u002Fi\u003E\u003C\u002Fa\u003E,解压并在Unity里打开 SuperRetroShooter_Starter项目——这是一个预先建好的纵向卷轴射击项目。\u003C\u002Fp\u003E\u003Cp\u003E\u003Cem\u003E注意\u003C\u002Fem\u003E:美术资源的版权属于 \u003Ca href=\&http:\u002F\\u002F?target=http%3A\u002F\u002Fopengameart.org\u002F\& class=\& wrap external\& target=\&_blank\& rel=\&nofollow noreferrer\&\u003EOpenGameArt\u003Ci class=\&icon-external\&\u003E\u003C\u002Fi\u003E\u003C\u002Fa\u003E的 Master484, Marcus, Luis Zuno 和 Skorpio。免授权音乐则来自于杰出的\u003Ca href=\&http:\u002F\\u002F?target=http%3A\u002F\\u002F\& class=\& wrap external\& target=\&_blank\& rel=\&nofollow noreferrer\&\u003EBensound\u003Ci class=\&icon-external\&\u003E\u003C\u002Fi\u003E\u003C\u002Fa\u003E\u003C\u002Fp\u003E\u003Cp\u003E随意看看里面的脚本吧,比如\u003Cem\u003EBarrier\u003C\u002Fem\u003E;他们都非常实用,不过这篇教程就不对它们详细阐述了。\u003C\u002Fp\u003E\u003Cp\u003E在浏览这些时,注意在游戏流程中Hierarchy(栏)里都发生了些什么是很有帮助的。因此我建议取消Game标签(\u003Cem\u003EGame Tab\u003C\u002Fem\u003E)工具栏(* toolbar \u003Cem\u003E)里的\u003C\u002Fem\u003EMaximize on Play*选项。\u003C\u002Fp\u003E\u003Cnoscript\u003E\u003Cimg src=\&https:\u002F\\u002Fv2-abd2a607acb_b.png\& data-rawwidth=\&700\& data-rawheight=\&54\& class=\&origin_image zh-lightbox-thumb\& width=\&700\& data-original=\&https:\u002F\\u002Fv2-abd2a607acb_r.png\&\u003E\u003C\u002Fnoscript\u003E\u003Cimg src=\&data:image\u002Fsvg+utf8,&svg%20xmlns='http:\u002F\u002Fwww.w3.org\u002FFsvg'%20width='700'%20height='54'&&\u002Fsvg&\& data-rawwidth=\&700\& data-rawheight=\&54\& class=\&origin_image zh-lightbox-thumb lazy\& width=\&700\& data-original=\&https:\u002F\\u002Fv2-abd2a607acb_r.png\& data-actualsrc=\&https:\u002F\\u002Fv2-abd2a607acb_b.png\&\u003E\u003Cbr\u003E\u003Cbr\u003E\u003Cp\u003E点击\u003Cem\u003Eplay\u003C\u002Fem\u003E按钮看看。 :]\u003C\u002Fp\u003E\u003Cp\u003E注意当发射时,在Hierarchy(栏)里实例化了大量的PlayerBullet(Clone)。当击中敌人或者离开屏幕时,他们会被销毁。\u003C\u002Fp\u003E\u003Cp\u003E更糟的是收集那些掉落的强化道具,他们迅速用子弹的复制体填满了Hierarchy(栏),随后又在下一秒里立刻销毁它们。\u003C\u002Fp\u003E\u003Cnoscript\u003E\u003Cimg src=\&https:\u002F\\u002Fv2-4cc3c31f1e9ca877f8023d_b.jpg\& data-rawwidth=\&700\& data-rawheight=\&705\& class=\&origin_image zh-lightbox-thumb\& width=\&700\& data-original=\&https:\u002F\\u002Fv2-4cc3c31f1e9ca877f8023d_r.jpg\&\u003E\u003C\u002Fnoscript\u003E\u003Cimg src=\&data:image\u002Fsvg+utf8,&svg%20xmlns='http:\u002F\u002Fwww.w3.org\u002FFsvg'%20width='700'%20height='705'&&\u002Fsvg&\& data-rawwidth=\&700\& data-rawheight=\&705\& class=\&origin_image zh-lightbox-thumb lazy\& width=\&700\& data-original=\&https:\u002F\\u002Fv2-4cc3c31f1e9ca877f8023d_r.jpg\& data-actualsrc=\&https:\u002F\\u002Fv2-4cc3c31f1e9ca877f8023d_b.jpg\&\u003E\u003Cbr\u003E\u003Cp\u003E在现在这个状态下,Super Retro Shooter这个游戏就像一个『内存的搅屎棍』,而你要做个英雄,让这个游戏全速工作并且更加严谨地使用资源。\u003C\u002Fp\u003E\u003Ch3\u003E是时候试试手了\u003C\u002Fh3\u003E\u003Cp\u003E在Hierarchy里点击\u003Cem\u003EGame Controller\u003C\u002Fem\u003E。这个对象(GameObject)会在场景里持续存在,适合在它上面添加对象池脚本。\u003C\u002Fp\u003E\u003Cp\u003E在Inspector中,点击\u003Cem\u003EAdd Component\u003C\u002Fem\u003E按钮,选择\u003Cem\u003ENew C# Script\u003C\u002Fem\u003E。起名为\u003Cem\u003EObjectPooler\u003C\u002Fem\u003E。\u003C\u002Fp\u003E\u003Cp\u003E\u003Cem\u003E双击\u003C\u002Fem\u003E新的脚本在MonoDevelop中打开它,并在 \u003Cem\u003E类\u003C\u002Fem\u003E 里添加以下代码:\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003Epublic static ObjectPooler SharedI\nvoid Awake() {\n
SharedInstance =\n}\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E有一些脚本会需要在游戏流程中访问对象池,因此用public static instance令各个脚本可以访问它而不需要从GameObject上获取Component的操作。\u003C\u002Fp\u003E\u003Cp\u003E在脚本顶端,增加以下using声明:\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003Eusing System.Collections.G\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E你将会使用(using)一个泛型(generic)列表来存储入池了的对象。这一声明允许你访问泛型数据结构,这样就可以在脚本里用到List类。\u003C\u002Fp\u003E\u003Cp\u003E\u003Cem\u003E注意\u003C\u002Fem\u003E:普通(泛型的原文Generic也有普通的意思)?\u003Cem\u003E没人\u003C\u002Fem\u003E想要普通!每个人都希望独特!\u003C\u002Fp\u003E\u003Cp\u003E在程序语言,比如C#里,泛型(普通)能让你的代码可以被很多不同的类型所使用,同时保持类型安全(type safety)。\u003C\u002Fp\u003E\u003Cp\u003E一个典型的应用就是在使用集合(collection)的时候使用泛型。这个方法限定了在数组(array)中存放对象的类型,以防比如在猫数组里放了个狗,尽管这会显得很神奇。:]\u003C\u002Fp\u003E\u003Cp\u003E说到列表(list),添加对象池列表以及两个新的公共变量:\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003Epublic GameObject objectToP\npublic int amountToP\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E这些变量的命名是很自明的(self-explanatory)。\u003C\u002Fp\u003E\u003Cp\u003E通过Unity的Inspector,你将能够指定GameObject入池,并且设置预实例化的数量。一会儿会做这个。\u003C\u002Fp\u003E\u003Cp\u003E然后,把这些代码加入\u003Cem\u003EStart()\u003C\u002Fem\u003E:\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003EpooledObjects = new List&GameObject&();\nfor (int i = 0; i
amountToP i++) {\nGameObject obj = (GameObject)Instantiate(objectToPool);\n
obj.SetActive(false); \n
pooledObjects.Add(obj);\n}\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003Efor循环会实例化numberToPool所指定的数量的objectToPoolGameObject。然后这些GameObject会被设置为inactive状态,之后再加入到pooledObjects列表中。\u003C\u002Fp\u003E\u003Cp\u003E回到Unity然后在Inspector中添加\u003Cem\u003EPlayer Bullet Prefab\u003C\u002Fem\u003E到\u003Cem\u003EobjectToPool\u003C\u002Fem\u003E变量上。在\u003Cem\u003EnumberToPool\u003C\u002Fem\u003E字段上填入\u003Cem\u003E20\u003C\u002Fem\u003E。\u003C\u002Fp\u003E\u003Cnoscript\u003E\u003Cimg src=\&https:\u002F\\u002Fv2-368af3e66d04fa714a6df9fc9b6d13e9_b.jpg\& data-rawwidth=\&500\& data-rawheight=\&785\& class=\&origin_image zh-lightbox-thumb\& width=\&500\& data-original=\&https:\u002F\\u002Fv2-368af3e66d04fa714a6df9fc9b6d13e9_r.jpg\&\u003E\u003C\u002Fnoscript\u003E\u003Cimg src=\&data:image\u002Fsvg+utf8,&svg%20xmlns='http:\u002F\u002Fwww.w3.org\u002FFsvg'%20width='500'%20height='785'&&\u002Fsvg&\& data-rawwidth=\&500\& data-rawheight=\&785\& class=\&origin_image zh-lightbox-thumb lazy\& width=\&500\& data-original=\&https:\u002F\\u002Fv2-368af3e66d04fa714a6df9fc9b6d13e9_r.jpg\& data-actualsrc=\&https:\u002F\\u002Fv2-368af3e66d04fa714a6df9fc9b6d13e9_b.jpg\&\u003E\u003Cbr\u003E\u003Cp\u003E再次运行游戏。现在场景(视图)里应该有20个预实例化好但无家可归的子弹了。\u003C\u002Fp\u003E\u003Cp\u003E干得好!你现在拥有一个对象池了 :]\u003C\u002Fp\u003E\u003Ch3\u003E深入对象池\u003C\u002Fh3\u003E\u003Cp\u003E回到\u003Cem\u003EObjectPooler\u003C\u002Fem\u003E脚本然后添加以下代码:\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003Epublic GameObject GetPooledObject() {\n\u002F\u002F1\nfor (int i = 0; i
pooledObjects.C i++) {\n\u002F\u002F2\n
if (!pooledObjects[i].activeInHierarchy) {\n
return pooledObjects[i];\n
\n}\n\u002F\u002F3 \\n}\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E首先要注意的是这个方法现在的返回值类型是GameObject而非void。这是指别的脚本可以向GetPooledObject请求一个池中的对象,而他会返回一个GameObject作为回应。其他会发生的事情如下:\u003C\u002Fp\u003E\u003Col\u003E\u003Cli\u003E\u003Cp\u003E这个方法使用for循环遍历你的pooledObjects列表。\u003C\u002Fp\u003E\u003C\u002Fli\u003E\u003Cli\u003E\u003Cp\u003E检查其中一个对象是否并非激活(active)状态。如果是激活的,循环到下一个。如果是非激活的,退出方法并把这个对象提交给对GetPooledObject的调用者。\u003C\u002Fp\u003E\u003C\u002Fli\u003E\u003Cli\u003E\u003Cp\u003E如果目前没有非激活状态的对象,退出方法并且不返回任何东西。\u003C\u002Fp\u003E\u003C\u002Fli\u003E\u003C\u002Fol\u003E\u003Cp\u003E现在,你可以向池子请求对象了,这需要替换掉原有的子弹的实例化和销毁代码,以对象池替代。\u003C\u002Fp\u003E\u003Cp\u003E玩家的子弹会在ShipController脚本里的两个方法中(被)实例化。\u003C\u002Fp\u003E\u003Cul\u003E\u003Cli\u003E\u003Cp\u003EShoot()\u003C\u002Fp\u003E\u003C\u002Fli\u003E\u003Cli\u003E\u003Cp\u003EActivateScatterShotTurret()\u003C\u002Fp\u003E\u003C\u002Fli\u003E\u003C\u002Ful\u003E\u003Cp\u003E在MonoDevelop里打开\u003Cem\u003EShipController\u003C\u002Fem\u003E脚本(并找到下列代码):\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003EInstantiate(playerBullet, turret.transform.position, turret.transform.rotation);\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E把\u003Cem\u003E两处\u003C\u002Fem\u003E实例都用以下代码替换:\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003EGameObject bullet = ObjectPooler.SharedInstance.GetPooledObject(); \n
if (bullet != null) {\n
bullet.transform.position = turret.transform.\n
bullet.transform.rotation = turret.transform.\n
bullet.SetActive(true);\n
}\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E\u003Cem\u003E注意\u003C\u002Fem\u003E: 在继续之前,确保在Shoot()和ActivateScatterShotTurret()方法中也做好了这样的替换。\u003C\u002Fp\u003E\u003Cp\u003E在之前,这些方法遍历玩家飞船上的激活着的子弹列表((基于加成道具)),然后在枪口位置与角度上实例化子弹。\u003C\u002Fp\u003E\u003Cp\u003E而现在改成了询问ObjectPooler脚本获取一个池中的对象。如果拿到了,将它设置到枪口的位置与角度,然后设为active状态来向敌人倾泻下你的弹火吧:]\u003C\u002Fp\u003E\u003Ch3\u003E归还对象池\u003C\u002Fh3\u003E\u003Cp\u003E当你不再需要使用子弹的时候,将它们归还到池中,而不是将对象销毁。\u003C\u002Fp\u003E\u003Cp\u003E有两个方法可以销毁不再需要的玩家子弹:\u003C\u002Fp\u003E\u003Cul\u003E\u003Cli\u003E\u003Cp\u003Enet当子弹移出屏幕时,调用DestroyByBoundary中的OnTriggerExit2D() 方法移除它们。\u003C\u002Fp\u003E\u003C\u002Fli\u003E\u003Cli\u003E\u003Cp\u003EOnTriggerEnter2D() in the EnemyDroneController script removes the bullet when it collides and destroys an enemy.\u003C\u002Fp\u003E\u003C\u002Fli\u003E\u003C\u002Ful\u003E\u003Cp\u003E*当子弹碰撞并摧毁敌人时,调用EnemyDroneController中的OnTriggerEnter2D()方法移除它们。\u003C\u002Fp\u003E\u003Cp\u003E在MonoDevelop中打开\u003Cem\u003EDestroyByBoundary\u003C\u002Fem\u003E,并将OnTriggerExit2D方法替换成如下代码:\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003Eif (other.gameObject.tag == \&Boundary\&) {\n
if (gameObject.tag == \&Player Bullet\&) {\n
gameObject.SetActive(false);\n
} else {\n
Destroy(gameObject);\n
}\n}\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E那些\u003Cem\u003E离开屏幕范围时需要被移除的对象\u003C\u002Fem\u003E可以使用这一段通用脚本加以控制。这段代码可以判断这些触发了碰撞的对象是否拥有Player Bullet标签——如果有,则将其设置为非活动状态,而不是直接销毁。\u003C\u002Fp\u003E\u003Cp\u003E类似地,打开\u003Cem\u003EEnemyDroneController\u003C\u002Fem\u003E并找到OnTriggerEnter2D()方法,将Destroy(other.gameObject);替换成这一行代码:\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003Eother.gameObject.SetActive(false);\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E等等,刀下留人!\u003C\u002Fp\u003E\u003Cp\u003E没错,我几乎都能\u003Cem\u003E看见\u003C\u002Fem\u003E你的鼠标指在了运行按钮上。在进行了这么一大段编程之后,你肯定对于测试对象池跃跃欲试了吧。先别忙!我们还要再改一个脚本——别担心,只是一点小改动。^_^\u003C\u002Fp\u003E\u003Cp\u003E在MonoDevelop中打开\u003Cem\u003EBasicMovement\u003C\u002Fem\u003E脚本,并将Start方法重命名为OnEnable。\u003C\u002Fp\u003E\u003Cnoscript\u003E\u003Cimg src=\&https:\u002F\\u002Fv2-bb068be5de80ded3b2a1fefcdbaab472_b.png\& data-rawwidth=\&700\& data-rawheight=\&969\& class=\&origin_image zh-lightbox-thumb\& width=\&700\& data-original=\&https:\u002F\\u002Fv2-bb068be5de80ded3b2a1fefcdbaab472_r.png\&\u003E\u003C\u002Fnoscript\u003E\u003Cimg src=\&data:image\u002Fsvg+utf8,&svg%20xmlns='http:\u002F\u002Fwww.w3.org\u002FFsvg'%20width='700'%20height='969'&&\u002Fsvg&\& data-rawwidth=\&700\& data-rawheight=\&969\& class=\&origin_image zh-lightbox-thumb lazy\& width=\&700\& data-original=\&https:\u002F\\u002Fv2-bb068be5de80ded3b2a1fefcdbaab472_r.png\& data-actualsrc=\&https:\u002F\\u002Fv2-bb068be5de80ded3b2a1fefcdbaab472_b.png\&\u003E\u003Cbr\u003E\u003Cp\u003E这里有个坑:使用对象池模式时,注意池中对象的生命周期会和之前有所不同。\u003C\u002Fp\u003E\u003Cp\u003E好了,你\u003Cem\u003E可以\u003C\u002Fem\u003E点击运行了。^_^\u003C\u002Fp\u003E\u003Cp\u003E当你射击时,在Hierarchy中的玩家子弹自动从未激活状态变为激活状态。它们会在移出屏幕、或击中敌机的时候,以优雅代码应有的方式变成回未激活的状态。\u003C\u002Fp\u003E\u003Cp\u003E干的漂亮!\u003C\u002Fp\u003E\u003Cnoscript\u003E\u003Cimg src=\&https:\u002F\\u002Fv2-a984dec70fdcdae80dbfa_b.jpg\& data-rawwidth=\&700\& data-rawheight=\&739\& class=\&origin_image zh-lightbox-thumb\& width=\&700\& data-original=\&https:\u002F\\u002Fv2-a984dec70fdcdae80dbfa_r.jpg\&\u003E\u003C\u002Fnoscript\u003E\u003Cimg src=\&data:image\u002Fsvg+utf8,&svg%20xmlns='http:\u002F\u002Fwww.w3.org\u002FFsvg'%20width='700'%20height='739'&&\u002Fsvg&\& data-rawwidth=\&700\& data-rawheight=\&739\& class=\&origin_image zh-lightbox-thumb lazy\& width=\&700\& data-original=\&https:\u002F\\u002Fv2-a984dec70fdcdae80dbfa_r.jpg\& data-actualsrc=\&https:\u002F\\u002Fv2-a984dec70fdcdae80dbfa_b.jpg\&\u003E\u003Cbr\u003E\u003Cp\u003E但假如你收集了所有的加成道具,会怎样?\u003C\u002Fp\u003E\u003Cp\u003E“啊呀,子弹不够用了”?\u003C\u002Fp\u003E\u003Cnoscript\u003E\u003Cimg src=\&https:\u002F\\u002Fv2-fc6eb686d0efa7fe59bc80_b.jpg\& data-rawwidth=\&433\& data-rawheight=\&320\& class=\&origin_image zh-lightbox-thumb\& width=\&433\& data-original=\&https:\u002F\\u002Fv2-fc6eb686d0efa7fe59bc80_r.jpg\&\u003E\u003C\u002Fnoscript\u003E\u003Cimg src=\&data:image\u002Fsvg+utf8,&svg%20xmlns='http:\u002F\u002Fwww.w3.org\u002FFsvg'%20width='433'%20height='320'&&\u002Fsvg&\& data-rawwidth=\&433\& data-rawheight=\&320\& class=\&origin_image zh-lightbox-thumb lazy\& width=\&433\& data-original=\&https:\u002F\\u002Fv2-fc6eb686d0efa7fe59bc80_r.jpg\& data-actualsrc=\&https:\u002F\\u002Fv2-fc6eb686d0efa7fe59bc80_b.jpg\&\u003E\u003Cbr\u003E\u003Cp\u003E至高无上的游戏设计者当然可以控制一切!比如可以控制玩家的火力,玩家就会更加专注于射击目标,也可以反过来鼓励玩家随时随地射个痛快。\u003C\u002Fp\u003E\u003Cp\u003E若要实现这样的控制,只要修改最初的池中设定过的子弹数量就可以了。\u003C\u002Fp\u003E\u003Cp\u003E相反的,你也可以走另一条路线,给池中设置一大堆的子弹,直接满足收集了全部加成道具的场景。但这会产生一个问题:若你在百分之九十的情况下只需要50发弹药,为什么要为这难得一见的终极力量而给池中设计100一百发的容量呢?\u003C\u002Fp\u003E\u003Cp\u003E毕竟这样做,内存中就会有50发子弹几乎派不上用场了。\u003C\u002Fp\u003E\u003Cnoscript\u003E\u003Cimg src=\&https:\u002F\\u002Fv2-1e165cafd5a6c3a714f96f3b9f65ea38_b.png\& data-rawwidth=\&432\& data-rawheight=\&320\& class=\&origin_image zh-lightbox-thumb\& width=\&432\& data-original=\&https:\u002F\\u002Fv2-1e165cafd5a6c3a714f96f3b9f65ea38_r.png\&\u003E\u003C\u002Fnoscript\u003E\u003Cimg src=\&data:image\u002Fsvg+utf8,&svg%20xmlns='http:\u002F\u002Fwww.w3.org\u002FFsvg'%20width='432'%20height='320'&&\u002Fsvg&\& data-rawwidth=\&432\& data-rawheight=\&320\& class=\&origin_image zh-lightbox-thumb lazy\& width=\&432\& data-original=\&https:\u002F\\u002Fv2-1e165cafd5a6c3a714f96f3b9f65ea38_r.png\& data-actualsrc=\&https:\u002F\\u002Fv2-1e165cafd5a6c3a714f96f3b9f65ea38_b.png\&\u003E\u003Cbr\u003E\u003Ch3\u003E超级扩展对象池\u003C\u002Fh3\u003E\u003Cp\u003E现在你将学习如何修改对象池,以在运行时按需增加池中的对象数量。\u003C\u002Fp\u003E\u003Cp\u003E在MonoDevelop中打开\u003Cem\u003EObjectPooler\u003C\u002Fem\u003E脚本,并将以下代码加入到公有变量中:\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003Epublic bool shouldExpand =\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E这个代码会在Inspector中创建一个复选框,用来决定是否可以增加池中对象的数量。\u003C\u002Fp\u003E\u003Cp\u003E在GetPooledObject()中,将修改为如下代码:\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003Eif (shouldExpand) {\n
GameObject obj = (GameObject)Instantiate(objectToPool);\n
obj.SetActive(false);\n
pooledObjects.Add(obj);\\n} else {\\n}\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E当向池中请求玩家子弹时,若找不到未激活的子弹可以使用,这段代码就会设法扩展对象池,而不是简单的退出。如果这样做,你将实例化一个新子弹,设置其为未激活状态,将其加入到池中,并作为返回值交给请求者。\u003C\u002Fp\u003E\u003Cp\u003E在Unity里点击\u003Cem\u003E运行\u003C\u002Fem\u003E,然后试试看吧。搞点儿加成道具,射个痛快!你的20发子弹的对象池是可以根据需要自行扩展的!\u003C\u002Fp\u003E\u003Cnoscript\u003E\u003Cimg src=\&https:\u002F\\u002Fv2-b666fa7e69fd66d47f4b59ec_b.jpg\& data-rawwidth=\&700\& data-rawheight=\&739\& class=\&origin_image zh-lightbox-thumb\& width=\&700\& data-original=\&https:\u002F\\u002Fv2-b666fa7e69fd66d47f4b59ec_r.jpg\&\u003E\u003C\u002Fnoscript\u003E\u003Cimg src=\&data:image\u002Fsvg+utf8,&svg%20xmlns='http:\u002F\u002Fwww.w3.org\u002FFsvg'%20width='700'%20height='739'&&\u002Fsvg&\& data-rawwidth=\&700\& data-rawheight=\&739\& class=\&origin_image zh-lightbox-thumb lazy\& width=\&700\& data-original=\&https:\u002F\\u002Fv2-b666fa7e69fd66d47f4b59ec_r.jpg\& data-actualsrc=\&https:\u002F\\u002Fv2-b666fa7e69fd66d47f4b59ec_b.jpg\&\u003E\u003Cbr\u003E\u003Cnoscript\u003E\u003Cimg src=\&https:\u002F\\u002Fv2-dedbc0223c2_b.png\& data-rawwidth=\&650\& data-rawheight=\&481\& class=\&origin_image zh-lightbox-thumb\& width=\&650\& data-original=\&https:\u002F\\u002Fv2-dedbc0223c2_r.png\&\u003E\u003C\u002Fnoscript\u003E\u003Cimg src=\&data:image\u002Fsvg+utf8,&svg%20xmlns='http:\u002F\u002Fwww.w3.org\u002FFsvg'%20width='650'%20height='481'&&\u002Fsvg&\& data-rawwidth=\&650\& data-rawheight=\&481\& class=\&origin_image zh-lightbox-thumb lazy\& width=\&650\& data-original=\&https:\u002F\\u002Fv2-dedbc0223c2_r.png\& data-actualsrc=\&https:\u002F\\u002Fv2-dedbc0223c2_b.png\&\u003E\u003Cbr\u003E\u003Ch3\u003E对象池群\u003C\u002Fh3\u003E\u003Cp\u003EInvariably, lots of bullets mean lots of enemies, lots of explosions, lots of enemy bullets and so on.\u003C\u002Fp\u003E\u003Cp\u003E通常情况下,大量的子弹就意味着大量的敌人,不停的爆炸效果,敌人们大量发射的子弹,以及其他方方面面开销。\u003C\u002Fp\u003E\u003Cp\u003E要为这场疯狂屠戮做准备,你需要扩展你的对象池以便于处理复杂的对象类型。接下来的一步,将实现在Inspector中的相同位置调节 不同类型自带的参数。\u003C\u002Fp\u003E\u003Cp\u003E在\u003Cem\u003EObjectPooler\u003C\u002Fem\u003E类的\u003Cem\u003E前面\u003C\u002Fem\u003E加入以下代码:\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E[System.Serializable]\npublic class ObjectPoolItem {\n}\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E[System.Serializable]允许你在Inspector里实例化这个类的对象。\u003C\u002Fp\u003E\u003Cp\u003E接下来将objectToPool,amountToPool 和 shouldExpand这三个变量移动到新的ObjectPoolItem类里。注意:在移动过程中会遇到报错,不过我们稍后会搞定它。\u003C\u002Fp\u003E\u003Cp\u003E更新之后的ObjectPoolItem类应该是这样的:\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E[System.Serializable]\npublic class ObjectPoolItem {\npublic int amountToP\npublic GameObject objectToP\npublic bool shouldE\n}\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E现在一些ObjectPoolItem的实例就能修改自己成员数据与行为了。\u003C\u002Fp\u003E\u003Cp\u003E当你添加了上面的共有变量(public variables),你需要确保从ObjectPooler中\u003Cem\u003E删除\u003C\u002Fem\u003E它们。将下列代码加入ObjectPooler中:\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003Epublic List&ObjectPoolItem& itemsToP\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E新的变量列表可以让你控制ObjectPooler的实例\u003C\u002Fp\u003E\u003Cp\u003E接下来你需要调整ObjectPooler里的Start()以保证ObjectPooler的所有实例都在你的池列表中。将Start()的代码修改为如下:\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003EpooledObjects = [new](http:\u002F\\u002Fsearch?q=new+)
List&GameObject&();\nforeach (ObjectPoolItem item in itemsToPool) {\nfor (int i = 0; i item.amountToP i++) {\nGameObject obj = (GameObject)Instantiate(item.objectToPool);\nobj.SetActive(false);\npooledObjects.Add(obj);\n}\n}\n}\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E在这里,通过一个foreach循环来遍历\u003Cem\u003EObjectPoolItem\u003C\u002Fem\u003E的所有实例,并把合适的对象添加到对象池中。\u003C\u002Fp\u003E\u003Cp\u003E你可能会想知道怎样从对象池里请求一个特定的对象——有时候你需要一枚子弹,有时候你需要多来几颗海王星,诸如此类。\u003C\u002Fp\u003E\u003Cp\u003E将GetPooledObject里的代码调整为如下:\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003Efor (int i = 0; i pooledObjects.C i++) {\nif (!pooledObjects[i].activeInHierarchy && pooledObjects[i].tag == tag) {\nreturn pooledObjects[i];\n}\n}\nforeach (ObjectPoolItem item in itemsToPool) {\nif (item.objectToPool.tag == tag) {\nif (item.shouldExpand) {\nGameObject obj = (GameObject)Instantiate(item.objectToPool);\nobj.SetActive(false);\npooledObjects.Add(obj);\\n}\n}\n}\\n}\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E现在你的GetPooledObject多了一个string参数,这样游戏就可以按照相应的tag标签来进行索引。这个方法会在对象池里检索一个具有此标签的非活动对象,然后将其返回。\u003C\u002Fp\u003E\u003Cp\u003E此外,在找不到相应的对象的情况下,它会根据标签来检索相关的ObjectPoolItem实例并检查是否可以将其扩展。\u003C\u002Fp\u003E\u003Ch3\u003E增加Tag标签\u003C\u002Fh3\u003E\u003Cp\u003E这里先拿子弹来举例,之后你可以加入其他的对象。\u003C\u002Fp\u003E\u003Cp\u003E在MonoDevelop里打开\u003Cem\u003EShipController\u003C\u002Fem\u003E脚本。在Shoot()和ActivateScatterShotTurret()里找到如下代码:\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003EGameObject bullet = ObjectPooler.SharedInstance.GetPooledObject();\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E在代码里添加Player Bullet这一参数作为Tag标签。\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003EGameObject bullet = ObjectPooler.SharedInstance.GetPooledObject(“Player Bullet”);\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E回到Unity里点击\u003Cem\u003EGameController\u003C\u002Fem\u003E ,在Inspector打开它。\u003C\u002Fp\u003E\u003Cp\u003E在\u003Cem\u003EItemsToPool\u003C\u002Fem\u003E 中增加一个新的道具,然后在其里面加入20发玩家的子弹。\u003C\u002Fp\u003E\u003Cp\u003E点击\u003Cem\u003EPlay\u003C\u002Fem\u003E来确保别的东西没有被乱动过。:]\u003C\u002Fp\u003E\u003Cp\u003E好!\u003Cem\u003E现在\u003C\u002Fem\u003E你已经学会怎么向对象池里添加新的对象了。\u003C\u002Fp\u003E\u003Cp\u003E将\u003Cem\u003EItemsToPool\u003C\u002Fem\u003E 的大小改为\u003Cem\u003E3\u003C\u002Fem\u003E,然后新加入两类敌人的舰船。按照如下内容来调整\u003Cem\u003EItemsToPool\u003C\u002Fem\u003E 的实例参数。\u003C\u002Fp\u003E\u003Cp\u003EElement 1:\u003C\u002Fp\u003E\u003Cp\u003E\u003Cem\u003EObject to Pool: EnemyDroneType1\u003Cbr\u003EAmount To Pool: 6\u003Cbr\u003EShould Expand: Unchecked\u003C\u002Fem\u003E\u003C\u002Fp\u003E\u003Cp\u003EElement 2\u003C\u002Fp\u003E\u003Cp\u003E\u003Cem\u003EObject to Pool: EnemyDroneType2\u003Cbr\u003EAmount to Pool: 6\u003Cbr\u003EShould Expand: Unchecked\u003C\u002Fem\u003E\u003C\u002Fp\u003E\u003Cp\u003E如处理子弹时一样,你需要改变这两类敌舰instantiate和destroy的方法。\u003C\u002Fp\u003E\u003Cp\u003E敌舰在执行GameController脚本时进行实例化,并在EnemyDroneController这一脚本中销毁。\u003C\u002Fp\u003E\u003Cp\u003E之前已经做过一次了,所以这里我们会快一点。-w-\u003C\u002Fp\u003E\u003Cp\u003E打开\u003Cem\u003EGameController\u003C\u002Fem\u003E脚本。在\u003Cem\u003ESpawnEnemyWaves()\u003C\u002Fem\u003E中找到\u003Cem\u003EenemyType1 instantiation code\u003C\u002Fem\u003E:\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003EInstantiate(enemyType1, spawnPosition, spawnRotation);\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E将其替换为如下代码:\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003EGameObject enemy1 = ObjectPooler.SharedInstance.GetPooledObject(\&Enemy Ship 1\&); \nif (enemy1 != null) {\nenemy1.transform.position = spawnP\nenemy1.transform.rotation = spawnR\nenemy1.SetActive(true);\n}\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E找到\u003Cem\u003EenemyType2 instantiation\u003C\u002Fem\u003E这段代码:\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003EInstantiate(enemyType2, spawnPosition, spawnRotation);\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E将其替换为:\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003EGameObject enemy2 = ObjectPooler.SharedInstance.GetPooledObject(\&Enemy Ship 2\&); \nif (enemy2 != null) {\nenemy2.transform.position = spawnP\nenemy2.transform.rotation = spawnR\nenemy2.SetActive(true);\n}\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E最后,打开\u003Cem\u003EEnemyDroneController\u003C\u002Fem\u003E 脚本,目前OnTriggerExit2D()在敌舰离开屏幕之后就会销毁这个敌舰对象,这可不太行。(这太浪费了!)\u003C\u002Fp\u003E\u003Cp\u003E找到此行代码:\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003EDestroy(gameObject);\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E将其替换为如下代码以确保敌人还会回到对象池:\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003EgameObject.SetActive(false);\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E与此类似,在OnTriggerEnter2D()中敌人碰到玩家子弹也会被销毁。找到\u003Cem\u003EDestroy()\u003C\u002Fem\u003E:\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003EDestroy(gameObject);\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E替换为如下:\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003EgameObject.SetActive(false);\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E按\u003Cem\u003Eplay\u003C\u002Fem\u003E按钮,观察所有子弹和敌人的实例在活动与非活动状态之间的切换,以及他们在屏幕上出现的时机。\u003C\u002Fp\u003E\u003Cnoscript\u003E\u003Cimg src=\&https:\u002F\\u002Fv2-1a377d1b3c1fe05b36d3_b.jpg\& data-rawwidth=\&700\& data-rawheight=\&747\& class=\&origin_image zh-lightbox-thumb\& width=\&700\& data-original=\&https:\u002F\\u002Fv2-1a377d1b3c1fe05b36d3_r.jpg\&\u003E\u003C\u002Fnoscript\u003E\u003Cimg src=\&data:image\u002Fsvg+utf8,&svg%20xmlns='http:\u002F\u002Fwww.w3.org\u002FFsvg'%20width='700'%20height='747'&&\u002Fsvg&\& data-rawwidth=\&700\& data-rawheight=\&747\& class=\&origin_image zh-lightbox-thumb lazy\& width=\&700\& data-original=\&https:\u002F\\u002Fv2-1a377d1b3c1fe05b36d3_r.jpg\& data-actualsrc=\&https:\u002F\\u002Fv2-1a377d1b3c1fe05b36d3_b.jpg\&\u003E\u003Cbr\u003E\u003Cp\u003E你的可再生船很棒棒哦~\u003C\u002Fp\u003E\u003Cnoscript\u003E\u003Cimg src=\&https:\u002F\\u002Fv2-6ae7b26ebfc210e72ae3653e_b.png\& data-rawwidth=\&650\& data-rawheight=\&480\& class=\&origin_image zh-lightbox-thumb\& width=\&650\& data-original=\&https:\u002F\\u002Fv2-6ae7b26ebfc210e72ae3653e_r.png\&\u003E\u003C\u002Fnoscript\u003E\u003Cimg src=\&data:image\u002Fsvg+utf8,&svg%20xmlns='http:\u002F\u002Fwww.w3.org\u002FFsvg'%20width='650'%20height='480'&&\u002Fsvg&\& data-rawwidth=\&650\& data-rawheight=\&480\& class=\&origin_image zh-lightbox-thumb lazy\& width=\&650\& data-original=\&https:\u002F\\u002Fv2-6ae7b26ebfc210e72ae3653e_r.png\& data-actualsrc=\&https:\u002F\\u002Fv2-6ae7b26ebfc210e72ae3653e_b.png\&\u003E\u003Cbr\u003E\u003Ch3\u003E何去何从\u003C\u002Fh3\u003E\u003Cp\u003E感谢你跑完这段教程。这里是完整工程的\u003Ca href=\&http:\u002F\\u002F?target=https%3A\u002F\u002Fkoenig-\u002Fuploads\u002F\u002FSuperRetroShooter_Complete.zip\& class=\& wrap external\& target=\&_blank\& rel=\&nofollow noreferrer\&\u003E链接\u003Ci class=\&icon-external\&\u003E\u003C\u002Fi\u003E\u003C\u002Fa\u003E。\u003C\u002Fp\u003E\u003Cp\u003E在这个教程里,你了改写一个现有的游戏,通过给其加入对象池的方式,从可以预期的过载中拯救玩家的CPU,并解决了因此而会产生的跳帧和电池过热问题。\u003C\u002Fp\u003E\u003Cp\u003E你在脚本的切换和内容整合上熟练度+5。\u003C\u002Fp\u003E\u003Cp\u003E如果想了解更多教程,可以检阅所有\u003Ca href=\&http:\u002F\\u002F?target=http%3A\u002F\\u002Ftutorials\& class=\& wrap external\& target=\&_blank\& rel=\&nofollow noreferrer\&\u003E教程\u003Ci class=\&icon-external\&\u003E\u003C\u002Fi\u003E\u003C\u002Fa\u003E。\u003C\u002Fp\u003E\u003Cp\u003E希望这份教程对你有用!我想知道这份教程是如何帮助你做出一个屌屌的东西,或者将一个现有的app变得屌屌的。\u003C\u002Fp\u003E\u003Cbr\u003E\u003Cp\u003E可以在评论里附上你的作品的链接,同样欢迎问题与关于改进的讨论!我很期待与你聊聊关于对象池,Unity以及干掉外星人的事情。\u003C\u002Fp\u003E&,&updated&:new Date(&T06:55:26.000Z&),&canComment&:false,&commentPermission&:&anyone&,&commentCount&:5,&collapsedCount&:0,&likeCount&:72,&state&:&published&,&isLiked&:false,&slug&:&&,&isTitleImageFullScreen&:false,&rating&:&none&,&titleImage&:&https:\u002F\\u002Fv2-8bc06c9bae545dac44e17cafdfc39cbc_r.jpg&,&links&:{&comments&:&\u002Fapi\u002Fposts\u002F2Fcomments&},&reviewers&:[],&topics&:[{&url&:&https:\u002F\\u002Ftopic\u002F&,&id&:&&,&name&:&Unity(游戏引擎)&},{&url&:&https:\u002F\\u002Ftopic\u002F&,&id&:&&,&name&:&内存优化&},{&url&:&https:\u002F\\u002Ftopic\u002F&,&id&:&&,&name&:&游戏开发&}],&adminClosedComment&:false,&titleImageSize&:{&width&:450,&height&:216},&href&:&\u002Fapi\u002Fposts\u002F&,&excerptTitle&:&&,&column&:{&slug&:&games&,&name&:&游戏玄学社&},&tipjarState&:&inactivated&,&annotationAction&:[],&sourceUrl&:&&,&pageCommentsCount&:5,&hasPublishingDraft&:false,&snapshotUrl&:&&,&publishedTime&:&T14:55:26+08:00&,&url&:&\u002Fp\u002F&,&lastestLikers&:[{&bio&:&程序员&,&isFollowing&:false,&hash&:&fcec97cdca711&,&uid&:130600,&isOrg&:false,&slug&:&cys-71-50&,&isFollowed&:false,&description&:&&,&name&:&cys&,&profileUrl&:&https:\u002F\\u002Fpeople\u002Fcys-71-50&,&avatar&:{&id&:&da8e974dc&,&template&:&https:\u002F\\u002F{id}_{size}.jpg&},&isOrgWhiteList&:false,&isBanned&:false},{&bio&:null,&isFollowing&:false,&hash&:&b9887adea7e7d&,&uid&:84,&isOrg&:false,&slug&:&liu-yang-30-32&,&isFollowed&:false,&description&:&&,&name&:&大写的银&,&profileUrl&:&https:\u002F\\u002Fpeople\u002Fliu-yang-30-32&,&avatar&:{&id&:&da8e974dc&,&template&:&https:\u002F\\u002F{id}_{size}.jpg&},&isOrgWhiteList&:false,&isBanned&:false},{&bio&:&从人民中来,为人民服务。&,&isFollowing&:false,&hash&:&cecbbcb2cfc80&,&uid&:88,&isOrg&:false,&slug&:&ta-cai-ge&,&isFollowed&:false,&description&:&&,&name&:&她才哥&,&profileUrl&:&https:\u002F\\u002Fpeople\u002Fta-cai-ge&,&avatar&:{&id&:&27b8f35e338b4f23b82f&,&template&:&https:\u002F\\u002F50\u002F{id}_{size}.jpg&},&isOrgWhiteList&:false,&isBanned&:false},{&bio&:&&,&isFollowing&:false,&hash&:&036bdf20bdcc31d3e7a2&,&uid&:837700,&isOrg&:false,&slug&:&loner-13-32&,&isFollowed&:false,&description&:&&,&name&:&只读用户&,&profileUrl&:&https:\u002F\\u002Fpeople\u002Floner-13-32&,&avatar&:{&id&:&v2-ba566cee8c9a7c708b451&,&template&:&https:\u002F\\u002F50\u002F{id}_{size}.jpg&},&isOrgWhiteList&:false,&isBanned&:false},{&bio&:&blitz kid&,&isFollowing&:false,&hash&:&d4858962edda692c04e64b&,&uid&:263300,&isOrg&:false,&slug&:&bian-ce-33&,&isFollowed&:false,&description&:&从零开始的gameplay&,&name&:&creakcc&,&profileUrl&:&https:\u002F\\u002Fpeople\u002Fbian-ce-33&,&avatar&:{&id&:&v2-e2e5da1de26a7fc4fd9ff26&,&template&:&https:\u002F\\u002F50\u002F{id}_{size}.jpg&},&isOrgWhiteList&:false,&isBanned&:false}],&summary&:&\u003Cimg src=\&http:\u002F\\u002Fv2-49bef758b355f7b0x112.png\& data-rawwidth=\&250\& data-rawheight=\&250\& class=\&origin_image inline-img zh-lightbox-thumb\& data-original=\&http:\u002F\\u002Fv2-49bef758b355f7b046280b_r.png\&\u003E本文由游戏玄学翻译社制作发布,首发于\u003Ca href=\&http:\u002F\\u002Farchives\u002F1F\& data-title=\&泰然网\& class=\&\&\u003E泰然网\u003C\u002Fa\u003E! 翻译:太昊,兔角鹿,小木曾;Review: 风雨时;统筹润色:杨雍 想象一下:你正在打飞机!!哦,不,测试你最新的和最棒的一个射击游戏。敌人在以你能掌握的最快速度来回飞行,然后,砰!卡了一帧之后,你就…&,&reviewingCommentsCount&:0,&meta&:{&previous&:null,&next&:{&isTitleImageFullScreen&:true,&rating&:&none&,&titleImage&:&https:\u002F\\u002F50\u002Fv2-d6f25d8bbc7b00a973aa7e_xl.jpg&,&links&:{&comments&:&\u002Fapi\u002Fposts\u002F2Fcomments&},&topics&:[{&url&:&https:\u002F\\u002Ftopic\u002F&,&id&:&&,&name&:&游戏&},{&url&:&https:\u002F\\u002Ftopic\u002F&,&id&:&&,&name&:&游戏开发&},{&url&:&https:\u002F\\u002Ftopic\u002F&,&id&:&&,&name&:&游戏设计&}],&adminClosedComment&:false,&href&:&\u002Fapi\u002Fposts\u002F&,&excerptTitle&:&&,&author&:{&bio&:&?游戏人 ?《Cocos2d-x 之Lua 核心编程 》作者 ?@泰然网()创始人&,&isFollowing&:false,&hash&:&054facfefc74f42a5b00&,&uid&:76,&isOrg&:false,&slug&:&ivenyang&,&isFollowed&:false,&description&:&在逆袭的邪路上,越滚越远。。。。&,&name&:&杨雍&,&profileUrl&:&https:\u002F\\u002Fpeople\u002Fivenyang&,&avatar&:{&id&:&4f209fef6bcfc345bf66&,&template&:&https:\u002F\\u002F50\u002F{id}_{size}.jpg&},&isOrgWhiteList&:false,&isBanned&:false},&column&:{&slug&:&games&,&name&:&游戏玄学社&},&content&:&\u003Cblockquote\u003E\u003Cp\u003E本文由\u003Ca href=\&https:\u002F\\u002F?target=http%3A\\u002FFutm_source%3Dcowlevel\& class=\& wrap external\& target=\&_blank\& rel=\&nofollow noreferrer\&\u003E「@玄学翻译社」\u003Ci class=\&icon-external\&\u003E\u003C\u002Fi\u003E\u003C\u002Fa\u003E企划制作!欢迎关注本专栏!\u003C\u002Fp\u003E\u003Cp\u003E企划:宋雅文、杨雍;翻译:满月、王蛇无毒、Thest、一个兔角鹿、风语时;校对:刘嘉俊、太昊。\u003C\u002Fp\u003E\u003C\u002Fblockquote\u003E\u003Cimg src=\&https:\u002F\\u002F50\u002Fv2-ffbf724db685e86cdebcf8_b.jpg\& data-rawwidth=\&636\& data-rawheight=\&358\& class=\&origin_image zh-lightbox-thumb\& width=\&636\& data-original=\&https:\u002F\\u002F50\u002Fv2-ffbf724db685e86cdebcf8_r.jpg\&\u003E\u003Cbr\u003E\u003Cp\u003EDuang~~ Duang~~ Duang~~\u003C\u002Fp\u003E\u003Cp\u003E历年来,许多游戏试图去模拟胸部的摇晃。乃至一个专门的名词的产生「胸部物理」(俗称“乳摇系统”,下文统称“乳摇系统”)。\u003C\u002Fp\u003E\u003Cp\u003E若你玩过有乳摇系统的游戏,可能会发现很少有游戏中的胸部动起来和现实里一样,是受重力影响的一团脂肪。游戏中这些胸部一般都像是有自我意志的氢气球。\u003C\u002Fp\u003E\u003Cp\u003E某些游戏对胸部表现太不真实,以至于论者认为这是性别歧视,或者游戏行业物化女性的结果。\u003C\u002Fp\u003E\u003Cp\u003E我见过人吐槽游戏开发者是否从未接触过真实的胸部。我也见过有人暗示游戏开发者不知道怎么在游戏里去恰当的表现女性特征,以及因为做游戏的女性太少,所以才会有茫茫多奇形怪状的胸部。\u003Cbr\u003E\u003Cem\u003E(编者注:游戏人,单身狗,没朋友)\u003C\u002Fem\u003E\u003C\u002Fp\u003E\u003Cp\u003E我也在想,这些看法有道理吗?很多人对为什么乳摇系统糟糕提出疑问,却少见讲述胸部创造过程的干货。搜索一番之后,我发现了\u003Ca href=\&https:\u002F\\u002F?target=http%3A\u002F\\u002Fthreads\u002Fbouncy-breast-physics-nsfw.2F\& class=\& wrap external\& target=\&_blank\& rel=\&nofollow noreferrer\&\u003E很多\u003Ci class=\&icon-external\&\u003E\u003C\u002Fi\u003E\u003C\u002Fa\u003E\u003Ca href=\&https:\u002F\\u002F?target=http%3A\u002F\\u002Fquestions\u002F2Fsuggestions-for-breast-physics.html\& class=\& wrap external\& target=\&_blank\& rel=\&nofollow noreferrer\&\u003E业余\u003Ci class=\&icon-external\&\u003E\u003C\u002Fi\u003E\u003C\u002Fa\u003E\u003Ca href=\&https:\u002F\\u002F?target=http%3A\u002F\u002Fblenderartists.org\u002Fforum\u002Fshowthread.php%3F192897-How-to-apply-bounce-to-breast-bones-in-female-model\& class=\& wrap external\& target=\&_blank\& rel=\&nofollow noreferrer\&\u003E开发者\u003Ci class=\&icon-external\&\u003E\u003C\u002Fi\u003E\u003C\u002Fa\u003E 似乎\u003Ca href=\&https:\u002F\\u002F?target=http%3A\u002F\\u002Fthreads\u002Ftechnique-to-apply-bouncy-breasts-and-hair-physics.6F\& class=\& wrap external\& target=\&_blank\& rel=\&nofollow noreferrer\&\u003E真的\u003Ci class=\&icon-external\&\u003E\u003C\u002Fi\u003E\u003C\u002Fa\u003E 不知怎样在游戏中处理乳摇系统。各种论坛里竟有不少帖子和教程讨论如何实现优秀的乳摇系统。有人做出了四个章节的幻灯片,名为“\u003Ca href=\&https:\u002F\\u002F?target=http%3A\u002F\u002Fwww.slideshare.net\u002FMatumitFilmSombunjaroen%3Futm_campaign%3Dprofiletracking%26utm_medium%3Dsssite%26utm_source%3Dssslideview\& class=\& wrap external\& target=\&_blank\& rel=\&nofollow noreferrer\&\u003EUnity中的乳摇之道\u003Ci class=\&icon-external\&\u003E\u003C\u002Fi\u003E\u003C\u002Fa\u003E”。还有人开发了专门工具供其他开发者使用,从而一起揭开“乳到底怎么摇”的神秘面纱。\u003C\u002Fp\u003E\u003Cp\u003E而同时,资深游戏开发者们已经在胸部的合理摇动这条路上探寻了将近二十年之久。\u003C\u002Fp\u003E\u003Ch3\u003E1996:一切的开始\u003C\u002Fh3\u003E\u003Cp\u003E1996年一款名为「饿狼传说2」的游戏发布。这款游戏被称为是对「街霸2」的\&\u003Ca href=\&https:\u002F\\u002F?target=http%3A\u002F\u002Fwww.hardcoregaming101.net\u002Ffatalfury\u002Ffatalfury2.htm\& class=\& wrap external\& target=\&_blank\& rel=\&nofollow noreferrer\&\u003E公然抄袭\u003Ci class=\&icon-external\&\u003E\u003C\u002Fi\u003E\u003C\u002Fa\u003E\&。「饿狼传说2」 也确实有自己独树一帜的设计点:在玩家生命值极低的时候可以搓动华丽的“超必杀技”来摆脱困境,一举逆转整个局势。\u003C\u002Fp\u003E\u003Cp\u003E不过讲真,「饿狼传说2」对游戏有一巨大贡献之:史上\u003Ca href=\&https:\u002F\\u002F?target=http%3A\u002F\\u002Fbreast-bounce\u002F2F\& class=\& wrap external\& target=\&_blank\& rel=\&nofollow noreferrer\&\u003E第一个\u003Ci class=\&icon-external\&\u003E\u003C\u002Fi\u003E\u003C\u002Fa\u003E 胸部会自己摇动的角色。\u003C\u002Fp\u003E\u003Cimg src=\&https:\u002F\\u002F50\u002Fv2-ae4ff57f0b_b.jpg\& data-rawwidth=\&640\& data-rawheight=\&354\& class=\&origin_image zh-lightbox-thumb\& width=\&640\& data-original=\&https:\u002F\\u002F50\u002Fv2-ae4ff57f0b_r.jpg\&\u003E\u003Cbr\u003E\u003Cp\u003E(源:\u003Ca href=\&https:\u002F\\u002F?target=http%3A\u002F\u002Fyoutu.be\u002FBO9cT7JRTnY\& class=\& wrap external\& target=\&_blank\& rel=\&nofollow noreferrer\&\u003ETheInnocentSinful1\u003Ci class=\&icon-external\&\u003E\u003C\u002Fi\u003E\u003C\u002Fa\u003E)\u003C\u002Fp\u003E\u003Cp\u003E\u003Cstrong\u003E不知火舞\u003C\u002Fstrong\u003E,这个角色以一对生动活现的胸部而著称。即使现在「饿狼传说」系列已经过气,它仍然给我们留下了宝贵的遗产:让乳摇成为格斗游戏的标配。\u003C\u002Fp\u003E\u003Cp\u003E\u003Cstrong\u003EStreet Fighter 4\u003C\u002Fstrong\u003E\u003Cbr\u003E\u003Cstrong\u003E街霸4\u003C\u002Fstrong\u003E\u003C\u002Fp\u003E\u003Cimg src=\&https:\u002F\\u002F50\u002Fv2-79fdd18d10f_b.jpg\& data-rawwidth=\&640\& data-rawheight=\&353\& class=\&origin_image zh-lightbox-thumb\& width=\&640\& data-original=\&https:\u002F\\u002F50\u002Fv2-79fdd18d10f_r.jpg\&\u003E\u003Cbr\u003E\u003Cp\u003E(Source: \u003Ca href=\&https:\u002F\\u002F?target=https%3A\u002F\\u002Fchannel\u002FUCGRLgaqENNL4olJ5-A34IFQ\& class=\& wrap external\& target=\&_blank\& rel=\&nofollow noreferrer\&\u003ECeruleanNight\u003Ci class=\&icon-external\&\u003E\u003C\u002Fi\u003E\u003C\u002Fa\u003E)\u003Cbr\u003E(源:\u003Ca href=\&https:\u002F\\u002F?target=https%3A\u002F\\u002Fchannel\u002FUCGRLgaqENNL4olJ5-A34IFQ\& class=\& wrap external\& target=\&_blank\& rel=\&nofollow noreferrer\&\u003ECeruleanNight\u003Ci class=\&icon-external\&\u003E\u003C\u002Fi\u003E\u003C\u002Fa\u003E)\u003C\u002Fp\u003E\u003Cp\u003E\u003Cstrong\u003ESoul Calibur\u003C\u002Fstrong\u003E\u003Cbr\u003E\u003Cstrong\u003E剑魂\u003C\u002Fstrong\u003E\u003C\u002Fp\u003E\u003Cimg src=\&https:\u002F\\u002F50\u002Fv2-cf7c92f73f6bdd6f29d406db40da203e_b.jpg\& data-rawwidth=\&640\& data-rawheight=\&355\& class=\&origin_image zh-lightbox-thumb\& width=\&640\& data-original=\&https:\u002F\\u002F50\u002Fv2-cf7c92f73f6bdd6f29d406db40da203e_r.jpg\&\u003E\u003Cbr\u003E\u003Cp\u003E(源:\u003Ca href=\&https:\u002F\\u002F?target=https%3A\u002F\\u002Fchannel\u002FUCb7ElR0N_nIIu_Ojx56TR_A\& class=\& wrap external\& target=\&_blank\& rel=\&nofollow noreferrer\&\u003EThegamerwalkthroughs\u003Ci class=\&icon-external\&\u003E\u003C\u002Fi\u003E\u003C\u002Fa\u003E)\u003C\u002Fp\u003E\u003Cp\u003E\u003Cstrong\u003ESkullgirls\u003C\u002Fstrong\u003E\u003Cbr\u003E\u003Cstrong\u003E骷髅女孩\u003C\u002Fstrong\u003E\u003C\u002Fp\u003E\u003Cimg src=\&https:\u002F\\u002F50\u002Fv2-bfbf1c2cac0e609_b.jpg\& data-rawwidth=\&640\& data-rawheight=\&356\& class=\&origin_image zh-lightbox-thumb\& width=\&640\& data-original=\&https:\u002F\\u002F50\u002Fv2-bfbf1c2cac0e609_r.jpg\&\u003E\u003Cbr\u003E\u003Cp\u003E(源:\u003Ca href=\&https:\u002F\\u002F?target=https%3A\u002F\\u002Fchannel\u002FUCglDB87yRIO7druwPcyyqSQ\& class=\& wrap external\& target=\&_blank\& rel=\&nofollow noreferrer\&\u003EPoccola_margheritaCi class=\&icon-external\&\u003E\u003C\u002Fi\u003E\u003C\u002Fa\u003E)\u003C\u002Fp\u003E\u003Cp\u003E当然,这些年来并不是只有格斗游戏搭载了乳摇系统。\u003C\u002Fp\u003E\u003Cp\u003E\u003Cstrong\u003ETomb Raider\u003C\u002Fstrong\u003E\u003Cbr\u003E\u003Cstrong\u003E古墓丽影\u003C\u002Fstrong\u003E\u003C\u002Fp\u003E\u003Cimg src=\&https:\u002F\\u002F50\u002Fv2-170af90c578f_b.jpg\& data-rawwidth=\&640\& data-rawheight=\&360\& class=\&origin_image zh-lightbox-thumb\& width=\&640\& data-original=\&https:\u002F\\u002F50\u002Fv2-170af90c578f_r.jpg\&\u003E\u003Cbr\u003E\u003Cp\u003E(源:\u003Ca href=\&https:\u002F\\u002F?target=https%3A\u002F\\u002Fchannel\u002FUCsdeD9hmbNtwqW57JI7DCrg\& class=\& wrap external\& target=\&_blank\& rel=\&nofollow noreferrer\&\u003EsysCi class=\&icon-external\&\u003E\u003C\u002Fi\u003E\u003C\u002Fa\u003E)\u003C\u002Fp\u003E\u003Cp\u003E\u003Cstrong\u003EResident Evil\u003C\u002Fstrong\u003E\u003Cbr\u003E\u003Cstrong\u003E生化危机\u003C\u002Fstrong\u003E\u003C\u002Fp\u003E\u003Cimg src=\&https:\u002F\\u002F50\u002Fv2-8b3ef4f499c82b300e88ec8a321f4af6_b.jpg\& data-rawwidth=\&640\& data-rawheight=\&356\& class=\&origin_image zh-lightbox-thumb\& width=\&640\& data-original=\&https:\u002F\\u002F50\u002Fv2-8b3ef4f499c82b300e88ec8a321f4af6_r.jpg\&\u003E\u003Cbr\u003E\u003Cp\u003E(资料来源:\u003Ca href=\&https:\u002F\\u002F?target=https%3A\u002F\\u002Fchannel\u002FUC4wH23WmLtL20lReS_TZAPg\& class=\& wrap external\& target=\&_blank\& rel=\&nofollow noreferrer\&\u003ESaladtoser69\u003Ci class=\&icon-external\&\u003E\u003C\u002Fi\u003E\u003C\u002Fa\u003E)\u003C\u002Fp\u003E\u003Cp\u003E\u003Cstrong\u003E崛起:罗马之子\u003C\u002Fstrong\u003E\u003C\u002Fp\u003E\u003Cp\u003E当开发者不给胸部添加物理效果时,不少技术宅玩家就会通过制作mod的方式自己实现。其中典型的就是老滚「天际」的最强乳摇系统mod:\u003C\u002Fp\u003E\u003Cimg src=\&https:\u002F\\u002F50\u002Fv2-c738b827d4822cb8fdea6e0fbe2c832b_b.jpg\& data-rawwidth=\&640\& data-rawheight=\&362\& class=\&origin_image zh-lightbox-thumb\& width=\&640\& data-original=\&https:\u002F\\u002F50\u002Fv2-c738b827d4822cb8fdea6e0fbe2c832b_r.jpg\&\u003E\u003Cbr\u003E\u003Cp\u003E(资料来源: \u003Ca href=\&https:\u002F\\u002F?target=https%3A\u002F\\u002Fchannel\u002FUCa16DLkbM09dzsVJl9mYUhQ\& class=\& wrap external\& target=\&_blank\& rel=\&nofollow noreferrer\&\u003EMarek Iwanowicz\u003Ci class=\&icon-external\&\u003E\u003C\u002Fi\u003E\u003C\u002Fa\u003E)\u003C\u002Fp\u003E\u003Cp\u003E在2009年,有一个\u003Ca href=\&https:\u002F\\u002F?target=http%3A\u002F\\u002Fnwn\u002F\u002Femeralds-breasts.html\& class=\& wrap external\& target=\&_blank\& rel=\&nofollow noreferrer\&\u003E第二人生的mod\u003Ci class=\&icon-external\&\u003E\u003C\u002Fi\u003E\u003C\u002Fa\u003E允许玩家自行给角色添加乳摇系统效果。由于这个mod太受欢迎而被采纳进游戏本体——现如今玩家们纷纷\u003Ca href=\&https:\u002F\\u002F?target=http%3A\u002F\\u002Ft5\u002FEnglish-Knowledge-Base\u002FControlling-your-avatar-s-appearance\u002F}

我要回帖

更多关于 unity 子弹对象池 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信