Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/265.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/unity3d/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 使用具有不同返回类型的协同程序_C#_Unity3d_Coroutine - Fatal编程技术网

C# 使用具有不同返回类型的协同程序

C# 使用具有不同返回类型的协同程序,c#,unity3d,coroutine,C#,Unity3d,Coroutine,在这个函数中,我想得到一个介于0和列表最大大小之间的随机索引 然后我使用该随机索引,以便在列表中选择一个随机节点 我遍历if语句,检查其他对象是否未使用我选择的随机节点 如果没有其他对象使用该随机节点,我将返回该节点,以便调用此方法的对象可以使用它 但是,如果该节点当前正被另一个对象使用,则我不想再次执行该函数,直到它得到一个可以使用的节点,所以我返回该函数本身 结果是一个溢出错误,因为它被无限调用(游戏仍然有效)。我的第一个想法是使用延迟(协程),这样函数就不会被频繁调用;但问题是我需要返回类

在这个函数中,我想得到一个介于0和列表最大大小之间的随机索引

然后我使用该随机索引,以便在列表中选择一个随机节点

我遍历if语句,检查其他对象是否未使用我选择的随机节点

如果没有其他对象使用该随机节点,我将返回该节点,以便调用此方法的对象可以使用它

但是,如果该节点当前正被另一个对象使用,则我不想再次执行该函数,直到它得到一个可以使用的节点,所以我返回该函数本身

结果是一个溢出错误,因为它被无限调用(游戏仍然有效)。我的第一个想法是使用延迟(协程),这样函数就不会被频繁调用;但问题是我需要返回类型DodgeNode

public class DodgeLocations : MonoBehaviour {
public List<DodgeNode> nodes;

private DodgeNode randomNode;
private int randomIndex;

public DodgeNode SelectRandomNode(){
    randomIndex = Random.Range(0, nodes.Count);     
    randomNode = nodes[randomIndex];                

    // If the random node is not currently taken (which means if an enemy isn't currently attacking it)
    if (!randomNode.IsTaken ()) {
        // Then the random node is now taken; and other enemies can't touch that node, until the current enemy finishes attacking it    
        randomNode.IsTaken (true);
        return randomNode;
    } else {
        return SelectRandomNode (); // If the node is taken, ask again if there's another node that's free to attack
    }
}
}


找到了我的“解决方案”

免责声明:我是一名初级程序员/学生

我拿了一张纸,试着写下我所有的思考过程,最后得到了另一个“解决方案”。我没有尝试在SelectRandomNode()中调用WaitForSeconds,而是决定在所有节点都被占用时,使SelectRandomNode()返回null。在IEnumerator AttackPlayerNode()中,我有以下代码:

// If the bat doesn't have a node to target
            while(nodeToTarget == null){
                yield return new WaitForSeconds(0.5f);
                nodeToTarget = dodgeLocations.SelectRandomNode();
            }  
由于我返回null,这个while循环将一直运行,直到节点打开为止。这仍然会产生溢出错误(我应该这样认为),但我现在使用的是WaitForSeconds,这将减少对打开节点的检查频率,从而防止溢出错误(据我所知)

这可能是一个非常难看/临时的解决方案,但我可以在将来随时回去进行优化!这困扰了我整整一天,我很高兴我现在可以专注于我比赛的其他元素

