Godot游戏练习01-第18节-玩家死亡与复活,游戏失败检测

在之前的章节中我们实现了敌人与玩家的相互攻击, 碰撞判断, 但是一直未正式处理玩家的死亡事件, 也没有判定游戏失败, 今天先实现机制

看看效果

  • 玩家在多次受伤后死亡
  • 所有玩家死亡后游戏结束, 目前仅回到主菜单
  • 游戏可以再次正常开始

实现过程

玩家死亡

我们之前添加过Player的HurtboxComponent组件, 在生命值耗尽时仅打印信息

现在在生命值耗尽后添加queue_free(), 简单处理Player死亡信号, 死亡就释放节点

但是在添加简单的处理后, 实际运行时发生了报错, 当Client的Player在移动时死亡, 非常容易复现

gdscript 复制代码
E 0:00:28:334   get_node: Node not found: "Main/YSortRoot/Player219449027/PlayerInputMultiplayerSynchronizerComponent" (relative to "/root").
  <C++ Error>   Method/function failed. Returning: nullptr
  <C++ Source>  scene/main/node.cpp:1963 @ get_node()

报错如下:

  • Client的Player在移动中, Player节点的authority为1(服务器), 而Player节点下的InputComponent的authority为Client本身
  • 数据的同步方向是从authority同步到其他peer, Client的Player移动时就从其InputComponent一直同步到服务器和其他peer
  • 但所有Player节点的authority属于服务器, 也只有服务器在检测物理事件, 当服务器检测到Client的Player死亡时, 执行了queue_free()
  • 服务器上的Player节点先释放, 之后通过网络同步释放Client上的Player节点, 这中间有网络延时
  • 在该延时期间, Client上的InputComponent组件还在尝试向Server同步输入信息, 而Server上的对应节点已经不存在了, 出现报错

该问题暂时标记为问题1, 下次处理

玩家重生

首先在main.gd记录死亡的玩家

gdscript 复制代码
var died_peers: Array[int] = []

# other codes...

func _ready() -> void:
	multiplayer_spawner.spawn_function = func(data):
		print("[peer %s] Spawn player: %s, pos: %s" % [multiplayer.get_unique_id(), data.peer_id, player_spawn_marker.global_position])
		var player = PLAYER.instantiate() as Player
		player.name = "Player%s" % [data.peer_id]
		player.input_peer_id = data.peer_id
		player.global_position = player_spawn_marker.global_position
		if is_multiplayer_authority():
			player.died.connect(_on_player_died.bind(data.peer_id))
		return player
	_peer_ready.rpc_id(1)
	if is_multiplayer_authority():
		enemy_spawn_component.round_completed.connect(_on_round_completed)
	else:
		multiplayer.server_disconnected.connect(_on_server_disconnected)

# other codes...

func _on_player_died(peer_id: int) -> void:
	died_peers.append(peer_id)
	_check_game_over()

player新增died信号, 在死亡时触发, main中创建player时监听

然后在EnemySpawnComponent组件中检测round完成时发出round_completed信号, 依旧在main中监听

之后直接调用spawn函数处理所有死亡的玩家, 即可实现新一轮游戏中所有玩家复活的效果

gdscript 复制代码
func _on_round_completed() -> void:
	for peer_id in died_peers:
		multiplayer_spawner.spawn({ "peer_id": peer_id })
	died_peers.clear()

游戏失败检测

当所有Player都死亡, 则判定为失败, 在每一个Player的死亡信号触发时进行一次检测

若游戏失败, 则关闭multiplayer_peer, 并回到主菜单 (注意: 这是服务器的逻辑)

gdscript 复制代码
func _end_game() -> void:
	multiplayer.multiplayer_peer = null
	get_tree().change_scene_to_file("res://ui/menu/main_menu.tscn")


func _check_game_over() -> void:
	# multiplayer.get_peers 返回所有已连接的peer,不包含自身
	var all_peers := multiplayer.get_peers()
	all_peers.append(multiplayer.get_unique_id())
	var is_game_over := true
	for peer_id in all_peers:
		if not died_peers.has(peer_id):
			is_game_over = false
			break
	if is_game_over:
		_end_game()

服务器关闭multiplayer_peer, 实际上会导致已连接的peer收到server_disconnected信号

进行监听和处理即可

在上面的_ready中已经展现, 当Client收到该信号时也调用_end_game, 关闭peer并切换场景

gdscript 复制代码
multiplayer.server_disconnected.connect(_on_server_disconnected)

# other codes...

func _on_server_disconnected() -> void:
	_end_game()

保存, 运行, 效果可以, 但是又出现报错

gdscript 复制代码
E 0:00:18:010   main.gd:40 @ _end_game(): Removing a CollisionObject node during a physics callback is not allowed and will cause undesired behavior. Remove with call_deferred() instead.
  <C++ Source>  scene/2d/physics/collision_object_2d.cpp:98 @ _notification()
  <Stack Trace> main.gd:40 @ _end_game()
                main.gd:53 @ _check_game_over()
                main.gd:58 @ _on_player_died()
                player.gd:69 @ _on_health_depleted()
                health_component.gd:15 @ take_damage()
                hurtbox_component.gd:15 @ take_damage()
                hurtbox_component.gd:22 @ _on_area_entered()

通过调用栈和报错信息可知, 该错误是物理回调(area_entered)中经过一系列判断触发了_end_game切换场景移除了碰撞体导致

也就是说: Godot中不允许在物理回调中移除碰撞体 , 报错信息中推荐使用call_deferred处理

这里记为问题2, 下次处理

相关推荐
不会计算机的g_c__b1 小时前
Java坦克大战游戏项目分析与实现
游戏
前端不太难6 小时前
鸿蒙游戏 Store 设计(AI + 多端)
人工智能·游戏·harmonyos
wanhengidc6 小时前
服务器如何防范爬虫攻击?
运维·服务器·网络·爬虫·游戏·智能手机
张老师带你学7 小时前
unity 树资源 有樱花树 buildin
科技·游戏·unity·游戏引擎·模型
魔士于安7 小时前
unity 植物 不常见 花 触手植物
游戏·unity·游戏引擎·贴图·模型
上海云盾-小余8 小时前
游戏接口防刷与防外挂:API 安全加固与请求风控实战方案
安全·游戏
魔士于安8 小时前
unity=>传送门特效 带自由视角旋转放大 鼠标操作
前端·游戏·unity·游戏引擎·贴图·模型
FL16238631299 小时前
基于opensees+activateTcl实现一个记忆配对游戏
游戏
代码小书生20 小时前
游戏多功能助手!PC电脑单机游戏难度适配,新手游玩体验优化设置调节!支持龙胤立志传、宗门起源、灰烬之国、杀戮尖塔2、克鲁赛德战记等
游戏·单机游戏·修改器·游戏教程·游戏助手·电脑游戏·游戏技巧