You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity骰子开发问题:点击反向移动但Y轴高度未变化,求解决

骰子翻滚功能修复方案

问题背景

开发游戏时需要实现骰子功能:点击骰子某一面,骰子向点击的反方向移动(比如点击背面则向前移动),同时要模拟真实的重力翻滚效果(Y轴高度随翻滚变化)。但当前代码仅改变X、Z轴位置,Y轴高度无变化,无法实现真实的翻滚效果。

重力效果示意图

当前使用的代码:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class MoveCube : MonoBehaviour
{
Vector3 movePosition;
public float time = 0.02f;

[HideInInspector]
public bool blockClick = false;

private void Start()
{
    movePosition = transform.position;
}

private void Update()
{
    blockClick = false;

    if (Input.GetMouseButtonDown(0))
    {
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
        RaycastHit raycastHit;

        if (Physics.Raycast(ray, out raycastHit))
        {
            string hitName = raycastHit.transform.name;

            if (hitName == "MoveCube")
            {
                move(raycastHit);
                blockClick = true;
            }
        }
    }

    transform.position = Vector3.Lerp(
            transform.position,
            movePosition,
            time);
}

public void move(RaycastHit raycastHit)
{
    Vector3 incomingVec = raycastHit.normal - Vector3.up;

    // South
    if (incomingVec == new Vector3(0, -1, -1))
    {
        movePosition = movePosition + new Vector3(0, 0, 1);
        return;
    }

    // North
    if (incomingVec == new Vector3(0, -1, 1))
    {
        movePosition = movePosition + new Vector3(0, 0, -1);
        return;
    }

    // West
    if (incomingVec == new Vector3(-1, -1, 0))
    {
        movePosition = movePosition + new Vector3(1, 0, 0);
        return;
    }

    // East
    if (incomingVec == new Vector3(1, -1, 0))
    {
        movePosition = movePosition + new Vector3(-1, 0, 0);
        return;
    }
}
}

当前效果示意图

问题分析

当前代码仅修改了movePosition的X、Z轴坐标,没有处理骰子翻滚时的Y轴高度变化,同时只是平移位置而非模拟真实的翻滚旋转,导致无法呈现重力作用下的骰子滚动效果。

修复方案

要实现真实的骰子翻滚,需要同时处理位置偏移和旋转角度,并且在翻滚过程中让Y轴高度随旋转自然变化。以下是修改后的代码:

using System.Collections;
using UnityEngine;

public class MoveCube : MonoBehaviour
{
    public float rollDuration = 0.5f; // 翻滚一次的时长
    private bool isRolling = false; // 是否正在翻滚,防止重复点击

    private void Update()
    {
        if (isRolling) return;

        if (Input.GetMouseButtonDown(0))
        {
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            if (Physics.Raycast(ray, out RaycastHit hit) && hit.transform.name == "MoveCube")
            {
                StartCoroutine(RollDice(hit.normal));
            }
        }
    }

    private IEnumerator RollDice(Vector3 hitNormal)
    {
        isRolling = true;
        Vector3 rollAxis;
        Vector3 targetPosition = transform.position;
        Quaternion targetRotation = transform.rotation;

        // 根据点击的面确定翻滚轴和目标位置、旋转
        if (hitNormal == Vector3.forward) // 点击前面,向后翻滚
        {
            rollAxis = Vector3.right;
            targetPosition += Vector3.back;
            targetRotation *= Quaternion.Euler(-90, 0, 0);
        }
        else if (hitNormal == Vector3.back) // 点击后面,向前翻滚
        {
            rollAxis = Vector3.right;
            targetPosition += Vector3.forward;
            targetRotation *= Quaternion.Euler(90, 0, 0);
        }
        else if (hitNormal == Vector3.left) // 点击左面,向右翻滚
        {
            rollAxis = Vector3.forward;
            targetPosition += Vector3.right;
            targetRotation *= Quaternion.Euler(0, 0, 90);
        }
        else if (hitNormal == Vector3.right) // 点击右面,向左翻滚
        {
            rollAxis = Vector3.forward;
            targetPosition += Vector3.left;
            targetRotation *= Quaternion.Euler(0, 0, -90);
        }
        else if (hitNormal == Vector3.up) // 点击上面,向下翻滚(可选)
        {
            rollAxis = Vector3.right;
            targetPosition += Vector3.down;
            targetRotation *= Quaternion.Euler(180, 0, 0);
        }
        else if (hitNormal == Vector3.down) // 点击下面,向上翻滚(可选)
        {
            rollAxis = Vector3.right;
            targetPosition += Vector3.up;
            targetRotation *= Quaternion.Euler(-180, 0, 0);
        }
        else
        {
            isRolling = false;
            yield break;
        }

        float elapsedTime = 0f;
        Vector3 startPosition = transform.position;
        Quaternion startRotation = transform.rotation;

        // 平滑执行翻滚动画
        while (elapsedTime < rollDuration)
        {
            elapsedTime += Time.deltaTime;
            float t = elapsedTime / rollDuration;
            // 使用SmoothStep让动画更自然
            t = Mathf.SmoothStep(0, 1, t);
            transform.position = Vector3.Lerp(startPosition, targetPosition, t);
            transform.rotation = Quaternion.Lerp(startRotation, targetRotation, t);
            yield return null;
        }

        // 确保最终位置和旋转精确到位
        transform.position = targetPosition;
        transform.rotation = targetRotation;
        isRolling = false;
    }
}

关键修改点

  • 使用协程实现平滑的翻滚动画,避免直接瞬间移动/旋转导致的生硬效果
  • 同时处理位置偏移和旋转角度,让Y轴高度随翻滚自然变化(比如向前翻滚时,骰子会先抬起再落下,Y轴先升后降)
  • 添加isRolling状态锁,防止在翻滚过程中重复点击
  • 使用Quaternion处理旋转,确保骰子翻滚的角度正确
  • 基于点击面的法线方向(hitNormal)直接判断翻滚方向,比原代码的incomingVec更直观可靠

额外注意事项

  • 确保骰子对象添加了Collider组件(比如BoxCollider),否则射线检测无法生效
  • 如果骰子是标准立方体,边长为1的话,位置偏移量设置为1即可匹配翻滚后的位置;如果边长不同,需要调整偏移量为对应长度
  • 可以调整rollDuration参数来控制翻滚的快慢,数值越小翻滚越快

内容的提问来源于stack exchange,提问作者olios

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 23:25:25