Impedindo uma combinação de força de salto de corpo rígido e magnitude de salto no Unity3D


10

Estou construindo um jogo de corrida de mármore bastante simples no Unity3D. A bola é um objeto de física 3D que se move apenas nos eixos X e Y. Tem a capacidade de rolar para a esquerda e direita e pular. Coisas bem básicas, exceto pelo fato de eu ter atingido um problema de quebra de jogo: ao cair e bater no chão, a magnitude do salto da bola pode ser combinada com sua força de salto para criar um salto extra alto. Isso significa que, com o pressionamento oportuno do botão, o jogador pode fazer com que a bola salte exponencialmente mais alto, atingindo alturas involuntárias. Não consigo projetar níveis corretamente até que essa falha seja corrigida. Eu ilustrei este exemplo:

Ball Bouncing vs Ball Bouncing + Jumping

O salto, no entanto, não é tão simples quanto apenas atirar a bola para cima. Para facilitar a complexidade do design de níveis, programei o ângulo de salto em relação à superfície em que a bola está rolando.

Comparação de ângulo de salto de bola

A Figura 3 , nessa ilustração, é como meu jogo funciona até agora; não Figura 4 . Isso torna a solução do problema de salto + salto muito mais desafiadora, porque não posso simplesmente medir e definir uma força ou velocidade exata no eixo Y. Isso resulta em um comportamento estranho, que se torna dramaticamente mais perceptível à medida que a bola viaja em encostas mais íngremes.

Até agora, consegui conceber uma solução para todos os outros problemas de design deste jogo e depois descobrir como programá-los, mas este me deixou paralisado. Eu tentei várias abordagens diferentes, mas nenhuma delas funcionou.

Aqui está o script C # que controla o salto da bola:

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

Minha última tentativa foi tentar jump - rigidbody.velocity.magnitude * 50 , para reduzir o poder de salto pela velocidade que a bola está viajando. Quase resolveu o problema do salto + salto, reduzindo proporcionalmente a força do salto para zero, à medida que a velocidade da bola atingia o que parecia ser o equivalente em velocidade.magnitude. Ele funcionou parado, mas o problema é que também é responsável pela magnitude enquanto a bola está no chão, impedindo que ela role a toda velocidade e pule. Eu estava perto, mas não estava lá!

Sou um programador iniciante e estou perplexo aqui. Alguém pode me ajudar a encontrar uma solução criativa para esse problema? Contanto que o jogador seja capaz de saltar continuamente e pular cada vez mais alto, não consigo projetar nenhum nível, porque todos eles serão capazes de ser enganados. Eu adoraria seguir em frente - esse problema me impede há muito tempo, por isso gostaria muito de receber alguns conselhos!


Boa pergunta :) você tentou brincar com materiais físicos? Você pode definir o valor de retorno do terreno como zero (ou valor muito baixo). Talvez também o jogador, isso depende.
M156

Respostas:


0

Antes de tudo, quero dizer que sua pergunta está muito bem escrita e é um prazer :), você só precisa remover o que não é necessário no código (fontes de áudio etc.) e seria perfeito. Um brinde por isso.

Para a resposta, você pode limitar sua velocidade ao pular, o que impediria que você atingisse velocidades muito altas ao pressionar o botão de pular.


0

Enquanto eu, pessoalmente, adoro pular coelhos ... Como ponto de partida, devemos conhecer a "Velocidade do salto" pretendida como uma velocidade delta. Esta figura representa o aumento de velocidade (na linha com o "Jump Normal") durante o instante de pular uma vez.

Qualquer velocidade que o jogador já esteja alinhada com o Jump Normal pode ser vista como uma "Energia de salto" pré-existente. Isso leva a uma solução direta: a velocidade delta instantânea pode ser limitada, de modo que nunca faça com que o jogador seja acelerado além da velocidade alvo.

Para medir sua velocidade de salto pré-existente, podemos pegar o produto escalar do seu vetor de salto normalizado e a velocidade do seu jogador:

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

A "velocidade existente" também é forçada não negativa aqui; Quando o jogador está caindo, uma velocidade de salto negativa existente compensará sua queda, permitindo que eles pulem no ar se acionarem o salto enquanto caem.

Agora que sabemos quanto reduzir a velocidade delta com precisão, podemos calcular o "Vetor de salto" efetivo escalando o Salto Normal para a velocidade delta de destino.

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

Desta vez, a velocidade de salto ajustada está sendo forçada de forma não negativa; Se o jogador já estiver subindo mais rápido do que deveria saltar, atingiria uma velocidade ajustada negativa, o que lhes permitirá usar a ação de "pular" como freio. (para diminuir a velocidade de salto pretendida instantaneamente!)

Nota: acredito que seu contato X e Y já estão normalizados como um par. Eu incluí detalhes explícitos por uma questão de integridade.


0

Essa resposta talvez seja mais uma mudança de design do que você está procurando, mas e quanto a isso - a bola tem um curto período depois que o botão de salto é pressionado, onde permanece firmemente no chão e cancela qualquer momento vertical ascendente (talvez esmagando um para indicar uma compressão semelhante a uma mola) e depois salta para cima após a conclusão desse período. Isso resolveria o problema do momento do salto aumentando, embora também permitisse aos jogadores controlar se eles saltaram ou apenas saltaram. Também adiciona um atraso à função de salto, que pode ser visto como bom (parece mais natural) ou ruim (não permite que os jogadores tenham tempo suficiente para responder).

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.