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!

Resgate
fonte
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.

Bluk
fonte
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.

MickLH
fonte
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).

Bynine
fonte