first commit

This commit is contained in:
2026-04-07 03:14:32 +03:00
commit b3992fec6b
1026 changed files with 366769 additions and 0 deletions

View File

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

View File

@@ -0,0 +1,56 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AstronautAnimator : MonoBehaviour
{
private Animator _animator;
private float _ratio = 1f;
private float _massFactor = 1f;
private bool _isJumping;
void Start()
{
_animator = GetComponent<Animator>();
}
public void SetRatio(float ratio)
{
_ratio = ratio;
}
public void SetMass(float mass)
{
float clamped = Mathf.Clamp(mass, 30f, 100f);
float t = Mathf.InverseLerp(30f, 100f, clamped);
_massFactor = Mathf.Lerp(1.05f, 0.95f, t);
}
public void SetWalking(bool isWalking)
{
_animator.SetBool("isWalking", isWalking);
}
public void Jump()
{
_animator.SetTrigger("Jump");
_isJumping = true;
}
void Update()
{
bool isGrounded = Physics.CheckSphere(transform.parent.position + Vector3.down * 0.5f, 0.3f);
float animSpeed = (_ratio <= 0.2f)
? 1.5f
: Mathf.Clamp(1f / (_ratio * 1.5f), 0.15f, 3f);
if (isGrounded)
{
_isJumping = false;
_animator.speed = animSpeed * _massFactor;
}
else if (_isJumping)
{
_animator.speed = animSpeed * _massFactor;
}
}
}

View File

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

View File

@@ -0,0 +1,204 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
public class AstronautController : MonoBehaviour
{
[SerializeField] private float _moveSpeed = 3f;
[SerializeField] private float _jumpForce = 10f;
[SerializeField] private Planet _currentPlanet = Planet.Earth;
[SerializeField] private AstronautAnimator _astronautAnimator;
[SerializeField] private TextMeshProUGUI _planetText;
[SerializeField] private TextMeshProUGUI _gravityText;
public Planet CurrentPlanet => _currentPlanet;
public enum Planet
{
Mercury, Venus, Earth, Moon, Mars, Jupiter, Saturn, Uranus, Neptune, Sun
}
private Rigidbody _rb;
private bool _isGrounded;
private bool _isJumping;
private float _ratio = 1f;
float GetGravity(Planet planet)
{
switch (planet)
{
case Planet.Mercury: return 3.72f;
case Planet.Venus: return 8.87f;
case Planet.Earth: return 9.81f;
case Planet.Moon: return 1.62f;
case Planet.Mars: return 3.721f;
case Planet.Jupiter: return 24.79f;
case Planet.Saturn: return 10.44f;
case Planet.Uranus: return 8.69f;
case Planet.Neptune: return 11.15f;
case Planet.Sun: return 274f;
default: return 9.81f;
}
}
string GetPlanetNameUA(Planet planet)
{
switch (planet)
{
case Planet.Mercury: return "Меркурій";
case Planet.Venus: return "Венера";
case Planet.Earth: return "Земля";
case Planet.Moon: return "Місяць";
case Planet.Mars: return "Марс";
case Planet.Jupiter: return "Юпітер";
case Planet.Saturn: return "Сатурн";
case Planet.Uranus: return "Уран";
case Planet.Neptune: return "Нептун";
case Planet.Sun: return "Сонце";
default: return "";
}
}
float GetSpeedMultiplier()
{
if (_currentPlanet == Planet.Moon) return 1.5f;
return Mathf.Clamp(1f / (_ratio * 1.5f), 0.15f, 3f);
}
void ApplyGravity()
{
Physics.gravity = new Vector3(0, -GetGravity(_currentPlanet), 0);
_ratio = GetGravity(_currentPlanet) / 9.81f;
_astronautAnimator.SetRatio(_ratio);
if (_planetText != null)
_planetText.text = GetPlanetNameUA(_currentPlanet);
}
IEnumerator ShowGravityText()
{
_gravityText.text = "Прискорення падіння: " + GetGravity(_currentPlanet).ToString("F2") + " м/с²";
float t = 0f;
while (t < 1f)
{
t += Time.deltaTime * 2f;
_gravityText.alpha = t;
yield return null;
}
yield return new WaitForSeconds(4f);
t = 1f;
while (t > 0f)
{
t -= Time.deltaTime * 2f;
_gravityText.alpha = t;
yield return null;
}
}
float GetClampedMass()
{
return Mathf.Clamp(_rb.mass, 30f, 100f);
}
void Start()
{
_rb = GetComponent<Rigidbody>();
_rb.freezeRotation = true;
if (_gravityText != null)
_gravityText.alpha = 0f;
ApplyGravity();
_astronautAnimator.SetMass(GetClampedMass());
}
public void RefreshMass()
{
_astronautAnimator.SetMass(GetClampedMass());
}
void OnCollisionEnter(Collision collision)
{
if (collision.gameObject.CompareTag("Ground"))
{
_isGrounded = true;
_isJumping = false;
}
}
void OnCollisionExit(Collision collision)
{
if (collision.gameObject.CompareTag("Ground"))
{
_isGrounded = false;
}
}
void Update()
{
if (Input.GetKeyDown(KeyCode.Alpha1)) { _currentPlanet = Planet.Mercury; ApplyGravity(); }
if (Input.GetKeyDown(KeyCode.Alpha2)) { _currentPlanet = Planet.Venus; ApplyGravity(); }
if (Input.GetKeyDown(KeyCode.Alpha3)) { _currentPlanet = Planet.Earth; ApplyGravity(); }
if (Input.GetKeyDown(KeyCode.Alpha4)) { _currentPlanet = Planet.Moon; ApplyGravity(); }
if (Input.GetKeyDown(KeyCode.Alpha5)) { _currentPlanet = Planet.Mars; ApplyGravity(); }
if (Input.GetKeyDown(KeyCode.Alpha6)) { _currentPlanet = Planet.Jupiter; ApplyGravity(); }
if (Input.GetKeyDown(KeyCode.Alpha7)) { _currentPlanet = Planet.Saturn; ApplyGravity(); }
if (Input.GetKeyDown(KeyCode.Alpha8)) { _currentPlanet = Planet.Uranus; ApplyGravity(); }
if (Input.GetKeyDown(KeyCode.Alpha9)) { _currentPlanet = Planet.Neptune; ApplyGravity(); }
if (Input.GetKeyDown(KeyCode.Alpha0)) { _currentPlanet = Planet.Sun; ApplyGravity(); }
if (Input.GetKeyDown(KeyCode.Keypad1)) { _currentPlanet = Planet.Mercury; ApplyGravity(); }
if (Input.GetKeyDown(KeyCode.Keypad2)) { _currentPlanet = Planet.Venus; ApplyGravity(); }
if (Input.GetKeyDown(KeyCode.Keypad3)) { _currentPlanet = Planet.Earth; ApplyGravity(); }
if (Input.GetKeyDown(KeyCode.Keypad4)) { _currentPlanet = Planet.Moon; ApplyGravity(); }
if (Input.GetKeyDown(KeyCode.Keypad5)) { _currentPlanet = Planet.Mars; ApplyGravity(); }
if (Input.GetKeyDown(KeyCode.Keypad6)) { _currentPlanet = Planet.Jupiter; ApplyGravity(); }
if (Input.GetKeyDown(KeyCode.Keypad7)) { _currentPlanet = Planet.Saturn; ApplyGravity(); }
if (Input.GetKeyDown(KeyCode.Keypad8)) { _currentPlanet = Planet.Uranus; ApplyGravity(); }
if (Input.GetKeyDown(KeyCode.Keypad9)) { _currentPlanet = Planet.Neptune; ApplyGravity(); }
if (Input.GetKeyDown(KeyCode.Keypad0)) { _currentPlanet = Planet.Sun; ApplyGravity(); }
float currentSpeed = _moveSpeed * GetSpeedMultiplier();
float h = Input.GetAxis("Horizontal");
float v = Input.GetAxis("Vertical");
bool isWalking = Mathf.Abs(h) > 0.1f || Mathf.Abs(v) > 0.1f;
if (!_isJumping)
{
Vector3 move = new Vector3(-h, 0, -v).normalized * currentSpeed;
_rb.velocity = new Vector3(move.x, _rb.velocity.y, move.z);
if (isWalking)
transform.forward = new Vector3(-h, 0, -v).normalized;
}
_astronautAnimator.SetWalking(isWalking && !_isJumping);
bool canJump = _currentPlanet == Planet.Sun ? true : (_isGrounded && !_isJumping);
if (Input.GetKeyDown(KeyCode.Space) && canJump)
{
_isJumping = true;
_astronautAnimator.Jump();
StartCoroutine(JumpForceDelay());
}
}
IEnumerator JumpForceDelay()
{
float animSpeed = (_currentPlanet == Planet.Moon)
? 1.5f
: Mathf.Clamp(1f / (_ratio * 1.5f), 0.15f, 3f);
float delay = 0.7f / animSpeed;
yield return new WaitForSeconds(delay);
_rb.velocity = new Vector3(_rb.velocity.x, 0, _rb.velocity.z);
float clampedMass = GetClampedMass();
float savedMass = _rb.mass;
_rb.mass = 75f;
_rb.AddForce(Vector3.up * _jumpForce, ForceMode.Impulse);
_rb.mass = savedMass;
float bonus = Mathf.Lerp(1.5f, -1.5f, Mathf.InverseLerp(30f, 100f, clampedMass));
_rb.AddForce(Vector3.up * bonus, ForceMode.VelocityChange);
if (_gravityText != null)
StartCoroutine(ShowGravityText());
if (_currentPlanet == Planet.Sun)
_isJumping = false;
}
}

View File

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

View File

