Jump to content
Unity Insider Forum

Grid erstellen


Wikked

Recommended Posts

Hi,

 

ich bastle momentan an einem Projekt rum bei dem es möglich sein soll Einheiten auf einem Grid abzustellen.

Folgendes ist schon vorhanden.

 

Das Grid wird am Anfang initialisiert mit Würfel, die in alle Richtungen den selben Abstand haben.

Per Button kann man dann die gewünschte Einheit aussuchen und diese (aktuell) beliebig abstellen, jedoch können keine zwei Einheiten auf der selben Position sein (Prüfung durch Collider).

 

Nun kann man aber eben diese Einheiten auch in den Abständen abstellen, was natürlich nicht gehen sollte, sprich ich brauch ein Grid & Snap Funktion mit der ich die Einheiten nur auf einen Würfel abstellen kann (bei großen Einheiten auch über mehrere Würfel).

Das ganze soll übrigens in 3D sein.

 

Mir fällt bloß kein Ansatz ein, wie ich das ganze realisieren kann und würde mich über Hilfe freuen.

 

mfg

Link zu diesem Kommentar
Auf anderen Seiten teilen

Hi,

 

so ganz schlüssig wurde ich aus deiner Antwort noch nicht.

Werden deine Einheiten von dem Gitter/Feld selber erstell?

  • Wie wäre es, dass jedes Feld ein Script mit OnMouseDown besitzt.
  • Dazu eine Boolean Variable die auf False geht wenn OnMouseDown ausgeführt wurde.

Bei der Bewegung von Einheiten könnte man mit GameObject.Find die umliegenden Objekte überprüfen. Diese sollten dann eine passende Bezeichnungen besitzen.

  • Beispiel:
  • Vector3(10,5,3) wird zu 10_5_3

Gleichzeitig kann man mehrere Objekte in der Umgebung für große Einheiten Überprüfen.

 

Ich hoffe meine Ideen sind passend für dich.

 

mfg

Cooky

Link zu diesem Kommentar
Auf anderen Seiten teilen

Jau.

Das Problem wäre für mich einfach schon die Datenstruktur.

Ich nehm mal an dass sein Grid locker aus ein paar tausend Würfeln besteht.

Es ist schonmal völlig überflüssig diese Würfel als GameObjects in der Szene zu haben. Du brauchst ja nicht mehr als das Bounding Volumen eines Würfels, also würde ich auch bloß die generieren.

 

Und Unity müsste ja auch die ganzen OnMouse Events packen und managen, ein paar tausend, das ist zwar für heutige CPUs jetzt nicht so das Problem aber halt irgendwie überflüssig.

Zu dem GameObject.Find noch: Du möchtest eigentlich selten deine Daten abhängig von strings finden müssen, schon allein weil du dann alles ändern musst, wenn du auch bloß einen Character änderst.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Das Thema lässt mich irgendwie nicht aus der Ruhe. Selbst beim Frühstück musste ich nach einer Lösung suchen. Ein Lösungsansatz kam mir im Gespräch mit einem Mathematiker.

 

Dieser lautet in etwa so:

 

-Eine Arraya Boolen oder Int, etc. speichert die Werte der Felder.

-Die Arraya hat die Werte für jedes Feld. (Feld(1,1) ist Arraya[0] Feld(1,2) ist Arraya[1]….)

-Da das ganze ein Schachfeld(Spalte/Zeile hat immer dieselbe Länge) ist kann über eine Schleife die Zeile bzw. Spalte bestimmt werden.

 

Beispiel:

10x10 Feld

Feld(2,5) abfragen.

x=2

z=5

Feld=14

 

public int[] feld;

if(position.x>1)
{
feld[(position.x-1)*10+position.z-1]=1;		  
}
else{
feld[position.z-1]=1;

}

 

 

 

Um Objekte im Raster zu platzieren gibt es verschiedene Möglichkeiten.

Meine Idee wäre ein Raycast(Strahl) der vom Cursor weggesendet wird und auf die ausgewählte Layer trifft. Gleichzeitig brauch man kein Feld, da man ein Würfel/Bausymbol/etc. an der Maus anheften kann.

 

 

 

public class C_Test : MonoBehaviour {
public Camera spielKamera;
public LayerMask layer;
public GameObject prefab;
// Update is called once per frame
void Update () {
	Ray ray = spielKamera.ScreenPointToRay(Input.mousePosition);
	RaycastHit hit;
	if(Physics.Raycast(ray,out hit, Mathf.Infinity, layer))
	{
		 if(Input.GetMouseButton(0))
		 {
			 Instantiate(prefab, new Vector3(Mathf.RoundToInt(hit.point.x), 0, Mathf.RoundToInt(hit.point.z)), Quaternion.identity);
		 }

	}
}
}

 

