Jump to content
Unity Insider Forum

Mathematische Berechnung


Kojote

Recommended Posts

Hi!

Ich habe gerade ein kleines Matheproblem wo ich nicht weiter kommt. Problem ist ein vierbeiniger Character. Diesen möchte ich gern an das Terrain ausrichten.

Hirarchy ist:

  • Cat (Rotation 0|0|0)
  • - Root (Rotation (-90|0|90)
  • -- Global (Rotation (0|0|0))

Ich kann durch den Controller nicht die feinabstimmung für den Boden machen. Es ist ein gekaufter Controller und nach Tage langem testen bin ich da mit meinem Latain am Ende dort die Feinabstimmung hinein zu bekommen.

Also hab ich mir überlegt die Feinabstimmung in einem Child vorzunehmen. Im ersten Child (Root) funktioniert dies ebenfalls nicht, da der Controller auch darauf zugreift. Noch ein Child weiter unten (Global) kann ich ansetzen und die kleine Angleichung vornehmen.

Das wäre mein Script:

if(alignSurface) {
	RaycastHit HitDown;
    if(Physics.Raycast(new Vector3(transform.position.x, transform.position.y + 0.25f, transform.position.z), -transform.up, out HitDown, 5, groundLayers)) {
    Quaternion slopeRotation = Quaternion.FromToRotation(transform.up, HitDown.normal);
    transform.GetChild(0).GetChild(0).rotation = Quaternion.Slerp(transform.GetChild(0).GetChild(0).rotation, slopeRotation, Time.deltaTime * 5f);
}

Problem ist, dass das Modell von Haus aus im Child Root eine Drehung in X und Z hat. das verdreht mir auch Global.

Hat einer ne Idee wie ich das berechnen kann?

Grüße von Kojote

Link zu diesem Kommentar
Auf anderen Seiten teilen

Ein Stück weiter bin ich nun gekommen:

 

if(alignSurface) {
                    RaycastHit hit;
                    
                    Vector3 raycastPoint = new Vector3(transform.GetChild(0).GetChild(0).position.x, transform.GetChild(0).GetChild(0).position.y + 0.25f, transform.GetChild(0).GetChild(0).position.z);

                    if(Physics.Raycast(raycastPoint, -transform.GetChild(0).GetChild(0).forward, out hit, 5, groundLayers)) {
                        var targetRotation = Quaternion.FromToRotation(transform.GetChild(0).GetChild(0).transform.forward, hit.normal) * transform.GetChild(0).GetChild(0).rotation;
                        transform.GetChild(0).GetChild(0).rotation = Quaternion.Slerp(transform.GetChild(0).GetChild(0).rotation, targetRotation, Time.deltaTime * speed);
                    }

                    Debug.DrawRay(new Vector3(transform.position.x, transform.position.y + 0.25f, transform.position.z), -transform.up, Color.white);
                    Debug.DrawRay(raycastPoint, -transform.GetChild(0).GetChild(0).forward, Color.red, 10);
                    Debug.DrawRay(raycastPoint, transform.GetChild(0).GetChild(0).forward, Color.blue, 10);
                }

Damit richtet er sich erst einmal am Boden aus. Passend wäre es noch, wenn er einen "Von - Bis" Bereich hat auf den er reagiert, dass er sich nicht an jeder kleinen Delle ausrichtet.

Zweites Problem ist noch, dass er sich nicht in Laufrichtung ausrichtet. Nach ein paar Minuten herum laufen, schaut er leicht schräg nach vorn. Man müsste also irgend etwas machen, dass die Nachkorrektur immer in Richtung Laufrichtung passiert.

Dazu müsste ich die Z-Achse auf 0 setzen. Problem ist, wie mach ich das bei einem Quaternion. Ich finde nirgendwo eine Umrechnung Quaterion zu Euler.

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Hallo,

vor 12 Stunden schrieb Kojote:

Dazu müsste ich die Z-Achse auf 0 setzen. Problem ist, wie mach ich das bei einem Quaternion. Ich finde nirgendwo eine Umrechnung Quaterion zu Euler.

@KojoteVielleicht hilft dir das weiter.

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TestQuatternion : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        Quaternion myrotation; // Variable vom Type Quaternion erzeugen

        myrotation = transform.rotation; // der Variable myrotation: den Quaternion des GameObject's übergeben
        Debug.Log(myrotation.eulerAngles); // Euler Ausgeben

        myrotation.eulerAngles = new Vector3(0f,0f,45f); // neuen Euler von Z erstellen

        transform.rotation = myrotation; // den neuen Quaternion dem Transform vom Gameobject zuweisen
        Debug.Log(myrotation.eulerAngles); // neuen Euler ausgeben
    }

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

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

@Jog Danke dir!

Aus irgend einem Grund funktioniert es aber nicht.

Hier mal meine Änderung:

                if(alignSurface) {
                    RaycastHit hit;
                    
                    Vector3 raycastPoint = new Vector3(transform.GetChild(0).GetChild(0).position.x, transform.GetChild(0).GetChild(0).position.y + 0.25f, transform.GetChild(0).GetChild(0).position.z);

                    if(Physics.Raycast(raycastPoint, -transform.GetChild(0).GetChild(0).forward, out hit, 5, groundLayers)) {
                        Quaternion targetRotation = Quaternion.FromToRotation(transform.GetChild(0).GetChild(0).transform.forward, hit.normal) * transform.GetChild(0).GetChild(0).rotation;


                        targetRotation.eulerAngles = new Vector3(targetRotation.eulerAngles.x, targetRotation.eulerAngles.y, 0);


                        transform.GetChild(0).GetChild(0).rotation = Quaternion.Slerp(transform.GetChild(0).GetChild(0).rotation, targetRotation, Time.deltaTime * 2);
                    }
                }

Wenn ich ohne die mittlerste Zeile arbeite ist Z nur 0 - 3°. Wenn ich die Zeile einfüge ist Z plötzlich zwischen 100 und 300 groß.

Hier mal ein Video dazu, was passiert: https://streamable.com/3qi6j4

Link zu diesem Kommentar
Auf anderen Seiten teilen

Hallo,

@Kojote Hmm...Genau kann ich es so nicht sagen.

Es könnte an dieser Zeile liegen: 

Quaternion targetRotation = Quaternion.FromToRotation(transform.GetChild(0).GetChild(0).transform.forward, hit.normal) * transform.GetChild(0).GetChild(0).rotation;

Hier übergibst du Gedrehte Koordinaten im World Space, brauchst aber wahrscheinlich nicht Gedrehte Koordinaten im Local Space.

Wie gesagt, genau sagen kann ich es aber nicht.

Gruß Jog

Link zu diesem Kommentar
Auf anderen Seiten teilen

Habs jetzt noch mal ein wenig angepasst, dass ich eine maximale Neigung für das Tier habe:

private void PlayerAlign() {
            if(alignSurface) {
                RaycastHit hit;

                Transform child = null;
                if(child == null) { child = transform.GetChild(0).GetChild(0); }


                Vector3 raycastPoint = new Vector3(child.position.x, child.position.y + 0.25f, child.position.z);

                if(Physics.Raycast(raycastPoint, -child.forward, out hit, 5, groundLayers)) {
                    Quaternion targetRotation = Quaternion.FromToRotation(child.forward, hit.normal) * child.rotation;
                    child.rotation = Quaternion.Slerp(child.rotation, targetRotation, Time.deltaTime * 2);

                    PlayerAlignCalculation(child);
                }
            }
        }

        private void PlayerAlignCalculation(Transform child) {
            child.rotation = Quaternion.Euler(Mathf.Clamp(WrapAngle(child.transform.eulerAngles.x), -MaxForwardRotation, MaxForwardRotation) * (1 - MildForwardValue), child.transform.eulerAngles.y, Mathf.Clamp(WrapAngle(child.transform.eulerAngles.z), -MaxHorizontalRotation, MaxHorizontalRotation) * (1 - MildHorizontalValue));
        }

[Range(0f, 1f)]
        public float MildForwardValue = 0f;

        [Range(0f, 90f)]
        public float MaxForwardRotation = 90f;

        [Range(0f, 1f)]
        public float MildHorizontalValue = 0f;

        [Range(0f, 90f)]
        public float MaxHorizontalRotation = 90f;

        public float WrapAngle(float angle) {
            angle %= 360;

            if(angle > 180)
                return angle - 360;

            return angle;
        }

Rein von der Logik her müsste es funktionieren. Tut es aber nicht. 🤨

Link zu diesem Kommentar
Auf anderen Seiten teilen

private void PlayerAlign() {
	if(alignSurface) {
    	RaycastHit hit;

        Transform child = null;
        if(child == null) { child = transform.GetChild(0).GetChild(0); }


        Vector3 raycastPoint = new Vector3(child.position.x, child.position.y + 0.25f, child.position.z);

        if(Physics.Raycast(raycastPoint, -child.forward, out hit, 5, groundLayers)) {
		Quaternion targetRotation = Quaternion.FromToRotation(child.forward, hit.normal) * child.rotation;
            child.rotation = Quaternion.Slerp(child.rotation, targetRotation, Time.deltaTime * 2);

            PlayerAlignCalculation(child);
        }
	}
}

private void PlayerAlignCalculation(Transform child) {
	child.rotation = Quaternion.Euler(
      Mathf.Clamp(WrapAngle(child.transform.eulerAngles.x), -MaxForwardRotation, MaxForwardRotation) * 
      (1 - MildForwardValue), 	
      
      child.transform.eulerAngles.y, 
      
      Mathf.Clamp(WrapAngle(child.transform.eulerAngles.z), -MaxHorizontalRotation, MaxHorizontalRotation) * 
      (1 - MildHorizontalValue)
    );
}

[Range(0f, 1f)]
public float MildForwardValue = 0f;

[Range(0f, 90f)]
public float MaxForwardRotation = 90f;

[Range(0f, 1f)]
public float MildHorizontalValue = 0f;

[Range(0f, 90f)]
public float MaxHorizontalRotation = 90f;

public float WrapAngle(float angle) {
	angle %= 360;
    if(angle > 180)
    	return angle - 360;
	return angle;
}

Sauberer bekomm ichs leider nicht hin, hoffe du kannst es nun besser lesen. :(

OK, ich erkläre es noch mal präziser.

Ich habe einen Vierbeinigen Charcter. Wenn ich eine Steigung nach oben laufe, bleibt der Character in der Horizontalen, sprich er passt sich nicht an das Terrain an was dafür sorgt das entweder immer Vorderbeine oder Hinterbeine in der Luft schweben.

wr54ao6c.jpg

Auf dem Parent Objekt sitzt die Steuerung und in dieser wollte ich nun eine Erweiterung einbauen, dass der Character sich dem Boden anpasst. Direkt das Parent Objekt an den Boden anpassen funktioniert nicht. Der Charkter fängt an zu wackeln und über den Boden zu stottern. Aus dem Grund bin ich dazu über gegangen lieder eine kleine Korrektur im Child Position durchzuführen. Problem ist, dass das Modell von Haus aus im Child Global eine X und Z Drehung drin hat.

Ich habe es nun mit der Methode PlayerAlign hinbekommen, dass er sich am Boden ausrichtet.

Problem ist nun:

A: Die Z Achse ist immer um knapp 1° nach rechts gedreht. Sieht man fast nicht aber ist unschön und würde es wollen, dass Z immer 0 ist, damit er nach vorn schaut.

B: Mit der Methode PlayerAlignCalculation wollte ich eine maximale Neigung einbauen. Die Methode funktionier jedoch nicht, da er im laufen sich um sich selber dreht. Siehte hier: https://streamable.com/3qi6j4

Hoffe ich konnte es nun besser erklären. :)

Link zu diesem Kommentar
Auf anderen Seiten teilen

Dies hier war mein Versuch es direkt bim Parent einzubauen:

        private void PlayerAlign() {
            
            if(alignSurfacePlayer) {
                RaycastHit hit;
                Vector3 position = new Vector3(transform.position.x, transform.position.y + 0.25f, transform.position.z);

                if(Physics.Raycast(position, -transform.up, out hit, 5, alignmentLayerMask)) {
                    Quaternion targetRotation = Quaternion.FromToRotation(transform.up, hit.normal) * transform.rotation;
                    transform.rotation = Quaternion.Lerp(transform.rotation, targetRotation, Time.deltaTime * 2);
                }
                Debug.DrawRay(transform.position, -transform.up, Color.white, 5);
            }
        }

Aber heraus kommt das:

https://streamable.com/8cjpfb

Er richtet sich kein bisschen am Gelände aus, rutscht aber herum.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Naja, klar rutscht der rum wenn du ihn nach oben verschiebst, er nicht ganz gerade ist, und du ihn dann irgendwo wieder auf den Boden snapst.

Warum verschiebst du beim Alignen überhaupt ein Transform? Eigentlich sollte deine Figur ein Position haben, diese Position bewegt sich, und da wo die Position ist schaust du den Normalenvektor des Bodens an. Was du aber hast ist, dass das Ding sich bewegt, seine Ausrichtung wiederum von der Position abhängt und seine Ausrichtung wiederum von der Position. Darum rutscht du auch. Mach deinen Ground Check lieber unabhängig von der Ausrichtung der Figur. Also Vector3.down statt -transform.up.

Link zu diesem Kommentar
Auf anderen Seiten teilen

        private void PlayerAlign() {
            
            if(alignSurfacePlayer) {
                RaycastHit hit;

                Quaternion qTarget = Quaternion.LookRotation(desiredDisplacement, Vector3.up);

                Vector3 position = new Vector3(transform.position.x, transform.position.y + 0.25f, transform.position.z);

                if(Physics.Raycast(position, Vector3.down, out hit, 5, alignmentLayerMask)) {
                    Quaternion targetRotation = Quaternion.FromToRotation(transform.up, hit.normal) * transform.rotation;
                    transform.rotation = Quaternion.Lerp(transform.rotation, targetRotation * qTarget, Time.deltaTime * 2);
                }
                Debug.DrawRay(position, Vector3.down, Color.white, 5);
            }
        }

Er rutscht aber immer noch hin und her und auch nur der Y-Winkel ändert sich, alle anderen nicht. Ist hier immer noch was falsch?

Link zu diesem Kommentar
Auf anderen Seiten teilen

private void PlayerAlign() {
            if(alignSurfacePlayer) {
                RaycastHit hit;

                Vector3 position = new Vector3(transform.position.x, transform.position.y + 0.25f, transform.position.z);

                if(Physics.Raycast(position, Vector3.down, out hit, 5, alignmentLayerMask)) {
                    Quaternion targetRotation = Quaternion.FromToRotation(transform.up, hit.normal);
                    transform.rotation = Quaternion.Lerp(transform.rotation, targetRotation, Time.deltaTime * 2);
                }
                Debug.DrawRay(position, Vector3.down, Color.white, 5);
            }
        }

Ich verstehs nicht. :( Ich hab mir nun X Versionen mit Google gesucht und mach es genau so wie die. Passt das nun oder wieder was falsch?

EDIT: Rutscht wieder. :(

Link zu diesem Kommentar
Auf anderen Seiten teilen

vor 22 Stunden schrieb Kojote:
private void PlayerAlign() {
	if(alignSurface) {
    	RaycastHit hit;

        Transform child = null;
        if(child == null) { child = transform.GetChild(0).GetChild(0); }


        Vector3 raycastPoint = new Vector3(child.position.x, child.position.y + 0.25f, child.position.z);

        if(Physics.Raycast(raycastPoint, -child.forward, out hit, 5, groundLayers)) {
		Quaternion targetRotation = Quaternion.FromToRotation(child.forward, hit.normal) * child.rotation;
            child.rotation = Quaternion.Slerp(child.rotation, targetRotation, Time.deltaTime * 2);

            PlayerAlignCalculation(child);
        }
	}
}

private void PlayerAlignCalculation(Transform child) {
	child.rotation = Quaternion.Euler(
      Mathf.Clamp(WrapAngle(child.transform.eulerAngles.x), -MaxForwardRotation, MaxForwardRotation) * 
      (1 - MildForwardValue), 	
      
      child.transform.eulerAngles.y, 
      
      Mathf.Clamp(WrapAngle(child.transform.eulerAngles.z), -MaxHorizontalRotation, MaxHorizontalRotation) * 
      (1 - MildHorizontalValue)
    );
}

[Range(0f, 1f)]
public float MildForwardValue = 0f;

[Range(0f, 90f)]
public float MaxForwardRotation = 90f;

[Range(0f, 1f)]
public float MildHorizontalValue = 0f;

[Range(0f, 90f)]
public float MaxHorizontalRotation = 90f;

public float WrapAngle(float angle) {
	angle %= 360;
    if(angle > 180)
    	return angle - 360;
	return angle;
}

Sauberer bekomm ichs leider nicht hin, hoffe du kannst es nun besser lesen. :(

OK, ich erkläre es noch mal präziser.

Ich habe einen Vierbeinigen Charcter. Wenn ich eine Steigung nach oben laufe, bleibt der Character in der Horizontalen, sprich er passt sich nicht an das Terrain an was dafür sorgt das entweder immer Vorderbeine oder Hinterbeine in der Luft schweben.

wr54ao6c.jpg

Auf dem Parent Objekt sitzt die Steuerung und in dieser wollte ich nun eine Erweiterung einbauen, dass der Character sich dem Boden anpasst. Direkt das Parent Objekt an den Boden anpassen funktioniert nicht. Der Charkter fängt an zu wackeln und über den Boden zu stottern. Aus dem Grund bin ich dazu über gegangen lieder eine kleine Korrektur im Child Position durchzuführen. Problem ist, dass das Modell von Haus aus im Child Global eine X und Z Drehung drin hat.

Ich habe es nun mit der Methode PlayerAlign hinbekommen, dass er sich am Boden ausrichtet.

Problem ist nun:

A: Die Z Achse ist immer um knapp 1° nach rechts gedreht. Sieht man fast nicht aber ist unschön und würde es wollen, dass Z immer 0 ist, damit er nach vorn schaut.

B: Mit der Methode PlayerAlignCalculation wollte ich eine maximale Neigung einbauen. Die Methode funktionier jedoch nicht, da er im laufen sich um sich selber dreht. Siehte hier: https://streamable.com/3qi6j4

Hoffe ich konnte es nun besser erklären. :)

OK, ich hab mich noch mal durch den Code komplett durchgequält. Es scheint das Serverseitig da etwas zurück gesendet wird, dass die Position auch die ist, die sie laut Server sein soll. Verhindern soll dies, dass man im Client nicht mogelt. Gute Funktion, hier leider etwas nervig.

Bleibt also nur die Option es im zweiten Child zu machen, wie oben schon beschrieben. Hier hatte zwar die Ausrichtung funktioniert, sprich die Methode PlayerAlign, nicht jedoch die Nachkorrektur PlayerAlignCalculation bei der ich eine maximale Neigung angeben möchte.

Habe jetzt nur einmal in der PlayerAlignCalculation folgendes gemacht:

childTransform.rotation = Quaternion.Euler(0, 0, 0);

Im Spiel wird daraus 90 | -90 | -90

Hast du hier vielleicht eine Idee @Sascha?

Link zu diesem Kommentar
Auf anderen Seiten teilen

Das ist ein echt komplexes Ding. Hast bisher auch glaube ich nicht erwähnt, dass da Networking drinsteckt.

Grundsätzlich verstehe ich aber nicht, wo das Problem herkommt. Position kommt vom Server, Rotation wird vom Client berechnet. Das Rutschen muss ja dann vom Server kommen und sollte eigentlich nix mehr mit dem Rotationscode zu tun haben. Wenn die Rotation lokal passiert, dann sollte der Rotationscode beim Server auch gar nicht mehr ausgeführt werden.

Jedenfalls... Was liest du aus, dass du 90, -90, 90 kriegst? Wieder .rotation? Oder schaust du im Inspektor? Letzterer zeigt die lokale Rotation an. Wenn du also 0, 0, 0 als globale Rotation reinsteckst und dann was anderes als lokale Rotation rauskriegst, dann würde ich sagen, die Parents sind gedreht.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Mmm,.. Parent Gameobject hinzufügen. Charakters (Katze?) als Child in diesem Parent Gameobject einfügen, und dann das ParentObject mit Position, Rotation ansprechen. 

Nur eine flüchtige Idee. Wenn nicht funktioniert dann hab ich wohl die Aufgabe nicht verstanden :) 

lg 
Ares

Link zu diesem Kommentar
Auf anderen Seiten teilen

Geht leider nicht, da das Parent durch die Steuerung zwangsläufig die Katze sein muss.

 

Zitat

Aber du sagst ja, dass du einen autoritativen Server hast. Der kann den Client also zu einer anderen Position zwingen. Oder nicht? Das hieße, dass der Server eine andere Position rauskriegt als der Client, und deswegen rutscht das Ding. Richtig?

Korrekt. Der Server weis wo der Spieler ist, wenn der Spieler nach der Meinung des Servers an einer falschen Stelle steht wird er etwas korregiert, nögenfalls teleportiert. Genau so sieht es bei der Rotation aus.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Und wenn du die Rotationsgeschichte nicht drin hast? Dann wird nicht gerutscht? Das heißt, dass entweder Server oder Client (oder beide) beim Ausführen deines Rotationscodes die Position verändern (was nicht sein sollte) und danach sind die sich nicht mehr einig.

Dann musst du rausfinden, warum einer von beiden beim Rotieren die Position verändert und das fixen.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Archiviert

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

×
×
  • Neu erstellen...