如何在2D场景中防止Unity在游戏过程中冻结?

huangapple go评论105阅读模式
英文:

How do I prevent Unity from freezing during gameplay in a 2D scene?

问题

在Unity场景中玩耍时,游戏会冻结。我有以下代码:

  1. using UnityEngine;
  2. public class PlatformManager : MonoBehaviour
  3. {
  4. [SerializeField] private GameObject[] platformPrefabs;
  5. [SerializeField] private Transform player;
  6. [SerializeField] private float spawnDistance = 10f;
  7. [SerializeField] private float platformWidth = 4f;
  8. private float lastSpawnPositionY = -4.5f;
  9. private float lastSpawnPositionX = 0f;
  10. private void OnTriggerEnter2D(Collider2D collision)
  11. {
  12. Destroy(collision.gameObject);
  13. }
  14. private void Update()
  15. {
  16. if (player.position.y > lastSpawnPositionY - spawnDistance)
  17. {
  18. SpawnPlatform();
  19. }
  20. }
  21. private void SpawnPlatform()
  22. {
  23. float randomX1 = GetRandomXPosition();
  24. float randomX2 = GetRandomXPosition();
  25. float randomY1 = lastSpawnPositionY + Random.Range(1.5f, 2.7f);
  26. float randomY2 = randomY1 + Random.Range(0.2f, 0.5f);
  27. while (Mathf.Abs(randomX1 - randomX2) < platformWidth)
  28. {
  29. randomX2 = GetRandomXPosition();
  30. }
  31. Vector2 spawnPosition1 = new Vector2(randomX1, randomY1);
  32. Vector2 spawnPosition2 = new Vector2(randomX2, randomY2);
  33. int platformIndex = GetRandomPlatformIndex();
  34. GameObject platformPrefab = platformPrefabs[platformIndex];
  35. Instantiate(platformPrefab, spawnPosition1, Quaternion.identity);
  36. Instantiate(platformPrefabs[0], spawnPosition2, Quaternion.identity);
  37. lastSpawnPositionY = Mathf.Max(randomY1, randomY2);
  38. lastSpawnPositionX = Mathf.Max(randomX1, randomX2);
  39. }
  40. private float GetRandomXPosition()
  41. {
  42. float randomX = Random.Range(-7f, 7f);
  43. while (Mathf.Abs(randomX - lastSpawnPositionX) < platformWidth)
  44. {
  45. randomX = Random.Range(-7f, 7f);
  46. }
  47. return randomX;
  48. }
  49. private int GetRandomPlatformIndex()
  50. {
  51. float[] probabilities = { 0.1f, 0.05f, 0.03f, 0.1f, 0.05f };
  52. float totalProbability = 0f;
  53. foreach (float probability in probabilities)
  54. {
  55. totalProbability += probability;
  56. }
  57. float randomValue = Random.Range(0f, totalProbability);
  58. float cumulativeProbability = 0f;
  59. for (int i = 0; i < probabilities.Length; i++)
  60. {
  61. cumulativeProbability += probabilities[i];
  62. if (randomValue <= cumulativeProbability)
  63. {
  64. return i;
  65. }
  66. }
  67. return 0;
  68. }
  69. }

这是生成游戏中平台的代码。我在舞台上有一个附有Box Collider并启用了Is Trigger Enter的物体,所以我在代码中使用了这个方法。也许代码中的某些问题导致Unity冻结。另外,你能告诉我如何使用相同的逻辑将平台向上移动,而不是删除和重新创建它们吗?

英文:

