Unity开发中导弹路径散射的原理与实现

Unity开发中导弹路径散射的原理与实现

前言

前面我们学习了导弹的追踪的效果,但是在动画或游戏中,我们经常可以看到导弹发射后的弹道是不规则的,扭扭曲曲的飞行,然后击中目标。

这期我们就讲一下不规则路径飞行的逻辑,在游戏中是如何实现的。

逻辑原理

首先迎面走来的是初级的散射效果原理图,在发射点和目标点之间有一个散射经过点,重点来了:**利用三维空间中球形公式,给定球心,随机返回球面上一点。**然后让导弹经过随机点再击打目标,就会形成随机散射的效果。

多点也是一样的道理,把路径点经过换算之后再赋值导弹路径点,然后形成不规则散射的效果。

这里可以发现,导弹的路径是折线效果,按标准应该是曲线效果。两者的区别就在于导弹在两点之间的过渡函数,折线是平滑过渡,曲线是贝塞尔曲线过渡,选的过渡函数不同实现的效果也不一样。由于贝塞尔曲线过渡较为复杂,这里就用平滑过渡演示原理

代码实现

导弹自身脚本

这里将散射的范围用变量表示,实现可控的效果,想大范围就大范围、想小范围就小范围。将脚本挂载到导弹的预制体上之后给相应的变量赋值,例如:散射半径、爆炸特效、子弹移动速度,其他变量通过外部脚本赋值。

csharp 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SpherePoint : MonoBehaviour
{
    [Header("散射半径")]
    public float radius;
    
    public GameObject FX;//爆炸特效
    public Transform endPoint;   // 目标点  
    public List<Transform> wayPoints; // 中间点列表  
    public float speed = 10f;         // 子弹移动速度  

    public int currentWaypointIndex = 0; // 当前处理的中间点索引  
    public Vector3 currentTargetPosition; // 当前目标位置  

    // Start is called before the first frame update
    void Start()
    {
        if (wayPoints.Count > 0)
        {
            currentTargetPosition = GetRandomPointOnSphere(wayPoints[0].position, radius);
        }
        else
        {
            currentTargetPosition = endPoint.position;
        }


    }

    // Update is called once per frame
    void Update()
    {
    	BulletMovement(transform);
    }

    /// <summary>
    /// 随机获取中间点周围的散射经过点
    /// </summary>
    /// <param name="center">中间点坐标</param>
    /// <param name="r">散射半径</param>
    /// <returns></returns>
    public static Vector3 GetRandomPointOnSphere(Vector3 center, float r)
    {
        // 生成随机的经度和纬度  
        float u = UnityEngine.Random.value * 2 * Mathf.PI; // 经度 [0, 2*PI]  
        float v = UnityEngine.Random.value * Mathf.PI; // 纬度 [0, PI]  

        // 将球坐标转换为笛卡尔坐标  
        float x = center.x + r * Mathf.Sin(v) * Mathf.Cos(u);
        float y = center.y + r * Mathf.Sin(v) * Mathf.Sin(u);
        float z = center.z + r * Mathf.Cos(v);

		//返回指定球心的球面上随机一点
        return new Vector3(x, y, z);
    }

    private void BulletMovement(Transform bulletTran)
    {
        // 子弹朝向当前目标位置  
        bulletTran.LookAt(currentTargetPosition);

        bulletTran.position += bulletTran.forward * speed * Time.deltaTime;      //向前移动

        // 检查子弹是否到达当前目标位置  
        if (Vector3.Distance(bulletTran.position, currentTargetPosition) < 0.1f)
        {
            // 如果当前点不是最后一个中间点,则更新下一个目标位置为下一个中间点  
            if (currentWaypointIndex < wayPoints.Count)
            {
                currentWaypointIndex++;
                if (currentWaypointIndex < wayPoints.Count)
                {
                    currentTargetPosition = GetRandomPointOnSphere(wayPoints[currentWaypointIndex].position, radius);
                }
                else
                {
                    currentTargetPosition = endPoint.position; // 最后一个中间点后,目标位置是终点  
                }
            }
            // 如果已经到达终点,可以选择销毁子弹或其他操作  
            else if (currentTargetPosition == endPoint.position)
            {
                GameObject tempFX = Instantiate(FX, bulletTran.position, bulletTran.rotation);  //生成一个爆炸特效 并给予位置和旋转信息
                Destroy(gameObject);//销毁自己
                Destroy(tempFX, 0.3f);//销毁爆炸效果
                currentWaypointIndex = 0;//重置路径索引
            }
        }
    }

}

外部控制脚本

将导弹的击打目标和散射路径点通过脚本告诉导弹。

csharp 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class InstantiateBullet : MonoBehaviour
{
    public GameObject bullet;//导弹预制体
    public Vector3 startPoint; // 导弹出生发射点  
    public Quaternion missileRotation;//导弹出生时方向
    public Transform endPoint;   // 目标点  
    public List<Transform> wayPoints; // 中间点列表  
    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
    	//鼠标点击左键发射导弹
        if (Input.GetMouseButtonDown(0))
        {
            GameObject bu = Instantiate(bullet, startPoint, missileRotation);
            bu.GetComponent<SpherePoint>().endPoint = endPoint;
            bu.GetComponent<SpherePoint>().wayPoints = wayPoints;
        }
    }
}

外部脚本我挂载到了Main Camera相机上。

应用效果

先看个正面的:

再来个侧面的:

好了,结束。

结语

学会后要多尝试,变成自己的东西,为己所用,赶快自己尝试下吧。有什么问题可以评论区或私信留言,下期见,拜拜。

相关推荐
ujainu5 小时前
Flutter + OpenHarmony 游戏开发进阶:用户输入响应——GestureDetector 实现点击发射
flutter·游戏·openharmony
ujainu5 小时前
Flutter + OpenHarmony 实现无限跑酷游戏开发实战—— 对象池化、性能优化与流畅控制
flutter·游戏·性能优化·openharmony·endless runner
小李也疯狂6 小时前
Unity 中的立方体贴图(Cubemaps)
unity·游戏引擎·贴图·cubemap
牛掰是怎么形成的6 小时前
Unity材质贴图引用陷阱:包体暴涨真相
unity·材质·贴图
呆呆敲代码的小Y6 小时前
【Unity工具篇】| 超实用工具LuBan,快速上手使用
游戏·unity·游戏引擎·unity插件·luban·免费游戏·游戏配置表
EQ-雪梨蛋花汤7 小时前
【Unity优化】Unity多场景加载优化与资源释放完整指南:解决Additive加载卡顿、预热、卸载与内存释放问题
unity·游戏引擎
我的offer在哪里7 小时前
用 Unity 从 0 做一个「可以玩的」游戏,需要哪些步骤和流程
游戏·unity·游戏引擎
串流游戏联盟8 小时前
启程!手机也能邂逅暖暖万相奇观
游戏·远程工作
泡泡茶壶ᐇ8 小时前
Unity游戏开发入门指南:从零开始理解游戏引擎核心概念
unity·游戏引擎
User_芊芊君子8 小时前
HCCL高性能通信库编程指南:构建多卡并行训练系统
人工智能·游戏·ai·agent·测评