Jump to content
Unity Insider Forum

Diskussion, wie man die Maus-Steuerung eines Raumschiffes realisieren könnte


Footloose

Recommended Posts

Also ich habe mir das halt so gedacht dass das Raumschiff vom beschleunigne und abbremsen mal abgesehen mit der Maus gesteuert wird.
Dasselbe gilt wo möglich auch für die Waffen.
Daher sollte ein LookAt eigentlich reichen, nur hatte das ja nicht wirklich funktioniert, wobei ich glaube dass man dann noch einen Abstand einbauen könnte.
Eigentlich sollte das ausreichen.

Ich werde damit mal ein bisschen rumarbeiten und mal schauen ob ich was hinbekomme ;) 
Danke für deine Hilfe :):):) 

Liebe Grüße ;) 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Ja, das "Problem" am LookAt ist eben nur, daß es das Raumschiff in alle Richtungen rotieren lässt. Man kann hier aber beispielsweise eine Achse fixieren. Man führt dabei das LookAt aus und speichert sich vorher die Rotationsachse des Raumschiffe, die man nicht verdrehen möchte (so wie ich es beim 1. Skript gemacht hatte). 
Das verbleibende Problem ist eben nun immer noch wie man die Z-Position der Maus im 3D-Raum setzt. Die X und Y-Position der Maus haben wir ja. Wenn man aber nun mit der Maus über das Raumschiff klickt, welche Z-Position soll die Maus nun einnehmen, vor dem Raumschiff, gleiche Höhe oder hinter dem Raumschiff. Je nachdem wie man die Z-Position hier setzt wirkt sich das LookAt unterschiedlich aus.

Entweder du verwendest hier LookAt:

var oldRot : Quaternion = transform.rotation ;
transform.LookAt ( destination ) ;
var newRot : Quaternion = transform.rotation ;
transform.rotation = Quaternion.Lerp ( oldRot , newRot, speed ) ;

oder eben:

var pos : Vector3 = destination-transform.position;
var newRot = Quaternion.LookRotation(pos);
transform.rotation = Quaternion.Lerp(transform.rotation, newRot, speed);

Beide Lösungen sollten "eigentlich" gleichwertig sein.

PS:
Ich teste gerade eine Skriptversion mit LookAt mit einer Follower-Kamera und es verhält sich sehr seltsam. Die Neupositionierung der Kamera scheint das Skript stark zu stören. Es Objekt fängt an stark zu vibrieren.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Wäre schon krass wenn man das in Unity kaum umsetzen könnte.
Ich werde das morgen wenn ich aufstehe mal austesten und mich dann nochmal melden.

Das Z der Maus sollte natürlich etwas vor der Schiff sein, damit das Raumschiff sich nicht einfach so dreht.

Link zu diesem Kommentar
Auf anderen Seiten teilen

So hier ein simples Raumschiffsteuerungsskript. Ich habe alles noch einmal stark vereinfacht und einen kleinen Trick angewandt, indem ich die Z-Position der Maus nach hinten versetzt habe. Du musst beide Skripte verwenden, ansonsten kommt es wieder zu "unerwünschten Effekten":
Kamera:

// Skript für die Kamera
using UnityEngine;
using System.Collections;
// Skript für die Kamera
public class Follow : MonoBehaviour
{

    // The target we are following
    public Transform target;
    public float distance = 5.0f;

    void LateUpdate()
    {
        if (!target) return;

        // Always look at the target
        transform.LookAt(target);
        transform.position = target.position - new Vector3(0, 0, distance);
    }
}

Raumschiff:

using UnityEngine;
using System.Collections;
using UnityEngine.Networking;

public class CorvetteMovement : MonoBehaviour
{
    // Zer0Cools Deklarationen
    public float rotationspeed;
    public float MouseZOffset = 2.5f;
    private Camera mainCamera;

    void Start()
    {
        mainCamera = Camera.main;
    }

    // Meine Deklarationen
    public GameObject laserBeamPrefab;
    public Transform laserBeamSpawnt11;
    public Transform laserBeamSpawnt12;
    public Transform laserBeamSpawnt21;
    public Transform laserBeamSpawnt22;
    public Transform laserBeamSpawnt31;
    public Transform laserBeamSpawnt32;
    public Transform laserBeamSpawnt41;
    public Transform laserBeamSpawnt42;