While I'm playing in a scene in Unity, the game freezes. I have this code:

  1. using UnityEngine;
  2. public class PlatformManager : MonoBehaviour
  3. {
  4. [SerializeField] private GameObject[] platformPrefabs;
  5. [SerializeField] private Transform player;
  6. [SerializeField] private float spawnDistance = 10f;
  7. [SerializeField] private float platformWidth = 4f;
  8. private float lastSpawnPositionY = -4.5f;
  9. private float lastSpawnPositionX = 0f;
  10. private void OnTriggerEnter2D(Collider2D collision)
  11. {
  12. Destroy(collision.gameObject);
  13. }
  14. private void Update()
  15. {
  16. if (player.position.y &gt; lastSpawnPositionY - spawnDistance)
  17. {
  18. SpawnPlatform();
  19. }
  20. }
  21. private void SpawnPlatform()
  22. {
  23. float randomX1 = GetRandomXPosition();
  24. float randomX2 = GetRandomXPosition();
  25. float randomY1 = lastSpawnPositionY + Random.Range(1.5f, 2.7f);
  26. float randomY2 = randomY1 + Random.Range(0.2f, 0.5f);
  27. while (Mathf.Abs(randomX1 - randomX2) &lt; platformWidth)
  28. {
  29. randomX2 = GetRandomXPosition();
  30. }
  31. Vector2 spawnPosition1 = new Vector2(randomX1, randomY1);
  32. Vector2 spawnPosition2 = new Vector2(randomX2, randomY2);
  33. int platformIndex = GetRandomPlatformIndex();
  34. GameObject platformPrefab = platformPrefabs[platformIndex];
  35. Instantiate(platformPrefab, spawnPosition1, Quaternion.identity);
  36. Instantiate(platformPrefabs[0], spawnPosition2, Quaternion.identity);
  37. lastSpawnPositionY = Mathf.Max(randomY1, randomY2);
  38. lastSpawnPositionX = Mathf.Max(randomX1, randomX2);
  39. }
  40. private float GetRandomXPosition()
  41. {
  42. float randomX = Random.Range(-7f, 7f);
  43. while (Mathf.Abs(randomX - lastSpawnPositionX) &lt; platformWidth)
  44. {
  45. randomX = Random.Range(-7f, 7f);
  46. }
  47. return randomX;
  48. }
  49. private int GetRandomPlatformIndex()
  50. {
  51. float[] probabilities = { 0.1f, 0.05f, 0.03f, 0.1f, 0.05f };
  52. float totalProbability = 0f;
  53. foreach (float probability in probabilities)
  54. {
  55. totalProbability += probability;
  56. }
  57. float randomValue = Random.Range(0f, totalProbability);
  58. float cumulativeProbability = 0f;
  59. for (int i = 0; i &lt; probabilities.Length; i++)
  60. {
  61. cumulativeProbability += probabilities[i];
  62. if (randomValue &lt;= cumulativeProbability)
  63. {
  64. return i;
  65. }
  66. }
  67. return 0;
  68. }
  69. }

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 循环。如果随机生成永远无法满足退出条件怎么办?

更新

在我之前的回答中,我犯了一个错误的假设。我以为新的随机位置应该在前一个位置的范围内,从 -77。但实际上这是你的绝对范围。

所以实际上,对于第一个 randomX1,有两个潜在的范围

  • 从前一个位置的左侧 platformWidth 单位到 -7f 的范围
  • 从前一个位置的右侧 platformWidth 单位到 7f 的范围

限制在于只有在范围实际上大于 platformWidth 时才使用范围。

这里有一些图像,以更好地理解下面的代码

如何在2D场景中防止Unity在游戏过程中冻结?
如何在2D场景中防止Unity在游戏过程中冻结?

所以类似于:

  1. var minPointRange1a = -7f;
  2. var maxPointRange1a = lastSpawnPositionX - platformWidth;
  3. var sizeRange1a = maxPointRange1a - minPointRange1a;
  4. var range1a = Random.Range(minPointRange1a, maxPointRange1a);
  5. var minPointRange1b = lastSpawnPositionX + platformWidth;
  6. var maxPointRange1b = 7f;
  7. var sizeRange1b = maxPointRange1b - minPointRange1b;
  8. var range1b = Random.Range(minPointRange1b, maxPointRange1b);
  9. // 如果只有一个选项?
  10. float randomX1;
  11. if (sizeRange1a > 0 && sizeRange1b <= 0)
  12. {
  13. randomX1 = range1a;
  14. }
  15. else if (sizeRange1a <= 0)
  16. {
  17. randomX1 = range1b;
  18. }
  19. else
  20. {
  21. // 如果两者都可能,根据选项之间的大小分布选择第一个或第二个选项
  22. randomX1 = Random.value <= sizeRange1a / (sizeRange1a + sizeRange1b) ? range1a : range1b;
  23. }

