Unity 5:管理动态创建的游戏对象的简洁方法
Unity 5: Clean way to manage dynamically created GameObjects
在 Unity 5 中,"clean" 管理动态创建的游戏对象的方法是什么?
我写了一个 creates/destroys 几个 GameObjects
的组件 (MonoBehavior
)。这些对象作为选择角色部分的自定义定制系统的一部分加载 - hair/clothes 等。这意味着它们对玩家可见,在编辑器中可见,但不应该在编辑器中编辑。正在加载的对象是带有骨架的网格。
脚本以这种方式运行:
- 从资源加载游戏对象(确切的对象在脚本中确定,它们不是预制件)
- 将它们附加到场景的某个部分(不一定是它自己的节点)
- 销毁时删除。
删除:
protected void unloadLastResource(){
if (lastResourceInstance){
if (Application.isEditor){
GameObject.DestroyImmediate(lastResourceInstance);
}
else
GameObject.Destroy(lastResourceInstance);
Resources.UnloadUnusedAssets();
lastResourceInstance = null;
}
}
创作:
GameObject target = getEffectiveTargetNode();
Object resource = Resources.Load(newResourceName);
instance = Instantiate(resource) as GameObject;
instance.hideFlags = HideFlags.HideAndDontSave;
instance.transform.parent = target.transform;
instance.transform.localPosition = Vector3.zero;
instance.transform.localRotation = Quaternion.identity;
instance.transform.localScale = Vector3.one;
销毁处理程序:
void OnDestroy(){
unloadLastResource();
}
这在编辑器中似乎工作正常,但是当我从游戏模式切换回编辑模式时,我收到很多警告:
Destroying object multiple times. Don't use DestroyImmediate on the same object in OnDisable or OnDestroy.
UnityEngine.Object:DestroyImmediate(Object)
我在场景层次结构的顶层得到了一堆新的对象树(那些应该被删除的 - 树源自与原始 "resources" 一起加载并附加的对象)。
那么,如何干净利落地处理动态创建的游戏对象?
我需要知道我需要设置哪些标志,以及我应该采取哪些步骤来确保该对象不会 "leak" 进入场景并在我删除创建它的组件时被正确销毁。
建议?
"ResourceLoader"
使用的完整基础 class
public class BaseResourceLoader : MonoBehaviour {
public GameObject targetNode = null;
protected GameObject lastTargetNode{
get{return lastTargetNodeInternal;}
}
private GameObject lastTargetNodeInternal = null;
protected bool targetNodeChanged(){
return targetNode != lastTargetNode;
}
protected string lastResourceName{
get{return lastResourceNameInternal;}
}
private string lastResourceNameInternal = "";
//private Object lastResource;
private GameObject lastResourceInstance;
protected GameObject getEffectiveTargetNode(){
if (targetNode == null)
return this.gameObject;
return targetNode;
}
public void reloadResource(){
loadNewResource(lastResourceNameInternal, true);
}
protected void unloadLastResource(){
if (lastResourceInstance){
if (Application.isEditor){
GameObject.DestroyImmediate(lastResourceInstance);
}
else
GameObject.Destroy(lastResourceInstance);
Resources.UnloadUnusedAssets();
lastResourceInstance = null;
}
lastResourceNameInternal = "";
}
protected void loadNewResource(string newResourceName, bool forceReload){
if ((newResourceName == lastResourceNameInternal) && !forceReload)
return;
GameObject instance = null;
if (newResourceName != ""){
GameObject target = getEffectiveTargetNode();
Object resource = Resources.Load(newResourceName);
instance = Instantiate(resource) as GameObject;
instance.hideFlags = HideFlags.HideAndDontSave;
instance.transform.parent = target.transform;
instance.transform.localPosition = Vector3.zero;
instance.transform.localRotation = Quaternion.identity;
instance.transform.localScale = Vector3.one;
}
unloadLastResource ();
lastResourceInstance = instance;
lastResourceNameInternal = newResourceName;
lastTargetNodeInternal = targetNode;
}
void OnDestroy(){
unloadLastResource();
}
}
错误消息意味着您的代码将创建一个无限递归,因为您正在从 OnDestroy()
中销毁一个对象,这将再次对该对象调用 OnDestroy()
等等。 .
让我分享这段代码,我将其用作我所有行为的基础class:
using UnityEngine;
using System.Collections.Generic;
namespace de.softfun.drawntogether {
public class EnhancedBehavior : MonoBehaviour {
private List<GameObject> linkedObjects = new List<GameObject>();
protected void destroy(params GameObject[] objects) {
foreach (GameObject o in objects) {
try {
Destroy(o);
} catch {
continue;
}
}
}
public void LinkObjects(params GameObject[] objects) {
foreach (GameObject o in objects) {
linkedObjects.Add(o);
}
}
void OnDestroy() {
foreach (GameObject o in linkedObjects) {
destroy(o);
}
}
protected T instantiate<T>(T prefab, bool addLink = true) where T : Object {
T o = (T)Instantiate(prefab);
if (addLink && (o is GameObject)) {
linkedObjects.add(o);
}
return o;
}
}
}
这里的技巧是我收集了 GameObjects
中的 List
绑定到这个 MonoBehaviour
并且如果 'parent' 被销毁则应该被销毁。使用此 class 的 instantiate
时,创建的对象会自动添加到 List
,除非在此方法中将 addLink
设置为 false
。
也许你可以使用这个或类似的东西。
我想通了。
问题是由这一行引起的:
instance.hideFlags = HideFlags.HideAndDontSave;
在层次结构中,此标志仅影响一个对象,不影响对象的子对象。因此,如果您在层次结构中为根对象设置此标志并且场景被保存,根将被销毁,但它的子对象将被序列化(这意味着它们将在场景重新加载时出现在检查器中)并且您将获得大量关于多次销毁同一对象的警告。
要处理该问题,需要遍历整个层次结构并将标志设置为层次结构中的所有对象。这解决了问题并消除了所有错误。
void makeHierarchyHidden(GameObject obj){
obj.gameObject.hideFlags = HideFlags.HideAndDontSave;
foreach(Transform child in obj.transform){
makeHierarchyHidden(child.gameObject);
}
}
.....
makeHierarchyHidden (newObject);
.....
不清楚这是否仅在从磁盘加载非预制件时发生,或者仅在所有分层对象的一般情况下发生。
在 Unity 5 中,"clean" 管理动态创建的游戏对象的方法是什么?
我写了一个 creates/destroys 几个 GameObjects
的组件 (MonoBehavior
)。这些对象作为选择角色部分的自定义定制系统的一部分加载 - hair/clothes 等。这意味着它们对玩家可见,在编辑器中可见,但不应该在编辑器中编辑。正在加载的对象是带有骨架的网格。
脚本以这种方式运行:
- 从资源加载游戏对象(确切的对象在脚本中确定,它们不是预制件)
- 将它们附加到场景的某个部分(不一定是它自己的节点)
- 销毁时删除。
删除:
protected void unloadLastResource(){
if (lastResourceInstance){
if (Application.isEditor){
GameObject.DestroyImmediate(lastResourceInstance);
}
else
GameObject.Destroy(lastResourceInstance);
Resources.UnloadUnusedAssets();
lastResourceInstance = null;
}
}
创作:
GameObject target = getEffectiveTargetNode();
Object resource = Resources.Load(newResourceName);
instance = Instantiate(resource) as GameObject;
instance.hideFlags = HideFlags.HideAndDontSave;
instance.transform.parent = target.transform;
instance.transform.localPosition = Vector3.zero;
instance.transform.localRotation = Quaternion.identity;
instance.transform.localScale = Vector3.one;
销毁处理程序:
void OnDestroy(){
unloadLastResource();
}
这在编辑器中似乎工作正常,但是当我从游戏模式切换回编辑模式时,我收到很多警告:
Destroying object multiple times. Don't use DestroyImmediate on the same object in OnDisable or OnDestroy.
UnityEngine.Object:DestroyImmediate(Object)
我在场景层次结构的顶层得到了一堆新的对象树(那些应该被删除的 - 树源自与原始 "resources" 一起加载并附加的对象)。
那么,如何干净利落地处理动态创建的游戏对象?
我需要知道我需要设置哪些标志,以及我应该采取哪些步骤来确保该对象不会 "leak" 进入场景并在我删除创建它的组件时被正确销毁。
建议?
"ResourceLoader"
使用的完整基础 classpublic class BaseResourceLoader : MonoBehaviour {
public GameObject targetNode = null;
protected GameObject lastTargetNode{
get{return lastTargetNodeInternal;}
}
private GameObject lastTargetNodeInternal = null;
protected bool targetNodeChanged(){
return targetNode != lastTargetNode;
}
protected string lastResourceName{
get{return lastResourceNameInternal;}
}
private string lastResourceNameInternal = "";
//private Object lastResource;
private GameObject lastResourceInstance;
protected GameObject getEffectiveTargetNode(){
if (targetNode == null)
return this.gameObject;
return targetNode;
}
public void reloadResource(){
loadNewResource(lastResourceNameInternal, true);
}
protected void unloadLastResource(){
if (lastResourceInstance){
if (Application.isEditor){
GameObject.DestroyImmediate(lastResourceInstance);
}
else
GameObject.Destroy(lastResourceInstance);
Resources.UnloadUnusedAssets();
lastResourceInstance = null;
}
lastResourceNameInternal = "";
}
protected void loadNewResource(string newResourceName, bool forceReload){
if ((newResourceName == lastResourceNameInternal) && !forceReload)
return;
GameObject instance = null;
if (newResourceName != ""){
GameObject target = getEffectiveTargetNode();
Object resource = Resources.Load(newResourceName);
instance = Instantiate(resource) as GameObject;
instance.hideFlags = HideFlags.HideAndDontSave;
instance.transform.parent = target.transform;
instance.transform.localPosition = Vector3.zero;
instance.transform.localRotation = Quaternion.identity;
instance.transform.localScale = Vector3.one;
}
unloadLastResource ();
lastResourceInstance = instance;
lastResourceNameInternal = newResourceName;
lastTargetNodeInternal = targetNode;
}
void OnDestroy(){
unloadLastResource();
}
}
错误消息意味着您的代码将创建一个无限递归,因为您正在从 OnDestroy()
中销毁一个对象,这将再次对该对象调用 OnDestroy()
等等。 .
让我分享这段代码,我将其用作我所有行为的基础class:
using UnityEngine;
using System.Collections.Generic;
namespace de.softfun.drawntogether {
public class EnhancedBehavior : MonoBehaviour {
private List<GameObject> linkedObjects = new List<GameObject>();
protected void destroy(params GameObject[] objects) {
foreach (GameObject o in objects) {
try {
Destroy(o);
} catch {
continue;
}
}
}
public void LinkObjects(params GameObject[] objects) {
foreach (GameObject o in objects) {
linkedObjects.Add(o);
}
}
void OnDestroy() {
foreach (GameObject o in linkedObjects) {
destroy(o);
}
}
protected T instantiate<T>(T prefab, bool addLink = true) where T : Object {
T o = (T)Instantiate(prefab);
if (addLink && (o is GameObject)) {
linkedObjects.add(o);
}
return o;
}
}
}
这里的技巧是我收集了 GameObjects
中的 List
绑定到这个 MonoBehaviour
并且如果 'parent' 被销毁则应该被销毁。使用此 class 的 instantiate
时,创建的对象会自动添加到 List
,除非在此方法中将 addLink
设置为 false
。
也许你可以使用这个或类似的东西。
我想通了。
问题是由这一行引起的:
instance.hideFlags = HideFlags.HideAndDontSave;
在层次结构中,此标志仅影响一个对象,不影响对象的子对象。因此,如果您在层次结构中为根对象设置此标志并且场景被保存,根将被销毁,但它的子对象将被序列化(这意味着它们将在场景重新加载时出现在检查器中)并且您将获得大量关于多次销毁同一对象的警告。
要处理该问题,需要遍历整个层次结构并将标志设置为层次结构中的所有对象。这解决了问题并消除了所有错误。
void makeHierarchyHidden(GameObject obj){
obj.gameObject.hideFlags = HideFlags.HideAndDontSave;
foreach(Transform child in obj.transform){
makeHierarchyHidden(child.gameObject);
}
}
.....
makeHierarchyHidden (newObject);
.....
不清楚这是否仅在从磁盘加载非预制件时发生,或者仅在所有分层对象的一般情况下发生。