137 lines
4.5 KiB
C#
137 lines
4.5 KiB
C#
using UnityEngine;
|
|
using UnityEngine.Events;
|
|
using Obi;
|
|
|
|
[RequireComponent(typeof(ObiSolver))]
|
|
public class TangledRopesGameController : MonoBehaviour
|
|
{
|
|
public TangledPegSlot[] pegSlots;
|
|
public float pegHoverHeight = 1;
|
|
public float maxPegDistanceFromSlot = 1.5f;
|
|
public int framesWithoutContactsToWin = 30;
|
|
public UnityEvent onFinish = new UnityEvent();
|
|
|
|
private TangledPeg selectedPeg;
|
|
private Plane floor = new Plane(Vector3.up, 0);
|
|
private int framesSinceLastContact = 0;
|
|
|
|
void OnEnable()
|
|
{
|
|
GetComponent<ObiSolver>().OnParticleCollision += Solver_OnParticleCollision;
|
|
}
|
|
|
|
private void OnDisable()
|
|
{
|
|
GetComponent<ObiSolver>().OnParticleCollision -= Solver_OnParticleCollision;
|
|
}
|
|
|
|
private TangledPegSlot FindCandidateSlot(TangledPeg peg)
|
|
{
|
|
// Go over all slots, find the closest one to the peg that's closer than
|
|
// maxPegDistanceFromSlot:
|
|
|
|
TangledPegSlot closest = null;
|
|
float closestDistance = float.MaxValue;
|
|
|
|
foreach (TangledPegSlot slot in pegSlots)
|
|
{
|
|
// reset slot color, to make sure it looks normal if it's not a candidate for our peg.
|
|
slot.ResetColor();
|
|
|
|
// ignore occupied slots:
|
|
if (slot.currentPeg != null)
|
|
continue;
|
|
|
|
Vector3 slotOnFloor = floor.ClosestPointOnPlane(slot.transform.position);
|
|
Vector3 pegOnFloor = floor.ClosestPointOnPlane(peg.transform.position);
|
|
|
|
float distance = Vector3.Distance(slotOnFloor, pegOnFloor);
|
|
if (distance < closestDistance && distance < maxPegDistanceFromSlot)
|
|
{
|
|
closest = slot;
|
|
closestDistance = distance;
|
|
}
|
|
}
|
|
|
|
return closest;
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
|
|
|
|
// User clicks, cast a ray towards the floor, see if it hits any peg.
|
|
if (Input.GetMouseButtonDown(0))
|
|
{
|
|
if (Physics.Raycast(ray, out RaycastHit hit))
|
|
{
|
|
// if the ray hit a peg, store it as the selected peg and lift it off from its current slot.
|
|
if (hit.transform.TryGetComponent(out TangledPeg peg) && peg.currentSlot != null)
|
|
{
|
|
selectedPeg = peg;
|
|
selectedPeg.UndockFromCurrentSlot();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (selectedPeg != null)
|
|
{
|
|
// Make selected peg follow the mouse cursor:
|
|
if (floor.Raycast(ray, out float enter))
|
|
selectedPeg.MoveTowards(ray.GetPoint(enter) + Vector3.up * pegHoverHeight);
|
|
|
|
// Try to find a suitable slot to drop the peg:
|
|
TangledPegSlot closest = FindCandidateSlot(selectedPeg);
|
|
|
|
// If we found a candidate slot, tint it to give the player some visual feedback:
|
|
if (closest != null)
|
|
closest.Tint();
|
|
|
|
// Drop selected peg:
|
|
if (Input.GetMouseButtonUp(0))
|
|
{
|
|
// If we could find a free slot nearby, connect to it:
|
|
if (closest != null)
|
|
{
|
|
selectedPeg.currentSlot = null;
|
|
selectedPeg.DockInSlot(closest);
|
|
closest.ResetColor();
|
|
}
|
|
else // If we couldn't find one or if it's too far, return to current slot.
|
|
{
|
|
selectedPeg.DockInSlot(selectedPeg.currentSlot);
|
|
}
|
|
|
|
selectedPeg = null;
|
|
}
|
|
}
|
|
|
|
// If all ropes have been contact-free for a certain amount of frames, trigger finish event.
|
|
if (framesSinceLastContact >= framesWithoutContactsToWin && onFinish != null)
|
|
onFinish.Invoke();
|
|
|
|
}
|
|
|
|
private void Solver_OnParticleCollision(ObiSolver s, ObiNativeContactList e)
|
|
{
|
|
// Count contacts between different ropes (that is, exclude self-contacts):
|
|
int contactsBetweenRopes = 0;
|
|
|
|
for (int i = 0; i < e.count; ++i)
|
|
{
|
|
var ropeA = s.particleToActor[s.simplices[e[i].bodyA]].actor;
|
|
var ropeB = s.particleToActor[s.simplices[e[i].bodyB]].actor;
|
|
|
|
if (ropeA != ropeB)
|
|
contactsBetweenRopes++;
|
|
}
|
|
|
|
// If there's no contacts, bump the amount of frames we've been contact-free.
|
|
// Otherwise reset the amount of frames to zero.
|
|
if (contactsBetweenRopes == 0)
|
|
framesSinceLastContact++;
|
|
else
|
|
framesSinceLastContact = 0;
|
|
}
|
|
}
|