【游戏开发实战】Unity 2D游戏物理运动曲线轨迹预测,以愤怒的小鸟为例,轨迹曲线云团圈圈
文章目录
- 一、前言
- 二、思考分析
- 三、场景搭建
- 1、导入图片素材
- 2、鸟预设
- 3、地面环境
- 4、曲线的点预设
- 5、预览效果
- 四、代码
- 1、鸟脚本:Bird.cs
- 2、曲线预测器:Trajectory.cs
- 3、游戏管理器:GameManager.cs
- 五、挂脚本
- 六、运行测试
一、前言
点关注不迷路,持续输出Unity干货文章。
嗨,大家好,我是新发。我们先来看一个画面,愤怒的小鸟,发射之前没用轨迹预测。

我们可以不可以在发射之前就预测出曲线轨迹并显示出来呢?
本文就来教大家如何在Unity中实现物理运动曲线预测吧。
本文最终效果:

本文Demo工程已上传到CodeChina,感兴趣的同学可自行下载学习。
地址:https://codechina.csdn.net/linxinfa/UnityAngryBirdCurveTrajectory
注:我使用的Unity版本:2020.2.7f1c1 (64-bit)。

二、思考分析
根据高中物理的斜抛运动路径公式:
水平方向: s x = v x t s_x=v_xt sx=vxt
竖直方向: s y = v y t − ( 1 / 2 ) g t 2 s_y=v_yt-(1/2)gt^2 sy=vyt−(1/2)gt2
我们只要知道 v x v_x vx和 v y v_y vy即可预测出曲线的轨迹,而 ( v x , v y ) (v_x, v_y) (vx,vy)就是速度向量。
那么,我们怎么知道 ( v x , v y ) (v_x, v_y) (vx,vy)呢?
手指按下的位置作为起始点,手指抬起的位置作为结束点,起始点 - 结束点即可得到一个从结束点指向起始点的向量,这个就是我们要的速度向量 ( v x , v y ) (v_x, v_y) (vx,vy)。

现在我们翻译成代码,先定义些必要的变量:
///
/// 手指的起始点
///
private Vector2 m_startPoint;
///
/// 手指的结束点
///
private Vector2 m_endPoint;
///
/// 起始点和结束点的距离
///
private float m_distance;
///
/// 方向向量,从结束点指向起始点的归一化向量
///
private Vector2 m_direction;
起始点和结束点的坐标,可以通过手指的屏幕坐标转世界坐标得到,例:
// 起始点坐标
m_startPoint = Camera.main.ScreenToWorldPoint(Input.mousePosition);
// ...// 结束点坐标
m_endPoint = Camera.main.ScreenToWorldPoint(Input.mousePosition);
手指抬起的时候,计算速度向量:
// 放大速度倍数
float factor = 4f;m_distance = Vector2.Distance(m_startPoint, m_endPoint);
m_direction = (m_startPoint - m_endPoint).normalized;
Vector2 speed = m_direction * m_distance * factor;
有了这个speed,我们就可以预测轨迹了。
假设鸟的坐标为Vector3 birdPos,根据斜抛路径公式,那么预测曲线轨迹点的坐标(posX, posY)就是这样:
float posX = birdPos.x + speed.x * t;
float posY = birdPos.x + speed.y * t - 0.5f * Physics2D.gravity.magnitude * t * t;
注意,上面我们用了Physics2D.gravity.magnitude这个值,它就是重力加速度g。
它的值可以在Project Settings中设置:

另外,我们需要让鸟根据初始的speed做斜抛运动,这里要用到Rigidbody2D的AddForce接口,例:
rigidbody2D.AddForce(speed, ForceMode2D.Impulse);
好了,思考完毕,开始动手实际操作吧。
三、场景搭建
1、导入图片素材
为了方便演示,我们先找点图片素材搭建场景,素材图片:




2、鸟预设
先做个鸟预设,

身上挂上必要的物理组件,

为了让鸟弹到地面上之后有一个弹性碰撞,我们创建一个Physics Material 2D,

重名名为bounciness(这个图标很形象呀)

设置摩擦力和弹力分别为0.5、0.6。

3、地面环境
做个地面预设,左右两边放两个带碰撞体的石柱,防止鸟飞出屏幕外。

地面添加两个碰撞体,不然鸟会掉到屏幕外。

4、曲线的点预设
为了描绘曲线,我们用这个小云团作为一个个点,

将其做成预设,只需要基本的SpriteRenderer组件即可。

5、预览效果
运行Unity,预览下效果,可以看到,我们的鸟已经具备物理特性了。

