U
08 / 22 · 11 分钟
常见问题

Unity 做 3D 游戏:五个阶段,只做一个项目

简短回答

新手想做一个 Unity 游戏,该从哪里开始?

用五个阶段做出一款 3D 动作游戏——Knight、Mage、Archer 在同一个竞技场对战——从空项目一路到手机上的 APK,不要求你提前会写代码。每个阶段都以一个能按、能测的东西收尾,所以项目是被做完,而不是被重开。

这就是完整路线:这头是一个空的 Unity 项目,那头是一个 3D 动作游戏——Knight、Mage 和 Archer 在一个竞技场里对战,就装在你口袋里的手机上。五个阶段,自始至终同一个项目,也不要求你提前会写代码。每个阶段结束时都留下一个能看、能按的东西,所以你任何时候都不用猜自己做的到底跑没跑起来。

第一个游戏做不完,原因很少是难度。更常见的是:屏幕上的这个项目跟你想做的不太一样,于是你重新开始——换个引擎、换个更大的点子、翻开一个从来没人看完的教程系列第三集。做完一个项目学到的,比弃坑十个多得多,道理很朴素:只有做完,你才会被拖着走过那段烦人的尾巴——构建设置、没人测过的分辨率、三个敌人同屏时掉到 30 的那些帧。

60 秒准备

  1. 1

    Unity Hub > Installs > Install Unity,选 Unity 6 LTS 那个版本。模块列表里勾上 Android Build Support 和 Microsoft Visual Studio Community:Hub 会把编辑器和 IDE 一起装好,所以你根本没有一个单独的 Visual Studio 配置步骤可以搞砸。

  2. 2

    New project,模板选 Universal 3D,放在 SSD 上,名字取短、不带空格。Android 模块现在就勾,别等五个阶段之后再去开 File▸Build Profiles —— 事后补模块等于重装一次,而第 5 阶段绝不是发现你漏装它的好地方。

  3. 3

    编辑器提示登录时用免费的 Unity ID 登录。激活的是 Personal 许可证,五个阶段全都够用 —— 不用买,也不用再装别的。没登录之前 Unity 压根不会打开,所以它是第三步,不是页脚的一句注释。

地图:五个阶段,同一个场景

每个阶段对应课程里的一个标签页,下面每个名字都直接链到那个页面并列出它的所有课程。数量和分钟数取自课程本身,所以在这里不可能过期。

第 1 关 · 基础 — 11 课,100 分钟

  • 一片贴了材质的地面,还有一格由代码而不是鼠标摆出来的方块网格——所以以后换一种排布也行。

  • Knight、Mage、Archer 三个角色用 1、2、3 切换,外加一个带音乐、读入竞技场的开场界面。

第 2 关 · 角色 — 9 课,114 分钟

  • Knight 有了身体:CharacterController、重力、能稳稳落到第 1 关方块上的跳跃,以及跟着动作走的动画。

  • Q/E/R 三个带冷却的技能,从对象池里复用的弹道,以及会追击、会掉血、会死的敌人。

第 3 关 · 镜头 — 6 课,67 分钟

  • 第三人称镜头绕着竞技场转而不穿墙,被打中时还会震——而在它之前你已经亲手写过一次跟随相机,所以你知道那个包在替你干什么。

  • 一套输入映射同时供键盘、手柄和屏幕摇杆:还是那场战斗,一根拇指就能打。

第 4 关 · 世界 — 7 课,83 分钟

  • 灰色盒子变成了一个地方:地形、烘焙光加上天空盒和雾、用 1×1 米模块拼出来的布景、脚步声和一个混音器——而且做的同时帧率还得保住。

  • demo 变成了游戏:从四个出生点刷出的一波波敌人、一个胜利面板、一个失败面板,还有一个不用关掉应用就能重来的按钮。

第 5 关 · 手机 — 7 课,83 分钟

  • 能拿去发布的 Player Settings:包名、IL2CPP 和 ARM64、一个 keystore,还有一个你交给别人、对方就能直接安装的 APK。

  • 关掉应用也不会丢的存档、为手机而不是为编辑器调好的 URP,以及在你手上那台设备上的 60 FPS。

第 1 关:地面、方块和三个角色

第 1 关故意做得小,但它不是热身:它就是整门课后续发生的那个竞技场。走完时你有:一片地面、由两层循环生成的方块网格、三个能走动操控的角色,以及一个带音乐、能载入战斗的开场界面。在所有东西变好看之前,这些已经都存在了。

