资讯动态

Unity 2D新手项目:Ruby‘s Adventure主角与第一脚本全解析

发布时间:2026/9/9 7:24:10 来源:尧图企业网站定制
做Unity 2D新手项目Ruby‘s Adventure是绕不开的一课。这是Unity官方放出来的免费2D游戏案例大家习惯叫它“Ruby的冒险”而“主角和第一脚本”这一段正好是整个项目里从“摆场景”转向“写逻辑”的关键节点。你会发现主角落地之后不再是一张静态图片通过Rigidbody2D、Animator和一份C#脚本它第一次能被键盘控制、随方向切换动画。这一节解决的不只是“让角色动起来”更重要的是让你理解Unity推荐的组件化协作方式GameObject只是壳动画、物理、输入处理各司其职脚本负责把能力串起来。适合谁刚学完Unity界面操作、准备进入2D游戏开发的新手以及想要快速搭一个2D横版或俯视角色控制器的同学。下面我按自己实际做这个项目的顺序把这节课拆开讲透。1. 项目整体拆解为什么Unity官方教程要让你先做“主角和第一脚本”1.1 Ruby’s Adventure在2D学习路径中的位置Unity官方学习路径里Ruby‘s Adventure是给“已经会基础操作、但还没有写过完整游戏逻辑”的开发者准备的。前面你可能已经学会了创建场景、摆放Tilemap、给地面加碰撞器但这些操作都还停留在编辑器层面。到了“主角和第一脚本”这一节才第一次真正进入代码驱动的游戏循环玩家按方向键角色移动动画切换摄像机跟随。这个位置安排得很讲究。移动控制是所有动作游戏的地基后面你想加敌人巡逻、拾取物品、触发机关、播放对话都离不开“主角能走动”这个大前提。而且Ruby项目刻意把复杂度控制得很好没有跳跃、没有重力、没有敌人AI主角就是一个在俯视角场景里漫游的小女孩。这种设计让新手能把全部注意力放在“脚本是怎么和组件协作”上而不是被一堆物理参数淹没。1.2 主角对象背后的组件化设计思路很多人第一次接触Unity会犯一个思维错误以为“主角”是某种特殊物体写代码时想在一个脚本里把移动、动画、碰撞全部搞定。实际上Unity的核心思想是组件化。一个名为Ruby的GameObject本质上只是一个容器它身上挂了SpriteRenderer负责显示Animator负责播放动画Rigidbody2D负责物理模拟BoxCollider2D负责碰撞检测最后再加一个你自己写的脚本负责接收输入并指挥这些组件工作。这样的好处是职责清晰。显示出问题查SpriteRenderer和动画面板物理出问题查刚体和碰撞体输入没反应查脚本和Input设置。你不用在一个几百行的脚本里翻找问题根源。后面写敌人、NPC、道具时你会发现几乎所有对象都是同一套模式先组装组件再写一个控制脚本。Ruby这个项目就是一次最标准的组件化示范。2. 动手前准备Unity版本、2D项目模板和官方素材导入2.1 选对Unity版本和项目模板做Ruby‘s AdventureUnity版本建议选LTS版本目前主流是2021 LTS或2022 LTS别追最新版。不是说新版不能用而是这套官方教程用旧版Input Manager写新版本默认可能开启新输入系统会让新手平白多踩一个坑。我自己带人做这个项目时最常见的卡点是打开项目后脚本报错一查往往是输入系统不兼容。创建项目时在Unity Hub里选“2DBuilt-In Render Pipeline”模板即可。如果编辑器里看到Universal 2D模板也能用但对这个项目来说内置渲染管线更省事。重点看一个设置Edit Project Settings Player Other Settings Active Input Handling。如果你用的是Input.GetAxisRaw这种老接口这里需要是Input Manager (Old)或者Both。如果当前是Input System Package (New)旧代码会直接报“InvalidOperationException: You are trying to read Input using the Input class, but you have switched active Input handling to Input System package”解决办法就是把这个选项改成Both或Old改完重启编辑器。2.2 导入官方素材与认识项目结构素材获取方式很简单打开Unity Learn页面找到Ruby‘s Adventure课程按引导把资源包导入当前项目或者在Asset Store里搜索官方2D Beginner教程资源。导入后Project窗口会多出一个文件夹里面通常包含Art、Prefabs、Scenes、Scripts等子目录。官方示例场景可以直接打开跑起来看看最终效果角色可以在一个2D场景里自由走动播放跑步动画切换方向。这个素材包对新手特别友好因为它把美术资源、动画剪辑、预制体都准备好了你不需要自己画图也不用手K动画。我建议拿到素材后先花5分钟把Prefabs目录里的Player预制体拖到场景里看一下重点观察它的Inspector面板有哪些组件、Animator Controller里挂了哪些状态。这种“先看成品再动手拆”的学习方式比一上来就自己从零组装高效得多。2.3 场景与相机初始设置如果你的场景是空场景拖入Ruby角色后还要调整相机。2D项目里相机默认是Orthographic投影模式意味着没有透视变形适合做2D游戏。相机的Size参数决定屏幕上能看到多少世界单位数值越小画面拉得越近。Ruby素材的美术风格是小型像素角色相机Size一般设置在5到8之间具体要看你的Tilemap尺寸。场景里铺好地面后把相机拖到能看到角色全景的位置即可。另外建议在项目里添加一个“Player”的Sorting Layer。选中Ruby角色在SpriteRenderer组件里可以把Sorting Layer设为Player并设一个合适的Order in Layer值。Sorting Layer从上到下决定渲染顺序Order in Layer在同层内进一步排序。比如地面是Default层角色放在Player层这样角色永远盖在地面上不会被地面的图块挡掉。这是2D开发里非常基础但特别重要的层级概念Ruby项目里你就能直观体会到。3. 创建主角精灵、动画状态机与物理组件的组合3.1 场景中生成玩家角色有两种方式在场景里创建主角直接拖官方素材包里的Player预制体或者手动把Ruby的Sprite拖进场景再补组件。新手我建议直接拖预制体因为玩起来不会出错。但这里的学习重点不是“能跑”而是搞清楚预制体上每个组件的作用所以哪怕你用预制体也要逐个打开看看。SpriteRenderer负责显示角色贴图默认会关联一张Ruby的正面立绘。如果你发现角色显示异常先检查SpriteRenderer的Sprite字段有没有正确赋值再检查Sprite导入设置里的Filter Mode和PPUPixels Per Unit。PPU决定这个精灵在世界坐标中的大小比如素材是16x16像素、PPU设为32那这个角色在世界里就是0.5个单位宽。如果角色在画面里显得过大或过小先调PPU和相机Size不要直接暴力改Transform的Scale否则后续碰撞器不好对齐。3.2 动画状态机与参数设计接下来是Animator。主角平时站着不动要播待机动画移动时要播跑步动画而且上下左右四个方向最好都有对应动作。这套逻辑用Animator Controller实现。打开Animator窗口你会看到状态机面板。官方素材包里往往自带一个Controller里面可能已经有一些状态和过渡你要做的是确认“控制切换的参数”和“脚本里写入的参数”名字一致。如果你是自己从头创建推荐用2D Blend Tree而不是一堆Transition。Blend Tree适合处理方向运动把Idle、Up、Down、Left、Right这些动画剪辑混合到一个连续的状态里。具体操作在Animator窗口右键 Create State From New Blend Tree双击进入Blend Tree然后在Inspector里把Blend Type设为2D Simple DirectionalParameters选两个float类型参数比如moveX和moveY。接着把四个方向的跑步动画拖进Motion列表分别设置它们对应的坐标比如向左是-1,0向右是1,0向上是0,1向下是0,-1。脚本里只要持续写入moveX、moveY状态机就会自动切换对应动画。参数类型不要用错。在Animator的Parameters面板里moveX、moveY要设为Float后面脚本用anim.SetFloat写入。如果官方自带的Controller里参数名和你脚本不一致比如它叫Speed你在脚本里就写“Speed”别强行改成moveX。新手最常见的动画不切换问题十有八九是参数名对不上。3.3 物理体与碰撞体的实际配置值给角色添加Rigidbody2D时注意几个关键属性。Body Type设为Dynamic这是动态刚体会受到物理模拟影响Gravity Scale设为0因为这个2D游戏是俯视角角色不需要重力往下掉。这一步非常重要如果你忘记设0角色一运行就会往下掉。Constraints里建议勾选Freeze Rotation Z否则角色撞到障碍物时可能会旋转看起来像倒地了一样。BoxCollider2D要手动调整大小。直接点Edit Collider按钮在Scene视图里把绿色线框拖到和角色身体差不多大小即可注意不要覆盖住整个Sprite的透明区域否则会出现“明明没碰到墙壁却卡住”的错觉。不同版本的免费素材里Ruby角色形态略有差异但原则是碰撞器贴合身体轮廓稍微缩一点都比完全覆盖好。Ruby项目里捡物品、触发对话都是靠碰撞器触发所以这一步别马虎。4. PlayerController脚本移动逻辑的完整实现与逐行解读4.1 脚本文件命名与MonoBehaviour生命周期在Project窗口右键 Create C# Script把脚本命名为PlayerController。注意文件名必须和类名一致这是C#的硬性要求改掉类名却没改文件名会导致“The associated script can not be loaded”的报错。脚本创建后直接拖到Ruby角色上或者用Add Component搜索脚本名挂载。Unity脚本的生命周期方法最开始只需要关注三个Awake在对象创建时调用适合获取组件引用Start在第一帧Update之前调用适合做初始化Update每帧调用适合处理输入和逻辑。还有一个FixedUpdate是固定时间间隔调用的适合处理物理操作。Ruby项目里移动代码放Update还是FixedUpdate其实都能跑但为了规范我下面会给出两种写法之间的取舍说明。4.2 基于Input Manager的移动代码逐行解析下面这份代码是Ruby项目最经典的PlayerController我一行行讲using System.Collections; using System.Collections.Generic; using UnityEngine; public class PlayerController : MonoBehaviour { public float speed 5f; private Rigidbody2D rb; private Animator anim; void Start() { rb GetComponentRigidbody2D(); anim GetComponentAnimator(); } void Update() { float moveX Input.GetAxisRaw(Horizontal); float moveY Input.GetAxisRaw(Vertical); Vector2 movement new Vector2(moveX, moveY).normalized * speed; rb.velocity movement; anim.SetFloat(moveX, moveX); anim.SetFloat(moveY, moveY); anim.SetFloat(speed, Mathf.Max(Mathf.Abs(moveX), Mathf.Abs(moveY))); if (moveX 0) { transform.localScale new Vector3(1, 1, 1); } else if (moveX 0) { transform.localScale new Vector3(-1, 1, 1); } } }先看输入部分。Input.GetAxisRaw(“Horizontal”)会返回-1、0或1分别代表左、不按、右Vertical对应上下。注意GetAxisRaw和GetAxis的区别GetAxis带平滑滤波松开按键后数值会逐渐回到0角色会有“滑行”手感GetAxisRaw是即时值响应更直接适合像素风2D游戏。Ruby这个项目里我推荐GetAxisRaw控制更跟手。然后看向量处理。new Vector2(moveX, moveY)直接乘speed斜向移动时会出问题同时按上和右向量长度是√2约1.414角色斜着走会比直线快。加一个.normalized就能把向量长度保持为1这样四条线方向速度一致。这个细节很多人会漏但对游戏手感影响很大。再解释为什么用rb.velocity而不是transform.Translate。Rigidbody2D是物理组件它的运动应该交给物理系统管理。如果用transform.Translate直接改位置会绕过物理模拟可能导致碰撞检测不准确甚至穿过墙壁。虽然很多老教程喜欢用transform.Translate但Ruby项目这种2D俯视角游戏用Rigidbody2D.velocity更规范。由于velocity是按物理帧更新的这里不需要乘Time.deltaTime直接赋速度值即可。动画部分用anim.SetFloat把moveX、moveY和speed写进状态机。如果Blend Tree只靠moveX、moveY驱动那speed可以不用但如果你用状态机里的Transitionspeed可能作为“是否移动”的开关。Mathf.Max(Mathf.Abs(moveX), Mathf.Abs(moveY))的意思是取两个方向绝对值较大的那个这样斜走时speed也能正常为1。最后是角色翻转。Ruby素材的Sprite默认朝左或朝右通过翻转Transform的localScale.x可以让角色左右转向。这套方法简单直接但如果素材里已经做了四方向动画你也可以不用localScale而是靠Blend Tree切换左右方向动画。两种方案选一种即可不要混用否则会出现角色在某个方向变扁的怪异效果。4.3 基于新Input System的可选方案如果你的Unity项目激活的是新输入系统上面代码会报错。解决方案有两个一个是前面说过把Active Input Handling改成Both或Old另一个是用新Input System重写移动逻辑。这里给一个简化版参考using UnityEngine; using UnityEngine.InputSystem; public class PlayerController : MonoBehaviour { public float speed 5f; private Rigidbody2D rb; private Vector2 moveInput; public void OnMove(InputAction.CallbackContext context) { moveInput context.ReadValueVector2(); } void FixedUpdate() { Vector2 movement moveInput.normalized * speed; rb.velocity movement; } }使用新输入系统时你要给角色添加PlayerInput组件配置InputAction资产并确保Action名为Move的“Performed”“Canceled”事件指向PlayerController的OnMove方法。这个方案代码更简洁但配置过程对新手来说反而多了一层理解成本。我个人的建议是第一次做Ruby项目直接用旧Input Manager跑通逻辑理解“输入→逻辑→物理→动画”这条链路之后再回来学新输入系统。否则你会在输入系统的配置环节陷进去忘了主角为什么要动。4.4 摄像机跟随的实现与LateUpdate角色动起来之后如果不做摄像机跟随角色跑两步就出屏幕了。Ruby项目里摄像机跟随用的是平滑跟随。方法很简单新建脚本CameraFollow挂到主相机上把目标设为Ruby角色。using UnityEngine; public class CameraFollow : MonoBehaviour { public Transform target; public float smoothTime 0.2f; private Vector3 velocity Vector3.zero; void LateUpdate() { if (target null) { return; } Vector3 targetPosition target.position; targetPosition.z -10f; transform.position Vector3.SmoothDamp(transform.position, targetPosition, ref velocity, smoothTime); } }为什么在LateUpdate而不是Update因为Update阶段主角刚移动完摄像机在LateUpdate里再跟随可以保证这一帧的画面完整所有对象的位置都更新完了相机才挪过去不会出现角色先移动、相机晚半拍的视觉卡顿。SmoothDamp是做阻尼平滑0.2秒的smoothTime意味着相机不是瞬间跳到角色身上而是带一点追赶效果手感更柔和。如果想做“硬跟随”直接transform.position target.position再设z为-10即可。另一个关键点是相机z轴。相机默认在z-10的位置视角朝正z方向看。如果跟随脚本里直接把target.position赋给相机相机的z会变成0然后就看不到场景了。所以不管用哪种跟随写法都要把z固定为-10或者用相机初始的z值。5. 调试与避坑新手做Ruby项目最容易踩的坑5.1 角色不动、动画不切换的排查顺序做Ruby项目新手问得最多的就是“脚本挂上了角色还是不动”。遇到这个问题我建议按这个顺序排查。第一步看Console窗口有没有红色报错编译错误会导致所有脚本失效最常见的是类名和文件名不一致、少了一个大括号。第二步看Inspector面板里脚本组件有没有显示“No script”或者“Script cannot be loaded”有的话检查文件名和类名。第三步看Rigidbody2D和Animator组件是否存在脚本里GetComponent如果找不到组件会返回null调用rb.velocity时会直接报空引用。第四步看Input.GetAxisRaw是否真的拿到了输入最简单的验证方式是在Update里加一行Debug.Log(moveX “,” moveY)运行后按方向键看Console输出。如果数值始终是0说明输入系统配置有问题按前面说的检查Active Input Handling。动画不切换的坑又是另一类。脚本里anim.SetFloat的参数名必须和Animator面板里的参数名完全一致大小写也要一致。另一个问题是Transition条件设置错误比如你在Blend Tree的入口加了一个“speed大于0.1才切换”的条件但脚本里只写了moveX、moveY那动画永远切换不了。建议把Blend Tree的参数直接设为moveX、moveY脚本同步写入这两个值逻辑最简单。5.2 角色抖动、卡顿与物理参数失常角色运行时抖动通常是几个原因叠加的。第一是刚体的Interpolate没有设置。Rigidbody2D组件里有一个Interpolate选项默认None如果角色移动看起来一顿一顿的改成Interpolate可以平滑渲染帧和物理帧之间的间隙。这个选项不会改变真实物理状态只影响视觉表现2D像素游戏加上它观感会好很多。第二是角色卡在墙壁边缘。检查BoxCollider2D的大小是不是比Sprite可视部分大了一圈尤其是贴图有透明边时碰撞器默认会包住整个贴图像素范围。另一种情况是Sorting Layer没问题但Collider Editor Mode无意间改动了碰撞器偏移了。双击角色打开Collider的Edit Collider把边界重新拉一下。第三是角色运动速度异常快或者加normalized之后移动速度感觉不对。先用Debug.Log把movement向量打出来看看如果speed设成5但实际感觉像飞检查脚本里是否同时存在多个移动逻辑比如既有PlayerController又沿用了某个旧脚本两个脚本同时给rb.velocity赋值就会互相覆盖。5.3 我在实际项目里的几个扩展建议Ruby项目跑通之后别急着关掉。我个人建议趁热打铁做两个扩展。第一个是把移动改成真正的8方向动画控制。官方示例里大多数版本只有左右翻转和上下方向感觉像横版游戏。如果你把Blend Tree的Type设为2D Freeform Directional并把上下左右动画都挂进去角色就能朝8个方向自然移动游戏手感立刻提升一个档次。第二个是加一个“交互锁”比如角色进入对话、打开菜单时禁用移动输入。实现方式很简单在PlayerController里声明一个public bool canMove trueUpdate开始时判断if (!canMove) { rb.velocity Vector2.zero; return; }。Ruby后面有对话系统、机关触发这个bool会在你实现NPC交互时起到大作用。还有一个细节值得注意脚本对组件的引用最好在Start里获取一次不要在Update里反复GetComponent。GetComponent有性能开销虽然单个角色感觉不到但养成缓存引用的习惯对后续开发复杂项目非常有帮助。这也是Ruby官方教程里很早就在强调的代码习惯。最后再分享一个我做Ruby项目时常用的调试思路把角色当成一个“信号中心”。输入产生信号脚本处理信号动画和物理响应信号。出问题时从信号源头逐段排查按键有没有信号脚本有没有收到信号组件有没有响应信号。用这个思路你会发现大多数问题都能在五分钟内定位。做完整个Ruby项目真正得到的不是那几十行代码而是这套排查和拆解问题的能力。后面做任何2D游戏再回头看你写的第一个PlayerController就会明白这一课的价值在哪里。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价