Unity中如何实现类似愤怒小鸟的3D拖拽射击机制?
问题描述
开发3D游戏时实现类似《愤怒小鸟》的拖拽发射机制:点击球体沿蓝色线拖拽,释放后球体沿红色线运动击中胶囊体,但实际球体沿绿色线运动,完全没有深度感。尝试使用ScreenToWorldPoint()时出现异常,点击物体后物体会消失。
附当前代码(DragAndShoot.cs):
using System.Collections; using System.Collections.Generic; using UnityEngine; [RequireComponent(typeof(Rigidbody))] [RequireComponent(typeof(Collider))] public class DragAndShoot : MonoBehaviour { private Vector3 mousePressDownPos; private Vector3 mouseReleasePos; private Rigidbody rb; private bool isShoot; void Start() { rb = GetComponent<Rigidbody>(); } void Update() { //if isShoot is true, then the ball is already shot. Wait for it to stop moving. //after it has stopped moving, we can shoot again. if(isShoot && rb.velocity.magnitude < 0.1f) { isShoot = false; } } private void OnMouseDown() { mousePressDownPos = Input.mousePosition; } private void OnMouseUp() { mouseReleasePos = Input.mousePosition; Shoot(mouseReleasePos-mousePressDownPos); } private float forceMultiplier = 3; void Shoot(Vector3 Force) { if(isShoot) return; rb.AddForce(-Force * forceMultiplier); isShoot = true; } }
问题排查与修复建议
核心问题分析
- 屏幕坐标直接用于3D力向量错误:
Input.mousePosition返回的是屏幕像素坐标(X/Y对应屏幕宽高,Z值始终为0),直接用这个向量计算力,只会在X/Y平面施加力,完全没有Z轴(深度)分量,导致球体运动无3D深度感。 ScreenToWorldPoint使用错误:直接传入Input.mousePosition时,Z值为0,会将点转换到相机近裁剪面的位置,若相机朝向与物体位置不匹配,会导致物体瞬移到相机附近甚至视野外,看起来像消失。
修复步骤
1. 获取拖拽的世界空间偏移量
将屏幕拖拽操作转换为世界空间中的方向向量,通过射线检测+平面相交的方式,获取拖拽的3D空间坐标:
private Camera mainCam; private Vector3 dragStartWorldPos; void Start() { rb = GetComponent<Rigidbody>(); mainCam = Camera.main; // 提前缓存主相机,避免重复调用 } private void OnMouseDown() { // 发射射线,计算鼠标在物体所在平面上的交点 Ray ray = mainCam.ScreenPointToRay(Input.mousePosition); Plane dragPlane = new Plane(mainCam.transform.forward, transform.position); if (dragPlane.Raycast(ray, out float enter)) { dragStartWorldPos = ray.GetPoint(enter); } } private void OnMouseUp() { Ray ray = mainCam.ScreenPointToRay(Input.mousePosition); Plane dragPlane = new Plane(mainCam.transform.forward, transform.position); if (dragPlane.Raycast(ray, out float enter)) { Vector3 dragEndWorldPos = ray.GetPoint(enter); Vector3 forceDir = dragStartWorldPos - dragEndWorldPos; // 反向拖拽方向,对应愤怒小鸟的发射逻辑 Shoot(forceDir); } }
2. 修正力的施加方式
改用世界空间向量施加力,同时使用Impulse模式提升发射手感:
private float forceMultiplier = 10f; // 可根据需求调整系数大小 void Shoot(Vector3 forceDir) { if (isShoot) return; // 使用冲量模式瞬间施加力,更符合拖拽发射的手感 rb.AddForce(forceDir * forceMultiplier, ForceMode.Impulse); isShoot = true; }
3. 避免物体瞬移的正确用法
如果需要单独使用ScreenToWorldPoint,必须传入有效Z值(比如物体到相机的距离):
// 示例:获取物体到相机的距离作为Z深度 float depth = Vector3.Distance(mainCam.transform.position, transform.position); Vector3 worldPos = mainCam.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, depth));
额外优化建议
- 添加拖拽视觉反馈:比如拖拽时绘制轨迹线,或显示球体的预览位置,提升玩家操作感知。
- 限制最大拖拽距离:通过
Vector3.ClampMagnitude限制力向量长度,避免球体被发射过远:float maxDragDistance = 2f; // 自定义最大拖拽距离 forceDir = Vector3.ClampMagnitude(forceDir, maxDragDistance);
内容的提问来源于stack exchange,提问作者yetty
相关产品推荐
相关产品推荐

