2017-01-12 10 views
2

この関数では、0とリストの最大サイズの間のランダムなインデックスを取得したかったのです。戻り値の異なるコルーチンの使用

ランダムなインデックスを使用して、のノードをランダムに選ぶことができます。

私は、if文を通過し、私が選んだそのランダムノードを使用していない他オブジェクトかどうかを確認します。

オブジェクトは、ランダムノードを使用していない場合は、私は、このメソッドを呼び出したオブジェクトはそれを使用することができますノードことを返します。そのノードは現在、別のオブジェクトで使用されている場合

しかし、その後、私はそれはそれは使用することができますノードを取得するまで、再び機能を通過するwan'tので、私は関数自体を返します。

無限に呼び出される(ゲームはまだ機能している)ので、結果はオーバーフローエラーです。私の最初の考えは遅れ(コルーチン)を使うことだったので、関数はあまり頻繁に呼び出されません。問題は、返品タイプ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 
    } 
} 

}

私はコルーチンを使用して、私はこのことについて考えた場合、それがどのように見えるだろうかブレインストーミング。もちろん

public class DodgeLocations : MonoBehaviour { 

public List<DodgeNode> nodes; 

private DodgeNode randomNode; 
private int randomIndex; 

IEnumerator SelectRandomNode(){ 
    yield return new WaitForSeconds(2f); 
    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); 
     yield return randomNode; 
    } else { 
     yield return SelectRandomNode(); // If the node is taken, ask again if there's another node that's free to attack 
    } 
} 

}

私はのIEnumeratorの戻り値の型を持つ関数を持つタイプDodgeNodeの何かを返すことができないので、これは間違っています。しかし、私はまだWaitForSecondsを使用したいので、IEnumerator戻り値の型が必要です。

ここでの問題は、私はDodgeNodeを返すようにしたいということですが、同じで、私はWaitForSeconds動作するのための戻り値の型のIEnumeratorをする機能を必要としています。Serliteに対応して


、私はこの機能を使用していところですが(無関係な多くのコードを削除):私の "ソリューション" を発見

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) { 
     nodeToTarget = dodgeLocations.SelectRandomNode(); 
     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 
    } 
} 

}


免責事項:私は初心者プログラマー/学生です

私は紙を手に入れて、思考のプロセスをすべて書き出しようとしたが、代わりの "解決策"で終わった。むしろSelectRandomNode()WaitForSecondsを呼び出そうとしようとするよりも、私はすべてのノードが占有された場合SelectRandomNode()リターンヌルを作ることにしました。 のIEnumerator AttackPlayerNode()では、私はこのコードを持っている:

// If the bat doesn't have a node to target 
      while(nodeToTarget == null){ 
       yield return new WaitForSeconds(0.5f); 
       nodeToTarget = dodgeLocations.SelectRandomNode(); 
      } 

私はヌルを返していますので、このwhileループはが開いているノードまで続けるだろう。これはまだオーバーフローエラー(私は思うはずです)を生成しますが、今はWaitForSecondsを使用しています。これはオープンノードのチェック頻度を減らし、オーバーフローエラーを防止します。

これはおそらく非常に醜い/一時的な解決策ですが、私はいつでも将来最適化のために戻ることができます!これは1日中私を忘れさせていました。私は今、ゲームの他の要素に集中することができてうれしいです。あなたはおそらくすでに疑わたよう

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 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; 
    } 
} 
+0

私の "ソリューション" を発見されましたか? – Serlite

+0

上記のDodgeNodeを使用している箇所を追加しました。私は何も残していないことを願っています(無関係のコードをたくさん削除して、クラスタ化されていないようにしなければなりませんでした)。 – Junycode

答えて

0

代わりの "解決策"が見つかりました。私の投稿を編集しました。

CTRL + F太字に次のテキスト:あなたが返さ `DodgeNode`を使用する予定です

+2

答えを実際に編集して答えに入れるべきです。あなたの質問の本文にそれを置かないでください。なぜなら、人々はそこを探していないからです。 – Serlite

1

、あなたは単にそれが間違ってやっています。

単純なループで再帰を使うのは間違っています。最悪の場合、あなたの方法ではなく、次のようになります。当然のことながら

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; 
} 

、それはそこに可能かどう:

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; 
     } 
    } 
} 

ベター前にランダムに選んで起動する適格ノードを識別することです適格なノードではない場合は、そのケースを適切に処理する必要があります。あなたの質問から、「適切な」がこのシナリオになるのは明らかではありません。

ローカル変数の代わりにrandomIndexをインスタンスフィールドとして保存する理由を明示していません。元のコレクションに対するインデックスであることが本当に必要な場合は、元のインデックスを追跡するためにもう少し作業を行う必要があります(Select()インデックスを列挙アイテムと共に渡すオーバーロードを参照)。しかし基本的な考え方は同じです。「取らない」収集、および「とら」コレクション:あなたはむしろeligible配列にあなたがノードを選択する必要があるたびに再作成たくない場合

、あなたは2つのコレクションを維持する必要があります。次に、必要に応じてノードを一方から他方に移動するだけです。これらのコレクションが比較的小さい場合(数百、または数千のアイテムのみ)、これらは通常のオブジェクトであることがあります。List<T>コレクションを大きくすると要素を削除するコストが高くなる可能性があります(残りの要素が移動するため)LinkedList<T>を使用することをお勧めします。別に


:あなたは、現在の値、およびそれを設定するには1を返すために、2つのオーバーロード、1でIsTaken()を宣言しているように見えます。これは貧弱なデザインです。理想的には、プロパティでなければならないので、メソッドの呼び出しに必要な()は除外できます。何らかの理由でプロパティがうまく動作しない場合は、設定方法に別の名前が必要です(SetIsTaken()

+0

ええと、実際の生活では、袋の中にあるものは、それを袋から出して、その旗を得ました。だから、私のリストはもっと自然に聞こえます:o) –

+0

初心者プログラマーとして、ベストプラクティスのアドバイスをいただきありがとうございます。また、これをすべて念頭に置いてください(そして、醜いコードに必要な変更を加えました)!ループの再帰は私が一層深く考えるようになったら、とてもばかげていました! – Junycode

関連する問題