    // Update is called once per frame
    void Update()
    {

        // Beschleunigung
        transform.position = transform.position + transform.forward * Time.deltaTime;
        // Ende des Beschleunigungsscripts

        // Zer0Cools Script Anfang
        Vector3 mousePos = Input.mousePosition;

        float distanceCameraZ = this.transform.position.z - mainCamera.transform.position.z;
        distanceCameraZ = distanceCameraZ + MouseZOffset;
        Vector3 mousepos3D = Camera.main.ScreenToWorldPoint(new Vector3(mousePos.x, mousePos.y, distanceCameraZ));
        mousepos3D.z = mousepos3D.z + (mousepos3D.y - this.transform.position.y);
        Debug.DrawLine(mousepos3D, this.transform.position, Color.red);

        transform.LookAt(mousepos3D);
    }
}

Je kleiner du den Mausoffset setzt, desto stärker neigt sich das Raumschiff, da die Mausposition näher an das Raumschiff rückt.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Hier noch einmal eine Variante, wo du das Raumschiff auch in eine komplett andere Richtung steuern kannst mit den Tasten "left" und "right". Allerdings ist die Steuerung sehr gewöhnungsbedürftig, da du das Target des Schiffes über die Tasten in Z verschiebst und sich das Schiff darauf ausrichtet. Ich habe das Target daher sichtbar gemacht.

https://uploadfiles.io/805vd
 

using UnityEngine;
using System.Collections;
using UnityEngine.Networking;

public class CorvetteMovement : MonoBehaviour
{
    // Zer0Cools Deklarationen
    public float rotationspeed;
    public float MouseZOffset = 2.5f;
    private Camera mainCamera;
    private GameObject sphere;

    void Start()
    {

        mainCamera = Camera.main;
        sphere = GameObject.CreatePrimitive(PrimitiveType.Sphere);
        sphere.transform.position = new Vector3(0, 0, 0);
        sphere.transform.localScale = new Vector3(0.2f,0.2f, 0.2f);
        sphere.GetComponent<Renderer>().material.color = Color.red;
    }

    // Meine Deklarationen
    public GameObject laserBeamPrefab;
    public Transform laserBeamSpawnt11;
    public Transform laserBeamSpawnt12;
    public Transform laserBeamSpawnt21;
    public Transform laserBeamSpawnt22;
    public Transform laserBeamSpawnt31;
    public Transform laserBeamSpawnt32;
    public Transform laserBeamSpawnt41;
    public Transform laserBeamSpawnt42;
    private bool Rotated = false;
    private float ZOffset;

    // Update is called once per frame
    void Update()
    {

        // Beschleunigung
        transform.position = transform.position + transform.forward * Time.deltaTime;
        // Ende des Beschleunigungsscripts

        // Zer0Cools Script Anfang
        Rotated = false;
        if (Input.GetKey("left"))
        {
            //transform.RotateAround(transform.position, Vector3.up, Time.deltaTime * -rotationspeed);
            Rotated = true;
            ZOffset = ZOffset + (Time.deltaTime * -5f);        
        }

        if (Input.GetKey("right"))
        {
            //transform.RotateAround(transform.position, Vector3.up, Time.deltaTime * rotationspeed);
            ZOffset = ZOffset + (Time.deltaTime * 5f);
            Rotated = true;
        }

            Vector3 mousePos = Input.mousePosition;

            float distanceCameraZ = this.transform.position.z - mainCamera.transform.position.z;
            distanceCameraZ = distanceCameraZ + MouseZOffset;
            Vector3 mousepos3D = Camera.main.ScreenToWorldPoint(new Vector3(mousePos.x, mousePos.y, distanceCameraZ));
            mousepos3D.z = mousepos3D.z + (mousepos3D.y - this.transform.position.y);

            mousepos3D.z = mousepos3D.z + ZOffset;

            Debug.DrawLine(mousepos3D, this.transform.position, Color.red);

            transform.LookAt(mousepos3D);
            sphere.transform.position = mousepos3D;

    }
}

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Ich hab noch eine bessere Steuerungsmöglichkeit gefunden (die Tasten "left" und "right" braucht man nun nicht mehr). Und noch einen Fehler gefixed. Ich hatte den Abstand der Kamera nicht korrekt berechnet (was zu falschen Z-Koordinaten der Maus führte). Folgendes Skript funktioniert nur, wenn du die Kamera direkt als Child unter das Raumschiff legst. Damit rotiert die Kamera automatisch immer mit in die Richtung wohin der Mauszeiger zeigt. Somit lässt sich das Raumschiff nun in alle Richtungen des Raumes mit Hilfe der Maus steuern. Es gibt immer noch ein paar Probleme mit der Kamera, ich vermute die Kopplung zwischen Raumschiff und Kamera ist zu starr. Man müsste hier vermutlich ein Kamerafollowskript schreiben, welches sich langsam an das Raumschiff anpasst. Aber abgesehen davon, finde ich diese Steuerung für ein Raumschiff schon recht optimal:

