Поворот 2D объекта по оси Z и Y
Нужно поворачивать 2D объект вокруг оси Z, а так же сделать мнимое только визуальное вращение вокруг оси Y. Дополнительно нужно было сделать остановку поворота по Y при выборе объекта (сделано через GetMouseButtonDown(0)) и подвязать логику автоматического режима (метод AutoShoot).
Я решил, что плавно вращаться вокруг оси Y будет только sprite, а часть объекта GunScript будет резко поворачиваться в нужную позицию, чтобы избегать ошибок (вложенность и начальное положение приведены на изображении). Итоговый результат работает, но крайне грязно и неграмотно реализован.
Как лучше реализовать такой формат поворота объекта? Есть ли альтернативное простое решение (поворот по Y с помощью Animation или смена 2D изображений, создающие иллюзию трёхмерности)? Я указал дополнительные задачи для более подробного описания цели, их реализую сам.
public class GunControls : MonoBehaviour
{
[SerializeField] private GunScript _gun;
private GunStruct _planetGun;
[SerializeField, Range(0.5f, 10f)] private float _timePlanetHalfRotation;
[SerializeField, Range(0.005f, 10f)] private float _deltaTimePlanetRotation;
[SerializeField, Range(0.005f, 10f)] private float _speedSpritsRotate;
private Camera _mainCamera;
private static GunScript selectedGun = null;
[SerializeField] private int numberOfSelectedWeapon = 0;
void Start()
{
_mainCamera = Camera.main;
_planetGun = new WeaponStruct { weapon = _gun, timeShoot = _timePlanetHalfRotation, nextViewType = _gun.FieldViewVector.GetOpposite() };
}
void Update()
{
Vector3 worldMousePosition = _mainCamera.ScreenToWorldPoint(UnityEngine.Input.mousePosition);
if (Input.GetMouseButtonDown(0))
{
selectedGun = _planetGun.weapon;
if ((worldMousePosition.x > 0 && selectedGun.transform.right.x > 0) || (worldMousePosition.x < 0 && selectedGun.transform.right.x < 0))
{ }
else {
selectedGun.transform.localRotation = Quaternion.Euler(0f, 0f, 180f - selectedGun.transform.eulerAngles.z);
}
//установление направления и поворота по умолчанию
selectedGun.FieldViewVector = MainFieldViewType.Right;
selectedGun.SpriteRotationY.localRotation = Quaternion.Euler(Vector3.zero);
return;
}
if (selectedGun != null)//управление выбранным орудием
{
RotateWeapon_Z(selectedGun, CalculateRotation_Z(worldMousePosition, selectedGun.transform.position), Time.deltaTime);
if (Input.GetMouseButtonDown(1))
{
_planetGun.timeShoot = _timePlanetHalfRotation;
_planetGun.nextViewType = _planetGun.nextViewType.GetOpposite();
selectedGun = null;
}
}
AutoShoot(Time.deltaTime);
}
private void AutoShoot(float deltaTime)
{
GunScript weapon = _planetGun.weapon;
//Debug.Log("оружие перезаряжается (отчитывается время перезарядки до 0f)");
if (weapon != selectedGun)
{
_planetGun.timeShoot -= deltaTime;
//если в моментах, когда может самостоятельно стрелять
if (_planetGun.timeShoot >= _timePlanetHalfRotation - _deltaTimePlanetRotation || _planetGun.timeShoot <= _deltaTimePlanetRotation)
{
//если повернулся: перешел из состояния 'смотрит вправо' в 'смотрит влево' и наоборот
if (_planetGun.timeShoot <= _timePlanetHalfRotation / 2f && _planetGun.nextViewType != weapon.FieldViewVector)
{
weapon.FieldViewVector = _planetGun.nextViewType;
weapon.transform.localRotation = Quaternion.Euler(0f, 0f, 180f - weapon.transform.eulerAngles.z);
}
//если началась новая половина оборота
if (_planetGun.timeShoot <= 0)
{
_planetGun.timeShoot = _timePlanetHalfRotation;
_planetGun.nextViewType = _planetGun.nextViewType.GetOpposite();
}
else
{
//Debug.Log("стреляет автоматически (по очереди вправо/влево)");
}
}
RotateWeapon_Y(weapon);
}
}
private float CalculateRotation_Z(Vector3 finalPosition, Vector3 position)//подсчет угла поворота
{
Vector3 diference = finalPosition - position;
float rotateZ = Mathf.Atan2(diference.y, diference.x) * Mathf.Rad2Deg;
return rotateZ;
}
private void RotateWeapon_Z(GunScript weapon, float rotateZ, float deltaTime)//поворот по оси Z
{
weapon.transform.localRotation = Quaternion.Slerp(weapon.transform.localRotation, Quaternion.Euler(0f, 0f, rotateZ), weapon.RotationSpeed * deltaTime);
weapon.SpriteRotationZ.localRotation = Quaternion.Euler(0f, 0f, weapon.transform.localEulerAngles.z);
}
private void RotateWeapon_Y(GunScript weapon)//поворот по оси Y (никак не синхронизирован с поворотом 'логики' в методе AutoShoot)
{
weapon.SpriteRotationY.localRotation *= Quaternion.Euler(Vector3.up * _speedSpritsRotate);
}
}
public struct GunStruct
{
public GunScript weapon;
public float timeShoot;
public MainFieldViewType nextViewType;//указатель следующего направления
}
public enum MainFieldViewType//указатель направления орудия
{
Right, Left
}
public static class FieldViewType
{
public static MainFieldViewType GetOpposite(this MainFieldViewType a)
{
return (MainFieldViewType)((-1) * (int)a);
}
}
public class GunScript : MonoBehaviour
{
public MainFieldViewType FieldViewVector;
public Transform SpriteRotationY;
public Transform SpriteRotationZ;
public float RotationSpeed;
}
