Jump to content
Unity Insider Forum

Springen geht manchmal nicht


Ideentoeter

Recommended Posts

Hallo,

 

folgendes Problem: Wir haben eine Figur, die läuft und springt, ganz klassisch. Jetzt besteht das Problem, das die Figur manchmal bei Druck auf die Leertaste nicht springt. Halt selten, aber es kommt vor. Ich hab keine Ahnung, woran das liegen kann, da ich es irgendwie nicht rekonstruieren kann. Es scheint immer mal ganz random zu passieren.

 

Jetzt hatte ich diesbezüglich was gefunden, wo geraten wurde, die Tastenabfrage in 'Update' statt 'fixedUpdate' zu packen, das ist aber schon der Fall.

 

Jetzt weiß ich gerade nicht weiter. Kann da irgendwas in den Blendtree's verwurstet sein oder ist es eher ein Codeproblem? Hatte jemand vielleicht mal ein ähnliches Problem und könnte mir helfen?

 

Danke und Gruß

Link zu diesem Kommentar
Auf anderen Seiten teilen

Denk ich mir eigentlich auch. Mh, also ich poste mal ein Stück aus dem Script, von dem ich denke, dass da was sein könnte. Ich hoffe mein Programmierer hat damit kein Problem :D Ich selbst hab davon nur rudimentär Ahnung.

Wir nutzen nen Capsule Collider und Rigidbody statt dem Unity Character Controller

 

   // Use this for initialization
   void Awake()
   {
    this.animator = this.gameObject.GetComponent<Animator>();
    //animator.SetLayerWeight(1, 1);
    //animator.SetLayerWeight(2, 1);
    this.moveDirectionCurrent = transform.forward;
    this.capsuleCollider = this.gameObject.GetComponent<CapsuleCollider>();
   }
   // Update is called once per frame
   void Update()
   {
    hInput = Input.GetAxis("Horizontal");
    // Mathf.Pow(negativ number, number) returns NaN
    h = hInput > StartMoveOffset || hInput < -StartMoveOffset ? Mathf.Pow(Mathf.Abs(hInput), 0.8f) : 0f;
    v = Input.GetAxis("Vertical");
    mouseX = Input.GetAxis("Mouse X");
    mouseY = Input.GetAxis("Mouse Y");
    jumpButtonPressed = Input.GetKeyDown(KeyCode.Space);
    Update_Animator();
   }
   void FixedUpdate()
   {
    this.applyPlayerMovement();
    this.isGrounded = this.checkIfGrounded();
    this.checkCollision();
   }
   void LateUpdate()
   {
    this.calculatePlayerMovement();
   }
   #endregion unity events
   /// <summary>
   /// Testet per Spherecast ob der Charakter grounded ist. Die Sphere hat den Radius des CapsuleColliders.
   /// </summary>
   /// <returns>true, wenn der Abstand zum Boden kleiner als 0.1 ist</returns>
   bool checkIfGrounded()
   {
    RaycastHit hit;
    if (Physics.SphereCast(transform.position + new Vector3(0, 0.7f, 0), this.capsuleCollider.radius, Vector3.down, out hit))
    {
	    return transform.position.y - hit.point.y < 0.1f;
    }
    return false;
   }

Link zu diesem Kommentar
Auf anderen Seiten teilen

Das selbe ist mir bereits beim Unity Mecanim Tutorial aufgefallen, manchmal springt dieser Roboter einfach nicht, ich fands aber so selten, und ehrlich gesagt auch so unwichtig (ich denke da würde sich keiner drüber aufregen) dass ich es ignoriert habe, wie gesagt, kommt nicht wirklich oft vor...

 

Würde mich aber auch interessieren woran das liegen könnte :)

Link zu diesem Kommentar
Auf anderen Seiten teilen

Jo, ich sehe das Problem. Und es ist immer noch das FixedUpdate-Update-Problem :)

 

Aber fangen wir mal am Anfang an:

