Відповідь насправді досить проста, якщо ви займаєтеся математикою. Ви маєте фіксовану відстань 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
.
Редагував деякі частини. Завдяки @ens за вказівку на різницю відстані.
ОП заявив, що його пістолет має кут, тому ось ми переходимо до зображення, спочатку пояснення:
Ми вже знаємо з попереднього розрахунку, куди слід орієнтуватися на червону лінію відповідно до синьої. Тож спочатку орієнтуючись на синю лінію:
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);
Наведена вище техніка буде обчислена в 3D, тож якщо ви хочете отримати двовимірний результат, вам потрібно позбутися осі Z (саме це я припускаю, де гравітація, але якщо ви нічого не змінили, в Unity це вісь Y, яка знаходиться вгору або вниз, тобто гравітація знаходиться на осі 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
метод в Єдності.
Заключний блок коду для додаткового пояснення того, що ми робимо:
//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);
Якщо ви зробили все правильно, вам слід отримати подібну сцену ( посилання на папір єдності ):
Що я маю на увазі під завжди позитивними значеннями:
Метод Z може дати негативні значення:
Для прикладу сцени знайдіть пакунок єдності за цим посиланням .
Ось код, який я використав у сцені (на башті):
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.
}
}