Godot游戏练习01-第6节-添加子弹

0 阅读2分钟

加上像素画后的游戏已经有一点感觉了, 让我们继续加上子弹, 游戏的雏形即将显现

子弹像素资源

Aseprite启动!

新建32*32

按B使用铅笔

选择颜色, 随便描两下!

image.png

子弹场景

子弹场景比较简单, 处理初始化时的位置+旋转+方向同步, 每帧移动, 以及2秒之后自动销毁即可

image.png

同步属性中勾选Spawn, 表示在加入节点树时, 同步指定属性, 后面的Replicate选择Never表示后续不再进行同步

子弹生成

  1. 在项目设置 - 输入映射中, 添加attack输入, 将鼠标左键设置为触发按钮

  2. player场景新增Timer, 取名"AttackTimer", 用于控制攻击间隔, 设置为0.25s, 设置为OneShot, 我们不需要重复触发

  3. 输入同步组件PlayerInputMultiplayerSynchronizerComponent新增is_attack_pressing同步属性, 表示对应的authority玩家是否正按下攻击按钮(鼠标左键)

    检测: is_attack_pressing = Input.is_action_pressed("attack"), 记得添加到同步列表, 设置为OnChang, 有变化时同步

  4. 在player脚本中检测, 仅服务端(authority)检测, 如果输入组件的is_attack_pressing为true, 则尝试攻击

以下是player中生成子弹的主要源码

func _process(_delta: float) -> void:
	var aim_vector := player_input_multiplayer_synchronizer_component.aim_vector
	weapon_root.look_at(weapon_root.global_position + aim_vector)
	if is_multiplayer_authority():
		var input := player_input_multiplayer_synchronizer_component.move_vector
		velocity = input * move_speed
		move_and_slide()
		if player_input_multiplayer_synchronizer_component.is_attack_pressing:
			_try_to_attack()


func _try_to_attack() -> void:
	if not attack_timer.is_stopped():
		return
	attack_timer.start()
	var bullet := BULLET.instantiate() as Bullet
	bullet.global_position = weapon_root.global_position
	bullet.direction = player_input_multiplayer_synchronizer_component.aim_vector
	bullet.rotation = bullet.direction.angle()
	get_parent().add_child(bullet, true)

_try_to_attack首先检测攻击计时器(CD), 若可以生成子弹, 则创建实例, 设置其初始位置, 方向, 旋转, 并且将它添加到player的父节点, 也就是Main节点

注意: add_child(bullet, true)的第二个参数true, 表示强制使用一个可读的名称, 这样子弹实例同步到其余peer时可以保持名称一致, Godot中的多人同步接口必须依赖一致的节点路径, 否则导致报错

最后一步, 将bullet场景添加到Main中的MultiplayerSpawner节点的auto spawn list中, 让bullet自动从authority同步到所有peer

看看效果

还不错! 有那么点感觉了

动画2.gif

等等, 控制台有报错, 我们看看

image.png

在客户端peer上发生了despawn时的报错, 并且提示UNAUTHORIZED, 说明我们在非authority的peer上删除了authority管理的节点(子弹)

我们需要稍加修改, bullet场景上的Timer触发应当仅在服务端触发, 客户端不需要在Timer触发后释放节点

  • 将Timer默认的process_mode设置为Disable
  • 在ready回调中, 判断is_multiplayer_authority, 如果是authority, 才打开Timer, 并监听timeout信号

再运行试试, 没有任何报错, 问题解决!