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编写一个简单游戏的流程,下一讲,将会专门介绍向量数学方面的知识。

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

推荐阅读更多精彩内容