feat(core): implement playable CTF prototype with 6 core scripts
Implements the minimal playable core for the teaser prototype: - Game.cs: Bootstrap, scene setup, scoring, round reset, win condition - Unit.cs: Movement, route following, tagging, respawn - RouteDrawer.cs: Click-drag route input with LineRenderer preview - Flag.cs: Pickup, drop, return mechanics - Visibility.cs: Fog of war via SpriteRenderer visibility - SimpleAI.cs: Enemy AI that chases player flag All game objects created programmatically (no Editor setup required). Uses RuntimeInitializeOnLoadMethod for automatic bootstrap. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
70
Backyard CTF/Assets/Scripts/SimpleAI.cs
Normal file
70
Backyard CTF/Assets/Scripts/SimpleAI.cs
Normal file
@@ -0,0 +1,70 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public class SimpleAI : MonoBehaviour
|
||||
{
|
||||
public Unit[] aiUnits;
|
||||
public Flag playerFlag;
|
||||
public Transform aiBase;
|
||||
|
||||
float decisionTimer;
|
||||
const float DecisionInterval = 0.5f;
|
||||
const float RandomOffset = 1.5f;
|
||||
|
||||
void Update()
|
||||
{
|
||||
decisionTimer -= Time.deltaTime;
|
||||
if (decisionTimer <= 0)
|
||||
{
|
||||
MakeDecisions();
|
||||
decisionTimer = DecisionInterval;
|
||||
}
|
||||
}
|
||||
|
||||
void MakeDecisions()
|
||||
{
|
||||
if (playerFlag == null || aiBase == null) return;
|
||||
|
||||
for (int i = 0; i < aiUnits.Length; i++)
|
||||
{
|
||||
var unit = aiUnits[i];
|
||||
if (unit == null || unit.isTaggedOut) continue;
|
||||
|
||||
Vector2 target;
|
||||
|
||||
if (unit.hasFlag)
|
||||
{
|
||||
// Has flag - return to base
|
||||
target = aiBase.position;
|
||||
}
|
||||
else if (playerFlag.carriedBy != null && playerFlag.carriedBy.team == Unit.Team.Enemy)
|
||||
{
|
||||
// Friendly unit has flag - escort or find something else to do
|
||||
// For now, just patrol near own base
|
||||
target = (Vector2)aiBase.position + Random.insideUnitCircle * 5f;
|
||||
}
|
||||
else if (playerFlag.carriedBy != null)
|
||||
{
|
||||
// Player has our flag - chase the carrier
|
||||
target = playerFlag.carriedBy.transform.position;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Flag is at home or dropped - go for it
|
||||
target = playerFlag.transform.position;
|
||||
}
|
||||
|
||||
// Add slight randomness to prevent all units clumping
|
||||
Vector2 randomOffset = Random.insideUnitCircle * RandomOffset;
|
||||
|
||||
// Offset based on unit index for some spread
|
||||
float angle = (i / (float)aiUnits.Length) * Mathf.PI * 2f;
|
||||
Vector2 spreadOffset = new Vector2(Mathf.Cos(angle), Mathf.Sin(angle)) * 1f;
|
||||
|
||||
Vector2 finalTarget = target + randomOffset * 0.5f + spreadOffset;
|
||||
|
||||
// Simple route: straight line to target
|
||||
unit.SetRoute(new List<Vector2> { finalTarget });
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user