Làm cách nào tôi có thể khởi chạy GameObject tại mục tiêu nếu tôi được cung cấp mọi thứ ngoại trừ góc phóng của nó?


11

Tôi đang cố gắng phóng một vật thể vào một mục tiêu, với vị trí của nó, vị trí mục tiêu của nó, tốc độ phóng và trọng lực. Tôi đang theo công thức này từ Wikipedia :

θ= =mộtrctmộtn(v2±v4-g(gx2+2yv2)gx)

Tôi đã đơn giản hóa mã theo khả năng tốt nhất của mình, nhưng tôi vẫn không thể liên tục đạt được mục tiêu. Tôi chỉ đang xem xét quỹ đạo cao hơn, trong số hai có sẵn từ lựa chọn + - trong công thức.

Có ai biết tôi đang làm gì sai không?

using UnityEngine;

public class Launcher : MonoBehaviour
{
    public float speed = 10.0f;

    void Start()
    {
        Launch(GameObject.Find("Target").transform);
    }

    public void Launch(Transform target)
    {
        float angle = GetAngle(transform.position, target.position, speed, -Physics2D.gravity.y);
        var forceToAdd = new Vector2(Mathf.Cos(angle), Mathf.Sin(angle)) * speed;
        GetComponent<Rigidbody2D>().AddForce(forceToAdd, ForceMode2D.Impulse);
    }

    private float GetAngle(Vector2 origin, Vector2 destination, float speed, float gravity)
    {
        float angle = 0.0f;

        //Labeling variables to match formula
        float x = Mathf.Abs(destination.x - origin.x);
        float y = Mathf.Abs(destination.y - origin.y);
        float v = speed;
        float g = gravity;

        //Formula seen above
        float valueToBeSquareRooted = Mathf.Pow(v, 4) - g * (g * Mathf.Pow(x, 2) + 2 * y * Mathf.Pow(v, 2));
        if (valueToBeSquareRooted >= 0)
        {
            angle = Mathf.Atan((Mathf.Pow(v, 2) + Mathf.Sqrt(valueToBeSquareRooted)) / g * x);
        }
        else
        {
            //Destination is out of range
        }

        return angle;
    }
}

Hai điều nổi bật với tôi. -Physics2D.gravity.y và angle = Mathf.Atan ((Mathf.Pow (v, 2) + Mathf.Sqrt (valueToBeSapesRooted)) / g * x);, công thức dự đoán trọng lực sẽ là giá trị dương như 9,81 , thứ hai là mẫu số gx, cách bạn có nó, bạn chia cho g, sau đó nhân thời gian x, bạn nên có mẫu số (g * x) để bội số xảy ra trước khi chia.
Mike White

Câu trả lời:


14

Tôi hơi nghi ngờ khi sử dụng atan ở đây, vì tỷ lệ tiếp tuyến bắn ra vô cùng ở một số góc nhất định và có thể dẫn đến các lỗi số (thậm chí bên ngoài trường hợp không xác định / chia cho trường hợp 0 ​​để chụp thẳng lên / xuống).

Sử dụng các công thức đã được giải quyết trong câu trả lời này , chúng ta có thể tham số hóa điều này theo thời gian (chưa biết ban đầu) để tác động T, bằng cách sử dụng chữ cái đầu speedcủa tên lửa:

// assuming x, y are the horizontal & vertical offsets from source to target,
// and g is the (positive) gravitational acceleration downwards
// and speed is the (maximum) launch speed of the projectile...

b = speed*speed - y * g
discriminant = b*b - g*g * (x*x + y*y)

if(discriminant < 0)
  return CANNOT_REACH_TARGET; // Out of range, need higher shot velocity.

discRoot = sqrt(discriminant);

// Impact time for the most direct shot that hits.
T_min = sqrt((b - discRoot) * 2 / (g * g));

// Impact time for the highest shot that hits.
T_max = sqrt((b + discRoot) * 2 / (g * g));

Bạn có thể chọn T_min hoặc T_max (hoặc một cái gì đó ở giữa nếu bạn muốn bắn với tốc độ lên tới nhưng không nhất thiết phải bằng một số tối đa)

Quỹ đạo ví dụ

