部队战争

部队战争

球球大作战部队全面战争系统,并为其配备法术

敌人 AI

using UnityEngine;
using UnityEngine.AI;

public class EnemyAI : MonoBehaviour
{
    public NavMeshAgent agent;

    [Header("Orb Searching")]
    public float searchRange = 100f;

    [Header("Orb Collection")]
    public float collectDistance = 2f;
    private Transform targetOrb;

    private OrbCollector collector;

    void Start()
    {
        agent = GetComponent<NavMeshAgent>();
        collector = GetComponent<OrbCollector>();

        if(collector == null)
        {
            Debug.LogWarning(
            "Enemy needs an OrbCollector component!");
        }

        InvokeRepeating(
        "FindOrb",
        0,
        2);
    }

    void Update()
    {
        if(targetOrb != null)
        {
            agent.SetDestination(
            targetOrb.position);

            float distance =
            Vector3.Distance(
            transform.position,
            targetOrb.position);

            if(distance <= collectDistance)
            {
                CollectOrb();
            }
        }
    }

    void FindOrb()
    {
        GameObject[] orbs =
        GameObject.FindGameObjectsWithTag("Orb");

        float closest =
        Mathf.Infinity;

        GameObject best = null;

        foreach(GameObject orb in orbs)
        {
            float distance =
            Vector3.Distance(
            transform.position,
            orb.transform.position);

            if(distance < closest &&
            distance <= searchRange)
            {
                closest = distance;
                best = orb;
            }
        }

        if(best != null)
        {
            targetOrb =
            best.transform;
        }
    }

    void CollectOrb()
    {
        if(collector != null)
        {
            collector.CollectOrb(1);
        }

        Destroy(
        targetOrb.gameObject);

        targetOrb = null;
    }
}

结束

敌人生成

using UnityEngine;

public class EnemySpawner : MonoBehaviour
{
    public GameObject enemyPrefab;

    public Terrain terrain;

    public int enemyAmount = 50;

    void Start()
    {
        SpawnEnemies();
    }

    void SpawnEnemies()
    {
        TerrainData data =
        terrain.terrainData;

        Vector3 terrainPos =
        terrain.transform.position;

        for(int i = 0; i < enemyAmount; i++)
        {
            float x =
            Random.Range(
            0,
            data.size.x);

            float z =
            Random.Range(
            0,
            data.size.z);

            float y =
            data.GetHeight(
            (int)x,
            (int)z);

            Vector3 spawn =
            new Vector3(
            x,
            y,
            z)
            + terrainPos;

            Instantiate(
            enemyPrefab,
            spawn,
            Quaternion.identity);
        }
    }
}

结束

第一人称视角

using UnityEngine;
[RequireComponent(typeof(CharacterController))]
public class FPVPlayerController : MonoBehaviour
{
    [Header("Player Camera")]
    public Camera playerCamera;

    [Header("Movement")]
    public float walkSpeed = 5f;
    public float sprintSpeed = 9f;
    public float crouchSpeed = 2.5f;

    [Header("Jump")]
    public float jumpHeight = 2f;
    public int maxJumps = 2;

    [Header("Gravity")]
    public float gravity = -20f;

    [Header("Mouse Look")]
    public float mouseSensitivity = 200f;

    [Header("Crouch")]
    public float standingHeight = 2f;
    public float crouchHeight = 1f;

    public float cameraStandHeight = 0.8f;
    public float cameraCrouchHeight = 0.4f;

    public float crouchSmooth = 10f;

    private CharacterController controller;

    private Vector3 velocity;

    private float xRotation;

    private int jumpsLeft;

    private bool crouching;

    void Start()
    {
        controller =
        GetComponent<CharacterController>();

        Cursor.lockState =
        CursorLockMode.Locked;

        Cursor.visible = false;

        jumpsLeft = maxJumps;
    }

    void Update()
    {
        Look();

        Move();

        Jump();

        Crouch();

        ApplyGravity();
    }

    void Look()
    {
        float mouseX =
        Input.GetAxis("Mouse X")
        * mouseSensitivity
        * Time.deltaTime;

        float mouseY =
        Input.GetAxis("Mouse Y")
        * mouseSensitivity
        * Time.deltaTime;

        xRotation -= mouseY;

        xRotation =
        Mathf.Clamp(
        xRotation,
        -90f,
        90f);

        playerCamera.transform.localRotation =
        Quaternion.Euler(
        xRotation,
        0,
        0);

        transform.Rotate(
        Vector3.up *
        mouseX);
    }