@@ -0,0 +1,25 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraFollow : MonoBehaviour
{
[SerializeField] private Transform _target;
[SerializeField] private float _smoothSpeed = 5f;
private float _offsetY;
private float _minY;
void Start()
{
_offsetY = transform.position.y - _target.position.y;
_minY = transform.position.y;
}
void LateUpdate()
{
float targetY = Mathf.Max(_target.position.y + _offsetY, _minY);
Vector3 newPos = new Vector3(transform.position.x, targetY, transform.position.z);
transform.position = Vector3.Lerp(transform.position, newPos, Time.deltaTime * _smoothSpeed);
}
}

View File

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

View File

@@ -0,0 +1,162 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraTransition : MonoBehaviour
{
[SerializeField] private Camera _cinematicCamera;
[SerializeField] private Camera _astronautCamera;
[SerializeField] private Camera _secretCamera;
[SerializeField] private Transform _endPoint;
[SerializeField] private float _duration = 2f;
[SerializeField] private GameObject _astronautCanvas;
[SerializeField] private GameObject _planetCanvas;
[SerializeField] private GameObject _secretCanvas;
[SerializeField] private UnityEngine.UI.Button _secretButton;
[SerializeField] private AstronautController _astronautController;
[SerializeField] private GameObject _secretPlanet;
[SerializeField] private GameObject _mainPlanet;
private Vector3 _startPos;
private Quaternion _startRot;
void Start()
{
_startPos = _cinematicCamera.transform.position;
_startRot = _cinematicCamera.transform.rotation;
_cinematicCamera.gameObject.SetActive(true);
_astronautCamera.gameObject.SetActive(false);
_secretCamera.gameObject.SetActive(false);
_astronautController.enabled = false;
_astronautCanvas.SetActive(false);
_secretCanvas.SetActive(false);
_planetCanvas.SetActive(true);
_secretButton.gameObject.SetActive(false);
_secretPlanet.SetActive(false);
}
void Update()
{
if (_astronautCanvas.activeSelf && Input.GetKeyDown(KeyCode.LeftControl))
{
_secretButton.gameObject.SetActive(true);
}
}
public void StartTransition()
{
_planetCanvas.SetActive(false);
StartCoroutine(FlyToAstronaut());
}
public void ReturnToPlanets()
{
_astronautCanvas.SetActive(false);
_astronautController.enabled = false;
_astronautCamera.gameObject.SetActive(false);
_cinematicCamera.gameObject.SetActive(true);
StartCoroutine(FlyBack());
}
public void GoToSecret()
{
_astronautCanvas.SetActive(false);
_astronautController.enabled = false;
_astronautCamera.gameObject.SetActive(false);
_cinematicCamera.gameObject.SetActive(true);
StartCoroutine(FlyToSecret());
}
public void ReturnFromSecret()
{
_secretCanvas.SetActive(false);
_secretCamera.gameObject.SetActive(false);
_cinematicCamera.gameObject.SetActive(true);
StartCoroutine(FlyFromSecret());
}
IEnumerator FlyToAstronaut()
{
float t = 0f;
Vector3 startPos = _cinematicCamera.transform.position;
Quaternion startRot = _cinematicCamera.transform.rotation;
while (t < 1f)
{
t += Time.deltaTime / _duration;
_cinematicCamera.transform.position = Vector3.Lerp(startPos, _endPoint.position, Mathf.SmoothStep(0f, 1f, t));
_cinematicCamera.transform.rotation = Quaternion.Lerp(startRot, _endPoint.rotation, Mathf.SmoothStep(0f, 1f, t));
yield return null;
}
_cinematicCamera.gameObject.SetActive(false);
_astronautCamera.gameObject.SetActive(true);
_astronautCanvas.SetActive(true);
_astronautController.enabled = true;
}
IEnumerator FlyBack()
{
float t = 0f;
Vector3 startPos = _cinematicCamera.transform.position;
Quaternion startRot = _cinematicCamera.transform.rotation;
while (t < 1f)
{
t += Time.deltaTime / _duration;
_cinematicCamera.transform.position = Vector3.Lerp(startPos, _startPos, Mathf.SmoothStep(0f, 1f, t));
_cinematicCamera.transform.rotation = Quaternion.Lerp(startRot, _startRot, Mathf.SmoothStep(0f, 1f, t));
yield return null;
}
_planetCanvas.SetActive(true);
}
IEnumerator FlyToSecret()
{
float t = 0f;
Vector3 startPos = _cinematicCamera.transform.position;
Quaternion startRot = _cinematicCamera.transform.rotation;
Vector3 secretPos = _secretCamera.transform.position;
Quaternion secretRot = _secretCamera.transform.rotation;
while (t < 1f)
{
t += Time.deltaTime / _duration;
_cinematicCamera.transform.position = Vector3.Lerp(startPos, secretPos, Mathf.SmoothStep(0f, 1f, t));
_cinematicCamera.transform.rotation = Quaternion.Lerp(startRot, secretRot, Mathf.SmoothStep(0f, 1f, t));
yield return null;
}
_cinematicCamera.gameObject.SetActive(false);
_secretCamera.gameObject.SetActive(true);
_secretCanvas.SetActive(true);
_secretPlanet.SetActive(true);
_mainPlanet.SetActive(false);
}
IEnumerator FlyFromSecret()
{
float t = 0f;
Vector3 startPos = _cinematicCamera.transform.position;
Quaternion startRot = _cinematicCamera.transform.rotation;
Vector3 targetPos = _astronautCamera.transform.position;
Quaternion targetRot = _astronautCamera.transform.rotation;
while (t < 1f)
{
t += Time.deltaTime / _duration;
_cinematicCamera.transform.position = Vector3.Lerp(startPos, targetPos, Mathf.SmoothStep(0f, 1f, t));
_cinematicCamera.transform.rotation = Quaternion.Lerp(startRot, targetRot, Mathf.SmoothStep(0f, 1f, t));
yield return null;
}
_cinematicCamera.gameObject.SetActive(false);
_astronautCamera.gameObject.SetActive(true);
_astronautController.enabled = true;
_astronautCanvas.SetActive(true);
_mainPlanet.SetActive(true);
_secretPlanet.SetActive(false);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 10bffe552295e6f458dec97634b9fcec
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 TMPro;
public class PlanetDropdown : MonoBehaviour
{
[SerializeField] private TMP_Dropdown _dropdown;
[SerializeField] private PlanetWeight.PlanetName _planet;
[SerializeField] private TextMeshProUGUI _planetNameText;
public TMP_Dropdown Dropdown => _dropdown;
public int PlanetIndex => (int)_planet;
void Start()
{
if (_planetNameText != null)
_planetNameText.text = _planet.ToString();
_dropdown.onValueChanged.AddListener(OnValueChanged);
}
void OnValueChanged(int index)
{
if (_planetNameText != null)
_planetNameText.text = _planet.ToString();
}
}

View File

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

View File

@@ -0,0 +1,61 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlanetLighting : MonoBehaviour
{
[SerializeField] private AstronautController _controller;
private Light _light;
void Start()
{
_light = GetComponent<Light>();
}
void Update()
{
switch (_controller.CurrentPlanet)
{
case AstronautController.Planet.Mercury:
_light.color = new Color(1f, 0.9f, 0.7f);
_light.intensity = 1.8f;
break;
case AstronautController.Planet.Venus:
_light.color = new Color(1f, 0.7f, 0.3f);
_light.intensity = 1.4f;
break;
case AstronautController.Planet.Earth:
_light.color = new Color(1f, 0.98f, 0.9f);
_light.intensity = 1f;
break;
case AstronautController.Planet.Moon:
_light.color = new Color(0.7f, 0.75f, 0.9f);
_light.intensity = 0.6f;
break;
case AstronautController.Planet.Mars:
_light.color = new Color(1f, 0.5f, 0.2f);
_light.intensity = 0.8f;
break;
case AstronautController.Planet.Jupiter:
_light.color = new Color(1f, 0.85f, 0.6f);
_light.intensity = 0.4f;
break;
case AstronautController.Planet.Saturn:
_light.color = new Color(0.9f, 0.8f, 0.5f);
_light.intensity = 0.35f;
break;
case AstronautController.Planet.Uranus:
_light.color = new Color(0.4f, 0.8f, 1f);
_light.intensity = 0.2f;
break;
case AstronautController.Planet.Neptune:
_light.color = new Color(0.2f, 0.4f, 1f);
_light.intensity = 0.15f;
break;
case AstronautController.Planet.Sun:
_light.color = new Color(1f, 1f, 0.8f);
_light.intensity = 5f;
break;
}
}
}

View File

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

View File

@@ -0,0 +1,89 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
using UnityEngine.UI;
public class PlanetWeight : MonoBehaviour
{
[SerializeField] private TextMeshProUGUI _resultText;
[SerializeField] private Button _checkButton;
[SerializeField] private Rigidbody _astronautRb;
public enum PlanetName
{
Ìåðêóð³é, Âåíåðà, Çåìëÿ, ̳ñÿöü, Ìàðñ, Þï³òåð, Ñàòóðí, Óðàí, Íåïòóí, Ñîíöå
}
float[] _gravities = { 3.7f, 8.87f, 9.81f, 1.62f, 3.71f, 24.79f, 10.44f, 8.69f, 11.15f, 274f };
void OnEnable()
{
RebuildDropdowns();
}
void RebuildDropdowns()
{
float mass = _astronautRb != null ? _astronautRb.mass : 75f;
PlanetDropdown[] planetDropdowns = FindObjectsOfType<PlanetDropdown>();
foreach (var pd in planetDropdowns)
{
pd.Dropdown.ClearOptions();
pd.Dropdown.options.Add(new TMP_Dropdown.OptionData("Îáðàòè"));
foreach (var g in _gravities)
{
float w = mass * g;
pd.Dropdown.options.Add(new TMP_Dropdown.OptionData(Mathf.RoundToInt(w) + " Í"));
}
pd.Dropdown.value = 0;
pd.Dropdown.RefreshShownValue();
}
_checkButton.onClick.RemoveAllListeners();
_checkButton.onClick.AddListener(CheckAll);
_resultText.alpha = 0f;
}
void CheckAll()
{
float mass = _astronautRb != null ? _astronautRb.mass : 75f;
int correct = 0;
PlanetDropdown[] planetDropdowns = FindObjectsOfType<PlanetDropdown>();
foreach (var pd in planetDropdowns)
{
int planetIndex = pd.PlanetIndex;
float correctWeight = mass * _gravities[planetIndex];
int selectedIndex = pd.Dropdown.value - 1;
if (selectedIndex < 0) continue;
float selectedWeight = mass * _gravities[selectedIndex];
if (Mathf.Abs(selectedWeight - correctWeight) < 1f)
{
correct++;
pd.Dropdown.GetComponentInParent<Image>().color = Color.green;
}
else
{
pd.Dropdown.GetComponentInParent<Image>().color = Color.red;
}
}
if (correct == planetDropdowns.Length)
{
_resultText.text = "Áëèñêó÷å! Öå ïðàâèëüíà â³äïîâ³äü!";
_resultText.color = Color.green;
}
else
{
_resultText.text = "Ìàéæå! Ñïðîáóé ùå ðàç";
_resultText.color = Color.red;
}
StartCoroutine(ShowText());
}
IEnumerator ShowText()
{
float t = 0f;
while (t < 1f) { t += Time.deltaTime * 2f; _resultText.alpha = t; yield return null; }
yield return new WaitForSeconds(3f);
t = 1f;
while (t > 0f) { t -= Time.deltaTime * 2f; _resultText.alpha = t; yield return null; }
}
}

View File

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

View File

@@ -0,0 +1,32 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
public class SecretDropdown : MonoBehaviour
{
public enum SecretPlanet
{
<EFBFBD>, , 調, <EFBFBD>, , , , , , <EFBFBD>
}
[SerializeField] private TMP_Dropdown _dropdown;
[SerializeField] private SecretPlanet _planet;
[SerializeField] private TextMeshProUGUI _planetNameText;
public TMP_Dropdown Dropdown => _dropdown;
public int PlanetIndex => (int)_planet;
void Start()
{
if (_planetNameText != null)
_planetNameText.text = _planet.ToString();
_dropdown.onValueChanged.AddListener(OnValueChanged);
}
void OnValueChanged(int index)
{
if (_planetNameText != null)
_planetNameText.text = _planet.ToString();
}
}

View File

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

View File

@@ -0,0 +1,88 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
using UnityEngine.UI;
public class SecretPlanetWeight : MonoBehaviour
{
[SerializeField] private TextMeshProUGUI _resultText;
[SerializeField] private Button _checkButton;
[SerializeField] private Rigidbody _astronautRb;
private const float _earthGravity = 9.81f;
float[] _gravities = { 274f, 3.7f, 8.87f, 1.62f, 3.71f, 24.79f, 10.44f, 8.69f, 11.15f, 9.81f };
void OnEnable()
{
RebuildDropdowns();
}
void RebuildDropdowns()
{
float mass = _astronautRb != null ? _astronautRb.mass : 75f;
SecretDropdown[] secretDropdowns = FindObjectsOfType<SecretDropdown>(true);
foreach (var sd in secretDropdowns)
{
sd.Dropdown.ClearOptions();
sd.Dropdown.options.Add(new TMP_Dropdown.OptionData("Îáðàòè"));
foreach (var g in _gravities)
{
float weightN = mass * g;
float massKg = weightN / _earthGravity;
sd.Dropdown.options.Add(new TMP_Dropdown.OptionData(Mathf.RoundToInt(massKg) + " êã"));
}
sd.Dropdown.value = 0;
sd.Dropdown.RefreshShownValue();
}
_checkButton.onClick.RemoveAllListeners();
_checkButton.onClick.AddListener(CheckAll);
_resultText.alpha = 0f;
}
void CheckAll()
{
float mass = _astronautRb != null ? _astronautRb.mass : 75f;
int correct = 0;
SecretDropdown[] secretDropdowns = FindObjectsOfType<SecretDropdown>(true);
foreach (var sd in secretDropdowns)
{
int planetIndex = sd.PlanetIndex;
float correctWeightN = mass * _gravities[planetIndex];
float correctMassKg = correctWeightN / _earthGravity;
int selectedIndex = sd.Dropdown.value - 1;
if (selectedIndex < 0) continue;
float selectedWeightN = mass * _gravities[selectedIndex];
float selectedMassKg = selectedWeightN / _earthGravity;
if (Mathf.Abs(selectedMassKg - correctMassKg) < 1f)
{
correct++;
sd.Dropdown.GetComponentInParent<Image>().color = Color.green;
}
else
{
sd.Dropdown.GetComponentInParent<Image>().color = Color.red;
}
}
if (correct == secretDropdowns.Length)
{
_resultText.text = "Áëèñêó÷å! Öå ïðàâèëüíà â³äïîâ³äü!";
_resultText.color = Color.green;
}
else
{
_resultText.text = "Ìàéæå! Ñïðîáóé ùå ðàç";
_resultText.color = Color.red;
}
StartCoroutine(ShowText());
}
IEnumerator ShowText()
{
float t = 0f;
while (t < 1f) { t += Time.deltaTime * 2f; _resultText.alpha = t; yield return null; }
yield return new WaitForSeconds(3f);
t = 1f;
while (t > 0f) { t -= Time.deltaTime * 2f; _resultText.alpha = t; yield return null; }
}
}

View File

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

View File

@@ -0,0 +1,106 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BalanceScale : MonoBehaviour
{
[SerializeField] private Transform[] _leftSlots;
[SerializeField] private Transform[] _rightSlots;
[SerializeField] private Transform[] _scaleMarks;
[SerializeField] private Transform _slider;
[SerializeField] private float _animationSpeed = 2f;
private Transform[] _leftOccupied;
private Transform[] _rightOccupied;
private Vector3 _targetSliderPos;
private void Awake()
{
_leftOccupied = new Transform[_leftSlots.Length];
_rightOccupied = new Transform[_rightSlots.Length];
if (_slider != null)
_targetSliderPos = _slider.localPosition;
}
private void Update()
{
if (_slider == null) return;
_slider.localPosition = Vector3.Lerp(
_slider.localPosition,
_targetSliderPos,
Time.deltaTime * _animationSpeed
);
}
public void PlaceLeft(Transform obj)
{
Place(obj, _leftSlots, _leftOccupied);
}
public void PlaceRight(Transform obj)
{
Place(obj, _rightSlots, _rightOccupied);
}
private void Place(Transform obj, Transform[] slots, Transform[] occupied)
{
for (int i = 0; i < occupied.Length; i++)
if (occupied[i] == obj) occupied[i] = null;
for (int i = 0; i < slots.Length; i++)
{
if (occupied[i] == null)
{
occupied[i] = obj;
obj.SetParent(null);
Collider col = slots[i].GetComponent<Collider>();
Vector3 targetPos = col != null ? col.bounds.center : slots[i].position;
obj.position = targetPos;
obj.rotation = slots[i].rotation;
UpdateSlider();
return;
}
}
obj.SetParent(null, true);
}
public void RemoveObject(Transform obj)
{
for (int i = 0; i < _leftOccupied.Length; i++)
if (_leftOccupied[i] == obj) _leftOccupied[i] = null;
for (int i = 0; i < _rightOccupied.Length; i++)
if (_rightOccupied[i] == obj) _rightOccupied[i] = null;
UpdateSlider();
}
private float GetMass(Transform[] occupied)
{
float total = 0f;
foreach (var obj in occupied)
{
if (obj == null) continue;
LiquidContainer lc = obj.GetComponent<LiquidContainer>()
?? obj.GetComponentInChildren<LiquidContainer>();
Rigidbody rb = obj.GetComponent<Rigidbody>();
if (lc != null) total += lc.GetTotalMassKg();
else if (rb != null) total += rb.mass;
}
return total;
}
private void UpdateSlider()
{
if (_scaleMarks == null || _scaleMarks.Length == 0) return;
float leftMass = GetMass(_leftOccupied);
float rightMass = GetMass(_rightOccupied);
float displayMass = Mathf.Max(leftMass, rightMass);
int targetIndex = Mathf.Clamp(Mathf.RoundToInt(displayMass), 0, _scaleMarks.Length - 1);
_targetSliderPos = _slider.parent != null
? _slider.parent.InverseTransformPoint(_scaleMarks[targetIndex].position)
: _scaleMarks[targetIndex].position;
}
}

View File

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

View File

@@ -0,0 +1,114 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BeakerContainer : MonoBehaviour
{
[System.Serializable]
public class LiquidLayer
{
public LiquidContainer.LiquidType liquidType;
public Color color;
public float density;
public float amount;
}
[SerializeField] private GameObject _layerTemplate;
[SerializeField] private float _emptyMassKg = 0.15f;
[SerializeField] private float _volumeLiters = 0.25f;
[SerializeField] public float capacity = 1f;
public List<LiquidLayer> layers = new List<LiquidLayer>();
private List<GameObject> _layerObjects = new List<GameObject>();
private Rigidbody _rb;
public bool IsFull() => TotalAmount() >= 1f;
public bool IsEmpty() => TotalAmount() <= 0f;
public float TotalAmount()
{
float total = 0f;
foreach (var l in layers) total += l.amount;
return total;
}
private void Awake()
{
_rb = GetComponent<Rigidbody>();
if (_layerTemplate != null)
_layerTemplate.SetActive(false);
}
public void AddLiquid(Color color, float amount, float density, LiquidContainer.LiquidType type)
{
float canAdd = Mathf.Clamp(amount, 0f, 1f - TotalAmount());
if (canAdd <= 0f) return;
LiquidLayer existing = layers.Find(l => l.liquidType == type);
if (existing != null)
{
existing.amount += canAdd;
}
else
{
layers.Add(new LiquidLayer
{
liquidType = type,
color = color,
density = density,
amount = canAdd
});
}
layers.Sort((a, b) => b.density.CompareTo(a.density));
UpdateVisual();
UpdateMass();
}
public void UpdateMass()
{
if (_rb == null) return;
float liquidMass = 0f;
foreach (var l in layers)
liquidMass += l.amount * _volumeLiters * l.density;
_rb.mass = _emptyMassKg + liquidMass;
}
public void UpdateVisual()
{
foreach (var obj in _layerObjects)
Destroy(obj);
_layerObjects.Clear();
if (_layerTemplate == null || layers.Count == 0) return;
float yOffset = 0f;
foreach (var layer in layers)
{
GameObject go = Instantiate(_layerTemplate, _layerTemplate.transform.parent);
go.SetActive(true);
Vector3 pos = _layerTemplate.transform.localPosition;
pos.y = yOffset;
go.transform.localPosition = pos;
Vector3 scale = _layerTemplate.transform.localScale;
scale.y = Mathf.Max(0.01f, layer.amount);
go.transform.localScale = scale;
Renderer rend = go.GetComponent<Renderer>();
if (rend != null)
{
Material mat = new Material(rend.sharedMaterial);
if (mat.HasProperty("_c1")) mat.SetColor("_c1", layer.color);
if (mat.HasProperty("_c2")) mat.SetColor("_c2", layer.color);
rend.material = mat;
}
yOffset += scale.y;
_layerObjects.Add(go);
}
}
}

View File

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

View File

@@ -0,0 +1,45 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Breakable : MonoBehaviour
{
[SerializeField] private GameObject _debrisRoot;
[SerializeField] private float _minFallSpeed = 2f;
[SerializeField] private float _explosionForce = 100f;
[SerializeField] private float _explosionRadius = 2f;
[SerializeField] private float _disappearTime = 3f;
private Rigidbody _rb;
private bool _broken = false;
void Start()
{
_rb = GetComponent<Rigidbody>();
_debrisRoot.SetActive(false);
}
void OnCollisionEnter(Collision collision)
{
if (_broken) return;
if (_rb.velocity.y < -_minFallSpeed)
Break(collision.GetContact(0).point);
}
void Break(Vector3 contactPoint)
{
_broken = true;
_debrisRoot.SetActive(true);
_debrisRoot.transform.parent = null;
Rigidbody[] pieces = _debrisRoot.GetComponentsInChildren<Rigidbody>();
foreach (var piece in pieces)
{
piece.velocity = _rb.velocity;
piece.AddExplosionForce(_explosionForce, contactPoint, _explosionRadius);
}
Destroy(_debrisRoot, _disappearTime);
Destroy(gameObject);
}
}

View File

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

View File

@@ -0,0 +1,35 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraController : MonoBehaviour
{
[SerializeField] private float _mouseSensitivityX = 2f;
[SerializeField] private float _mouseSensitivityY = 2f;
[SerializeField] private float _minVerticalAngle = -75f;
[SerializeField] private float _maxVerticalAngle = 45f;
[SerializeField] private Transform _controller;
private float _xRotation = 0f;
private float _yRotation = 0f;
void Update()
{
float mouseX = Input.GetAxis("Mouse X") * _mouseSensitivityX;
float mouseY = Input.GetAxis("Mouse Y") * _mouseSensitivityY;
_yRotation += mouseX;
_xRotation -= mouseY;
_xRotation = Mathf.Clamp(_xRotation, _minVerticalAngle, _maxVerticalAngle);
transform.localRotation = Quaternion.Euler(_xRotation, 0f, 0f);
if (_controller != null)
{
_controller.rotation = Quaternion.Euler(0f, _yRotation, 0f);
}
}
}

View File

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

View File

@@ -0,0 +1,98 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CursorSwitcher : MonoBehaviour
{
[SerializeField] private Texture2D defaultCursor;
[SerializeField] private Texture2D targetCursor;
[SerializeField] private Texture2D knobCursor;
[SerializeField] private Texture2D liquidCursor;
[SerializeField] private float interactDistance = 3f;
public float InteractDistance => interactDistance;
[SerializeField] private LayerMask interactMask = ~0;
[SerializeField] private HandController handController;
private Camera _camera;
private bool _isTargeting;
void Start()
{
_camera = GetComponent<Camera>();
Cursor.visible = true;
Cursor.lockState = CursorLockMode.None;
SetDefaultCursor();
}
void Update()
{
Ray ray = _camera.ScreenPointToRay(Input.mousePosition);
RaycastHit[] hits = Physics.RaycastAll(ray, interactDistance, interactMask);
bool holding = handController != null && handController.IsHoldingAny();
bool hasTarget = false;
bool hasLiquid = false;
bool hasBreakable = false;
bool hasScale = false;
bool hasKnob = false;
foreach (var hit in hits)
{
Transform current = hit.collider.transform;
while (current != null)
{
if (current.CompareTag("Target")) hasTarget = true;
if (current.CompareTag("Liquid")) hasLiquid = true;
if (current.CompareTag("Breakable")) hasBreakable = true;
if (holding && current.CompareTag("Scale")) hasScale = true;
if (current.CompareTag("Knob") || current.CompareTag("Drawer") || current.CompareTag("Door")) hasKnob = true;
current = current.parent;
}
}
if (hasTarget || hasBreakable || hasScale)
{
SetTargetCursor(); _isTargeting = true;
}
else if (hasLiquid)
{
SetLiquidCursor(); _isTargeting = true;
}
else if (hasKnob)
{
SetKnobCursor(); _isTargeting = true;
}
else if (_isTargeting)
{
SetDefaultCursor();
_isTargeting = false;
}
}
void SetDefaultCursor()
{
if (defaultCursor != null)
Cursor.SetCursor(defaultCursor, new Vector2(16, 16), CursorMode.Auto);
}
void SetTargetCursor()
{
if (targetCursor != null)
Cursor.SetCursor(targetCursor, new Vector2(16, 16), CursorMode.Auto);
}
void SetKnobCursor()
{
if (knobCursor != null)
Cursor.SetCursor(knobCursor, new Vector2(16, 16), CursorMode.Auto);
}
void SetLiquidCursor()
{
if (liquidCursor != null)
Cursor.SetCursor(liquidCursor, new Vector2(16, 16), CursorMode.Auto);
else
SetTargetCursor();
}
}

View File

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

View File

@@ -0,0 +1,57 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DisappearOnGround : MonoBehaviour
{
[SerializeField] private float _disappearDelay = 0.5f;
[SerializeField] private float _disappearDuration = 1.5f;
private bool _isFading = false;
void OnCollisionEnter(Collision collision)
{
if (_isFading) return;
if (collision.gameObject.CompareTag("Ground"))
StartCoroutine(FadeAndDestroy());
}
IEnumerator FadeAndDestroy()
{
_isFading = true;
yield return new WaitForSeconds(_disappearDelay);
Renderer[] renderers = GetComponentsInChildren<Renderer>();
foreach (var r in renderers)
{
foreach (var mat in r.materials)
{
mat.SetFloat("_Mode", 2);
mat.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
mat.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
mat.SetInt("_ZWrite", 0);
mat.DisableKeyword("_ALPHATEST_ON");
mat.EnableKeyword("_ALPHABLEND_ON");
mat.DisableKeyword("_ALPHAPREMULTIPLY_ON");
mat.renderQueue = 3000;
}
}
float t = 0f;
while (t < 1f)
{
t += Time.deltaTime / _disappearDuration;
foreach (var r in renderers)
foreach (var mat in r.materials)
{
Color c = mat.color;
c.a = Mathf.Lerp(1f, 0f, t);
mat.color = c;
}
yield return null;
}
Destroy(gameObject);
}
}

View File

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

54
Assets/Scripts/Drawer.cs Normal file
View File

@@ -0,0 +1,54 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Drawer : MonoBehaviour
{
[SerializeField] private float _speed = 3f;
[SerializeField] private float _openZ = 0.873f;
[SerializeField] private Camera _camera;
[SerializeField] private float _interactDistance = 3f;
private bool _isOpen = false;
private float _targetZ;
private float _closedZ;
private bool _hasTarget = false;
public bool IsOpen => _isOpen;
void Start()
{
_closedZ = transform.localPosition.z;
_targetZ = _closedZ;
}
void Update()
{
if (Input.GetMouseButtonDown(0))
{
Ray ray = _camera.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out RaycastHit hit, _interactDistance))
{
if (hit.collider.transform == transform ||
hit.collider.transform.IsChildOf(transform))
{
_isOpen = !_isOpen;
_targetZ = _isOpen ? _openZ : _closedZ;
_hasTarget = true;
}
}
}
if (!_hasTarget) return;
Vector3 pos = transform.localPosition;
pos.z = Mathf.MoveTowards(pos.z, _targetZ, Time.deltaTime * _speed);
transform.localPosition = pos;
if (Mathf.Abs(pos.z - _targetZ) < 0.001f)
{
pos.z = _targetZ;
transform.localPosition = pos;
_hasTarget = false;
}
}
}

