Jump to content
Unity Insider Forum

Einheitenselektierung mit Drawtexture


Recommended Posts

Hallo,

ich möchte für mein Projekt die Unit-Selection programmieren, folgendes habe ich mir gedacht:

  • Bei Linksklick [wenn gedrückt wird]: Abspeichern der Mausposition
  • Bei Linksklick [wenn losgelassen wird]: Abspeichern der Mausposition
    Zeichnen eines Rechtecks entsprechend der Mauspositionen

public Texture2D rectTexture;
Vector3 mousePos;
List<GameObject> selectableUnits;
void DrawRectangle()
{
 if (Input.GetButtonDown("Fire1"))
 {
  mousePos = Input.mousePosition;
 }
 else if (Input.GetButtonUp ("Fire1"))
 {
  GUI.DrawTexture (new Rect(mousePos.x, mousePos.y, Input.mousePosition.x - mousePos.x, Input.mousePosition.y - mousePos.y), rectTexture);
 }
}
void OnGUI()
{
 DrawRectangle();
}

Wie kann ich vergleichen, ob sich eine Unit im ausgewähleten Bereich befindet oder nicht?

Bisher denke ich an folgendes:

Liste mit allen Units, die man anklicken kann [Tag = "selectable"] wird erstellt und

es wird in einer Schleife die Koordinaten der selectable gameObjects mit den

Koordinateninhalt [Pixeln] des Rechtecks verglichen. Doch die Perfomance würde denke ich mal sehr unter dieser Implementierung leiden...

Link zu diesem Kommentar
Auf anderen Seiten teilen

Es kommt etwas darauf an wie dein Spiel aufgebaut ist. Ich habe beispielsweise immer eine Liste alle meiner Gegner, Einheiten etc. die in meinen Spiel vorhanden sind. Hat man so eine Liste kann man einfach jedes Element durchgehen und es prüfen ob es innerhalb von zwei Koordinaten ist. Um mal ein Beispiel zu geben. Hier ein simples Beispiel. "Unit" ist die einheit und hat mit "Vector3 position" seine aktuelle Position. Das ist hier nur eine simple Klasse. Bei dir müsste das dann halt eben die Klasse sein das deine Einheit repräsentiert. Die Position findet man ja unter "transform.position".

 

Ansonsten fügst du deiner Unit dann einfach eine Methode hinzu das prüft ob es sich innerhalb zwei Vectoren befindet.

 

