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

应用效果

先看个正面的:

再来个侧面的:

好了,结束。

结语

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

相关推荐
q567315232 小时前
Python/Django 服务器升级脚本
服务器·开发语言·python·游戏·django
孑渡2 小时前
【LeetCode】每日一题:跳跃游戏
python·算法·leetcode·游戏·职场和发展
陈言必行3 小时前
Unity 资源 之 Sweet Cakes Icon套装,110个高品质蛋糕图标分享
unity·游戏引擎
Huanzhi_Lin3 小时前
游戏中的坐标转换函数*2(laya2D)
游戏
唐小墨同学4 小时前
机械拆装-基于Unity-装配功能的实现
unity·游戏引擎
向宇it4 小时前
【unity实战】使用unity的新输入系统InputSystem+有限状态机设计一个玩家状态机控制——实现玩家的待机 移动 闪避 连击 受击 死亡状态切换
开发语言·游戏·unity·游戏引擎
ZKY_244 小时前
【APK】Unity出android包,报错 Gradle build failed.See the Console for details
unity
云储储5 小时前
植物大战僵尸融合嫁接版 MAC 版本下载安装详细教程
游戏·macos·mac·pvz·mac游戏·植物大战僵尸融合嫁接版
向宇it5 小时前
【unity实战】使用Unity实现动作游戏的攻击 连击 轻重攻击和打击感
开发语言·游戏·unity·游戏引擎
Q_hd7 小时前
【嵌入式】探索嵌入式世界:在ARM上构建俄罗斯方块游戏的奇妙之旅
linux·c语言·arm开发·游戏