View File

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

View File

@@ -0,0 +1,32 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DrawerContent : MonoBehaviour
{
[SerializeField] private Drawer _drawer;
private Rigidbody _rb;
private Collider _collider;
void Start()
{
_rb = GetComponent<Rigidbody>();
_collider = GetComponent<Collider>();
SetPhysics(false);
}
void Update()
{
if (_drawer.IsOpen)
SetPhysics(true);
else
SetPhysics(false);
}
void SetPhysics(bool active)
{
_rb.isKinematic = !active;
_collider.enabled = active;
}
}

View File

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

View File

@@ -0,0 +1,27 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GridGroup : MonoBehaviour
{
[SerializeField] private int columns = 3;
[SerializeField] private float spacingX = 3f;
[SerializeField] private float spacingY = 3f;
void Start()
{
for (int i = 0; i < transform.childCount; i++)
{
int row = i / columns;
int col = i % columns;
Transform child = transform.GetChild(i);
child.localPosition = new Vector3(
col * spacingX,
-row * spacingY,
0f
);
}
}
}

View File

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

View File

@@ -0,0 +1,260 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class HandController : MonoBehaviour
{
[SerializeField] private Animator _animator;
[SerializeField] private Transform _rightGrabPoint;
[SerializeField] private Transform _leftGrabPoint;
[SerializeField] private float _interactDistance = 3f;
[SerializeField] private float _reachSpeed = 6f;
[SerializeField] private float _reachOffset = 0.15f;
[SerializeField] private LayerMask _interactMask = ~0;
[SerializeField] private float _liquidReturnDistance = 3f;
[SerializeField] private float _holdScale = 0.6f;
[SerializeField] private Vector3 _liquidLocalOffset = new Vector3(0f, 0.0005f, 0f);
private Camera _camera;
private Transform _rightHeldObject;
private Transform _leftHeldObject;
private Vector3 _rightGrabStartLocalPos;
private Vector3 _leftGrabStartLocalPos;
private bool _isAnimating;
private Dictionary<Transform, Vector3> _originalPositions = new Dictionary<Transform, Vector3>();
private Dictionary<Transform, Quaternion> _originalRotations = new Dictionary<Transform, Quaternion>();
private Dictionary<Transform, Vector3> _originalScales = new Dictionary<Transform, Vector3>();
public Transform RightHeldObject => _rightHeldObject;
public Transform LeftHeldObject => _leftHeldObject;
void Start()
{
_camera = Camera.main;
_rightGrabStartLocalPos = _rightGrabPoint.localPosition;
_leftGrabStartLocalPos = _leftGrabPoint.localPosition;
}
void Update()
{
if (_isAnimating) return;
if (Input.GetKeyDown(KeyCode.R)) HandleRight();
if (Input.GetKeyDown(KeyCode.L)) HandleLeft();
}
void HandleRight()
{
if (_rightHeldObject != null)
{
Release(_rightHeldObject, _rightGrabPoint, _rightGrabStartLocalPos, "RightOpen");
_rightHeldObject = null;
return;
}
TryGrab(_rightGrabPoint, _rightGrabStartLocalPos, "RightGrab", true);
}
void HandleLeft()
{
if (_leftHeldObject != null)
{
Release(_leftHeldObject, _leftGrabPoint, _leftGrabStartLocalPos, "LeftOpen");
_leftHeldObject = null;
return;
}
TryGrab(_leftGrabPoint, _leftGrabStartLocalPos, "LeftGrab", false);
}
void TryGrab(Transform grabPoint, Vector3 grabStartLocalPos, string animName, bool isRight)
{
Ray ray = _camera.ScreenPointToRay(Input.mousePosition);
RaycastHit[] hits = Physics.RaycastAll(ray, _interactDistance, _interactMask);
foreach (var hit in hits)
{
Transform target = FindTarget(hit.collider.transform);
if (target == null) continue;
StartCoroutine(ReachAndGrab(target, grabPoint, grabStartLocalPos, animName, isRight));
return;
}
}
Transform FindTarget(Transform current)
{
while (current != null)
{
if (current.CompareTag("Target")) return current;
if (current.CompareTag("Liquid")) return current;
if (current.CompareTag("Breakable")) return current;
current = current.parent;
}
return null;
}
IEnumerator ReachAndGrab(Transform target, Transform grabPoint, Vector3 grabStartLocalPos, string animName, bool isRight)
{
_isAnimating = true;
NotifyScaleRemoved(target);
_originalPositions[target] = target.position;
_originalRotations[target] = target.rotation;
_originalScales[target] = target.lossyScale;
Vector3 direction = (target.position - grabPoint.position).normalized;
Vector3 reachTargetPos = grabPoint.position + direction * _reachOffset;
Vector3 startPos = grabPoint.position;
float t = 0f;
while (t < 1f)
{
t += Time.deltaTime * _reachSpeed;
grabPoint.position = Vector3.Lerp(startPos, reachTargetPos, t);
yield return null;
}
Vector3 originalScale = target.lossyScale;
SetupPhysics(target, true);
target.SetParent(grabPoint, true);
Vector3 parentScale = grabPoint.lossyScale;
Vector3 holdScale = originalScale * _holdScale;
target.localScale = new Vector3(
holdScale.x / parentScale.x,
holdScale.y / parentScale.y,
holdScale.z / parentScale.z
);
target.rotation = Quaternion.Euler(0f, grabPoint.rotation.eulerAngles.y, 0f);
if (target.CompareTag("Liquid"))
target.localPosition = _liquidLocalOffset;
else
target.localPosition = Vector3.zero;
if (isRight) _rightHeldObject = target;
else _leftHeldObject = target;
_animator.Play(animName);
yield return ReturnGrabPoint(grabPoint, grabStartLocalPos);
_isAnimating = false;
}
IEnumerator ReturnGrabPoint(Transform grabPoint, Vector3 startLocalPos)
{
Vector3 startPos = grabPoint.localPosition;
float t = 0f;
while (t < 1f)
{
t += Time.deltaTime * _reachSpeed;
grabPoint.localPosition = Vector3.Lerp(startPos, startLocalPos, t);
yield return null;
}
}
void Release(Transform obj, Transform grabPoint, Vector3 grabStartLocalPos, string animName)
{
Ray ray = _camera.ScreenPointToRay(Input.mousePosition);
bool placedOnScale = false;
RaycastHit[] hits = Physics.RaycastAll(ray, _interactDistance);
foreach (var hit in hits)
{
ScaleSurface scale = hit.collider.GetComponentInParent<ScaleSurface>();
if (scale != null)
{
obj.SetParent(null, true);
SetupPhysics(obj, false);
if (_originalScales.ContainsKey(obj))
obj.localScale = _originalScales[obj];
scale.PlaceObject(obj);
placedOnScale = true;
break;
}
BalanceScale balance = hit.collider.GetComponentInParent<BalanceScale>();
if (balance != null)
{
obj.SetParent(null, true);
SetupPhysics(obj, false);
if (_originalScales.ContainsKey(obj))
obj.localScale = _originalScales[obj];
bool isLeft = Vector3.Dot(
hit.point - balance.transform.position,
balance.transform.right) < 0;
if (isLeft) balance.PlaceLeft(obj);
else balance.PlaceRight(obj);
placedOnScale = true;
break;
}
}
if (!placedOnScale)
{
obj.SetParent(null, true);
SetupPhysics(obj, false);
if (_originalScales.ContainsKey(obj))
obj.localScale = _originalScales[obj];
if (obj.CompareTag("Liquid") && _originalPositions.ContainsKey(obj))
{
float dist = Vector3.Distance(obj.position, _originalPositions[obj]);
if (dist <= _liquidReturnDistance)
{
obj.position = _originalPositions[obj];
obj.rotation = _originalRotations[obj];
Rigidbody rb = obj.GetComponent<Rigidbody>();
if (rb != null) StartCoroutine(FreezeOnPlace(rb));
}
}
}
_originalPositions.Remove(obj);
_originalRotations.Remove(obj);
_originalScales.Remove(obj);
StartCoroutine(ReturnGrabPoint(grabPoint, grabStartLocalPos));
_animator.Play(animName);
}
private IEnumerator FreezeOnPlace(Rigidbody rb)
{
rb.isKinematic = true;
yield return new WaitForSeconds(0.5f);
rb.isKinematic = false;
}
void NotifyScaleRemoved(Transform obj)
{
MassController[] allMass = FindObjectsOfType<MassController>();
foreach (var mc in allMass)
mc.RemoveObject(obj.GetComponent<Rigidbody>());
ScaleSurface[] allSurfaces = FindObjectsOfType<ScaleSurface>();
foreach (var surface in allSurfaces)
surface.RemoveObject(obj);
BalanceScale[] allBalances = FindObjectsOfType<BalanceScale>();
foreach (var balance in allBalances)
balance.RemoveObject(obj);
}
void SetupPhysics(Transform obj, bool disable)
{
Rigidbody rb = obj.GetComponent<Rigidbody>();
Collider[] colliders = obj.GetComponentsInChildren<Collider>();
if (rb != null)
{
rb.isKinematic = disable;
rb.useGravity = !disable;
}
foreach (var col in colliders)
col.enabled = !disable;
}
public bool IsHoldingAny()
{
return _rightHeldObject != null || _leftHeldObject != null;
}
}

