Unity3D'de Sert Cisim Atlama Gücü ve Sıçrama Büyüklüğünün Birleşmesini Önleme


10

Unity3D'de oldukça basit bir mermer yarış oyunu inşa ediyorum. Top, yalnızca X ve Y eksenlerinde hareket eden bir 3D fizik nesnesidir. Sola ve sağa dönme ve zıplama yeteneğine sahiptir. Oldukça basit şeyler, ancak bir oyun kırma problemine çarptığımda: Yere düştüğünde ve yere çarptığında, topun sıçrama büyüklüğü, ekstra yüksek bir sıçrama oluşturmak için atlama gücüyle birleştirilebilir. Bu, iyi zamanlanmış düğmeye basıldığında, oyuncunun topun katlanarak daha yüksek bir şekilde sıçramasına ve istenmeyen yüksekliklere ulaşmasına neden olabileceği anlamına gelir. Bu aksaklık giderilene kadar seviyeleri düzgün bir şekilde tasarlayamıyorum. Bu örneği gösterdim:

Top Zıplayan vs Top Zıplayan + Atlama

Ancak zıplama, topu yukarı doğru fırlatmak kadar basit değildir. Seviye tasarımında daha fazla karmaşıklık sağlamak için, atlama açısını topun yuvarlandığı yüzeye göre programladım.

Bilya Atlama Açısı Karşılaştırması

Şekil 3 , bu örnekte, oyunumun şu ana kadar nasıl çalıştığı; Şekil 4 değil . Bu, sıçrama + atlama problemini çözmeyi çok daha zorlaştırır, çünkü Y ekseninde kesin bir kuvvet veya hız ölçüp ayarlayamıyorum. Bunu yapmak, top daha dik yamaçlarda ilerledikçe daha belirgin hale gelen garip davranışlarla sonuçlanır.

Şimdiye kadar, bu oyundaki diğer tüm tasarım sorunlarına bir çözüm tasarlayabildim ve sonra bunları nasıl programlayacağımı öğrendim, ama bu beni sıkıştı. Birkaç farklı yaklaşım denedim, ama hiçbiri işe yaramadı.

Topun zıplamasını kontrol eden C # betiği:

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

En son denemem, atlama gücünü topun hareket hızıyla düşürmek için zıplamayı denemekti - rigidbody.velocity.magnitude * 50 . Topun hızı, hızdaki eşdeğeri gibi görünen şeye ulaştıkça, sıçrama kuvvetini orantılı olarak sıfıra düşürerek hemen hemen sıçrama + atlama problemini çözdü. Duruştan çalıştı, ancak sorun şudur, top topraklandığında büyüklüğün hesabını yapar, topun tam hızda yuvarlanmasını ve zıplamasını önler. Yakındım, ama tam olarak orada değil!

Ben acemi bir programcıyım ve burada güldüm. Herkes bu soruna yaratıcı bir çözüm bulmama yardımcı olabilir mi? Oyuncu sürekli zıplayabildiği ve daha yüksek ve daha yüksek zıplayabildiği sürece, herhangi bir seviye tasarlayamıyorum, çünkü hepsi sadece aldatılabilecek. Devam etmek isterdim - bu sorun beni uzun zamandır geri tutuyor, bu yüzden bazı tavsiyeleri çok takdir ediyorum!


Güzel soru :) fizik malzemelerle oynamayı denedin mi? Zeminin zıplamasını sıfıra (veya çok düşük bir değere) ayarlayabilirsiniz. Belki oyuncu da değişir.
M156

Yanıtlar:


0

Her şeyden önce, sorunuzun çok iyi yazılmış olduğunu ve bir zevk olduğunu söylemek istiyorum :), sadece kodda gerekli olmayanları kaldırmanız gerekir (ses kaynakları, vb.) Ve mükemmel olurdu. Şerefe.

Cevap için, atlama sırasında hızınızı sıkıştırabilirsiniz , bu da atlama düğmesine basarken çok yüksek hızlara ulaşmanızı önler.


0

Şahsen tavşanı atlamayı sevdiğimde ... Bir başlangıç ​​noktası olarak, istenen "Atlama Hızı" nı bir delta hızı olarak bilmeliyiz. Bu şekil bir kez atlama anında hız artışını ("Normal Atla" ile aynı doğrultuda) temsil eder.

Oyuncunun Zıplama Normaline zaten uygun olan tüm hızları önceden var olan bir "Zıplama Enerjisi" olarak görülebilir. Bu, basit bir çözüme yol açar: Anlık delta hızı, oyuncunun hedef hızın ötesinde hızlanmasına neden olmayacak şekilde sınırlandırılabilir.

Önceden var olan Atlama Hızınızı ölçmek için, normalize edilmiş Atlama Vektörünüzün nokta ürününü ve oyuncunuzun hızını alabiliriz:

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

"Mevcut Hız" da burada negatif olmayan bir şekilde zorlanır; Oyuncu düştüğünde, negatif bir mevcut atlama hızı düşmelerini telafi eder ve düşerken zıplamayı tetiklerse ince havaya sıçramasına izin verir.

Artık delta hızını tam olarak ne kadar azaltacağımızı bildiğimize göre, Normal Atlama Normalini hedef delta hızına ölçekleyerek etkili "Atlama Vektörü" nü hesaplayabiliriz.

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

Bu kez Ayarlanmış Atlama Hızı negatif olmayan bir şekilde zorlanır; Eğer oyuncu zıplayabileceğinden daha hızlı yükseliyorsa, negatif bir hız elde edecek ve bu da "zıplama" eylemini fren olarak kullanmalarına izin verecektir. (hedeflenen atlama hızına anında yavaşlamak için!)

Not: X ve Y kontağınızın zaten bir çift olarak normalleştirildiğine inanıyorum. Yine de bütünlük uğruna açık ayrıntı ekledim.


0

Bu cevap belki de aradığınızdan daha fazla bir tasarım değişikliğidir, ancak buna ne dersiniz - top zıplama düğmesine basıldıktan sonra yerde sıkıca durduğu ve yukarı doğru dikey momentumu iptal ettiği kısa bir süreye sahiptir (belki de yay benzeri bir sıkıştırmayı belirtmek için biraz), daha sonra bu süre sona erdikten sonra yukarı doğru sıçrar. Bu, zıplamanın sıçramaya ivme katması sorununu çözecektir, ancak oyuncuların zıplayıp zıplamadıklarını kontrol etmelerine de izin verecektir. Ayrıca iyi (daha doğal hisseder) veya kötü (oyuncuların cevap vermesi için yeterli zaman tanımaz) olarak görülen atlama işlevine bir gecikme ekler.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.