Godot概览之角色之间的交互

上一讲完成了基本项目的创建,这一讲会带着大家快速的实现角色之间的交互,Godot中角色之间的交互使用信号来完成。

一、角色之间的简单交互

按照上一篇文章创建一个新的项目,添加assets和scene两个文件夹,在assets中加入一些图片资源。
首先创建一个新的2D场景,在2D场景中添加Sprite和一个Button。


添加一个信号

在Sprite中添加相应的代码,让角色开始运动

extends Sprite

var flag = false

func _process(delta):
    if flag    :
        rotation += PI/2 * delta

设置了一个flag来控制角色的运动,之后需要为按钮添加相应的信号


添加信号

添加成功之后有如下一些显示


信号添加成功

以上代码代表着只要按下按钮,之后就发_on_Button_pressed(),这个信号是传递给这个节点的Sprite,在这个信号里面修改了flag的值。此时只要点击按钮,就可以角色就开始运动了。也可以直接使用set_process方法来进行处理,此时就可以不需要flag标签了。

extends Sprite
#var flag = false
func _process(delta):
#    if flag    :
    rotation += PI/2 * delta
func _on_Button_pressed():
#    print("ok")
#    flag = !flag
    set_process(!is_processing())

二、实现角色之间的复杂交互

下面将会实现一个Tank和齿轮之间的交互,通过这个例子让我一起来体会一下信号的特点。

  • Saw角色的创建
    为了进行碰撞,角色就不能仅仅只是Sprite了,但是依然需要Sprite,通过Sprite可以插入Texture(图片素材)。按照流程添加角色的Scene(名称是Saw),此时要添加碰撞检测,需要选择Area2D。这个是带碰撞检测节点需要添加Sprite和CollistionShape2D并且为后者添加碰撞区域。


    添加Saw节点

为Saw添加saw.gd的脚本代码

extends Area2D

#export导出的变量可以在配置项中设置
export var max_dis = 100 #最大的距离,默认是100
export var move_dir = "h" #转动的方向是水平 v是垂直
var dis = 0 #记录移动的距离
var dir = 1 #移动的方向

func _process(delta):
    dis += dir * 1
    if dis >= max_dis*delta or dis <= -1 * max_dis*delta:
        dir *= -1
    if move_dir == "h":
        position.x += 200 * dir * delta
    if move_dir == "v":
        position.y += 200 * dir * delta

    rotation += -PI * delta #移动的过程中不断进行旋转。

以上代码中创建了两个变量max_dis和move_dir,一个用来存储最大的移动距离,另一个用来确定移动方向,是水平还是垂直(在后续讲解完成向量之后,会有更好的控制方法)。这两个变量都设置为export,这表示可以在Saw的属性栏进行值的修改。代码的基本思路是,让Saw不断反复移动,如果移动距离大于max_dis或者小于-max_dis,就变向,相当于修改dir的值,在移动端过程中不断的旋转。

  • 创建一个可以上下移动的角色

按照同样一种方式创建一个Area2D,之后添加两个节点(Sprite和CollisionShape2D),Sprite为了加入材质,CollisionShape2D加入碰撞检测区域

var speed = 200

func _process(delta):
    if Input.is_action_pressed("ui_up"):
        position.y -= speed*delta
    if Input.is_action_pressed("ui_down"):
        position.y += speed*delta
    if Input.is_action_pressed("ui_left"):
        position.x -= speed*delta
    if Input.is_action_pressed("ui_right"):
        position.x += speed*delta
  • 将角色添加到舞台

启动游戏之后,Saw开始运行,角色也开始根据键盘控制进行移动,但是角色现在和电锯接触之后没有任何反应,下一步就要添加碰撞检测。

  • 添加碰撞检测

只要是Area2D的角色都可以进行碰撞检测,碰撞检测需要使用到信号(Signal),具体的操作流程如下所示:


Saw的信号

area_entered就是我们需要使用的信号,接下来,实现的效果是当tank碰到saw之后,有个生命,生命会减少。所以首先为Tank增加一个life的变量。

var life = 100

此时如果把信号添加在其中的一个Saw上会有一些问题,如图所示


为Saw添加信号

运行发现,只有第一个Saw节点能够出发信号。其他节点都没有办法触发。这样就需要为每个节点都添加。所以信号必须在tank上添加,让Saw节点来接收信号,此时就可以正常运行,但是代码却是在Saw中编写,这样就需要在Saw中获得Tank节点。


为Tank添加信号

具体的代码如下所示

func _on_Tank_area_entered(area):
#    print("hit")
    var tankNode = get_node("../Tank")
