unity3D技术分享Unity基础入门分享征服Unity3d

Unity官方教程 2D Roguelike(4):角色移动

2019-08-23  本文已影响0人  小巷里有只猫
2D Roguelike 最终效果

前言

Unity官方教程 2D Roguelike(3):移动逻辑中,我们完成了基本的移动逻辑,并且可以控制大胡子进行关卡内移动。这一节我们主要完成的内容是:

本节你将学会什么?

在击打障碍墙和被敌人攻击的时候,我们期望大胡子除了扣血掉血之外,还有明显的动作变化让我们辨别。所以接下来我们先实现角色动画的转换功能。

一、实现角色动画转换

角色动画的转换功能,指的是两种情况:

  1. 角色遇到障碍墙并且进行攻击,这时候角色的动画从idle切换到chop,并且在chop动画结束之后切换回idle。
  2. 角色遇到敌人并且被敌人攻击,这时候角色的动画从idle切换到hit,并且在hit动画结束之后切换回idle。
角色动画转换关系

转换逻辑主要是通过动画控制器实现。

第1步:增加动画转换的触发参数

我们双击Animation Controllers文件夹下的Player打开Animator面板。

Player动画控制器

在Animator面板,我们可以看到一共存在3个动画状态:PlayerIdle、PlayerHit、PlayerChop,其中橙色的为默认状态,也就是当游戏运行的时候角色会播放此动画。

Animator

在设置动画状态之间的转换关系之前,我们需要添加一些参数作为转换的触发条件。首先选中左上角的Parameters按钮,点击下方输入框的右侧+号,选择Trigger,新增一个参数playerChop

参数playerChop

使用同样的方法再增加一个命名为playerHit的参数。Trigger类型的参数类似于布尔值,当选中激活的时候也就是为真,条件满足,则进行状态间的切换,然后立刻重置。

第2步:设置动画状态转换

我们来看看如何设置待机动画和劈砍动画的转换。右键PlayerIdle,选择Make Transition,再点击PlayerChop,可以看到两个状态之间建立起了连接,箭头方向代表是从PlayerIdle状态转换为PlayerChop。

Make Transition

当然,劈砍动画读完之后是需要切换回待机的,所以我们还需要右键PlayerChop,再次选择Make Transition,创建一条返回到PlayerIdle的线。

Make Transition

选中高亮从PlayerIdle出发到PlayerChop的线,我们会发现连线也是有属性设置的,可以设置比如是否有打断时间、过度持续时长、触发转换的条件等。

我们只需要设置以下几项:

从idle到chop的transition

现在需要配置从PlayerChop到PlayerIdle的转换。选中高亮从PlayerChop到PlayerIdle的白线:

从chop到idle的transition

如此就完成了PlayerIdle和PlayerChop之间的互相转换的设置。用同样的步骤,我们连接PlayerIdle和PlayerHit两种状态并且进行设置,当然,从待机到受伤的Conditions是选择playerHit条件的。

PlayerIdle和PlayerHit的transition

OK,我们可以来测试一下动画转换是否成功。
左键点击Animator面板标签栏不松开,拖动到下面Console右侧,然后运行游戏,分别选中playerChop和playerHit触发器,注意查看大胡子的动作。

测试动画转换

可以看到大胡子在选择对应触发器的情况下做出了正确的反应,配置正确!

二、劈砍障碍墙

前面提过,遇到障碍墙的时候是在OnCantMove()内执行敲墙相关逻辑。那我们就在Player脚本里增加以下代码:

敲墙相关代码 成功敲墙

可以注意到,大胡子的确是成功把障碍墙砍倒了,但是我只按了一次向右方向键,它却执行了4次敲墙操作让墙消失了(Wall类设置墙生命是4)。回顾之前我们为了解决单次输入却多次移动而临时添加的修正代码,其实也很好理解,是由于基类里AttempMove()方法内最后一行,我们让开关为true了,执行了多次Update()。后续写怪物逻辑的时候会同步修正这个问题。

playerTurn为true

三、拾取食物和饮料