// Skript für das Raumschiff
using UnityEngine;
using System.Collections;
using UnityEngine.Networking;

public class CorvetteMovement : MonoBehaviour
{
    public float rotationspeed = 2f;
    public float MouseZOffset = 5f;
    private Camera mainCamera;
    private GameObject sphere;

    void Start()
    {
        mainCamera = Camera.main;
        sphere = GameObject.CreatePrimitive(PrimitiveType.Sphere);
        sphere.transform.position = new Vector3(0, 0, 0);
        sphere.transform.localScale = new Vector3(0.2f, 0.2f, 0.2f);
        sphere.GetComponent<Renderer>().material.color = Color.red;
    }

    private bool Rotated = false;
    private float ZOffset;

    // Update is called once per frame
    void Update()
    {
        // Vorwärtsbeschleunigung
        transform.position = transform.position + transform.forward * Time.deltaTime;

        Vector3 mousePos = Input.mousePosition;

        float distanceCameraZ = (this.transform.position - mainCamera.transform.position).magnitude;

        Vector3 mousepos3D = Camera.main.ScreenToWorldPoint(new Vector3(mousePos.x, mousePos.y, distanceCameraZ + MouseZOffset));

        Debug.DrawLine(mousepos3D, this.transform.position, Color.red);

        Quaternion oldRot = transform.rotation;
        transform.LookAt(mousepos3D, this.transform.up);
        Quaternion newRot = transform.rotation;
        transform.rotation = Quaternion.Lerp(oldRot, newRot, Time.deltaTime * rotationspeed);        

        /*
        Vector3 pos = mousepos3D - transform.position;
        Quaternion newRot = Quaternion.LookRotation(pos);
        transform.rotation = Quaternion.Lerp(transform.rotation, newRot, Time.deltaTime * rotationspeed);
        */
        
        sphere.transform.position = mousepos3D;
    }
}

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Hier noch ein Kamerafollowskript. Jetzt kann man die Kamera wieder als Child des Raumschiffes herrausnehmen:

// Skript für die Kamera
using UnityEngine;
using System.Collections;

public class Follow : MonoBehaviour
{

    // The target we are following
    public Transform target;
    public float ZOffset = 5f;
    public float YOffset = 2f;
    public float rotationDamping = 1f;
    public float positionDamping = 1f;

    void LateUpdate()
    {
        if (!target) return;

        Quaternion currentRotation = transform.rotation;
        Quaternion targetRotation = target.rotation;

        Vector3 currentPosition = transform.position;
        Vector3 targetPosition = target.position - (target.forward * ZOffset) + (target.up * YOffset);
        transform.position = Vector3.Lerp(currentPosition, targetPosition, Time.deltaTime * positionDamping);
        transform.rotation = Quaternion.Lerp(currentRotation, targetRotation, Time.deltaTime * rotationDamping);
    }
}

Leider hat das Ganze immer noch ein paar kleine Macken:
1) Bei der Rotation kommt es bei mir ca. alle 180 Grad zu einem Miniruckler
2) Wenn man das Raumschiff extrem nach oben oder unten neigt und die Maus direkt über das Raumschiff platziert, dann fängt das Raumschiff an zu "springen". Hier kann sich "LookAt" nicht "entscheiden" wie die richtige Rotation aussehen soll. Man muss diese Extremposition der Maus erkennen und korrigieren, so daß das Raumschiff hier eine definierte Rotation bekommt.

Woher der Wackler bei der Rotation kommt ist mir rätselhaft. Ich vermute der Ruckler entsteht bei der Rotation des Raumschiffes, aber man kann es im Editor nicht genau erkennen. Das der Wackler von der Kamera kommt glaube ich nicht, diese habe ich ja nun durch das Kamerafollowskript entkoppelt.

Link zu diesem Kommentar
Auf anderen Seiten teilen

So hier mal die Steuerung in Aktion:
https://ufile.io/suz6i

Nochmal zu Punkt 2)
Die Steuerung geriet ins Schlingern, wenn das Raumschiff entlang der Y-Achse flog. Die "Ursache" lag am "transform.LookAt":
public void LookAt(Transform target, Vector3 worldUp = Vector3.up);
Dieses bezieht sich per Default auf die globale Y-Achse. Ich konnte das Problem lösen, indem ich nun die lokale Y-Achse des Raumschiffes verwende. Ich habe es oben im Skript bereits geändert.

