Zapobieganie kombinacji siły skoku sztywnego ciała i wielkości odbicia w Unity3D

10

Buduję dość prostą marmurową grę wyścigową w Unity3D. Piłka to obiekt fizyki 3D, który porusza się tylko w osiach X i Y. Ma możliwość toczenia się w lewo i prawo oraz skakania. Całkiem proste rzeczy, z wyjątkiem tego, że natrafiłem na problem przełomowy: kiedy spadam i uderzam o ziemię, siłę odbicia piłki można połączyć z jej siłą skoku, aby uzyskać wyjątkowo wysoki skok. Oznacza to, że przy odpowiednim naciśnięciu przycisku gracz może spowodować, że piłka odbije się wykładniczo wyżej, osiągając niezamierzone wysokości. Nie mogę poprawnie zaprojektować poziomów, dopóki ten błąd nie zostanie naprawiony. Zilustrowałem ten przykład:

Odbijanie piłki vs Odbijanie piłki + Skakanie

Skakanie nie jest jednak tak proste, jak wystrzelenie piłki prosto w górę. W celu ułatwienia bardziej złożonego projektowania poziomów zaprogramowałem kąt skoku w stosunku do powierzchni, po której toczy się piłka.

Porównanie kąta skoku piłki

Rycina 3 na tej ilustracji pokazuje, jak do tej pory działa moja gra; nie rysunek 4 . To sprawia, że ​​rozwiązanie problemu odskoku i skoku jest o wiele trudniejsze, ponieważ nie mogę po prostu zmierzyć i ustawić dokładnej siły lub prędkości na osi Y. Takie postępowanie powoduje dziwne zachowanie, które staje się znacznie bardziej zauważalne, gdy piłka porusza się po bardziej stromych zboczach.

Do tej pory udało mi się znaleźć rozwiązanie wszystkich innych problemów projektowych w tej grze, a następnie dowiedzieć się, jak je zaprogramować, ale ten problem mnie utknął. Próbowałem wielu różnych podejść, ale żadne z nich nie zadziałało.

Oto skrypt C #, który kontroluje skok piłki:

using UnityEngine;
using System.Collections;

public class BallJumping : MonoBehaviour {

    public System.Action onJump;
    public Rigidbody objRigidbody; // Set this to the player
    public bool isGrounded; // Determines whether or not the ball is on the ground
    public Transform groundChecker; // A child object that's slightly larger than the ball
    public float groundRadius = 0.6f;
    public LayerMask whatIsGround; // Determines what layers qualify as ground
    public AudioClip jumpSFX;
    public AudioClip stickyJumpSFX;
    private float p_WillJumpTimeRemaining; // Grace periods before/after hitting the ground to trigger jump
    private float p_CanJumpTimeRemaining;
    public float earlyJumpToleranceDuration = 0.2f;
    public float lateJumpToleranceDuration = 0.2f;
    public float jump = 500f; // Jumping power
    private float halfJump = 250f; // Used for the sticky puddles
    public bool stuck = false; // Used for sticky materials
    private float contactX;
    private float contactY;


    // Input for jumping
    void Update () {
        if (Input.GetButtonDown ("Jump") && isGrounded == true) {
            ProcessJump();
        }
    }


    // Continuously checks whether or not the ball is on the ground
    void FixedUpdate () {
        if (Physics.CheckSphere (groundChecker.position, groundRadius, whatIsGround) == true) {
            isGrounded = true;
        } else {
            isGrounded = false;
        }
    }


    // Sets a grace period for before or after the ball contacts the ground for jumping input
    void ProcessJump () {
        bool boolGetJump = Input.GetButtonDown("Jump");

        if (boolGetJump && isGrounded == false) {
            p_WillJumpTimeRemaining = earlyJumpToleranceDuration;
        } else {
            if (p_WillJumpTimeRemaining > 0) {
                p_WillJumpTimeRemaining -= Time.fixedDeltaTime;
            }
        }

        if (isGrounded) {
            p_CanJumpTimeRemaining = lateJumpToleranceDuration;
        }

        if (isGrounded || p_WillJumpTimeRemaining > 0) {
            Jump();
        }

        if (p_CanJumpTimeRemaining > 0) {
            p_CanJumpTimeRemaining -= Time.fixedDeltaTime;
        }
    }


    // Sticky puddles script -- hinders jumping while in the puddle
    void OnTriggerEnter (Collider collision) {
        if (collision.gameObject.tag == "Sticky") {
            stuck = true;
        }
    }

    void OnTriggerExit (Collider collision) {
        if (collision.gameObject.tag == "Sticky") {
            stuck = false;
        }
    }


    // Calculates the normals for the jump angle
    void OnCollisionStay (Collision collision) {
        Debug.Log ("Collision.");
        foreach (ContactPoint contact in collision.contacts) {
            contactX = contact.normal.x;
            contactY = contact.normal.y;
        }
    }


    // Controls jumping
    void Jump() {
        Debug.Log ("Jump.");
        p_WillJumpTimeRemaining = 0.0f;
        p_CanJumpTimeRemaining = 0.0f;
        halfJump = jump * 0.5f; // Cuts jumping force in half while in a sticky puddle

        GetComponent<AudioSource>().volume = 1;
        GetComponent<AudioSource>().pitch = Random.Range (0.9f, 1.1f);

        if (stuck == false) {
            objRigidbody.AddForce (contactX * jump, contactY * jump, 0);
            GetComponent<AudioSource>().clip = jumpSFX;
            GetComponent<AudioSource>().Play ();
        }
        else if (stuck == true) {
            objRigidbody.AddForce (contactX * halfJump, contactY * halfJump, 0);
            GetComponent<AudioSource>().clip = stickyJumpSFX;
            GetComponent<AudioSource>().Play ();
        }


        if (onJump != null) {
            onJump();
        }
    }
}

