Syncing spawned nodes
Asked Answered
A

1

0

Hello Godot community!

I've been using Godot on and off four a couple years now, but I've recently gotten into multiplayer with Godot 4.1. I've got players that can move around, but syncing other things has not been going well. For example, each player has a weapon that can change it's mesh based on what resource the player is using. The rotation of the weapon is synced, the muzzle flash animation is synced, but the mesh is invisible for clients. Additionally, I have spawner nodes that will add an enemy instance as a child of the parent, and in this case that would be the world node. The health of an enemy is synced between two players, but their position is not. For both examples I have added the property to sync in the MultiplayerSyncronizer node, and the enemy scene was added to the MultiplayerSpawned in the world node.

Here is the code for the enemy:

extends CharacterBody3D

@onready var HUDsprite = $HUD/Sprite3D
@onready var subviewport = $HUD/Sprite3D/SubViewport

@onready var health_bar = $HUD/Sprite3D/SubViewport/ProgressBar

@onready var navagent = $NavigationAgent3D

@onready var sight = $Sight

@onready var crit_box = $Critical_Hitbox
@onready var body_box = $Body_Hitbox

@export var health = 15: set = _set_health
@export var speed = 3
@export var view_distance = 10

var enet_peer = ENetMultiplayerPeer.new()

var looking_at = []

func update_target_location(target_location):
	navagent.set_target_position(target_location)


@rpc("any_peer", "reliable")
func damage(value):
	#if not is_multiplayer_authority(): return
	
	print('hit')
	var new_health = health - value
	_set_health(new_health)
	_set_health.rpc(new_health)
	print(health)
	
	if health <= 0:
		queue_free()


@rpc("any_peer", "reliable")
func _set_health(value):
	#if not is_multiplayer_authority(): return
	health = value
	get_node("HUD/Sprite3D/SubViewport/ProgressBar").value = health


func _ready():
	#if not is_multiplayer_authority(): return
	multiplayer.multiplayer_peer = enet_peer
	
	health_bar.max_value = health
	
	HUDsprite.texture = subviewport.get_texture()
	
	crit_box.connect("shot", _on_critical_hitbox_shot)
	body_box.connect("shot", _on_body_hitbox_shot)
	sight.connect("area_entered", _on_sight_area_entered)
	sight.connect("area_exited", _on_sight_area_exited)


func _physics_process(delta):
	#if not is_multiplayer_authority(): return
	
	$SyncNode.health = health
	$SyncNode.pos = global_position
	$SyncNode.rot = rotation
	
	if !looking_at.is_empty():
		navagent.target_position = looking_at[0].global_position
		look_at(looking_at[0].global_position)
	
	move.rpc(delta)

@rpc("any_peer")
func move(delta):
	#if not is_multiplayer_authority(): return
	
	if !looking_at.is_empty():
		navagent.target_position = looking_at[0].global_position
		look_at(looking_at[0].global_position)
		
	var current_location = global_transform.origin
	var next_location = navagent.get_next_path_position()
	
	var new_velocity = (next_location - current_location).normalized() * speed
	velocity = new_velocity
	
	move_and_slide()


@rpc("any_peer")
func look(object):
	if object.get_parent() not in looking_at:
		looking_at.append(object.get_parent())

@rpc("any_peer", "call_local")
func _on_critical_hitbox_shot(value, object):
	#if not is_multiplayer_authority(): return
	
	print('hit by ', object.get_parent().get_parent())
	#print("hit for %s damage" % value)
	damage(value * 2)
	damage.rpc(value * 2)
	
	#print(object._get_username())
	look.rpc_id(object.get_parent().get_parent().get_parent().multiplayer.get_unique_id(), object)


@rpc("any_peer", "call_remote")
func _on_body_hitbox_shot(value, object):
	#if not is_multiplayer_authority(): return
	
	print('hit by ', object.get_parent())
	#print("hit for %s damage" % value)
	damage(value)
	damage.rpc(value)

	look.rpc_id(object.get_parent().get_parent().get_parent().multiplayer.get_unique_id(), object)


func _on_sight_area_entered(area):
	#if not is_multiplayer_authority(): return
	
	#print(area)
	if area.get_parent() not in looking_at:
		looking_at.append(area.get_parent())


func _on_navigation_agent_3d_target_reached():
	pass # Replace with function body.


func _on_sight_area_exited(area):
	#if not is_multiplayer_authority(): return
	
	looking_at.erase(area)


@rpc("any_peer")
func get_random_radius(object_pos: Vector3, radius: float) -> Vector3:
	# Generate a random point within a cube
	var random_point = Vector3(randf(), 0, randf())

	# Normalize the point to ensure it's within the radius
	random_point = random_point.normalized() * radius

	# Translate the point to the object's position
	random_point += object_pos

	return random_point


func _on_timer_timeout():
	var rand = randi_range(1, 5)
	if rand == 1:
		navagent.target_position = get_random_radius(self.position, 5)
	elif rand == 2:
		look_at(get_random_radius(self.position, 5))

Any help would be appreciated!

Abranchiate answered 31/3, 2023 at 1:9 Comment(0)
G
0

AFAIK the RPC commands are for things like functions or variable syncing. All the graphics and animation are done on the client.

Galling answered 31/3, 2023 at 6:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.