คำตอบนั้นง่ายมากถ้าคุณคิดเลข คุณมีระยะทางคงที่ของ Y และระยะทางตัวแปร X (ดูรูปที่ 1) คุณต้องหามุมระหว่าง Z และ X และหมุนป้อมปืนของคุณให้มากขึ้น
ขั้นตอนที่ 1 - หาระยะห่างระหว่างแนวป้อมปืน (V) และแนวปืน (W) ซึ่งเป็น Y (นี่คงที่ แต่ไม่เจ็บในการคำนวณ) รับระยะทางจากป้อมปืนไปยังเป้าหมาย (ซึ่งก็คือ X)
ขั้นตอนที่ 2 - หาร Y ด้วย X จากนั้นรับค่าไฮเพอร์โบลิกไซน์ของค่า
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.
ขั้นตอนที่ 3 - หมุนป้อมปืนให้มากขึ้น (รอบแกนที่เดินจากด้านบนลงไปด้านล่างสุดมีแนวโน้มมากขึ้นที่แกนขึ้น แต่มีเพียงคุณเท่านั้นที่รู้ส่วนนั้น)
gameObject.transform.Rotate(Vector3.up, turnAngle);
แน่นอนในกรณีนี้คุณจำเป็นต้องใช้มันเพื่อเปิดทวนเข็มนาฬิกาดังนั้นคุณอาจต้องเพิ่มลบในด้านหน้าของ turnAngle -turnAngle
มีในขณะที่
แก้ไขบางส่วน ขอบคุณ @ens ที่ชี้ให้เห็นความแตกต่างของระยะทาง
OP กล่าวว่าปืนของเขามีมุมดังนั้นเราไปดูภาพก่อนอธิบายในภายหลัง:
เรารู้อยู่แล้วจากการคำนวณก่อนหน้านี้ว่าจะเล็งไปที่เส้นสีแดงตามเส้นสีฟ้า ดังนั้นเล็งไปที่เส้นสีน้ำเงินก่อน:
float turnAngle = angleBetweenTurretAndTarget - angleBetweenTurretAndGun;
turret.transform.Rotate(Vector3.up, turnAngle);
การคำนวณเพียงอย่างเดียวที่แตกต่างกันที่นี่คือการคำนวณ "X Prime" (X ') เพราะมุมระหว่างปืนและป้อมปืน (มุม "a") เปลี่ยนระยะห่างระหว่างเส้น
//(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);
ส่วนต่อไปนี้มีความจำเป็นเท่านั้นหากคุณกำลังทำปืนป้อมปืนแบบแยกส่วน (เช่นผู้ใช้สามารถเปลี่ยนปืนบนป้อมปืนและปืนที่แตกต่างกันมีมุมที่แตกต่างกัน) หากคุณทำสิ่งนี้ในเครื่องมือแก้ไขคุณสามารถดูได้ว่ามุมของปืนนั้นเป็นอย่างไรตามป้อมปืน
มีสองวิธีในการหามุม "a" หนึ่งวิธีคือ transform.up:
float angleBetween = Vector3.Angle(turret.transform.up, gun.transform.up);
เทคนิคข้างต้นจะคำนวณในแบบ 3 มิติดังนั้นหากคุณต้องการผลลัพธ์แบบ 2 มิติคุณต้องกำจัดแกน Z (นั่นคือสิ่งที่ฉันคิดว่าแรงโน้มถ่วงอยู่ที่ใด แต่ถ้าคุณเปลี่ยนอะไรเลยในเอกภาพมันคือแกน Y ที่ขึ้นหรือลง ie แรงโน้มถ่วงอยู่บนแกน Y ดังนั้นคุณอาจต้องเปลี่ยนสิ่งต่าง ๆ ):
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);
วิธีที่สองคือวิธีการหมุน (ฉันกำลังคิดแบบ 2D ในกรณีนี้):
double angleRadians = Mathf.Asin(turret.transform.rotation.z - gun.transform.rotation.z);
double angle = 2 * Mathf.Rad2Deg * angleRadians;
อีกครั้งรหัสเหล่านี้จะให้ค่าที่เป็นบวกกับคุณดังนั้นคุณอาจต้องเพิ่มหรือลบจำนวนขึ้นอยู่กับมุม (มีการคำนวณด้วยเช่นกัน แต่ฉันจะไม่ไปในเชิงลึก) จุดเริ่มต้นที่ดีคือVector2.Dot
วิธีการใน Unity
บล็อกสุดท้ายของรหัสสำหรับคำอธิบายเพิ่มเติมเกี่ยวกับสิ่งที่เรากำลังทำอยู่:
//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);
หากคุณทำทุกอย่างถูกต้องคุณควรได้ฉากเช่นนี้ ( ลิงก์สำหรับ unitypackage ):
สิ่งที่ฉันหมายถึงโดยค่าบวกเสมอ:
วิธี Z สามารถให้ค่าลบ:
สำหรับฉากตัวอย่างรับunitypackage จากลิงค์นี้
นี่คือรหัสที่ฉันใช้ในฉาก (บนป้อมปืน):
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.
}
}
โค้ดดัดแปลง 3D พร้อม X และ Z เป็นระนาบ 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.
}
}