接下来就是写代码实现我们的业务逻辑了。
四、代码
撸起袖子写代码,代码如下,注释我写得比较清楚,就不赘述了。
1、鸟脚本:Bird.cs
// Bird.csusing UnityEngine;///
/// 鸟
///
public class Bird : MonoBehaviour
{[HideInInspector] public Rigidbody2D rb;[HideInInspector] public CircleCollider2D col;[HideInInspector] public Vector3 pos{get { return transform.position; }}private void Awake(){rb = GetComponent<Rigidbody2D>();col = GetComponent<CircleCollider2D>();}/// /// 给鸟一个推力,将鸟推出去/// /// 速度向量public void Push(Vector2 speed){rb.AddForce(speed, ForceMode2D.Impulse);}/// /// 激活物理/// public void ActivateRb(){rb.isKinematic = false;}/// /// 禁用物理/// public void DesActivateRb(){rb.velocity = Vector3.zero;rb.angularVelocity = 0f;rb.isKinematic = true;}
}
2、曲线预测器:Trajectory.cs
// Trajectory.csusing UnityEngine;///
/// 曲线预测器
///
public class Trajectory : MonoBehaviour
{/// /// 预测点的数量/// [SerializeField] private int m_dotsNum = 20;/// /// 点物体的父节点/// [SerializeField] private GameObject m_dotsParent;/// /// 点预设/// [SerializeField] private GameObject m_dotsPrefab;/// /// 点间距/// [SerializeField] private float m_dotSpacing = 0.01f;/// /// 点的最小缩放/// [SerializeField] [Range(0.01f, 0.3f)] private float m_dotMinScale = 0.1f;/// /// 点的最大缩放/// [SerializeField] [Range(0.3f, 1f)] private float m_dotMaxScale = 1f;private Transform[] m_dotsList;private Vector2 m_pos;private float m_timeStamp;private void Start(){Hide();PrepareDots();}/// /// 准备轨迹点/// private void PrepareDots(){m_dotsList = new Transform[m_dotsNum];m_dotsPrefab.transform.localScale = Vector3.one * m_dotMaxScale;float scale = m_dotMaxScale;float scaleFactor = scale / m_dotsNum;for (int i = 0; i < m_dotsNum; ++i){var dot = Instantiate(m_dotsPrefab).transform;dot.parent = m_dotsParent.transform;dot.localScale = Vector3.one * scale;if (scale > m_dotMinScale)scale -= scaleFactor;m_dotsList[i] = dot;}}/// /// 更新点坐标/// /// 鸟的坐标/// 初始速度向量public void UpdateDots(Vector2 birdPos, Vector2 pushSpeed){m_timeStamp = m_dotSpacing;for (int i = 0; i < m_dotsNum; ++i){m_pos.x = birdPos.x + pushSpeed.x * m_timeStamp;m_pos.y = (birdPos.y + pushSpeed.y * m_timeStamp) - 0.5f * Physics2D.gravity.magnitude * m_timeStamp * m_timeStamp;m_dotsList[i].position = m_pos;m_timeStamp += m_dotSpacing;}}/// /// 显示预测轨迹/// public void Show(){m_dotsParent.SetActive(true);}/// /// 隐藏预测轨迹/// public void Hide(){m_dotsParent.SetActive(false);}
}
3、游戏管理器:GameManager.cs
// GameManager.cs
using UnityEngine;///
/// 游戏管理器
///
public class GameManager : MonoBehaviour
{/// /// 鸟/// public Bird bird;/// /// 轨迹预测器/// public Trajectory trajectory;/// /// 主摄像机/// private Camera m_cam;/// /// 力大小/// [SerializeField]private float m_speedFactor = 4f;/// /// 是否拉动中/// private bool m_isDragging = false;/// /// 手指的起始点/// private Vector2 m_startPoint;/// /// 手指的结束点/// private Vector2 m_endPoint;/// /// 起始点和结束点的距离/// private float m_distance;/// /// 方向向量,从结束点指向起始点的归一化向量/// private Vector2 m_direction;/// /// 力向量/// private Vector2 m_pushSpeed;private void Start(){m_cam = Camera.main;bird.DesActivateRb();}private void Update(){// 检测鼠标/手指行为if(Input.GetMouseButtonDown(0)){m_isDragging = true;OnDragStart();}if (Input.GetMouseButtonUp(0)){m_isDragging = false;OnDragEnd();}if (m_isDragging){OnDrag();}}/// /// 开始拉/// private void OnDragStart(){// 禁用物理bird.DesActivateRb();// 起始点m_startPoint = m_cam.ScreenToWorldPoint(Input.mousePosition);// 显示轨迹trajectory.Show();}/// /// 拉中/// private void OnDrag(){m_endPoint = m_cam.ScreenToWorldPoint(Input.mousePosition);m_distance = Vector2.Distance(m_startPoint, m_endPoint);m_direction = (m_startPoint - m_endPoint).normalized;m_pushSpeed = m_direction * m_distance * m_speedFactor;trajectory.UpdateDots(bird.pos, m_pushSpeed);}/// /// 拉结束/// private void OnDragEnd(){bird.ActivateRb();bird.Push(m_pushSpeed);// 隐藏轨迹trajectory.Hide();}
}
五、挂脚本
鸟预设上挂Bird脚本。

曲线预测器挂Trajectory脚本,并赋值对应的参数。

游戏管理器挂GameManager脚本,并赋值对应的参数。

六、运行测试
运行Unity,测试效果如下,可以看到,飞行轨迹与我们的预测的曲线轨迹一致。

完毕。
喜欢Unity的同学,不要忘记点击关注,如果有什么Unity相关的技术难题,也欢迎留言或私信~
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