LG

Cooky

Link zu diesem Kommentar
Auf anderen Seiten teilen

-Da das ganze ein Schachfeld(Spalte/Zeile hat immer dieselbe Länge) ist kann über eine Schleife die Zeile bzw. Spalte bestimmt werden.

 

Beispiel:

10x10 Feld

Feld(2,5) abfragen.

x=2

z=5

Feld=14

 

public int[] feld;

if(position.x>1)
{
feld[(position.x-1)*10+position.z-1]=1;		  
}
else{
feld[position.z-1]=1;

}

Da habe ich ein paar Sachen nicht verstanden. Was machst du, wenn position.z == 0 ist? Wo ist die Schleife, von der du gesprochen hast? Was hat es mit dem feld[...]=1 auf sich und warum hat das Feld am Anfang den Wert 14?

 

 

Instantiate(prefab, new Vector3(Mathf.RoundToInt(hit.point.x), 0, Mathf.RoundToInt(hit.point.z)), Quaternion.identity);

Das löst aber nicht das Problem, dass in den Lücken gebaut werden kann. Der neue Bauplatz soll auf einem Rasterpunkt sein.

 

Wenn du schon an jedem möglichen Gitterpunkt einen Würfel stehen hast, kannst du ja per Raycast feststellen, über welchem Gitterpunkt sich die Maus befindet. Das neue Objekt instantiierst du dann einfach an den Koordinaten des Würfels. Wenn du mit der Maus über keinem Würfel stehst, kann eben nichts erzeugt werden.

 

Alternativ kannst du auch feststellen, welcher Würfel sich am nächsten zur Maus befindet (z.B. über Vector3.Distance) und diesen evtl. durch andere Farbe oder einen Rahmen hervorheben.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Also ich hab das gestern Nacht noch halbwegs hinbekommen. Aber erstmal nur mit einem Objekt, das auch nur ein Feld groß ist. Es wird auch immer nur in der Mitte von den Bausteinen platziert.

 

Für alle die es interessiert Ausschnitt vom Code:

 

 

void Update () {
	if (currentShip != null && hasPlaced == false)
	{
		Vector3 m = Input.mousePosition;
		m = new Vector3(m.x, m.y, transform.position.y);	  
		Vector3 p = Camera.main.ScreenToWorldPoint(m);
		Vector3 position = FindNearestNode();
		currentShip.position = new Vector3(position.x, 1, position.z);

		if(Input.GetMouseButtonDown(0))
		{
			if(IsLegalPosition()) {
				hasPlaced = true;
			}
		}

		if (Input.GetKeyDown(KeyCode.R))
		{
			currentShip.transform.Rotate(new Vector3(0, 90, 0),90);
		}
	}
}
Vector3 FindNearestNode()
{
	Vector3 nearestNode = Vector3.zero;
	Vector3 m = Input.mousePosition;
	m = new Vector3(m.x, m.y, transform.position.y);
	Vector3 p = Camera.main.ScreenToWorldPoint(m);
	RaycastHit hit = new RaycastHit();
	Ray ray = new Ray(new Vector3(p.x, 10, p.z), Vector3.down);
	if (Physics.Raycast(ray, out hit, Mathf.Infinity, Layer))
	{
		for (int i = 0; i < Nodes.Count; i++)
		{
			if (Nodes[i] == hit.transform.position)
			{
				nearestNode = Nodes[i];
			}
		}
	}
	return nearestNode;
}

 

Nodes ist eine Vector3 Liste, die eben alle Positionen der Bausteine gespeichert hat.

IsLegalPosition() gibt bloß an ob da schon was draufsteht oder nicht.

 

 

Mein einzigstes Problem ist jetzt, dass wenn ich zwischen den Feldern einen Abstand hab und mit dem Objekt an der Maus zwischen den Feldern lande, dann springt das immer zum erst Platzierten Feld, ohne Abstand zwischen den Felder funktioniert das einwandfrei.

Ich glaub das liegt aber daran, dass ich ja Anfangs den nearestNode auf zero hab und wenn mein Rayhit eben kein Node trifft, dann gibt die Funktion eben den (0,0,0) Feld zurück :S

Link zu diesem Kommentar
Auf anderen Seiten teilen

Hast du nicht irgendwo eine Liste deiner Würfel ?

 

Cooky hat ja im Grunde versucht ein 2D Array in ein 1 - dimensionales zu packen. (Array übrigens, ohne a am Ende)