顺序是特意排的:四节短 C#,然后编辑器,然后才是场景。不是为了教你编程——是为了让一个 Unity 脚本能被读懂:变量装着什么,Update 为什么每帧跑一次,一个 class 在你见过它躺在 Inspector 里之后到底是什么,以及第一个游戏到底需要多少 if、for 和 List——比你担心的少。接着是那几个你真正会住在里面的窗口。最后才是地板上的一个物体。C# 在 Unity 前,Unity 在游戏前,而每一步花的是半天,不是一个月。

ArenaBuilder.cs — the block grid, in two loops
using UnityEngine;

public class ArenaBuilder : MonoBehaviour
{
    public GameObject block;
    public Vector2Int size = new Vector2Int(6, 6);
    public float spacing = 2f;

    void Start()
    {
        for (int x = 0; x < size.x; x++)
        {
            for (int z = 0; z < size.y; z++)
            {
                Vector3 pos = transform.position + new Vector3(x * spacing, 0f, z * spacing);
                // The fourth argument makes the block a child of this object.
                Instantiate(block, pos, Quaternion.identity, transform);
            }
        }
    }
}

一层循环套一层循环,出来三十六个方块。这就是生成而非摆放的意义:到第 4 关想把竞技场撑大时,网格改一个数字就行,没人需要再拖一次 cube。

第 2、3、4 关:第一下攻击、镜头,以及一个能打的地方

第 2 关:会自己回本的机制

角色拿到 CharacterController、重力,以及一个真能落在第 1 关方块上的跳跃;接着 Animator 状态机把双腿从脚本手里接走,于是 idle、run、jump、attack 都来自一棵混合树,加上由移动代码本来就写好的那些变量驱动的过渡。值得大声 记住的是技能:每个技能都是一个 ScriptableObject 资源,装着它的冷却、伤害和弹道。

SkillData.cs — the numbers live in an asset, the logic lives in a script
using UnityEngine;

[CreateAssetMenu(menuName = "Game/Skill")]
public class SkillData : ScriptableObject
{
    public string skillName;
    public float cooldown = 1f;
    public int damage = 10;
    public GameObject projectile;
}

// The caster keeps one gate per slot; the asset only supplies the numbers.
public class SkillCaster : MonoBehaviour
{
    public SkillData[] skills;
    float[] nextReadyAt;

    void Awake() => nextReadyAt = new float[skills.Length];

    public bool TryCast(int slot)
    {
        if (Time.time < nextReadyAt[slot]) return false;
        nextReadyAt[slot] = Time.time + skills[slot].cooldown;
        return true;
    }
}

划算的正是这个切分。别人想调的数值待在 Inspector 里,而不是躲在弹道逻辑也住着的那个类里,所以你重调 Slash 的平衡时不可能弄坏发射它的东西。第四个技能就是文件夹里多一个资源、已有数组里多一项——这就是为什么跟 ScriptableObject 待一小时,比写一星期 if 链更值。

第 3 关:人们最爱跳过的那部分

第 3 关决定除了你之外还有没有人能玩。跟随相机你先手写一次——在 LateUpdate 里朝一个偏移做 Lerp——然后再认识 Cinemachine,于是你清楚那台虚拟相机和它的 brain 到底在替你做什么,视野穿进墙里时该怀疑谁。接着 Input System 的一张 action map 换掉项目里每一处按键判断:一个有名字的 Move 动作,由键盘、手柄摇杆和 Canvas 上的屏幕摇杆共同驱动。最后那个 binding,就是一个 Unity 项目和一个手机游戏之间的差别。

第 4 关:从 demo 到游戏

第 4 关就在同一个 Arena.unity 上做两件事。第一件是给它穿衣服——地形、烘焙的方向光加天空盒和雾、用 1×1 米模块和 prefab 变体拼出的布景、脚步声和一个混音器——同时用 LOD 组、合批和遮挡剔除让装扮后的场景仍然跑得动,因为 20 FPS 的漂亮竞技场不能玩。然后 GameDirector 把它变成游戏:从四个出生点刷出的波次、胜利面板、失败面板,还有重开。那里没有任何东西是从零想出来的,只是把第 1 到第 3 关的系统接到了一起,而那个接线,就是人们说的游戏循环。

  • 伤害是走 CharacterStats 上的一个事件,而不是引用那个被打中的对象,所以血条、伤害数字和受击白光都订阅同一个事实。这一关后面全部内容都靠这个模式撑着。

  • 敌人走的是烘焙出来的 NavMesh,而不是手写的追击逻辑。想多花那十五分钟,这才是该花的地方:路线在 Scene 视图里看得见,第 4 关把石头挪走时重新 bake 一次就行。