public class DodgeLocations : MonoBehaviour {

public List<DodgeNode> nodes;

private DodgeNode randomNode;

// Returns a randomly chosen node
public DodgeNode SelectRandomNode(){
    int randomIndex = Random.Range(0, nodes.Count);    
    randomNode = nodes[randomIndex];            

    if (!randomNode.isTaken) {
        randomNode.IsTaken (true);
        return randomNode;
    } else {
        return null; // <--- What was changed
    }
}

正如你可能已经怀疑的那样,你只是做错了

使用递归进行简单循环是错误的。在最坏的情况下,您的方法应该如下所示:

public DodgeNode SelectRandomNode(){

    while (true)
    {
        randomIndex = Random.Range(0, nodes.Count);
        randomNode = nodes[randomIndex];                

        // If the random node is not currently taken (which means if an enemy isn't currently attacking it)
        if (!randomNode.IsTaken ()) {
            // Then the random node is now taken; and other enemies can't touch that node, until the current enemy finishes attacking it    
            randomNode.IsTaken (true);
            return randomNode;
        }
    }
}
最好在开始随机拾取合格节点之前确定它们:

public DodgeNode SelectRandomNode(){
    DodgeNode[] eligible = nodes.Where(n => !n.IsTaken()).ToArray();

    randomIndex = Random.Range(0, eligible.Length);
    randomNode = nodes[randomIndex];
    randomNode.IsTaken(true);

    return randomNode;
}
当然,如果可能没有任何符合条件的节点,您需要适当地处理这种情况。你的问题不清楚在这种情况下什么是“合适的”

从您提供的简单示例中不清楚为什么要将
randomIndex
存储为实例字段而不是局部变量。如果确实需要将其作为相对于原始集合的索引,则需要做更多的工作来跟踪原始索引(请参阅将索引与枚举项一起传递的
Select()
重载)。但基本思想是一样的

如果您不希望每次需要选择节点时都重新创建
qualified
数组,那么您应该维护两个集合:“not take”集合和“take”集合。然后根据需要将节点从一个移动到另一个。如果这些集合相对较小(数百项,或者可能只有数千项),则它们可以是常规的
列表
对象。较大的集合删除元素的成本可能较高(由于移动了剩余的元素),在这种情况下,您可能更喜欢使用
LinkedList


旁白:您似乎用两个重载声明了
IsTaken()
,一个用于返回当前值,另一个用于设置当前值。这是拙劣的设计。理想情况下,它应该只是一个属性,因此可以省略方法调用所需的
()
。如果某个属性由于某种原因不适用于您,那么设置方法应该有不同的名称,如
SetIsTaken()

我找到了另一个“解决方案”,并编辑了上面的文章


CTRL+F以下粗体文本:找到了我的“解决方案”

您计划在哪里使用返回的
DodgeNode
?我在上面添加了使用DodgeNode的位置。希望我没有漏掉任何东西(必须删除很多不相关的代码,这样它就不会被聚集)。嗯,在现实生活中,袋子里的东西是通过从袋子里拿出来的来取旗的。因此,我觉得这个列表听起来更自然:o)作为一个新手程序员,我非常感谢您对最佳实践的建议,并将记住所有这些(并对我难看的代码进行了必要的更改)!当我开始深入思考时,循环的递归非常愚蠢!你应该把你的答案编辑成你的答案——不要把它放在你的问题正文里,因为人们不会在那里寻找它。
public class Bat : Enemy {

private DodgeNode nodeToTarget;               // Node that bat want's to attack
private Vector3 startPoint;                   // Bat's original position
private Vector3 endPoint;                     // Bat's end position

void Start(){
    startCoroutine(AttackPlayerNode());
}

IEnumerator AttackPlayerNode(){
    while (true) {
        // If the bat doesn't have a node to target
        while(nodeToTarget == null){
            yield return new WaitForSeconds(0.5f); // Prevent overflow error
            nodeToTarget = dodgeLocations.SelectRandomNode1();
        }  
        endPoint = nodeToTarget.transform.position;   
        yield return new WaitForSeconds (2f);
        yield return StartCoroutine(MoveToPoint(startPoint, endPoint));            
        nodeToTarget.IsFree(); // This makes the Node free for other object to use it
        nodeToTarget = null;
    }
}
public DodgeNode SelectRandomNode(){

    while (true)
    {
        randomIndex = Random.Range(0, nodes.Count);
        randomNode = nodes[randomIndex];                

        // If the random node is not currently taken (which means if an enemy isn't currently attacking it)
        if (!randomNode.IsTaken ()) {
            // Then the random node is now taken; and other enemies can't touch that node, until the current enemy finishes attacking it    
            randomNode.IsTaken (true);
            return randomNode;
        }
    }
}
public DodgeNode SelectRandomNode(){
    DodgeNode[] eligible = nodes.Where(n => !n.IsTaken()).ToArray();

    randomIndex = Random.Range(0, eligible.Length);
    randomNode = nodes[randomIndex];
    randomNode.IsTaken(true);

    return randomNode;
}