U
章 01基础11 课 — 点击打开
11 / 11 · 20 分钟
基础★ 练习

练习:竞技场初具雏形

到目前为止的项目

Assets/

Scenes/

Arena.unity

FloorKnightMageArcherCanvas

Scripts/

CharacterSwitcher.csSwapCamera.csGameManager.csArenaHud.csGridSpawner.cs→ArenaBuilder.cs

Prefabs/

P_Cube

你已经有的这一课新增这一课替换

练习60–90 分钟

搭一座赛场,课程剩下的全部内容都在这里进行。只需要一个叫 Arena 的场景:一块地面,一组由代码生成的方块网格 —— 就是 tab 02 里你要跳来跳去的那些方块 —— 三个可以切换的角色,以及屏幕上一块小小的数值面板。

复用的东西

int / float / bool变量类型Start / Update生命周期public field → Inspectorpublic 字段if / for / List分支与循环Plane + material地面与材质Prefab + InstantiatePrefab 与生成GetComponent获取组件
动态图解

做完之后的场景,看起来和跑起来都应该是这样。

要做什么,一步一步来

第 1 部分 — 舞台

  • 新建一个叫 Arena 的场景,存到 Assets/Scenes。从这里到课程结束,每一课都在这一个文件里进行 —— 你之后唯一还会创建的场景就是开场画面。

  • 一块 Plane 当地面,放在 0,0,0,scale 设成 3,1,3,配上你自己做的深色 material。

  • 把 Camera 大致放在 0, 14, -16,旋转 38, 0, 0,让整个地面都在画面里。

  • Directional Light 旋转 50, -30, 0,阴影调柔和。

第 2 部分 — 方块网格

写一个脚本来生成整个网格。一个 cube 都不许手动摆 —— 一旦发现自己在拖 cube,就退回去改循环。

ArenaBuilder.cs — fill in the TODOs
using System.Collections.Generic;
using UnityEngine;

public class ArenaBuilder : MonoBehaviour
{
    [Header("Grid")]
    [SerializeField] private GameObject blockPrefab;
    [SerializeField] private int columns = 4;
    [SerializeField] private int rows = 4;
    [SerializeField] private float spacing = 1.6f;
    [SerializeField] private Transform blockParent;

    // every block we made, so we can count and clear them
    private readonly List<GameObject> blocks = new List<GameObject>();

    public int BlockCount => blocks.Count;

    void Start()
    {
        Build();
    }

    void Update()
    {
        // press R to rebuild with fresh random colours
        if (Input.GetKeyDown(KeyCode.R))
            Build();
    }

    public void Build()
    {
        Clear();

        float offsetX = (columns - 1) * spacing * 0.5f;
        float offsetZ = (rows - 1) * spacing * 0.5f;

        for (int x = 0; x < columns; x++)
        {
            for (int z = 0; z < rows; z++)
            {
                Vector3 pos = new Vector3(
                    x * spacing - offsetX,
                    0.5f,
                    z * spacing - offsetZ);

                GameObject b = Instantiate(blockPrefab, pos, Quaternion.identity, blockParent);
                b.name = $"Block_{x}_{z}";

                // TODO 1: give it a random colour
                // TODO 2: give it a random height between 0.5 and 2.5

                blocks.Add(b);
            }
        }
    }

    public void Clear()
    {
        foreach (GameObject b in blocks)
            if (b != null) Destroy(b);

        blocks.Clear();
    }
}

第 3 部分 — 三个角色

  • 三个 capsule 放在 Y = 1,颜色各不相同,全都作为一个叫 Characters 的空物体的子物体。

  • 三个都挂 SimpleMover,GameManager 上挂 CharacterSwitcher,按键用 1 / 2 / 3 和 Tab。

  • Camera 跟随当前被操作的那个角色。

第 4 部分 — 数值面板

一个小小的 HUD,用来证明你的脚本之间能互相通信。这是你第一次让两个自己的脚本去读第三个脚本里的数据。

ArenaHud.cs — attach to any object, no UI package needed
using UnityEngine;

public class ArenaHud : MonoBehaviour
{
    [SerializeField] private ArenaBuilder builder;
    [SerializeField] private CharacterSwitcher switcher;

    // OnGUI is the quick-and-dirty way to draw text. Fine for an exercise,
    // never ship it — real UI lives on a Canvas, like the HudPanel from the last lesson.
    void OnGUI()
    {
        GUI.skin.label.fontSize = 20;
        GUI.Label(new Rect(20, 20, 400, 30), "Blocks: " + builder.BlockCount);
        GUI.Label(new Rect(20, 50, 400, 30), "Active: " + switcher.ActiveCharacter.name);
        GUI.Label(new Rect(20, 80, 400, 30), "R = rebuild");
    }
}

完成标准

  • ✓按 Play 后能看到地面和 16 个颜色、高度随机的方块,一个都不是手动摆的。
  • ✓按 R 会用新的随机值重建网格,数量统计依然正确。
  • ✓按 1、2、3 把控制权交给不同角色,同一时间只有一个能动。
  • ✓Camera 平滑地跟随当前角色,不抖动。
  • ✓HUD 实时显示方块数量和当前角色的名字。
  • ✓Console 是干净的 —— 没有你脚本产生的报错和警告。
  • ✓不用改代码,只在 Inspector 里调 columns、rows、spacing,结果就跟着变。

想再多做点

加分项 1

让方块用物理掉落:加 Rigidbody,把它们生成在 5 m 高处。

加分项 2

方块颜色不按随机,而是按到中心的距离来上。

加分项 3

加第四个角色,让切换器自动适配任意长度的列表。

加分项 4

让当前角色能推动方块:给方块加 Rigidbody,角色用 CharacterController 移动,再在 OnControllerColliderHit 里把它们推开。