Moją ostatnią próbą było spróbować skoku - rigidbody.velocity.magnitude * 50 , aby zmniejszyć siłę skoku o prędkość, z jaką porusza się piłka. Prawie rozwiązało to problem odbicia i skoku, proporcjonalnie zmniejszając siłę skoku do zera, gdy prędkość piłki osiągnęła wartość, która wydawała się być równoważna z prędkością prędkości. Działa bezruchowo, ale problem polega na tym, że uwzględnia również wielkość, gdy piłka jest uziemiona, co zapobiega toczeniu się piłki z pełną prędkością i skakaniu. Byłem blisko, ale nie do końca!

Jestem początkującym programistą i utknąłem tutaj. Czy ktoś może mi pomóc znaleźć twórcze rozwiązanie tego problemu? Tak długo, jak gracz jest w stanie nieustannie podskakiwać i skakać coraz wyżej, nie mogę projektować żadnych poziomów, ponieważ wszystkie będą mogły zostać oszukane. Chciałbym iść naprzód - ten problem powstrzymywał mnie od dłuższego czasu, dlatego bardzo doceniam kilka rad!

Okup
źródło
Ładne pytanie :) czy próbowałeś bawić się materiałami fizycznymi? Możesz ustawić sprężystość podłoża na zero (lub bardzo niską wartość). Może zależy też od gracza.
M156

Odpowiedzi:

0

Przede wszystkim chcę powiedzieć, że twoje pytanie jest bardzo dobrze napisane i to przyjemność :), wystarczy usunąć to, co nie jest konieczne w kodzie (źródła audio itp.) I byłoby idealnie. Pozdrawiam za to.

Na odpowiedź, można zacisnąć swoją prędkość podczas skoków, które uniemożliwiają osiągnięcie zbyt wysokich prędkości po naciśnięciu przycisku skoku.

Bluk
źródło
0

Podczas gdy ja osobiście uwielbiam skakanie króliczka ... Jako punkt wyjścia powinniśmy poznać zamierzoną „prędkość skoku” jako prędkość delta. Liczba ta przedstawia wzrost prędkości (w linii z „normalnym skokiem”) w chwili jednokrotnego skoku.

Każda prędkość, którą gracz ma już zgodnie z normalnym skokiem, może być postrzegana jako istniejąca wcześniej „energia skoku”. Prowadzi to do prostego rozwiązania: chwilowa prędkość delta może być ograniczona tak, że nigdy nie spowoduje przyspieszenia gracza powyżej prędkości docelowej.

Aby zmierzyć istniejącą wcześniej prędkość skoku, możemy wziąć iloczyn iloczynu znormalizowanego wektora skoku i prędkości gracza:

Vector2 JumpNormal = Vector2(contactX, contactY).normalized;
Vector2 PlayerVelocity = objRigidbody.velocity;
float ExistingSpeed = Vector2.Dot(PlayerVelocity, JumpNormal);
if (ExistingSpeed < 0) ExistingSpeed = 0;

„Istniejąca prędkość” jest tu również wymuszana jako nieujemna; Kiedy gracz spada, ujemna istniejąca prędkość skoku zrekompensuje jego upadek, umożliwiając mu odbicie się na cienkim powietrzu, jeśli uruchomi skok podczas upadku.

Teraz, gdy wiemy już, jak dokładnie zmniejszyć prędkość delta, możemy obliczyć skuteczny „wektor skoku” poprzez skalowanie normalnego skoku do docelowej prędkości delta.

float AdjustedSpeed = JumpSpeed - ExistingSpeed;
if (AdjustedSpeed < 0) AdjustedSpeed = 0;
Vector2 JumpVector = JumpNormal * AdjustedSpeed;
objRigidbody.velocity += JumpVector;

Tym razem Zmodyfikowana prędkość skoku jest wymuszana nieujemnie; Jeśli gracz już rośnie szybciej, niż powinien być w stanie skoczyć, osiągnie ujemną skorygowaną prędkość, co pozwoli mu użyć akcji „skoku” jako hamulca. (aby natychmiast zwolnić do zamierzonej prędkości skoku!)

Uwaga: uważam, że twój kontakt X i Y są już znormalizowane jako para. Podałem jednak wyraźne szczegóły ze względu na kompletność.

MickLH
źródło
0

Ta odpowiedź może być bardziej zmianą projektu, niż szukasz, ale co powiesz na to - piłka ma krótki okres po naciśnięciu przycisku skoku, gdzie mocno opiera się o ziemię i niweluje pęd pionowy w górę (może zgnieść bit oznaczający sprężynującą kompresję), a następnie przeskakuje w górę po zakończeniu tego okresu. To rozwiązałoby problem dodawania pędu do skoku, chociaż pozwoliłoby również graczom kontrolować, czy odbijali się, czy po prostu skakali. Dodaje także opóźnienie funkcji skoku, co można uznać za dobre (bardziej naturalne) lub złe (nie pozwala graczom na wystarczającą ilość czasu na odpowiedź).

Bynine
źródło