StellarXipher/Assets/Puzzles/Untangle/Scripts/UntangleGame.cs
EthanPisani 457d850e0e
All checks were successful
Build project / Build for (StandaloneLinux64, 6000.0.37f1) (push) Successful in 5m1s
Build project / Build for (StandaloneWindows64, 6000.0.37f1) (push) Successful in 5m41s
Build project / Publish to itch.io (StandaloneLinux64) (push) Successful in 5s
Build project / Publish to itch.io (StandaloneWindows64) (push) Successful in 6s
add door animations and puzzles level2
2025-03-27 18:29:36 -04:00

348 lines
9.6 KiB
C#

using UnityEngine;
using UnityEngine.UI;
using System.Collections.Generic;
using TMPro;
public class UntangleGame : MonoBehaviour
{
public class Point
{
public float x, y;
public GameObject visual;
public int index;
}
public class Edge
{
public int a, b; // indices of connected points
public bool isCrossed;
public LineRenderer line;
}
[Header("Game Objects")]
public GameObject pointPrefab;
public LineRenderer edgePrefab;
[Header("Game Settings")]
public int pointCount = 10;
public float playAreaSize = 8f;
[Header("Colors")]
public Color normalEdgeColor = Color.black;
public Color crossedEdgeColor = Color.red;
public Color pointColor = Color.blue;
public Color dragPointColor = Color.white;
public Color cursorPointColor = Color.gray;
public Color neighborPointColor = Color.red;
[Header("Visual Settings")]
public float pointRadius = 0.2f;
public float dragThreshold = 0.5f;
[Header("Options")]
public bool snapToGrid = false;
public bool showCrossedEdges = true;
public bool showVertexNumbers = false;
private List<Point> points = new List<Point>();
private List<Edge> edges = new List<Edge>();
private int draggedPoint = -1;
private int cursorPoint = -1;
private bool isSolved = false;
private Camera mainCamera;
private Material lineMaterial;
void Start()
{
mainCamera = Camera.main;
// Create material for lines to prevent pink color
lineMaterial = new Material(Shader.Find("UI/Default"));
lineMaterial.color = normalEdgeColor;
GenerateGame();
}
void GenerateGame()
{
// Clear existing game
foreach (Point p in points)
{
if (p.visual != null) Destroy(p.visual);
}
foreach (Edge e in edges)
{
if (e.line != null) Destroy(e.line.gameObject);
}
points.Clear();
edges.Clear();
isSolved = false;
// Create points in a circle (local space)
for (int i = 0; i < pointCount; i++)
{
float angle = i * 2 * Mathf.PI / pointCount;
Point p = new Point
{
x = Mathf.Sin(angle) * playAreaSize * 0.4f,
y = Mathf.Cos(angle) * playAreaSize * 0.4f,
index = i
};
points.Add(p);
}
// Create edges (simplified planar graph)
for (int i = 0; i < pointCount; i++)
{
int connections = Mathf.Min(3, pointCount - i - 1);
for (int j = 1; j <= connections; j++)
{
int k = (i + j) % pointCount;
edges.Add(new Edge { a = i, b = k });
}
}
CreateVisuals();
CheckCrossings();
}
void CreateVisuals()
{
// Create point visuals
foreach (Point p in points)
{
p.visual = Instantiate(pointPrefab, transform);
p.visual.transform.localPosition = new Vector3(p.x, p.y, 0);
p.visual.transform.localScale = Vector3.one * pointRadius * 2;
if (showVertexNumbers)
{
Text text = p.visual.GetComponentInChildren<Text>();
if (text != null)
{
text.text = p.index.ToString();
text.enabled = showVertexNumbers;
}
}
}
// Create edge visuals
foreach (Edge e in edges)
{
e.line = Instantiate(edgePrefab, transform);
e.line.useWorldSpace = false;
e.line.material = lineMaterial;
e.line.startWidth = 0.1f;
e.line.endWidth = 0.1f;
UpdateEdgeVisual(e);
}
}
void UpdateEdgeVisual(Edge e)
{
Point a = points[e.a];
Point b = points[e.b];
e.line.startColor = e.isCrossed && showCrossedEdges ? crossedEdgeColor : normalEdgeColor;
e.line.endColor = e.line.startColor;
e.line.SetPosition(0, new Vector3(a.x, a.y, 0));
e.line.SetPosition(1, new Vector3(b.x, b.y, 0));
}
void Update()
{
HandleInput();
UpdateVisuals();
}
void HandleInput()
{
Vector2 localMousePos;
RectTransformUtility.ScreenPointToLocalPointInRectangle(
GetComponent<RectTransform>(),
Input.mousePosition,
mainCamera,
out localMousePos);
Debug.Log($"Mouse Position: {localMousePos}");
if (Input.GetMouseButtonDown(0))
{
int closest = FindClosestPoint(localMousePos);
if (closest != -1) draggedPoint = closest;
}
else if (Input.GetMouseButton(0) && draggedPoint != -1)
{
if (snapToGrid)
{
float gridSize = playAreaSize / (pointCount - 1);
points[draggedPoint].x = Mathf.Round(localMousePos.x / gridSize) * gridSize;
points[draggedPoint].y = Mathf.Round(localMousePos.y / gridSize) * gridSize;
}
else
{
points[draggedPoint].x = localMousePos.x;
points[draggedPoint].y = localMousePos.y;
}
// Keep within bounds
points[draggedPoint].x = Mathf.Clamp(points[draggedPoint].x, -playAreaSize/2, playAreaSize/2);
points[draggedPoint].y = Mathf.Clamp(points[draggedPoint].y, -playAreaSize/2, playAreaSize/2);
CheckCrossings();
}
else if (Input.GetMouseButtonUp(0))
{
draggedPoint = -1;
}
else
{
cursorPoint = FindClosestPoint(localMousePos);
}
}
int FindClosestPoint(Vector2 localPosition)
{
int closest = -1;
float minDist = float.MaxValue;
for (int i = 0; i < points.Count; i++)
{
float dist = Vector2.Distance(localPosition, new Vector2(points[i].x, points[i].y));
Debug.Log($"Point {i}: Distance {dist}");
if (dist < dragThreshold && dist < minDist)
{
minDist = dist;
closest = i;
}
}
Debug.Log($"Closest point: {closest} at distance {minDist}");
return closest;
}
void UpdateVisuals()
{
// Update point positions
for (int i = 0; i < points.Count; i++)
{
Point p = points[i];
p.visual.transform.localPosition = new Vector3(p.x, p.y, 0);
// Update point color
Image img = p.visual.GetComponent<Image>();
if (img != null)
{
if (i == draggedPoint)
img.color = dragPointColor;
else if (i == cursorPoint)
img.color = cursorPointColor;
else if (draggedPoint != -1 && IsConnected(draggedPoint, i))
img.color = neighborPointColor;
else
img.color = pointColor;
}
}
// Update edges
foreach (Edge e in edges)
{
UpdateEdgeVisual(e);
}
}
bool IsConnected(int a, int b)
{
foreach (Edge e in edges)
{
if ((e.a == a && e.b == b) || (e.a == b && e.b == a))
return true;
}
return false;
}
void CheckCrossings()
{
bool anyCrossings = false;
// Reset all edges
foreach (Edge e in edges)
{
e.isCrossed = false;
}
// Check all edge pairs for crossings
for (int i = 0; i < edges.Count; i++)
{
Edge e1 = edges[i];
Point a1 = points[e1.a];
Point a2 = points[e1.b];
for (int j = i + 1; j < edges.Count; j++)
{
Edge e2 = edges[j];
Point b1 = points[e2.a];
Point b2 = points[e2.b];
// Skip if edges share a point
if (e1.a == e2.a || e1.a == e2.b || e1.b == e2.a || e1.b == e2.b)
continue;
if (DoLinesIntersect(a1, a2, b1, b2))
{
e1.isCrossed = true;
e2.isCrossed = true;
anyCrossings = true;
}
}
}
isSolved = !anyCrossings;
}
bool DoLinesIntersect(Point a1, Point a2, Point b1, Point b2)
{
Vector2 p1 = new Vector2(a1.x, a1.y);
Vector2 p2 = new Vector2(a2.x, a2.y);
Vector2 p3 = new Vector2(b1.x, b1.y);
Vector2 p4 = new Vector2(b2.x, b2.y);
float d = (p2.x - p1.x) * (p4.y - p3.y) - (p2.y - p1.y) * (p4.x - p3.x);
if (d == 0) return false;
float u = ((p3.x - p1.x) * (p4.y - p3.y) - (p3.y - p1.y) * (p4.x - p3.x)) / d;
float v = ((p3.x - p1.x) * (p2.y - p1.y) - (p3.y - p1.y) * (p2.x - p1.x)) / d;
return (u >= 0) && (u <= 1) && (v >= 0) && (v <= 1);
}
public void OnSnapToGridToggle(bool value)
{
snapToGrid = value;
}
public void OnShowCrossedEdgesToggle(bool value)
{
showCrossedEdges = value;
foreach (Edge e in edges)
{
UpdateEdgeVisual(e);
}
}
public void OnVertexNumbersToggle(bool value)
{
showVertexNumbers = value;
foreach (Point p in points)
{
Text text = p.visual.GetComponentInChildren<Text>();
if (text != null)
{
text.enabled = value;
}
}
}
public void OnRegenerateClick()
{
GenerateGame();
}
}