( T_minlà quỹ đạo màu đỏ nông ở phía dưới, vàT_max là màu xanh lá cây cao. Bất kỳ quỹ đạo nào giữa chúng đều khả thi ở một tốc độ khả thi nào đó.

Bây giờ chúng tôi đã tính một giá trị cho T, phần còn lại rất đơn giản:

vx = x/T;
vy = y/T + T*g/2;

velocity = (vx, vy);

Bạn có thể sử dụng vận tốc này trực tiếp (nó có chiều dài bằng với speedxây dựng) hoặc nếu bạn thực sự cần biết góc, bạn có thể sử dụngatan2(vy, vx)


Chỉnh sửa: để áp dụng điều này cho nhiều trường hợp hơn, đây là phiên bản 3D:

Vector3 toTarget = target.position - transform.position;

// Set up the terms we need to solve the quadratic equations.
float gSquared = Physics.gravity.sqrMagnitude;
float b = speed * speed + Vector3.Dot(toTarget, Physics.gravity);    
float discriminant = b * b - gSquared * toTarget.sqrMagnitude;

// Check whether the target is reachable at max speed or less.
if(discriminant < 0) {
    // Target is too far away to hit at this speed.
    // Abort, or fire at max speed in its general direction?
}

float discRoot = Mathf.Sqrt(discriminant);

// Highest shot with the given max speed:
float T_max = Mathf.Sqrt((b + discRoot) * 2f / gSquared);

// Most direct shot with the given max speed:
float T_min = Mathf.Sqrt((b - discRoot) * 2f / gSquared);

// Lowest-speed arc available:
float T_lowEnergy = Mathf.Sqrt(Mathf.Sqrt(toTarget.sqrMagnitude * 4f/gSquared));

float T = // choose T_max, T_min, or some T in-between like T_lowEnergy

// Convert from time-to-hit to a launch velocity:
Vector3 velocity = toTarget / T - Physics.gravity * T / 2f;

// Apply the calculated velocity (do not use force, acceleration, or impulse modes)
projectileBody.AddForce(velocity, ForceMode.VelocityChange);

Phải, tôi đã tìm ra giải pháp bằng cách cắm thời gian như đã biết, nhưng tôi muốn lực lượng được biết đến.
Evorlor

1
Yea, Jost Petrie và @DMGregory là những diễn đàn inthis. :) không còn nghi ngờ gì nữa
Hamza Hasan

1
Aw, shucks, cảm ơn cả hai! :) @Evorlor discRootlà căn bậc hai của phân biệt đối xử , là phần xuất hiện dưới dấu hiệu căn bậc hai trong công thức bậc hai . bbằng -1 lần biến b trong công thức bậc hai. Thật không may, tôi không biết một cái tên mô tả hơn cho nó. (Tôi đã nhân nó với -1 khi chỉ định làm gọn các bước sau, vì điểm trừ hàng đầu đã được tích hợp sẵn và không ảnh hưởng đến bình phương). Xem câu trả lời khác cho một dẫn xuất đầy đủ, mặc dù một vài hình vuông bị thiếu (sẽ khắc phục trong thời gian ngắn)
DMGregory

1
Đường cong màu xanh và màu vàng thể hiện điều gì?
Slipp D. Thompson

3
@ SlippD. Các phương trình cho các giá trị thời gian này nằm trong câu trả lời được liên kết
DMGregory

3

Nhờ DMGregory, giờ đây tôi có tập lệnh mở rộng C # có thể được sử dụng cho việc này. Phiên bản gần đây nhất có thể được tìm thấy trên GitHub .

using UnityEngine;

public static class Rigidbody2DExtensions
{
    /// <summary>
    /// Applies the force to the Rigidbody2D such that it will land, if unobstructed, at the target position.  The arch [0, 1] determines the percent of arch to provide between the minimum and maximum arch.  If target is out of range, it will fail to launch and return false; otherwise, it will launch and return true.  This only takes the Y gravity into account, and X gravity will not affect the trajectory.
    /// </summary>
    public static bool SetTrajectory(this Rigidbody2D rigidbody2D, Vector2 target, float force, float arch = 0.5f)
    {
        Mathf.Clamp(arch, 0, 1);
        var origin = rigidbody2D.position;
        float x = target.x - origin.x;
        float y = target.y - origin.y;
        float gravity = -Physics2D.gravity.y;
        float b = force * force - y * gravity;
        float discriminant = b * b - gravity * gravity * (x * x + y * y);
        if (discriminant < 0)
        {
            return false;
        }
        float discriminantSquareRoot = Mathf.Sqrt(discriminant);
        float minTime = Mathf.Sqrt((b - discriminantSquareRoot) * 2) / Mathf.Abs(gravity);
        float maxTime = Mathf.Sqrt((b + discriminantSquareRoot) * 2) / Mathf.Abs(gravity);
        float time = (maxTime - minTime) * arch + minTime;
        float vx = x / time;
        float vy = y / time + time * gravity / 2;
        var trajectory = new Vector2(vx, vy);
        rigidbody2D.AddForce(trajectory, ForceMode2D.Impulse);
        return true;
    }
}

-6

Cá nhân, tôi thậm chí sẽ không sử dụng bất kỳ loại công thức phức tạp.

GetComponent<Rigidbody2D>.AddForce((target.transform.position - transform.position) * someSortOfMultiplier());

Nó chỉ bắn nó theo hướng của mục tiêu. Và nếu bạn muốn bù cho trọng lực, khoảng cách, v.v., hãy đặt thành someSortOfMultiplier()một hàm trả về một số float sẽ bù khi nhân với mã trên.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.