    void Move()
    {
        float x =
        Input.GetAxis("Horizontal");

        float z =
        Input.GetAxis("Vertical");

        Vector3 move =
        transform.right * x +
        transform.forward * z;

        float speed = walkSpeed;

        if(Input.GetKey(KeyCode.LeftShift)
        && !crouching)
        {
            speed = sprintSpeed;
        }

        if(crouching)
        {
            speed = crouchSpeed;
        }

        controller.Move(
        move *
        speed *
        Time.deltaTime);
    }

    void Jump()
    {
        if(controller.isGrounded)
        {
            jumpsLeft = maxJumps;
        }

        if(Input.GetKeyDown(KeyCode.Space)
        && jumpsLeft > 0)
        {
            velocity.y =
            Mathf.Sqrt(
            jumpHeight *
            -2f *
            gravity);

            jumpsLeft--;
        }
    }

    void ApplyGravity()
    {
        if(controller.isGrounded &&
        velocity.y < 0)
        {
            velocity.y = -2f;
        }

        velocity.y +=
        gravity *
        Time.deltaTime;

        controller.Move(
        velocity *
        Time.deltaTime);
    }

    void Crouch()
    {
        if(Input.GetKey(KeyCode.LeftControl))
        {
            crouching = true;

            controller.height =
            Mathf.Lerp(
            controller.height,
            crouchHeight,
            Time.deltaTime *
            crouchSmooth);

            playerCamera.transform.localPosition =
            Vector3.Lerp(
            playerCamera.transform.localPosition,
            new Vector3(
            0,
            cameraCrouchHeight,
            0),
            Time.deltaTime *
            crouchSmooth);
        }
        else
        {
            crouching = false;

            controller.height =
            Mathf.Lerp(
            controller.height,
            standingHeight,
            Time.deltaTime *
            crouchSmooth);

            playerCamera.transform.localPosition =
            Vector3.Lerp(
            playerCamera.transform.localPosition,
            new Vector3(
            0,
            cameraStandHeight,
            0),
            Time.deltaTime *
            crouchSmooth);
        }
    }
}

结束

方尖碑

using UnityEngine;
using System.Collections.Generic;

public class TerrainObeliskSpawner : MonoBehaviour
{
    [Header("Terrain")]
    public Terrain terrain;

    [Header("Obelisk Prefab")]
    public GameObject obeliskPrefab;

    [Header("Spawn Settings")]
    public int amountToSpawn = 20;

    public float minDistanceBetweenObelisks = 50f;
    public float spawnHeightOffset = 1f;

    [Header("Lifetime")]
    public bool enableDecay = true;

    public float decayTime = 300f;
    public float respawnTime = 60f;

    private List<GameObject> spawnedObelisks = new List<GameObject>();

    void Start()
    {
        SpawnObelisks();
    }

    public void SpawnObelisks()
    {
        int attempts = 0;
        int maxAttempts = amountToSpawn * 50;

        while (spawnedObelisks.Count < amountToSpawn && attempts < maxAttempts)
        {
            attempts++;

            Vector3 spawnPosition = GetRandomTerrainPosition();

            if (IsValidPosition(spawnPosition))
            {
                GameObject obelisk = Instantiate(
                    obeliskPrefab,
                    spawnPosition,
                    Quaternion.identity
                );

                spawnedObelisks.Add(obelisk);

                if(enableDecay)
                {
                    StartCoroutine(DecayObelisk(obelisk));
                }
            }
        }
    }

    Vector3 GetRandomTerrainPosition()
    {
        Vector3 terrainSize = terrain.terrainData.size;

        float x = Random.Range(0, terrainSize.x);
        float z = Random.Range(0, terrainSize.z);

        float y = terrain.SampleHeight(
            new Vector3(x,0,z)
        );

        return terrain.transform.position +
        new Vector3(
            x,
            y + spawnHeightOffset,
            z
        );
    }

    bool IsValidPosition(Vector3 position)
    {
        foreach(GameObject obj in spawnedObelisks)
        {
            if(obj == null)
                continue;

            float distance = Vector3.Distance(
                position,
                obj.transform.position
            );

            if(distance < minDistanceBetweenObelisks)
                return false;
        }

        return true;
    }

