在Unity3D中防止刚体跳跃力和弹跳幅度的组合


10

我正在Unity3D中构建一个相当简单的大理石赛车游戏。球是仅在X和Y轴上移动的3D物理对象。它具有左右滚动和跳跃的能力。非常基本的东西,除了我遇到了打破比赛的问题:当摔倒并撞击地面时,球的反弹幅度可以与其弹跳力相结合,从而产生超高跳跃。这意味着,通过适当地按下按钮,玩家可以使球反弹得更高,达到意想不到的高度。在解决此故障之前,我无法正确设计关卡。我已经说明了这个示例:

弹跳球vs弹跳球+跳跃

但是,跳跃并不像直接将球向上射击那样简单。为了简化关卡设计中的复杂性,我将跳跃角度编程为相对于球在其上滚动的表面。

球跳角比较

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

我最近的尝试是尝试跳跃-刚体。速度。幅度* 50,以通过球的行进速度降低跳跃力。当球的速度达到看上去与速度,幅度相等时,通过将跳跃力成比例地减小到零,它几乎解决了弹跳+跳跃问题。它从静止状态开始工作,但问题是,它还考虑了球着地时的大小,从而阻止了球全速滚动和跳跃。我离那儿很近,但还不在那里!

我是新手程序员,在这里很困惑。谁能帮助我找到解决这个问题的创造性方法?只要玩家能够不断弹跳并跳得更高,我就无法设计任何关卡,因为它们只会被欺骗。我很乐意继续前进-这个问题困扰了我很长时间,因此,我非常感谢您提供一些建议!


好问题:)您是否尝试过使用物理材料?您可以将地面的反弹度设置为零(或非常低的值)。也许也是玩家,这取决于。
M156 2016年

Answers:


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

这个答案可能更多的是设计变更,而不是您想要的。但是-按下跳跃按钮后,球在短时间内停留在地面上,并牢固地停留在地面上,抵消了任何向上的垂直动量(也许压扁了位表示类似弹簧的压缩),然后在该时间段结束后向上跳跃。这将解决跳动动量增加跳动的问题,尽管这也将允许玩家控制是否跳动或跳动。它还为跳跃功能增加了延迟,可以将其视为好(感觉更自然)或不好(不允许玩家有足够的时间进行响应)。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.