Lock Puzzle Controller Script
unknown
plain_text
2 years ago
7.5 kB
17
Indexable
using System; using System.Collections; using System.Collections.Generic; using System.Linq; using DG.Tweening; using UnityEngine; using UnityEngine.Events; using UnityEngine.EventSystems; public class LockerController : MonoBehaviour, IPointerDownHandler, IPointerUpHandler { [SerializeField, Tooltip("The Z rotation for which the locker current number should be 0")] private float rotationOffset = 297; [SerializeField, Tooltip("The amount of numbers or positions the locker has")] private int numbersQuantity = 40; [SerializeField, Tooltip("The time for the handle to rotate")] private float rotationTime = 0.5f; [SerializeField, Tooltip("The time for the handle to rotate on resetting")] private float resetRotationTime = 2f; [SerializeField, Tooltip("The amount of units the mouse has to be moved before moving the number")] private float mouseRequiredAngle = 45; [SerializeField] private int firstNumber; private bool isDragging = false; public UnityEvent OnCorrectNumber; public UnityEvent OnWrongNumber; public UnityEvent OnPuzzleSolved; public UnityEvent OnReset; private Camera cam; private int previousNumber; private int currentNumber; private int CurrentNumber { get { return currentNumber; // float value = -(Angle - rotationOffset) / NumberAngle % numbersQuantity + numbersQuantity; // if (value == numbersQuantity) // value = 0; // return (int)value; } set { if (value == CurrentNumber) return; DangerouslySetCurrentNumber(value); OnNumberChange.Invoke(currentNumber); } } private void DangerouslySetCurrentNumber(int value) { int temp = value % numbersQuantity; if (temp < 0) temp += numbersQuantity; currentNumber = temp; } private float Angle => transform.rotation.eulerAngles.z; private float NumberAngle => 360f / numbersQuantity; private Action<int> OnNumberChange; private int[] target = null; private bool[] status = {false, false, false, false}; private readonly bool[] direction = {false, true, false, true}; // true = clockwise private bool wasMovingClockwise = false; private bool IsMovingClockwise() { if (previousNumber == (numbersQuantity - 1) && CurrentNumber == 0) return true; if (CurrentNumber == (numbersQuantity - 1) && previousNumber == 0) return false; return CurrentNumber > previousNumber; } private void ResetStatus() => status = new [] {false, false, false, false}; private void Puzzle() { if (previousNumber == CurrentNumber) return; bool isMovingClockwise = IsMovingClockwise(); bool wasMovingClockwiseTemp = wasMovingClockwise; wasMovingClockwise = isMovingClockwise; if (status[status.Length - 2]) // waiting for the last one { if (isMovingClockwise == direction[direction.Length - 1] && CurrentNumber == target[target.Length - 1]) { status[status.Length - 1] = true; OnPuzzleSolved.Invoke(); } } if (wasMovingClockwiseTemp == isMovingClockwise) return; // moving to the same side int targetIndex = Array.IndexOf(status, false); if (previousNumber != target[targetIndex]) { ResetStatus(); return; } if (wasMovingClockwiseTemp != direction[targetIndex]) { ResetStatus(); return; } status[targetIndex] = true; } private Vector2 originPosition; private void Awake() { OnNumberChange += DoRotation; OnNumberChange += PlayClick; cam = Camera.main; } private void OnEnable() { originPosition = transform.position; SetTarget(); } private void SetTarget() { if (target != null) return; if (Game.Current.LockPuzzleTarget != null) { target = Game.Current.LockPuzzleTarget; return; } target = new int[4]; target[0] = firstNumber; System.Random random = new System.Random(); for (int i = 1; i < target.Length; i++) { int proposed; do proposed = random.Next(0, numbersQuantity); while (target.Contains(proposed)); target[i] = proposed; } Game.Current.LockPuzzleTarget = target; } private void PlayClick(int number) { int targetIndex = Array.IndexOf(status, false); if (number == target[targetIndex] && IsMovingClockwise() == direction[targetIndex]) OnCorrectNumber.Invoke(); else OnWrongNumber.Invoke(); } private static float ConvertTo360(float angle) { if (angle < 0) angle += 360; if (angle >= 360) angle -= 360; return angle; } private static int Floor(float number) { if (number < 0) return -1 * Mathf.FloorToInt(number * -1); return Mathf.FloorToInt(number); } private void DoRotation(int targetNumber) => DoRotation(targetNumber, null); private void DoRotation(int targetNumber, float? customRotationTime) { float tempRotationTime = customRotationTime ?? rotationTime; float targetAngle = (rotationOffset - targetNumber * NumberAngle); targetAngle = ConvertTo360(targetAngle); Vector3 endValue = Vector3.forward * targetAngle; transform.DORotate(endValue, tempRotationTime, RotateMode.Fast).SetEase(Ease.Linear); } private void Update() { HandleDragging(); Puzzle(); previousNumber = CurrentNumber; // for debug only current = CurrentNumber; } private Vector2 initialMouseVector; private void HandleDragging() { if (!isDragging) return; Vector2 newPos = cam.ScreenToWorldPoint(Input.mousePosition); Vector2 newMouseVector = newPos - originPosition; float deltaAngle = Vector2.SignedAngle(initialMouseVector, newMouseVector); int deltaNumber = -1 * Floor(deltaAngle / mouseRequiredAngle); CurrentNumber += deltaNumber; if (deltaNumber != 0) initialMouseVector = newMouseVector; } public void Reset() { DangerouslySetCurrentNumber(0); DoRotation(0, resetRotationTime); ResetStatus(); OnReset?.Invoke(); } public void OnPointerDown(PointerEventData eventData) { if (eventData.button == PointerEventData.InputButton.Left) { Vector2 initialMousePos = cam.ScreenToWorldPoint(Input.mousePosition); initialMouseVector = initialMousePos - originPosition; isDragging = true; } else { isDragging = false; } } public void OnPointerUp(PointerEventData eventData) { isDragging = false; } [Header("Debug")] [SerializeField] private int current; }
Editor is loading...