    System.Collections.IEnumerator DecayObelisk(GameObject obelisk)
    {
        yield return new WaitForSeconds(decayTime);

        if(obelisk != null)
        {
            spawnedObelisks.Remove(obelisk);
            Destroy(obelisk);
        }

        yield return new WaitForSeconds(respawnTime);

        SpawnSingleObelisk();
    }

    void SpawnSingleObelisk()
    {
        int attempts = 0;

        while(attempts < 100)
        {
            attempts++;

            Vector3 pos = GetRandomTerrainPosition();

            if(IsValidPosition(pos))
            {
                GameObject obelisk = Instantiate(
                    obeliskPrefab,
                    pos,
                    Quaternion.identity
                );

                spawnedObelisks.Add(obelisk);

                if(enableDecay)
                    StartCoroutine(DecayObelisk(obelisk));

                break;
            }
        }
    }
}

结束

能量球

using UnityEngine;
public class Orb : MonoBehaviour
{
    public int orbValue = 1;
    public float rotateSpeed = 100f;

    private void Update()
    {
        transform.Rotate(Vector3.up * rotateSpeed * Time.deltaTime);
    }

    private void OnTriggerEnter(Collider other)
    {
        OrbCollector collector = other.GetComponent<OrbCollector>();

        if (collector != null)
        {
            collector.CollectOrb(orbValue);
            Destroy(gameObject);
        }
    }
}

结束

能量球收集器

using UnityEngine;

public class OrbCollector : MonoBehaviour
{
    [Header("Collector")]
    public string collectorName;

    [Header("Orb Storage")]
    public int orbs = 0;

    [Header("Troop Spawning")]
    public GameObject troopPrefab;
    public Transform troopSpawnPoint;
    public int orbsPerTroop = 5;

    public int troopsSpawned = 0;

    public void CollectOrb(int amount)
    {
        orbs += amount;

        Debug.Log(
        collectorName +
        " has " +
        orbs +
        " orbs");

        CheckSpawn();
    }

    void CheckSpawn()
    {
        while(orbs >= orbsPerTroop)
        {
            SpawnTroop();

            orbs -= orbsPerTroop;
        }
    }

    void SpawnTroop()
    {
        if(troopPrefab == null)
        {
            Debug.LogWarning(
            "No troop prefab assigned");
            return;
        }

        Vector3 spawnPosition;

        if(troopSpawnPoint != null)
        {
            spawnPosition =
            troopSpawnPoint.position;
        }
        else
        {
            spawnPosition =
            transform.position +
            transform.forward * 3;
        }

        GameObject troop =
        Instantiate(
        troopPrefab,
        spawnPosition,
        Quaternion.identity);

        TroopAI ai =
        troop.GetComponent<TroopAI>();

        if(ai != null)
        {
            ai.owner =
            transform;
        }

        troopsSpawned++;

        Debug.Log(
        "Spawned troop " +
        troopsSpawned);
    }
}

结束

能量球生成

using UnityEngine;

public class OrbSpawner : MonoBehaviour
{
    public GameObject orbPrefab;
    public Terrain terrain;

    public int amount = 200;

    public float heightOffset = 1f;

    void Start()
    {
        SpawnAllOrbs();
    }

    void SpawnAllOrbs()
    {
        if(terrain == null)
        {
            Debug.LogError("No terrain assigned");
            return;
        }

        TerrainData data = terrain.terrainData;

        Vector3 terrainPosition =
        terrain.transform.position;

        for(int i = 0; i < amount; i++)
        {
            float x =
            Random.Range(
            0,
            data.size.x);

            float z =
            Random.Range(
            0,
            data.size.z);

            float y =
            data.GetHeight(
            (int)x,
            (int)z);

            Vector3 position =
            new Vector3(
            x,
            y + heightOffset,
            z)
            + terrainPosition;

            Instantiate(
            orbPrefab,
            position,
            Quaternion.identity);
        }
    }
}

结束

传送门

using UnityEngine;

public class Portal : MonoBehaviour
{
    [Header("Linked Portal")]
    public Portal linkedPortal;

    [Header("Teleport Settings")]
    public float teleportCooldown = 1f;

    private bool canTeleport = true;

