Major changes: - Map: 80x40 (was 38x18) - requires zoom/pan to navigate - CameraController: pinch-to-zoom, drag-to-pan, scroll wheel zoom - Neighborhood layout: streets grid with house rows, backyards, fences - All gaps minimum 2.5 units wide for unit passage - Streets visible on ground (gray paths) Bug fixes: - Tagging: only captured unit goes to jail, never the instigator - Collision handled once (by lower instance ID) - Respawn offset fixed for landscape mode New constants: - CameraMinZoom/MaxZoom/StartZoom - MinGapSize, StreetWidth - VisionRadius increased to 6 (larger map) Co-Authored-By: Claude <noreply@anthropic.com>
188 lines
5.3 KiB
C#
188 lines
5.3 KiB
C#
using UnityEngine;
|
|
using UnityEngine.InputSystem;
|
|
|
|
public class CameraController : MonoBehaviour
|
|
{
|
|
// Zoom settings
|
|
public float minZoom = 5f;
|
|
public float maxZoom = 25f;
|
|
public float zoomSpeed = 2f;
|
|
public float pinchZoomSpeed = 0.1f;
|
|
|
|
// Pan settings
|
|
public float panSpeed = 1f;
|
|
|
|
Camera cam;
|
|
Vector2 lastPanPosition;
|
|
bool isPanning;
|
|
float lastPinchDistance;
|
|
bool isPinching;
|
|
|
|
// Track if we started on a unit (don't pan if drawing route)
|
|
bool startedOnUnit;
|
|
|
|
void Start()
|
|
{
|
|
cam = Camera.main;
|
|
}
|
|
|
|
void Update()
|
|
{
|
|
HandleMouseInput();
|
|
HandleTouchInput();
|
|
ClampCameraPosition();
|
|
}
|
|
|
|
void HandleMouseInput()
|
|
{
|
|
var mouse = Mouse.current;
|
|
if (mouse == null) return;
|
|
|
|
// Scroll wheel zoom
|
|
float scroll = mouse.scroll.ReadValue().y;
|
|
if (Mathf.Abs(scroll) > 0.01f)
|
|
{
|
|
Zoom(-scroll * zoomSpeed * Time.deltaTime * 10f);
|
|
}
|
|
|
|
// Right-click pan (left-click is for route drawing)
|
|
if (mouse.rightButton.wasPressedThisFrame)
|
|
{
|
|
lastPanPosition = mouse.position.ReadValue();
|
|
isPanning = true;
|
|
}
|
|
else if (mouse.rightButton.wasReleasedThisFrame)
|
|
{
|
|
isPanning = false;
|
|
}
|
|
|
|
if (isPanning && mouse.rightButton.isPressed)
|
|
{
|
|
Vector2 currentPos = mouse.position.ReadValue();
|
|
Vector2 delta = currentPos - lastPanPosition;
|
|
Pan(-delta);
|
|
lastPanPosition = currentPos;
|
|
}
|
|
}
|
|
|
|
void HandleTouchInput()
|
|
{
|
|
var touch = Touchscreen.current;
|
|
if (touch == null) return;
|
|
|
|
int touchCount = 0;
|
|
foreach (var t in touch.touches)
|
|
{
|
|
if (t.press.isPressed) touchCount++;
|
|
}
|
|
|
|
if (touchCount == 2)
|
|
{
|
|
// Two finger pinch zoom and pan
|
|
var touch0 = touch.touches[0];
|
|
var touch1 = touch.touches[1];
|
|
|
|
Vector2 pos0 = touch0.position.ReadValue();
|
|
Vector2 pos1 = touch1.position.ReadValue();
|
|
float currentDistance = Vector2.Distance(pos0, pos1);
|
|
|
|
if (!isPinching)
|
|
{
|
|
isPinching = true;
|
|
lastPinchDistance = currentDistance;
|
|
lastPanPosition = (pos0 + pos1) / 2f;
|
|
}
|
|
else
|
|
{
|
|
// Pinch zoom
|
|
float deltaDistance = lastPinchDistance - currentDistance;
|
|
Zoom(deltaDistance * pinchZoomSpeed);
|
|
lastPinchDistance = currentDistance;
|
|
|
|
// Two-finger pan
|
|
Vector2 currentCenter = (pos0 + pos1) / 2f;
|
|
Vector2 delta = currentCenter - lastPanPosition;
|
|
Pan(-delta);
|
|
lastPanPosition = currentCenter;
|
|
}
|
|
|
|
isPanning = false; // Don't single-finger pan while pinching
|
|
}
|
|
else if (touchCount == 1)
|
|
{
|
|
isPinching = false;
|
|
|
|
var primaryTouch = touch.primaryTouch;
|
|
Vector2 touchPos = primaryTouch.position.ReadValue();
|
|
|
|
// Check if touch started on a unit
|
|
if (primaryTouch.press.wasPressedThisFrame)
|
|
{
|
|
Vector2 worldPos = cam.ScreenToWorldPoint(touchPos);
|
|
var hit = Physics2D.OverlapPoint(worldPos);
|
|
startedOnUnit = hit != null && hit.GetComponent<Unit>() != null;
|
|
|
|
if (!startedOnUnit)
|
|
{
|
|
lastPanPosition = touchPos;
|
|
isPanning = true;
|
|
}
|
|
}
|
|
else if (primaryTouch.press.wasReleasedThisFrame)
|
|
{
|
|
isPanning = false;
|
|
startedOnUnit = false;
|
|
}
|
|
|
|
// Single finger pan (only if not drawing route)
|
|
if (isPanning && !startedOnUnit && primaryTouch.press.isPressed)
|
|
{
|
|
Vector2 delta = touchPos - lastPanPosition;
|
|
Pan(-delta);
|
|
lastPanPosition = touchPos;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
isPinching = false;
|
|
isPanning = false;
|
|
}
|
|
}
|
|
|
|
void Zoom(float delta)
|
|
{
|
|
float newSize = cam.orthographicSize + delta;
|
|
cam.orthographicSize = Mathf.Clamp(newSize, minZoom, maxZoom);
|
|
}
|
|
|
|
void Pan(Vector2 screenDelta)
|
|
{
|
|
// Convert screen delta to world delta
|
|
float worldUnitsPerPixel = cam.orthographicSize * 2f / Screen.height;
|
|
Vector3 worldDelta = new Vector3(
|
|
screenDelta.x * worldUnitsPerPixel * panSpeed,
|
|
screenDelta.y * worldUnitsPerPixel * panSpeed,
|
|
0
|
|
);
|
|
|
|
cam.transform.position += worldDelta;
|
|
}
|
|
|
|
void ClampCameraPosition()
|
|
{
|
|
// Keep camera within map bounds (with some padding for zoom)
|
|
float halfHeight = cam.orthographicSize;
|
|
float halfWidth = halfHeight * cam.aspect;
|
|
|
|
float minX = -Game.MapWidth / 2f + halfWidth;
|
|
float maxX = Game.MapWidth / 2f - halfWidth;
|
|
float minY = -Game.MapHeight / 2f + halfHeight;
|
|
float maxY = Game.MapHeight / 2f - halfHeight;
|
|
|
|
Vector3 pos = cam.transform.position;
|
|
pos.x = Mathf.Clamp(pos.x, minX, maxX);
|
|
pos.y = Mathf.Clamp(pos.y, minY, maxY);
|
|
cam.transform.position = pos;
|
|
}
|
|
}
|