今天要做的是像玛丽奥游戏里的那样,添加一些金币旋转漂浮在空中,玩家控制的角色一碰到它便会发出“叮”的一声,便往上漂浮且慢慢消失!让我们来实现这个功能把。
(怎么感觉自己像是在写教程一样 O - O)
首先我们要思考这个金币有什么样的特性呢?
- 它是个金币
- 它会被游戏角色碰到
- 它是漂浮着的,还会动
- 游戏角色碰到它之后它会慢慢往上升,同时慢慢消失
- 还会播放声音
它是个金币
知道他的特性之后就好解决了,首先它能被碰到,说明它有碰撞体。这里我们利用Area2D
来实现,Area2D
是用来包含碰撞检测节点CollisionShape2D
的容器,而CollisionShape2D
可以用来检测碰撞。
然后,我们要做个金币。而且还会动,所以我们利用Godot的AnimatedSprite
来实现图像和动画的效果,新建一个名为金币的场景,并在场景内添加AnimatedSprite
。Sprite
在2d游戏中是一个或一组连续可动的图片,他们的连续播放形成了动画。
添加好AnimatedSprite
后,我们在IDE右侧的属性编辑窗口中新增一个SpriteFrames
,点击后把准备好的精灵素材拖入下方出现的容器里,并添加CollisionShape2D
且拉伸到完全覆盖金币图片,然后点击属性里的Playing
复选框即可实现动画效果。
它会被碰到
这里我们要利用Godot一个很重要的特性:Signal
,官方介绍如下:
信号是Godot的 观察者 模式的版本。它们允许一个节点发出其他节点可以监听和响应的消息。例如,与其持续检查按钮是否被按下,不如在按下按钮时发出信号。
在我的理解里大概意思:是一个在街上你碰到我了,我可以主动开口想你说话。以前写软件编程的时候像是:我在路上碰到你,我跟你说希望你说话,然后你才开口说话。当然这只是我自己粗浅的个人理解,也许以后学更深入的地方会更明朗一些吧。
怎么创建信号?我们可以通过代码的方式也可以通过图形界面的方式。
选中Area2D父节点,在右侧的面板上点击节点按钮,然后点击下方信号。之后会出现一列当前场景里所有可以使用的信号方法。其中body_entered(body: Node)
的作用是:当设定的碰撞区域检测到和其他有碰撞属性的空间时发送信号。下面设置接受方法的名称,然后便会自动切换到Script模式,并生成相应的代码。
extends Area2D
func _on_Coin_body_entered(body):
queue_free()
当然,这一过程你可以直接通过代码实现,但是我前几天看别人视频写过,但是现在忘记0 0,之后想起来再加进来把。
然后我们在函数主体中调用queue_free()
函数,这个函数是在系统空闲的时候销毁当前对象。也可以用free()
函数,但是我看官方文档说这个函数是立即销毁对象,如果在销毁期间调用了该函数可能会引起游戏错误或者崩溃。
运行后可以发现角色一碰到金币金币就自动消失了。
它会上下漂浮,玩家碰到会有声音并慢慢上浮消失
这里我们要利用Godot的AnimationPlayer
来创建动画控制器,它可以控制场景内的多个物件的动画效果。添加后点击下方的动画标签,选择新建,并键入动画名字后就能下新建动画了
创建好后我们点击下方的动画标签进入动画编辑窗口,然后点击右侧的钥匙图案即可插入相应属性的关键帧,几乎所有的属性都可以用于插入关键帧,甚至是通过关键帧调用方法、音频、动画等各种属性。
我们在第零秒插入一个offset
属性的关键帧,然后在第0.4秒再插入一个offset
的关键帧,并把第一个关键帧的offset-y
值设为0,第二个设为-8,也就是说在第0秒开始金币会慢慢向上移动,到第四秒达到设定值停止,然后点击右侧的循环按钮即可。
运行游戏可以看到,我们到目前为止我们就完成了金币的上下飘动效果(这里要注意,我在设置的时候碰到一个问题,金币实例化到主场景后不会有上下飘动的效果,后来找了好久发现没开启加载后自动播放,也就是上图右上方的红框区域)
利用这个我们可以玩出很多花样,我们可以创建第二个动画,用于游戏角色碰撞到金币时播放的动画:一直向上飘去,并慢慢消失。我们可以同样通过添加offset-y
控制金币向上移动,并且为modulate
添加一个关键,让他慢慢透明化。(modulate可以用于控制贴图的颜色、透明度。)
这里视频作者说了一个小BUG,就是如果在动画持续时间之内手速够快的话,在金币消失之前可以再吃一次金币,所以要在正常状态下将Area2D
的monitoring
属性开启,在角色碰到金币的动画里将monitoring
设为关闭
我们先在Area2D
下添加一个AudioSteamPlayer
组件用于播放声音。(这里说下我踩的坑:我在导入的时候没注意到音频素材默认情况下是勾选了Loop的按钮,第一次测试的声音一直播放,导致后面一个判断没办法进行,找了好久发现导入的时候要把Loop关掉,并重新导入才可以,看下图。)
准备好后我们就可以过AnimationPlayer
的方法调用轨道在特定的时间调用AudioSteamPlayer
的play(0)
方法播放声音,并在播放完之后调用Area2D
的queue_free()
销毁自己。
然后再脚本里写下如下代码:
extends Area2D
func _on_Coin_body_entered(body):
# 直接播放角色碰到金币后的动画
$AnimationPlayer.play("picked")
上面说的通过AnimationPlayer调用声音播放和动画结束后销毁自己只是一种方式,你也可以通过纯代码的方法实现这个功能,具体代码如下:
extends Area2D
func _on_Coin_body_entered(body):
# 播放声音
$AudioStreamPlayer.play(0)
# 等待声音播放完毕
yield($AudioStreamPlayer,"finished")
# 声音播放完毕后销毁自己
queue_free()