如何解决 Unity3d 编辑器因视差脚本池大小增加而滞后的问题?
How to resolve Unity3d Editor lags with increased pool size for parallaxer script?
我正在制作一款 2d 平台游戏。
当附加到我的预制件的视差脚本的池大小增加时,Unity3D 应用程序滞后并冻结在更大的池大小。此事件以前没有出现在其他池大小大得多的项目中。无论游戏方面如何,该事件似乎都会持续存在。
附带预制件的视差脚本
池大小:10(开始滞后)
换挡速度:-1
产卵率:1
Y 生成范围:
最小 Y:0
最大 Y:2.72
默认重生位置
X: 12.81
Y:-0.03Z:0
立即生成:未选中
立即重生位置
X: 0 Y: 0 Z: 0
目标纵横比:
X: 10 Y: 16
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Parallaxer : MonoBehaviour {
class PoolObject {
public Transform transform;
public bool inUse;
public PoolObject(Transform t) { transform = t; }
public void Use() { inUse = true; }
public void Dispose() { inUse = false; }
}
[System.Serializable]
public struct YSpawnRange {
public float minY;
public float maxY;
}
public GameObject Prefab;
public int poolSize;
public float shiftSpeed;
public float spawnRate;
public YSpawnRange ySpawnRange;
public Vector3 defaultSpawnPos;
public bool spawnImmediate;
public Vector3 immediateSpawnPos;
public Vector2 targetAspectRatio;
float spawnTimer;
PoolObject[] poolObjects;
float targetAspect;
GameManager game;
void Awake() {
Configure();
}
void Start() {
game = GameManager.Instance;
}
void OnEnable() {
GameManager.OnGameOverConfirmed += OnGameOverConfirmed;
}
void OnDisable() {
GameManager.OnGameOverConfirmed -= OnGameOverConfirmed;
}
void OnGameOverConfirmed() {
for (int i = 0; i < poolObjects.Length; i++) {
poolObjects[i].Dispose();
poolObjects[i].transform.position = Vector3.one * 1000;
}
Configure();
}
void Update() {
if (game.GameOver) return;
Shift();
spawnTimer += Time.deltaTime;
if (spawnTimer > spawnRate) {
Spawn();
spawnTimer = 0;
}
}
void Configure() {
//spawning pool objects
targetAspect = targetAspectRatio.x / targetAspectRatio.y;
poolObjects = new PoolObject[poolSize];
for (int i = 0; i < poolObjects.Length; i++) {
GameObject go = Instantiate(Prefab) as GameObject;
Transform t = go.transform;
t.SetParent(transform);
t.position = Vector3.one * 1000;
poolObjects[i] = new PoolObject(t);
}
if (spawnImmediate) {
SpawnImmediate();
}
}
void Spawn() {
//moving pool objects into place
Transform t = GetPoolObject();
if (t == null) return;
Vector3 pos = Vector3.zero;
pos.y = Random.Range(ySpawnRange.minY, ySpawnRange.maxY);
pos.x = (defaultSpawnPos.x * Camera.main.aspect) / targetAspect;
t.position = pos;
}
void SpawnImmediate() {
Transform t = GetPoolObject();
if (t==null) return;
Vector3 pos = Vector3.zero;
pos.y = Random.Range(ySpawnRange.minY, ySpawnRange.maxY);
pos.x = (immediateSpawnPos.x * Camera.main.aspect) / targetAspect;
t.position = pos;
Spawn();
}
void Shift() {
//loop through pool objects
//moving them
//discarding them as they go off screen
for (int i = 0; i < poolObjects.Length; i++) {
poolObjects[i].transform.position += Vector3.right * shiftSpeed * Time.deltaTime;
CheckDisposeObject(poolObjects[i]);
}
}
void CheckDisposeObject(PoolObject poolObject) {
//place objects off screen
if (poolObject.transform.position.x < (-defaultSpawnPos.x * Camera.main.aspect) / targetAspect) {
poolObject.Dispose();
poolObject.transform.position = Vector3.one * 1000;
}
}
Transform GetPoolObject() {
//retrieving first available pool object
for (int i = 0; i < poolObjects.Length; i++) {
if (!poolObjects[i].inUse) {
poolObjects[i].Use();
return poolObjects[i].transform;
}
}
return null;
}
}
当您更改对象的 transform.position 时,如果该对象嵌套在游戏层次结构中的其他对象中,这可能是一个昂贵的过程。与其更改对象的变换,不如考虑在不使用时将 GameObject 设置为非活动状态。此外,与其每次需要 GetPoolObject() 时都遍历 PoolObjects 列表,不如考虑创建一个未使用对象的队列。这有两个目的:
- 您不再需要通过 属性
跟踪对象是否正在使用
- 您无需遍历所有对象即可找到可用对象。
队列示例:
Queue<PoolObject> availableObjects = new Queue<PoolObject>();
class PoolObject
{
public Transform transform;
public bool inUse;
public PoolObject(Transform t) { transform = t; }
public void Use() { inUse = true; }
public void Dispose() { inUse = false; }
}
void CheckDisposeObject(PoolObject poolObject)
{
//place objects off screen
if (poolObject.transform.position.x < (-defaultSpawnPos.x * Camera.main.aspect) / targetAspect)
{
availableObjects.Enqueue(poolObject);
poolObject.transform.gameObject.SetActive(false);
}
}
Transform GetPooledObject()
{
if(availableObjects.Count > 0)
Transform poolObj = availableObjects.Dequeue();
poolObj.gameObject.SetActive(true);
return poolObj;
else
return null;
}
我正在制作一款 2d 平台游戏。
当附加到我的预制件的视差脚本的池大小增加时,Unity3D 应用程序滞后并冻结在更大的池大小。此事件以前没有出现在其他池大小大得多的项目中。无论游戏方面如何,该事件似乎都会持续存在。
附带预制件的视差脚本
池大小:10(开始滞后)
换挡速度:-1
产卵率:1
Y 生成范围:
最小 Y:0
最大 Y:2.72
默认重生位置
X: 12.81
Y:-0.03Z:0
立即生成:未选中
立即重生位置
X: 0 Y: 0 Z: 0
目标纵横比:
X: 10 Y: 16
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Parallaxer : MonoBehaviour {
class PoolObject {
public Transform transform;
public bool inUse;
public PoolObject(Transform t) { transform = t; }
public void Use() { inUse = true; }
public void Dispose() { inUse = false; }
}
[System.Serializable]
public struct YSpawnRange {
public float minY;
public float maxY;
}
public GameObject Prefab;
public int poolSize;
public float shiftSpeed;
public float spawnRate;
public YSpawnRange ySpawnRange;
public Vector3 defaultSpawnPos;
public bool spawnImmediate;
public Vector3 immediateSpawnPos;
public Vector2 targetAspectRatio;
float spawnTimer;
PoolObject[] poolObjects;
float targetAspect;
GameManager game;
void Awake() {
Configure();
}
void Start() {
game = GameManager.Instance;
}
void OnEnable() {
GameManager.OnGameOverConfirmed += OnGameOverConfirmed;
}
void OnDisable() {
GameManager.OnGameOverConfirmed -= OnGameOverConfirmed;
}
void OnGameOverConfirmed() {
for (int i = 0; i < poolObjects.Length; i++) {
poolObjects[i].Dispose();
poolObjects[i].transform.position = Vector3.one * 1000;
}
Configure();
}
void Update() {
if (game.GameOver) return;
Shift();
spawnTimer += Time.deltaTime;
if (spawnTimer > spawnRate) {
Spawn();
spawnTimer = 0;
}
}
void Configure() {
//spawning pool objects
targetAspect = targetAspectRatio.x / targetAspectRatio.y;
poolObjects = new PoolObject[poolSize];
for (int i = 0; i < poolObjects.Length; i++) {
GameObject go = Instantiate(Prefab) as GameObject;
Transform t = go.transform;
t.SetParent(transform);
t.position = Vector3.one * 1000;
poolObjects[i] = new PoolObject(t);
}
if (spawnImmediate) {
SpawnImmediate();
}
}
void Spawn() {
//moving pool objects into place
Transform t = GetPoolObject();
if (t == null) return;
Vector3 pos = Vector3.zero;
pos.y = Random.Range(ySpawnRange.minY, ySpawnRange.maxY);
pos.x = (defaultSpawnPos.x * Camera.main.aspect) / targetAspect;
t.position = pos;
}
void SpawnImmediate() {
Transform t = GetPoolObject();
if (t==null) return;
Vector3 pos = Vector3.zero;
pos.y = Random.Range(ySpawnRange.minY, ySpawnRange.maxY);
pos.x = (immediateSpawnPos.x * Camera.main.aspect) / targetAspect;
t.position = pos;
Spawn();
}
void Shift() {
//loop through pool objects
//moving them
//discarding them as they go off screen
for (int i = 0; i < poolObjects.Length; i++) {
poolObjects[i].transform.position += Vector3.right * shiftSpeed * Time.deltaTime;
CheckDisposeObject(poolObjects[i]);
}
}
void CheckDisposeObject(PoolObject poolObject) {
//place objects off screen
if (poolObject.transform.position.x < (-defaultSpawnPos.x * Camera.main.aspect) / targetAspect) {
poolObject.Dispose();
poolObject.transform.position = Vector3.one * 1000;
}
}
Transform GetPoolObject() {
//retrieving first available pool object
for (int i = 0; i < poolObjects.Length; i++) {
if (!poolObjects[i].inUse) {
poolObjects[i].Use();
return poolObjects[i].transform;
}
}
return null;
}
}
当您更改对象的 transform.position 时,如果该对象嵌套在游戏层次结构中的其他对象中,这可能是一个昂贵的过程。与其更改对象的变换,不如考虑在不使用时将 GameObject 设置为非活动状态。此外,与其每次需要 GetPoolObject() 时都遍历 PoolObjects 列表,不如考虑创建一个未使用对象的队列。这有两个目的:
- 您不再需要通过 属性 跟踪对象是否正在使用
- 您无需遍历所有对象即可找到可用对象。
队列示例:
Queue<PoolObject> availableObjects = new Queue<PoolObject>();
class PoolObject
{
public Transform transform;
public bool inUse;
public PoolObject(Transform t) { transform = t; }
public void Use() { inUse = true; }
public void Dispose() { inUse = false; }
}
void CheckDisposeObject(PoolObject poolObject)
{
//place objects off screen
if (poolObject.transform.position.x < (-defaultSpawnPos.x * Camera.main.aspect) / targetAspect)
{
availableObjects.Enqueue(poolObject);
poolObject.transform.gameObject.SetActive(false);
}
}
Transform GetPooledObject()
{
if(availableObjects.Count > 0)
Transform poolObj = availableObjects.Dequeue();
poolObj.gameObject.SetActive(true);
return poolObj;
else
return null;
}