HuldaGnodim 发表于 2022-7-31 19:28

Unity中的射击(火箭弹、激光、子弹轨迹)

最近在学习 B站某不知名大佬:奥飒姆_Awesome 的视频,发现自己的一些知识缺漏,本文对一些困惑的点进行学习和总结。
附上主页:奥飒姆_Awesome的个人空间_哔哩哔哩_Bilibili
项目链接: https://github.com/RedFF0000/TopDownShooter
(十分惋惜的是,Awesome大佬已经停更一年了,可能在忙其他的吧,还是很感谢他的分享,讲的很好。)

Transform.right的妙用

问题:让枪械转向鼠标所在位置
方法:Transform.right = mousePos
分析:在unity3d中,我们经常用LookAt()函数来改变物体的朝向。在2d游戏中是否有更好的办法呢?
参考API的解释(Transform-right - Unity 脚本 API),Transform.right/up/forward代表的是物体的 右、上、前方向对应世界坐标的向量,对其赋值会使得物体发生旋转。
我们假设枪口指向的位置是物体的 “右”方向,则直接使用 Transform.right = mousePos,即可完成朝向。


真的是非常的简便。
参考:https://its201.com/article/qq_19895789/83149394

火箭弹的设计思路

1. 火箭弹初始化

核心代码:
    IEnumerator DelayFire(float delay)
    {
      yield return new WaitForSeconds(delay);

      int median = rocketNum / 2;
      for (int i = 0; i < rocketNum; i++)
      {
            GameObject bullet = ObjectPool.Instance.GetObject(bulletPrefab);
            bullet.transform.position = muzzlePos.position;

            if (rocketNum % 2 == 1)
            {
                bullet.transform.right = Quaternion.AngleAxis(rocketAngle * (i - median), Vector3.forward) * direction;
            }
            else
            {
                bullet.transform.right = Quaternion.AngleAxis(rocketAngle * (i - median) + rocketAngle / 2, Vector3.forward) * direction;
            }
            bullet.GetComponent<Rocket>().SetTarget(mousePos);
      }
    }

[*]Quaternion.AngleAxis
Quaternion-AngleAxis - Unity 脚本 API
创建一个围绕axis旋转angle度的旋转。

[*]第一个参数:旋转的角度
[*]第二个参数:旋转的参考轴
问题1:第二个参数为什么是 Vector3.forward
// 参考api
      //
      // 摘要:
      //   Shorthand for writing Vector3(1, 0, 0).
      public static Vector3 right { get; }
      //
      // 摘要:
      //   Shorthand for writing Vector3(-1, 0, 0).
      public static Vector3 left { get; }
      //
      // 摘要:
      //   Shorthand for writing Vector3(0, 1, 0).
      public static Vector3 up { get; }
      //
      // 摘要:
      //   Shorthand for writing Vector3(0, 0, -1).
      public static Vector3 back { get; }
      //
      // 摘要:
      //   Shorthand for writing Vector3(0, 0, 1).
      public static Vector3 forward { get; }
Vector3.forward代表的是unity中的Z轴方向


因为教程是2d游戏教程,因此以z轴作为参考轴,就能够实现2d视角下的子弹偏移(白色箭头为子弹轨迹)


将子弹轨迹分两种情况,第一种是奇数个火箭弹(rocketNum %2==1)。
if (rocketNum % 2 == 1)
{
    bullet.transform.right = Quaternion.AngleAxis(rocketAngle * (i - median), Vector3.forward) * direction;
}
第二种是偶数个火箭弹


else
{
    bullet.transform.right = Quaternion.AngleAxis(rocketAngle * (i - median) + rocketAngle / 2, Vector3.forward) * direction;
}
2. 每个火箭弹的逻辑

核心逻辑:每个子弹的转向和移动    private void FixedUpdate()
    {
      direction = (targetPos - transform.position).normalized;

      if (!arrived)
      {
            transform.right = Vector3.Slerp(transform.right, direction, lerp / Vector2.Distance(transform.position, targetPos));
            rigidbody.velocity = transform.right * speed;
      }
      if (Vector2.Distance(transform.position, targetPos) < 1f && !arrived)
      {
            arrived = true;
      }
    }

[*]转向:
transform.right = Vector3.Slerp(transform.right, direction, lerp / Vector2.Distance(transform.position, targetPos));
            rigidbody.velocity = transform.right * speed;Vector3.Slerp:在两个向量之间进行球面插值
官网API:Vector3-Slerp - Unity 脚本 API
public staticVector3Slerp(Vector3a,Vector3b, floatt);
其中:a代表起始方向,b代表目标方向, t的范围是,t为0.5即代表a和b方向的中间方向,以此类推。

这里的起始方向是:子弹的朝向,终止方向是:目标方向,这两个都很好理解。
t为什么是:lerp / Vector2.Distance(transform.position, targetPos) ?
如果使用固定值 lerp,在FixUpdate每帧调用,会导致子弹到达目标点的转向不可控,可能导致距离较远时转向过头,或者距离不到时转向过小。
Vector2.Distance(transform.position, targetPos) 代表距离目标点的距离,除以这个距离,可以保证:
距离目标点较远时转向较小,距离目标点较近时转向较大。
这里有个疑惑:Vector2.Distance(transform.position, targetPos) 不会等于0吗?
有大佬了解的话可以解答下,我猜测是因为C#的float计算基本不会有相等的情况,但是不会有万一吗?最终效果:可以看上面的链接视频





激光枪设计思路

1. debug版激光枪

核心代码:
    protected override void Fire()
    {
      RaycastHit2D hit2D = Physics2D.Raycast(muzzlePos.position, direction, 30);
      // 只能在debug模式的时候看到
      Debug.DrawLine(muzzlePos.position, hit2D.point);
    }
Physics2D.Raycast:向场景中的碰撞体投射射线,RaycastHit2D返回的投射数量。
Physics2D-Raycast - Unity 脚本 API
2. LineRender组件

关键参数如下:


游戏中的效果:


关键代码:
    protected override void Fire()
    {
      RaycastHit2D hit2D = Physics2D.Raycast(muzzlePos.position, direction, 30);

      // Debug.DrawLine(muzzlePos.position, hit2D.point);
      laser.SetPosition(0, muzzlePos.position);
      laser.SetPosition(1, hit2D.point);
其中laser.SetPosition是设置激光的起始位置。
再加上一点点粒子特效,最终游戏中的效果如下:


3. 使用URP进行激光后处理

这部分我还没消化,后面学习了会补充上笔记。最终效果:


高速枪械(仅看得到轨迹)

基本原理和激光枪设计类似,只是修改了LineRender的颜色看下最终效果:

jquave 发表于 2022-7-31 19:29

写的很好,感谢

fwalker 发表于 2022-7-31 19:36

谢谢
页: [1]
查看完整版本: Unity中的射击(火箭弹、激光、子弹轨迹)