Wenn's nicht serialisiert werden muss im Editor kannst du also einfach deine Würfel in ein 2D Array hauen:

public GameObject[,] cubes = new GameObject[10, 10];

Wenn die Würfel alle 1^3 Ausmaße haben kannst du die Koordinaten dann auch direkt als Index verwenden.

 

Dann kannst du deine Position nehmen und entweder durch dein ganzes Array iterieren oder auch einfach die Koordinaten, die dir dein Ray liefert, runden (hier wieder vorausgesetzt dass der Index = Koordinaten ist.)

public Vector3[,] cubes = new Vector3[10, 10];
Vector3 GetClosestCube (Vector3 hitPos)
{
	var result = cubes[0, 0];
	var x = Mathf.RoundToInt(hitPos.x);
	var z = Mathf.RoundToInt(hitPos.z);
	 // Nicht ausser Reichweite des Arrays
	if (x > 0 && x < cubes.GetLength(0) && z > 0 && z < cubes.GetLength(1))
		result = cubes[x, z];
	return result;
}

 

Wenn das nicht geht schmeisst du halt alle Würfel in eine Liste / Array und iterierst jedes mal drüber, dauert bei 100 ja nicht allzu lange.

public Vector3[] cubes = new Vector3[100];
Vector3 GetClosestCube (Vector3 hitPos)
{
	var result = cubes[0];
	var distance = float.MaxValue;

	for (int i = 0; i < cubes.Length; i++)
	{
	     // Die Distanz vom aktuellen Würfel zur Hit Position quadriert
		var delta = (cubes[i] - hitPos).sqrMagnitude;
		if (delta < distance)
		{
			result = cubes[i];
			distance = delta;
		}
	}
	return result;
}

Link zu diesem Kommentar
Auf anderen Seiten teilen

Ja das Grid hab ich soweit ganz gut nun hinbekommen.

Grade häng ich aber wenn es darum geht ein Objekt, das auf zwei Felder stehen soll zu implementieren.

 

Hab zuerst versucht dem Objekt zwei BoxCollider zu geben, jeweils so groß wie ein Feld und dann eben für jeden einzelnen zu checken ob sich darunter bzw. ob beide BoxCollider mit einem FeldCollider treffen. Aber irgendwie komm ich da nicht weiter.

 

Gehts evtl. einfacher wenn ich meinem Objekt bloß einen Collider geb? Gibt es ne Funktion die mir als Liste/Array alle Collider zurück gibt die mit dem ObjektCollider kollidieren? OnTriggerEnter würde bei meiner Klassenstruktur nicht funktionieren :/ (und die gibt ja auch bloß ein Collider zurück oder?)

Link zu diesem Kommentar
Auf anderen Seiten teilen

Ich würde dem einzufügenden Objekt ein Empty Gameobjekt hinzufügen, an dem es sich mit einem Kreuzungspunkt verbinden kann. Wenn das Objekt mehrere Kreuzungspunkte besetzt, dann kommen da auch mehrere Empties ran.

 

Sobald das Objekt eingefügt werden soll, kannst du dann prüfen, ob diese Empties sich mit freien Kreuzungspunkten decken.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Hab mal ne Frage zu einem Error, da ich ehrlich gesagt keine Ahnung hab was die da von mir wollen.

 

Fehlermeldung

 

 

ArgumentException: GetComponent requires that the requested component 'GameObject' derives from MonoBehaviour or Component or is an interface.

UnityEngine.GameObject.GetComponentsInChildren[GameObject] (Boolean includeInactive) (at C:/buildslave/unity/build/artifacts/generated/common/runtime/UnityEngineGameObjectBindings.gen.cs:142)

UnityEngine.GameObject.GetComponentsInChildren[GameObject] () (at C:/buildslave/unity/build/artifacts/generated/common/runtime/UnityEngineGameObjectBindings.gen.cs:159)

ShipPlacement.Update () (at Assets/Scripts/ShipPlacement.cs:65)

 

 

Der Code dazu

 

 

else if (size == 2)
		{
			GameObject[] checkSlots = selectedShip.GetComponentsInChildren<GameObject>();
			bool check1 = checkSlots[0].GetComponent<SlotChecker>().checker;
			bool check2 = checkSlots[1].GetComponent<SlotChecker>().checker;
...
}

 

Zeile 65 ist die wo das GameObject Array erzeugt wird.

selectedShip ist vom Typ GameObject

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Archiviert

Dieses Thema ist jetzt archiviert und für weitere Antworten gesperrt.

×
×
  • Neu erstellen...