Vielleicht ist ja noch jemand hier im Forum, der sich das sporadische Ruckeln beim Drehen erklären kann. Der Code ist im Endeffekt ja nicht kompliziert und eine Testszene kann leicht nachgebaut werden. Ich habe den Code auch noch einmal auf die Steuerung reduziert.

PS:
Das Ruckeln scheint bei mir zu verschwinden, wenn ich V-Sync-Count in den Qualitysettings auf "Dont Sync" stelle (damit habe ich dann eine Framerate > 60fps). Trotzdem ist es seltsam, daß ein V-Sync zu einem Ruckler führt, zumal alle Rotationen und Bewegungen framenratenabhängig sind.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Ihr habt es zwar wohl schon gelöst aber hier nochmal meinen Code:

public void Aim (Vector3 worldpos)//worldpos ist die Zielposition in Weltkoordinaten.
	{
		//left or right ?  Ich hatte also links und rechts auf meinem Schiff Geschütztürme. Abhängig davon ob das Ziel links oder rechts war wurden nur die jeweiligen Türme bewegt.
		if (transform.InverseTransformPoint (worldpos).x < 0) {
			// Turret consists of gun on base on slot.
			// turn base around it's up axis
			// turn gun around it's X axis
			rotateTurret (leftGuns, worldpos);
		} 
		else {
			// Turret consists of gun on base on slot.
			// turn base around it's up axis
			// turn gun around it's X axis
			rotateTurret (rightGuns, worldpos);
		}
	}
void rotateTurret (List<StarshipTurret> guns, Vector3 worldpos)
	{
		foreach (StarshipTurret t in guns) {
			/**
            **/
			Vector3 baseTargetRot = t.turretBase.InverseTransformPoint (worldpos);
			baseTargetRot.y = 0;// Die Y Achse auf 0 damit nur die Drehung auf der X-Z Ebene genommwen wird
			Quaternion targetRot = Quaternion.FromToRotation (Vector3.forward, (baseTargetRot).normalized); // Siehe Vector3.forward. Ich nutze lokale Koordinaten für meinen Turm. turretBase ist die "Wanne"
			t.turretBase.rotation *= targetRot;			
			Vector3 baseGunRot = t.turretGun.InverseTransformPoint (worldpos);
			baseGunRot.x = 0;//Nochmal das gleiche - dieses mal die Drehung auf der Y-Z Ebene.			
			targetRot = Quaternion.FromToRotation (Vector3.forward, (baseGunRot).normalized);
			t.turretGun.rotation *= targetRot;
		}
	}

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

@Zer0Cool:
Das schaut doch schon echt gut aus :) 
Ich werde noch eine seitliche Rotation einbauen, damit der Spieler das Schiff auspendeln kann. Oder ich
mache einen Script dass es automatisch langsam in die richtige Rotation zurückgeht.


Außerdem folgt die Kamera dem Schiff nicht, weil ich mein gespawntes Schiff der Kamera noch nicht als Ziel zugewiesen hab.
Hab grad vergessen wie das geht^^ Aber ansonsten läuft dein Script und schaut schon ganz gut aus.
Vielen Dank für deine Hilfe. Ich denke deine Lösung lässt sich gut als allgemeine Basis für eine solche Steuerung verwenden :) 


@Torigas:
Ich konnte dein Script leider noch nicht testen, denke das werde ich aber auch noch machen ;) 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Das mit der zusätzlichen Rotation ist nicht so einfach, das Schiff rotiert ja immer in Richtung Mausposition. Du müsstet für eine seitliche "Extra"-Rotation diese Zwischenspeichern (aufakkumulieren zwischen den Frames) und dann diese immer "in Summe" nach der LookAt-Rotation hinzufügen. Also als Beispiel, wenn der Spieler sagen wir pro Frame 5 Grad nach Links rotieren will diese 5 Grad abspeichern (und dann drehen) und wenn er im nächsten Frame nochmal 5 Grad rotieren will beides aufsummieren = 10 Grad und diese Drehung dann immer jeweils nach der LookAt-Rotation in Summe ausführen.
Nach dem Spawn (gespawntes Objekt zwischenspeichern) eine Referenz auf das Kameraskript besorgen (bzw. schon vorher) und dem Kameraskript dann die Referenz auf das gespawnte Raumschiff übergeben. 
Bin auch soweit zufrieden mit der Steuerung, nur die sporadischen Miniruckler bei der Rotation wenn ich die FPS der Bildschirmrate anpasse (V-Sync) gefällt mir nicht. Aber ich habe keine Ahnung wie man das verhindern kann, würde mich auch nicht wundern, wenn es im Build nicht ruckelt oder auch bei einer anderen Unityversion.

