首页 > 解决方案 > 找到一个角度和速度来发射弹丸以到达特定点

问题描述

我正在尝试制作一门可以射击 Unity 中 3D 世界中的目标的大炮...我有一个LaunchConfig枚举->

public enum LauncherConfig
{
    CalculateNone = 0,
    CalculateAngle = 1,
    CalculateVelocity = 2,
    CalculateBoth = 3
}

选择时CalculateAngle,用户可以输入弹丸的初始速度,并且必须计算弹丸需要发射的角度才能到达目标。对CalculateVelocity. CalculateNone允许用户输入这两个值并CalculateBoth 计算这两个值。

What equations do I use to calculate the values so that they line up, ie when CalculateAngleis selected the velocity calculated should be such that the projectile looks natural coming out of the barrel. 同样CalculateBoth,应计算角度,以便计算出的速度将弹丸从枪管发射出去,而不是任何其他方向。

我有我当前的位置(Vector3),目标位置(Vector3)。计算出的角度将影响大炮的 x 旋转。y 旋转已与目标对齐,使其面向目标;

这是该课程的代码

using System;
using UnityEngine;

public class Launcher : MonoBehaviour
{
    [SerializeField] private LauncherSettings settings = default;
    [SerializeField] private Transform target = default;
    private Transform partToRotateY;
    private Transform partToRotateX;
    private Transform projectileSpawnPosition;
    private float x;
    private float y;
    private Vector3 velocity;

    private void Awake()
    {
        partToRotateX = transform.GetChild(0);
        partToRotateY = transform.GetChild(1);
        projectileSpawnPosition = partToRotateX.GetChild(0);
        settings.inputController = new InputActions.Launcher();
        settings.inputController.Automatic.Launch.performed += _ => Shoot();
    }

    private void Update()
    {
        CalculateVelocity();
        CalculateAngle();
        LookAtTarget();
        Shoot();
    }
    
    private void OnEnable()
    {
        settings.inputController.Enable();
    }

    private void OnDisable()
    {
        settings.inputController.Disable();
    }

    private void LookAtTarget()
    {
        Vector3 direction = target.position - transform.position;
        Quaternion lookRotation = Quaternion.LookRotation(direction);
        y = lookRotation.eulerAngles.y;
        Quaternion rotationY = Quaternion.Euler(0f, y, 0f);
        Quaternion rotationX = Quaternion.Euler(-x, y, 0f);
        partToRotateY.rotation = Quaternion.Slerp(partToRotateY.rotation, rotationY, Time.deltaTime * settings.rotationSpeed);
        partToRotateX.rotation = Quaternion.Slerp(partToRotateX.rotation, rotationX, Time.deltaTime * settings.rotationSpeed);
    }

    private float nextTimeToFire = 0f;
    
    private void Shoot()
    {
        nextTimeToFire -= Time.deltaTime;
        if (!(nextTimeToFire <= 0f)) return;
        nextTimeToFire = 1 / settings.fireRate;
        var rb = Instantiate(settings.projectilePrefab, projectileSpawnPosition.position, Quaternion.identity).GetComponent<Rigidbody>();
        rb.velocity = velocity;
    }

    private void CalculateAngle()
    {
        if (settings.launcherConfig == LauncherConfig.CalculateVelocity ||
            settings.launcherConfig == LauncherConfig.CalculateNone)
        {
            x = Mathf.Clamp(settings.launchAngle, -20f, 80f);
        }
        else
        {
            var position = target.position;
            var position1 = transform.position;
            var dist = Math.Sqrt(Mathf.Pow((position.x - position1.x), 2) + Mathf.Pow((position.y - position1.y), 2));
            var a = Physics.gravity.y * Mathf.Pow((float) dist, 2) / (2 * Mathf.Pow(velocity.magnitude, 2));
            var b = (float) -dist;
            var c = position.z - position1.z + a;
            x = (float) Math.Atan2(QuadraticRoot(a, b, c), 1);
            Debug.Log(x);
        }
    }

    private void CalculateVelocity()
    {
        if (settings.launcherConfig == LauncherConfig.CalculateAngle ||
            settings.launcherConfig == LauncherConfig.CalculateNone)
        {
            velocity = partToRotateX.forward * settings.velocity;
        }
        else
        {
            float h;
            if (settings.launcherConfig == LauncherConfig.CalculateBoth && settings.calculateMaxHeight)
            {
                h = Mathf.Abs(target.position.y - partToRotateX.position.y * settings.maxHeightMultiplier);
            }
            else
            {
                h = settings.maxHeight;
            }

            var position = partToRotateX.position;
            var position1 = target.position;
            var gravity = Physics.gravity.y;

            var displacementY = position1.y - position.y;
            var displacementXZ = new Vector3 (position1.x - position.x, 0, position1.z - position.z);
            var time = Mathf.Sqrt(-2*h/gravity) + Mathf.Sqrt(2*(displacementY - h)/gravity);
            var velocityY = Vector3.up * Mathf.Sqrt (-2 * gravity * h);
            var velocityXZ = displacementXZ / time;

            velocity = velocityXZ + velocityY * -Mathf.Sign(gravity);
        }
    }
    
    private double QuadraticRoot(double a, double b, double c){
        double D = Math.Pow(b, 2) - (4 * a * c);
        return (-b + Math.Sqrt(D)) / (2 * a);
    }
}

LauncherSettings 类...

using Attributes.DrawIf;
using Attributes.DrawIfAndOr;
using UnityEngine;

[CreateAssetMenu]
public class LauncherSettings : ScriptableObject
{
    public InputActions.Launcher inputController;
    public LauncherConfig launcherConfig = LauncherConfig.CalculateVelocity;
    public GameObject projectilePrefab = default;
    public float rotationSpeed = 5f;
    public float fireRate = 5f;
    [DrawIfAndOr(nameof(launcherConfig), LauncherConfig.CalculateAngle, LauncherConfig.CalculateNone)]
    public float velocity = 200f;
    [DrawIfAndOr(nameof(launcherConfig), LauncherConfig.CalculateVelocity, LauncherConfig.CalculateNone)]
    public float launchAngle = 0f;
    [DrawIf(nameof(launcherConfig), LauncherConfig.CalculateBoth)]
    public bool calculateMaxHeight = true;
    [DrawIf(nameof(calculateMaxHeight), false)]
    public float maxHeight;
    [DrawIf(nameof(calculateMaxHeight), true)]
    public float maxHeightMultiplier = 5f;
}

如果第一个参数等于另一个参数,则DrawIfand是隐藏检查器中的值的属性。DrawIfAndOr你可以完全忽略它们。速度的计算是由 Sebastian Lague ( https://www.youtube.com/channel/UCmtyQOKKmrMVaKuRXz02jbQ ) 在他的运动学方程 (E03: 球问题) ( https://youtu.be/IvT8hjy6q4o ) 中计算的。角度的计算来自我的另一个问题的答案(找到一个角度来发射射弹以达到特定点)。

任何帮助表示赞赏...谢谢...

标签: c#unity3dphysicsprojectile

解决方案


推荐阅读