#    print(tankNode.life)
    if tankNode.life >= 0:
        tankNode.life -= 10
    print(tankNode.life)

需要强调一下,此处获取节点使用了get_node(../Tank)../表示的是上一级路径,这种方式是非常不合理的一种获取节点方法,在实际的应用中都不会使用,但是由于本章节的内容只是让大家概览一下流程,所以先使用这种不合理的方案,在后续的内容中会更新更合理的处理方案。
接下来,坦克受伤的代码客观来说应该在tank中编写更合理一些,所以可以在坦克中创建一个方法,injured方法。

#Tank.gd中添加代码
func injured(lost_life):
    life -= lost_life
    if life <= 0:
        life = 0
    print(life)

改造Saw中的代码

func _on_Tank_area_entered(area):
    var tankNode = get_node("../Tank")
#    print(tankNode.life)
#    if tankNode.life >= 0:
#        tankNode.life -= 10
#    print(tankNode.life)
    tankNode.injured(10)#让tank损失10点生命

接着来实现Tank受伤之后,有一些反馈,改变一下颜色。首先为tank节点添加一个动画。


为Tank添加动画节点

点击 Add Track添加一个轨道,选择Property Track,选择Sprite节点,之后添加modulate,通过这个可以设置Sprite的基本属性。之后将帧调整为0.4,意味着可以设置4帧的动画。


为modulate添加动画的关键帧

modulate可以调整节点的颜色和透明度,之后添加五个关键帧,分别在0.1和0.3部分设置值即可。


设置关键帧

播放之后会发现tank已经开始闪动了,之后在tank的受伤的代码中添加这个播放效果即可。

func injured(lost_life):
    life -= lost_life
    if life <= 0:
        life = 0
#    print(life)
    get_node("AnimationPlayer").play("injured")

最后添加一个血条,血条需要使用TexureProcess节点,所以先创建一个新的lifebar的scene,之后在assets中添加血条的图片


添加血条节点

下一步就是当收到伤害的时候通知血条减少值即可,此时需要创建一个自定义的信号来进行处理。

signal life_change(final_life)

在Main场景中,选中Tank,之后会发现多了一个信号life_change,之后将Lifebar拖入到主场景中,这个信号连接到Lifebar。连接Lifebar之前需要为Lifebar增加相应的gdscript代码

extends TextureProgress

func _on_Tank_life_change(final_life):
    value = final_life

最后一步就是在Tank中发信号

func injured(lost_life):
    life -= lost_life
    if life <= 0:
        life = 0
#    print(life)
    get_node("AnimationPlayer").play("injured")
    emit_signal("life_change",life)#发信号life_change,传入最终的值

实例中,各个节点的代码

#Tank
extends Area2D

var speed = 200
var life = 100

signal life_change(final_life)

func _ready():
    position.x = 500
    position.y = 200

func _process(delta):
    if Input.is_action_pressed("ui_left"):
        position.x -= speed * delta
    if Input.is_action_pressed("ui_right"):
        position.x += speed * delta
    if Input.is_action_pressed("ui_up"):
        position.y -= speed * delta
    if Input.is_action_pressed("ui_down"):
        position.y += speed * delta

func injured(lost_life):
    life -= lost_life
    if life <= 0:
        life = 0
#    print(life)
    get_node("AnimationPlayer").play("injured")
    emit_signal("life_change",life)#发信号life_change,传入最终的值

Saw的代码

extends Area2D

#export导出的变量可以在配置项中设置
export var max_dis = 100 #最大的距离,默认是100
export var move_dir = "h" #转动的方向是水平 v是垂直
var dis = 0 #记录移动的距离
var dir = 1 #移动的方向

func _process(delta):
    dis += dir * 1
    if dis >= max_dis or dis <= -1 * max_dis:
        dir *= -1
    if move_dir == "h":
        position.x += 200 * dir * delta
    if move_dir == "v":
        position.y += 200 * dir * delta

    rotation += -PI * delta



func _on_Tank_area_entered(area):
#    print("hit")
    var tankNode = get_node("../Tank")
#    print(tankNode.life)
#    if tankNode.life >= 0:
#        tankNode.life -= 10
#    print(tankNode.life)
    tankNode.injured(10)#让tank损失10点生命

Lifebar的代码

extends TextureProgress

func _on_Tank_life_change(final_life):
    value = final_life

通过这个案例,可以让大家感受得到Godot编写一个简单游戏的流程,下一讲,将会专门介绍向量数学方面的知识。

你可能感兴趣的:(Godot概览之角色之间的交互)