Unity官方教程 2D Roguelike(1):动画和预制件

2D Roguelike 最终效果

前言

Unity官方教程 2D Roguelike 英文原版视频教程地址 ——> 传送门

由于官方视频使用的Unity版本较老,部分方法已经不再使用,本文已经采用较新的Unity版本和对应的替代方法。另外为了更方便理解,文章讲述的顺序和官方视频会有少许出入(结果是一致的)。

电脑环境:Windows10
Unity版本:2017.2.0f3
脚本/代码编辑器:MonoDevelop 5.9.6

目标游戏理解

在开始一个项目之前,需要清楚即将要制作的是什么样的一个游戏,这样才能更容易地理解官方代码。简单来说,2D Roguelike 是一个基于回合和瓷砖的地牢探险类游戏,玩家控制大胡子(角色)移动,进行拾取食物、躲避敌人等操作以便更好地通关进入下一个关卡,直到耗尽生命游戏结束。它具备以下内容:

  • 每次尝试移动(比如键盘按一次方向键)都会扣除生命。
  • 拾取食物可以恢复定量生命。
  • 可以攻击打碎障碍墙从而开辟路线。
  • 大胡子需要避开怪物,以免被攻击减少生命。
  • 越后面的关卡,怪物数量越多。

一个规则比较简单的2D游戏,OK,摩拳擦掌,开始吧!ᕦ(・ㅂ・)ᕤ

本节你将学会什么?

  • 如何给GameObject添加动画Animation
  • 如何重写动画控制器
  • 对Tag/Layer/Sorting Layer有基本的了解

一、新建一个项目Project

如何创建项目不再赘述,有需要的朋友可以查看Unity官方教程 2D UFO(上)的内容了解一下。本次新项目的名称为2D Roguelike(可自定义),记得是选2D!

二、在Asset Store中下载素材

Ctrl+9(或菜单Window->Asset Store)打开Asset Store商店,在最上方搜索栏输入我们想要查找的游戏资源名称。本例是2D Roguelike,回车之后第一个结果就是我们想要的。

Asset Store

点击该资源,在页面找到Download按钮,下载完成之后即可导入Import。(已下载过了就只有Import按钮)

Import导入按钮

点击Import之后,会弹出导入资源列表选项。在这里一定要确保所有资源都勾选了(默认情况下是全部勾选),保险起见也可以点击All按钮选中全部资源,然后便可以点击Import按钮来进行导入操作了。

导入资源选择

导入成功之后,我们可以看到在Project窗口的Assets文件夹下多出了很多资源文件。

Assets文件夹

Audio:存放音乐、音效文件
Fonts:存放字体文件
Sprites:存放精灵图

删除2个无用的文件夹_Complete-GameTutorialInfo,新增以下四个资源文件夹。

  • Animations 包含AnimationControllersAnimations文件夹,分别存放动画管理器和动画
  • Prefabs 存放预制件文件
  • Scenes 存放场景文件
  • Scripts 存放脚本文件
最终的Assets文件夹

合理分类资源文件,以便查找和管理,是个非常必要的好习惯。

三、保存场景

在保存场景之前先调整下Unity编辑器的窗口布局,根据游戏界面和自己的工作习惯。编辑器右上角的Layout按钮点击打开进行选择即可改变布局,在这里我们推荐选择Default布局,也是官方推荐使用的。

Layout

保存场景的方法在之前Unity官方教程 2D UFO(上)有提到,不再赘述。场景保存在Scenes文件夹,名字依旧是Main

四、创建角色动画和预制件

大胡子有三种动画状态需要添加:空闲idle、劈砍chop、受击hit。默认情况下大胡子处于idle状态,当大胡子击打障碍墙的时候需要切换到chop状态,被怪物攻击的时候播放hit动画。为了让大胡子动起来,并且和其他对象进行互动比如碰撞,还需要给大胡子添加刚体和碰撞器。

第1步:大胡子添加idle动画(空闲)

通过菜单GameObject->Create Empty(快捷键Ctrl+Shift+N)添加一个空白游戏对象,重置Transform,命名为Player

创建游戏对象之后,良好的习惯是先重置Transform和改名。

打开Sprites文件夹,点击里面精灵图的右侧小三角打开图片集,同时选中前六张,鼠标左键拖着它们移动到Player再松开。

把idle动画每一帧拖到Player

这时候会弹出存放动画的窗口,默认是Sprites文件夹,我们改一下,把它保存在之前已经创建好的文件夹Animations下的子文件夹Animations内,命名为PlayerIdle

保存PlayerIdle动画

我们可以看到,Animations文件夹下自动生成了一个动画控制器Player,它控制大胡子当前播放哪个动画。把它拖到AnimationControllers文件夹,这里专门存放动画管理器文件。