    private void OnTriggerEnter(Collider other)
    {
        if (!canTeleport || linkedPortal == null)
            return;

        if (other.CompareTag("Player") || other.CompareTag("Enemy"))
        {
            Teleport(other);
        }
    }

    void Teleport(Collider target)
    {
        canTeleport = false;
        linkedPortal.canTeleport = false;

        CharacterController controller = target.GetComponent<CharacterController>();

        if (controller != null)
            controller.enabled = false;

        target.transform.position =
            linkedPortal.transform.position +
            linkedPortal.transform.forward * 2f;

        target.transform.rotation =
            linkedPortal.transform.rotation;

        if (controller != null)
            controller.enabled = true;

        Invoke(nameof(ResetTeleport), teleportCooldown);
        linkedPortal.Invoke(nameof(ResetTeleport), teleportCooldown);
    }

    void ResetTeleport()
    {
        canTeleport = true;
    }
}

结束

传送门地形

using UnityEngine;

public class TerrainPortalSpawner : MonoBehaviour
{
    [Header("Terrain")]
    public Terrain terrain;

    [Header("Portal Prefabs")]
    public GameObject portalAPrefab;
    public GameObject portalBPrefab;

    [Header("Spawn Settings")]
    public float spawnHeight = 2f;

    [Header("Portal Amount")]
    public int portalPairs = 3; // 生成多少对 A/B

    [Header("Portal Distance")]
    public float minPortalDistance = 50f;
    public float maxPortalDistance = 300f;

    [Header("Portal Life Cycle")]
    public float portalLifetime = 120f;
    public float respawnTime = 60f;

    private GameObject[] portalA;
    private GameObject[] portalB;

    void Start()
    {
        SpawnPortals();
    }

    void SpawnPortals()
    {
        DestroyPortals();

        portalA = new GameObject[portalPairs];
        portalB = new GameObject[portalPairs];

        for(int i = 0; i < portalPairs; i++)
        {
            Vector3 posA = GetRandomTerrainPosition();

            Vector3 posB = GetDistancePosition(posA);

            portalA[i] = Instantiate(
                portalAPrefab,
                posA,
                Quaternion.identity
            );

            portalB[i] = Instantiate(
                portalBPrefab,
                posB,
                Quaternion.identity
            );

            Portal a = portalA[i].GetComponent<Portal>();
            Portal b = portalB[i].GetComponent<Portal>();

            if(a != null && b != null)
            {
                a.linkedPortal = b;
                b.linkedPortal = a;
            }
        }

        Invoke(nameof(DecayPortals), portalLifetime);
    }

    Vector3 GetDistancePosition(Vector3 start)
    {
        Vector3 position;

        int attempts = 0;

        do
        {
            position = GetRandomTerrainPosition();
            attempts++;
        }
        while(
            Vector3.Distance(start, position) < minPortalDistance ||
            Vector3.Distance(start, position) > maxPortalDistance
        && attempts < 100);

        return position;
    }

    void DecayPortals()
    {
        DestroyPortals();

        Invoke(nameof(SpawnPortals), respawnTime);
    }

    void DestroyPortals()
    {
        if(portalA != null)
        {
            foreach(GameObject p in portalA)
            {
                if(p != null)
                    Destroy(p);
            }
        }

        if(portalB != null)
        {
            foreach(GameObject p in portalB)
            {
                if(p != null)
                    Destroy(p);
            }
        }
    }

    Vector3 GetRandomTerrainPosition()
    {
        Vector3 terrainPos = terrain.transform.position;

        float x = Random.Range(
            0,
            terrain.terrainData.size.x
        );

        float z = Random.Range(
            0,
            terrain.terrainData.size.z
        );

        float y = terrain.SampleHeight(
            new Vector3(
                terrainPos.x + x,
                0,
                terrainPos.z + z
            )
        );

        return new Vector3(
            terrainPos.x + x,
            y + spawnHeight,
            terrainPos.z + z
        );
    }
}

结束

房间

using UnityEngine;
using System.Collections;

public class ObeliskRoomInterior : MonoBehaviour
{
    [Header("Player")]
    public Transform player;

    [Header("Room Prefab")]
    public GameObject roomPrefab;

    [Header("Room Settings")]
    public Transform outsideExitPoint;

    public float enterDistance = 4f;
    public float roomDepth = -500f;

    [Header("Spawn Protection")]
    public float spawnHeightOffset = 1.5f;
    public float teleportDelay = 0.1f;