拾取食物和饮料是为了补充角色的生命(或者称呼为血量、食物、体力、点数都可以),这个生命有以下逻辑:

那么,我们首先需要有生命这个成员属性,才能在编写拾取食物逻辑时调用。

第1步:增加角色生命属性

角色的生命属性贯穿于整个游戏过程,包括进入新的关卡也会把这个属性继承过去,因此理应是在GameController脚本中声明设定。

playerFoodPoints

在GameController脚本中增加图中所示代码,新增一个公共成员playerFoodPoints,代表角色的生命值,初始值是100。
然后切换到Player脚本,增加如下代码。

生命值food

第2步:拾取食物和饮料

前面制作素材的预制件的时候曾提过,把Food、Soda等的碰撞器Is Trigger选项选中,是为了可以在脚本代码里使用OnTriggerEnter2D等方法执行检测到碰撞之后的操作。那我们就把这个方法加入到Player脚本。

OnTriggerEnter2D()

代码简析:

拾取食物和饮料

很顺利,但是我们不知道生命是否真的增加了,那么可以试试增加Debug.Log代码来打印信息。

使用Debug.Log打印出来的信息会出现在Unity的Console控制台,测试程序的时候会经常利用这个方法来定位问题所在。

在OnTriggerEnter2D()方法内增加代码,把food值打印出来。

增加log打印

再运行游戏,并且切换到Console窗口。

有日志的吃东西

可以看到,在吃了一堆红果子之后,food值打印出来是110,原来的100加上新增10点生命,数目是正确的!后面的苏打水同理。
测试完毕,记得把这两句debug代码删掉啦~

四、被敌人攻击

在Player脚本里增加一个LoseFood()方法,代码如下:

LoseFood() GameOver

五、移动到Exit进入下一关

角色移动到关卡右上角的Exit格子的时候,是进入下一关卡,也就是说重新加载场景生成下一关卡的地图。

Restart

Invoke()可以实现根据时间调用指定的方法,延时加载场景,这样视觉上过渡更加平滑,不突兀。

enabled = false 可以禁用脚本,避免角色在加载新场景之前就再次移动了。

重新加载场景

这时候我们有个疑问:为什么重新加载了没有生成新的关卡?(无视粉红色,那是我个人配置的背景色...)


为什么呢?

真相只有一个,“罪魁祸首”就是它:DontDestroyOnLoad()

DontDestroyOnLoad()

GameController作为游戏管理器,从头到尾只能有一个并且直到游戏结束才需要销毁,所以在之前讲单例模式的时候调用了DontDestroyOnLoad()方法来保证重新加载场景的时候不销毁实例。
那不销毁的情况下为什么会导致不生成新关卡呢?这和Awake()的特点有关系:

在游戏对象被销毁之前的整个生命周期之中,Awake()只执行一次!

所以重新加载场景之后,Awake()不执行,自然就不能调用InitGame()方法来生成随机关卡了。

注释DontDestroyOnLoad()

我们试试把代码注释掉再运行游戏。

重新生成新关卡

的确是有效了,但是这样做会导致游戏管理器每次进入新关卡就被摧毁,需要保留继承的数据比如人物生命、回合开关等都会被还原为初始值。因此,我们是建议使用另外更好的办法来实现重新加载场景时生成下一关的,后续会补充。
测试完毕,记得把两条杠去掉啦~

六、本篇收尾

我们讲过,角色意图移动的时候就会扣1点生命,现查漏补缺,把相关逻辑代码加上:

重写AttempMove方法

判断游戏是否结束这段代码之前也在LoseFood()方法内用过,所以我们可以把这段代码提炼出来作为一个新的方法,这样以后再有需要的地方就可以直接调用,实现代码复用。

CheckIfGameOver

感兴趣的童鞋还可以用Debug打印下food值,看是不是每走一步扣1点生命。
角色移动的逻辑基本都实现了,完成了这个小游戏最核心的机制,接下来就是一马平川啦!

上一章传送门:移动逻辑
下一章传送门:敌人移动

上一篇 下一篇

猜你喜欢

热点阅读