View File

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

View File

@@ -0,0 +1,55 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class KnobController : MonoBehaviour
{
[SerializeField] private MassController _massController;
[SerializeField] private float _rotationDuration = 0.3f;
[SerializeField] private Vector3[] _positions;
private int _currentIndex = 0;
private bool _isRotating = false;
private void OnMouseDown()
{
if (_isRotating) return;
RotateToNextPosition();
if (_massController != null)
_massController.SwitchUnit();
}
private void RotateToNextPosition()
{
_currentIndex++;
if (_currentIndex >= _positions.Length)
_currentIndex = 0;
StartCoroutine(RotateTo(_positions[_currentIndex]));
}
private IEnumerator RotateTo(Vector3 targetEuler)
{
_isRotating = true;
Quaternion start = transform.rotation;
Quaternion end = Quaternion.Euler(targetEuler);
float time = 0f;
while (time < _rotationDuration)
{
time += Time.deltaTime;
float t = time / _rotationDuration;
transform.rotation = Quaternion.Slerp(start, end, t);
yield return null;
}
transform.rotation = end;
_isRotating = false;
}
}

View File

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

View File

@@ -0,0 +1,116 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class LiquidContainer : MonoBehaviour
{
public enum LiquidType
{
None, Water, Milk, Oil, Honey,
StrawberrySyrup, CherrySyrup, Chlorophyll, Phenolphthalein, Bromothymol
}
[SerializeField] private Renderer _liquidRenderer;
[SerializeField] private float _minScaleY = 0.01f;
[SerializeField] public float capacity = 0.01f;
[SerializeField] private float _emptyMassKg = 0.05f;
public LiquidType liquidType = LiquidType.None;
[Range(0f, 1f)] public float amount = 0f;
public Color color = Color.blue;
public float density = 1.0f;
private Rigidbody _rb;
public bool IsEmpty() => amount <= 0f;
public bool IsFull() => amount >= 1f;
public float GetTotalMassKg() => _emptyMassKg + capacity * amount * density;
static float GetDensity(LiquidType type)
{
switch (type)
{
case LiquidType.Honey: return 1.4f;
case LiquidType.StrawberrySyrup: return 1.1f;
case LiquidType.CherrySyrup: return 1.05f;
case LiquidType.Milk: return 1.03f;
case LiquidType.Bromothymol: return 1.02f;
case LiquidType.Chlorophyll: return 1.01f;
case LiquidType.Water: return 1.0f;
case LiquidType.Oil: return 0.9f;
case LiquidType.Phenolphthalein: return 0.79f;
default: return 1.0f;
}
}
private void Awake()
{
_rb = GetComponent<Rigidbody>();
}
private void Start()
{
if (liquidType != LiquidType.None && amount > 0f)
density = GetDensity(liquidType);
if (_liquidRenderer != null && amount > 0f && _liquidRenderer.material.HasProperty("_c1"))
color = _liquidRenderer.material.GetColor("_c1");
UpdateVisual();
UpdateMass();
}
private void OnValidate()
{
if (liquidType != LiquidType.None && amount > 0f)
density = GetDensity(liquidType);
}
public void AddLiquid(Color newColor, float addAmount, float liquidDensity, LiquidType type)
{
float canAdd = Mathf.Clamp(addAmount, 0f, 1f - amount);
if (canAdd <= 0f) return;
color = Color.Lerp(color, newColor, canAdd / (amount + canAdd));
amount += canAdd;
density = liquidDensity;
liquidType = type;
UpdateVisual();
UpdateMass();
}
public float RemoveLiquid(float removeAmount)
{
float taken = Mathf.Min(amount, removeAmount);
amount -= taken;
if (amount <= 0f)
{
liquidType = LiquidType.None;
density = 1f;
}
UpdateVisual();
UpdateMass();
return taken;
}
public void UpdateMass()
{
if (_rb == null) return;
_rb.mass = _emptyMassKg + capacity * amount * density;
}
public void UpdateVisual()
{
if (_liquidRenderer == null) return;
if (amount <= 0f)
{
_liquidRenderer.gameObject.SetActive(false);
return;
}
_liquidRenderer.gameObject.SetActive(true);
Vector3 scale = _liquidRenderer.transform.localScale;
scale.y = Mathf.Max(_minScaleY, amount);
_liquidRenderer.transform.localScale = scale;
Material mat = _liquidRenderer.material;
if (mat.HasProperty("_c1")) mat.SetColor("_c1", color);
if (mat.HasProperty("_c2")) mat.SetColor("_c2", color);
if (!mat.HasProperty("_c1") && !mat.HasProperty("_c2")) mat.color = color;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ff1b86068fc363241861b40a7bff4f59
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 LiquidPourer : MonoBehaviour
{
[SerializeField] private HandController _handController;
[SerializeField] private KeyCode _pourKey = KeyCode.E;
void Update()
{
if (!Input.GetKeyDown(_pourKey)) return;
Transform right = _handController.RightHeldObject;
Transform left = _handController.LeftHeldObject;
if (right == null || left == null) return;
if (!right.CompareTag("Liquid") || !left.CompareTag("Liquid")) return;
LiquidContainer containerRight = right.GetComponent<LiquidContainer>()
?? right.GetComponentInParent<LiquidContainer>()
?? right.GetComponentInChildren<LiquidContainer>();
LiquidContainer containerLeft = left.GetComponent<LiquidContainer>()
?? left.GetComponentInParent<LiquidContainer>()
?? left.GetComponentInChildren<LiquidContainer>();
if (containerRight == null || containerLeft == null) return;
LiquidContainer source = null;
LiquidContainer target = null;
if (!containerRight.IsEmpty() && !containerLeft.IsFull())
{
source = containerRight;
target = containerLeft;
}
else if (!containerLeft.IsEmpty() && !containerRight.IsFull())
{
source = containerLeft;
target = containerRight;
}
if (source == null || target == null) return;
float sourceRealLiters = source.amount * source.capacity;
float targetFreeSpace = (1f - target.amount) * target.capacity;
float transferLiters = Mathf.Min(sourceRealLiters, targetFreeSpace);
if (transferLiters <= 0f) return;
float sourceRemoveAmount = transferLiters / source.capacity;
float targetAddAmount = transferLiters / target.capacity;
Color pouringColor = source.color;
float pouringDensity = source.density;
LiquidContainer.LiquidType pouringType = source.liquidType;
source.RemoveLiquid(sourceRemoveAmount);
target.AddLiquid(pouringColor, targetAddAmount, pouringDensity, pouringType);
}
}

View File

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

View File

@@ -0,0 +1,104 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
public class MassController : MonoBehaviour
{
[SerializeField] private TextMeshProUGUI _weightText;
[SerializeField] private float _animationSpeed = 2f;
private List<Rigidbody> _bodies = new List<Rigidbody>();
private Coroutine _weightRoutine;
private float _currentDisplayedValue = 0f;
private enum UnitMode { Kg, G, Mg }
private UnitMode _currentMode = UnitMode.Kg;
private void Reset()
{
Collider c = GetComponent<Collider>();
if (c != null) c.isTrigger = true;
}
private void Update()
{
if (_bodies.Count > 0) UpdateWeight();
}
public void AddObject(Rigidbody rb)
{
if (rb == null) return;
if (!_bodies.Contains(rb))
_bodies.Add(rb);
}
public void RemoveObject(Rigidbody rb)
{
if (rb == null) return;
_bodies.Remove(rb);
UpdateWeight();
}
public void SwitchUnit()
{
_currentMode = _currentMode switch
{
UnitMode.Kg => UnitMode.G,
UnitMode.G => UnitMode.Mg,
_ => UnitMode.Kg
};
UpdateWeight();
}
private void UpdateWeight()
{
float totalKg = 0f;
foreach (var rb in _bodies)
{
if (rb == null) continue;
LiquidContainer lc = rb.GetComponent<LiquidContainer>()
?? rb.GetComponentInChildren<LiquidContainer>();
totalKg += lc != null ? lc.GetTotalMassKg() : rb.mass;
}
float target = ConvertValue(totalKg);
if (_weightRoutine != null) StopCoroutine(_weightRoutine);
_weightRoutine = StartCoroutine(AnimateWeight(target));
}
private float ConvertValue(float kg)
{
return _currentMode switch
{
UnitMode.G => kg * 1000f,
UnitMode.Mg => kg * 1_000_000f,
_ => kg
};
}
private string GetUnitLabel()
{
return _currentMode switch
{
UnitMode.G => " g",
UnitMode.Mg => " mg",
_ => " kg"
};
}
private IEnumerator AnimateWeight(float targetValue)
{
float elapsed = 0f;
float startValue = _currentDisplayedValue;
float duration = 1f / _animationSpeed;
while (elapsed < duration)
{
elapsed += Time.deltaTime;
_currentDisplayedValue = Mathf.Lerp(startValue, targetValue, elapsed / duration);
_weightText.text = _currentDisplayedValue.ToString("F2") + GetUnitLabel();
yield return null;
}
_currentDisplayedValue = targetValue;
_weightText.text = targetValue.ToString("F2") + GetUnitLabel();
}
}

View File

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

View File

@@ -0,0 +1,70 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
[Header("Øâèäê³ñòü ðóõó")]
[Range(1f, 20f)]
[SerializeField] private float _movementSpeed = 5f;
[Header("Ïðèñêîðåííÿ ðóõó")]
[Range(1f, 5f)]
[SerializeField] private float _runMultiplier = 2f;
[Header("Ãðàâ³òàö³ÿ")]
[SerializeField] private float _gravity = -9.81f;
[Header("Ñèëà ñòðèáêó")]
[Range(1f, 20f)]
[SerializeField] private float _jumpHeight = 2f;
private CharacterController _characterController;
private Vector3 _controllerVelocity;
void Start()
{
_characterController = GetComponent<CharacterController>();
}
void Update()
{
HandleMovement();
HandleGravity();
HandleJump();
}
private void HandleMovement()
{
float moveX = Input.GetAxis("Horizontal");
float moveZ = Input.GetAxis("Vertical");
Vector3 movement = transform.right * moveX + transform.forward * moveZ;
float currentSpeed = _movementSpeed;
if (Input.GetKey(KeyCode.LeftShift))
{
currentSpeed *= _runMultiplier;
}
_characterController.Move(movement * currentSpeed * Time.deltaTime);
}
private void HandleGravity()
{
if (_characterController.isGrounded && _controllerVelocity.y < 0)
{
_controllerVelocity.y = -2f;
}
_controllerVelocity.y += _gravity * Time.deltaTime;
_characterController.Move(_controllerVelocity * Time.deltaTime);
}
private void HandleJump()
{
if (Input.GetButtonDown("Jump") && _characterController.isGrounded)
{
_controllerVelocity.y = Mathf.Sqrt(_jumpHeight * -2f * _gravity);
}
}
}

View File

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

View File

@@ -0,0 +1,39 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RaycastDebugge : MonoBehaviour
{
[SerializeField] private float distance = 200f;
[SerializeField] private LayerMask mask = ~0;
private Camera _camera;
void Awake()
{
_camera = GetComponent<Camera>();
if (_camera == null)
Debug.LogError("RaycastDebugger: logic error.");
}
void Update()
{
if (_camera == null) return;
Ray ray = _camera.ScreenPointToRay(Input.mousePosition);
Debug.DrawRay(ray.origin, ray.direction * 5f, Color.red);
if (Input.GetMouseButtonDown(0))
{
if (Physics.Raycast(ray, out RaycastHit hit, distance, mask, QueryTriggerInteraction.Collide))
{
Debug.Log($"CLICK HIT: {hit.collider.name} | tag={hit.collider.tag} | layer={LayerMask.LayerToName(hit.collider.gameObject.layer)} | dist={hit.distance:F2}");
}
else
{
Debug.Log("CLICK HIT: nothing");
}
}
}
}

View File

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

View File

@@ -0,0 +1,54 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RaycastDoor : MonoBehaviour
{
[SerializeField] private Camera _camera;
[SerializeField] private CursorSwitcher _cursorSwitcher;
[SerializeField] private float _speed = 3f;
[SerializeField] private float _openAngle = 81.087f;
[SerializeField] private float _closedAngle = 1.882f;
private bool _isOpen = false;
private Transform _doorPivot;
private Quaternion _targetRotation;
private bool _hasTarget = false;
void Update()
{
if (Input.GetMouseButtonDown(0))
{
Ray ray = _camera.ScreenPointToRay(Input.mousePosition);
RaycastHit[] hits = Physics.RaycastAll(ray, _cursorSwitcher.InteractDistance);
foreach (var hit in hits)
{
Transform current = hit.collider.transform;
while (current != null)
{
if (current.CompareTag("Door"))
{
_doorPivot = current.parent;
_isOpen = !_isOpen;
float angle = _isOpen ? _openAngle : _closedAngle;
_targetRotation = Quaternion.Euler(0f, angle, 0f);
_hasTarget = true;
return;
}
current = current.parent;
}
}
}
if (_hasTarget && _doorPivot != null)
{
_doorPivot.localRotation = Quaternion.Lerp(_doorPivot.localRotation, _targetRotation, Time.deltaTime * _speed);
if (Quaternion.Angle(_doorPivot.localRotation, _targetRotation) < 0.2f)
{
_doorPivot.localRotation = _targetRotation;
_hasTarget = false;
}
}
}
}

View File

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

View File

@@ -0,0 +1,12 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Rotation : MonoBehaviour
{
[SerializeField] private float _speed = 10f;
void Update()
{
transform.Rotate(Vector3.up, -_speed * Time.deltaTime, Space.World);
}
}

View File

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

View File

@@ -0,0 +1,54 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ScaleSurface : MonoBehaviour
{
[SerializeField] private Transform[] _slots;
private Transform[] _slotOccupied;
private void Awake()
{
_slotOccupied = new Transform[_slots.Length];
}
public void PlaceObject(Transform obj)
{
obj.SetParent(null);
for (int i = 0; i < _slotOccupied.Length; i++)
if (_slotOccupied[i] == obj)
_slotOccupied[i] = null;
for (int i = 0; i < _slots.Length; i++)
{
if (_slotOccupied[i] == null)
{
_slotOccupied[i] = obj;
Collider col = _slots[i].GetComponent<Collider>();
Vector3 targetPos = col != null ? col.bounds.center : _slots[i].position;
obj.position = targetPos;
obj.rotation = _slots[i].rotation;
MassController mc = GetComponentInParent<MassController>();
if (mc == null) mc = FindObjectOfType<MassController>();
if (mc != null)
mc.AddObject(obj.GetComponent<Rigidbody>());
return;
}
}
obj.SetParent(null, true);
}
public void RemoveObject(Transform obj)
{
for (int i = 0; i < _slotOccupied.Length; i++)
if (_slotOccupied[i] == obj)
_slotOccupied[i] = null;
}
}

View File

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

View File

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

View File

@@ -0,0 +1,14 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!134 &13400000
PhysicMaterial:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: Ball
dynamicFriction: 0.6
staticFriction: 0.6
bounciness: 0
frictionCombine: 3
bounceCombine: 1

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 1e3e49e7b8690b24f8e060e1f8f3f2b8
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 13400000
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,14 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CursorCustom : MonoBehaviour
{
[SerializeField] private Texture2D _cursorTexture;
[SerializeField] private Vector2 _hotspot = Vector2.zero;
private void Start()
{
Cursor.SetCursor(_cursorTexture, _hotspot, CursorMode.Auto);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ea6af50377e1dac498dd8c9337abf6bf
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 DraggableObject : MonoBehaviour
{
[HideInInspector] public float mass = 1f;
[HideInInspector] public bool isHeavy = false;
[HideInInspector] public Transform priorityTarget = null;
public Vector3 startPosition { get; private set; }
public Quaternion startRotation { get; private set; }
private Vector3 _mousePosition;
private Vector3 _mouseCameraPos;
private Vector3 _initialPosition;
private Rigidbody _rb;
private Camera _camera;
private Transform _scaleTarget;
private float _lerpPosition;
[HideInInspector] public bool isDragging;
[SerializeField] private float _minY = 0.2f;
[SerializeField] private float _maxDistanceFromStart = 30f;
private void Awake()
{
_rb = GetComponent<Rigidbody>();
}
private void Start()
{
_camera = Camera.main;
_rb.isKinematic = true;
startPosition = transform.position;
startRotation = transform.rotation;
}
private Vector3 GetMousePos()
{
return _camera != null ? _camera.WorldToScreenPoint(transform.position) : Vector3.zero;
}
private Transform GetNearestTargetToCursor()
{
if (priorityTarget != null && priorityTarget.gameObject.activeInHierarchy)
return priorityTarget;
var targets = new List<GameObject>();
targets.AddRange(GameObject.FindGameObjectsWithTag("ScaleTarget"));
targets.AddRange(GameObject.FindGameObjectsWithTag("MainScale"));
Transform nearest = null;
float minDist = float.MaxValue;
foreach (var t in targets)
{
Vector3 screenPos = _camera.WorldToScreenPoint(t.transform.position);
float d = Vector2.Distance(
new Vector2(Input.mousePosition.x, Input.mousePosition.y),
new Vector2(screenPos.x, screenPos.y));
if (d < minDist) { minDist = d; nearest = t.transform; }
}
return nearest;
}
private void OnMouseDown()
{
_rb.isKinematic = false;
_rb.useGravity = false;
_rb.velocity = Vector3.zero;
_scaleTarget = GetNearestTargetToCursor();
_initialPosition = transform.position;
_mousePosition = Input.mousePosition - GetMousePos();
isDragging = true;
}
private void Update()
{
if (!isDragging || _camera == null) return;
_rb.angularVelocity = Vector3.zero;
_mouseCameraPos = _camera.ScreenToWorldPoint(Input.mousePosition - _mousePosition);
float targetZ = _scaleTarget != null ? _scaleTarget.position.z : _initialPosition.z;
_lerpPosition = _scaleTarget != null
? Mathf.InverseLerp(_initialPosition.x, _scaleTarget.position.x, _mouseCameraPos.x)
: 0f;
transform.position = new Vector3(
_mouseCameraPos.x,
Mathf.Max(_mouseCameraPos.y, _minY),
Mathf.Lerp(_initialPosition.z, targetZ, _lerpPosition)
);
if (Input.GetMouseButtonUp(0))
{
isDragging = false;
_rb.useGravity = true;
_rb.isKinematic = false;
}
}
private void FixedUpdate()
{
if (isDragging) return;
if (Vector3.Distance(transform.position, startPosition) > _maxDistanceFromStart)
ResetToStart();
}
public void ResetToStart()
{
_rb.velocity = Vector3.zero;
_rb.angularVelocity = Vector3.zero;
_rb.isKinematic = true;
transform.position = startPosition;
transform.rotation = startRotation;
isDragging = false;
}
private void OnTriggerEnter(Collider other)
{
if (isDragging) return;
if (!other.CompareTag("ScalePan")) return;
StartCoroutine(SnapToPan(other.transform));
}
private IEnumerator SnapToPan(Transform pan)
{
_rb.isKinematic = true;
_rb.useGravity = false;
Vector3 targetPos = pan.position + Vector3.up * 0.1f;
float t = 0f;
Vector3 startPos = transform.position;
while (t < 1f)
{
t += Time.deltaTime * 5f;
transform.position = Vector3.Lerp(startPos, targetPos, t);
yield return null;
}
_rb.isKinematic = false;
_rb.useGravity = true;
}
}

View File

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

View File

@@ -0,0 +1,112 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
public class MainScaleZone : MonoBehaviour
{
[SerializeField] private GameObject _mainScaleObject;
[SerializeField] private Collider _mainScaleTrigger;
[SerializeField] private TextMeshProUGUI _resultText;
[SerializeField] private float _showResultTime = 2f;
private bool _isChecking = false;
private bool _canCheck = false;
private void Start()
{
_mainScaleObject.SetActive(false);
_resultText.gameObject.SetActive(false);
SetTag(false);
}
private void Update()
{
if (Input.GetMouseButtonDown(1))
{
if (_mainScaleObject.activeSelf)
{
_mainScaleObject.SetActive(false);
SetTag(false);
SetPriorityForAllBalls(null);
_canCheck = false;
}
else
{
_mainScaleObject.SetActive(true);
SetTag(true);
SetPriorityForAllBalls(_mainScaleTrigger.transform);
_canCheck = false;
StartCoroutine(EnableCheckDelay());
}
}
if (!_mainScaleObject.activeSelf || _isChecking || !_canCheck) return;
Collider[] cols = Physics.OverlapSphere(
_mainScaleTrigger.bounds.center,
_mainScaleTrigger.bounds.extents.magnitude
);
foreach (var col in cols)
{
DraggableObject ball = col.GetComponent<DraggableObject>();
if (ball == null) continue;
if (ball.isDragging) continue;
Rigidbody rb = col.GetComponent<Rigidbody>();
if (rb != null && rb.velocity.magnitude > 0.1f) continue;
_isChecking = true;
SetTag(false);
if (ball.isHeavy)
StartCoroutine(ShowResultAndRestart("³ðíî!"));
else
StartCoroutine(ShowResultAndContinue("Ñïðîáóé ùå"));
break;
}
}
private void SetPriorityForAllBalls(Transform target)
{
DraggableObject[] balls = FindObjectsOfType<DraggableObject>();
foreach (var ball in balls)
ball.priorityTarget = target;
}
private IEnumerator EnableCheckDelay()
{
yield return new WaitForSeconds(2f);
_canCheck = true;
}
private IEnumerator ShowResultAndRestart(string text)
{
NewScaleZone scaleZone = FindObjectOfType<NewScaleZone>();
scaleZone?.ShowSuccess();
_resultText.text = text;
_resultText.gameObject.SetActive(true);
yield return new WaitForSeconds(_showResultTime);
_mainScaleObject.SetActive(false);
_resultText.gameObject.SetActive(false);
SetPriorityForAllBalls(null);
FindObjectOfType<WeighingManager>()?.FullRestart();
scaleZone?.RestartAttempts();
_isChecking = false;
}
private IEnumerator ShowResultAndContinue(string text)
{
_resultText.text = text;
_resultText.gameObject.SetActive(true);
yield return new WaitForSeconds(_showResultTime);
_mainScaleObject.SetActive(false);
_resultText.gameObject.SetActive(false);
SetPriorityForAllBalls(null);
FindObjectOfType<WeighingManager>()?.ResetAllBalls();
FindObjectOfType<NewScaleZone>()?.RegisterAttempt();
_isChecking = false;
}
private void SetTag(bool active)
{
if (_mainScaleTrigger != null)
_mainScaleTrigger.gameObject.tag = active ? "MainScale" : "Untagged";
}
}

View File

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

View File

@@ -0,0 +1,161 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class NewScaleZone : MonoBehaviour
{
[Header("Scale Parts")]
[SerializeField] private Transform _chainsOriginLeft;
[SerializeField] private Transform _chainsOriginRight;
[SerializeField] private Transform _beam;
[Header("Zones")]
[SerializeField] private Collider _leftZoneCollider;
[SerializeField] private Collider _rightZoneCollider;
[Header("Button")]
[SerializeField] private Button _startButton;
[SerializeField] private Sprite _spriteStart;
[SerializeField] private Sprite _spriteInProgress;
[Header("UI")]
[SerializeField] private TMPro.TextMeshProUGUI _attemptText;
[Header("Settings")]
[SerializeField] private float _tiltSpeed = 1f;
[SerializeField] private float _maxTiltAngle = 15f;
[SerializeField] private float _maxChainOffset = 0.2f;
[SerializeField] private float _showResultTime = 1.5f;
private int _attempt = 1;
private Vector3 _beamStartRot;
private Vector3 _leftChainStartPos;
private Vector3 _rightChainStartPos;
private bool _isWeighing = false;
public int Attempt => _attempt;
private void Start()
{
_beamStartRot = _beam.localEulerAngles;
_leftChainStartPos = _chainsOriginLeft.localPosition;
_rightChainStartPos = _chainsOriginRight.localPosition;
_startButton.onClick.AddListener(OnStartPressed);
UpdateAttemptText();
}
private float GetMassInZone(Collider zone)
{
Collider[] colliders = Physics.OverlapBox(
zone.bounds.center,
zone.bounds.extents,
Quaternion.identity
);
float total = 0f;
foreach (var col in colliders)
{
if (col.gameObject == zone.gameObject) continue;
Rigidbody rb = col.GetComponent<Rigidbody>();
if (rb != null) total += rb.mass;
}
return total;
}
private void OnStartPressed()
{
if (_isWeighing) return;
StartCoroutine(WeighRoutine());
}
private IEnumerator WeighRoutine()
{
_isWeighing = true;
_startButton.image.sprite = _spriteInProgress;
float leftMass = GetMassInZone(_leftZoneCollider);
float rightMass = GetMassInZone(_rightZoneCollider);
float diff = rightMass - leftMass;
float normalized = Mathf.Clamp(diff / 2f, -1f, 1f);
float targetAngle = -normalized * _maxTiltAngle;
float elapsed = 0f;
float duration = 1.5f;
while (elapsed < duration)
{
elapsed += Time.deltaTime * _tiltSpeed;
float t = Mathf.Clamp01(elapsed / duration);
float angle = Mathf.Lerp(0f, targetAngle, t);
_beam.localEulerAngles = new Vector3(
_beamStartRot.x, _beamStartRot.y, _beamStartRot.z + angle);
float offset = normalized * _maxChainOffset * t;
_chainsOriginLeft.localPosition = new Vector3(
_leftChainStartPos.x, _leftChainStartPos.y - offset, _leftChainStartPos.z);
_chainsOriginRight.localPosition = new Vector3(
_rightChainStartPos.x, _rightChainStartPos.y + offset, _rightChainStartPos.z);
yield return null;
}
yield return new WaitForSeconds(_showResultTime);
elapsed = 0f;
while (elapsed < duration)
{
elapsed += Time.deltaTime * _tiltSpeed;
float t = Mathf.Clamp01(elapsed / duration);
float angle = Mathf.Lerp(targetAngle, 0f, t);
_beam.localEulerAngles = new Vector3(
_beamStartRot.x, _beamStartRot.y, _beamStartRot.z + angle);
float offset = normalized * _maxChainOffset * (1f - t);
_chainsOriginLeft.localPosition = new Vector3(
_leftChainStartPos.x, _leftChainStartPos.y - offset, _leftChainStartPos.z);
_chainsOriginRight.localPosition = new Vector3(
_rightChainStartPos.x, _rightChainStartPos.y + offset, _rightChainStartPos.z);
yield return null;
}
_beam.localEulerAngles = _beamStartRot;
_chainsOriginLeft.localPosition = _leftChainStartPos;
_chainsOriginRight.localPosition = _rightChainStartPos;
WeighingManager manager = FindObjectOfType<WeighingManager>();
if (manager != null) manager.ResetAllBalls();
_attempt++;
UpdateAttemptText();
_startButton.image.sprite = _spriteStart;
_isWeighing = false;
}
private void UpdateAttemptText()
{
if (_attemptText != null)
_attemptText.text = "Ñïðîáà " + _attempt;
}
public void ShowSuccess()
{
int count = _attempt <= 1 ? 1 : _attempt - 1;
if (_attemptText != null)
_attemptText.text = "Ïðîéäåíî ç " + count + " ñïðîá!";
}
public void RegisterAttempt()
{
if (_isWeighing) return;
_attempt++;
UpdateAttemptText();
}
public void RestartAttempts()
{
_attempt = 1;
UpdateAttemptText();
}
}

View File

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

View File

@@ -0,0 +1,42 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class WeighingManager : MonoBehaviour
{
[SerializeField] private DraggableObject[] _balls;
[SerializeField] private float _normalMass = 1f;
[SerializeField] private float _heavyMass = 1.5f;
private void Start()
{
AssignMasses();
}
public void AssignMasses()
{
foreach (var ball in _balls)
{
ball.mass = _normalMass;
ball.isHeavy = false;
ball.GetComponent<Rigidbody>().mass = _normalMass;
}
int heavyIndex = Random.Range(0, _balls.Length);
_balls[heavyIndex].mass = _heavyMass;
_balls[heavyIndex].isHeavy = true;
_balls[heavyIndex].GetComponent<Rigidbody>().mass = _heavyMass;
}
public void ResetAllBalls()
{
foreach (var ball in _balls)
ball.ResetToStart();
}
public void FullRestart()
{
foreach (var ball in _balls)
ball.ResetToStart();
AssignMasses();
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 61ea5660dd2544b4fbe62b90469e654e
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 ScaleZone : MonoBehaviour
{
private List<Rigidbody> _objects = new List<Rigidbody>();
private void OnTriggerEnter(Collider other)
{
Rigidbody rb = other.GetComponentInParent<Rigidbody>();
if (rb == null) return;
if (!rb.CompareTag("Target")) return;
if (!_objects.Contains(rb))
_objects.Add(rb);
}
private void OnTriggerExit(Collider other)
{
Rigidbody rb = other.GetComponentInParent<Rigidbody>();
if (rb == null) return;
_objects.Remove(rb);
}
public float TotalWeight
{
get
{
float total = 0f;
foreach (var rb in _objects)
{
if (rb != null)
total += rb.mass;
}
return total;
}
}
}

View File

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

15
Assets/Scripts/UIText.cs Normal file
View File

@@ -0,0 +1,15 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class UIText : MonoBehaviour
{
[SerializeField] private float _yRotation = -180f;
[SerializeField] private float _xRotation = 53.483f;
void LateUpdate()
{
transform.rotation = Quaternion.Euler(_xRotation, _yRotation, 0f);
}
}

View File

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