Link zu diesem Kommentar
Auf anderen Seiten teilen

So, das Skript erwartet nun das "Follow"-Skript an der Main-Kamera und weißt sich dann selbst als Ziel zu:

using UnityEngine;
using System.Collections;
using UnityEngine.Networking;

public class CorvetteMovement : MonoBehaviour
{
    public float rotationspeed = 2f;
    public float MouseZOffset = 5f;
    public bool showMousePosition = true;
    private Camera mainCamera;
    private GameObject sphere;


    void Start()
    {
        mainCamera = Camera.main;

        // Code fürs Hinzufügen des Raumschiffes zur Kamera
        Follow followScript = mainCamera.GetComponent<Follow>();
        if (followScript != null) followScript.target = this.transform;
        else Debug.Log("Kein Followskript an der Kamera gefunden");

        if (showMousePosition)
        {
            sphere = GameObject.CreatePrimitive(PrimitiveType.Sphere);
            sphere.transform.position = new Vector3(0, 0, 0);
            sphere.transform.localScale = new Vector3(0.2f, 0.2f, 0.2f);
            sphere.GetComponent<Renderer>().material.color = Color.red;
        }
    }

    // Update is called once per frame
    void Update()
    {
        Vector3 mousePos = Input.mousePosition;
        float distanceCameraZ = (this.transform.position - mainCamera.transform.position).magnitude;
        Vector3 mousepos3D = Camera.main.ScreenToWorldPoint(new Vector3(mousePos.x, mousePos.y, distanceCameraZ + MouseZOffset));
        if (showMousePosition) sphere.transform.position = mousepos3D;
        // Debug.DrawLine(mousepos3D, this.transform.position, Color.red);

        Quaternion oldRot = transform.rotation;
        transform.LookAt(mousepos3D, this.transform.up);
        Quaternion newRot = transform.rotation;
        transform.rotation = Quaternion.Lerp(oldRot, newRot, Time.deltaTime * rotationspeed);

        /*
        Vector3 pos = mousepos3D - transform.position;
        Quaternion newRot = Quaternion.LookRotation(pos);
        transform.rotation = Quaternion.Lerp(transform.rotation, newRot, Time.deltaTime * rotationspeed);
        */

        // Vorwärtsbeschleunigung
        transform.position = transform.position + transform.forward * Time.deltaTime;
    }
}

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Super :) 
Ich lade mal ein Video hoch wie das jetzt bei mir ausschaut:

Final.avi

Ja, die Quali des Videos ist schon mies, ich brauche dringend ein neues Aufnahmeprogramm^^


Ist noch nicht perfekt aber es wird.

Ich werde das Script jetzt noch für die Geschütze umschreiben.
Danke für die Hilfe nochmal ;) 

Vielleicht kommen mit der Zeit ja noch andere Lösungen dazu?
Liebe Grüße :) 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Naja die ist nicht von mir.

Die ist aus dem SpaceForUnity-Asset.

Ich mache gerade mein Praktikum und da habe ich als Projekt die Aufgabe, ein
GameDesignDokument zu entwerfen und einen Prototypen dazu.

Nach meinem Praktikum beginne ich mein Studium (Bachelor of Arts), wo ich
u.a. 3D lerne. Da möchte ich dass dann halt weiter verfeinern.
 

Ich hoffe ich finde in der Zeit noch ein paar Mitstreiter denen die Idee dahinter gefällt.
Mit dem Prototypen möchte ich dann ungefähr zeigen können, was ich vorhabe.
Die Steuerung ist (auch dank dir) jetzt schon sehr nah an dem, was ich mir
vorgestellt habe.

Als nächstes kommt ein System, wo die verschiedenen Schiffe Lebenspunkte bekommen
etc.
Da das Host-Client-System bereits funktioniert, hoffe ich dann bald ein paar erste Testrunden zocken zu können.
Wenn du möchtest kannst du ja ne Runde mitmachen. Kann dir aber nicht versprechen wann das der Fall sein wird ;) 

Liebe Grüße :) 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Archiviert

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

×
×
  • Neu erstellen...