Initial commit

This commit is contained in:
2026-03-17 18:10:00 +02:00
commit bc30c054ea
1911 changed files with 3034729 additions and 0 deletions

8
Assets/Scripts/Age.meta Normal file
View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 2e2247a86126c0e4b900c3c1a9f1f2c3
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,153 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AgingController : MonoBehaviour
{
[Header("Ìàòåð³àëè øê³ðè")]
public Material faceMaterial;
public Material neckMaterial;
public Material bodyMaterial;
[Header("Çà÷³ñêè")]
public GameObject youngHair;
public GameObject oldHaircut;
[Header("Áîðîäà")]
public GameObject beard;
[Header("Blend Shapes")]
public SkinnedMeshRenderer bodyMesh;
public int bellyBlendIndex = 4;
public int overweightBlendIndex = 1;
[Header("×àñ")]
public float agingDuration = 20f;
[HideInInspector] public float currentAge = 25f;
[HideInInspector] public bool isFinished = false;
private Material[] oldHairMats;
private Material[] youngHairMats;
private Material[] beardMats;
private float timer = 0f;
private Color currentHairColor = new Color(0.80f, 0.80f, 0.80f);
private Color _lastAppliedColor = Color.clear;
private int _currentPlanetIndex = 2;
private HairColorSettings hairColorSettings;
void Awake()
{
hairColorSettings = FindObjectOfType<HairColorSettings>();
youngHairMats = GetMaterials(youngHair);
oldHairMats = GetMaterials(oldHaircut);
beardMats = GetMaterials(beard);
RestartAging();
}
void Update()
{
if (isFinished) return;
if (hairColorSettings != null)
{
Color newColor = hairColorSettings.GetHairColor(_currentPlanetIndex);
if (newColor != _lastAppliedColor)
{
_lastAppliedColor = newColor;
currentHairColor = newColor;
ApplyColorToMats(oldHairMats);
ApplyColorToMats(beardMats);
}
}
timer += Time.deltaTime;
float t = Mathf.Clamp01(timer / agingDuration);
currentAge = Mathf.Lerp(25f, 70f, t);
faceMaterial.SetFloat("_AgingProgress", t);
bodyMesh.SetBlendShapeWeight(bellyBlendIndex, t * 100f);
bodyMesh.SetBlendShapeWeight(overweightBlendIndex, t * 10f);
SetAlpha(youngHairMats, 1f - t);
if (t >= 0.4f)
{
float hairT = Mathf.Clamp01((t - 0.4f) / 0.6f);
SetAlphaWithColor(oldHairMats, hairT);
SetAlphaWithColor(beardMats, hairT);
}
if (t >= 1f) isFinished = true;
}
public void RestartAging()
{
faceMaterial.SetFloat("_AgingProgress", 0f);
SetAlpha(oldHairMats, 0f);
SetAlpha(beardMats, 0f);
SetAlpha(youngHairMats, 1f);
bodyMesh.SetBlendShapeWeight(bellyBlendIndex, 0f);
bodyMesh.SetBlendShapeWeight(overweightBlendIndex, 0f);
timer = 0f;
currentAge = 25f;
isFinished = false;
_lastAppliedColor = Color.clear;
}
public void ApplyHairColor(int planetIndex)
{
if (hairColorSettings == null) return;
_currentPlanetIndex = planetIndex;
currentHairColor = hairColorSettings.GetHairColor(planetIndex);
_lastAppliedColor = currentHairColor;
ApplyColorToMats(oldHairMats);
ApplyColorToMats(beardMats);
}
void ApplyColorToMats(Material[] mats)
{
foreach (var m in mats)
{
Color c = m.GetColor("_BaseColor");
c.r = currentHairColor.r;
c.g = currentHairColor.g;
c.b = currentHairColor.b;
m.SetColor("_BaseColor", c);
}
}
Material[] GetMaterials(GameObject obj)
{
var renderers = obj.GetComponentsInChildren<Renderer>();
var list = new List<Material>();
foreach (var r in renderers)
foreach (var m in r.materials)
list.Add(m);
return list.ToArray();
}
void SetAlpha(Material[] mats, float alpha)
{
foreach (var m in mats)
{
Color c = m.GetColor("_BaseColor");
c.a = alpha;
m.SetColor("_BaseColor", c);
}
}
void SetAlphaWithColor(Material[] mats, float alpha)
{
foreach (var m in mats)
{
Color c = m.GetColor("_BaseColor");
c.r = currentHairColor.r;
c.g = currentHairColor.g;
c.b = currentHairColor.b;
c.a = alpha;
m.SetColor("_BaseColor", c);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 49e589cdd4d181045ab5adda62877ab3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,18 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
public class AgingUI : MonoBehaviour
{
public AgingController agingController;
public TextMeshProUGUI ageText;
public TextMeshProUGUI planetText;
void Update()
{
int age = Mathf.RoundToInt(agingController.currentAge);
ageText.text = "³ê: " + age + " ³ç 70 ðîê³â";
planetText.text = "Íà Çåìë³ ïðîéøëî: 70 ðîê³â";
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a416d6325763bd94992f465ce1466203
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,43 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class HairColorSettings : MonoBehaviour
{
[Header("Ęîëłđ ńčâčíč íŕ đłçíčő ďëŕíĺňŕő")]
public bool useDefaultGray = true;
[Header("Ęîëüîđč ńčâčíč äë˙ ęîćíîż ďëŕíĺňč")]
public Color mercuryHairColor = new Color(0.85f, 0.85f, 0.85f);
public Color venusHairColor = new Color(0.95f, 0.90f, 0.80f);
public Color earthHairColor = new Color(0.80f, 0.80f, 0.80f);
public Color moonHairColor = new Color(0.90f, 0.90f, 0.95f);
public Color marsHairColor = new Color(0.85f, 0.75f, 0.70f);
public Color jupiterHairColor = new Color(0.80f, 0.85f, 0.90f);
public Color saturnHairColor = new Color(0.90f, 0.85f, 0.75f);
public Color uranusHairColor = new Color(0.75f, 0.90f, 0.90f);
public Color neptuneHairColor = new Color(0.70f, 0.75f, 0.95f);
public Color plutoHairColor = new Color(0.80f, 0.80f, 0.90f);
private static readonly Color defaultGray = Color.white;
public Color GetHairColor(int planetIndex)
{
if (useDefaultGray) return defaultGray;
switch (planetIndex)
{
case 0: return mercuryHairColor;
case 1: return venusHairColor;
case 2: return earthHairColor;
case 3: return moonHairColor;
case 4: return marsHairColor;
case 5: return jupiterHairColor;
case 6: return saturnHairColor;
case 7: return uranusHairColor;
case 8: return neptuneHairColor;
case 9: return plutoHairColor;
default: return defaultGray;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c2e109373c9aed240a398aabb35c34dc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,133 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
public class PlanetSelector : MonoBehaviour
{
public AgingController agingController;
public TimeManager timeManager;
public Button previousButton;
public Button nextButton;
public TextMeshProUGUI ageText;
public TextMeshProUGUI planetText;
public Material skyboxMaterial;
private int currentPlanetIndex = 2;
private string[] planetNames = {
"Ìåðêóð³¿", "Âåíåð³", "Çåìë³", "̳ñÿö³",
"Ìàðñ³", "Þï³òåð³", "Ñàòóðí³", "Óðàí³", "Íåïòóí³", "Ïëóòîí³"
};
private float[] dayLengthHours = {
1407.6f, 5832.5f, 24f, 708.7f,
24.6f, 9.9f, 10.7f, 17.2f, 16.1f, 153.3f
};
private float[] daysInYear = {
88f, 225f, 365f, 365f,
687f, 4333f, 10759f, 30687f, 60190f, 90560f
};
private float[] agingDurations = {
5f, 10f, 20f, 20f, 28f, 40f, 40f, 40f, 40f, 40f
};
private float[] orbitalYears = {
0.24f, 0.62f, 1f, 1f, 1.88f, 11.86f, 29.46f, 84.01f, 164.8f, 248f
};
private Color[] skyboxColors =
{
new Color(0.75f,0.78f,0.85f),
new Color(1.25f,0.85f,0.55f),
new Color(1f,1f,1f),
new Color(0.75f,0.85f,1.2f),
new Color(1.35f,0.55f,0.45f),
new Color(0.7f,1.15f,1.25f),
new Color(0.95f,0.65f,1.35f),
new Color(0.55f,1.25f,1.1f),
new Color(0.45f,0.65f,1.35f),
new Color(0.7f,0.7f,1.35f)
};
private float[] skyboxExposure =
{
0.7f,
0.8f,
0.9f,
0.95f,
1.15f,
0.9f,
0.8f,
0.7f,
0.6f,
0.9f
};
private float[] skyboxRotation =
{
10f, 25f, 0f, 5f, 15f,
40f, 60f, 90f, 120f, 160f
};
void Start()
{
ApplyPlanet();
}
void Update()
{
if (timeManager.showAge)
{
int earthAge = Mathf.RoundToInt(agingController.currentAge);
ageText.text = "³ê: " + earthAge + " ³ç 70 ðîê³â";
float planetAge = timeManager.fromBeginning
? agingController.currentAge / orbitalYears[currentPlanetIndex]
: (agingController.currentAge - 25f) / orbitalYears[currentPlanetIndex];
planetText.text = "Íà " + planetNames[currentPlanetIndex] + " ïðîéøëî: " + planetAge.ToString("F1") + " ðîê³â";
}
else
{
ageText.text = "Òðèâàë³ñòü äîáè íà " + planetNames[currentPlanetIndex] + ": " + dayLengthHours[currentPlanetIndex].ToString("F1") + " ãîäèí";
planetText.text = "Ó ðîö³ íà " + planetNames[currentPlanetIndex] + ": " + daysInYear[currentPlanetIndex].ToString("F0") + " ä³á";
}
}
public void OnNextButton()
{
if (currentPlanetIndex < planetNames.Length - 1)
{
currentPlanetIndex++;
ApplyPlanet();
}
}
public void OnPreviousButton()
{
if (currentPlanetIndex > 0)
{
currentPlanetIndex--;
ApplyPlanet();
}
}
void ApplyPlanet()
{
agingController.agingDuration = agingDurations[currentPlanetIndex];
agingController.RestartAging();
agingController.ApplyHairColor(currentPlanetIndex);
skyboxMaterial.SetColor("_Tint", skyboxColors[currentPlanetIndex]);
skyboxMaterial.SetFloat("_Exposure", skyboxExposure[currentPlanetIndex]);
skyboxMaterial.SetFloat("_Rotation", skyboxRotation[currentPlanetIndex]);
DynamicGI.UpdateEnvironment();
previousButton.interactable = currentPlanetIndex > 0;
nextButton.interactable = currentPlanetIndex < planetNames.Length - 1;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a51db86c8a5cf5049b54adefdf69354f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,19 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TimeManager : MonoBehaviour
{
public bool showAge = true;
public bool fromBeginning = false;
public void ToggleMode(bool value)
{
showAge = value;
}
public void ToggleFromBeginning(bool value)
{
fromBeginning = value;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6d5f0adf8bd0cb840bc2cdd81739a4fc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 403c540f6d109ba4aa01773a8c9a1979
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,85 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CountryData : MonoBehaviour
{
public string countryName = "Unknown";
public string countryNameUkr;
public float utcOffset = 0f;
public string utcLabel = "UTC+0";
public bool hasAnomaly = false;
public string anomalyFact = "";
public Vector3 centerOnGlobe;
[Header("DST")]
public float baseUtcOffset = 0f;
public string baseUtcLabel = "UTC+0";
public float dstUtcOffset = 0f;
public string dstUtcLabel = "UTC+0";
public int dstStartMonth = 0;
public int dstEndMonth = 0;
private Material _mat;
private static readonly int PropBase = Shader.PropertyToID("_BaseColor");
private static readonly int PropHover = Shader.PropertyToID("_HoverStrength");
private static readonly int PropSelect = Shader.PropertyToID("_SelectStrength");
public void Init(Material mat) { _mat = mat; }
public void SetDefaultColor(Color color)
{
if (_mat != null) _mat.SetColor(PropBase, color);
}
public void OnHoverEnter()
{
if (_mat != null) _mat.SetFloat(PropHover, 0.35f);
}
public void OnHoverExit()
{
if (_mat != null) _mat.SetFloat(PropHover, 0f);
}
public void Select()
{
if (_mat != null)
{
_mat.SetFloat(PropHover, 0f);
_mat.SetFloat(PropSelect, 1f);
}
}
public void Deselect()
{
if (_mat != null)
{
_mat.SetFloat(PropHover, 0f);
_mat.SetFloat(PropSelect, 0f);
}
}
public void RefreshDST()
{
if (dstStartMonth == 0) return;
int month = System.DateTime.Now.Month;
bool isDST;
if (dstStartMonth > dstEndMonth)
isDST = (month >= dstStartMonth || month <= dstEndMonth);
else
isDST = (month >= dstStartMonth && month <= dstEndMonth);
utcOffset = isDST ? dstUtcOffset : baseUtcOffset;
utcLabel = isDST ? dstUtcLabel : baseUtcLabel;
}
public string GetCurrentTime()
{
System.TimeSpan offset = System.TimeSpan.FromMinutes(utcOffset * 60.0);
System.DateTime local = System.DateTime.UtcNow + offset;
return local.ToString("dd MMMM, HH:mm", new System.Globalization.CultureInfo("uk-UA"));
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b759dcae01e29714496ed7cb7cd68bb4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,294 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
public class CountrySelector : MonoBehaviour
{
[Header("UI")]
public TextMeshProUGUI countryNameText;
public TextMeshProUGUI utcText;
public TextMeshProUGUI timeText;
public TextMeshProUGUI anomalyText;
[Header("References")]
public PlaneController planeController;
public GlobeBuilder globeBuilder;
public string startCountryName = "Ukraine";
private CountryData _selected;
private CountryData _hovered;
private CountryData _startCountry;
private Camera _cam;
private float _lastClickTime;
private CountryData _lastClickedCountry;
private const float DoubleClickTime = 0.35f;
private bool _startMessageShown = false;
private Coroutine _hideCoroutine;
void Start()
{
_cam = Camera.main;
HideAll();
CountryData[] all = FindObjectsOfType<CountryData>();
foreach (CountryData c in all)
{
if (c.countryName == startCountryName)
{
_startCountry = c;
break;
}
}
if (_startCountry == null && all.Length > 0)
_startCountry = all[0];
if (!_startMessageShown)
{
_startMessageShown = true;
StartCoroutine(ShowStartMessage());
}
}
string GetDisplayName(CountryData country)
{
return !string.IsNullOrEmpty(country.countryNameUkr)
? country.countryNameUkr
: country.countryName;
}
IEnumerator ShowStartMessage()
{
if (countryNameText != null)
{
countryNameText.gameObject.SetActive(true);
utcText.gameObject.SetActive(true);
timeText.gameObject.SetActive(true);
countryNameText.text = "Ïî÷èíàºìî íàø ïîë³ò ç Óêðà¿íè!";
utcText.text = "UTC+2";
timeText.text = "Çàðàç: " + System.DateTime.UtcNow.AddHours(2).ToString("HH:mm");
yield return new WaitForSeconds(2f);
float t = 0f;
while (t < 1f)
{
t += Time.deltaTime;
float alpha = 1f - t;
countryNameText.canvasRenderer.SetAlpha(alpha);
utcText.canvasRenderer.SetAlpha(alpha);
timeText.canvasRenderer.SetAlpha(alpha);
yield return null;
}
HideAll();
ResetAlpha();
}
}
void Update()
{
if (planeController == null || !planeController.IsFlying())
{
HandleHover();
HandleClick();
HandleHotkeys();
}
}
void HandleHotkeys()
{
if (Input.GetKeyDown(KeyCode.Alpha1))
FlyToByName("American Samoa");
if (Input.GetKeyDown(KeyCode.Alpha2))
FlyToByName("Kiribati");
}
void FlyToByName(string name)
{
CountryData[] all = FindObjectsOfType<CountryData>();
foreach (CountryData c in all)
{
if (c.countryName == name)
{
FlyTo(c);
return;
}
}
}
void HandleHover()
{
Ray ray = _cam.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out RaycastHit hit))
{
CountryData c = hit.collider.GetComponent<CountryData>()
?? hit.collider.GetComponentInParent<CountryData>();
if (c != _hovered)
{
if (_hovered != null && _hovered != _selected) _hovered.OnHoverExit();
_hovered = c;
if (_hovered != null && _hovered != _selected) _hovered.OnHoverEnter();
}
}
else
{
if (_hovered != null && _hovered != _selected) _hovered.OnHoverExit();
_hovered = null;
}
}
void HandleClick()
{
if (!Input.GetMouseButtonDown(0)) return;
if (UnityEngine.EventSystems.EventSystem.current != null &&
UnityEngine.EventSystems.EventSystem.current.IsPointerOverGameObject()) return;
Ray ray = _cam.ScreenPointToRay(Input.mousePosition);
if (!Physics.Raycast(ray, out RaycastHit hit)) { DeselectAll(); return; }
CountryData clicked = hit.collider.GetComponent<CountryData>()
?? hit.collider.GetComponentInParent<CountryData>();
if (clicked == null) { DeselectAll(); return; }
float timeSinceLastClick = Time.time - _lastClickTime;
bool isDoubleClick = timeSinceLastClick < DoubleClickTime && _lastClickedCountry == clicked;
_lastClickTime = Time.time;
_lastClickedCountry = clicked;
if (isDoubleClick)
FlyTo(clicked);
else
SingleClick(clicked);
}
void SingleClick(CountryData country)
{
if (_hideCoroutine != null) StopCoroutine(_hideCoroutine);
ResetAlpha();
if (_selected != null && _selected != country) _selected.Deselect();
_selected = country;
_selected.Select();
if (globeBuilder != null) globeBuilder.SetRotation(false);
if (country == _startCountry)
ShowFullInfo(country);
else
ShowBasicInfo(country);
}
void ShowBasicInfo(CountryData country)
{
HideAll();
if (countryNameText != null)
{
countryNameText.gameObject.SetActive(true);
countryNameText.text = GetDisplayName(country);
}
if (utcText != null)
{
utcText.gameObject.SetActive(true);
utcText.text = country.utcLabel;
}
}
void ShowFullInfo(CountryData country)
{
if (_hideCoroutine != null) StopCoroutine(_hideCoroutine);
ResetAlpha();
HideAll();
if (countryNameText != null)
{
countryNameText.gameObject.SetActive(true);
countryNameText.text = GetDisplayName(country);
}
if (utcText != null)
{
utcText.gameObject.SetActive(true);
utcText.text = country.utcLabel;
}
if (timeText != null)
{
timeText.gameObject.SetActive(true);
timeText.text = "Çàðàç: " + country.GetCurrentTime();
}
if (anomalyText != null)
{
anomalyText.gameObject.SetActive(country.hasAnomaly);
if (country.hasAnomaly)
anomalyText.text = country.anomalyFact;
}
_hideCoroutine = StartCoroutine(AutoHide());
}
IEnumerator AutoHide()
{
yield return new WaitForSeconds(5f);
float t = 0f;
while (t < 1f)
{
t += Time.deltaTime;
float alpha = 1f - t;
if (countryNameText != null) countryNameText.canvasRenderer.SetAlpha(alpha);
if (utcText != null) utcText.canvasRenderer.SetAlpha(alpha);
if (timeText != null) timeText.canvasRenderer.SetAlpha(alpha);
if (anomalyText != null) anomalyText.canvasRenderer.SetAlpha(alpha);
yield return null;
}
HideAll();
ResetAlpha();
}
void FlyTo(CountryData destination)
{
if (planeController == null || _startCountry == null) return;
if (destination == _startCountry) return;
if (_hideCoroutine != null) StopCoroutine(_hideCoroutine);
HideAll();
ResetAlpha();
if (_selected != null) { _selected.Deselect(); _selected = null; }
if (globeBuilder != null) globeBuilder.SetRotation(false);
planeController.Fly(
_startCountry,
destination,
() =>
{
_startCountry = destination;
ShowFullInfo(destination);
}
);
}
void DeselectAll()
{
if (_hideCoroutine != null) StopCoroutine(_hideCoroutine);
if (_selected != null) { _selected.Deselect(); _selected = null; }
HideAll();
ResetAlpha();
if (globeBuilder != null) globeBuilder.SetRotation(true);
}
void HideAll()
{
if (countryNameText != null) countryNameText.gameObject.SetActive(false);
if (utcText != null) utcText.gameObject.SetActive(false);
if (timeText != null) timeText.gameObject.SetActive(false);
if (anomalyText != null) anomalyText.gameObject.SetActive(false);
}
void ResetAlpha()
{
if (countryNameText != null) countryNameText.canvasRenderer.SetAlpha(1f);
if (utcText != null) utcText.canvasRenderer.SetAlpha(1f);
if (timeText != null) timeText.canvasRenderer.SetAlpha(1f);
if (anomalyText != null) anomalyText.canvasRenderer.SetAlpha(1f);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0fee30d2d4a34434b81c3ec6fe81d531
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,38 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DSTManager : MonoBehaviour
{
public GameObject globeObject;
private float _timer;
private const float INTERVAL = 3600f;
void Start()
{
RefreshAll();
_timer = INTERVAL;
}
void Update()
{
_timer -= Time.deltaTime;
if (_timer <= 0f)
{
RefreshAll();
_timer = INTERVAL;
}
}
void RefreshAll()
{
if (globeObject == null) return;
foreach (Transform child in globeObject.transform)
{
if (child.gameObject.name == "Ocean") continue;
CountryData cd = child.GetComponent<CountryData>();
if (cd != null) cd.RefreshDST();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d5ed4cbb71b7fe14ebf089cde731e793
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,77 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GlobeBuilder : MonoBehaviour
{
public float globeRadius = 5f;
public float rotationSpeed = 3f;
public bool autoRotate = true;
public Material countryMaterial;
private static readonly Color[] Palette = new Color[]
{
new Color(0.98f, 0.85f, 0.65f),
new Color(0.65f, 0.85f, 0.72f),
new Color(0.72f, 0.82f, 0.95f),
new Color(0.95f, 0.78f, 0.72f),
new Color(0.88f, 0.95f, 0.68f),
new Color(0.95f, 0.90f, 0.65f),
new Color(0.78f, 0.88f, 0.95f),
new Color(0.95f, 0.72f, 0.85f),
new Color(0.72f, 0.95f, 0.88f),
new Color(0.90f, 0.80f, 0.95f),
new Color(0.95f, 0.88f, 0.78f),
new Color(0.75f, 0.92f, 0.75f),
};
void Awake()
{
SetupCountries();
}
void Update()
{
if (autoRotate)
transform.Rotate(Vector3.up, rotationSpeed * Time.deltaTime);
}
void SetupCountries()
{
CountryData[] countries = GetComponentsInChildren<CountryData>();
for (int i = 0; i < countries.Length; i++)
{
CountryData country = countries[i];
Renderer rend = country.GetComponent<Renderer>();
if (rend == null) rend = country.GetComponentInChildren<Renderer>();
if (rend == null) continue;
Material mat = countryMaterial != null
? new Material(countryMaterial)
: new Material(rend.sharedMaterial);
rend.material = mat;
country.Init(mat);
Color baseColor = Palette[i % Palette.Length];
if (country.hasAnomaly)
baseColor = Color.Lerp(baseColor, new Color(1f, 0.5f, 0.3f), 0.3f);
country.SetDefaultColor(baseColor);
MeshFilter mf = country.GetComponent<MeshFilter>();
if (mf == null) mf = country.GetComponentInChildren<MeshFilter>();
if (mf != null && mf.sharedMesh != null)
{
Vector3 worldCenter = country.transform.TransformPoint(mf.sharedMesh.bounds.center);
Vector3 localCenter = transform.InverseTransformPoint(worldCenter);
country.centerOnGlobe = localCenter.normalized * globeRadius;
}
else
{
Vector3 localPos = transform.InverseTransformPoint(country.transform.position);
country.centerOnGlobe = localPos.normalized * globeRadius;
}
}
}
public void SetRotation(bool enabled) => autoRotate = enabled;
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a84917a6b77810746883628c2f7e6dfa
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,62 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GlobeRotator : MonoBehaviour
{
public float rotationSpeed = 200f;
public float zoomSpeed = 2f;
public float minZoom = 5f;
public float maxZoom = 25f;
public float smoothing = 8f;
private Vector3 _lastMousePos;
private bool _isDragging;
private Camera _cam;
private float _deltaX;
private float _deltaY;
void Start()
{
_cam = Camera.main;
}
void Update()
{
if (Input.GetMouseButtonDown(1))
{
_lastMousePos = Input.mousePosition;
_isDragging = true;
}
if (Input.GetMouseButtonUp(1))
_isDragging = false;
float targetX = 0f;
float targetY = 0f;
if (_isDragging)
{
Vector3 delta = Input.mousePosition - _lastMousePos;
targetX = -delta.x * rotationSpeed * Time.deltaTime;
targetY = delta.y * rotationSpeed * Time.deltaTime;
_lastMousePos = Input.mousePosition;
}
_deltaX = Mathf.Lerp(_deltaX, targetX, Time.deltaTime * smoothing);
_deltaY = Mathf.Lerp(_deltaY, targetY, Time.deltaTime * smoothing);
transform.Rotate(Vector3.up, _deltaX, Space.World);
Vector3 rightAxis = _cam.transform.right;
transform.Rotate(rightAxis, _deltaY, Space.World);
float scroll = Input.GetAxis("Mouse ScrollWheel");
if (scroll != 0f && _cam != null)
{
float dist = Vector3.Distance(_cam.transform.position, transform.position);
dist = Mathf.Clamp(dist - scroll * zoomSpeed, minZoom, maxZoom);
_cam.transform.position = transform.position - _cam.transform.forward * dist;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3ce1bde26601ec34097dc616cb361d11
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,15 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GlobeStartOrientation : MonoBehaviour
{
public float rotationX = 0f;
public float rotationY = 0f;
public float rotationZ = 0f;
void Awake()
{
transform.rotation = Quaternion.Euler(rotationX, rotationY, rotationZ);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1ae5fee76ecb9594199e9e1247f33970
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,191 @@
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;
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4b4d4da9516068841af91f9f45d9a1c1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,18 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DroneMovement : MonoBehaviour
{
public float speed = 20f;
private bool _moving = true;
public void StopMoving() { _moving = false; }
public void ResumeMoving() { _moving = true; }
void Update()
{
if (!_moving) return;
transform.position += Vector3.forward * speed * Time.deltaTime;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 921562a23f10f764d804dca0f461fe90
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,63 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DroneTrigger : MonoBehaviour
{
public LightWave wave1;
public LightWave wave2;
public DroneMovement droneMovement;
public float droneSpeed = 40f;
public float davidSpeed = 10f;
public float davidSimDelay = 0.5f;
private bool _fired = false;
public void Reset() { _fired = false; }
void Update()
{
if (_fired) return;
if (transform.position.z >= 0f)
{
_fired = true;
droneMovement.StopMoving();
bool isSimulation = GameManager.Instance != null && GameManager.Instance.isSimulation;
bool isView2 = GameManager.Instance != null && GameManager.Instance.isView2;
if (isView2)
StartCoroutine(LaunchView2());
else if (isSimulation)
StartCoroutine(LaunchSim());
else
StartCoroutine(LaunchReal());
}
}
IEnumerator LaunchSim()
{
float delay = davidSimDelay / (droneSpeed / davidSpeed);
wave1.StartWave();
wave1.SetDroneText(true, isSimulation: true, simDelay: delay);
yield return new WaitForSeconds(delay);
wave2.StartWave();
wave2.SetDroneText(false, isSimulation: true, simDelay: delay);
}
IEnumerator LaunchReal()
{
wave1.StartWave();
wave1.SetDroneText(true, isSimulation: false, simDelay: 0);
wave2.StartWave();
wave2.SetDroneText(false, isSimulation: false, simDelay: 0);
yield return null;
}
IEnumerator LaunchView2()
{
wave1.StartWave();
wave2.StartWave();
wave1.showBothTextsAtOnce = true;
wave1.wave2ref = wave2;
wave1.ShowTextForBoth();
yield return new WaitForSeconds(0.1f);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c0857b8d92647f6488cca6fef417806d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,146 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GameManager : MonoBehaviour
{
public static GameManager Instance { get; private set; }
[Header("Îáåðè òèï ñèìóëÿö³¿")]
public bool isSimulation = true;
[Header("Âèä")]
public bool isView2 = false;
[Header("Ãåðîé")]
public bool useDrone = false;
[Header("Êàìåðè")]
public Camera davidCamera;
public Camera droneCamera;
public Camera droneInnerCamera;
public Camera mainCamera;
[Header("Îá'ºêòè")]
public GameObject david;
public GameObject drone;
[HideInInspector] public double simV = 2000.0;
[HideInInspector] public double simVDrone = 8000.0;
[HideInInspector] public double view2Jitter = 0.0;
private bool _lastIsView2;
private bool _lastUseDrone;
private Vector3 _davidStartPos;
private Vector3 _droneStartPos;
void Awake()
{
if (Instance == null) Instance = this;
else Destroy(gameObject);
GenerateSimV();
}
public void GenerateSimV()
{
simV = 500.0 + Random.Range(0f, 1000f);
simVDrone = 2000.0 + Random.Range(0f, 2000f);
view2Jitter = Random.Range(-5.0f, 5.0f);
}
void Start()
{
if (david != null) _davidStartPos = david.transform.position;
if (drone != null) _droneStartPos = drone.transform.position;
_lastIsView2 = isView2;
_lastUseDrone = useDrone;
ApplySettings();
}
void Update()
{
if (_lastIsView2 != isView2 || _lastUseDrone != useDrone)
{
_lastIsView2 = isView2;
_lastUseDrone = useDrone;
ResetAll();
}
}
void ResetAll()
{
GenerateSimV();
FindObjectOfType<HeroTrigger>()?.Reset();
FindObjectOfType<DroneTrigger>()?.Reset();
FindObjectOfType<RestartManager>()?.HideRestart();
FindObjectOfType<RewindManager>()?.FullReset();
LightWave[] waves = FindObjectsOfType<LightWave>(true);
foreach (var wave in waves)
{
wave.ResetSize();
wave.ResetText();
wave.gameObject.SetActive(false);
}
if (david != null) david.transform.position = _davidStartPos;
if (drone != null) drone.transform.position = _droneStartPos;
ApplySettings();
if (david != null && david.activeInHierarchy)
{
var hm = david.GetComponentInChildren<HeroMovement>();
if (hm != null) hm.ResumeMoving();
var anim = david.GetComponent<Animator>();
if (anim != null)
{
anim.Rebind();
anim.Play("FastRun", 0, 0f);
anim.Update(0f);
}
}
if (drone != null && drone.activeInHierarchy)
{
var dm = drone.GetComponentInChildren<DroneMovement>();
if (dm != null) dm.ResumeMoving();
}
}
#if UNITY_EDITOR
void OnValidate()
{
UnityEditor.EditorApplication.delayCall += () =>
{
if (this == null) return;
ApplySettings();
};
}
#endif
void ApplySettings()
{
if (david != null) david.SetActive(!useDrone);
if (drone != null) drone.SetActive(useDrone);
if (isView2)
{
if (mainCamera != null) mainCamera.enabled = true;
if (davidCamera != null) davidCamera.enabled = false;
if (droneCamera != null) droneCamera.enabled = false;
if (droneInnerCamera != null) droneInnerCamera.enabled = false;
}
else
{
if (mainCamera != null) mainCamera.enabled = false;
if (useDrone)
{
if (droneCamera != null) droneCamera.enabled = true;
if (droneInnerCamera != null) droneInnerCamera.enabled = true;
if (davidCamera != null) davidCamera.enabled = false;
}
else
{
if (davidCamera != null) davidCamera.enabled = true;
if (droneCamera != null) droneCamera.enabled = false;
if (droneInnerCamera != null) droneInnerCamera.enabled = false;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3968d894f5db4c942a4aba5d0e358007
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,18 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class HeroMovement : MonoBehaviour
{
public float speed = 5f;
private bool _moving = true;
public void StopMoving() { _moving = false; }
public void ResumeMoving() { _moving = true; }
void Update()
{
if (!_moving) return;
transform.position += Vector3.forward * speed * Time.deltaTime;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ec8eebfd822edc04da976040b29c5285
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,59 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class HeroTrigger : MonoBehaviour
{
public LightWave wave1;
public LightWave wave2;
public Animator animator;
public HeroMovement heroMovement;
private bool _fired = false;
public void Reset() { _fired = false; }
void Update()
{
if (_fired) return;
if (transform.position.z >= 0f)
{
_fired = true;
heroMovement.StopMoving();
bool isView2 = GameManager.Instance != null && GameManager.Instance.isView2;
bool isSimulation = GameManager.Instance != null && GameManager.Instance.isSimulation;
if (isView2)
StartCoroutine(LaunchView2());
else
StartCoroutine(LaunchView1(isSimulation));
}
}
IEnumerator LaunchView1(bool isSimulation)
{
if (isSimulation)
{
wave1.StartWave();
yield return new WaitForSeconds(0.5f);
wave2.StartWave();
yield return new WaitForSeconds(1f);
}
else
{
wave1.StartWave();
wave2.StartWave();
yield return new WaitForSeconds(1f);
}
animator.SetTrigger("Fall");
}
IEnumerator LaunchView2()
{
wave1.StartWave();
wave2.StartWave();
wave1.showBothTextsAtOnce = true;
wave1.wave2ref = wave2;
wave1.ShowTextForBoth();
yield return new WaitForSeconds(0.1f);
animator.SetTrigger("Fall");
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c97a008d970294a4bb82a052d33dd0cc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

204
Assets/Scripts/LightWave.cs Normal file
View File

@@ -0,0 +1,204 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
public class LightWave : MonoBehaviour
{
public float growSpeed = 2f;
public float maxMultiplier = 9f;
public bool isFront = false;
public Camera heroCamera;
public TextMeshProUGUI waveText;
[HideInInspector] public bool showBothTextsAtOnce = false;
[HideInInspector] public LightWave wave2ref;
[HideInInspector] public float _eventTime;
[HideInInspector] public bool _textSet = false;
private bool _running = false;
private float _sizeMultiplier = 1f;
private ParticleSystem _ps;
private float _baseStartSize;
private const double SpeedOfLight = 299792458.0;
private const double HeroSpeed = 10.0;
private const double DroneSpeed = 40.0;
private const double DistFront = 9.99;
private const double DistBack = 9.57;
private const double DistObserver = 20.0;
void Awake()
{
_ps = GetComponent<ParticleSystem>();
var main = _ps.main;
_baseStartSize = main.startSizeMultiplier;
_ps.Stop();
gameObject.SetActive(false);
if (waveText != null)
waveText.gameObject.SetActive(false);
}
public void StartWave()
{
gameObject.SetActive(true);
_ps.Play();
_running = true;
_eventTime = Time.time;
_sizeMultiplier = 1f;
_textSet = false;
showBothTextsAtOnce = false;
wave2ref = null;
bool isDrone = GameManager.Instance != null && GameManager.Instance.useDrone;
if (isDrone)
{
var main = _ps.main;
main.startSizeMultiplier = _baseStartSize;
return;
}
bool isView2 = GameManager.Instance != null && GameManager.Instance.isView2;
if (!isView2)
ShowText(this);
}
public void ShowTextForBoth()
{
ShowText(this);
if (wave2ref != null)
ShowText(wave2ref);
}
void Update()
{
if (!_running) return;
bool isView2 = GameManager.Instance != null && GameManager.Instance.isView2;
bool isDrone = GameManager.Instance != null && GameManager.Instance.useDrone;
if (!isFront && !isView2 && !isDrone)
{
_sizeMultiplier += growSpeed * Time.deltaTime;
_sizeMultiplier = Mathf.Min(_sizeMultiplier, maxMultiplier);
var main = _ps.main;
main.startSizeMultiplier = _baseStartSize * _sizeMultiplier;
}
}
void ShowText(LightWave wave)
{
if (wave.waveText == null) return;
bool isSimulation = GameManager.Instance != null && GameManager.Instance.isSimulation;
bool isView2 = GameManager.Instance != null && GameManager.Instance.isView2;
string label = wave.isFront ? "Õâèëþ 1" : "Õâèëþ 2";
double jitter = isView2 && GameManager.Instance != null ? GameManager.Instance.view2Jitter : 0.0;
if (isSimulation)
{
double simC = 5000.0;
double simV = GameManager.Instance != null ? GameManager.Instance.simV : 2000.0;
double dist = isView2 ? (DistObserver + jitter) : (wave.isFront ? DistFront : DistBack);
double relSpeed = isView2 ? simC : (wave.isFront ? (simC + simV) : (simC - simV));
double timeS = dist / relSpeed;
wave.waveText.text = $"Çàô³êñóâàëè {label} ÷åðåç {timeS:F5} ñ";
}
else
{
double dist = isView2 ? (DistObserver + jitter) : (wave.isFront ? DistFront : DistBack);
double relSpeed = isView2 ? SpeedOfLight : (wave.isFront ? (SpeedOfLight + HeroSpeed) : (SpeedOfLight - HeroSpeed));
double timeNs = (dist / relSpeed) * 1e9;
wave.waveText.text = $"Çàô³êñóâàëè {label} ÷åðåç {timeNs:F2} íñ";
}
wave.waveText.gameObject.SetActive(true);
wave._textSet = true;
if (!wave.isFront)
{
RestartManager rm = FindObjectOfType<RestartManager>();
if (rm != null)
rm.ShowRestart();
}
}
public void SetDroneText(bool front, bool isSimulation, float simDelay)
{
if (waveText == null) return;
bool isView2 = GameManager.Instance != null && GameManager.Instance.isView2;
string label = front ? "Õâèëþ 1" : "Õâèëþ 2";
double droneJitter = isView2 && GameManager.Instance != null ? GameManager.Instance.view2Jitter : 0.0;
if (isSimulation)
{
double simC = 5000.0;
double simV = GameManager.Instance != null ? GameManager.Instance.simVDrone : 3000.0;
double dist = isView2 ? (DistObserver + droneJitter) : (front ? DistFront : DistBack);
double relSpeed = isView2 ? simC : (front ? (simC + simV) : (simC - simV));
double timeS = dist / relSpeed;
waveText.text = $"Çàô³êñóâàëè {label} ÷åðåç {timeS:F5} ñ";
}
else
{
double dist = isView2 ? (DistObserver + droneJitter) : (front ? DistFront : DistBack);
double relSpeed;
if (isView2)
relSpeed = SpeedOfLight;
else
relSpeed = front ? (SpeedOfLight + DroneSpeed) : (SpeedOfLight - DroneSpeed);
double timeNs = (dist / relSpeed) * 1e9;
waveText.text = $"Çàô³êñóâàëè {label} ÷åðåç {timeNs:F2} íñ";
}
waveText.gameObject.SetActive(true);
_textSet = true;
if (!front)
{
RestartManager rm = FindObjectOfType<RestartManager>();
if (rm != null)
rm.ShowRestart();
}
}
bool IsAnyPartVisible(Camera cam, Vector3 center, float radius)
{
Vector3[] points =
{
center + Vector3.forward * radius,
center + Vector3.back * radius,
center + Vector3.left * radius,
center + Vector3.right * radius,
center + Vector3.up * radius,
center + Vector3.down * radius
};
foreach (var p in points)
{
Vector3 v = cam.WorldToViewportPoint(p);
if (v.z > 0 && v.x >= 0 && v.x <= 1 && v.y >= 0 && v.y <= 1)
return true;
}
return false;
}
public void ResetText()
{
_textSet = false;
if (waveText != null)
waveText.gameObject.SetActive(false);
}
public void ResetSize()
{
_sizeMultiplier = 1f;
var main = _ps.main;
main.startSizeMultiplier = _baseStartSize;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b2fac9f18fd56554c9199b7c7077c033
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,33 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class RestartManager : MonoBehaviour
{
public GameObject restartButton;
void Start()
{
if (restartButton != null)
restartButton.SetActive(false);
}
public void ShowRestart()
{
FindObjectOfType<RewindManager>()?.ShowSlider();
if (restartButton != null)
restartButton.SetActive(true);
}
public void HideRestart()
{
if (restartButton != null)
restartButton.SetActive(false);
}
public void RestartSimulation()
{
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9cf39e3294a054840b308b54cd2ce697
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,228 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class RewindManager : MonoBehaviour
{
[Header("Îá'ºêòè ñöåíè")]
public GameObject david;
public GameObject drone;
public ParticleSystem wave1PS;
public ParticleSystem wave2PS;
[Header("UI")]
public Slider rewindSlider;
private bool _recording = false;
private bool _rewinding = false;
private bool _animating = false;
private Vector3 _davidStartPos;
private Vector3 _droneStartPos;
private Vector3 _davidFinalPos;
private Vector3 _droneFinalPos;
private HeroMovement _heroMovement;
private DroneMovement _droneMovement;
private Animator _davidAnimator;
private int _stopFrameIndex = -1;
private struct FrameState
{
public float wave1Time;
public float wave2Time;
public bool wave1Active;
public bool wave2Active;
public int animStateHash;
public float animNormalizedTime;
}
private List<FrameState> _frames = new List<FrameState>();
void Awake()
{
if (david != null)
{
_davidStartPos = david.transform.position;
_heroMovement = david.GetComponentInChildren<HeroMovement>();
_davidAnimator = david.GetComponent<Animator>();
}
if (drone != null)
{
_droneStartPos = drone.transform.position;
_droneMovement = drone.GetComponentInChildren<DroneMovement>();
}
if (rewindSlider != null)
{
rewindSlider.gameObject.SetActive(false);
rewindSlider.onValueChanged.AddListener(OnSliderChanged);
}
_recording = true;
}
public void ShowSlider()
{
_recording = false;
_stopFrameIndex = _frames.Count - 1;
if (david != null) _davidFinalPos = david.transform.position;
if (drone != null) _droneFinalPos = drone.transform.position;
if (rewindSlider != null)
{
rewindSlider.maxValue = _frames.Count - 1;
rewindSlider.onValueChanged.RemoveListener(OnSliderChanged);
rewindSlider.value = _frames.Count - 1;
rewindSlider.onValueChanged.AddListener(OnSliderChanged);
rewindSlider.gameObject.SetActive(true);
}
}
void Update()
{
if (!_recording || _rewinding) return;
FrameState frame = new FrameState();
frame.wave1Active = wave1PS != null && wave1PS.gameObject.activeSelf;
frame.wave2Active = wave2PS != null && wave2PS.gameObject.activeSelf;
frame.wave1Time = frame.wave1Active ? wave1PS.time : 0f;
frame.wave2Time = frame.wave2Active ? wave2PS.time : 0f;
if (_davidAnimator != null && david != null && david.activeInHierarchy)
{
AnimatorStateInfo info = _davidAnimator.GetCurrentAnimatorStateInfo(0);
frame.animStateHash = info.fullPathHash;
frame.animNormalizedTime = info.normalizedTime;
}
_frames.Add(frame);
}
void OnSliderChanged(float value)
{
if (_animating) return;
if (_frames.Count == 0) return;
_rewinding = true;
Time.timeScale = 0f;
int index = Mathf.RoundToInt(value);
index = Mathf.Clamp(index, 0, _frames.Count - 1);
float t = _frames.Count > 1 ? (float)index / (_frames.Count - 1) : 1f;
if (david != null) david.transform.position = Vector3.Lerp(_davidStartPos, _davidFinalPos, t);
if (drone != null) drone.transform.position = Vector3.Lerp(_droneStartPos, _droneFinalPos, t);
FrameState frame = _frames[index];
if (_davidAnimator != null && frame.animStateHash != 0 && david != null && david.activeInHierarchy)
{
_davidAnimator.Play(frame.animStateHash, 0, frame.animNormalizedTime);
_davidAnimator.Update(0f);
}
if (wave1PS != null && frame.wave1Active)
{
wave1PS.gameObject.SetActive(true);
wave1PS.Stop(true, ParticleSystemStopBehavior.StopEmittingAndClear);
wave1PS.Simulate(frame.wave1Time, true, true);
}
if (wave2PS != null && frame.wave2Active)
{
wave2PS.gameObject.SetActive(true);
wave2PS.Stop(true, ParticleSystemStopBehavior.StopEmittingAndClear);
wave2PS.Simulate(frame.wave2Time, true, true);
}
}
public void OnSliderReleased(float savedValue)
{
if (_animating) return;
_animating = true;
_rewinding = false;
Time.timeScale = 1f;
int index = Mathf.RoundToInt(savedValue);
index = Mathf.Clamp(index, 0, _frames.Count - 1);
float animateTo = rewindSlider.maxValue;
if (_stopFrameIndex >= 0 && index < _stopFrameIndex)
{
GameManager.Instance?.GenerateSimV();
if (_heroMovement != null) _heroMovement.ResumeMoving();
if (_droneMovement != null) _droneMovement.ResumeMoving();
FindObjectOfType<DroneTrigger>()?.Reset();
FindObjectOfType<HeroTrigger>()?.Reset();
FindObjectOfType<RestartManager>()?.HideRestart();
_stopFrameIndex = -1;
if (_davidAnimator != null && david != null && david.activeInHierarchy)
{
_davidAnimator.Rebind();
_davidAnimator.Play("FastRun", 0, 0f);
_davidAnimator.Update(0f);
}
}
if (wave1PS != null && !_frames[index].wave1Active)
{
wave1PS.Stop();
wave1PS.gameObject.SetActive(false);
wave1PS.GetComponent<LightWave>()?.ResetText();
}
if (wave2PS != null && !_frames[index].wave2Active)
{
wave2PS.Stop();
wave2PS.gameObject.SetActive(false);
wave2PS.GetComponent<LightWave>()?.ResetText();
}
if (wave1PS != null && wave1PS.gameObject.activeSelf) wave1PS.Play();
if (wave2PS != null && wave2PS.gameObject.activeSelf) wave2PS.Play();
StartCoroutine(AnimateSlider(savedValue, animateTo));
}
public void FullReset()
{
StopAllCoroutines();
Time.timeScale = 1f;
_frames.Clear();
_stopFrameIndex = -1;
_rewinding = false;
_animating = false;
_recording = true;
if (rewindSlider != null)
{
rewindSlider.gameObject.SetActive(false);
rewindSlider.onValueChanged.RemoveListener(OnSliderChanged);
rewindSlider.value = 0f;
rewindSlider.onValueChanged.AddListener(OnSliderChanged);
}
}
private IEnumerator AnimateSlider(float from, float to)
{
rewindSlider.onValueChanged.RemoveListener(OnSliderChanged);
rewindSlider.maxValue = to;
float current = from;
while (current < to)
{
current += 60f * Time.unscaledDeltaTime;
current = Mathf.Min(current, to);
rewindSlider.value = current;
yield return null;
}
rewindSlider.onValueChanged.AddListener(OnSliderChanged);
_animating = false;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 50ca7b84aa023b442a5a2a0909282d07
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,28 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
public class SliderReleaseHandler : MonoBehaviour, IPointerUpHandler, IDragHandler
{
public RewindManager rewindManager;
private Slider _slider;
private float _lastDragValue;
void Awake()
{
_slider = GetComponent<Slider>();
_lastDragValue = _slider.value;
}
public void OnDrag(PointerEventData eventData)
{
_lastDragValue = _slider.value;
}
public void OnPointerUp(PointerEventData eventData)
{
rewindManager.OnSliderReleased(_lastDragValue);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fe4934f350ebb4945b6806938c59bf6e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: