Jump to content
Unity Insider Forum

Teil 3: A* Pathfinder


Recommended Posts

Also dir fehlen einfach noch einige grundlegende Kenntnisse in C# sonst wären dir die Fehler selbst aufgefallen... das ist net schlimm so lernt man daraus :)

es ist so... wenn du in C# eine ArrayList hast dann ist der Typ der Elemente in der Liste ja unklar, denn eine ArrayList kann vom Integer bis zu einem komplizierten Objekt alles aufnehmen.

wenn du jetzt auf ein Objekt in dieser Liste in C# zugreifen möchtest, musst du vorher sagen welches Objekt du erwartest.

 

kurzes beispiel, du möchtest aus der Liste openList auf ein element zugreifen und dieses einer Variable vom Typ PathNode zuweisen:

PathNode pNode = openList[i]; // FALSCH

PathNode pNode = (PathNode)openList[i]; //RICHTIG


das nennt man auch "casten" was man da machen muss!

 

wenn du jetzt auf eine Eigenschaft eines objektes in einer ArrayListe zugreifen möchtest, dann musst du das genau so machen:

int gValue = openList[i].gValue; //FALSCH

int gValue = ((PathNode)openList[i]).gValue;//RICHTIG

 

Dabei musst du auf eine korrekte Klammerung achten!!

 

alle Fehler waren ausschließlich im PathFinder. Ich häng dir die korrigierte Datei an, alle Fehler sind dementsprechend makiert!

 

Gruß Stephan

PathFinder.cs.zip

Link zu diesem Kommentar
Auf anderen Seiten teilen

Und wieder was neues gelernt. Ich war ganz schön naiv zu glauben, ich könnte als Anfänger mal eben an nem Pathfinding Script rumfummeln ohne das dabei völliger Murks bei rumkommt. :S Naja, jetzt funktioniert jedenfalls alles. Danke für deine Hilfe und die Zeit die du in die Korrektur investiert hast, Stephan.

 

Ich freu mich schon auf den 4. Teil dieser Tutorialreihe.

 

mfg Noah

Link zu diesem Kommentar
Auf anderen Seiten teilen

Ich muss dir leider sagen das ich momentan keine Zeit habe ein weiteres Tutorial zu machen, vlt. in einem monat oder später.. kann ich dir nicht sagen! Aber ich denke mit dem was du jetzt gelernt hast solltest du mit ein wenig Fantasy selbst in der Lage sein das System in ein Spiel ein zu bauen um es auf eine KI oder für was auch immer an zu wenden!

 

Wenn ich eins mache dann in C# obwohl das Tutorial in JavaScript geschrieben wurde, dazu werde ich dann aber erst nochmal das gesamte bisherige in C# ummünzen müssen!

Link zu diesem Kommentar
Auf anderen Seiten teilen

  • 2 months later...

Ich hab mir mal versucht dein Tutorial in C# umzuschreiben und hab auch das casten beachtet .... nur die Funktion welche den Pfad ausgibt bereitet mir Probleme....

Ich habe das in 3 Scripts unterteil das eine heist Waypoint, das ist diese hier

