first commit
This commit is contained in:
8
Assets/Scripts/Asrtronaut.meta
Normal file
8
Assets/Scripts/Asrtronaut.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e023903994c4036408fd2c4bc2de05a7
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
56
Assets/Scripts/Asrtronaut/AstronautAnimator.cs
Normal file
56
Assets/Scripts/Asrtronaut/AstronautAnimator.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/Asrtronaut/AstronautAnimator.cs.meta
Normal file
11
Assets/Scripts/Asrtronaut/AstronautAnimator.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4a7b2628c1767c64f8b1c59620ac9153
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
204
Assets/Scripts/Asrtronaut/AstronautController.cs
Normal file
204
Assets/Scripts/Asrtronaut/AstronautController.cs
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
11
Assets/Scripts/Asrtronaut/AstronautController.cs.meta
Normal file
11
Assets/Scripts/Asrtronaut/AstronautController.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cc8b6ef8dd6ffc64b95ee2cbea5060ac
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
25
Assets/Scripts/Asrtronaut/CameraFollow.cs
Normal file
25
Assets/Scripts/Asrtronaut/CameraFollow.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/Asrtronaut/CameraFollow.cs.meta
Normal file
11
Assets/Scripts/Asrtronaut/CameraFollow.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9969e7e7ef928ad4f91ef1baf675454d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
162
Assets/Scripts/Asrtronaut/CameraTransition.cs
Normal file
162
Assets/Scripts/Asrtronaut/CameraTransition.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/Asrtronaut/CameraTransition.cs.meta
Normal file
11
Assets/Scripts/Asrtronaut/CameraTransition.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 10bffe552295e6f458dec97634b9fcec
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
28
Assets/Scripts/Asrtronaut/PlanetDropdown.cs
Normal file
28
Assets/Scripts/Asrtronaut/PlanetDropdown.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/Asrtronaut/PlanetDropdown.cs.meta
Normal file
11
Assets/Scripts/Asrtronaut/PlanetDropdown.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4fd961003a4484e4695c1ed8bd9b4422
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
61
Assets/Scripts/Asrtronaut/PlanetLighting.cs
Normal file
61
Assets/Scripts/Asrtronaut/PlanetLighting.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/Asrtronaut/PlanetLighting.cs.meta
Normal file
11
Assets/Scripts/Asrtronaut/PlanetLighting.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0a564b0668864d74db51987ffa73db47
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
89
Assets/Scripts/Asrtronaut/PlanetWeight.cs
Normal file
89
Assets/Scripts/Asrtronaut/PlanetWeight.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/Asrtronaut/PlanetWeight.cs.meta
Normal file
11
Assets/Scripts/Asrtronaut/PlanetWeight.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5a3b19f6fbf62354185f8be3c13b58c8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
32
Assets/Scripts/Asrtronaut/SecretDropdown.cs
Normal file
32
Assets/Scripts/Asrtronaut/SecretDropdown.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/Asrtronaut/SecretDropdown.cs.meta
Normal file
11
Assets/Scripts/Asrtronaut/SecretDropdown.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4254f48f2352dd24c9a198caadfa13c4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
88
Assets/Scripts/Asrtronaut/SecretPlanetWeight.cs
Normal file
88
Assets/Scripts/Asrtronaut/SecretPlanetWeight.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/Asrtronaut/SecretPlanetWeight.cs.meta
Normal file
11
Assets/Scripts/Asrtronaut/SecretPlanetWeight.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 64282c7b6bc4c73498089be8b1864a09
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
106
Assets/Scripts/BalanceScale.cs
Normal file
106
Assets/Scripts/BalanceScale.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/BalanceScale.cs.meta
Normal file
11
Assets/Scripts/BalanceScale.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bfc0a966c40322b45acc8e069363c627
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
114
Assets/Scripts/BeakerContainer.cs
Normal file
114
Assets/Scripts/BeakerContainer.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/BeakerContainer.cs.meta
Normal file
11
Assets/Scripts/BeakerContainer.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2918f10a74fa9c74499cea70432b5ec0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
45
Assets/Scripts/Breakable.cs
Normal file
45
Assets/Scripts/Breakable.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/Breakable.cs.meta
Normal file
11
Assets/Scripts/Breakable.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d3abec6b9d1f18044bd57797f34d69e9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
35
Assets/Scripts/CameraController.cs
Normal file
35
Assets/Scripts/CameraController.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/CameraController.cs.meta
Normal file
11
Assets/Scripts/CameraController.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a79b38e2cfec7d44abc20a01d97dc3d6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
98
Assets/Scripts/CursorSwitcher.cs
Normal file
98
Assets/Scripts/CursorSwitcher.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/CursorSwitcher.cs.meta
Normal file
11
Assets/Scripts/CursorSwitcher.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 130edb9c2206bfe498f679728ddcc71e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
57
Assets/Scripts/DisappearOnGround.cs
Normal file
57
Assets/Scripts/DisappearOnGround.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/DisappearOnGround.cs.meta
Normal file
11
Assets/Scripts/DisappearOnGround.cs.meta
Normal 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
54
Assets/Scripts/Drawer.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/Drawer.cs.meta
Normal file
11
Assets/Scripts/Drawer.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c0be759a41b8d3f49a273c0e84a7eae5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
32
Assets/Scripts/DrawerContent.cs
Normal file
32
Assets/Scripts/DrawerContent.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/DrawerContent.cs.meta
Normal file
11
Assets/Scripts/DrawerContent.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b2c71ac03cf4018439bf0c92770ffbdb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
27
Assets/Scripts/GridGroup.cs
Normal file
27
Assets/Scripts/GridGroup.cs
Normal 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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/GridGroup.cs.meta
Normal file
11
Assets/Scripts/GridGroup.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b272a56153ebcc74abb52d8fc6fca994
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
260
Assets/Scripts/HandController.cs
Normal file
260
Assets/Scripts/HandController.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/HandController.cs.meta
Normal file
11
Assets/Scripts/HandController.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d89b98cd6b620fc4c93052ebec2e8d39
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
55
Assets/Scripts/KnobController.cs
Normal file
55
Assets/Scripts/KnobController.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/KnobController.cs.meta
Normal file
11
Assets/Scripts/KnobController.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 789e3bae5e67be74b8d6c74638cc2c26
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
116
Assets/Scripts/LiquidContainer.cs
Normal file
116
Assets/Scripts/LiquidContainer.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/LiquidContainer.cs.meta
Normal file
11
Assets/Scripts/LiquidContainer.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ff1b86068fc363241861b40a7bff4f59
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
62
Assets/Scripts/LiquidPourer.cs
Normal file
62
Assets/Scripts/LiquidPourer.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/LiquidPourer.cs.meta
Normal file
11
Assets/Scripts/LiquidPourer.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 95bcb526c45649949945a23dc8947cd1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
104
Assets/Scripts/MassController.cs
Normal file
104
Assets/Scripts/MassController.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/MassController.cs.meta
Normal file
11
Assets/Scripts/MassController.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f77fadce4ff62ee489e5d1ce3fabb853
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
70
Assets/Scripts/PlayerController.cs
Normal file
70
Assets/Scripts/PlayerController.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/PlayerController.cs.meta
Normal file
11
Assets/Scripts/PlayerController.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 20794c24accfb184bbd79fbb16d91181
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
39
Assets/Scripts/RaycastDebugge.cs
Normal file
39
Assets/Scripts/RaycastDebugge.cs
Normal 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/RaycastDebugge.cs.meta
Normal file
11
Assets/Scripts/RaycastDebugge.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8e1db6f281c5c444d962703e1af915aa
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
54
Assets/Scripts/RaycastDoor.cs
Normal file
54
Assets/Scripts/RaycastDoor.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/RaycastDoor.cs.meta
Normal file
11
Assets/Scripts/RaycastDoor.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 647684ac0e343704b88bc8a5c88426c4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
12
Assets/Scripts/Rotation.cs
Normal file
12
Assets/Scripts/Rotation.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/Rotation.cs.meta
Normal file
11
Assets/Scripts/Rotation.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 10ad79ac05e32694cb9ef3ed95683ef9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
54
Assets/Scripts/ScaleSurface.cs
Normal file
54
Assets/Scripts/ScaleSurface.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/ScaleSurface.cs.meta
Normal file
11
Assets/Scripts/ScaleSurface.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9b675618a29727c469a84f5d938c4f3e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Scripts/ScaleTest.meta
Normal file
8
Assets/Scripts/ScaleTest.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 68c658172411a364fb2855a91c2b9005
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
14
Assets/Scripts/ScaleTest/Ball.physicMaterial
Normal file
14
Assets/Scripts/ScaleTest/Ball.physicMaterial
Normal 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
|
||||
8
Assets/Scripts/ScaleTest/Ball.physicMaterial.meta
Normal file
8
Assets/Scripts/ScaleTest/Ball.physicMaterial.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1e3e49e7b8690b24f8e060e1f8f3f2b8
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 13400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
14
Assets/Scripts/ScaleTest/CursorCustom.cs
Normal file
14
Assets/Scripts/ScaleTest/CursorCustom.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/ScaleTest/CursorCustom.cs.meta
Normal file
11
Assets/Scripts/ScaleTest/CursorCustom.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ea6af50377e1dac498dd8c9337abf6bf
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
146
Assets/Scripts/ScaleTest/DraggableObject.cs
Normal file
146
Assets/Scripts/ScaleTest/DraggableObject.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/ScaleTest/DraggableObject.cs.meta
Normal file
11
Assets/Scripts/ScaleTest/DraggableObject.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2355647c3dcd1024db76db0ee01add9a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
112
Assets/Scripts/ScaleTest/MainScaleZone.cs
Normal file
112
Assets/Scripts/ScaleTest/MainScaleZone.cs
Normal 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";
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/ScaleTest/MainScaleZone.cs.meta
Normal file
11
Assets/Scripts/ScaleTest/MainScaleZone.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0da993b310702744097f97f6222567ad
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
161
Assets/Scripts/ScaleTest/NewScaleZone.cs
Normal file
161
Assets/Scripts/ScaleTest/NewScaleZone.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/ScaleTest/NewScaleZone.cs.meta
Normal file
11
Assets/Scripts/ScaleTest/NewScaleZone.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2dda24731fdf5b242b6dcd09c60a7e1a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
42
Assets/Scripts/ScaleTest/WeighingManager.cs
Normal file
42
Assets/Scripts/ScaleTest/WeighingManager.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/ScaleTest/WeighingManager.cs.meta
Normal file
11
Assets/Scripts/ScaleTest/WeighingManager.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 61ea5660dd2544b4fbe62b90469e654e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
43
Assets/Scripts/ScaleZone.cs
Normal file
43
Assets/Scripts/ScaleZone.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/ScaleZone.cs.meta
Normal file
11
Assets/Scripts/ScaleZone.cs.meta
Normal 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
15
Assets/Scripts/UIText.cs
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
11
Assets/Scripts/UIText.cs.meta
Normal file
11
Assets/Scripts/UIText.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eb2290dead5638f4597708437b0b47c1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user