第 5 关:装进真正的手机

最后一关是几个小时的设置,外加一个回报整门课的瞬间:游戏跑在一台不是你电脑的机器上。把构建目标切到 Android,登记场景列表,设置包名、IL2CPP 和 ARM64,然后点 Build。光是这样你就有了一个能 USB 安装、或者发给别人让他们自己装好的 APK。而上传 Google Play 要的是 AAB,在同一个窗口里多勾一个框就有。

  1. 1

    切换平台会让 Unity 把所有贴图重新导入成 Android 能读的格式,所以先点下去再去煮咖啡。然后在 File▸Build Profiles 里按 Add Open Scenes:没列进那个清单的 scene 在游戏里就不存在,这也是构建打开后一片黑最常见的原因。

  2. 2

    debug 构建会自己签名,所以测试不需要 keystore;Google Play 认的是签好名的 release 构建。keystore 文件和它的密码要留好——丢了意味着丢掉应用的身份,而不是丢掉一个设置窗口。

  3. 3

    在真机上行为不对时,adb logcat 就是手机正在讲的那段对话。一条带过滤的命令,游戏自己关掉的原因就摆在屏幕上——构建成品那篇里有它,还有导致大多数启动闪退的 IL2CPP 代码裁剪。

  4. 4

    帧率是最后一个 boss:并非每台手机都会把 Application.targetFrameRate 自己设成 60,而编辑器里的 FPS 完全说明不了真机。修法就是性能那节课里那些无聊的事——压缩贴图、减少 draw call、给布景加 LOD、用 URP 而不是内置管线——而且要在那台手机上量,不是凑在它旁边看。

让第一个游戏永远做不完的五个错误

  • 范围。第一个想法永远偏大,因为那是你最熟的那个。开放世界生存游戏要一百个系统;一个竞技场、三个角色、一套波次循环只要大约十个,而这十个正是所有游戏都需要的那十个。一直砍到计划让你有点不好意思——那个尺寸差不多就对。

  • 项目做到一半换引擎。别处的草更绿,只是因为你还踩在上面。每个引擎都在第二个月左右有一堵墙,换引擎并不能带你翻过去——它只会把计数清零。先用手上这个,把一样东西做完。

  • 从不发布。没人玩的构建会让每个决定都悬着,于是你永远不用回答到底哪个错了。只有发布算测试:一个不是你本人的人拿起手机,在你没看着的地方卡住。这很难受,而这份难受就是全部的产出。

  • 等美术。灰盒阶段不是难看所以跳过的部分,它是游戏必须好玩的那部分。一间全是胶囊和方块、不能玩的屋子,换上漂亮角色依然不能玩,只是改起来更贵。先把方块起跳的手感调对,再把模型放上去,不碰代码。

  • 转圈式学习。第九个讲 CharacterController 的教程感觉像进步,其实什么都没换来,因为你第十个不知道的东西,只有在第九个上栽跟头时才会冒出来。读手册只是学习;让一个胶囊体迈过方块而不掉穿地面才是工程。做后者,等项目向你索要答案时再去查。

什么才算做完

  • ✓游戏从菜单开始,能打一波,也能输——然后按一个按钮重来,而不是关掉应用。
  • ✓构建装在手机上、编辑器已经关掉:不接任何电脑,游戏也能跑。
  • ✓一个从没见过这个项目的人能玩两分钟,而且没问你按哪个键。
  • ✓在那台设备上它稳得住 60 FPS,或者你说得出哪里掉、为什么掉。
  • ✓关掉应用再打开,进度会回来——存档活着,你改过的那个设置也活着。
  • ✓你能用一句话说出还缺什么。那句话就是你在下一个项目里最先修的东西——这就是重点:因为这一个做完了,下一个会开始得更快。

下一步只有一页那么长:打开第一个标签页 基础,从第一课开始。它默认你从没写过代码,而结束时你的场景里会有一片地面,三个角色站在上面。

做出这个的那一课

60 秒看懂 C#:变量

这篇指南单独成篇,是一份可以直接照着做的配方。在课程里,同样的东西会作为贯穿全部五个章的那个项目的一部分来搭建 —— 基础,第 01 课。