Lock Puzzle Controller Script
unknown
plain_text
3 years ago
7.5 kB
24
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...