C# 找到发射弹丸的角度和速度,以达到特定点

C# 找到发射弹丸的角度和速度,以达到特定点,c#,unity3d,physics,projectile,C#,Unity3d,Physics,Projectile,我正在尝试制作一门大炮,它可以在统一的3D世界中射击目标。。。我有一个LaunchConfigenum-> public enum LauncherConfig { CalculateNone = 0, CalculateAngle = 1, CalculateVelocity = 2, CalculateBoth = 3 } 当选择CalculateAngle时,用户可以输入弹丸的初始速度,并且必须计算弹丸到达目标所需的发射角度。 同样适用于Calculate

我正在尝试制作一门大炮,它可以在统一的3D世界中射击目标。。。我有一个
LaunchConfig
enum->

public enum LauncherConfig
{
    CalculateNone = 0,
    CalculateAngle = 1,
    CalculateVelocity = 2,
    CalculateBoth = 3
}
当选择
CalculateAngle
时,用户可以输入弹丸的初始速度,并且必须计算弹丸到达目标所需的发射角度。 同样适用于
CalculateVelocity
calculateOne
允许用户输入两个值,
CalculateBoth
计算两个值

我应该使用什么方程式来计算这些值,以便它们对齐,即,当选择
CalculateAngle
时,计算的速度应该使弹丸看起来自然地从枪管中出来。对于
CalculateBoth
,应计算角度,以便计算出的速度将弹丸发射出枪管,而不是任何其他方向

我有我的当前位置(矢量3),目标位置(矢量3)。 计算的角度将影响加农炮的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);
    }
}
如果第一个参数等于另一个,则
DrawIf
DrawIfAndOr
是在inspector中隐藏变量的属性。你可以完全忽略它们。速度的计算是通过 Sebastian Lague()在他的运动学方程中(E03:球问题)()。角度的计算是根据对我的另一个问题()的回答得出的

感谢您的帮助。。。
谢谢…

我的演算有点生疏,但是vtcos(ang)=x。所以我假设你知道v是初速。T是飞行所花费的时间(您可以用总距离来定义以简化)。x是你降落的地方。所以你可以得到ang=arcos(vt/x)或者我认为是这样。摩擦力为0的情况就这样了。等等。。。那么您想根据起点和终点来确定轨迹?加农炮要改变强度和角度吗?如果是这样的话,我制作了一个Trajection类,可以使用起点、终点和最大高度确定抛物线路径,如果你愿意,我会发送一个链接(在另一个StackOverflow线程上)@1MangomMaster1是的,这基本上就是我想要的,但是这些值可以改变,就像我想把大炮的角度增加两倍,然后计算速度,同样的速度,如果我想让它有两倍的速度,角度也应该改变…你可以确定高度,就像我说的,你有起点,所以我想你可以搞乱起点和高度来得到角度,从那里它会做弧到终点检查我提供的解决方案,我把我做的代码的图像,这是一种解决办法,但它工作得非常好,所以我想这是好的,而且,我在那里添加了一个图片中没有的修复,所以请注意。
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;
}