练习:竞技场初具雏形
Assets/
Scenes/
Arena.unity
Scripts/
Prefabs/
你已经有的这一课新增这一课替换
搭一座赛场,课程剩下的全部内容都在这里进行。只需要一个叫 Arena 的场景:一块地面,一组由代码生成的方块网格 —— 就是 tab 02 里你要跳来跳去的那些方块 —— 三个可以切换的角色,以及屏幕上一块小小的数值面板。
复用的东西
做完之后的场景,看起来和跑起来都应该是这样。
要做什么,一步一步来
第 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,就退回去改循环。
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,用来证明你的脚本之间能互相通信。这是你第一次让两个自己的脚本去读第三个脚本里的数据。
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,结果就跟着变。
想再多做点
让方块用物理掉落:加 Rigidbody,把它们生成在 5 m 高处。
方块颜色不按随机,而是按到中心的距离来上。
加第四个角色,让切换器自动适配任意长度的列表。
让当前角色能推动方块:给方块加 Rigidbody,角色用 CharacterController 移动,再在 OnControllerColliderHit 里把它们推开。