using UnityEngine;
using System.Collections;
public class Waypoint : MonoBehaviour
{
public static bool DebugMode = true;
public static Waypoint[] allWaypoints;
public Color DrawLineColor = new Color (1, 0, 0, 1);
public float MaxWaypointDistance = 5;
//Distanz zur Erkennung anderer Waypoints
public Waypoint[] neighbour;
// Use this for initialization
void Start ()
{
 GameObject[] allWaypointsGM = GameObject.FindGameObjectsWithTag ("Waypoint");
 allWaypoints = new Waypoint[allWaypointsGM.Length];

 for (int i = 0; i < allWaypointsGM.Length; i++) {
  allWaypoints[i] = allWaypointsGM[i].GetComponent<Waypoint> ();

 }
 CheckForNeighbours ();
}

// Update is called once per frame
void Update ()
{
 //Block zum Zeichen der Nachbarn
 if (neighbour.Length != 0 && DebugMode) {

  foreach (Waypoint gm in neighbour) {
   Debug.DrawLine (transform.position, gm.transform.position, DrawLineColor);


  }

 }
 //Ende des Nachbar Blocks


}
void CheckForNeighbours ()
{
 Vector3 myPos = gameObject.transform.position;
 //Alle Waypoints durchsuchen
 foreach (Waypoint testingObject in allWaypoints) {
  //überprüfung auf eigenen Punkt
  if (testingObject != this.gameObject.GetComponent<Waypoint> ()) {
   //vorbereitende Vereinfachung zur Berechnung
   Vector3 testingPos = testingObject.transform.position;
   float deltaL = Vector3.Distance (myPos, testingPos);
   //überprüfung innerhalb des Radius
   if (deltaL <= MaxWaypointDistance) {
 //überprüfung durch Raycast
 bool isWayFree = true;
 RaycastHit[] hits;
 hits = Physics.RaycastAll (myPos, Vector3.Normalize (testingPos - myPos), deltaL);
 for (int i = 0; i < hits.Length; i++) {
  if (!hits[i].collider.isTrigger) {
   isWayFree = false;
   break;

  }


 }
 if (isWayFree) {
  //wenn frei an Array Funktion übergeben
  AddNeighbour (testingObject);
 }


   }
  }


 }


}
void AddNeighbour (Waypoint waypoint)
{
 if (DebugMode) {
  print (this.gameObject.name + " hat " + waypoint.name + " gefunden");
 }
 if (neighbour.Length == 0) {
  neighbour = new Waypoint[1];
  neighbour[0] = waypoint;
 } else {
  Waypoint[] tempneighbour = neighbour;
  int lenght = neighbour.Length;
  neighbour = new Waypoint[lenght + 1];
  for (int i = 0; i < tempneighbour.Length; i++) {
   neighbour[i] = tempneighbour[i];

  }
  neighbour[lenght] = waypoint;
 }

}
}
public static class PathFinder
{
public static GameObject[] GetPath (Waypoint start, Waypoint target)
{

 ArrayList openList = new ArrayList ();
 ArrayList closedList = new ArrayList ();
 ArrayList path = new ArrayList();
 PathNode startField = (PathNode)ScriptableObject.CreateInstance<PathNode>();
 startField.waypoint = start;
 PathNode targetField = null;
 PathNode currentPathNode;

 openList.Add (startField);

 if (startField != targetField) {
  Debug.Log ("1 - if Schleife durchlaufen");
  while (targetField == null && openList.Count > 0) {

   Debug.LogError ("while Schleife wird ausgeführt");

   int index = GetNodeWithLowestF (openList);
   currentPathNode = (PathNode)(openList[index]);
   openList.RemoveAt (index);
   closedList.Add (currentPathNode);

   Waypoint[] currentNeighbours = currentPathNode.waypoint.neighbour;
   Debug.Log (currentNeighbours.Length);

   for (int i = 0; i < currentNeighbours.Length; i++) {
 if (!(IsInClosedList (closedList, currentNeighbours[i]))) {
  int indexInOpenList = IsInOpenList (openList, currentNeighbours[i]);
  if (indexInOpenList >= 0) {
   //wenn in der Openlist
   //Neu berechnen
   float newGvalue = currentPathNode.g_value + Vector3.Distance (currentPathNode.waypoint.transform.position, ((PathNode)openList[indexInOpenList]).waypoint.transform.position);
   if (newGvalue < ((PathNode)openList[indexInOpenList]).g_value) {
    ((PathNode)openList[indexInOpenList]).parentNode = currentPathNode;
    ((PathNode)openList[indexInOpenList]).g_value = newGvalue;
    ((PathNode)openList[indexInOpenList]).f_value = newGvalue + ((PathNode)openList[indexInOpenList]).h_value;
    Debug.Log ("pathnode neu Berechnet");


   }

  } else {
   //Wenn nicht in der Openlist
   //Neue pathnode erzeugen
   float gvalue = currentPathNode.g_value + Vector3.Distance (currentPathNode.waypoint.transform.position, currentNeighbours[i].transform.position);
   float hvalue = Vector3.Distance (currentNeighbours[i].transform.position, target.transform.position);
   float fvalue = gvalue + hvalue;
   PathNode npnd = (PathNode)ScriptableObject.CreateInstance<PathNode>();
   npnd.waypoint = currentNeighbours[i];
   npnd.g_value = gvalue;
   npnd.h_value = hvalue;
   npnd.f_value = fvalue;
   npnd.parentNode = currentPathNode;
   openList.Add (npnd);
   Debug.Log ("Pathnode erzeugt");
   if (target == npnd.waypoint) {
    //pfad ausgeben
    path = ReturnPath (startField, targetField);

    Debug.LogWarning ("Found Target");
    break;

   }

  }
  //if
 }
 //if
   }
   //for
  }
  //while
 }
 //if
 GameObject[] ret = new GameObject[path.Count];
 for (int i = 0; i < path.Count; i++) {
  ret[i] = ((PathNode)path[i]).waypoint.gameObject;
 }
 return ret;
}
private static ArrayList ReturnPath (PathNode startField, PathNode Targetfield)
{
 ArrayList liste = new ArrayList ();
 PathNode currendPathnode = Targetfield;
 while (currendPathnode != startField) {

  liste.Add (currendPathnode.waypoint);
  Debug.Log("writen ok");
  currendPathnode = currendPathnode.parentNode;
 }
 liste.Reverse ();





 return liste;
}
private static int GetNodeWithLowestF (ArrayList OpenList)
{
 PathNode node = null;
 int index = -1;
 if (OpenList.Count > 0) {
  for (int i = 0; i < OpenList.Count; i++) {

   if (index == -1) {
 node = (PathNode)OpenList[i];
 index = i;
   } else if (node.f_value > ((PathNode)OpenList[i]).f_value) {

 node = (PathNode)(OpenList[i]);
 index = i;
   }
  }
 }
 return index;


}
private static bool IsInClosedList (ArrayList closedList, Waypoint waypoint)
{
 bool ret = false;
 for (int i = 0;i<closedList.Count;i++){
  if(((PathNode)closedList[i]).waypoint == waypoint){
   ret = true;

  }


 }
 return ret;
}
private static int IsInOpenList (ArrayList openList, Waypoint waypoint)
{
 int index = -1;
 for (int i = 0; i < openList.Count; i++) {
  if (((PathNode)openList[i]).waypoint == waypoint) {
   index = i;
   break;
  }

 }
 return index;

}
}

