192 lines
6.8 KiB
C#
192 lines
6.8 KiB
C#
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
|
|
public class PlaneController : MonoBehaviour
|
|
{
|
|
public GameObject planePrefab;
|
|
public float flightDuration = 5f;
|
|
public float arcHeight = 0.3f;
|
|
public float hoverHeight = 0.2f;
|
|
public float globeRadius = 5f;
|
|
public float rotateToDuration = 1.5f;
|
|
|
|
[Header("Camera Follow")]
|
|
public float cameraDistance = 3f;
|
|
public float cameraHeight = 0.5f;
|
|
public float cameraSmoothing = 3f;
|
|
|
|
private bool _flying = false;
|
|
private GameObject _plane;
|
|
private LineRenderer _trail;
|
|
private Transform _globe;
|
|
private Camera _mainCamera;
|
|
private Vector3 _cameraStartPos;
|
|
private Quaternion _cameraStartRot;
|
|
|
|
void Start()
|
|
{
|
|
_globe = GameObject.Find("Globe")?.transform;
|
|
_mainCamera = Camera.main;
|
|
SpawnPlane();
|
|
PlaceOverCountry("Ukraine");
|
|
}
|
|
|
|
void SpawnPlane()
|
|
{
|
|
if (planePrefab == null) return;
|
|
_plane = Instantiate(planePrefab);
|
|
if (_globe != null) _plane.transform.SetParent(_globe);
|
|
|
|
_trail = _plane.AddComponent<LineRenderer>();
|
|
_trail.positionCount = 0;
|
|
_trail.startWidth = 0.03f;
|
|
_trail.endWidth = 0.03f;
|
|
_trail.useWorldSpace = true;
|
|
_trail.material = new Material(Shader.Find("Sprites/Default"));
|
|
_trail.material.color = new Color(1f, 1f, 1f, 0.8f);
|
|
}
|
|
|
|
Vector3 GetLocalDirection(CountryData c)
|
|
{
|
|
Renderer rend = c.GetComponentInChildren<Renderer>();
|
|
Vector3 worldCenter = rend != null ? rend.bounds.center : c.transform.position;
|
|
if (_globe == null) return worldCenter.normalized;
|
|
return _globe.InverseTransformPoint(worldCenter).normalized;
|
|
}
|
|
|
|
public void PlaceOverCountry(string countryName)
|
|
{
|
|
if (_plane == null) return;
|
|
CountryData[] all = FindObjectsOfType<CountryData>();
|
|
foreach (CountryData c in all)
|
|
{
|
|
if (c.countryName == countryName)
|
|
{
|
|
SnapToSurface(c);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void SnapToSurface(CountryData country)
|
|
{
|
|
if (_plane == null || _globe == null) return;
|
|
Vector3 dir = GetLocalDirection(country);
|
|
_plane.transform.localPosition = dir * (globeRadius + hoverHeight);
|
|
_plane.transform.up = (_plane.transform.position - _globe.position).normalized;
|
|
}
|
|
|
|
public void Fly(CountryData fromCountry, CountryData toCountry, System.Action onArrived)
|
|
{
|
|
if (_flying || _plane == null) return;
|
|
StartCoroutine(FlyCoroutine(fromCountry, toCountry, onArrived));
|
|
}
|
|
|
|
IEnumerator FlyCoroutine(CountryData fromCountry, CountryData toCountry, System.Action onArrived)
|
|
{
|
|
_flying = true;
|
|
_trail.positionCount = 0;
|
|
|
|
_cameraStartPos = _mainCamera.transform.position;
|
|
_cameraStartRot = _mainCamera.transform.rotation;
|
|
|
|
Vector3 fromDir = GetLocalDirection(fromCountry);
|
|
float elapsed = 0f;
|
|
|
|
while (elapsed < flightDuration)
|
|
{
|
|
elapsed += Time.deltaTime;
|
|
float t = Mathf.Clamp01(elapsed / flightDuration);
|
|
float smoothT = Mathf.SmoothStep(0f, 1f, t);
|
|
|
|
Vector3 toDir = GetLocalDirection(toCountry);
|
|
Vector3 onSphere = Vector3.Slerp(fromDir, toDir, smoothT);
|
|
float arc = Mathf.Sin(smoothT * Mathf.PI) * arcHeight;
|
|
_plane.transform.localPosition = onSphere * (globeRadius + hoverHeight + arc);
|
|
|
|
float tNext = Mathf.Min(smoothT + 0.02f, 1f);
|
|
Vector3 nextOnSphere = Vector3.Slerp(fromDir, toDir, tNext);
|
|
float nextArc = Mathf.Sin(tNext * Mathf.PI) * arcHeight;
|
|
Vector3 nextWorld = _globe.TransformPoint(nextOnSphere * (globeRadius + hoverHeight + nextArc));
|
|
|
|
Vector3 worldForward = (nextWorld - _plane.transform.position).normalized;
|
|
Vector3 worldUp = (_plane.transform.position - _globe.position).normalized;
|
|
if (worldForward.magnitude > 0.001f)
|
|
_plane.transform.rotation = Quaternion.LookRotation(worldForward, worldUp);
|
|
|
|
Vector3 sideDir = Vector3.Cross(worldForward, worldUp).normalized;
|
|
Vector3 desiredPos = _plane.transform.position
|
|
+ sideDir * cameraDistance
|
|
+ worldUp * cameraHeight;
|
|
Quaternion desiredRot = Quaternion.LookRotation(
|
|
_plane.transform.position - desiredPos, worldUp);
|
|
|
|
_mainCamera.transform.position = Vector3.Lerp(
|
|
_mainCamera.transform.position, desiredPos,
|
|
Time.deltaTime * cameraSmoothing);
|
|
_mainCamera.transform.rotation = Quaternion.Slerp(
|
|
_mainCamera.transform.rotation, desiredRot,
|
|
Time.deltaTime * cameraSmoothing);
|
|
|
|
_trail.positionCount++;
|
|
_trail.SetPosition(_trail.positionCount - 1, _plane.transform.position);
|
|
|
|
yield return null;
|
|
}
|
|
|
|
SnapToSurface(toCountry);
|
|
_trail.positionCount = 0;
|
|
|
|
yield return StartCoroutine(RestoreCamera());
|
|
yield return StartCoroutine(RotateGlobeToCountry(toCountry));
|
|
|
|
_flying = false;
|
|
onArrived?.Invoke();
|
|
}
|
|
|
|
IEnumerator RestoreCamera()
|
|
{
|
|
float elapsed = 0f;
|
|
float duration = 0.8f;
|
|
Vector3 fromPos = _mainCamera.transform.position;
|
|
Quaternion fromRot = _mainCamera.transform.rotation;
|
|
|
|
while (elapsed < duration)
|
|
{
|
|
elapsed += Time.deltaTime;
|
|
float t = Mathf.SmoothStep(0f, 1f, elapsed / duration);
|
|
_mainCamera.transform.position = Vector3.Lerp(fromPos, _cameraStartPos, t);
|
|
_mainCamera.transform.rotation = Quaternion.Slerp(fromRot, _cameraStartRot, t);
|
|
yield return null;
|
|
}
|
|
_mainCamera.transform.position = _cameraStartPos;
|
|
_mainCamera.transform.rotation = _cameraStartRot;
|
|
}
|
|
|
|
IEnumerator RotateGlobeToCountry(CountryData country)
|
|
{
|
|
if (_globe == null || _mainCamera == null) yield break;
|
|
|
|
Vector3 countryWorldPos = _globe.TransformPoint(GetLocalDirection(country) * globeRadius);
|
|
Vector3 globeCenter = _globe.position;
|
|
Vector3 camDir = (_mainCamera.transform.position - globeCenter).normalized;
|
|
|
|
Quaternion startRot = _globe.rotation;
|
|
Quaternion targetRot = Quaternion.FromToRotation(
|
|
(countryWorldPos - globeCenter).normalized, camDir) * _globe.rotation;
|
|
|
|
float elapsed = 0f;
|
|
while (elapsed < rotateToDuration)
|
|
{
|
|
elapsed += Time.deltaTime;
|
|
float t = Mathf.SmoothStep(0f, 1f, elapsed / rotateToDuration);
|
|
_globe.rotation = Quaternion.Slerp(startRot, targetRot, t);
|
|
yield return null;
|
|
}
|
|
_globe.rotation = targetRot;
|
|
}
|
|
|
|
public bool IsFlying() => _flying;
|
|
}
|