Empêcher une combinaison de force de saut de Rigidbody et d'amplitude de rebond dans Unity3D

10

Je construis un jeu de course de marbre assez simple dans Unity3D. La balle est un objet physique 3D qui se déplace uniquement sur les axes X et Y. Il a la capacité de rouler à gauche et à droite et de sauter. Assez basique, sauf que j'ai rencontré un problème révolutionnaire: en tombant et en frappant le sol, l'amplitude du rebond de la balle peut être combinée avec sa force de saut pour créer un saut extra-haut. Cela signifie que, avec des pressions de bouton bien synchronisées, le joueur peut faire rebondir la balle de manière exponentielle plus haut, atteignant des hauteurs involontaires. Je ne peux pas concevoir correctement les niveaux tant que ce problème n'est pas résolu. J'ai illustré cet exemple:

Ball Bouncing vs Ball Bouncing + Jumping

Cependant, le saut n'est pas aussi simple que de simplement tirer le ballon vers le haut. Afin de faciliter la complexité de la conception des niveaux, j'ai programmé l'angle de saut par rapport à la surface sur laquelle la balle roule.

Comparaison d'angle de saut de balle

La figure 3 , dans cette illustration, montre comment mon jeu fonctionne jusqu'à présent; pas la figure 4 . Cela rend la résolution du problème de rebond + saut beaucoup plus difficile, car je ne peux pas simplement mesurer et définir une force ou une vitesse exacte sur l'axe Y. Cela entraîne un comportement étrange, qui devient considérablement plus visible lorsque la balle se déplace sur des pentes plus raides.

Jusqu'à présent, j'ai été en mesure de concevoir une solution à tous les autres problèmes de conception de ce jeu et de découvrir comment les programmer, mais celui-ci m'a bloqué. J'ai essayé un certain nombre d'approches différentes, mais aucune n'a fonctionné.

Voici le script C # qui contrôle le saut de la balle:

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();
        }
    }
}

Ma dernière tentative a été d'essayer de sauter - rigidbody.velocity.magnitude * 50 , pour réduire la puissance de saut de la vitesse à laquelle la balle se déplace. Il a presque résolu le problème de rebond + saut, en réduisant proportionnellement la force de saut à zéro lorsque la vitesse de la balle a atteint ce qui semblait être l'équivalent en vitesse.magnitude. Il a fonctionné à l'arrêt, mais le problème est qu'il tient également compte de l'amplitude pendant que la balle est au sol, empêchant la balle de rouler à pleine vitesse et de sauter. J'étais proche, mais pas tout à fait là!

Je suis un programmeur novice et je suis perplexe ici. Quelqu'un peut-il m'aider à trouver une solution créative à ce problème? Tant que le joueur est capable de rebondir et de sauter de plus en plus haut, je ne peux concevoir aucun niveau, car ils pourront tous être trompés. J'adorerais continuer - ce problème me retient depuis longtemps, alors j'apprécierais grandement quelques conseils!

Une rançon
la source
Belle question :) avez-vous essayé de jouer avec des matériaux physiques? Vous pouvez régler le rebond du sol à zéro (ou une valeur très faible). Peut-être aussi le joueur, ça dépend.
M156

Réponses:

0

Tout d'abord, je veux dire que votre question est très bien écrite et c'est un plaisir :), il vous suffirait de supprimer ce qui n'est pas nécessaire dans le code (audiosources, etc.) et ce serait parfait. Bravo pour ça.

Pour la réponse, vous pouvez restreindre votre vitesse lors du saut, ce qui vous empêcherait d'atteindre des vitesses trop élevées en appuyant sur le bouton de saut.

Bluk
la source
0

Bien que j'aime personnellement les sauts de lapin ... Comme point de départ, nous devrions connaître la "vitesse de saut" prévue comme une vitesse delta. Ce chiffre représente l'augmentation de la vitesse (dans la ligne avec le "Jump Normal") au moment de sauter une fois.

Toute vitesse que le joueur a déjà en ligne avec le Jump Normal peut être considérée comme une "Jump Energy" préexistante. Cela conduit à une solution simple: la vitesse delta instantanée peut être limitée de telle sorte qu'elle n'entraîne jamais d'accélération du joueur au-delà de la vitesse cible.

Afin de mesurer votre vitesse de saut préexistante, nous pouvons prendre le produit scalaire de votre vecteur de saut normalisé et la vitesse de votre joueur:

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

La "vitesse existante" est également forcée non négative ici; Lorsque le joueur tombe, une vitesse de saut existante négative compensera sa chute, ce qui lui permettra de rebondir dans les airs s'il déclenche le saut en tombant.

Maintenant que nous savons combien réduire précisément la vitesse delta, nous pouvons calculer le "vecteur de saut" efficace en mettant à l'échelle le saut normal à la vitesse delta cible.

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

Cette fois, la vitesse de saut ajustée est forcée non négative; Si le joueur monte déjà plus vite qu'il ne devrait pouvoir sauter, il atteindrait une vitesse ajustée négative, ce qui lui permet d'utiliser l'action de "saut" comme freins. (pour ralentir à la vitesse de saut prévue instantanément!)

Remarque: Je pense que vos contacts X et Y sont déjà normalisés en tant que paire. J'ai cependant inclus des détails explicites par souci d'exhaustivité.

MickLH
la source
0

Cette réponse est peut-être plus un changement de conception que ce que vous recherchez, mais qu'en est-il - la balle a une courte période après avoir appuyé sur le bouton de saut où elle reste fermement au sol et annule tout élan vertical vers le haut (peut-être en écrasant un peu pour désigner une compression de type ressort), puis saute vers le haut après la fin de cette période. Cela résoudrait le problème de l'élan du rebond s'ajoutant au saut, même si cela permettrait également aux joueurs de contrôler s'ils ont rebondi ou simplement sauté. Il ajoute également un retard à la fonction de saut, qui peut être considéré comme bon (semble plus naturel) ou mauvais (ne laisse pas suffisamment de temps aux joueurs pour répondre).

Bynine
la source