Câu trả lời thực sự khá dễ nếu bạn làm toán. Bạn có một khoảng cách cố định là Y và khoảng cách thay đổi là X (Xem hình 1). Bạn cần tìm ra góc giữa Z và X và xoay tháp pháo của bạn nhiều hơn nữa.
Bước 1 - Nhận khoảng cách giữa đường tháp pháo (V) và đường súng (W) là Y (điều này không đổi nhưng không ảnh hưởng đến tính toán). Lấy khoảng cách từ tháp pháo đến mục tiêu (là X).
Bước 2 - Chia Y cho X và sau đó lấy Hyperbolic Sine của giá trị
double turnRadians = Mathf.Asin(Y/X);
double angle = Mathf.Rad2Deg * turnRadians;
//where B is the red dot, A is a point on the X line and C is a point on the Z line.
Bước 3 - Xoay tháp pháo nhiều hơn nữa (xung quanh trục đi từ đỉnh của nó xuống dưới cùng, rất có thể là trục lên nhưng chỉ bạn mới có thể biết phần đó).
gameObject.transform.Rotate(Vector3.up, turnAngle);
Tất nhiên trong trường hợp này, bạn cần nó để quay ngược chiều kim đồng hồ, do đó bạn có thể cần thêm một điểm trừ trước ngã rẽ ở đó, như trong -turnAngle
.
Chỉnh sửa một số phần. Cảm ơn @ens đã chỉ ra sự khác biệt về khoảng cách.
OP cho biết khẩu súng của anh ta có một góc nên chúng ta đi, hình ảnh trước, giải thích sau:
Chúng ta đã biết từ tính toán trước, nơi nhắm đường màu đỏ theo đường màu xanh. Vì vậy, nhắm đến dòng màu xanh đầu tiên:
float turnAngle = angleBetweenTurretAndTarget - angleBetweenTurretAndGun;
turret.transform.Rotate(Vector3.up, turnAngle);
Tính toán duy nhất khác ở đây, là tính toán của "X Prime" (X ') vì góc giữa súng và tháp pháo (góc "a") đã thay đổi khoảng cách giữa các đường.
//(this part had a mistake of using previous code inside new variable names, YPrime and Y are shown as X' and X in the 2nd picture.
float YPrime = Cos(a)*Y; //this part is what @ens is doing in his answer
double turnRadians = Mathf.Asin(YPrime/X);
double angle = Mathf.Rad2Deg * turnRadians;
turret.transform.Rotate(Vector3.up, angle);
Phần tiếp theo này là CHỈ cần thiết nếu bạn đang thực hiện mô-đun súng tháp pháo (tức là người dùng có thể thay đổi súng trên tháp pháo và các loại súng khác nhau có các góc khác nhau). Nếu bạn đang thực hiện việc này trong trình chỉnh sửa, bạn có thể thấy góc súng theo tháp pháo là gì.
Có hai phương pháp để tìm góc "a", một là phương thức Transform.up:
float angleBetween = Vector3.Angle(turret.transform.up, gun.transform.up);
Kỹ thuật trên sẽ tính toán trong 3D, vì vậy nếu bạn muốn có kết quả 2D, bạn cần loại bỏ trục Z (đó là điều tôi cho là trọng lực ở đâu, nhưng nếu bạn không thay đổi gì, thì trong Unity đó là trục Y lên hoặc xuống, tức là trọng lực nằm trên trục Y, vì vậy bạn có thể phải thay đổi mọi thứ):
Vector2 turretVector = new Vector2(turret.transform.up.x, turret.transform.up.y);
Vector2 gunVector = new Vector2(gun.transform.up.x, gun.transform.up.y);
float angleBetween = Vector2.Angle(turretVector, gunVector);
Cách thứ hai là phương pháp xoay vòng (tôi đang nghĩ về 2D trong trường hợp này):
double angleRadians = Mathf.Asin(turret.transform.rotation.z - gun.transform.rotation.z);
double angle = 2 * Mathf.Rad2Deg * angleRadians;
Một lần nữa, tất cả các mã này sẽ cung cấp cho bạn các giá trị dương, do đó bạn có thể phải cộng hoặc trừ số tiền tùy thuộc vào góc (cũng có các tính toán cho điều đó, nhưng tôi sẽ không đi sâu hơn). Một nơi tốt để bắt đầu điều này sẽ là Vector2.Dot
phương pháp trong Unity.
Khối mã cuối cùng để giải thích thêm về những gì chúng tôi đang làm:
//turn turret towards target
turretTransform.up = targetTransform.position - turretTransform.position;
//adjust for gun angle
if (weaponTransform.localEulerAngles.z <180) //if the value is over 180 it's actually a negative for us
turretTransform.Rotate(Vector3.forward, 90 - b - a);
else
turretTransform.Rotate(Vector3.forward, 90 - b + a);
Nếu bạn đã làm mọi thứ đúng, bạn sẽ nhận được một cảnh như thế này ( liên kết cho gói unitypackage ):
Ý tôi là luôn luôn có giá trị dương:
Phương thức Z có thể cho các giá trị âm:
Đối với một cảnh ví dụ, hãy lấy unitypackage từ liên kết này .
Đây là mã tôi đã sử dụng trong cảnh (trên tháp pháo):
public class TurretAimCorrection : MonoBehaviour
{
public Transform targetTransform;
public Transform turretTransform;
public Transform weaponTransform;
private float f, d, x, y, h, b, a, weaponAngle, turnAngle;
private void Start()
{
TurnCorrection();
}
private void Update()
{
TurnCorrection();
}
void TurnCorrection()
{
//find distances and angles
d = Vector2.Distance(new Vector2(targetTransform.position.x, targetTransform.position.y), new Vector2(turretTransform.position.x, turretTransform.position.y));
x = Vector2.Distance(new Vector2(turretTransform.position.x, turretTransform.position.y), new Vector2(weaponTransform.position.x, weaponTransform.position.y));
weaponAngle = weaponTransform.localEulerAngles.z;
weaponAngle = weaponAngle * Mathf.Deg2Rad;
y = Mathf.Abs(Mathf.Cos(weaponAngle) * x);
b = Mathf.Rad2Deg * Mathf.Acos(y / d);
a = Mathf.Rad2Deg * Mathf.Acos(y / x);
//turn turret towards target
turretTransform.up = targetTransform.position - turretTransform.position;
//adjust for gun angle
if (weaponTransform.localEulerAngles.z < 180)
turretTransform.Rotate(Vector3.forward, 90 - b - a);
else
turretTransform.Rotate(Vector3.forward, 90 - b + a);
//Please leave this comment in the code. This code was made by
//http://gamedev.stackexchange.com/users/93538/john-hamilton a.k.a. CrazyIvanTR.
//This code is provided as is, with no guarantees. It has worked in local tests on Unity 5.5.0f3.
}
}
Mã được điều chỉnh 3D với X và Z là mặt phẳng 2D:
public class TurretAimCorrection : MonoBehaviour
{
public Transform targetTransform; //drag target here
public Transform turretTransform; //drag turret base or turret top part here
public Transform weaponTransform; //drag the attached weapon here
private float d, x, y, b, a, weaponAngle, turnAngle;
private void Start()
{
TurnAdjustment();
}
private void Update()
{
TurnAdjustment();
}
void TurnAdjustment()
{
d = Vector2.Distance(new Vector2(targetTransform.position.x, targetTransform.position.z), new Vector2(turretTransform.position.x, turretTransform.position.z));
x = Vector2.Distance(new Vector2(turretTransform.position.x, turretTransform.position.z), new Vector2(weaponTransform.position.x, weaponTransform.position.z));
weaponAngle = weaponTransform.localEulerAngles.y;
weaponAngle = weaponAngle * Mathf.Deg2Rad;
y = Mathf.Abs(Mathf.Cos(weaponAngle) * x);
b = Mathf.Rad2Deg * Mathf.Acos(y / d);
a = Mathf.Rad2Deg * Mathf.Acos(y / x);
//turn turret towards target
turretTransform.forward = new Vector3(targetTransform.position.x, 0, targetTransform.position.z) - new Vector3(turretTransform.position.x, 0, turretTransform.position.z);
//adjust for gun angle
if (weaponTransform.localEulerAngles.y < 180)
turretTransform.Rotate(Vector3.up, - a +b-90);
else
turretTransform.Rotate(Vector3.up, + a+ b - 90);
//Please leave this comment in the code. This code was made by
//http://gamedev.stackexchange.com/users/93538/john-hamilton a.k.a. CrazyIvanTR.
//This code is provided as is, with no guarantees. It has worked in local tests on Unity 5.5.0f3.
}
}