Player动画管理器

与此同时,因为Player添加了第一个动画,所以自动增加了两个组件。Sprite Renderer,精灵渲染器,管理图像的显示;Animator,控制是否播放动画。

Player的Inspector

第一个动画添加好了,让我们运行游戏测试一下吧!

抽搐的大胡子

有点担心,大胡子一副抽搐的样子,给人下一秒就要晕厥倒下的感觉 (;´д`)ゞ。
好吧不开玩笑,其实这是由于动画播放速度较快导致的。打开AnimationControllers文件夹,双击Player,打开Animator窗口。

Animator窗口

选中PlayerIdle状态,在右侧的Inspector窗口中修改Speed选项为0.5。这时候再运行游戏,就可以看到大胡子已经平静下来了。不错的开端!

第2步:大胡子添加chop、hit动画

添加chop和hit动画的操作其实是和添加idle动画一样的,只不过选择的图片不同。(不需要改这两个动画的速度٩(๑❛ᴗ❛๑)۶)

chop动画选择图片

chop动画保存命名为PlayerChop,Hit动画保存命名为PlayerHit

hit动画选择图片

我们会发现,添加后面2个动画的时候,并不会再新增动画控制器,而是在动画控制器内添加了两种动画状态。

Animator Controller 中新增的Chop和Hit状态

在后续的内容会提到如何在这三种动画状态中进行切换。

第3步:大胡子添加刚体和碰撞器组件

刚体能让大胡子受物理引擎控制,实现真实的物理表现效果比如移动。而没有碰撞器组件的话,两个物体之间也无法检测碰撞。
在Player的Inspector窗口点击最下方的Add Component按钮,搜索Rigidbody 2D添加。

Rigidbody 2D

继续搜索Box Collider 2D添加碰撞器组件。

Box Collider 2D

为了避免在相邻空间进行碰撞,我们把大胡子的碰撞尺寸缩小成0.9

Collider修改Size

第4步:制作Player预制件

关于为什么要制作预制件,之前在Unity官方教程 2D UFO(下)有详细说明,这里就不再细说了。
在制作Prefab之前,我们还需要对Player配置3个地方。

  1. Tag:选择Player。tag是Unity引擎里面的标签,一般用来对单一的GameObject进行标识。(对,有“Player”这个tag的GameObject就是大胡子没错了!)
Tag
  1. Layer:选择BlockingLayer。Layer就在Tag的右侧,是Unity引擎里面的层,作用和tag很相似也是对GameObject进行标识。但是他们的不同在于Layer常用于一组的GameObject,而Tag是单个。
Layer

3.Sorting Layer:选择Units。不知道大家思考过没有,当多个图片资源比如大胡子、食物、墙壁都在同一个坐标的时候,我们会看到什么?是大胡子在最上面覆盖了其他,还是食物,抑或墙壁?是什么控制了它们的渲染层级顺序?这其中的奥义就是Sorting Layer了!越上方的渲染层级越低,比如floor层就相当于背景,Units层级最高,优先看到的是Units层的图片,在此例中,大胡子和怪物都是Units层。

Sorting Layer

配置完毕,我们先运行游戏确保没问题再制作Prefab吧!

跳楼的大胡子

啊咧?!!!!∑(゚Д゚ノ) 大胡子直直地掉下来了!
前面提过,刚体可以让物体受物理引擎作用,所以不难判断出这是大胡子受到重力影响所以掉下来了。由于我们后续是通过变化大胡子的transform的位置来进行移动,并不依赖任何物理作用力,所以我们可以通过修改Body Type为Kinematic来解决这个问题。

Kinematic

Kinematic:开启运动学,开启后不再受力的影响,只能通过Transform属性来操作。

运行游戏,没问题了吧?(๑╹◡╹)ノ"""把Player往下拖到Prefabs文件夹即可生成预制件。

Player Prefab

五、创建怪物动画和预制件

创建怪物的动画和预制件的流程和角色是差不多的,只不过资源不一样。怪物只有两种动画:空闲idle、攻击attack。默认情况下怪物处于idle状态,当大胡子在隔壁格子并且轮到怪物回合的时候它会触发攻击操作播放attack动画。当然,怪物也有移动和碰撞,所以同样需要添加刚体和碰撞器。

第1步:怪物1添加idle、attack动画

删除Hierarchy窗口下的Player,通过菜单GameObject->Create Empty(快捷键Ctrl+Shift+N)添加一个空白游戏对象,重置Transform,命名为Enemy1
找到第一只怪物的idle动画帧图片,拖到Enemy1游戏对象上。

Enemy1的idle动画

依然是保存在Animations下的子文件夹Animations内,命名为Enemy1Idle。把生成的Enemy1控制器转移到AnimationControllers文件夹。

对资源进行合理分类保存是非常重要的习惯。

Enemy1控制器

运行游戏看看添加是否正常。

Enemy1的idle动画正常播放

动画正常播放,速度也可以。OK,接下来就是找到怪物1的攻击动画帧图片,和前面一样添加到Enemy1身上,保存为Enemy1Attack

Enemy1Attack

第2步:怪物1添加Rigidbody 2D和Box Collider 2D

怪物和角色一样,都会在关卡内走来走去,并且和其他元素(外墙、障碍墙、角色)均会发生碰撞。而这些都需要刚体和碰撞器的组件支持。添加这两个组件的方法在上面已经介绍,在这里只提一下,Box Collider 2D的Size选项均是1,Rigidbody 2D的Body Type选项为Kinematic

第3步:制作怪物1的预制件

在制作预制件之前,记得把Inspector窗口下的tag选择Enemy,Sorting Layer选择Units,Layer选择BlockingLayer。

Enemy1的Inspector

Layer,角色、怪物、外墙、障碍墙都会标识为BlockingLayer,这是因为此例中检测碰撞的方法是使用射线检测(后面脚本会写这方面内容),射线判断前方空间是否存在携带这个标识的碰撞体并且返回结果。(BlockingLayer是之前导入资源的时候预设好的,朋友们也可以自己添加新的Layer,然后在这一层检测碰撞)
最后,左键按住Enemy1不动往下拖到Prefabs文件夹,即可生成Enemy1预制件了。

Enemy1 Prefab

第4步:怪物2重写动画控制器

通过上面的操作,我们学到了给GameObject添加动画的一种方法,而接下来会接触到另外一个知识点:重写动画控制器。
由于怪物1和怪物2除了样子(图片资源)不同,其他机制均是一样。为了节省时间,我们把Enemy1游戏对象的名字修改成Enemy2

1改成2

然后和之前一样,分别制作Enemy2IdleEnemy2Attack动画。

Enemy2Idle
Enemy2Attack

这时候打开AnimationControllers文件夹,双击Enemy1控制器打开动画状态机Animator,会看到四种动画都已经作为状态添加在里面。

Enemy1控制器

由于还是原来的GameObject,只是修改了名字,所以并没有生成另外的动画控制器。
动画状态机Animator的功能是在不同的动画状态里添加转换逻辑,比如idle和attack之间是怎么转换,什么样的条件下会进行转换,过度耗时是多少秒等等。对于怪物1和怪物2来说,它们都拥有同样的状态和逻辑,区别仅仅是美术资源不同而已。那有什么办法可以节省人工,使用同一个动画状态机和同一个脚本控制,但是又可以播放不同的动画?机智的Unity提供了一个办法:动画重写控制器。让我们来看看是怎么实现的。
首先把Enemy1的Animator里的Enemy2Idle、Enemy2Attack状态删除。选中它们,右键菜单选中Delete确认删除。

删除Enemy2动画状态

在AnimationControllers文件夹内空白处右键,依次选择Create->Animator Override Controller,创建了一个动画重写控制器,我们命名为Enemy2

创建重写控制器

可以看到这个重写控制器右侧Inspector窗口内有个Controller选项,可以选择继承哪个动画控制器。在这里,我们可以把Enemy1直接拖入到输入框,或者点击输入框右侧的小圆圈选择Enemy1。

把Enemy1拖入到重写控制器

继承Enemy1的动画控制器后,还需要把美术资源也替换掉。Enemy1Attack改写成Enemy2Attack,Enemy1Idle改写成Enemy2Idle,这样重写控制器就配置好了。

重写Enemy2控制器

这时候,怪物2的Animator组件使用的控制器还是Enemy1。选择Enemy2游戏对象,在Animator组件的Controller选项点击右侧小圆圈,在弹出的窗口中选择Enemy2即可。

Enemy2的Animator

这时候运行游戏,可以看到怪物2的动画正常。

Enemy2的Idle动画播放正常

通过这样重写控制器,以后在怪物1的动画控制器添加逻辑的时候,怪物2自动继承,不需要再额外给怪物2添加一样的逻辑。我们这个项目才2个小怪,如果怪物类型或者动画类型再多一点,重写控制器就非常有必要。而且修改起来也只需要改一个地方即可,非常方便。其实个人觉得和Prefab预制件有点相似。
由于动画状态的转换逻辑和后面的移动碰撞内容有关联,我们会放到后面讲到角色怪物脚本的时候再添加。
把Enemy2游戏对象往下一拖,怪物2的预制件也做好啦!

Enemy2 Prefab

六、制作素材的预制件

回到文章最开头看游戏动画,我们可以看到游戏的每个关卡除了大胡子和怪物,还包含6个元素:地板floor、外墙outerWall、障碍墙/内墙wall、食物food、苏打水soda、离开exit。所以我们接下来把这些资源预制件都做出来,便于后面生成关卡的时候使用。

第1步:Floor

柿子先挑软的捏!作为构成关卡地板的floor是最简单的了,又不碰撞又不和其他元素互动。首先我们把上集残留的Enemy2删掉,再新建GameObject,改名为Floor1。(为了每次随机生成关卡的时候显示没那么单调,地砖一共8个样式)

创建新的GameObject的时候,重置transform和改名两个操作可以避免一些异常问题产生。

Floor1

给Floor1添加组件Sprite Renderer,精灵渲染器,可以控制图像的显示。

image.png

组件的Sprite选项,点击小圆圈在弹出的面板内选择文件名末尾数字是32的瓷砖图片。

选择图片

前面提过渲染层级里面,地板是最底层的,因此我们把Sorting Layer设置为Floor

修改Sorting Layer

把Floor1拖到Prafabs文件夹,第一张地板预制件就做好了。

Floor1 Prefab

按照以上流程,我们把其他Floor预制件都做好。Floor2-8(直接改名,不需要创建新的空白对象),选Sprite(33-39),往下一拖即可。

Floor1-8

第2步:Exit

Exit的作用是,大胡子移动到这个格子的时候会进入下一个关卡。
把Floor8更名为Exit,Sprite换成20号。

Exit图片

要判断角色是否移动到这里,需要添加一个Box Collider 2D来检测碰撞。但我们并非真的想阻止角色移动到这里,只是想知道角色是否到了这个格子,所以需要把Is Trigger选项选中。

Is Trigger,触发器,选中之后就没有碰撞效果,但可以在脚本代码里使用OnTriggerEnter等方法执行检测到碰撞之后的操作。

Is Trigger

为了让大胡子识别碰撞到的是Exit,需要把Tag设置成Exit。Exit的图片渲染层级比地板高,但是又比角色低,所以Sorting Layer选择Items。把Exit拖到Prafabs文件夹,Exit预制件完成。

Tag和Sorting Layer

第3步:Food

角色移动到Food格的时候,Food消失,同时角色增加一定数量的生命。因此Food和Exit一样需要添加Box Collider 2D来检测碰撞,而且也是激活触发器Is Trigger。渲染层级是Items,Tag是Food。
所以很简单,把Exit更名成Food,Sprite Renderer的Sprite更换成19号图,Tag改成Food,往下一拖,Food预制件就做好了。

Food Prefab

第4步:Soda

Soda作用同Food,差别在于增加的生命数值可以不同。把Food更名成Soda,Sprite Renderer的Sprite更换成18号图,Tag改成Soda,往下一拖。

image.png

第5步:OuterWall

关卡最外围的一圈墙,阻碍玩家和怪物移动到关卡外面。避免单调,一共3张不同的外墙图片。
把Soda改名成OuterWall1,Sprite换成25号(特点是石头多)。

OuterWall1

外墙需要碰撞器来阻碍角色和怪物进入到它的空间,所以把Is Trigger取消选中。

不选Is Trigger

Layer选择Blocking Layer,这样角色和怪物在进行射线检测是否存在这个标记的碰撞体的时候才能检测到它。
Sorting Layer选择Floor。(虽然其实选什么都没什么影响,其他资源正常情况下不会和它在同一个位置,但是严谨点好。)
Tag选择Untagged,不需要标签。

OuterWall1

把OuterWall1拖到Prefabs文件夹生成预制件。按上述步骤把OuterWall2、OuterWall3的预制件也做了(改名、换Sprite、拖),三个外墙的图片序号是25、26、28哦,不要选错了|ू•ૅω•́)ᵎᵎᵎ。

全部OuterWall预制件

第6步:Wall

每个关卡随机生成障碍墙在不同位置,能阻碍角色和怪物通行,同时角色可以劈砍这些障碍墙来开辟道路。
把OuterWall3改名为Wall1,Sprite换成21号(特点是像荆棘、灌木丛)。

Wall1

把Sorting Layer改成Items(层级比floor高,这样才能显示在上面)。
其他维持不变,生成预制件之后把其他7个的预制件也做出来。Sprite序号是21、22、23、24、27、29、30、31

Wall预制件

这些资源都准备好了,接下来可以进入新的环节——制作关卡٩(๑❛ᴗ❛๑)۶

加油哦!
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,214评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,307评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,543评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,221评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,224评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,007评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,313评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,956评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,441评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,925评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,018评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,685评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,234评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,240评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,464评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,467评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,762评论 2 345

推荐阅读更多精彩内容