找回密码
 立即注册
查看: 351|回复: 2

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

[复制链接]
发表于 2022-7-31 19:28 | 显示全部楼层 |阅读模式
最近在学习 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的范围是[0, 1],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的颜色
看下最终效果:

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
发表于 2022-7-31 19:29 | 显示全部楼层
写的很好,感谢
发表于 2022-7-31 19:36 | 显示全部楼层
谢谢
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-9-22 04:16 , Processed in 0.093403 second(s), 26 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表