【问题标题】:Spawning the desired number of prefabs产生所需数量的预制件
【发布时间】:2020-01-11 06:53:01
【问题描述】:

如何使用以下代码实例化所需数量的预制件?我需要生成一个(并且只有一个)玩家预制件、X 个敌人和一个(并且只有一个)最终游戏预制件。

    private void GenerateEnemies(int xMax, int zMax)
    {
        GameObject landscape = new GameObject("ENEMIES");


        for (int z = 0; z < zMax; z++)
        {
            for (int x = 0; x < xMax; x++)
            {
                randNum = Random.Range(0, 100);

                if (randNum < 10 )
                {
                    Instantiate(enemy1, new Vector3(x * 10, 0, z * 10), Quaternion.Euler(0, 0, 0));//, landscape.transform);
                }
                else if (randNum < 20)
                {
                    Instantiate(enemy2, new Vector3(x * 10, 0, z * 10), Quaternion.Euler(0, 0, 0));//, landscape.transform);
                }

                else if (randNum < 30)
                {
                    Instantiate(enemy3, new Vector3(x * 10, 0, z * 10), Quaternion.Euler(0, 0, 0));//, landscape.transform);
                }

                else if (randNum < 40)
                {
                    Instantiate(enemy4, new Vector3(x * 10, 0, z * 10), Quaternion.Euler(0, 0, 0));//, landscape.transform);
                }

                else if (randNum < 50)
                {
                    Instantiate(enemy5, new Vector3(x * 10, 0, z * 10), Quaternion.Euler(0, 0, 0));//, landscape.transform);
                }

            }
        }
    }

【问题讨论】:

  • Random.Range(0, 100) 与 if-block-chain 仅处理小于 50 的值。您期望什么?这样的 if-block-chain 也不是好的代码,有更好的方法可以做到这一点(例如发布的随机索引 derHugo 或 Weighted List system)。

标签: c# list unity3d spawn


【解决方案1】:

只要在循环之外做你一次性的事情!

还有

randNum = Random.Range(0, 100);

然后你只使用 5 种不同的情况,并且只有当值更小 50 时(所以大约有一半的情况根本没有发生任何事情......)。如果这是有意的.. ok-ish .. 否则我宁愿使用列表和随机索引:

// HINT: Rather have a list for your prefabs
// this shrinks your code a lot
public List<GameObject/*or whatever type*/> eneymPrefabs = new List<GameObject>();
public Gamebject playerPrefab;
public GameObject endGamePrefab;

private void GenerateEnemies(int xMax, int zMax)
{
    var landscape = new GameObject("ENEMIES");

    // Do these only once
    // store the references in case you need them later
    var player = Instantiate(playerPrefab);
    var endGame = Instantiate(endGamePrefab);

    for (int z = 0; z < zMax; z++)
    {
        for (int x = 0; x < xMax; x++)
        {
            // simply pick a random index from the prefab list
            int randIndex = Random.Range(0, eneymPrefabs.Count);

            // and get the according random prefab
            var enemyPrefab = enemyPrefabs[randIndex];

            if(enemyPrefab) Instantiate(enemyPrefab, new Vector3(x * 10, 0, z * 10), Quaternion.identity /*, landscape.transform*/);
        }
    }
}

或者Draco18s提到的加权列表的例子

[Serializable]
public class WeightedPrefab
{
    public GameObject Prefab;
    public int Weight = 1;
}

public List<WeightedPrefab> weightedEnemyPrefabs;
public Gamebject playerPrefab;
public GameObject endGamePrefab;

private void GenerateEnemies(int xMax, int zMax)
{
    // create a temp list using the weights and random index on this one
    var enemyPrefabs = new List<GameObject>();
    foreach(var item in weightedEnemyPrefabs)
    {
        for(var i = 0; i < item.Weight; i++)
        {
            enemyPrefabs.Add(item.Prefab);
        }
    }

    // Rest stays the same

    var landscape = new GameObject("ENEMIES");

    // Do these only once
    // store the references in case you need them later
    var player = Instantiate(playerPrefab);
    var endGame = Instantiate(endGamePrefab);

    for (int z = 0; z < zMax; z++)
    {
        for (int x = 0; x < xMax; x++)
        {
            // simply pick a random index from the prefab list
            int randIndex = Random.Range(0, eneymPrefabs.Count);

            // and get the according random prefab
            var enemyPrefab = enemyPrefabs[randIndex];

            if(enemyPrefab) Instantiate(enemyPrefab, new Vector3(x * 10, 0, z * 10), Quaternion.identity /*, landscape.transform*/);
        }
    }
}

如果不是在每种情况下都实例化敌人是故意的,您仍然可以使用这两种方法并将预制参考留空 &rightarrow;对于该索引,不会实例化任何内容。


敌人和最终游戏应该是网格的一部分

在这种情况下,我会首先将整个网格组合写入一个列表。从这个列表中随机选择两个条目,然后将 player 和 endGame 放在那里。然后挡住这两个格子位置,不要在那里生成敌人:

[Serializable]
public class WeightedPrefab
{
    public GameObject Prefab;
    public int Weight = 1;
}

public List<WeightedPrefab> weightedEnemyPrefabs;
public Gamebject playerPrefab;
public GameObject endGamePrefab;

