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相机上。

应用效果

先看个正面的:

再来个侧面的:

好了,结束。

结语

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

相关推荐
神码编程5 小时前
【Unity功能集】TextureShop纹理工坊(五)选区
unity·游戏引擎·shader·ps选区
m0_7482517210 小时前
Android webview 打开本地H5项目(Cocos游戏以及Unity游戏)
android·游戏·unity
benben04410 小时前
Unity3D仿星露谷物语开发7之事件创建动画
unity·游戏引擎
gantengsheng10 小时前
基于51单片机和OLED12864的小游戏《贪吃蛇》
单片机·嵌入式硬件·游戏·51单片机
264玫瑰资源库10 小时前
从零开始C++棋牌游戏开发之第三篇:游戏的界面布局设计
开发语言·c++·python·游戏·pygame·源代码管理
264玫瑰资源库11 小时前
从零开始C++游戏开发之第七篇:游戏状态机与回合管理
开发语言·c++·游戏
林枫依依13 小时前
Unity2021.3.16f1可以正常打开,但是Unity2017.3.0f3却常常打开闪退或者Unity2017编辑器运行起来就闪退掉
unity
虾球xz13 小时前
游戏引擎学习第57天
学习·游戏引擎
windwind200016 小时前
游戏关卡设计方法的杂感
游戏·关卡设计
异次元的归来1 天前
Unity DOTS中的share component
unity·游戏引擎