注意:Random.value 基本上等于 Random.Range(0f, 1f)

然后对于第二个值,现在更加棘手,因为现在你已经有两个你希望与之保持一定距离的点,所以你可能需要在对象中放置多达三个范围。

如何在2D场景中防止Unity在游戏过程中冻结?
如何在2D场景中防止Unity在游戏过程中冻结?

所以我们只需要继续检查这些范围和边缘情况

  1. var minPointRange2a = -7f;
  2. var maxPointRange2a = Mathf.Min(lastSpawnPositionX, randomX1) - platformWidth;
  3. var sizeRange2a = maxPointRange2a - minPointRange2a;
  4. var range2a = Random.Range(minPointRange2a, maxPointRange2a);
  5. var minPointRange2b = Mathf.Min(lastSpawnPositionX, randomX1) + platformWidth;
  6. var maxPointRange2b = Mathf.Max(lastSpawnPositionX, randomX1) - platformWidth;
  7. var sizeRange2b = maxPointRange2b - minPointRange2b;
  8. var range2b = Random.Range(minPointRange2b, maxPointRange2b);
  9. var minPointRange2c = Mathf.Max(lastSpawnPositionX, randomX1) + platformWidth;
  10. var maxPointRange2c = 7f;
  11. var sizeRange2c = maxPointRange2c - minPointRange2c;
  12. var range2c = Random.Range(minPointRange2c , maxPointRange2c);
  13. // 再次分布在这些范围之间(肯定还有更优雅的解决方案 ^^)
  14. var cases = 0;
  15. if (sizeRange2a > 0) cases += 1;
  16. if (sizeRange2b > 0) cases += 2;
  17. if (sizeRange2c > 0) cases += 4;
  18. float randomX2;
  19. switch(cases)
  20. {
  21. case 1: // 只有 range2a 有效
  22. randomX2 = range2a;
  23. break;
  24. case 2: // 只有 range2b 有效
  25. randomX2 = range2b;
  26. break;
  27. case 4: // 只有 range2c 有效
  28. randomX2 = range2c;
  29. break;
  30. case 3: // range2a 和 range2b 都有效
  31. randomX2 = Random.value > sizeRange2a / (sizeRange2a + sizeRange2b) ? range2b : range2a;
  32. break;
  33. case 5: // range2a 和 range2c 都有效
  34. randomX2 = Random.value > sizeRange2a / (sizeRange2a + sizeRange2c) ? range2c : range2a;
  35. break;
  36. case 6: // range2b 和 range2c 都有效
  37. randomX2 = Random.value > sizeRange2b / (sizeRange2b + sizeRange2c) ? range2c : range2b;
  38. break;
  39. case 7: // 三个都有效
  40. var rnd = Random.value;
  41. randomX2 = rnd <= sizeRange2a / (sizeRange2a + sizeRange2b + sizeRange2c) ? range2a : rnd <= sizeRange2b / (sizeRange2a + sizeRange2b + sizeRange2c) ? range2b : range2c;
  42. break;
  43. default:
  44. throw new OperationInvalidException("无法找到 randomX2 的任何有效范围!");
  45. }

这可能可以改进,希望有更优雅的解决方案,只有在实际有效且需要时才执行 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 to 7f

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

如何在2D场景中防止Unity在游戏过程中冻结?
如何在2D场景中防止Unity在游戏过程中冻结?

