建立如下的场景与节点:
修改label的text和修改button的text
在node2d上添加脚本,进入编辑器
_ready() 函数在节点及其所有子节点进入活动场景时调用, 注意: _ready() 并不是构造器;构造器是 _init()。
脚本为节点添加行为。它用于控制节点的功能以及它如何与其他节点交互:子节点、父节点、同级节点等。
脚本的作用域是它附着的节点。
当某种特定行为发生时,信号会被“触发”,并且它们可以连接到任意脚本实例的任意函数。
信号主要用于GUI节点,不过其他节点也有信号,甚至可以在自己的脚本里定义自定义信号。
将“pressed(按下)”信号连接到一个自定义函数上。形成连接是第一部分,定义自定义函数是第二部分。
对于第一部分,Godot提供了两种创建连接的方式:通过编辑器的可视化界面或通过代码。
get_node("Label").text = "HELLO!"
get_node()的含义是获取节点的位置
func _ready():
get_node("Button").connect("pressed", self, "_on_Button_pressed")
最后的效果是一样的。
注意 get_node(path) 的工作原理。对于给定节点, get_node(path) 搜索它的直接子节点。
在上面代码中,Button必须是node2d的子项。如果Button是Label的子项,则获得它的代码是
get_node("Label/Button")
信号是Godot版本的观察者模式。它允许一个节点发送消息到其他节点,令其他节点也可以监听和响应这个消息。
比如当按键按下时信号就会发出,而不是不停地检查按键是否被按压。
信号是一种 解耦 游戏对象的方法,从而使代码更有条理,更易于管理
它们可以对任何感兴趣的可以订阅的对象发出信号和响应,而不是强制游戏对象去期望其他对象始终存在。
Godot的许多内置节点类型都提供可以用来检测事件的信号。
并且你还可以用代码signal自定义信号,emit发射信号。
signal my_signal
func _ready():
emit_signal("my_signal")
下载范例提供的图片,放入新建项目。
单击“添加/创建一个新节点”按钮,并向场景添加一个 Area2D 节点
Area2D节点是关于2d区域检测和2d物理影响。
使用 Area2D 类我们可以检测物体是否跑到游戏角色之中或者与游戏角色发生交叠,它是场景的根节点
在我们向 Area2D节点添加子节点之前,我们可能希望确保不会因为点击它们而不小心移动或缩放它们,那就选中该节点然后点击锁头右侧的图标,它的工具提示描述为“确保物体的子节点不会被选中。”
给Area2D添加AnimatedSprite子节点,它的功能是让精灵节点(sprite)可以使用多个纹理,并且将处理外观和动画。
AnimatedSprite节点要正常工作,需要提供动画帧资源(frames)
下边是一个动画列表。单击“默认”,并将其重命名为“right”。然后点击“添加”按钮,创建第二个动画名为“up”。将每个动画的两个图像(名为“playerGrey_up[1/2]”和“playerGrey_walk[1/2]”)拖到面板的“动画帧”一侧:
游戏角色图像对于游戏窗口来说有点太大,所以我们需要缩小它们。 单击 AnimatedSprite 节点并将 Scale 属性设置为 (0.5,0.5) 。 可以在 Node2D 标题下的属性面板中找到它
把CollisionShape2D节点添加到Area2D,该节点的功能是设置碰撞形状。
建一个capsuleshape型,用鼠标修改成:
现在我们需要添加一些内置节点所不具备的功能,因此我们要在Area2d添加一个脚本
export var speed = 400 # How fast the player will move (pixels/sec).
var screen_size # Size of the game window.
使用了export关键字后,节点属性也被修改,查询官网,这样做的目的是为了自己修改方便,它不是必须的
完全可以var speed =400 效果一样:
_ready() 函数会在节点进入场景树的时候被调用,这也是我们用来获取游戏窗口大小的好时候:
func _ready():
screen_size = get_viewport_rect().size
现在我们可以使用“process”()函数来定义角色将要做什么,_process() 会在每一帧上被调用,所以我们将使用它来更新游戏的元素。
对于这个角色,我们需要:
①检测输入。
②以给定方向移动。
③播放适当的动画。
可以通过 Input.is_action_pressed() 检测一个按键是否被按下,该函数会在按下时返回 true ,否则返回 false
func _process(delta):
var velocity = Vector2() # The player's movement vector.
if Input.is_action_pressed("ui_right"):
velocity.x += 1
if Input.is_action_pressed("ui_left"):
velocity.x -= 1
if Input.is_action_pressed("ui_down"):
velocity.y += 1
if Input.is_action_pressed("ui_up"):
velocity.y -= 1
if velocity.length() > 0:
velocity = velocity.normalized() * speed
$AnimatedSprite.play()
else:
$AnimatedSprite.stop()
分析一下这一段语句的作用:
① var velocity = Vector2(), 我们首先将速度设置为(0, 0),因为默认情况下,角色不应该移动 。
②if Input.is_action_pressed(),可以理解成固定写法,判断输入,配合input map使用,来避免pc,移动设备,主机等差异
③ velocity = velocity.normalized() * speed ,这个主要是为了避免多键同时输入,会导致对角线移动得更快,为了防止这种情况,让速度归一化 ,这意味将其长度设置为 1 ,并乘以所期望的值。初学可以理解成固定写法
④ $ 是 get_node() 的简写, A n i m a t e d S p r i t e . p l a y ( ) 与 g e t n o d e ( “ A n i m a t e d S p r i t e ” ) . p l a y ( ) 等 效 。 含 义 是 让 帧 动 画 “ 动 起 来 ” , 帧 动 画 本 身 是 一 张 一 张 静 态 的 图 片 , 而 p r o c e s s 函 数 每 一 帧 会 自 动 调 用 一 次 , 每 调 用 一 次 函 数 , 就 会 调 用 一 次 AnimatedSprite.play() 与 get_node(“AnimatedSprite”).play()等效。含义是让帧动画“动起来”,帧动画本身是一张一张静态的图片,而_process函数每一帧会自动调用一次,每调用一次函数,就会调用一次 AnimatedSprite.play()与getnode(“AnimatedSprite”).play()等效。含义是让帧动画“动起来”,帧动画本身是一张一张静态的图片,而process函数每一帧会自动调用一次,每调用一次函数,就会调用一次AnimatedSprite.play()或者$AnimatedSprite.stop()
但是按下f6,无法移动,是静态的。。查找错误中…………
是不是少了一部分代码?
position += velocity * delta
position.x = clamp(position.x, 0, screen_size.x)
position.y = clamp(position.y, 0, screen_size.y)
这段代码的作用是限制位置的,如果不限制,就不知道移动到哪里去了。
clamp夹住一个值意味着将其限制在一个给定区间内。
但是编译运行失败!
烦躁啊,各种缩排格式tab都尝试了,仍然出错。
unindent does not match any outer indentation level(缩进不匹配任何外在的缩进级别)。
折腾了1个小时终于解决,所以GDscript类python语言这一点真恶心,没有明确的边界,全靠缩进来分割。
错误原因:官方给的代码if段都是用空格来间隔的,而到了position段,我自己用了tab缩进,于是出错。
官方示例完全没提这个小细节。
统一成tab,或者统一成空格就正常了。
运行结果有两个细节:
①如果一点一点轻点方向键,图片会移动,但是它自身姿态不会变,只有连续按住不放自身才会“动”
①无论连续按住上,下,左右哪个键,图片运动的效果是一样的,并没有真正区分。