    [Header("Terrain Border")]
    public Terrain terrain;
    public float borderDistance = 30f;

    [Header("Input")]
    public KeyCode enterKey = KeyCode.E;
    public KeyCode exitKey = KeyCode.F;

    private GameObject spawnedRoom;
    private bool playerInside = false;

    void Update()
    {
        if (!playerInside)
        {
            float distance = Vector3.Distance(
                player.position,
                transform.position
            );

            if(distance <= enterDistance)
            {
                if(Input.GetKeyDown(enterKey))
                {
                    EnterRoom();
                }
            }
        }
        else
        {
            if(Input.GetKeyDown(exitKey))
            {
                ExitRoom();
            }
        }
    }

    void EnterRoom()
    {
        playerInside = true;

        Vector3 roomPosition = new Vector3(
            transform.position.x,
            roomDepth,
            transform.position.z
        );

        // 让房间远离地形边界
        if(terrain != null)
        {
            Vector3 terrainPos = terrain.transform.position;
            Vector3 terrainSize = terrain.terrainData.size;

            roomPosition.x = Mathf.Clamp(
                roomPosition.x,
                terrainPos.x + borderDistance,
                terrainPos.x + terrainSize.x - borderDistance
            );

            roomPosition.z = Mathf.Clamp(
                roomPosition.z,
                terrainPos.z + borderDistance,
                terrainPos.z + terrainSize.z - borderDistance
            );
        }

        spawnedRoom = Instantiate(
            roomPrefab,
            roomPosition,
            Quaternion.identity
        );

        ObeliskRoomExit exit =
            spawnedRoom.GetComponentInChildren<ObeliskRoomExit>();

        if(exit != null)
        {
            exit.owner = this;
            exit.player = player;
            exit.outsidePosition = outsideExitPoint.position;
        }

        Transform insideSpawn =
            spawnedRoom.transform.Find("InsideSpawn");

        if(insideSpawn != null)
        {
            StartCoroutine(TeleportInside(insideSpawn));
        }
        else
        {
            StartCoroutine(TeleportInside(spawnedRoom.transform));
        }
    }

    IEnumerator TeleportInside(Transform spawn)
    {
        // 等待房间碰撞体加载
        yield return new WaitForSeconds(teleportDelay);

        CharacterController controller =
            player.GetComponent<CharacterController>();

        if(controller != null)
        {
            controller.enabled = false;
        }

        player.position = spawn.position + Vector3.up * spawnHeightOffset;

        if(controller != null)
        {
            controller.enabled = true;
        }
    }

    public void ExitRoom()
    {
        playerInside = false;

        CharacterController controller =
            player.GetComponent<CharacterController>();

        if(controller != null)
        {
            controller.enabled = false;
        }

        player.position = outsideExitPoint.position;

        if(controller != null)
        {
            controller.enabled = true;
        }

        if(spawnedRoom != null)
        {
            Destroy(spawnedRoom);
        }
    }
}

结束

房间出口

using UnityEngine;

public class ObeliskRoomExit : MonoBehaviour
{
    [HideInInspector]
    public ObeliskRoomInterior owner;

    [HideInInspector]
    public Transform player;

    public Vector3 outsidePosition;

    public KeyCode exitKey = KeyCode.F;

    void Update()
    {
        if(player == null)
            return;

        float distance =
            Vector3.Distance(
                player.position,
                transform.position
            );

        if(distance < 3f)
        {
            if(Input.GetKeyDown(exitKey))
            {
                Exit();
            }
        }
    }

    void Exit()
    {
        owner.ExitRoom();
    }
}

结束

缩小

using UnityEngine;
using System.Collections.Generic;

public class SizeToggle : MonoBehaviour
{
    [Header("Objects To Resize")]
    public Transform player;

    public List<Transform> troops = new List<Transform>();

    [Header("Size Settings")]
    public float normalSize = 1f;
    public float shrunkenSize = 0.2f;

    [Header("Toggle Key")]
    public KeyCode toggleKey = KeyCode.T;

    private bool isSmall = false;

    void Start()
    {
        if (player == null)
            player = transform;
    }

    void Update()
    {
        if (Input.GetKeyDown(toggleKey))
        {
            ToggleSize();
        }
    }