private void GenerateEnemies(int xMax, int zMax)
{
    // Create a list of all awailable grid positions
    var gridPositions = new List<Vector2Int>();
    for (int z = 0; z < zMax; z++)
    {
        for (int x = 0; x < xMax; x++)
        {
            gridPositions.Add(new Vector2Int(x,z));
        }
    }

    // pick the two random positions for player and endgame
    var playerPosIndex = Random.Range(0, gridPositions.Count);
    var playerPos = gridPositions[playerPosIndex];
    gridPositions.RemoveAt(playerPosIndex);

    var endGamePosIndex = Random.Range(0, gridPositions.Count);
    var endGamePos = gridPositions[endGamePosIndex];
    gridPositions.RemoveAt(endGamePosIndex);

    // create a temp list using the weights and random index on this one
    var enemyPrefabs = new List<GameObject>();
    foreach(var item in weightedEnemyPrefabs)
    {
        for(var i = 0; i < item.Weight; i++)
        {
            enemyPrefabs.Add(item.Prefab);
        }
    }

    var landscape = new GameObject("ENEMIES");

    // Do these only once
    // store the references in case you need them later
    var player = Instantiate(playerPrefab, new Vector3(payerPos.x * 10, 0, playerPos.y * 10), Quaternion.identity /*, landscape.transform*/);
    var endGame = Instantiate(endGamePrefab, new Vector3(endGamePos.x * 10, 0, endGamePos.y * 10), Quaternion.identity /*, landscape.transform*/);

    for (int z = 0; z < zMax; z++)
    {
        for (int x = 0; x < xMax; x++)
        {
            // Now simply ignore the playerPos and endGamePos
            if(x == playerPos.x && z == playerPos.y) continue;
            if(x == endGamePos.x && z == endGamePos.y) continue;

            // pick a random index from the prefab list
            int randIndex = Random.Range(0, eneymPrefabs.Count);

            // and get the according random prefab
            var enemyPrefab = enemyPrefabs[randIndex];

            // do nothing if enemyPrefab is null otherwise instantiate
            if(enemyPrefab) Instantiate(enemyPrefab, new Vector3(x * 10, 0, z * 10), Quaternion.identity /*, landscape.transform*/);
        }
    }
}

【讨论】:

  • 感谢您的回复。我不喜欢使用列表方法,我已经尝试过了,但后来我意识到这是我循环的一种重复,因为我使用 xMax 和 zMax 系统来生成我的列表和特殊定位(如果我'我错了)。例如,如果我使用建议的 weightedEnemyPrefabs 和 foreach 方法,我如何将它与我的 xMax, zMax 循环集成,该循环已经做了一个列表?
  • 抱歉,我不明白您对 List 方法的担忧。正如所评论的那样,我们并没有真正理解为什么您要使用 0-99 之间的随机范围,然后只有 &lt; 50 ... xMax, zMax 的情况我不会称其为列表,而是网格。据我了解,您希望在每个网格点上生成一个敌方对象。或者你的意思是 player 和 endGame 也应该是这个网格的一部分?
  • 敌人和最终游戏应该是网格的一部分,是的。那是因为我使用相同的 xMax、zMax 来生成我的游戏关卡,方法是在每个位置生成地砖。我会尝试你的方法并让你知道
  • 我猜你的意思是玩家和 endGame,因为敌人已经在网格中;)
  • 大声笑,是的,对不起。我在游戏中尝试了脚本,我看到如果玩家作为预制件生成,它会丢失所有其他脚本(未直接附加到它),例如 Xp tetxt、生命点文本等。所以我想我不会将播放器生成为预制件。我会找到另一个解决方案
【解决方案2】:

考虑这段代码:

 for (int z = 0; z < zMax; z++)
 {
   for (int x = 0; x < xMax; x++)
   {
     randNum = Random.Range(1, 11);

     if (randNum == 1) Instantiate(enemy1, new Vector3(x * 10, 0, z * 10), Quaternion.Euler(0, 0, 0));//, landscape.transform);
     if (randNum == 2) Instantiate(enemy2, new Vector3(x * 10, 0, z * 10), Quaternion.Euler(0, 0, 0));//, landscape.transform);
     if (randNum == 3) Instantiate(enemy3, new Vector3(x * 10, 0, z * 10), Quaternion.Euler(0, 0, 0));//, landscape.transform);
     if (randNum == 4) Instantiate(enemy4, new Vector3(x * 10, 0, z * 10), Quaternion.Euler(0, 0, 0));//, landscape.transform);
     if (randNum == 5) Instantiate(enemy5, new Vector3(x * 10, 0, z * 10), Quaternion.Euler(0, 0, 0));//, landscape.transform);
   }
 }

应该清楚,这段代码和你发布的代码做的事情是一样的。

还应该清楚这更好,因为它总是以相同的速度运行(在游戏中让它随机运行更慢通常不好)

【讨论】:

  • It should also be clear this is better because it will always run at the same speed .. 怎么样 .. 这比 OP 的代码快在哪里?您可能想使用== 进行比较? switch-case 在这里会表现得更好。还要注意Random.Range 的第二个参数是int 独有的,所以它返回值0-9 而在OP 的代码中它是0-99 .. 这给出的概率略有不同
  • @derHugo -- 好点 -- 我的意思是这将始终以相同的速度运行 -- 并不是说​​它会运行得更快。最快的是查找数组,而不是开关。
猜你喜欢
  • 2019-01-01
  • 1970-01-01
  • 2020-05-30
  • 1970-01-01
  • 1970-01-01
  • 2018-06-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多