英文:
How do I prevent Unity from freezing during gameplay in a 2D scene?
问题
在Unity场景中玩耍时,游戏会冻结。我有以下代码:
using UnityEngine;
public class PlatformManager : MonoBehaviour
{
[SerializeField] private GameObject[] platformPrefabs;
[SerializeField] private Transform player;
[SerializeField] private float spawnDistance = 10f;
[SerializeField] private float platformWidth = 4f;
private float lastSpawnPositionY = -4.5f;
private float lastSpawnPositionX = 0f;
private void OnTriggerEnter2D(Collider2D collision)
{
Destroy(collision.gameObject);
}
private void Update()
{
if (player.position.y > lastSpawnPositionY - spawnDistance)
{
SpawnPlatform();
}
}
private void SpawnPlatform()
{
float randomX1 = GetRandomXPosition();
float randomX2 = GetRandomXPosition();
float randomY1 = lastSpawnPositionY + Random.Range(1.5f, 2.7f);
float randomY2 = randomY1 + Random.Range(0.2f, 0.5f);
while (Mathf.Abs(randomX1 - randomX2) < platformWidth)
{
randomX2 = GetRandomXPosition();
}
Vector2 spawnPosition1 = new Vector2(randomX1, randomY1);
Vector2 spawnPosition2 = new Vector2(randomX2, randomY2);
int platformIndex = GetRandomPlatformIndex();
GameObject platformPrefab = platformPrefabs[platformIndex];
Instantiate(platformPrefab, spawnPosition1, Quaternion.identity);
Instantiate(platformPrefabs[0], spawnPosition2, Quaternion.identity);
lastSpawnPositionY = Mathf.Max(randomY1, randomY2);
lastSpawnPositionX = Mathf.Max(randomX1, randomX2);
}
private float GetRandomXPosition()
{
float randomX = Random.Range(-7f, 7f);
while (Mathf.Abs(randomX - lastSpawnPositionX) < platformWidth)
{
randomX = Random.Range(-7f, 7f);
}
return randomX;
}
private int GetRandomPlatformIndex()
{
float[] probabilities = { 0.1f, 0.05f, 0.03f, 0.1f, 0.05f };
float totalProbability = 0f;
foreach (float probability in probabilities)
{
totalProbability += probability;
}
float randomValue = Random.Range(0f, totalProbability);
float cumulativeProbability = 0f;
for (int i = 0; i < probabilities.Length; i++)
{
cumulativeProbability += probabilities[i];
if (randomValue <= cumulativeProbability)
{
return i;
}
}
return 0;
}
}
这是生成游戏中平台的代码。我在舞台上有一个附有Box Collider并启用了Is Trigger Enter的物体,所以我在代码中使用了这个方法。也许代码中的某些问题导致Unity冻结。另外,你能告诉我如何使用相同的逻辑将平台向上移动,而不是删除和重新创建它们吗?
英文:
While I'm playing in a scene in Unity, the game freezes. I have this code:
using UnityEngine;
public class PlatformManager : MonoBehaviour
{
[SerializeField] private GameObject[] platformPrefabs;
[SerializeField] private Transform player;
[SerializeField] private float spawnDistance = 10f;
[SerializeField] private float platformWidth = 4f;
private float lastSpawnPositionY = -4.5f;
private float lastSpawnPositionX = 0f;
private void OnTriggerEnter2D(Collider2D collision)
{
Destroy(collision.gameObject);
}
private void Update()
{
if (player.position.y > lastSpawnPositionY - spawnDistance)
{
SpawnPlatform();
}
}
private void SpawnPlatform()
{
float randomX1 = GetRandomXPosition();
float randomX2 = GetRandomXPosition();
float randomY1 = lastSpawnPositionY + Random.Range(1.5f, 2.7f);
float randomY2 = randomY1 + Random.Range(0.2f, 0.5f);
while (Mathf.Abs(randomX1 - randomX2) < platformWidth)
{
randomX2 = GetRandomXPosition();
}
Vector2 spawnPosition1 = new Vector2(randomX1, randomY1);
Vector2 spawnPosition2 = new Vector2(randomX2, randomY2);
int platformIndex = GetRandomPlatformIndex();
GameObject platformPrefab = platformPrefabs[platformIndex];
Instantiate(platformPrefab, spawnPosition1, Quaternion.identity);
Instantiate(platformPrefabs[0], spawnPosition2, Quaternion.identity);
lastSpawnPositionY = Mathf.Max(randomY1, randomY2);
lastSpawnPositionX = Mathf.Max(randomX1, randomX2);
}
private float GetRandomXPosition()
{
float randomX = Random.Range(-7f, 7f);
while (Mathf.Abs(randomX - lastSpawnPositionX) < platformWidth)
{
randomX = Random.Range(-7f, 7f);
}
return randomX;
}
private int GetRandomPlatformIndex()
{
float[] probabilities = { 0.1f, 0.05f, 0.03f, 0.1f, 0.05f };
float totalProbability = 0f;
foreach (float probability in probabilities)
{
totalProbability += probability;
}
float randomValue = Random.Range(0f, totalProbability);
float cumulativeProbability = 0f;
for (int i = 0; i < probabilities.Length; i++)
{
cumulativeProbability += probabilities[i];
if (randomValue <= cumulativeProbability)
{
return i;
}
}
return 0;
}
}
This is the code that generates platforms in the game. I have an object on the stage that has a Box Collider attached to it and Is Trigger Enter enabled, so I work with this method in the code. Perhaps there are problems somewhere in the code that cause Unity to freeze. And also, could you please tell me how to move platforms up using the same logic instead of deleting and re-creating them?
答案1
得分: 1
以下是翻译好的内容:
一个非常潜在的冻结点是那些 while
循环。如果随机生成永远无法满足退出条件怎么办?
更新
在我之前的回答中,我犯了一个错误的假设。我以为新的随机位置应该在前一个位置的范围内,从 -7
到 7
。但实际上这是你的绝对范围。
所以实际上,对于第一个 randomX1
,有两个潜在的范围
- 从前一个位置的左侧
platformWidth
单位到-7f
的范围 - 从前一个位置的右侧
platformWidth
单位到7f
的范围
限制在于只有在范围实际上大于 platformWidth
时才使用范围。
这里有一些图像,以更好地理解下面的代码
所以类似于:
var minPointRange1a = -7f;
var maxPointRange1a = lastSpawnPositionX - platformWidth;
var sizeRange1a = maxPointRange1a - minPointRange1a;
var range1a = Random.Range(minPointRange1a, maxPointRange1a);
var minPointRange1b = lastSpawnPositionX + platformWidth;
var maxPointRange1b = 7f;
var sizeRange1b = maxPointRange1b - minPointRange1b;
var range1b = Random.Range(minPointRange1b, maxPointRange1b);
// 如果只有一个选项?
float randomX1;
if (sizeRange1a > 0 && sizeRange1b <= 0)
{
randomX1 = range1a;
}
else if (sizeRange1a <= 0)
{
randomX1 = range1b;
}
else
{
// 如果两者都可能,根据选项之间的大小分布选择第一个或第二个选项
randomX1 = Random.value <= sizeRange1a / (sizeRange1a + sizeRange1b) ? range1a : range1b;
}
注意:Random.value
基本上等于 Random.Range(0f, 1f)
然后对于第二个值,现在更加棘手,因为现在你已经有两个你希望与之保持一定距离的点,所以你可能需要在对象中放置多达三个范围。
所以我们只需要继续检查这些范围和边缘情况
var minPointRange2a = -7f;
var maxPointRange2a = Mathf.Min(lastSpawnPositionX, randomX1) - platformWidth;
var sizeRange2a = maxPointRange2a - minPointRange2a;
var range2a = Random.Range(minPointRange2a, maxPointRange2a);
var minPointRange2b = Mathf.Min(lastSpawnPositionX, randomX1) + platformWidth;
var maxPointRange2b = Mathf.Max(lastSpawnPositionX, randomX1) - platformWidth;
var sizeRange2b = maxPointRange2b - minPointRange2b;
var range2b = Random.Range(minPointRange2b, maxPointRange2b);
var minPointRange2c = Mathf.Max(lastSpawnPositionX, randomX1) + platformWidth;
var maxPointRange2c = 7f;
var sizeRange2c = maxPointRange2c - minPointRange2c;
var range2c = Random.Range(minPointRange2c , maxPointRange2c);
// 再次分布在这些范围之间(肯定还有更优雅的解决方案 ^^)
var cases = 0;
if (sizeRange2a > 0) cases += 1;
if (sizeRange2b > 0) cases += 2;
if (sizeRange2c > 0) cases += 4;
float randomX2;
switch(cases)
{
case 1: // 只有 range2a 有效
randomX2 = range2a;
break;
case 2: // 只有 range2b 有效
randomX2 = range2b;
break;
case 4: // 只有 range2c 有效
randomX2 = range2c;
break;
case 3: // range2a 和 range2b 都有效
randomX2 = Random.value > sizeRange2a / (sizeRange2a + sizeRange2b) ? range2b : range2a;
break;
case 5: // range2a 和 range2c 都有效
randomX2 = Random.value > sizeRange2a / (sizeRange2a + sizeRange2c) ? range2c : range2a;
break;
case 6: // range2b 和 range2c 都有效
randomX2 = Random.value > sizeRange2b / (sizeRange2b + sizeRange2c) ? range2c : range2b;
break;
case 7: // 三个都有效
var rnd = Random.value;
randomX2 = rnd <= sizeRange2a / (sizeRange2a + sizeRange2b + sizeRange2c) ? range2a : rnd <= sizeRange2b / (sizeRange2a + sizeRange2b + sizeRange2c) ? range2b : range2c;
break;
default:
throw new OperationInvalidException("无法找到 randomX2 的任何有效范围!");
}
这可能可以改进,希望有更优雅的解决方案,只有在实际有效且需要时才执行 Random.Range
,某些计算是为了更好地理解而故意重复的 - 但我希望这能给你一个想法。
英文:
One very potential freeze are those while
loops. What if the random generations never are able to fulfill the exit condition?
Update
In my previous answer I made a wrong assumption. I thought the new random positions should be in the range -7
to 7
from the previous one. But actually that is your absolute range.
So actually for the first randomX1
there is actually two potential ranges
- The range from
platformWidth
units left of the previous position down to-7f
- The range from
platformWidth
units right of the previous position up to7f
with the limitation that you only use a range if the range itself is actually bigger than platformWidth
.
Here some images for better understanding the code below
So something like e.g.
var minPointRange1a = -7f;
var maxPointRange1a = lastSpawnPositionX - platformWidth;
var sizeRange1a = maxPointRange1a - minPointRange1a;
var range1a = Random.Range(minPointRange1a, maxPointRange1a);
var minPointRange1b = lastSpawnPositionX + platformWidth;
var maxPointRange1b = 7f;
var sizeRange1b = maxPointRange1b - minPointRange1b;
var range1b = Random.Range(minPointRange1b, maxPointRange1b);
// Is there only one option anyway?
float randomX1;
if(sizeRange1a > 0 && sizeRange1b <= 0)
{
randomX1 = range1a;
}
else if(sizeRange1a <= 0)
{
randomX1 = range1b;
}
else
{
// are both possible take either the first or second option according to the size distribution between the options
randomX1 = Random.value <= sizeRange1a / (sizeRange1a + sizeRange1b) ? range1a : range1b;
}
Note: Random.value
basically equals Random.Range(0f, 1f)
Then for the second value it is even bit trickier as now you have already two points you want to have a certain distance from so there are up to three ranges you could potentially place the object in
so we just have to continue checking those ranges and edge cases
var minPointRange2a = -7f;
var maxPointRange2a = Mathf.Min(lastSpawnPositionX, randomX1) - platformWidth;
var sizeRange2a = maxPointRange2a - minPointRange2a;
var range2a = Random.Range(minPointRange2a, maxPointRange2a);
var minPointRange2b = Mathf.Min(lastSpawnPositionX, randomX1) + platformWidth;
var maxPointRange2b = Mathf.Max(lastSpawnPositionX, randomX1) - platformWidth;
var sizeRange2b = maxPointRange2b - minPointRange2b;
var range2b = Random.Range(minPointRange2b, maxPointRange2b);
var minPointRange2c = Mathf.Max(lastSpawnPositionX, randomX1) + platformWidth;
var maxPointRange2c = 7f;
var sizeRange2c = maxPointRange2c - minPointRange2c;
var range2c = Random.Range(minPointRange2c , maxPointRange2c);
// again distributing amongst these (certainly leaving a lot potential for improvement here ^^)
var cases = 0;
if (sizeRange2a > 0) cases += 1;
if (sizeRange2b > 0) cases += 2;
if (sizeRange2c > 0) cases += 4;
float randomX2;
switch(cases)
{
case 1: // only range2a is valid
randomX2 = range2a;
break;
case 2: // only range2b is valid
randomX2 = range2b;
break;
case 4: // only range2c is valid
randomX2 = range2c;
break;
case 3: // range2a and range2b are valid
randomX2 = Random.value > sizeRange2a / (sizeRange2a + sizeRange2b) ? range2b : range2a;
break;
case 5: // range2a and range2c are valid
randomX2 = Random.value > sizeRange2a / (sizeRange2a + sizeRange2c) ? range2c : range2a;
break;
case 6: // range2b and range2c are valid
randomX2 = Random.value > sizeRange2b / (sizeRange2b + sizeRange2c) ? range2c : range2b;
break;
case 7: // all three are valid
var rnd = Random.value;
randomX2 = rnd <= sizeRange2a / (sizeRange2a + sizeRange2b + sizeRange2c) ? range2a : rnd <= sizeRange2b / (sizeRange2a + sizeRange2b + sizeRange2c) ? range2b : range2c;
break;
default:
throw new OperationInvalidException("Was not able to find any valid range for randomX2!");
}
This can probably / hopefully be improved somehow - there might be more elegant solutions to this, only execute Random.Range
if actually valid and required and some calculations are intentionally repeated for better understanding - but I hope that gives you and idea
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论