    void ToggleSize()
    {
        isSmall = !isSmall;

        float newSize = isSmall ? shrunkenSize : normalSize;

        // 调整玩家大小
        player.localScale = Vector3.one * newSize;

        // 调整部队大小
        foreach (Transform troop in troops)
        {
            if (troop != null)
            {
                troop.localScale = Vector3.one * newSize;
            }
        }
    }
}

结束

地形雾

using UnityEngine;

public class TerrainFog : MonoBehaviour
{
    [Header("Fog Settings")]
    public bool enableFog = true;

    public FogMode fogMode = FogMode.ExponentialSquared;

    [Header("Fog Color")]
    public Color fogColor = Color.gray;

    [Header("Fog Density")]
    [Range(0f, 0.1f)]
    public float fogDensity = 0.02f;

    [Header("Linear Fog Distance")]
    public float startDistance = 50f;
    public float endDistance = 300f;

    void Start()
    {
        ApplyFog();
    }

    void Update()
    {
        ApplyFog();
    }

    void ApplyFog()
    {
        RenderSettings.fog = enableFog;
        RenderSettings.fogMode = fogMode;
        RenderSettings.fogColor = fogColor;

        if (fogMode == FogMode.Exponential ||
            fogMode == FogMode.ExponentialSquared)
        {
            RenderSettings.fogDensity = fogDensity;
        }
        else
        {
            RenderSettings.fogStartDistance = startDistance;
            RenderSettings.fogEndDistance = endDistance;
        }
    }
}

结束

地形墙

using UnityEngine;

public class TerrainBoundarySpawner : MonoBehaviour
{
    [Header("Terrain")]
    public Terrain terrain;

    [Header("Wall Settings")]
    public float wallHeight = 50f;

    public float wallThickness = 2f;

    public Material wallMaterial;

    void Start()
    {
        CreateWalls();
    }

    void CreateWalls()
    {
        if(terrain == null)
        {
            Debug.LogError(
            "Terrain not assigned");
            return;
        }

        TerrainData data =
        terrain.terrainData;

        Vector3 terrainPosition =
        terrain.transform.position;

        float width =
        data.size.x;

        float length =
        data.size.z;

        float height =
        wallHeight;

        // 北墙
        CreateWall(
            "North Wall",
            new Vector3(
                terrainPosition.x + width / 2,
                height / 2,
                terrainPosition.z + length
            ),
            new Vector3(
                width,
                height,
                wallThickness
            )
        );

        // 南墙
        CreateWall(
            "South Wall",
            new Vector3(
                terrainPosition.x + width / 2,
                height / 2,
                terrainPosition.z
            ),
            new Vector3(
                width,
                height,
                wallThickness
            )
        );

        // 东墙
        CreateWall(
            "East Wall",
            new Vector3(
                terrainPosition.x + width,
                height / 2,
                terrainPosition.z + length / 2
            ),
            new Vector3(
                wallThickness,
                height,
                length
            )
        );

        // 西墙
        CreateWall(
            "West Wall",
            new Vector3(
                terrainPosition.x,
                height / 2,
                terrainPosition.z + length / 2
            ),
            new Vector3(
                wallThickness,
                height,
                length
            )
        );
    }

    void CreateWall(
    string wallName,
    Vector3 position,
    Vector3 size)
    {
        GameObject wall =
        GameObject.CreatePrimitive(
        PrimitiveType.Cube);

        wall.name =
        wallName;

        wall.transform.position =
        position;

        wall.transform.localScale =
        size;

        if(wallMaterial != null)
        {
            Renderer renderer =
            wall.GetComponent<Renderer>();

            renderer.material =
            wallMaterial;
        }
    }
}

结束

时间影响

using UnityEngine;

public class TimeAffectedAI : MonoBehaviour
{
    public float timeMultiplier = 1f;

    public void SetTimeMultiplier(float value)
    {
        timeMultiplier = value;
    }

    public float GetTimeMultiplier()
    {
        return timeMultiplier;
    }
}

结束

时间领域能力

using UnityEngine;
using System.Collections.Generic;

public class TimeZoneAbility : MonoBehaviour
{
    [Header("Player or AI")]
    public Transform owner;

    [Header("Radius")]
    public float radius = 30f;

    [Header("Time Effects")]
    public float slowTime = 0.3f;
    public float fastTime = 2f;

    private bool slowActive;
    private bool fastActive;