Dann habe ich ein zweites welche die PathNode Klasse enthält(unity wollte für das Scriptable Objekt eine extra Datei)

using UnityEngine;
using System.Collections;
public class PathNode : ScriptableObject
{
public float f_value = 0;
public float g_value = 0;
public float h_value = 0;
public Waypoint waypoint;
public PathNode parentNode;
public PathNode (Waypoint wp)
{

 waypoint = wp;

}

}

 

und zuguter letzt das Script das dass testet

using UnityEngine;
using System.Collections;
public class testscript : MonoBehaviour {
public Waypoint start;
public Waypoint target;
public GameObject[] path;
// Use this for initialization
void Start () {


 }

// Update is called once per frame
void Update () {
if(Input.GetKeyDown(KeyCode.P)){

  path = PathFinder.GetPath(start,target);

 }
}

}

 

 

Unity meint ich hätte beim verfolgen des Pfads sofort eine NullReferenceException......ich hoffe ihr könnt mir helfen

 

Gruß Jonas

Link zu diesem Kommentar
Auf anderen Seiten teilen

  • 10 months later...

Hey top Tutorial! :D

 

Ich hab das Problem, das wenn ich den Path nun mit "PathFinder.GetPath(currentWayPoint,targetWayPoint);" mir hole, ich leider keine nicht genau weiss, wie ich auf dessen Transform zugreife um die Figur den Pfad entlanglaufen zu lassen. Oder hab ich gerade nen Denkfehler? :unsure: Wäre super, wenn vielleicht ein Codeschnipsel Beispiel posten könnte, dann wäre das vielleicht etwas klarer :)

 

mfg

Billy

Link zu diesem Kommentar
Auf anderen Seiten teilen

Also is schon bissl her das ich das geschrieben hab und ich muss grad selbst mich erstmal zurechtfinden, aber ich hatte ja ganz untem bei meinem tutorial nen Beispielscript drin das dir einfach durch Linien den Pfad zwischen 2 Punkten anzeigt da stehts ja eigentlich drin!

 

Guck da einfach mal nach.

Wenn nicht schau ich nochmal in meinem damaligen Projekt wofür ich das geschrieben habe dann kann ich auch mal posten wie ich das generell mit dem ablaufen des Pfades gemacht habe...

 

EDIT: arbeitest du mit C# oder JS? weil ich grad gesehn habe das du das C# Tutorial geliked hast ;) da hab ich dieses Script natürlich nicht drin.

Müsste aber ähnlich gehen nur das du vorher das Objekt in der Liste noch casten musst.

 

ArrayList path = PathFinder.GetPath(start,target);
Transform next = ((Waypoint)path[0]).transform;

 

Waypoint erbt ja von MonoBehaviour dadurch kommste ja einfach ans Transform Objekt ran.

 

Gruß Stephan

Link zu diesem Kommentar
Auf anderen Seiten teilen

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Gast
Auf dieses Thema antworten...

×   Du hast formatierten Text eingefügt.   Formatierung jetzt entfernen

  Only 75 emoji are allowed.

×   Dein Link wurde automatisch eingebettet.   Einbetten rückgängig machen und als Link darstellen

×   Dein vorheriger Inhalt wurde wiederhergestellt.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Lädt...
×
×
  • Neu erstellen...