Запобігання поєднанню стрибкових сил та величини відмов у Unity3D


10

Я будую досить просту мармурову гоночну гру в Unity3D. М'яч - це об'єкт фізики 3D, який рухається лише по осях X і Y. Він має здатність котитися вліво і вправо і стрибати. Досить основні речі, за винятком того, що я зіткнувся з проблемою розриву гри: При падінні та ударі об землю величину відскоку м'яча можна поєднувати зі своєю стрибковою силою для створення надзвичайно високого стрибка. Це означає, що при чіткому натисканні кнопок гравець може призвести до того, що м'яч підстрибує експоненціально вище, досягаючи ненавмисних висот. Я не можу правильно розробити рівні дизайну, поки ця проблема не буде виправлена. Я проілюстрував цей приклад:

М'яч відскакує проти м'яча підстрибує + стрибає

Стрибки, однак, не такі прості, як просто вистріл м'яча прямо вгору. Щоб полегшити більш складність в дизайні рівнів, я запрограмував кут стрибка таким чином, щоб він був відносно поверхні, на яку котиться м'яч.

Порівняння кута стрибка куля

На малюнку 3 на цій ілюстрації показано, як працює моя гра до цих пір; не Рисунок 4 . Це робить рішення проблеми відскоку + стрибка набагато складнішим, оскільки я не можу просто виміряти та встановити точну силу чи швидкість на осі Y. Це призводить до дивної поведінки, яка стає помітно помітнішою, коли м'яч рухається крутішими схилами.

Поки що мені вдалося розібратися у вирішенні всіх інших проблем із дизайном у цій грі, а потім дізнатися, як їх запрограмувати, але ця мене застрягла. Я спробував декілька різних підходів, але жоден з них не спрацював.

Ось сценарій C #, який керує стрибками м'яча:

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

Моєю останньою спробою було спробувати стрибок - rigidbody.velocity.magnitude * 50 , щоб зменшити силу стрибка на швидкість, з якою рухається м'яч. Це майже вирішило задачу відскоку + стрибка, пропорційно зменшивши силу стрибка до нуля, оскільки швидкість кулі досягла того, що здавалося еквівалентним у швидкості. Це спрацьовувало з затримки, але проблема полягає в тому, що він також враховує величину, коли м'яч заземлений, не даючи кульці котитися на повній швидкості і стрибати. Я був поруч, але не зовсім там!

Я початківець програміст, і я тут спотикаюся. Чи може хтось допомогти мені знайти творче рішення цієї проблеми? Поки гравець може постійно підстрибувати і стрибати все вище і вище, я не можу створити жодних рівнів, тому що всі вони просто зможуть пройти обман. Я хотів би рухатись вперед - ця тема вже давно стримує мене, тому я дуже вдячний за пораду!


Приємне запитання :) Ви спробували пограти з фізичними матеріалами? Ви можете встановити придатність землі до нуля (або дуже низького значення). Може бути і гравець, це залежить.
M156,

Відповіді:


0

Перш за все, я хочу сказати, що ваше запитання дуже добре написане, і це приємно :), вам просто потрібно видалити те, що не потрібно в коді (аудіо джерела тощо), і це було б ідеально. Ура за це.

Для відповіді ви можете зафіксувати свою швидкість при стрибку, що не дасть вам досягти занадто високих швидкостей при натисканні кнопки стрибка.


0

Поки я особисто люблю стрибки зайчика ... Як вихідний пункт, ми повинні знати задуману "стрибкову швидкість" як дельта-швидкість. Цей показник відображає збільшення швидкості (у рядку з «Нормальним стрибком») протягом моменту стрибка один раз.

Будь-яка швидкість, яку вже має гравець у відповідність зі звичайною стрибкою, може розглядатися як попередня "Перехідна енергія". Це призводить до прямого рішення: Миттєва дельта-швидкість може бути обмежена таким чином, що це ніколи не призведе до того, що гравець прискориться за межею швидкості руху.

Для того, щоб виміряти вашу попередню стрибкову швидкість, ми можемо взяти точковий добуток нормалізованого вашого вектора стрибків та швидкості вашого гравця:

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

"Існуюча швидкість" також вимушена тут не негативна; Коли гравець падає, негативна наявна швидкість стрибка компенсує їх падіння, дозволяючи їм підстрибувати на повітрі, якщо вони спрацьовують стрибок під час падіння.

Тепер, коли ми знаємо, наскільки точно зменшити швидкість дельти, ми можемо обчислити ефективний "Перехідний вектор", масштабуючи Нормальний стрибок до цільової швидкості дельти.

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

Цього разу відрегульована швидкість стрибка змушена бути негативною; Якщо гравець вже піднімається швидше, ніж він повинен мати можливість стрибати, він досягне негативної регульованої швидкості, що дозволяє їм використовувати дію "стрибок" як гальмо. (миттєво уповільнити до наміченої швидкості стрибка!)

Примітка. Я вважаю, що ваші контакти X і Y вже нормалізовані як пара. Я також включив чіткі деталі заради повноти.


0

Ця відповідь є, мабуть, більшою мірою зміною дизайну, ніж ви шукаєте, але як щодо цього - м'яч має короткий період після натискання кнопки стрибка, де він міцно тримається на землі і скасовує будь-який вертикальний імпульс вгору (можливо, стискаючи біт для позначення пружинного стиснення), потім підскакує вгору після закінчення цього періоду. Це дозволить вирішити проблему додавання імпульсу відскоку до стрибка, хоча це також дозволить гравцям контролювати, підстрибували вони чи просто стрибали. Це також додає затримку функції стрибка, яку можна сприймати як гарну (відчуває себе більш природною) або погану (не дає гравцям достатньо часу для відповіді).

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.