    void Update()
    {
        // Z = 减速时间
        if (Input.GetKeyDown(KeyCode.Z))
        {
            slowActive = !slowActive;
            fastActive = false;
        }

        // X = 加速时间
        if (Input.GetKeyDown(KeyCode.X))
        {
            fastActive = !fastActive;
            slowActive = false;
        }

        ApplyTime();
    }

    void ApplyTime()
    {
        if(owner == null)
            return;

        Collider[] targets = Physics.OverlapSphere(owner.position, radius);

        foreach(Collider target in targets)
        {
            TimeAffectedAI timeAI = target.GetComponent<TimeAffectedAI>();

            if(timeAI != null)
            {
                if(slowActive)
                {
                    timeAI.SetTimeMultiplier(slowTime);
                }
                else if(fastActive)
                {
                    timeAI.SetTimeMultiplier(fastTime);
                }
                else
                {
                    timeAI.SetTimeMultiplier(1f);
                }
            }
        }
    }

    void OnDrawGizmosSelected()
    {
        if(owner != null)
        {
            Gizmos.DrawWireSphere(owner.position, radius);
        }
    }
}

结束

商人 AI

using UnityEngine;
using UnityEngine.AI;

public class TraderAI : MonoBehaviour
{
    [Header("AI Movement")]
    public NavMeshAgent agent;
    public float wanderRadius = 20f;
    public float wanderTime = 5f;

    [Header("Player")]
    public Transform player;
    public float tradeDistance = 3f;

    [Header("Trading")]
    public string traderName = "Merchant";
    public int itemPrice = 10;
    public int stock = 10;

    [Header("Player Money")]
    public int playerMoney = 100;
    public int playerItems = 0;

    private float timer;
    private bool trading;

    void Start()
    {
        timer = wanderTime;
    }

    void Update()
    {
        if (player == null) return;

        float distance = Vector3.Distance(transform.position, player.position);

        // 在玩家附近停下
        if (distance <= tradeDistance)
        {
            agent.isStopped = true;

            if (Input.GetKeyDown(KeyCode.E))
            {
                trading = !trading;

                if (trading)
                    OpenTrade();
                else
                    CloseTrade();
            }
        }
        else
        {
            agent.isStopped = false;
            Wander();
        }
    }

    void Wander()
    {
        timer += Time.deltaTime;

        if (timer >= wanderTime)
        {
            Vector3 randomDirection = Random.insideUnitSphere * wanderRadius;
            randomDirection += transform.position;

            NavMeshHit hit;

            if (NavMesh.SamplePosition(randomDirection, out hit, wanderRadius, 1))
            {
                agent.SetDestination(hit.position);
            }

            timer = 0;
        }
    }

    void OpenTrade()
    {
        Debug.Log(traderName + " opened shop");

        Debug.Log("Press B to buy item for $" + itemPrice);
        Debug.Log("Press S to sell item");
    }

    void CloseTrade()
    {
        Debug.Log("Trade closed");
    }

    void LateUpdate()
    {
        if (!trading)
            return;

        // 购买
        if (Input.GetKeyDown(KeyCode.B))
        {
            Buy();
        }

        // 出售
        if (Input.GetKeyDown(KeyCode.S))
        {
            Sell();
        }
    }

    void Buy()
    {
        if(stock <= 0)
        {
            Debug.Log("Trader is out of stock");
            return;
        }

        if(playerMoney >= itemPrice)
        {
            playerMoney -= itemPrice;
            playerItems++;
            stock--;

            Debug.Log("Bought item!");
        }
        else
        {
            Debug.Log("Not enough money");
        }
    }

    void Sell()
    {
        if(playerItems > 0)
        {
            playerItems--;
            playerMoney += itemPrice;

            Debug.Log("Sold item!");
        }
        else
        {
            Debug.Log("You have nothing to sell");
        }
    }
}

结束

部队 AI

using UnityEngine;
using UnityEngine.AI;

public class TroopAI : MonoBehaviour
{
    [Header("Owner")]
    public Transform owner;

    [Header("Movement")]
    public NavMeshAgent agent;

    public float followDistance = 5f;

    public float updateRate = 0.5f;

    [Header("Combat")]
    public float health = 100;

    public float damage = 10;
    private float timer;

    void Start()
    {
        agent =
        GetComponent<NavMeshAgent>();
    }