Grafische Anwendungen wie Spiele haben immer die "Main Loop". Das ist eine Schleife, die einfach so lange läuft, wie die Anwendung aktiv ist.

Sie tut grundlegend immer abwechselnd genau zwei Dinge so schnell sie kann: Update und Rendering.

Im Update findet die Spiellogik statt. Positionen werden verändert, Farben getauscht, Objekte werden hinzugefügt oder gelöscht.

Beim Rendering wird die aktuelle Szene gerendert und danach angezeigt. Dann kommt wieder Update.

Da das Rendering uns beim Coden kaum interessiert und das meiste sowieso Unity macht, bezeichnet man diesen Zyklus als Update-Zyklus. Dieser läuft durchgehend und so schnell, wie es der PC halt hinkriegt.

 

Jetzt ist "so schnell wie du kannst" aber für besitmmte Sachen nur mäßig gut.

Gerade für Physik. Man stelle sich die Berechung einer Flugkurve bei 30 fps und bei 60 fps vor. Das Objekt wird jeweils woanders aufkommen. Die Lösung? FixedUpdate.

FixedUpdate funktioniert folgendermaßen: Es gibt den sog. "Fixed Timestep", der in Unity per default auf 0.02 Sekunden liegt.

In jedem Update schaut Unity, ob das letzte FixedUpdate schon so lange her ist, und wenn ja, führt es FixedUpdate aus.

Sollte aufgrund eines Mikrorucklers das letzte Mal schon z.B. 0.2 Sekunden her sein, führt Unity dann auch gleich zehn Mal FixedUpdate aus. Was dadruch passiert ist, dass FixedUpdate im Schnitt genau ein Mal alle 0.02 Sekunden ausgeführt wird. Unabhängig von der Framerate des Update-Zyklus.

 

Warum nun diese Erklärung...?

Das Problem liegt hier darin, dass die Input-Klasse im Update-Zyklus aktualisiert wird.

Welcher Button gerade gedrückt ist und welcher nicht, wird also jeden Frame ein Mal überprüft und aktualisiert.

Das gleiche gilt für GetButtonDown. Es schaut, ob der Button in diesem Frame gedrückt wurde und im letzten nicht.

 

Der Ablauf, bei dem das Springen funktioniert, sieht so aus:

  • Update, Button nicht gedrückt
  • Update, Button gedrückt, Zeit für FixedUpdate

Das passiert schon hin und wieder. 0.02 Sekunden pro FixedUpdate entspricht 50 fps, was schon ziemlich synchron zu den 60fps ist, die du haben dürftest.

 

Wenn das Springen fehlschlägt, sieht der Ablauf so aus:

  • Update, Button nicht gedrückt
  • Update, Button gedrückt (kein FixedUpdate!)
  • Update, Button immer noch oder nicht mehr gedrückt, Zeit für FixedUpdate

Wenn du jetzt in diesem Fall in FixedUpdate nach GetButtonDown fragst, kommt auf jeden false zurück, da das erstmalige Drücken des Knopfes schon mindestens ein Update zurück liegt, und sich die Input-Klasse schon mindestens ein zweites Mal hat updaten lassen.

 

Dein Fix hilft da gar nichts:

  • Update, Button nicht gedrückt - jumpButtonPressed = false
  • Update, Button gedrückt - jumpButtonPressed = true (kein FixedUpdate!)
  • Update, Button immer noch oder nicht mehr gedrückt - jumpButtonPressed = false, Zeit für FixedUpdate

Deine Variable spiegelt immer exakt den Rückgabewert von GetButtonDown wieder, und der ist eben false.

 

Lange Rede, kurzer Sinn: GetButtonDown kannst du für Dinge in FixedUpdate nicht benutzen, da es auf dem Update-Zyklus liegt.

Du musst also das "diesen Frame gedrückt, aber nicht im letzten Frame" selber nachimplementieren. Und dabei "Frame" durch "FixedUpdate" ersetzen.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Archiviert

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

×
×
  • Neu erstellen...