public class Unit {
public Vector3 position { get; private set; }
public Unit(Vector3 position) {
	this.position = position;
}
public bool isInRect(Vector3 a, Vector3  {
	var tx = this.position.x;
	var tz = this.position.z;

	var xlower = a.x < b.x ? a.x : b.x;
	var xupper = a.x > b.x ? a.x : b.x;
	var zlower = a.z < b.z ? a.z : b.z;
	var zupper = a.z > b.z ? a.z : b.z;

	if (tx >= xlower && tx <= xupper) {
		if (tz >= zlower && tz <= zupper) {
			return true;
		}
	}

	return false;
}
}

 

Sowas musst du halt deinem MonoBehaviour Klasse hinzufügen wenn du das so bei dir aufgebaut hast eben transform.position nutzen. Ansonsten wenn du eine Liste hast dann sieht der Code zum zurückgeben aller einheiten in einem Bereich so aus.

 

// Beispiel Liste mit Units
var units = new List<Unit>() {
new Unit(new Vector3(0, 0, 0)),
new Unit(new Vector3(1, 0, 0)),
new Unit(new Vector3(2, 0, 0)),
new Unit(new Vector3(0, 0, 1)),
new Unit(new Vector3(1, 0, 1)),
new Unit(new Vector3(2, 0, 1)),
new Unit(new Vector3(0, 0, 2)),
new Unit(new Vector3(1, 0, 2)),
new Unit(new Vector3(2, 0, 2)),
};

// Rückgabe aller Objekte in einer Selection
var startSelection = new Vector3(1, 0, 1);
var endSelection   = new Vector3(0, 0, 2);
var inRect = units.Where(u => u.isInRect(startSelection, endSelection)).ToList();
foreach (var unit in inRect) {
Debug.Log("unit: " + unit.position);
}

 

Ansonsten wird bei der Selektion die X und Z Achse beachtet da ich jetzt mal davon ausgehe das du ein Spiel hast wo du eine Top-Down View hast. Und die Y Achse, also die Höhe einer einheit ignoriert werden soll.

 

Ansonsten das einzige was du halt eben benötigst wenn du es so machst ist eine Liste aller deiner Einheiten. Wenn du solch eine Liste einmal hast ist es eigentlich simpel. Was sich dafür anbietet ist halt ein Globaler GameManager oder ähnliches. Und jede Unit die erzeugt wird fügt sich selbstständig dieser globalen Liste hinzu. Beziehungsweise wenn die Unit stirbt dann trägt es sich wieder aus der Liste aus.

 

Ansonsten musst du deine Selection startest. In der Camera Klasse gibt es Funktionen wie ScreenPointToRay oder ScreenToWorldPoint. Du musst halt beim starten der Selection den Vector3 bekommen auf den der nutzer sozusagen geclickt hat. Beim Beenden der aktion das gleiche. Hast du die beiden Vector3 kannst du das eben wie oben dann einfach durchlaufen.

 

Sehr wahrscheinlich gibt es noch andere Möglichkeiten. Jedoch wäre das der Weg der mir spontan einfällt und den ich gehen würde.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Danke, habs hingekriegt!

Btw, ich habe noch eine Frage, die sich auf ein anderes Thema bezieht:

Ich möchte einige 3D-Gebäude animieren, sodass diese bei bspw. noch 75% health etwas zerstört aussehen etc...

Kann man das in Unity nur durch Ersetzen des 3D.Modells hinkriegen, oder gibts auch einfache dynamische Methoden?

Link zu diesem Kommentar
Auf anderen Seiten teilen

Hmm,

was mir zu dem Thema so spontan einfällt wäre wenn du den Aufbau deiner Gebäude abänderst. Beispielsweise wenn du soetwas aufbaust.

 

Gebäude

.- 100% Gebäude

.- 75% Gebäude

.- 50% Gebäude

 

Auf "Gebäude" liegen dann deine Skripte und deine ganze Logik. Ansonsten hat "Gebäude" selber kein Mesh sondern ist eigentlich nur ein Empty GameObject. Stattdessen hast du unterhalb von Gebäude dann deine GameObjects die dein verschiedene States deines Gebäudes Repräsentieren. Standardmäßig ist dann nur das 100% Gebäude aktiv.

 

Wenn dein Gebäude dann beschädigt wird, und du 75% erreichst, würdest du dann hingehen und das 100% Gebäude deaktivieren und dann das 75% Gebäude einschalten. Bzw. anstatt die ganzen GameObjects zu aktivieren/deaktivieren reicht es wenn du die Mesh Renderer einfach aktivieren/deaktivieren würdest.

 

Das wäre eine Idee. Eventuell gibt es ja noch andere/bessere Ideen wie man das löst.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Zu dem Gebäutethema fällt mir auch noch was ein.

Du könntest die Teile die zerstört werden oder sich verschieben auch animieren.

Sagen wir Du hast eine Wand welche aus einem Objekt besteht. Bei einen Schaden von 75% oder wie auch immer, kannst Du wie oben beschrieben den Meshrender deaktivieren und ein anderen aktivieren.

Oder du löscht das Wandobjekt und instanzierst das gleiche aussehende Wandobjekt an der gleichen stelle, welches Du aber vorher in der Modellierungssoftware zerstört oder geteilt hast.

Die Einzelteile kannst Du auch mit nem Rigidbody und ner kleinen Explosionsforce versetzen, das sie ein bisschen rumfliegen oder runterfallen usw.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Mir ist gerade noch etwas anderes eingefallen. Man kann auch noch simpler einfach eine Mesh Variable haben, und diese dann den MeshFilter übergeben.

 

Also man definiert einfach simpel z.B. solche Sachen:

 

public Mesh fullHealth;
public Mesh halfHealth;

 

Im Unity Inspector kann man dann seine Meshes drauf ziehen. Man muss nur die Bedingung abfragen und das neue Mesh dem MeshFilter zuteilen. Sieht dann so aus.

 

f      = this.GetComponent<MeshFilter>();
f.mesh = fullHealth;

 

Ansonsten kann man damit auch simpel sein eigenes LOD System bauen.

 

using UnityEngine;
using System.Collections;
using System.Linq;

public class MyLOD : MonoBehaviour {
   [system.Serializable]
   public class MyLODEntry {
       public Mesh  mesh;
       public float distance;
   }
   public  MyLODEntry[] lods;

   private MeshFilter   f;
   private Transform    t;
   private Transform    player;
   private MyLODEntry[] olods;

   void Awake() {
       this.f      = this.GetComponent<MeshFilter>();
       this.t      = this.transform;
       this.player = GameObject.FindWithTag("Player").transform;
       olods       = lods.OrderByDescending(e => e.distance).ToArray();
   }

   void OnEnable() {
       StartCoroutine(CheckDistance());
   }

   void OnDisable() {
       StopAllCoroutines();
   }

   IEnumerator CheckDistance() {
       while ( true ) {
           var distance = Vector3.Distance(this.player.position, t.position);
           var entry = olods.FirstOrDefault(e => distance >= e.distance);
           if ( entry == null )
               entry = olods.Last();
           f.mesh = entry.mesh;
           yield return new WaitForSeconds(1f);
       }
   }
}

 

Ansonsten kann man den Aufbau oben auch leicht anpassen. Anstatt die Player Distance zu prüfen, nimmt man einfach das Leben des Gebäudes und schon hat man sein Mesh austausch System für Leben.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Archiviert

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

×
×
  • Neu erstellen...