    void Update()
    {
        if(owner == null)
        {
            return;
        }

        timer += Time.deltaTime;

        if(timer >= updateRate)
        {
            FollowOwner();

            timer = 0;
        }
    }

    void FollowOwner()
    {
        float distance =
        Vector3.Distance(
        transform.position,
        owner.position);

        if(distance > followDistance)
        {
            agent.SetDestination(
            owner.position);
        }
        else
        {
            agent.ResetPath();
        }
    }

    public void TakeDamage(float amount)
    {
        health -= amount;

        if(health <= 0)
        {
            Destroy(gameObject);
        }
    }
}

结束

部队存储

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;

public class SimpleObeliskTroopStorage : MonoBehaviour
{
    [Header("Storage")]
    public int maxTroops = 20;
    public float storageRange = 5f;

    [Header("Release")]
    public float releaseDistance = 2f;

    [Header("Controls")]
    public KeyCode storeKey = KeyCode.G;
    public KeyCode releaseKey = KeyCode.R;

    private List<GameObject> storedTroops = new List<GameObject>();

    void Update()
    {
        if(Input.GetKeyDown(storeKey))
        {
            StoreTroop();
        }

        if(Input.GetKeyDown(releaseKey))
        {
            ReleaseTroop();
        }
    }

    void StoreTroop()
    {
        if(storedTroops.Count >= maxTroops)
        {
            Debug.Log("Obelisk storage full");
            return;
        }

        Collider[] nearby =
        Physics.OverlapSphere(
            transform.position,
            storageRange
        );

        foreach(Collider col in nearby)
        {
            TroopAI troop =
            col.GetComponentInParent<TroopAI>();

            if(troop != null)
            {
                GameObject troopObject =
                troop.gameObject;

                storedTroops.Add(troopObject);

                troopObject.SetActive(false);

                Debug.Log(
                "Stored troop: "
                + troopObject.name
                );

                return;
            }
        }

        Debug.Log("No troop nearby");
    }

    void ReleaseTroop()
    {
        if(storedTroops.Count == 0)
        {
            Debug.Log("No troops stored");
            return;
        }

        GameObject troop =
        storedTroops[0];

        storedTroops.RemoveAt(0);

        Vector3 spawnPosition =
        transform.position +
        transform.forward * releaseDistance;

        troop.transform.position =
        spawnPosition;

        troop.transform.rotation =
        transform.rotation;

        troop.SetActive(true);

        NavMeshAgent agent =
        troop.GetComponent<NavMeshAgent>();

        if(agent != null)
        {
            agent.enabled = true;
            agent.Warp(spawnPosition);
        }

        Debug.Log(
        "Released troop: "
        + troop.name
        );
    }

    public int GetStoredTroopCount()
    {
        return storedTroops.Count;
    }

    void OnDrawGizmosSelected()
    {
        Gizmos.DrawWireSphere(
            transform.position,
            storageRange
        );
    }
}

结束

观察

using UnityEngine;
using System.Collections.Generic;

public class EnemyWatchCamera : MonoBehaviour
{
    [Header("Camera")]
    public Camera playerCamera;
    public Camera watchCamera;

    [Header("AI Settings")]
    public string aiTag = "Enemy";

    public float orbitDistance = 6f;
    public float orbitHeight = 3f;
    public float orbitSpeed = 120f;

    [Header("Controls")]
    public KeyCode watchKey = KeyCode.V;
    public KeyCode nextTargetKey = KeyCode.C;

    private List<GameObject> targets = new List<GameObject>();

    private int currentTarget = 0;

    private bool watching = false;

    private float orbitAngle;

    void Start()
    {
        watchCamera.enabled = false;
        playerCamera.enabled = true;

        FindTargets();
    }

    void Update()
    {
        if(Input.GetKeyDown(watchKey))
        {
            ToggleWatch();
        }

        if(watching && Input.GetKeyDown(nextTargetKey))
        {
            NextTarget();
        }

        if(watching)
        {
            OrbitCamera();
        }
    }

    void FindTargets()
    {
        targets.Clear();

        GameObject[] enemies = GameObject.FindGameObjectsWithTag(aiTag);

        foreach(GameObject enemy in enemies)
        {
            targets.Add(enemy);
        }
    }

    void ToggleWatch()
    {
        watching = !watching;

        if(watching)
        {
            FindTargets();
            playerCamera.enabled =