So something like e.g.

  1. var minPointRange1a = -7f;
  2. var maxPointRange1a = lastSpawnPositionX - platformWidth;
  3. var sizeRange1a = maxPointRange1a - minPointRange1a;
  4. var range1a = Random.Range(minPointRange1a, maxPointRange1a);
  5. var minPointRange1b = lastSpawnPositionX + platformWidth;
  6. var maxPointRange1b = 7f;
  7. var sizeRange1b = maxPointRange1b - minPointRange1b;
  8. var range1b = Random.Range(minPointRange1b, maxPointRange1b);
  9. // Is there only one option anyway?
  10. float randomX1;
  11. if(sizeRange1a &gt; 0 &amp;&amp; sizeRange1b &lt;= 0)
  12. {
  13. randomX1 = range1a;
  14. }
  15. else if(sizeRange1a &lt;= 0)
  16. {
  17. randomX1 = range1b;
  18. }
  19. else
  20. {
  21. // are both possible take either the first or second option according to the size distribution between the options
  22. randomX1 = Random.value &lt;= sizeRange1a / (sizeRange1a + sizeRange1b) ? range1a : range1b;
  23. }

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

如何在2D场景中防止Unity在游戏过程中冻结?
如何在2D场景中防止Unity在游戏过程中冻结?

so we just have to continue checking those ranges and edge cases

  1. var minPointRange2a = -7f;
  2. var maxPointRange2a = Mathf.Min(lastSpawnPositionX, randomX1) - platformWidth;
  3. var sizeRange2a = maxPointRange2a - minPointRange2a;
  4. var range2a = Random.Range(minPointRange2a, maxPointRange2a);
  5. var minPointRange2b = Mathf.Min(lastSpawnPositionX, randomX1) + platformWidth;
  6. var maxPointRange2b = Mathf.Max(lastSpawnPositionX, randomX1) - platformWidth;
  7. var sizeRange2b = maxPointRange2b - minPointRange2b;
  8. var range2b = Random.Range(minPointRange2b, maxPointRange2b);
  9. var minPointRange2c = Mathf.Max(lastSpawnPositionX, randomX1) + platformWidth;
  10. var maxPointRange2c = 7f;
  11. var sizeRange2c = maxPointRange2c - minPointRange2c;
  12. var range2c = Random.Range(minPointRange2c , maxPointRange2c);
  13. // again distributing amongst these (certainly leaving a lot potential for improvement here ^^)
  14. var cases = 0;
  15. if (sizeRange2a &gt; 0) cases += 1;
  16. if (sizeRange2b &gt; 0) cases += 2;
  17. if (sizeRange2c &gt; 0) cases += 4;
  18. float randomX2;
  19. switch(cases)
  20. {
  21. case 1: // only range2a is valid
  22. randomX2 = range2a;
  23. break;
  24. case 2: // only range2b is valid
  25. randomX2 = range2b;
  26. break;
  27. case 4: // only range2c is valid
  28. randomX2 = range2c;
  29. break;
  30. case 3: // range2a and range2b are valid
  31. randomX2 = Random.value &gt; sizeRange2a / (sizeRange2a + sizeRange2b) ? range2b : range2a;
  32. break;
  33. case 5: // range2a and range2c are valid
  34. randomX2 = Random.value &gt; sizeRange2a / (sizeRange2a + sizeRange2c) ? range2c : range2a;
  35. break;
  36. case 6: // range2b and range2c are valid
  37. randomX2 = Random.value &gt; sizeRange2b / (sizeRange2b + sizeRange2c) ? range2c : range2b;
  38. break;
  39. case 7: // all three are valid
  40. var rnd = Random.value;
  41. randomX2 = rnd &lt;= sizeRange2a / (sizeRange2a + sizeRange2b + sizeRange2c) ? range2a : rnd &lt;= sizeRange2b / (sizeRange2a + sizeRange2b + sizeRange2c) ? range2b : range2c;
  42. break;
  43. default:
  44. throw new OperationInvalidException(&quot;Was not able to find any valid range for randomX2!&quot;);
  45. }

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

huangapple
  • 本文由 发表于 2023年6月1日 21:11:30
  • 转载请务必保留本文链接:https://go.coder-hub.com/76382262.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定