Compare commits
5 Commits
Author | SHA1 | Date | |
---|---|---|---|
0d49415e2b | |||
184f8aa4c0 | |||
ebab517863 | |||
0ff0445442 | |||
58be46ff19 |
259
addons/godot_steam_sync/Lobby/lobby.gd
Normal file
@ -0,0 +1,259 @@
|
||||
extends Control
|
||||
|
||||
@onready var lobby_name_txt = $CreateLobbyCont/LobbyNameTxt
|
||||
@onready var lobby_visibility = $CreateLobbyCont/LobbyVisibilityBtn
|
||||
@onready var max_members = $CreateLobbyCont/MaxMembersBtn
|
||||
@onready var lobby_member = preload("res://addons/godot_steam_sync/Lobby/lobby_member.tscn")
|
||||
@onready var lobby_name_lbl = $LobbyPanel/MarginContainer/LobbyCont/Panel/LobbyNameLbl
|
||||
@onready var start_btn = $LobbyPanel/MarginContainer/LobbyCont/StartBtn
|
||||
var im_ready : bool = false
|
||||
func _ready() -> void:
|
||||
Steam.join_requested.connect(_on_lobby_join_requested)
|
||||
#Steam.lobby_chat_update.connect(_on_lobby_chat_update)
|
||||
Steam.lobby_created.connect(_on_lobby_created)
|
||||
#Steam.lobby_data_update.connect(_on_lobby_data_update)
|
||||
#Steam.lobby_invite.connect(_on_lobby_invite)
|
||||
Steam.lobby_joined.connect(_on_lobby_joined)
|
||||
#Steam.lobby_match_list.connect(_on_lobby_match_list)
|
||||
#Steam.lobby_message.connect(_on_lobby_message)
|
||||
Steam.persona_state_change.connect(_on_persona_change)
|
||||
Steam.p2p_session_request.connect(_on_p2p_session_request)
|
||||
Steam.p2p_session_connect_fail.connect(_on_p2p_session_connect_fail)
|
||||
|
||||
|
||||
# Check for command line arguments
|
||||
check_command_line()
|
||||
|
||||
func check_command_line() -> void:
|
||||
var these_arguments: Array = OS.get_cmdline_args()
|
||||
|
||||
# There are arguments to process
|
||||
if these_arguments.size() > 0:
|
||||
|
||||
# A Steam connection argument exists
|
||||
if these_arguments[0] == "+connect_lobby":
|
||||
|
||||
# Lobby invite exists so try to connect to it
|
||||
if int(these_arguments[1]) > 0:
|
||||
|
||||
# At this point, you'll probably want to change scenes
|
||||
# Something like a loading into lobby screen
|
||||
print("Command line lobby ID: %s" % these_arguments[1])
|
||||
join_lobby(int(these_arguments[1]))
|
||||
|
||||
#region JoinLobbyRegion
|
||||
func join_lobby(this_lobby_id: int) -> void:
|
||||
print("Attempting to join lobby %s" % this_lobby_id)
|
||||
|
||||
# Clear any previous lobby members lists, if you were in a previous lobby
|
||||
NetworkManager.LOBBY_MEMBERS.clear()
|
||||
|
||||
# Make the lobby join request to Steam
|
||||
Steam.joinLobby(this_lobby_id)
|
||||
|
||||
func _on_lobby_joined(lobby_id: int, _permissions: int, _locked: bool, response: int):
|
||||
# If joining succeed, this will be 1
|
||||
if response == 1:
|
||||
# Set this lobby ID as your lobby ID
|
||||
NetworkManager.LOBBY_ID = lobby_id
|
||||
# Print the lobby ID to a label
|
||||
lobby_name_lbl.set_text(Steam.getLobbyData(NetworkManager.LOBBY_ID,"name"))
|
||||
# Append to output
|
||||
print("[STEAM] Joined lobby "+str(NetworkManager.LOBBY_ID)+".\n")
|
||||
# Get the lobby members
|
||||
get_lobby_members()
|
||||
# Make the initial handshake
|
||||
make_p2p_handshake()
|
||||
change_lobby_ui(true)
|
||||
|
||||
# Else it failed for some reason
|
||||
else:
|
||||
# Get the failure reason
|
||||
var FAIL_REASON: String
|
||||
match response:
|
||||
2: FAIL_REASON = "This lobby no longer exists."
|
||||
3: FAIL_REASON = "You don't have permission to join this lobby."
|
||||
4: FAIL_REASON = "The lobby is now full."
|
||||
5: FAIL_REASON = "Uh... something unexpected happened!"
|
||||
6: FAIL_REASON = "You are banned from this lobby."
|
||||
7: FAIL_REASON = "You cannot join due to having a limited account."
|
||||
8: FAIL_REASON = "This lobby is locked or disabled."
|
||||
9: FAIL_REASON = "This lobby is community locked."
|
||||
10: FAIL_REASON = "A user in the lobby has blocked you from joining."
|
||||
11: FAIL_REASON = "A user you have blocked is in the lobby."
|
||||
$Frame/Main/Displays/Outputs/Output.append_text("[STEAM] Failed joining lobby "+str(lobby_id)+": "+str(FAIL_REASON)+"\n")
|
||||
# Reopen the server list
|
||||
#_on_Open_Lobby_List_pressed()
|
||||
|
||||
func _on_lobby_join_requested(lobby_id: int, friend_id: int):
|
||||
# Get the lobby owner's name
|
||||
var OWNER_NAME = Steam.getFriendPersonaName(friend_id)
|
||||
print("[STEAM] Joining "+str(OWNER_NAME)+"'s lobby...\n")
|
||||
# Attempt to join the lobby
|
||||
join_lobby(lobby_id)
|
||||
#endregion
|
||||
|
||||
func change_lobby_ui(current : bool):
|
||||
if current:
|
||||
$CreateLobbyCont.hide()
|
||||
$LobbyPanel.show()
|
||||
$MembersPnl.show()
|
||||
else:
|
||||
$CreateLobbyCont.show()
|
||||
$LobbyPanel.hide()
|
||||
$MembersPnl.hide()
|
||||
|
||||
#region CreateLobbyRegion
|
||||
func _on_create_lobby_btn_pressed():
|
||||
if NetworkManager.LOBBY_ID == 0:
|
||||
Steam.createLobby(lobby_visibility.get_selected_id(), int(max_members.get_item_text(max_members.get_selected_id())))
|
||||
|
||||
func _on_lobby_created(connect: int, this_lobby_id: int) -> void:
|
||||
if connect == 1:
|
||||
# Set the lobby ID
|
||||
NetworkManager.LOBBY_ID = this_lobby_id
|
||||
print("Created a lobby: %s" % NetworkManager.LOBBY_ID )
|
||||
# Set this lobby as joinable, just in case, though this should be done by default
|
||||
Steam.setLobbyJoinable(NetworkManager.LOBBY_ID, true)
|
||||
# Set some lobby data
|
||||
Steam.setLobbyData(NetworkManager.LOBBY_ID, "name", lobby_name_txt.text)
|
||||
Steam.setLobbyData(NetworkManager.LOBBY_ID, "mode", "Mechatronauts")
|
||||
|
||||
# Allow P2P connections to fallback to being relayed through Steam if needed
|
||||
var set_relay: bool = Steam.allowP2PPacketRelay(true)
|
||||
|
||||
get_lobby_members()
|
||||
change_lobby_ui(true)
|
||||
#endregion
|
||||
func get_lobby_members() -> void:
|
||||
# Clear your previous lobby list
|
||||
NetworkManager.LOBBY_MEMBERS.clear()
|
||||
for MEMBER in $MembersPnl/MarginContainer/ScrollContainer/VBoxContainer.get_children():
|
||||
MEMBER.hide()
|
||||
MEMBER.queue_free()
|
||||
# Get the number of members from this lobby from Steam
|
||||
var num_of_members: int = Steam.getNumLobbyMembers(NetworkManager.LOBBY_ID)
|
||||
|
||||
# Get the data of these players from Steam
|
||||
for this_member in range(0, num_of_members):
|
||||
# Get the member's Steam ID
|
||||
var member_steam_id: int = Steam.getLobbyMemberByIndex(NetworkManager.LOBBY_ID, this_member)
|
||||
|
||||
# Get the member's Steam name
|
||||
var member_steam_name: String = Steam.getFriendPersonaName(member_steam_id)
|
||||
|
||||
# Add them to the list
|
||||
add_member_to_list(member_steam_id,member_steam_name)
|
||||
if NetworkManager.STEAM_ID == Steam.getLobbyOwner(NetworkManager.LOBBY_ID):
|
||||
NetworkManager.IS_READY[member_steam_id] = false
|
||||
|
||||
else:
|
||||
start_btn.hide()
|
||||
start_btn.disabled = true
|
||||
|
||||
# A user's information has changed
|
||||
func _on_persona_change(this_steam_id: int, _flag: int) -> void:
|
||||
# Make sure you're in a lobby and this user is valid or Steam might spam your console log
|
||||
if NetworkManager.LOBBY_ID > 0:
|
||||
print("A user (%s) had information change, update the lobby list" % this_steam_id)
|
||||
# Update the player list
|
||||
get_lobby_members()
|
||||
|
||||
func add_member_to_list(steam_id: int, steam_name: String):
|
||||
print("Adding new player to the list: "+str(steam_id)+" / "+str(steam_name))
|
||||
# Add them to the list
|
||||
NetworkManager.LOBBY_MEMBERS.append({"steam_id":steam_id, "steam_name":steam_name })
|
||||
# Instance the lobby member object
|
||||
var THIS_MEMBER: Object = lobby_member.instantiate()
|
||||
# Add their Steam name and ID
|
||||
THIS_MEMBER.name = str(steam_id)
|
||||
THIS_MEMBER._set_Member(steam_id, steam_name)
|
||||
# Add the child node
|
||||
$MembersPnl/MarginContainer/ScrollContainer/VBoxContainer.add_child(THIS_MEMBER)
|
||||
|
||||
|
||||
#region P2PHandshake
|
||||
func make_p2p_handshake() -> void:
|
||||
print("Sending P2P handshake to the lobby")
|
||||
P2P._send_P2P_Packet(0,0, {"message": "handshake", "from": NetworkManager.STEAM_ID},Steam.P2P_SEND_RELIABLE)
|
||||
|
||||
func _on_p2p_session_request(remote_id: int) -> void:
|
||||
# Get the requester's name
|
||||
var this_requester: String = Steam.getFriendPersonaName(remote_id)
|
||||
print("%s is requesting a P2P session" % this_requester)
|
||||
# Accept the P2P session; can apply logic to deny this request if needed
|
||||
Steam.acceptP2PSessionWithUser(remote_id)
|
||||
# Make the initial handshake
|
||||
make_p2p_handshake()
|
||||
#endregion
|
||||
|
||||
#region LobbyEvents
|
||||
func _on_p2p_session_connect_fail(lobby_id: int, session_error: int) -> void:
|
||||
# Note the session errors are: 0 - none, 1 - target user not running the same game, 2 - local user doesn't own app, 3 - target user isn't connected to Steam, 4 - connection timed out, 5 - unused
|
||||
# If no error was given
|
||||
if session_error == 0:
|
||||
print("[WARNING] Session failure with "+str(lobby_id)+" [no error given].")
|
||||
# Else if target user was not running the same game
|
||||
elif session_error == 1:
|
||||
print("[WARNING] Session failure with "+str(lobby_id)+" [target user not running the same game].")
|
||||
# Else if local user doesn't own app / game
|
||||
elif session_error == 2:
|
||||
print("[WARNING] Session failure with "+str(lobby_id)+" [local user doesn't own app / game].")
|
||||
# Else if target user isn't connected to Steam
|
||||
elif session_error == 3:
|
||||
print("[WARNING] Session failure with "+str(lobby_id)+" [target user isn't connected to Steam].")
|
||||
# Else if connection timed out
|
||||
elif session_error == 4:
|
||||
print("[WARNING] Session failure with "+str(lobby_id)+" [connection timed out].")
|
||||
# Else if unused
|
||||
elif session_error == 5:
|
||||
print("[WARNING] Session failure with "+str(lobby_id)+" [unused].")
|
||||
# Else no known error
|
||||
else:
|
||||
print("[WARNING] Session failure with "+str(lobby_id)+" [unknown error "+str(session_error)+"].")
|
||||
|
||||
func _on_lobby_chat_update(this_lobby_id: int, change_id: int, making_change_id: int, chat_state: int) -> void:
|
||||
# Get the user who has made the lobby change
|
||||
var changer_name: String = Steam.getFriendPersonaName(change_id)
|
||||
|
||||
# If a player has joined the lobby
|
||||
if chat_state == Steam.CHAT_MEMBER_STATE_CHANGE_ENTERED:
|
||||
print("%s has joined the lobby." % changer_name)
|
||||
|
||||
# Else if a player has left the lobby
|
||||
elif chat_state == Steam.CHAT_MEMBER_STATE_CHANGE_LEFT:
|
||||
print("%s has left the lobby." % changer_name)
|
||||
|
||||
# Else if a player has been kicked
|
||||
elif chat_state == Steam.CHAT_MEMBER_STATE_CHANGE_KICKED:
|
||||
print("%s has been kicked from the lobby." % changer_name)
|
||||
|
||||
# Else if a player has been banned
|
||||
elif chat_state == Steam.CHAT_MEMBER_STATE_CHANGE_BANNED:
|
||||
print("%s has been banned from the lobby." % changer_name)
|
||||
|
||||
# Else there was some unknown change
|
||||
else:
|
||||
print("%s did... something." % changer_name)
|
||||
|
||||
# Update the lobby now that a change has occurred
|
||||
get_lobby_members()
|
||||
#endregion
|
||||
|
||||
func _on_ready_btn_pressed():
|
||||
|
||||
if NetworkManager.STEAM_ID == Steam.getLobbyOwner(NetworkManager.LOBBY_ID):
|
||||
im_ready = !im_ready
|
||||
#for ready in NetworkManager.IS_READY.keys():
|
||||
#if ready == NetworkManager.STEAM_ID:
|
||||
NetworkManager.IS_READY[NetworkManager.STEAM_ID] = im_ready
|
||||
else:
|
||||
im_ready = !im_ready
|
||||
P2P._send_P2P_Packet(0,Steam.getLobbyOwner(NetworkManager.LOBBY_ID),{"TYPE":NetworkManager.TYPES.READY,"steam_id":NetworkManager.STEAM_ID,"ready":im_ready},Steam.P2P_SEND_RELIABLE)
|
||||
|
||||
func _on_start_btn_pressed():
|
||||
if NetworkManager.STEAM_ID == Steam.getLobbyOwner(NetworkManager.LOBBY_ID):
|
||||
var result = NetworkManager.IS_READY.values().all(func(number): return number == true)
|
||||
if result:
|
||||
SceneManager.change_scene("res://godotsteam_sync_example/Test.tscn")
|
||||
|
34
addons/godot_steam_sync/Lobby/lobby_member.gd
Normal file
@ -0,0 +1,34 @@
|
||||
extends Panel
|
||||
|
||||
var STEAM_ID: int = 0
|
||||
|
||||
# Stored at the class level to enable comparisons when helper functions are called
|
||||
var AVATAR: Image
|
||||
|
||||
# Is it ready? Do stuff!
|
||||
func _ready():
|
||||
# connect some signals
|
||||
var SIGNAL_CONNECT: int = Steam.connect("avatar_loaded", Callable(self, "_loaded_Avatar"))
|
||||
|
||||
# Set this player up
|
||||
func _set_Member(steam_id: int, steam_name: String) -> void:
|
||||
# Set the ID and username
|
||||
STEAM_ID = steam_id
|
||||
$MarginContainer/HBoxContainer/ScrollContainer/UsernameLbl.set_text(steam_name)
|
||||
# Get the avatar and show it
|
||||
Steam.getPlayerAvatar(Steam.AVATAR_MEDIUM, STEAM_ID)
|
||||
|
||||
# Load an avatar
|
||||
func _loaded_Avatar(id: int, this_size: int, buffer: PackedByteArray) -> void:
|
||||
# Check we're only triggering a load for the right player, and check the data has actually changed
|
||||
if id == STEAM_ID and (not AVATAR or not buffer == AVATAR.get_data()):
|
||||
# Create the image and texture for loading
|
||||
AVATAR = Image.create_from_data(this_size, this_size, false, Image.FORMAT_RGBA8, buffer)
|
||||
# Apply it to the texture
|
||||
var AVATAR_TEXTURE: ImageTexture = ImageTexture.create_from_image(AVATAR)
|
||||
# Set it
|
||||
$MarginContainer/HBoxContainer/Avatar.set_texture(AVATAR_TEXTURE)
|
||||
|
||||
|
||||
func _on_view_btn_pressed():
|
||||
Steam.activateGameOverlayToUser("steamid", STEAM_ID)
|
55
addons/godot_steam_sync/Lobby/lobby_member.tscn
Normal file
@ -0,0 +1,55 @@
|
||||
[gd_scene load_steps=3 format=3 uid="uid://d26bfvwtqaeqw"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/godot_steam_sync/Lobby/lobby_member.gd" id="1_vi8x1"]
|
||||
|
||||
[sub_resource type="LabelSettings" id="LabelSettings_x51ve"]
|
||||
font_size = 10
|
||||
|
||||
[node name="LobbyMember" type="Panel"]
|
||||
custom_minimum_size = Vector2(236, 36)
|
||||
offset_right = 236.0
|
||||
offset_bottom = 36.0
|
||||
script = ExtResource("1_vi8x1")
|
||||
metadata/_edit_use_anchors_ = true
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="."]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme_override_constants/margin_left = 3
|
||||
theme_override_constants/margin_top = 3
|
||||
theme_override_constants/margin_right = 3
|
||||
theme_override_constants/margin_bottom = 3
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Avatar" type="TextureRect" parent="MarginContainer/HBoxContainer"]
|
||||
custom_minimum_size = Vector2(30, 30)
|
||||
layout_mode = 2
|
||||
expand_mode = 1
|
||||
|
||||
[node name="ScrollContainer" type="ScrollContainer" parent="MarginContainer/HBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="UsernameLbl" type="Label" parent="MarginContainer/HBoxContainer/ScrollContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
text = "Space War Name"
|
||||
label_settings = SubResource("LabelSettings_x51ve")
|
||||
horizontal_alignment = 1
|
||||
vertical_alignment = 1
|
||||
|
||||
[node name="ViewBtn" type="Button" parent="MarginContainer/HBoxContainer"]
|
||||
custom_minimum_size = Vector2(30, 30)
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(0, 1, 0, 1)
|
||||
theme_override_font_sizes/font_size = 14
|
||||
text = "View"
|
||||
|
||||
[connection signal="pressed" from="MarginContainer/HBoxContainer/ViewBtn" to="." method="_on_view_btn_pressed"]
|
172
addons/godot_steam_sync/Lobby/lobby_menu.tscn
Normal file
@ -0,0 +1,172 @@
|
||||
[gd_scene load_steps=2 format=3 uid="uid://cliutsom3dxmu"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/godot_steam_sync/Lobby/lobby.gd" id="1_2lhov"]
|
||||
|
||||
[node name="LobbyMenu" type="Control"]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
script = ExtResource("1_2lhov")
|
||||
|
||||
[node name="Panel" type="Panel" parent="."]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="MembersPnl" type="Panel" parent="."]
|
||||
visible = false
|
||||
layout_mode = 1
|
||||
anchors_preset = -1
|
||||
anchor_left = 1.0
|
||||
anchor_top = 1.0
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_left = -276.0
|
||||
offset_top = -262.0
|
||||
offset_right = -10.0
|
||||
offset_bottom = -12.0
|
||||
grow_horizontal = 0
|
||||
grow_vertical = 0
|
||||
metadata/_edit_use_anchors_ = true
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="MembersPnl"]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme_override_constants/margin_left = 5
|
||||
theme_override_constants/margin_top = 5
|
||||
theme_override_constants/margin_right = 5
|
||||
theme_override_constants/margin_bottom = 5
|
||||
|
||||
[node name="ScrollContainer" type="ScrollContainer" parent="MembersPnl/MarginContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="MembersPnl/MarginContainer/ScrollContainer"]
|
||||
layout_mode = 2
|
||||
alignment = 1
|
||||
|
||||
[node name="CreateLobbyCont" type="VBoxContainer" parent="."]
|
||||
layout_mode = 1
|
||||
anchors_preset = 8
|
||||
anchor_left = 0.5
|
||||
anchor_top = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 0.5
|
||||
offset_left = -143.0
|
||||
offset_top = -162.0
|
||||
offset_right = 143.0
|
||||
offset_bottom = 162.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="LobbyNameTxt" type="TextEdit" parent="CreateLobbyCont"]
|
||||
custom_minimum_size = Vector2(0, 40)
|
||||
layout_mode = 2
|
||||
placeholder_text = "Lobby Name"
|
||||
scroll_smooth = true
|
||||
|
||||
[node name="LobbyVisibilityBtn" type="OptionButton" parent="CreateLobbyCont"]
|
||||
custom_minimum_size = Vector2(300, 35)
|
||||
layout_mode = 2
|
||||
selected = 2
|
||||
item_count = 4
|
||||
popup/item_0/text = "PRIVATE"
|
||||
popup/item_1/text = "FRIENDS ONLY"
|
||||
popup/item_1/id = 1
|
||||
popup/item_2/text = "PUBLIC"
|
||||
popup/item_2/id = 2
|
||||
popup/item_3/text = "INVISIBLE"
|
||||
popup/item_3/id = 3
|
||||
|
||||
[node name="MaxMembersBtn" type="OptionButton" parent="CreateLobbyCont"]
|
||||
custom_minimum_size = Vector2(300, 35)
|
||||
layout_mode = 2
|
||||
selected = 1
|
||||
item_count = 4
|
||||
popup/item_0/text = "2"
|
||||
popup/item_1/text = "4"
|
||||
popup/item_1/id = 1
|
||||
popup/item_2/text = "8"
|
||||
popup/item_2/id = 2
|
||||
popup/item_3/text = "16"
|
||||
popup/item_3/id = 3
|
||||
|
||||
[node name="CreateLobbyBtn" type="Button" parent="CreateLobbyCont"]
|
||||
custom_minimum_size = Vector2(0, 70)
|
||||
layout_mode = 2
|
||||
text = "Create Lobby"
|
||||
|
||||
[node name="LobbyPanel" type="Panel" parent="."]
|
||||
visible = false
|
||||
layout_mode = 1
|
||||
anchors_preset = 8
|
||||
anchor_left = 0.5
|
||||
anchor_top = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 0.5
|
||||
offset_left = -171.0
|
||||
offset_top = -148.0
|
||||
offset_right = 171.0
|
||||
offset_bottom = 172.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="LobbyPanel"]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme_override_constants/margin_left = 10
|
||||
theme_override_constants/margin_top = 10
|
||||
theme_override_constants/margin_right = 10
|
||||
theme_override_constants/margin_bottom = 10
|
||||
|
||||
[node name="LobbyCont" type="VBoxContainer" parent="LobbyPanel/MarginContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Panel" type="Panel" parent="LobbyPanel/MarginContainer/LobbyCont"]
|
||||
custom_minimum_size = Vector2(0, 60)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="LobbyNameLbl" type="Label" parent="LobbyPanel/MarginContainer/LobbyCont/Panel"]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme_override_font_sizes/font_size = 27
|
||||
text = "Lobby Name"
|
||||
horizontal_alignment = 1
|
||||
vertical_alignment = 1
|
||||
|
||||
[node name="Space" type="Control" parent="LobbyPanel/MarginContainer/LobbyCont"]
|
||||
custom_minimum_size = Vector2(0, 55.06)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="ReadyBtn" type="Button" parent="LobbyPanel/MarginContainer/LobbyCont"]
|
||||
custom_minimum_size = Vector2(0, 80)
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 33
|
||||
text = "READY"
|
||||
|
||||
[node name="StartBtn" type="Button" parent="LobbyPanel/MarginContainer/LobbyCont"]
|
||||
custom_minimum_size = Vector2(0, 80)
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 33
|
||||
text = "Start"
|
||||
|
||||
[connection signal="pressed" from="CreateLobbyCont/CreateLobbyBtn" to="." method="_on_create_lobby_btn_pressed"]
|
||||
[connection signal="pressed" from="LobbyPanel/MarginContainer/LobbyCont/ReadyBtn" to="." method="_on_ready_btn_pressed"]
|
||||
[connection signal="pressed" from="LobbyPanel/MarginContainer/LobbyCont/StartBtn" to="." method="_on_start_btn_pressed"]
|
24
addons/godot_steam_sync/Lobby/main_menu.gd
Normal file
@ -0,0 +1,24 @@
|
||||
extends Control
|
||||
|
||||
@onready var online_text = $OnlineLbl
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
func _ready():
|
||||
|
||||
if NetworkManager.IS_ONLINE:
|
||||
online_text.text = "online"
|
||||
else:
|
||||
online_text.text = "offline"
|
||||
|
||||
|
||||
func _on_play_btn_pressed():
|
||||
get_tree().change_scene_to_file("res://addons/godot_steam_sync/Lobby/lobby_menu.tscn")
|
||||
|
||||
func _on_exit_btn_pressed():
|
||||
get_tree().quit()
|
||||
|
||||
|
||||
|
83
addons/godot_steam_sync/Lobby/main_menu.tscn
Normal file
@ -0,0 +1,83 @@
|
||||
[gd_scene load_steps=3 format=3 uid="uid://1d2bxfqc4m8x"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/godot_steam_sync/Lobby/main_menu.gd" id="1_6wvtt"]
|
||||
|
||||
[sub_resource type="SystemFont" id="SystemFont_j72ml"]
|
||||
font_names = PackedStringArray("Sylfaen")
|
||||
|
||||
[node name="MainMenu" type="Control"]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
script = ExtResource("1_6wvtt")
|
||||
|
||||
[node name="Panel" type="Panel" parent="."]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="OnlineLbl" type="Label" parent="."]
|
||||
layout_mode = 1
|
||||
anchors_preset = 3
|
||||
anchor_left = 1.0
|
||||
anchor_top = 1.0
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_left = -49.0
|
||||
offset_top = -23.0
|
||||
grow_horizontal = 0
|
||||
grow_vertical = 0
|
||||
theme_override_font_sizes/font_size = 0
|
||||
text = "online"
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="."]
|
||||
layout_mode = 1
|
||||
anchors_preset = 8
|
||||
anchor_left = 0.5
|
||||
anchor_top = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 0.5
|
||||
offset_left = -175.0
|
||||
offset_top = -35.0
|
||||
offset_right = 175.0
|
||||
offset_bottom = 35.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="PlayBtn" type="Button" parent="VBoxContainer"]
|
||||
custom_minimum_size = Vector2(350, 70)
|
||||
layout_mode = 2
|
||||
text = "Play Online
|
||||
"
|
||||
|
||||
[node name="ExitBtn" type="Button" parent="VBoxContainer"]
|
||||
custom_minimum_size = Vector2(350, 70)
|
||||
layout_mode = 2
|
||||
text = "Quit"
|
||||
|
||||
[node name="Titel" type="Label" parent="."]
|
||||
layout_mode = 1
|
||||
anchors_preset = 8
|
||||
anchor_left = 0.5
|
||||
anchor_top = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 0.5
|
||||
offset_left = -277.0
|
||||
offset_top = -201.0
|
||||
offset_right = 277.0
|
||||
offset_bottom = -143.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme_override_fonts/font = SubResource("SystemFont_j72ml")
|
||||
theme_override_font_sizes/font_size = 56
|
||||
text = "GODOT STEAM SYNC"
|
||||
uppercase = true
|
||||
|
||||
[connection signal="pressed" from="VBoxContainer/PlayBtn" to="." method="_on_play_btn_pressed"]
|
||||
[connection signal="pressed" from="VBoxContainer/ExitBtn" to="." method="_on_exit_btn_pressed"]
|
BIN
addons/godot_steam_sync/Media/1.png
Normal file
After Width: | Height: | Size: 4.4 KiB |
@ -2,16 +2,16 @@
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://c3lblkyavjtol"
|
||||
path="res://.godot/imported/2d_crosshair_dot.png-61f11b68155f3e5bb55efff33412dc26.ctex"
|
||||
uid="uid://14sorpwfe4hf"
|
||||
path="res://.godot/imported/1.png-d99a973fff7536c0ce6f3facaed6e0f4.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/core/player-controller/2d_crosshair_dot.png"
|
||||
dest_files=["res://.godot/imported/2d_crosshair_dot.png-61f11b68155f3e5bb55efff33412dc26.ctex"]
|
||||
source_file="res://addons/godot_steam_sync/Media/1.png"
|
||||
dest_files=["res://.godot/imported/1.png-d99a973fff7536c0ce6f3facaed6e0f4.ctex"]
|
||||
|
||||
[params]
|
||||
|
BIN
addons/godot_steam_sync/Media/2.png
Normal file
After Width: | Height: | Size: 6.3 KiB |
34
addons/godot_steam_sync/Media/2.png.import
Normal file
@ -0,0 +1,34 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://ctruhwnscx144"
|
||||
path="res://.godot/imported/2.png-252d97d15980179767bc6bbd64608b77.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/godot_steam_sync/Media/2.png"
|
||||
dest_files=["res://.godot/imported/2.png-252d97d15980179767bc6bbd64608b77.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
BIN
addons/godot_steam_sync/Media/image.png
Normal file
After Width: | Height: | Size: 23 KiB |
34
addons/godot_steam_sync/Media/image.png.import
Normal file
@ -0,0 +1,34 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://bbbywy0ofvkhx"
|
||||
path="res://.godot/imported/image.png-c17ce491b4f523d51f56704d6387b67a.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/godot_steam_sync/Media/image.png"
|
||||
dest_files=["res://.godot/imported/image.png-c17ce491b4f523d51f56704d6387b67a.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
@ -0,0 +1,55 @@
|
||||
extends Node
|
||||
|
||||
var IS_ON_STEAM: bool = false
|
||||
var IS_ON_STEAM_DECK: bool = false
|
||||
var IS_ONLINE: bool = false
|
||||
var IS_OWNED: bool = false
|
||||
var STEAM_ID: int = 0
|
||||
var STEAM_USERNAME: String = "No one"
|
||||
var LOBBY_ID: int = 0
|
||||
var LOBBY_MEMBERS: Array = []
|
||||
var DATA : Dictionary
|
||||
var LOBBY_MAX_MEMBERS: int = 4
|
||||
var GAME_STARTED : bool = false
|
||||
var IS_READY : Dictionary = {}
|
||||
|
||||
enum TYPES {START,READY,START_SCENE,TRANFORM_SYNC,PROPERTY,EVENT,RIGIDBODY_SYNC,SCENE_LOADED,COMMAND,VOICE,RAGDOLL,CHANGE_SCENE}
|
||||
|
||||
@onready var player = preload("res://godotsteam_sync_example/fpc/character.tscn")
|
||||
|
||||
|
||||
#region Initilazation
|
||||
func _ready() -> void:
|
||||
_initialize_Steam()
|
||||
|
||||
if IS_ON_STEAM_DECK:
|
||||
get_window().mode = Window.MODE_EXCLUSIVE_FULLSCREEN
|
||||
func _initialize_Steam() -> void:
|
||||
if Engine.has_singleton("Steam"):
|
||||
var INIT: Dictionary = Steam.steamInitEx(true,480)
|
||||
|
||||
# If the status isn't one, print out the possible error and quit the program
|
||||
if INIT['status'] != 0:
|
||||
print("[STEAM] Failed to initialize: "+str(INIT)+" Shutting down...")
|
||||
get_tree().quit()
|
||||
|
||||
# Is the user actually using Steam; if false, the app assumes this is a non-Steam version
|
||||
IS_ON_STEAM = true
|
||||
# Checking if the app is on Steam Deck to modify certain behaviors
|
||||
IS_ON_STEAM_DECK = Steam.isSteamRunningOnSteamDeck()
|
||||
# Acquire information about the user
|
||||
IS_ONLINE = Steam.loggedOn()
|
||||
IS_OWNED = Steam.isSubscribed()
|
||||
STEAM_ID = Steam.getSteamID()
|
||||
STEAM_USERNAME = Steam.getPersonaName()
|
||||
|
||||
# Check if account owns the game
|
||||
if IS_OWNED == false:
|
||||
print("[STEAM] User does not own this game")
|
||||
# Uncomment this line to close the game if the user does not own the game
|
||||
get_tree().quit()
|
||||
func _process(_delta: float) -> void:
|
||||
if IS_ON_STEAM:
|
||||
Steam.run_callbacks()
|
||||
|
||||
#endregion
|
@ -0,0 +1,85 @@
|
||||
class_name NetworkPlayerSpawner extends Node
|
||||
|
||||
## Where the players will be spawned.
|
||||
@export var spawn_pos : Node
|
||||
## The node the players will be in after they are spawned. It has to be at the center of the scene. It has to be named 'Players'.
|
||||
@export var players_parent_node : Node
|
||||
## Here you can choose which way each spawned player will move. If you choose an axis, the player born will automatically move one unit away. If the game is 2D, choosing the Z axis means the center.
|
||||
@export_flags("X" ,"Y" ,"Z") var spawn_distance_axis = 1
|
||||
|
||||
@export var increase_spawn_distance : Vector3
|
||||
|
||||
func _ready():
|
||||
players_parent_node.name = "Players"
|
||||
if spawn_pos is Node3D:
|
||||
for player in NetworkManager.LOBBY_MEMBERS.size():
|
||||
var instance_player : Node = NetworkManager.player.instantiate()
|
||||
instance_player.name = str(NetworkManager.LOBBY_MEMBERS[player]["steam_id"])
|
||||
var direction = spawn_check()
|
||||
match direction:
|
||||
"CENTER":
|
||||
instance_player.transform.origin = spawn_pos.transform.origin + Vector3(0, 0, 0)
|
||||
"X":
|
||||
instance_player.transform.origin = spawn_pos.transform.origin + Vector3(player + increase_spawn_distance.x, 0, 0)
|
||||
"Y":
|
||||
instance_player.transform.origin = spawn_pos.transform.origin + Vector3(0, player + increase_spawn_distance.y, 0)
|
||||
"Z":
|
||||
instance_player.transform.origin = spawn_pos.transform.origin + Vector3(0, 0, player + increase_spawn_distance.z)
|
||||
"XY":
|
||||
instance_player.transform.origin = spawn_pos.transform.origin + Vector3(player + increase_spawn_distance.x, player + increase_spawn_distance.y, 0)
|
||||
"XZ":
|
||||
instance_player.transform.origin = spawn_pos.transform.origin + Vector3(player + increase_spawn_distance.x, 0, player + increase_spawn_distance.z)
|
||||
"YZ":
|
||||
instance_player.transform.origin = spawn_pos.transform.origin + Vector3(0, player + increase_spawn_distance.y, player + increase_spawn_distance.z)
|
||||
"XYZ":
|
||||
instance_player.transform.origin = spawn_pos.transform.origin + Vector3(player + increase_spawn_distance.x, player + increase_spawn_distance.y, player + increase_spawn_distance.z)
|
||||
_:
|
||||
instance_player.transform.origin = spawn_pos.transform.origin + Vector3(0, 0, 0)
|
||||
players_parent_node.add_child(instance_player)
|
||||
|
||||
if instance_player.name == str(NetworkManager.STEAM_ID):
|
||||
instance_player.make_owner()
|
||||
NetworkManager.GAME_STARTED = true
|
||||
if spawn_pos is Node2D:
|
||||
for player in NetworkManager.LOBBY_MEMBERS.size():
|
||||
var instance_player : Node = NetworkManager.player.instantiate()
|
||||
instance_player.name = str(NetworkManager.LOBBY_MEMBERS[player]["steam_id"])
|
||||
var direction = spawn_check()
|
||||
|
||||
match direction:
|
||||
"CENTER":
|
||||
instance_player.position = spawn_pos.position + Vector2(0, 0)
|
||||
"X":
|
||||
instance_player.position = spawn_pos.position + Vector2(player + increase_spawn_distance.x, 0)
|
||||
"Y":
|
||||
instance_player.position = spawn_pos.position + Vector2(0, player + increase_spawn_distance.y)
|
||||
"XY":
|
||||
instance_player.position = spawn_pos.position + Vector2(player + increase_spawn_distance.x, player + increase_spawn_distance.y)
|
||||
_:
|
||||
instance_player.position = spawn_pos.position + Vector2(0, 0)
|
||||
players_parent_node.add_child(instance_player)
|
||||
if instance_player.name == str(NetworkManager.STEAM_ID):
|
||||
instance_player.make_owner()
|
||||
NetworkManager.GAME_STARTED = true
|
||||
|
||||
func spawn_check() -> String:
|
||||
match spawn_distance_axis:
|
||||
0:
|
||||
return "CENTER"
|
||||
1:
|
||||
return "X"
|
||||
2:
|
||||
return "Y"
|
||||
3:
|
||||
return "XY"
|
||||
4:
|
||||
return "Z"
|
||||
5:
|
||||
return "XZ"
|
||||
6:
|
||||
return "YZ"
|
||||
7:
|
||||
return "XYZ"
|
||||
_:
|
||||
return "CENTER"
|
||||
return "CENTER"
|
134
addons/godot_steam_sync/Network/RadomeSteamSync/P2P.gd
Normal file
@ -0,0 +1,134 @@
|
||||
extends Node
|
||||
|
||||
const PACKET_READ_LIMIT: int = 32
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
# Get packets only if lobby is joined
|
||||
if NetworkManager.LOBBY_ID > 0:
|
||||
_read_All_P2P_Packets()
|
||||
|
||||
|
||||
func _read_P2P_Packet() -> void:
|
||||
var packet_size0 : int = Steam.getAvailableP2PPacketSize(0)
|
||||
|
||||
#region Channel0
|
||||
if packet_size0 > 0:
|
||||
var this_packet0 : Dictionary = Steam.readP2PPacket(packet_size0, 0)
|
||||
|
||||
if this_packet0.is_empty() or this_packet0 == null:
|
||||
print("WARNING: read an empty packet with non-zero size!")
|
||||
|
||||
# Get the remote user's ID
|
||||
var packet_sender: int = this_packet0['remote_steam_id']
|
||||
|
||||
# Make the packet data readablev
|
||||
var packet_code: PackedByteArray = this_packet0['data']
|
||||
|
||||
# Decompress the array before turning it into a useable dictionary
|
||||
var READABLE: Dictionary = bytes_to_var(packet_code.decompress_dynamic(-1, FileAccess.COMPRESSION_GZIP))
|
||||
|
||||
# Append logic here to deal with packet data
|
||||
if READABLE.has("TYPE"):
|
||||
handle_packets(READABLE)
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
func _read_All_P2P_Packets(read_count: int = 0) -> void:
|
||||
if read_count >= PACKET_READ_LIMIT:
|
||||
return
|
||||
if Steam.getAvailableP2PPacketSize(0) > 0:
|
||||
_read_P2P_Packet()
|
||||
_read_All_P2P_Packets(read_count + 1)
|
||||
var packet_size : float
|
||||
|
||||
|
||||
func _send_P2P_Packet(channel: int,target: int, packet_data: Dictionary,send_type: int) -> bool:
|
||||
# Create a data array to send the data through
|
||||
var this_data: PackedByteArray
|
||||
|
||||
# Compress the PackedByteArray we create from our dictionary using the GZIP compression method
|
||||
var compressed_data: PackedByteArray = var_to_bytes(packet_data).compress(FileAccess.COMPRESSION_GZIP)
|
||||
this_data.append_array(compressed_data)
|
||||
packet_size = this_data.size()
|
||||
# If sending a packet to everyone
|
||||
if target == 0:
|
||||
# If there is more than one user, send packets
|
||||
if NetworkManager.LOBBY_MEMBERS.size() > 1:
|
||||
# Loop through all members that aren't you
|
||||
for this_member in NetworkManager.LOBBY_MEMBERS:
|
||||
if this_member['steam_id'] != NetworkManager.STEAM_ID:
|
||||
return Steam.sendP2PPacket(this_member['steam_id'], this_data, send_type, channel)
|
||||
else:
|
||||
return Steam.sendP2PPacket(target, this_data, send_type, channel)
|
||||
return false
|
||||
|
||||
|
||||
func handle_start_packet(READABLE):
|
||||
# This packet reading when someone ready
|
||||
if READABLE["TYPE"] == NetworkManager.TYPES.READY:
|
||||
NetworkManager.IS_READY[READABLE["steam_id"]] = READABLE["ready"]
|
||||
SceneManager.change_scene("res://godotsteam_sync_example/Test.tscn")
|
||||
# This packet reading when lobby leader change_scene
|
||||
#if READABLE["TYPE"] == NetworkManager.TYPES.START_SCENE:
|
||||
#SceneManager.change_scene(READABLE["scene"])
|
||||
|
||||
#
|
||||
|
||||
func handle_event_packets(READABLE):
|
||||
#if READABLE["TYPE"] == NetworkManager.TYPES.COMMAND:
|
||||
#print("COMMAND:" + READABLE["method"] + str(READABLE["args"]))
|
||||
#if READABLE["args"] != null:
|
||||
#Command.callv(READABLE["method"],READABLE["args"])
|
||||
#else:
|
||||
#Command.call(READABLE["method"])
|
||||
|
||||
if READABLE['TYPE'] == NetworkManager.TYPES.EVENT:
|
||||
if READABLE["args"] != null:
|
||||
get_tree().root.get_node(READABLE["node_path"]).callv(READABLE["method"],READABLE["args"])
|
||||
else:
|
||||
get_tree().root.get_node(READABLE["node_path"]).call(READABLE["method"])
|
||||
|
||||
if READABLE["TYPE"] == NetworkManager.TYPES.SCENE_LOADED:
|
||||
handle_scene_change(READABLE)
|
||||
|
||||
|
||||
func handle_property_packets(READABLE):
|
||||
|
||||
if READABLE['TYPE'] == NetworkManager.TYPES.TRANFORM_SYNC and NetworkManager.GAME_STARTED:
|
||||
if READABLE["property"] == "global_position":
|
||||
get_tree().root.get_node(READABLE["node_path"]).transform_buffer[0] = READABLE
|
||||
if READABLE["property"] == "rotation":
|
||||
get_tree().root.get_node(READABLE["node_path"]).transform_buffer[1] = READABLE
|
||||
if READABLE["property"] == "scale":
|
||||
get_tree().root.get_node(READABLE["node_path"]).transform_buffer[2] = READABLE
|
||||
|
||||
if READABLE["TYPE"] == NetworkManager.TYPES.RAGDOLL and NetworkManager.GAME_STARTED:
|
||||
get_tree().root.get_node(READABLE["node_path"]).transform_buffer = READABLE
|
||||
|
||||
if READABLE["TYPE"] == NetworkManager.TYPES.RIGIDBODY_SYNC and NetworkManager.GAME_STARTED:
|
||||
get_tree().root.get_node(READABLE["node_path"]).sync_data = READABLE
|
||||
|
||||
if READABLE["TYPE"] == NetworkManager.TYPES.PROPERTY and NetworkManager.GAME_STARTED:
|
||||
if !READABLE["interpolated"]:
|
||||
get_tree().root.get_node(READABLE["node_path"]).set(READABLE["property"],READABLE["value"])
|
||||
else:
|
||||
var DATA :Array = [READABLE["property"],READABLE["value"]]
|
||||
get_tree().root.get_node(READABLE["node_path"]).DATA = DATA
|
||||
|
||||
|
||||
func handle_voice(READABLE):
|
||||
if READABLE["TYPE"] == NetworkManager.TYPES.VOICE and NetworkManager.GAME_STARTED:
|
||||
get_tree().root.get_node(READABLE["node_path"]).process_voice_data(READABLE["voice_data"])
|
||||
#await get_tree().create_timer(0.1).timeout
|
||||
|
||||
|
||||
func handle_scene_change(READABLE):
|
||||
get_tree().change_scene_to_file(READABLE["scene"])
|
||||
|
||||
|
||||
func handle_packets(READABLE):
|
||||
handle_start_packet(READABLE)
|
||||
handle_event_packets(READABLE)
|
||||
handle_property_packets(READABLE)
|
||||
handle_voice(READABLE)
|
@ -0,0 +1,14 @@
|
||||
extends Node
|
||||
|
||||
#@onready var loadingScreen = preload("res://addons/godot_steam_sync/SceneChanger/LoadingScreen.tscn")
|
||||
#func send(method : String,args = null):
|
||||
#var DATA : Dictionary = {"player_id":NetworkManager.STEAM_ID,"TYPE":NetworkManager.TYPES.COMMAND,"args":args,"method":method}
|
||||
#P2P._send_P2P_Packet(0,0, DATA,Steam.P2P_SEND_RELIABLE)
|
||||
#
|
||||
#
|
||||
#func start_scene(scene : String):
|
||||
#NetworkManager.GAME_STARTED = false
|
||||
#var instance = loadingScreen.instantiate()
|
||||
#get_parent().add_child(instance)
|
||||
#instance.change(scene)
|
||||
#
|
@ -0,0 +1,15 @@
|
||||
@icon("res://addons/godot_steam_sync/Network/RadomeSteamSync/SyncNode/funcSyncIcon.png")
|
||||
class_name FuncSync extends Synchronizer
|
||||
|
||||
signal FuncCalled(method : String,args)
|
||||
|
||||
func _ready() -> void:
|
||||
connect("FuncCalled",_on_func_called)
|
||||
|
||||
func call_f(method : String,args = null):
|
||||
emit_signal("FuncCalled",method,args)
|
||||
|
||||
func _on_func_called(method, args):
|
||||
if NetworkManager.GAME_STARTED:
|
||||
var DATA : Dictionary = {"player_id":NetworkManager.STEAM_ID,"TYPE":NetworkManager.TYPES.EVENT,"args":args,"node_path":get_parent().get_path(),"method":method}
|
||||
P2P._send_P2P_Packet(0,0, DATA,Steam.P2P_SEND_RELIABLE)
|
@ -0,0 +1,71 @@
|
||||
@icon("res://addons/godot_steam_sync/Network/RadomeSteamSync/SyncNode/propertySyncIcon.png")
|
||||
class_name PropertySync extends Synchronizer
|
||||
|
||||
|
||||
@export_group("SETTINGS","is")
|
||||
## If true, onlu lobby owner will send the packet.
|
||||
@export var is_only_lobby_owner : bool = false
|
||||
|
||||
|
||||
@export_group("NODES","object")
|
||||
## Select player if its not player or not inside player you can make 'is_only_lobby_owner' true.
|
||||
@export var object_player : Node
|
||||
|
||||
@export_group("INTERPOLATION")
|
||||
@export var is_interpolated :bool = false
|
||||
@export var interpolation_value : float = 0.1
|
||||
|
||||
@export_group("")
|
||||
@export var property_list : PackedStringArray## Hangi ozellikler yollayacaksak
|
||||
|
||||
|
||||
|
||||
var property_type : Array
|
||||
var DATA : Array
|
||||
var path : NodePath
|
||||
var timer : Timer
|
||||
|
||||
func init_timer():
|
||||
timer = Timer.new()
|
||||
timer.process_callback = Timer.TIMER_PROCESS_PHYSICS
|
||||
timer.wait_time = 0.1
|
||||
add_child(timer)
|
||||
timer.autostart = true
|
||||
timer.start()
|
||||
timer.connect("timeout",_on_timer_timeout)
|
||||
|
||||
func _ready():
|
||||
init_timer()
|
||||
|
||||
if !is_interpolated:
|
||||
path = get_parent().get_path()
|
||||
set_process(false)
|
||||
else:
|
||||
path = get_path()
|
||||
if is_only_lobby_owner == false and object_player.name != str(NetworkManager.STEAM_ID):
|
||||
timer.stop()
|
||||
elif is_only_lobby_owner:
|
||||
if Steam.getLobbyOwner(NetworkManager.LOBBY_ID) != NetworkManager.STEAM_ID:
|
||||
timer.stop()
|
||||
|
||||
for property in property_list:
|
||||
var v = get_parent().get(property)
|
||||
property_type.append(v)
|
||||
|
||||
func _process(delta):
|
||||
# Data[0] property Data[1] value
|
||||
if (!DATA.is_empty()):
|
||||
get_parent().set(DATA[0],lerp(get_parent().get(DATA[0]),DATA[1],interpolation_value))
|
||||
|
||||
|
||||
|
||||
func _on_timer_timeout():
|
||||
for property in property_list.size():
|
||||
if property_type[property] != get_parent().get(property_list[property]) and NetworkManager.GAME_STARTED:
|
||||
var DATA : Dictionary = {"player_id":NetworkManager.STEAM_ID,"TYPE":NetworkManager.TYPES.PROPERTY,"value":get_parent().get(property_list[property]),"node_path":path,"property":property_list[property],"interpolated":is_interpolated}
|
||||
P2P._send_P2P_Packet(0,0, DATA,Steam.P2P_SEND_RELIABLE)
|
||||
property_type[property] = get_parent().get(property_list[property])
|
||||
|
||||
func abort():
|
||||
timer.stop()
|
||||
set_process(false)
|
@ -0,0 +1,76 @@
|
||||
@icon("res://Network/RadomeSteamSync/SyncNode/transformSyncIcon.png")
|
||||
class_name RagDollSync extends Synchronizer
|
||||
|
||||
@export_group("SETTINGS","is")
|
||||
## If true, onlu lobby owner will send the packet.
|
||||
@export var is_only_lobby_owner : bool = false
|
||||
|
||||
@export_group("NODES","object")
|
||||
## Select player if its not player or not inside player you can make 'is_only_lobby_owner' true.
|
||||
@export var object_player : Node
|
||||
|
||||
@export var interpolation : float = 0.35
|
||||
|
||||
signal simulating(status : bool)
|
||||
|
||||
var bones : Array[PhysicalBone3D]
|
||||
var packet_index_pos : int = 0
|
||||
var last_pos : Array[Vector3]
|
||||
var transform_buffer : Dictionary = {}
|
||||
var last_index_buffer : int
|
||||
var timer : Timer
|
||||
|
||||
func init_timer():
|
||||
timer = Timer.new()
|
||||
timer.process_callback = Timer.TIMER_PROCESS_PHYSICS
|
||||
timer.wait_time = 0.1
|
||||
add_child(timer)
|
||||
timer.connect("timeout",_on_pos_timer_timeout)
|
||||
|
||||
func _ready():
|
||||
init_timer()
|
||||
get_all_bones()
|
||||
|
||||
func get_pos_bone() -> Array[Vector3]:
|
||||
var pos : Array[Vector3]
|
||||
for bone in bones:
|
||||
pos.append(bone.global_transform.origin)
|
||||
|
||||
return pos
|
||||
|
||||
func get_all_bones():
|
||||
for i in get_parent().get_children():
|
||||
if is_instance_of(i,PhysicalBone3D):
|
||||
bones.append(i)
|
||||
|
||||
func _on_pos_timer_timeout():
|
||||
|
||||
if get_pos_bone() != last_pos and NetworkManager.GAME_STARTED:
|
||||
var pos = get_pos_bone()
|
||||
var DATA : Dictionary = {"Idx":packet_index_pos + 1,"TYPE":NetworkManager.TYPES.RAGDOLL,"value":pos,"node_path":get_path()}
|
||||
P2P._send_P2P_Packet(0,0, DATA,Steam.P2P_SEND_UNRELIABLE)
|
||||
packet_index_pos = packet_index_pos + 1
|
||||
last_pos = pos
|
||||
func _physics_process(delta):
|
||||
if transform_buffer.has("Idx") and NetworkManager.GAME_STARTED:
|
||||
if transform_buffer["Idx"] >= last_index_buffer :
|
||||
set_pos_bone(transform_buffer["value"])
|
||||
|
||||
func set_pos_bone(DATA):
|
||||
for i in range(bones.size()):
|
||||
var lerped_value = lerp(bones[i].global_transform.origin,DATA[i],interpolation)
|
||||
bones[i].global_transform.origin = lerped_value
|
||||
last_index_buffer = transform_buffer["Idx"]
|
||||
|
||||
func abort():
|
||||
set_process(false)
|
||||
timer.stop()
|
||||
|
||||
func _on_simulating(status : bool):
|
||||
if status== true:
|
||||
if is_only_lobby_owner and Steam.getLobbyOwner(NetworkManager.LOBBY_ID) == NetworkManager.STEAM_ID:
|
||||
timer.start()
|
||||
elif !is_only_lobby_owner and str(NetworkManager.STEAM_ID) == object_player.name:
|
||||
timer.start()
|
||||
else:
|
||||
timer.stop()
|
@ -0,0 +1,50 @@
|
||||
class_name RigidBodySync2D extends Synchronizer
|
||||
|
||||
@export var interpolation_value = 0.3
|
||||
|
||||
var packet_index: int = 0
|
||||
var state_data = null
|
||||
var last_index : int = 0
|
||||
var last_pos : Vector3 = Vector3.ZERO
|
||||
var pTimer : Timer
|
||||
|
||||
func init_timer():
|
||||
pTimer = Timer.new()
|
||||
pTimer.process_callback = Timer.TIMER_PROCESS_PHYSICS
|
||||
pTimer.wait_time = 1.0 / 30.0
|
||||
add_child(pTimer)
|
||||
pTimer.autostart = true
|
||||
pTimer.start()
|
||||
pTimer.connect("timeout",_on_timeout)
|
||||
|
||||
func _ready():
|
||||
init_timer()
|
||||
if Steam.getLobbyOwner(NetworkManager.LOBBY_ID) != NetworkManager.STEAM_ID:
|
||||
pTimer.stop()
|
||||
|
||||
func _on_timeout():
|
||||
if get_parent().position != last_pos and NetworkManager.GAME_STARTED:
|
||||
var DATA : Dictionary = {"Idx":packet_index,"player_id":NetworkManager.STEAM_ID,"TYPE":NetworkManager.TYPES.RIGIDBODY_SYNC,"value":get_parent().linear_velocity,"node_path":get_path(),}
|
||||
P2P._send_P2P_Packet(0,0, DATA,Steam.P2P_SEND_UNRELIABLE)
|
||||
packet_index = packet_index + 1
|
||||
last_pos = get_parent().linear_velocity
|
||||
|
||||
func update_physics_values():
|
||||
var target_linear_velocity = state_data["value"][0]
|
||||
var target_angular_velocity = state_data["value"][1]
|
||||
var target_position = state_data["value"][2]
|
||||
var target_rotation = state_data["value"][3]
|
||||
# Linear velocity için Lerp
|
||||
get_parent().linear_velocity = lerp(get_parent().linear_velocity, target_linear_velocity, interpolation_value)
|
||||
# Angular velocity için Lerp
|
||||
get_parent().angular_velocity = lerp(get_parent().angular_velocity, target_angular_velocity, interpolation_value)
|
||||
# Position için Lerp
|
||||
get_parent().position = lerp(get_parent().position, target_position, interpolation_value)
|
||||
# Rotation için Lerp (Quaternion için)
|
||||
get_parent().rotation = lerp(get_parent().rotation , target_rotation, interpolation_value)
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
if state_data != null and NetworkManager.GAME_STARTED:
|
||||
if state_data["Idx"] >= last_index :
|
||||
update_physics_values()
|
||||
last_index = state_data["Idx"]
|
@ -0,0 +1,54 @@
|
||||
class_name RigidBodySync3D extends Synchronizer
|
||||
|
||||
@export var interpolation_value = 0.3
|
||||
|
||||
var packet_index: int = 0
|
||||
var state_data = null
|
||||
var last_index : int = 0
|
||||
var last_pos : Vector3 = Vector3.ZERO
|
||||
var pTimer : Timer
|
||||
|
||||
func init_timer():
|
||||
pTimer = Timer.new()
|
||||
pTimer.process_callback = Timer.TIMER_PROCESS_PHYSICS
|
||||
pTimer.wait_time = 1.0 /30.0
|
||||
add_child(pTimer)
|
||||
pTimer.autostart = true
|
||||
pTimer.start()
|
||||
pTimer.connect("timeout",_on_timer_timeout)
|
||||
|
||||
func _ready():
|
||||
init_timer()
|
||||
if Steam.getLobbyOwner(NetworkManager.LOBBY_ID) != NetworkManager.STEAM_ID:
|
||||
pTimer.stop()
|
||||
|
||||
func _on_timer_timeout():
|
||||
if get_parent().position != last_pos and NetworkManager.GAME_STARTED:
|
||||
var DATA : Dictionary = {"Idx":packet_index,"player_id":NetworkManager.STEAM_ID,"TYPE":NetworkManager.TYPES.RIGIDBODY_SYNC,"value":[get_parent().linear_velocity,get_parent().angular_velocity,get_parent().position,get_parent().rotation],"node_path":get_path()}
|
||||
P2P._send_P2P_Packet(0,0, DATA,Steam.P2P_SEND_UNRELIABLE)
|
||||
packet_index = packet_index + 1
|
||||
last_pos = get_parent().position
|
||||
|
||||
|
||||
|
||||
func update_physics_values():
|
||||
var target_linear_velocity = state_data["value"][0]
|
||||
var target_angular_velocity = state_data["value"][1]
|
||||
var target_position = state_data["value"][2]
|
||||
var target_rotation = state_data["value"][3]
|
||||
# Linear velocity için Lerp
|
||||
get_parent().linear_velocity = lerp(get_parent().linear_velocity, target_linear_velocity, interpolation_value)
|
||||
# Angular velocity için Lerp
|
||||
get_parent().angular_velocity = lerp(get_parent().angular_velocity, target_angular_velocity, interpolation_value)
|
||||
# Position için Lerp
|
||||
get_parent().position = lerp(get_parent().position, target_position, interpolation_value)
|
||||
# Rotation için Lerp (Quaternion için)
|
||||
get_parent().rotation = lerp(get_parent().rotation , target_rotation, interpolation_value)
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
await get_tree().create_timer(0.1).timeout
|
||||
if state_data != null and NetworkManager.GAME_STARTED:
|
||||
if state_data["Idx"] >= last_index :
|
||||
update_physics_values()
|
||||
|
||||
last_index = state_data["Idx"]
|
@ -0,0 +1 @@
|
||||
class_name Synchronizer extends Node
|
@ -0,0 +1,127 @@
|
||||
@icon("res://addons/godot_steam_sync/Network/RadomeSteamSync/SyncNode/transformSyncIcon.png")
|
||||
class_name TransformSync2D extends Synchronizer
|
||||
|
||||
@export_group("SETTINGS","is")
|
||||
## If true, onlu lobby owner will send the packet.
|
||||
@export var is_only_lobby_owner : bool = false
|
||||
|
||||
@export_group("NODES","object")
|
||||
## Select player if its not player or not inside player you can make 'is_only_lobby_owner' true.
|
||||
@export var object_player : Node
|
||||
|
||||
@export_group("")
|
||||
@export var Position : bool = true
|
||||
@export var Rotation : bool = false
|
||||
@export var Scale : bool = false
|
||||
|
||||
|
||||
var packet_index_pos : int = 0
|
||||
var packet_index_rot : int = 0
|
||||
var packet_index_scale : int = 0
|
||||
|
||||
|
||||
|
||||
var last_pos : Vector2 = Vector2.ZERO
|
||||
var last_rot : float = 0
|
||||
var last_scale : Vector2 = Vector2.ZERO
|
||||
|
||||
var transform_buffer : Array = [null,null,null]
|
||||
var last_index_buffer : PackedInt32Array = [0,0,0]
|
||||
|
||||
var posTimer : Timer
|
||||
var rotTimer : Timer
|
||||
var sclTimer : Timer
|
||||
|
||||
|
||||
func init_pos_timer():
|
||||
posTimer = Timer.new()
|
||||
posTimer.process_callback = Timer.TIMER_PROCESS_PHYSICS
|
||||
posTimer.wait_time = 0.1
|
||||
add_child(posTimer)
|
||||
posTimer.autostart = true
|
||||
posTimer.start()
|
||||
posTimer.connect("timeout",_on_pos_timer_timeout)
|
||||
func init_rot_timer():
|
||||
rotTimer = Timer.new()
|
||||
rotTimer.process_callback = Timer.TIMER_PROCESS_PHYSICS
|
||||
rotTimer.wait_time = 0.1
|
||||
add_child(rotTimer)
|
||||
rotTimer.autostart = true
|
||||
rotTimer.start()
|
||||
rotTimer.connect("timeout",_on_rot_timer_timeout)
|
||||
func init_scl_timer():
|
||||
sclTimer = Timer.new()
|
||||
sclTimer.process_callback = Timer.TIMER_PROCESS_PHYSICS
|
||||
sclTimer.wait_time = 0.1
|
||||
add_child(sclTimer)
|
||||
sclTimer.autostart = true
|
||||
sclTimer.start()
|
||||
sclTimer.connect("timeout",_on_scale_timer_timeout)
|
||||
|
||||
func _ready():
|
||||
init_pos_timer()
|
||||
init_rot_timer()
|
||||
init_scl_timer()
|
||||
|
||||
if is_only_lobby_owner == false and object_player.name != str(NetworkManager.STEAM_ID):
|
||||
posTimer.stop()
|
||||
rotTimer.stop()
|
||||
sclTimer.stop()
|
||||
elif is_only_lobby_owner:
|
||||
if Steam.getLobbyOwner(NetworkManager.LOBBY_ID) != NetworkManager.STEAM_ID:
|
||||
posTimer.stop()
|
||||
rotTimer.stop()
|
||||
sclTimer.stop()
|
||||
|
||||
|
||||
if !Position:
|
||||
posTimer.stop()
|
||||
if !Rotation:
|
||||
rotTimer.stop()
|
||||
if !Scale:
|
||||
sclTimer.stop()
|
||||
|
||||
func _on_pos_timer_timeout():
|
||||
if get_parent().global_position != last_pos and NetworkManager.GAME_STARTED:
|
||||
var DATA : Dictionary = {"Idx":packet_index_pos + 1,"player_id":NetworkManager.STEAM_ID,"TYPE":NetworkManager.TYPES.TRANFORM_SYNC,"value":get_parent().global_position,"node_path":get_path(),"property":"global_position"}
|
||||
P2P._send_P2P_Packet(0,0, DATA,Steam.P2P_SEND_UNRELIABLE)
|
||||
packet_index_pos = packet_index_pos + 1
|
||||
last_pos = get_parent().global_position
|
||||
|
||||
func _on_rot_timer_timeout():
|
||||
if get_parent().rotation != last_rot and NetworkManager.GAME_STARTED:
|
||||
var DATA : Dictionary = {"Idx":packet_index_rot + 1,"player_id":NetworkManager.STEAM_ID,"TYPE":NetworkManager.TYPES.TRANFORM_SYNC,"value":get_parent().rotation,"node_path":get_path(),"property":"rotation"}
|
||||
P2P._send_P2P_Packet(0,0, DATA,Steam.P2P_SEND_UNRELIABLE)
|
||||
packet_index_rot = packet_index_rot + 1
|
||||
last_rot = get_parent().rotation
|
||||
|
||||
func _on_scale_timer_timeout():
|
||||
if get_parent().scale != last_scale and NetworkManager.GAME_STARTED:
|
||||
var DATA : Dictionary = {"Idx":packet_index_scale + 1,"player_id":NetworkManager.STEAM_ID,"TYPE":NetworkManager.TYPES.TRANFORM_SYNC,"value":get_parent().scale,"node_path":get_path(),"property":"scale"}
|
||||
P2P._send_P2P_Packet(0,0, DATA,Steam.P2P_SEND_UNRELIABLE)
|
||||
packet_index_scale = packet_index_scale + 1
|
||||
last_scale = get_parent().scale
|
||||
|
||||
func _process(delta):
|
||||
if transform_buffer[0] != null and NetworkManager.GAME_STARTED:
|
||||
if transform_buffer[0]["Idx"] >= last_index_buffer[0] :
|
||||
var lerped_value = lerp(get_parent().get(transform_buffer[0]["property"]),transform_buffer[0]["value"],0.1)
|
||||
get_parent().set(transform_buffer[0]["property"],lerped_value)
|
||||
last_index_buffer[0] = transform_buffer[0]["Idx"]
|
||||
# Rotation
|
||||
if transform_buffer[1] != null and NetworkManager.GAME_STARTED:
|
||||
if transform_buffer[1]["Idx"] >= last_index_buffer[1]:
|
||||
var lerped_value = lerp(get_parent().get(transform_buffer[1]["property"]),transform_buffer[1]["value"],0.1)
|
||||
get_parent().set(transform_buffer[1]["property"],lerped_value)
|
||||
last_index_buffer[1] = transform_buffer[1]["Idx"]
|
||||
# Scale
|
||||
if transform_buffer[2] != null and NetworkManager.GAME_STARTED:
|
||||
if transform_buffer[2]["Idx"] >= last_index_buffer[2]:
|
||||
var lerped_value = lerp(get_parent().get(transform_buffer[2]["property"]),transform_buffer[2]["value"],0.1)
|
||||
get_parent().set(transform_buffer[2]["property"],lerped_value)
|
||||
last_index_buffer[2] = transform_buffer[2]["Idx"]
|
||||
func abort():
|
||||
posTimer.stop()
|
||||
rotTimer.stop()
|
||||
sclTimer.stop()
|
||||
set_process(false)
|
@ -0,0 +1,126 @@
|
||||
@icon("res://addons/godot_steam_sync/Network/RadomeSteamSync/SyncNode/transformSyncIcon.png")
|
||||
class_name TransformSync3D extends Synchronizer
|
||||
|
||||
@export_group("SETTINGS","is")
|
||||
## If true, onlu lobby owner will send the packet.
|
||||
@export var is_only_lobby_owner : bool = false
|
||||
|
||||
@export_group("NODES","object")
|
||||
@export var object_player : Node ## Select player if its not player or not inside player you can make 'is_only_lobby_owner' true.
|
||||
|
||||
@export_group("")
|
||||
@export var Position : bool = true
|
||||
@export var Rotation : bool = false
|
||||
@export var Scale : bool = false
|
||||
|
||||
|
||||
var packet_index_pos : int = 0
|
||||
var packet_index_rot : int = 0
|
||||
var packet_index_scale : int = 0
|
||||
|
||||
|
||||
|
||||
var last_pos : Vector3 = Vector3.ZERO
|
||||
var last_rot : Vector3 = Vector3.ZERO
|
||||
var last_scale : Vector3 = Vector3.ZERO
|
||||
|
||||
var transform_buffer : Array = [null,null,null]
|
||||
var last_index_buffer : PackedInt32Array = [0,0,0]
|
||||
|
||||
var posTimer : Timer
|
||||
var rotTimer : Timer
|
||||
var sclTimer : Timer
|
||||
|
||||
|
||||
func init_pos_timer():
|
||||
posTimer = Timer.new()
|
||||
posTimer.process_callback = Timer.TIMER_PROCESS_PHYSICS
|
||||
posTimer.wait_time = 0.1
|
||||
add_child(posTimer)
|
||||
posTimer.autostart = true
|
||||
posTimer.start()
|
||||
posTimer.connect("timeout",_on_pos_timer_timeout)
|
||||
func init_rot_timer():
|
||||
rotTimer = Timer.new()
|
||||
rotTimer.process_callback = Timer.TIMER_PROCESS_PHYSICS
|
||||
rotTimer.wait_time = 0.1
|
||||
add_child(rotTimer)
|
||||
rotTimer.autostart = true
|
||||
rotTimer.start()
|
||||
rotTimer.connect("timeout",_on_rot_timer_timeout)
|
||||
func init_scl_timer():
|
||||
sclTimer = Timer.new()
|
||||
sclTimer.process_callback = Timer.TIMER_PROCESS_PHYSICS
|
||||
sclTimer.wait_time = 0.1
|
||||
add_child(sclTimer)
|
||||
sclTimer.autostart = true
|
||||
sclTimer.start()
|
||||
sclTimer.connect("timeout",_on_scale_timer_timeout)
|
||||
|
||||
func _ready():
|
||||
init_pos_timer()
|
||||
init_rot_timer()
|
||||
init_scl_timer()
|
||||
|
||||
if is_only_lobby_owner == false and object_player.name != str(NetworkManager.STEAM_ID):
|
||||
posTimer.stop()
|
||||
rotTimer.stop()
|
||||
sclTimer.stop()
|
||||
elif is_only_lobby_owner:
|
||||
if Steam.getLobbyOwner(NetworkManager.LOBBY_ID) != NetworkManager.STEAM_ID:
|
||||
posTimer.stop()
|
||||
rotTimer.stop()
|
||||
sclTimer.stop()
|
||||
|
||||
|
||||
if !Position:
|
||||
posTimer.stop()
|
||||
if !Rotation:
|
||||
rotTimer.stop()
|
||||
if !Scale:
|
||||
sclTimer.stop()
|
||||
|
||||
func _on_pos_timer_timeout():
|
||||
if get_parent().global_position != last_pos and NetworkManager.GAME_STARTED:
|
||||
var DATA : Dictionary = {"Idx":packet_index_pos + 1,"player_id":NetworkManager.STEAM_ID,"TYPE":NetworkManager.TYPES.TRANFORM_SYNC,"value":get_parent().global_position,"node_path":get_path(),"property":"global_position"}
|
||||
P2P._send_P2P_Packet(0,0, DATA,Steam.P2P_SEND_UNRELIABLE)
|
||||
packet_index_pos = packet_index_pos + 1
|
||||
last_pos = get_parent().global_position
|
||||
|
||||
func _on_rot_timer_timeout():
|
||||
if get_parent().rotation != last_rot and NetworkManager.GAME_STARTED:
|
||||
var DATA : Dictionary = {"Idx":packet_index_rot + 1,"player_id":NetworkManager.STEAM_ID,"TYPE":NetworkManager.TYPES.TRANFORM_SYNC,"value":get_parent().rotation,"node_path":get_path(),"property":"rotation"}
|
||||
P2P._send_P2P_Packet(0,0, DATA,Steam.P2P_SEND_UNRELIABLE)
|
||||
packet_index_rot = packet_index_rot + 1
|
||||
last_rot = get_parent().rotation
|
||||
|
||||
func _on_scale_timer_timeout():
|
||||
if get_parent().scale != last_scale and NetworkManager.GAME_STARTED:
|
||||
var DATA : Dictionary = {"Idx":packet_index_scale + 1,"player_id":NetworkManager.STEAM_ID,"TYPE":NetworkManager.TYPES.TRANFORM_SYNC,"value":get_parent().scale,"node_path":get_path(),"property":"scale"}
|
||||
P2P._send_P2P_Packet(0,0, DATA,Steam.P2P_SEND_UNRELIABLE)
|
||||
packet_index_scale = packet_index_scale + 1
|
||||
last_scale = get_parent().scale
|
||||
|
||||
func _process(delta):
|
||||
if transform_buffer[0] != null and NetworkManager.GAME_STARTED:
|
||||
if transform_buffer[0]["Idx"] >= last_index_buffer[0] :
|
||||
var lerped_value = lerp(get_parent().get(transform_buffer[0]["property"]),transform_buffer[0]["value"],0.1)
|
||||
get_parent().set(transform_buffer[0]["property"],lerped_value)
|
||||
last_index_buffer[0] = transform_buffer[0]["Idx"]
|
||||
# Rotation
|
||||
if transform_buffer[1] != null and NetworkManager.GAME_STARTED:
|
||||
if transform_buffer[1]["Idx"] >= last_index_buffer[1]:
|
||||
var lerped_value = lerp(get_parent().get(transform_buffer[1]["property"]),transform_buffer[1]["value"],0.1)
|
||||
get_parent().set(transform_buffer[1]["property"],lerped_value)
|
||||
last_index_buffer[1] = transform_buffer[1]["Idx"]
|
||||
# Scale
|
||||
if transform_buffer[2] != null and NetworkManager.GAME_STARTED:
|
||||
if transform_buffer[2]["Idx"] >= last_index_buffer[2]:
|
||||
var lerped_value = lerp(get_parent().get(transform_buffer[2]["property"]),transform_buffer[2]["value"],0.1)
|
||||
get_parent().set(transform_buffer[2]["property"],lerped_value)
|
||||
last_index_buffer[2] = transform_buffer[2]["Idx"]
|
||||
func abort():
|
||||
posTimer.stop()
|
||||
rotTimer.stop()
|
||||
sclTimer.stop()
|
||||
set_process(false)
|
@ -0,0 +1,118 @@
|
||||
extends Synchronizer
|
||||
class_name VoiceSync
|
||||
|
||||
var current_sample_rate: int = 48000
|
||||
var local_playback : AudioStreamGeneratorPlayback = null
|
||||
var local_voice_buffer: PackedByteArray = PackedByteArray()
|
||||
var use_optimal_sample_rate: bool = false
|
||||
var DATA : Dictionary
|
||||
const REF_SAMPLE_RATE : int = 48000
|
||||
|
||||
@export var loopback_enabled : bool = false
|
||||
@export var audio_node : AudioStreamPlayer
|
||||
@export var voice_key : String = "push_to_talk"
|
||||
|
||||
var streamplay : AudioStreamGenerator
|
||||
var playback_stream : AudioStreamGeneratorPlayback
|
||||
|
||||
func _ready():
|
||||
change_voice_settings(true)
|
||||
DATA = {
|
||||
"steam_id": NetworkManager.STEAM_ID,
|
||||
"TYPE": NetworkManager.TYPES.VOICE,
|
||||
"node_path": get_path(),
|
||||
"voice_data": {}
|
||||
}
|
||||
|
||||
if NetworkManager.LOBBY_MEMBERS.size() > 1:
|
||||
loopback_enabled = false
|
||||
if audio_node != null:
|
||||
audio_node.stream.mix_rate = current_sample_rate
|
||||
audio_node.play()
|
||||
local_playback = audio_node.get_stream_playback()
|
||||
|
||||
|
||||
func change_voice_settings(push_to_talk :bool):
|
||||
|
||||
if push_to_talk:
|
||||
Steam.setInGameVoiceSpeaking(NetworkManager.STEAM_ID, false)
|
||||
Steam.stopVoiceRecording()
|
||||
set_process_input(true)
|
||||
else:
|
||||
Steam.setInGameVoiceSpeaking(NetworkManager.STEAM_ID, true)
|
||||
Steam.startVoiceRecording()
|
||||
set_process_input(false)
|
||||
|
||||
func record(voice : bool):
|
||||
Steam.setInGameVoiceSpeaking(NetworkManager.STEAM_ID, voice)
|
||||
|
||||
if voice:
|
||||
Steam.startVoiceRecording()
|
||||
else:
|
||||
Steam.stopVoiceRecording()
|
||||
|
||||
func check_for_voice() -> void:
|
||||
var available_voice: Dictionary = Steam.getAvailableVoice()
|
||||
if available_voice['result'] == Steam.VOICE_RESULT_OK and available_voice['buffer'] > 0:
|
||||
|
||||
var voice_data: Dictionary = Steam.getVoice()
|
||||
if voice_data['result'] == Steam.VOICE_RESULT_OK:
|
||||
|
||||
DATA["voice_data"] = voice_data
|
||||
|
||||
P2P._send_P2P_Packet(0,0, DATA,Steam.P2P_SEND_RELIABLE)
|
||||
if loopback_enabled:
|
||||
process_voice_data(voice_data)
|
||||
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
|
||||
|
||||
if get_parent().name == str(NetworkManager.STEAM_ID):
|
||||
check_for_voice()
|
||||
if Input.is_action_pressed(voice_key):
|
||||
record(true)
|
||||
|
||||
if Input.is_action_just_released(voice_key):
|
||||
record(false)
|
||||
|
||||
func get_sample_rate() -> void:
|
||||
var optimal_sample_rate: int = Steam.getVoiceOptimalSampleRate()
|
||||
# SpaceWar uses 11000 for sample rate?!
|
||||
# If are using Steam's "optimal" rate, set it; otherwise we default to 48000
|
||||
if use_optimal_sample_rate:
|
||||
current_sample_rate = optimal_sample_rate
|
||||
else:
|
||||
current_sample_rate = 48000
|
||||
|
||||
|
||||
func process_voice_data(voice_data: Dictionary) -> void:
|
||||
get_sample_rate()
|
||||
var pitch : float = float(current_sample_rate)/REF_SAMPLE_RATE
|
||||
audio_node.set_pitch_scale(pitch)
|
||||
|
||||
var decompressed_voice: Dictionary = Steam.decompressVoice(
|
||||
voice_data['buffer'],
|
||||
current_sample_rate)
|
||||
|
||||
if (
|
||||
not decompressed_voice['result'] == Steam.VOICE_RESULT_OK
|
||||
|
||||
):
|
||||
return
|
||||
|
||||
if local_playback != null:
|
||||
if local_playback.get_frames_available() <= 0:
|
||||
return
|
||||
|
||||
local_voice_buffer = decompressed_voice['uncompressed']
|
||||
|
||||
|
||||
for i: int in range(0, mini(local_playback.get_frames_available() * 2, local_voice_buffer.size()), 2):
|
||||
var raw_value = local_voice_buffer.decode_s16(i)
|
||||
# Convert the 16-bit integer to a float on from -1 to 1
|
||||
var amplitude: float = float(raw_value) / 32768.0
|
||||
local_playback.push_frame(Vector2(amplitude, amplitude))
|
||||
#local_voice_buffer.remove_at(0)
|
||||
#local_voice_buffer.remove_at(0)
|
||||
|
After Width: | Height: | Size: 15 KiB |
@ -0,0 +1,34 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://d0hi31a5q15pi"
|
||||
path="res://.godot/imported/funcSyncIcon.png-ee0ebc7f5fc0f5cd37293630b8d418fc.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/godot_steam_sync/Network/RadomeSteamSync/SyncNode/funcSyncIcon.png"
|
||||
dest_files=["res://.godot/imported/funcSyncIcon.png-ee0ebc7f5fc0f5cd37293630b8d418fc.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
After Width: | Height: | Size: 11 KiB |
@ -0,0 +1,34 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://dvqalcb5gle3l"
|
||||
path="res://.godot/imported/propertySyncIcon.png-7230c83490a15b0c58480c84d77bc8ba.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/godot_steam_sync/Network/RadomeSteamSync/SyncNode/propertySyncIcon.png"
|
||||
dest_files=["res://.godot/imported/propertySyncIcon.png-7230c83490a15b0c58480c84d77bc8ba.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
After Width: | Height: | Size: 12 KiB |
@ -0,0 +1,34 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://choidi1eihrtd"
|
||||
path="res://.godot/imported/transformSyncIcon.png-70197aa309a4df07b6cd61ec5f7effd6.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/godot_steam_sync/Network/RadomeSteamSync/SyncNode/transformSyncIcon.png"
|
||||
dest_files=["res://.godot/imported/transformSyncIcon.png-70197aa309a4df07b6cd61ec5f7effd6.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
20
addons/godot_steam_sync/godot_steam_sync.gd
Normal file
@ -0,0 +1,20 @@
|
||||
@tool
|
||||
extends EditorPlugin
|
||||
|
||||
|
||||
func _enter_tree() -> void:
|
||||
|
||||
add_autoload_singleton("NetworkManager","res://addons/godot_steam_sync/Network/RadomeSteamSync/NetworkManager.gd")
|
||||
add_autoload_singleton("P2P","res://addons/godot_steam_sync/Network/RadomeSteamSync/P2P.gd")
|
||||
# add_autoload_singleton("Command","res://addons/godot_steam_sync/Network/RadomeSteamSync/SyncNode/CommandSync.gd")
|
||||
#add_autoload_singleton("SceneManager","res://addons/godot_steam_sync/SceneChanger/SceneManager.gd")
|
||||
|
||||
|
||||
|
||||
func _exit_tree() -> void:
|
||||
remove_autoload_singleton("NetworkManager")
|
||||
remove_autoload_singleton("P2P")
|
||||
# remove_autoload_singleton("Command")
|
||||
#remove_autoload_singleton("SceneManager")
|
||||
|
||||
|
7
addons/godot_steam_sync/plugin.cfg
Normal file
@ -0,0 +1,7 @@
|
||||
[plugin]
|
||||
|
||||
name="GodotSteamSync"
|
||||
description="Advanced GodotSteam Synchornizer and Lobby System."
|
||||
author="Radome"
|
||||
version="0.1"
|
||||
script="godot_steam_sync.gd"
|
@ -2,7 +2,7 @@ class_name Interactable
|
||||
|
||||
extends Node3D
|
||||
|
||||
var player_reference: Player
|
||||
#var player_reference: Player
|
||||
|
||||
|
||||
func interact():
|
||||
|
@ -1,36 +0,0 @@
|
||||
[gd_scene load_steps=7 format=3 uid="uid://dm31ddavxv5gt"]
|
||||
|
||||
[ext_resource type="Script" path="res://assets/core/interactables/ship-helm/ship_helm.gd" id="1_lsarv"]
|
||||
[ext_resource type="PackedScene" uid="uid://d2e3plio1db16" path="res://assets/core/interactables/ship-helm/ship_helm.blend" id="2_n3ctg"]
|
||||
[ext_resource type="Texture2D" uid="uid://becx0d2aass76" path="res://addons/kennysprototypetextures/Purple/texture_purple (1).png" id="3_sgyh6"]
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_vj8ph"]
|
||||
albedo_texture = ExtResource("3_sgyh6")
|
||||
uv1_triplanar = true
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_mtdgj"]
|
||||
albedo_texture = ExtResource("3_sgyh6")
|
||||
uv1_triplanar = true
|
||||
|
||||
[sub_resource type="BoxShape3D" id="BoxShape3D_u6a0w"]
|
||||
size = Vector3(1, 1.23828, 0.332275)
|
||||
|
||||
[node name="ShipHelm" type="StaticBody3D"]
|
||||
collision_layer = 256
|
||||
collision_mask = 7
|
||||
script = ExtResource("1_lsarv")
|
||||
|
||||
[node name="ship_helm" parent="." instance=ExtResource("2_n3ctg")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.681346, 0)
|
||||
|
||||
[node name="Cube" parent="ship_helm" index="0"]
|
||||
material_override = SubResource("StandardMaterial3D_vj8ph")
|
||||
|
||||
[node name="Torus" parent="ship_helm" index="1"]
|
||||
material_override = SubResource("StandardMaterial3D_mtdgj")
|
||||
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.901878, 0.0608378)
|
||||
shape = SubResource("BoxShape3D_u6a0w")
|
||||
|
||||
[editable path="ship_helm"]
|
@ -1,51 +0,0 @@
|
||||
[remap]
|
||||
|
||||
importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://d2e3plio1db16"
|
||||
path="res://.godot/imported/ship_helm.blend-ff92b4e852f739448ae2d5004545d8b0.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/core/interactables/ship-helm/ship_helm.blend"
|
||||
dest_files=["res://.godot/imported/ship_helm.blend-ff92b4e852f739448ae2d5004545d8b0.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
nodes/root_type=""
|
||||
nodes/root_name=""
|
||||
nodes/apply_root_scale=true
|
||||
nodes/root_scale=1.0
|
||||
nodes/import_as_skeleton_bones=false
|
||||
meshes/ensure_tangents=true
|
||||
meshes/generate_lods=true
|
||||
meshes/create_shadow_meshes=true
|
||||
meshes/light_baking=1
|
||||
meshes/lightmap_texel_size=0.2
|
||||
meshes/force_disable_compression=false
|
||||
skins/use_named_skins=true
|
||||
animation/import=true
|
||||
animation/fps=30
|
||||
animation/trimming=false
|
||||
animation/remove_immutable_tracks=true
|
||||
animation/import_rest_as_RESET=false
|
||||
import_script/path=""
|
||||
_subresources={}
|
||||
blender/nodes/visible=0
|
||||
blender/nodes/active_collection_only=false
|
||||
blender/nodes/punctual_lights=true
|
||||
blender/nodes/cameras=true
|
||||
blender/nodes/custom_properties=true
|
||||
blender/nodes/modifiers=1
|
||||
blender/meshes/colors=false
|
||||
blender/meshes/uvs=true
|
||||
blender/meshes/normals=true
|
||||
blender/meshes/tangents=true
|
||||
blender/meshes/skins=2
|
||||
blender/meshes/export_bones_deforming_mesh_only=false
|
||||
blender/materials/unpack_enabled=true
|
||||
blender/materials/export_materials=1
|
||||
blender/animation/limit_playback=true
|
||||
blender/animation/always_sample=true
|
||||
blender/animation/group_tracks=true
|
@ -1,15 +0,0 @@
|
||||
class_name ShipHelm
|
||||
extends Interactable
|
||||
|
||||
@export var parent_ship: Ship
|
||||
|
||||
func interact():
|
||||
if player_reference.is_network_authority:
|
||||
if !parent_ship.ship_is_piloted:
|
||||
player_reference.set_is_piloting(true)
|
||||
parent_ship.set_piloting_player(player_reference)
|
||||
|
||||
elif parent_ship.ship_is_piloted:
|
||||
player_reference.set_is_piloting(false)
|
||||
parent_ship.remove_piloting_player()
|
||||
|
@ -1,70 +0,0 @@
|
||||
[gd_scene load_steps=3 format=3 uid="uid://7t1x82gvrw8a"]
|
||||
|
||||
[ext_resource type="Script" path="res://assets/core/networking/scripts/lobby.gd" id="1_o4fbq"]
|
||||
[ext_resource type="PackedScene" uid="uid://biryul3n6thlw" path="res://assets/core/networking/scenes/user_box_prefab.tscn" id="2_dpthk"]
|
||||
|
||||
[node name="Lobby" type="Control" node_paths=PackedStringArray("host_button", "leave_button", "start_button", "show_lobbies_button", "user_list_box", "lobby_list_box")]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
script = ExtResource("1_o4fbq")
|
||||
host_button = NodePath("Panel/HBoxContainer/Buttons/Host")
|
||||
leave_button = NodePath("Panel/HBoxContainer/Buttons/Leave")
|
||||
start_button = NodePath("Panel/HBoxContainer/Buttons/Start")
|
||||
show_lobbies_button = NodePath("Panel/HBoxContainer/Buttons/ShowLobbies")
|
||||
user_list_box = NodePath("Panel/HBoxContainer/PlayerList")
|
||||
lobby_list_box = NodePath("Panel/HBoxContainer/LobbyList")
|
||||
user_box_prefab = ExtResource("2_dpthk")
|
||||
|
||||
[node name="Panel" type="Panel" parent="."]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="Panel"]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="Buttons" type="VBoxContainer" parent="Panel/HBoxContainer"]
|
||||
custom_minimum_size = Vector2(500, 0)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 6
|
||||
size_flags_vertical = 4
|
||||
|
||||
[node name="Host" type="Button" parent="Panel/HBoxContainer/Buttons"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 4
|
||||
text = "Host Lobby"
|
||||
|
||||
[node name="Start" type="Button" parent="Panel/HBoxContainer/Buttons"]
|
||||
layout_mode = 2
|
||||
disabled = true
|
||||
text = "Start"
|
||||
|
||||
[node name="ShowLobbies" type="Button" parent="Panel/HBoxContainer/Buttons"]
|
||||
layout_mode = 2
|
||||
text = "Show Lobbies"
|
||||
|
||||
[node name="Leave" type="Button" parent="Panel/HBoxContainer/Buttons"]
|
||||
layout_mode = 2
|
||||
disabled = true
|
||||
text = "Leave Lobby"
|
||||
|
||||
[node name="PlayerList" type="VBoxContainer" parent="Panel/HBoxContainer"]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="LobbyList" type="VBoxContainer" parent="Panel/HBoxContainer"]
|
||||
visible = false
|
||||
layout_mode = 2
|
@ -1,20 +0,0 @@
|
||||
[gd_scene load_steps=2 format=3 uid="uid://biryul3n6thlw"]
|
||||
|
||||
[ext_resource type="Texture2D" uid="uid://fwub8fvl2u4i" path="res://addons/ingameconsole/ps1hagrid.png" id="1_gtutw"]
|
||||
|
||||
[node name="UserBox" type="HBoxContainer"]
|
||||
offset_right = 610.0
|
||||
offset_bottom = 128.0
|
||||
|
||||
[node name="PFP" type="TextureRect" parent="."]
|
||||
custom_minimum_size = Vector2(128, 128)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 4
|
||||
texture = ExtResource("1_gtutw")
|
||||
expand_mode = 1
|
||||
|
||||
[node name="Username" type="Label" parent="."]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Hagrid 1"
|
||||
horizontal_alignment = 1
|
@ -1,418 +0,0 @@
|
||||
extends Node
|
||||
|
||||
const STEAM_APP_ID: int = 480
|
||||
|
||||
const PACKET_READ_LIMIT: int = 32
|
||||
|
||||
var level_scene: PackedScene
|
||||
var player_scene: PackedScene
|
||||
|
||||
var lobby_data
|
||||
var lobby_id: int = 0
|
||||
var lobby_members: Array = []
|
||||
var lobby_members_max: int = 10
|
||||
var lobby_vote_kick: bool = false
|
||||
var lobby_type: int = Steam.LOBBY_TYPE_PUBLIC
|
||||
var steam_id: int = 0
|
||||
var steam_username: String = ""
|
||||
var avatar_texture_cache: Dictionary = {}
|
||||
var is_host: bool = false
|
||||
var host_id: int
|
||||
|
||||
var uuid_counter: int = 0
|
||||
|
||||
var node_map: Dictionary = {}
|
||||
|
||||
signal lobby_created(lobby_id: int)
|
||||
signal lobby_joined(lobby_id: int)
|
||||
signal user_joined_lobby(user_id: int)
|
||||
signal user_left_lobby(user_id: int)
|
||||
signal host_left_lobby()
|
||||
signal on_game_started()
|
||||
signal on_lobbies_received(these_lobbies: Array)
|
||||
|
||||
signal property_update_received(node_id: int, property_name: String, value: Variant)
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
player_scene = load("res://assets/core/player-controller/scenes/player.tscn") as PackedScene
|
||||
level_scene = load("res://assets/core/enviroment/dev-level/dev-level.tscn") as PackedScene
|
||||
_init_steam()
|
||||
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
Steam.run_callbacks()
|
||||
|
||||
if lobby_id > 0:
|
||||
read_all_p2p_packets()
|
||||
|
||||
|
||||
func _init_steam() -> void:
|
||||
var init_result = Steam.steamInitEx(true, STEAM_APP_ID)
|
||||
print("Steam Init Result: " + str(init_result))
|
||||
|
||||
if init_result['status'] > 0:
|
||||
printerr("Steam failed to initialize, shutting down: " + str(init_result))
|
||||
#get_tree().quit()
|
||||
|
||||
steam_id = Steam.getSteamID()
|
||||
steam_username = Steam.getFriendPersonaName(steam_id)
|
||||
|
||||
Steam.p2p_session_request.connect(_on_p2p_session_request)
|
||||
Steam.p2p_session_connect_fail.connect(_on_p2p_session_connect_fail)
|
||||
|
||||
Steam.join_requested.connect(_on_lobby_join_requested)
|
||||
Steam.lobby_chat_update.connect(_on_lobby_chat_update)
|
||||
Steam.lobby_created.connect(_on_lobby_created)
|
||||
#Steam.lobby_data_update.connect(_on_lobby_data_update)
|
||||
#Steam.lobby_invite.connect(_on_lobby_invite)
|
||||
Steam.lobby_joined.connect(_on_lobby_joined)
|
||||
Steam.lobby_match_list.connect(_on_lobby_match_list)
|
||||
Steam.lobby_message.connect(_on_lobby_message)
|
||||
Steam.persona_state_change.connect(_on_persona_change)
|
||||
|
||||
|
||||
func _on_lobby_join_requested(this_lobby_id: int, friend_id: int):
|
||||
var owner_name: String = Steam.getFriendPersonaName(friend_id)
|
||||
print("Trying to join " + owner_name + "'s lobby...")
|
||||
join_lobby(this_lobby_id)
|
||||
|
||||
|
||||
func _on_lobby_chat_update(this_lobby_id: int, change_id: int, making_change_id: int, chat_state: int) -> void:
|
||||
var changer_name: String = Steam.getFriendPersonaName(change_id)
|
||||
|
||||
# If a player has joined the lobby
|
||||
if chat_state == Steam.CHAT_MEMBER_STATE_CHANGE_ENTERED:
|
||||
print("%s has joined the lobby." % changer_name)
|
||||
|
||||
elif chat_state == Steam.CHAT_MEMBER_STATE_CHANGE_LEFT:
|
||||
user_left_lobby.emit(change_id)
|
||||
print("%s has left the lobby." % changer_name)
|
||||
|
||||
elif chat_state == Steam.CHAT_MEMBER_STATE_CHANGE_KICKED:
|
||||
print("%s has been kicked from the lobby." % changer_name)
|
||||
|
||||
elif chat_state == Steam.CHAT_MEMBER_STATE_CHANGE_BANNED:
|
||||
print("%s has been banned from the lobby." % changer_name)
|
||||
|
||||
else:
|
||||
print("%s did... something." % changer_name)
|
||||
|
||||
get_lobby_members()
|
||||
|
||||
|
||||
func _on_lobby_created(connect: int, this_lobby_id: int) -> void:
|
||||
if connect == 1:
|
||||
lobby_id = this_lobby_id
|
||||
lobby_created.emit(lobby_id)
|
||||
print("Lobby created with ID: " + str(lobby_id))
|
||||
|
||||
Steam.setLobbyJoinable(lobby_id, true)
|
||||
Steam.setLobbyData(lobby_id, "name", str(steam_username) + "'s Lobby")
|
||||
|
||||
var set_relay: bool = Steam.allowP2PPacketRelay(true)
|
||||
|
||||
|
||||
#func _on_lobby_data_update():
|
||||
# pass
|
||||
|
||||
|
||||
#func _on_lobby_invite():
|
||||
# pass
|
||||
|
||||
|
||||
func _on_lobby_joined(this_lobby_id: int, _permissions: int, _locked: bool, response: int) -> void:
|
||||
if response == Steam.CHAT_ROOM_ENTER_RESPONSE_SUCCESS:
|
||||
lobby_id = this_lobby_id
|
||||
get_lobby_members()
|
||||
make_p2p_handshake()
|
||||
lobby_joined.emit(lobby_id)
|
||||
else:
|
||||
var fail_reason: String
|
||||
|
||||
match response:
|
||||
Steam.CHAT_ROOM_ENTER_RESPONSE_DOESNT_EXIST: fail_reason = "This lobby no longer exists."
|
||||
Steam.CHAT_ROOM_ENTER_RESPONSE_NOT_ALLOWED: fail_reason = "You don't have permission to join this lobby."
|
||||
Steam.CHAT_ROOM_ENTER_RESPONSE_FULL: fail_reason = "The lobby is now full."
|
||||
Steam.CHAT_ROOM_ENTER_RESPONSE_ERROR: fail_reason = "Uh... something unexpected happened!"
|
||||
Steam.CHAT_ROOM_ENTER_RESPONSE_BANNED: fail_reason = "You are banned from this lobby."
|
||||
Steam.CHAT_ROOM_ENTER_RESPONSE_LIMITED: fail_reason = "You cannot join due to having a limited account."
|
||||
Steam.CHAT_ROOM_ENTER_RESPONSE_CLAN_DISABLED: fail_reason = "This lobby is locked or disabled."
|
||||
Steam.CHAT_ROOM_ENTER_RESPONSE_COMMUNITY_BAN: fail_reason = "This lobby is community locked."
|
||||
Steam.CHAT_ROOM_ENTER_RESPONSE_MEMBER_BLOCKED_YOU: fail_reason = "A user in the lobby has blocked you from joining."
|
||||
Steam.CHAT_ROOM_ENTER_RESPONSE_YOU_BLOCKED_MEMBER: fail_reason = "A user you have blocked is in the lobby."
|
||||
|
||||
print("Failed to join this chat room: %s" % fail_reason)
|
||||
|
||||
|
||||
func _on_lobby_match_list(these_lobbies: Array) -> void:
|
||||
on_lobbies_received.emit(these_lobbies)
|
||||
|
||||
|
||||
func _on_lobby_message():
|
||||
pass
|
||||
|
||||
|
||||
func _on_persona_change(this_steam_id: int, _flags: int) -> void:
|
||||
if lobby_id > 0 and lobby_members:
|
||||
for member in lobby_members:
|
||||
if member["steam_id"] == this_steam_id:
|
||||
var user: String = str(this_steam_id) + ":" + Steam.getFriendPersonaName(this_steam_id)
|
||||
print("User (%s) had information change, updating lobby list." % user)
|
||||
get_lobby_members()
|
||||
break
|
||||
|
||||
|
||||
func _on_p2p_session_request(remote_id: int):
|
||||
var this_requester: String = Steam.getFriendPersonaName(remote_id)
|
||||
print("P2P session request from: " + this_requester)
|
||||
Steam.acceptP2PSessionWithUser(remote_id)
|
||||
make_p2p_handshake()
|
||||
|
||||
|
||||
func _on_p2p_session_connect_fail(steam_id: int, session_error: int) -> void:
|
||||
# If no error was given
|
||||
if session_error == 0:
|
||||
print("WARNING: Session failure with %s: no error given" % steam_id)
|
||||
|
||||
# Else if target user was not running the same game
|
||||
elif session_error == 1:
|
||||
print("WARNING: Session failure with %s: target user not running the same game" % steam_id)
|
||||
|
||||
# Else if local user doesn't own app / game
|
||||
elif session_error == 2:
|
||||
print("WARNING: Session failure with %s: local user doesn't own app / game" % steam_id)
|
||||
|
||||
# Else if target user isn't connected to Steam
|
||||
elif session_error == 3:
|
||||
print("WARNING: Session failure with %s: target user isn't connected to Steam" % steam_id)
|
||||
|
||||
# Else if connection timed out
|
||||
elif session_error == 4:
|
||||
print("WARNING: Session failure with %s: connection timed out" % steam_id)
|
||||
|
||||
# Else if unused
|
||||
elif session_error == 5:
|
||||
print("WARNING: Session failure with %s: unused" % steam_id)
|
||||
|
||||
# Else no known error
|
||||
else:
|
||||
print("WARNING: Session failure with %s: unknown error %s" % [steam_id, session_error])
|
||||
|
||||
|
||||
func host_lobby():
|
||||
if lobby_id == 0:
|
||||
Steam.createLobby(lobby_type, lobby_members_max)
|
||||
is_host = true
|
||||
host_id = steam_id
|
||||
else:
|
||||
printerr("Cannot host lobby, already in a lobby")
|
||||
|
||||
|
||||
func join_lobby(this_lobby_id: int):
|
||||
print("Attempting to join lobby: " + str(this_lobby_id))
|
||||
lobby_members.clear()
|
||||
Steam.joinLobby(this_lobby_id)
|
||||
host_id = Steam.getLobbyOwner(this_lobby_id)
|
||||
|
||||
|
||||
func leave_lobby():
|
||||
if lobby_id != 0:
|
||||
Steam.leaveLobby(lobby_id)
|
||||
lobby_id = 0
|
||||
host_id = 0
|
||||
|
||||
for this_member in lobby_members:
|
||||
if this_member["steam_id"] != steam_id:
|
||||
Steam.closeP2PSessionWithUser(this_member["steam_id"])
|
||||
|
||||
lobby_members.clear()
|
||||
print("Left lobby.")
|
||||
|
||||
|
||||
|
||||
func get_lobbies():
|
||||
Steam.addRequestLobbyListDistanceFilter(Steam.LOBBY_DISTANCE_FILTER_WORLDWIDE)
|
||||
Steam.requestLobbyList()
|
||||
|
||||
|
||||
func get_lobby_members():
|
||||
lobby_members.clear()
|
||||
var member_count: int = Steam.getNumLobbyMembers(lobby_id)
|
||||
|
||||
for this_member in range(0, member_count):
|
||||
var member_steam_id: int = Steam.getLobbyMemberByIndex(lobby_id, this_member)
|
||||
var member_steam_name: String = Steam.getFriendPersonaName(member_steam_id)
|
||||
lobby_members.append({"steam_id":member_steam_id, "steam_name":member_steam_name})
|
||||
user_joined_lobby.emit(member_steam_id)
|
||||
await request_player_avatar(member_steam_id)
|
||||
|
||||
|
||||
func make_p2p_handshake():
|
||||
print("Sending p2p handshake to the lobby...")
|
||||
send_p2p_packet(0, {"message":"handshake", "from":steam_id})
|
||||
|
||||
|
||||
func send_p2p_packet(target: int, packet_data: Dictionary) -> void:
|
||||
# Set the send_type and channel
|
||||
var send_type: int = Steam.P2P_SEND_RELIABLE
|
||||
var channel: int = 0
|
||||
|
||||
# Create a data array to send the data through
|
||||
var this_data: PackedByteArray
|
||||
|
||||
# Compress the PackedByteArray we create from our dictionary using the GZIP compression method
|
||||
var compressed_data: PackedByteArray = var_to_bytes(packet_data).compress(FileAccess.COMPRESSION_GZIP)
|
||||
this_data.append_array(compressed_data)
|
||||
|
||||
# If sending a packet to everyone
|
||||
if target == 0:
|
||||
# If there is more than one user, send packets
|
||||
if lobby_members.size() > 1:
|
||||
# Loop through all members that aren't you
|
||||
for this_member in lobby_members:
|
||||
if this_member['steam_id'] != steam_id:
|
||||
Steam.sendP2PPacket(this_member['steam_id'], this_data, send_type, channel)
|
||||
#print("Sent packet to %s." % this_member['steam_name'])
|
||||
# If sending a packet to everyone except the host
|
||||
elif target == 1:
|
||||
if lobby_members.size() > 1:
|
||||
# Loop through all members that aren't the host
|
||||
for this_member in lobby_members:
|
||||
if this_member['steam_id'] != host_id:
|
||||
Steam.sendP2PPacket(this_member['steam_id'], this_data, send_type, channel)
|
||||
# Else send it to someone specific
|
||||
else:
|
||||
Steam.sendP2PPacket(target, this_data, send_type, channel)
|
||||
|
||||
|
||||
func read_all_p2p_packets(read_count: int = 0):
|
||||
if read_count >= PACKET_READ_LIMIT:
|
||||
return
|
||||
|
||||
if Steam.getAvailableP2PPacketSize(0) > 0:
|
||||
read_p2p_packet()
|
||||
read_all_p2p_packets(read_count + 1)
|
||||
|
||||
|
||||
func read_p2p_packet() -> void:
|
||||
var packet_size: int = Steam.getAvailableP2PPacketSize(0)
|
||||
|
||||
# There is a packet
|
||||
if packet_size > 0:
|
||||
var this_packet: Dictionary = Steam.readP2PPacket(packet_size, 0)
|
||||
|
||||
if this_packet.is_empty() or this_packet == null:
|
||||
print("WARNING: read an empty packet with non-zero size!")
|
||||
|
||||
# Get the remote user's ID
|
||||
var packet_sender: int = this_packet['remote_steam_id']
|
||||
|
||||
# Make the packet data readable
|
||||
var packet_code: PackedByteArray = this_packet['data']
|
||||
|
||||
# Decompress the array before turning it into a useable dictionary
|
||||
var readable_data: Dictionary = bytes_to_var(packet_code.decompress_dynamic(-1, FileAccess.COMPRESSION_GZIP))
|
||||
|
||||
# Handshake packet
|
||||
if "message" in readable_data and readable_data["message"] == "handshake":
|
||||
if packet_sender != steam_id:
|
||||
print("Received handshake packet from %s." % Steam.getFriendPersonaName(packet_sender))
|
||||
|
||||
# Start game packet
|
||||
if "message" in readable_data and readable_data["message"] == "start_game":
|
||||
print("Received game start packet.")
|
||||
_on_game_started()
|
||||
|
||||
# Property update packet
|
||||
if "message" in readable_data and readable_data["message"] == "property_update":
|
||||
if "node_id" in readable_data and "property_name" in readable_data and "value" in readable_data:
|
||||
emit_signal("property_update_received", readable_data["node_id"], readable_data["property_name"], readable_data["value"])
|
||||
|
||||
|
||||
func register_node(node: Node) -> String:
|
||||
var uuid = generate_uuid()
|
||||
node_map[uuid] = node
|
||||
return uuid
|
||||
|
||||
|
||||
func generate_uuid() -> String:
|
||||
uuid_counter += 1
|
||||
return str(uuid_counter)
|
||||
|
||||
|
||||
func request_player_avatar(user_id: int, size: int = 128) -> void:
|
||||
var avatar_texture: ImageTexture = null
|
||||
Steam.getPlayerAvatar(Steam.AVATAR_LARGE, user_id)
|
||||
print("Attempting to get avatar for " + Steam.getFriendPersonaName(user_id) + ".....")
|
||||
|
||||
Steam.avatar_loaded.connect(func(loaded_user_id: int, avatar_size: int, avatar_buffer: PackedByteArray):
|
||||
if loaded_user_id == user_id:
|
||||
var avatar_image: Image = Image.create_from_data(avatar_size, avatar_size, false, Image.FORMAT_RGBA8, avatar_buffer)
|
||||
|
||||
if avatar_size != size:
|
||||
avatar_image.resize(size, size, Image.INTERPOLATE_LANCZOS)
|
||||
|
||||
avatar_texture = ImageTexture.create_from_image(avatar_image)
|
||||
avatar_texture_cache[user_id] = avatar_texture
|
||||
)
|
||||
|
||||
|
||||
while not avatar_texture_cache.has(user_id):
|
||||
await get_tree().process_frame
|
||||
|
||||
|
||||
func get_player_avatar(user_id: int) -> ImageTexture:
|
||||
if avatar_texture_cache.has(user_id):
|
||||
return avatar_texture_cache[user_id]
|
||||
else:
|
||||
await request_player_avatar(user_id)
|
||||
return avatar_texture_cache[user_id]
|
||||
|
||||
|
||||
func start_game():
|
||||
print("Starting game...")
|
||||
var packet_data = {"message":"start_game"}
|
||||
send_p2p_packet(0, packet_data)
|
||||
_on_game_started()
|
||||
|
||||
|
||||
func _on_game_started():
|
||||
print("Game started.")
|
||||
var level: Node = level_scene.instantiate()
|
||||
get_tree().root.add_child(level)
|
||||
on_game_started.emit()
|
||||
|
||||
|
||||
for member in lobby_members:
|
||||
var player: Player = player_scene.instantiate()
|
||||
player.name = "Player_" + str(member["steam_id"])
|
||||
player.username = Steam.getFriendPersonaName(member["steam_id"])
|
||||
player.steam_id = member["steam_id"]
|
||||
|
||||
if member["steam_id"] == steam_id:
|
||||
player.is_network_authority = true
|
||||
else:
|
||||
player.is_network_authority = false
|
||||
|
||||
level.add_child(player)
|
||||
|
||||
|
||||
func sync_property_to_host(node_id: String, property_name: String, value: Variant):
|
||||
var packet_data = {"message":"property_update", "node_id": node_id, "property_name": property_name, "value": value}
|
||||
send_p2p_packet(host_id, packet_data)
|
||||
|
||||
|
||||
func sync_property_to_peer(node_id: String, property_name: String, value: Variant, target_peer: int):
|
||||
var packet_data = {"message":"property_update", "node_id": node_id, "property_name": property_name, "value": value}
|
||||
send_p2p_packet(target_peer, packet_data)
|
||||
|
||||
|
||||
func sync_property_to_all(node_id: String, property_name: String, value: Variant):
|
||||
var packet_data = {"message":"property_update", "node_id": node_id, "property_name":property_name, "value":value}
|
||||
send_p2p_packet(0, packet_data)
|
||||
|
||||
|
||||
func sync_property_to_all_except_host(node_id: String, property_name: String, value: Variant):
|
||||
var packet_data = {"message":"property_update", "node_id": node_id, "property_name":property_name, "value":value}
|
||||
send_p2p_packet(1, packet_data)
|
7
assets/core/networking/scripts/SceneManager.gd
Normal file
@ -0,0 +1,7 @@
|
||||
extends Node
|
||||
|
||||
func change_scene(path: String):
|
||||
var packet: Dictionary = {'TYPE': NetworkManager.TYPES.CHANGE_SCENE,'scene': path}
|
||||
P2P._send_P2P_Packet(0, 0, packet, Steam.P2P_SEND_RELIABLE)
|
||||
get_tree().change_scene_to_file(path)
|
||||
|
@ -1,193 +0,0 @@
|
||||
extends Control
|
||||
|
||||
@export var host_button: Button
|
||||
@export var leave_button: Button
|
||||
@export var start_button: Button
|
||||
@export var show_lobbies_button: Button
|
||||
@export var user_list_box: VBoxContainer
|
||||
@export var lobby_list_box: VBoxContainer
|
||||
@export var user_box_prefab: PackedScene
|
||||
|
||||
var added_users = []
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
host_button.pressed.connect(_on_host)
|
||||
leave_button.pressed.connect(_on_leave)
|
||||
start_button.pressed.connect(_on_start)
|
||||
show_lobbies_button.pressed.connect(_on_show_lobbies)
|
||||
|
||||
NetworkManager.lobby_created.connect(_on_lobby_created)
|
||||
NetworkManager.lobby_joined.connect(_on_lobby_joined)
|
||||
NetworkManager.user_joined_lobby.connect(_on_user_joined_lobby)
|
||||
NetworkManager.user_left_lobby.connect(_on_user_left_lobby)
|
||||
NetworkManager.on_game_started.connect(_on_game_started)
|
||||
NetworkManager.on_lobbies_received.connect(_on_lobbies_received)
|
||||
|
||||
GameConsole.register_command(Command.new("update", update_ui, [], "Updates the lobby UI"))
|
||||
|
||||
|
||||
func _on_host():
|
||||
NetworkManager.host_lobby()
|
||||
show_lobbies_button.disabled = true
|
||||
|
||||
|
||||
func _on_leave():
|
||||
NetworkManager.leave_lobby()
|
||||
reset_ui()
|
||||
|
||||
|
||||
func _on_show_lobbies():
|
||||
lobby_list_box.visible = true
|
||||
NetworkManager.get_lobbies()
|
||||
|
||||
|
||||
func _on_lobbies_received(these_lobbies: Array):
|
||||
var friend_lobbies = get_lobbies_with_friends()
|
||||
|
||||
# Clear the lobby list box before adding new lobbies
|
||||
for child in lobby_list_box.get_children():
|
||||
child.queue_free()
|
||||
|
||||
if friend_lobbies.size() == 0:
|
||||
var no_lobbies_label = Label.new()
|
||||
no_lobbies_label.text = "No lobbies available"
|
||||
lobby_list_box.add_child(no_lobbies_label)
|
||||
else:
|
||||
for lobby_id in friend_lobbies.keys():
|
||||
# Pull lobby data from Steam, these are specific to our example
|
||||
var lobby_name: String = Steam.getLobbyData(lobby_id, "name")
|
||||
var lobby_mode: String = Steam.getLobbyData(lobby_id, "mode")
|
||||
|
||||
# Get the current number of members
|
||||
var lobby_num_members: int = Steam.getNumLobbyMembers(lobby_id)
|
||||
|
||||
# Create a button for the lobby
|
||||
var lobby_button: Button = Button.new()
|
||||
lobby_button.set_text(lobby_name + " " + str(lobby_num_members) + "/" + str(Steam.getLobbyMemberLimit(lobby_id)))
|
||||
lobby_button.set_size(Vector2(800, 50))
|
||||
lobby_button.set_name("lobby_%s" % lobby_id)
|
||||
lobby_button.connect("pressed", Callable(self, "join_lobby").bind(lobby_id))
|
||||
|
||||
# Add the new lobby to the list
|
||||
lobby_list_box.add_child(lobby_button)
|
||||
|
||||
|
||||
|
||||
func get_lobbies_with_friends() -> Dictionary:
|
||||
var results: Dictionary = {}
|
||||
|
||||
for i in range(0, Steam.getFriendCount()):
|
||||
var steam_id: int = Steam.getFriendByIndex(i, Steam.FRIEND_FLAG_IMMEDIATE)
|
||||
var game_info: Dictionary = Steam.getFriendGamePlayed(steam_id)
|
||||
|
||||
if game_info.is_empty():
|
||||
# This friend is not playing a game
|
||||
continue
|
||||
else:
|
||||
# They are playing a game, check if it's the same game as ours
|
||||
var app_id: int = game_info['id']
|
||||
var lobby = game_info['lobby']
|
||||
|
||||
if app_id != Steam.getAppID() or lobby is String:
|
||||
# Either not in this game, or not in a lobby
|
||||
continue
|
||||
|
||||
if not results.has(lobby):
|
||||
results[lobby] = []
|
||||
|
||||
results[lobby].append(steam_id)
|
||||
|
||||
return results
|
||||
|
||||
|
||||
func get_friends_in_lobbies() -> Dictionary:
|
||||
var results: Dictionary = {}
|
||||
|
||||
for i in range(0, Steam.getFriendCount()):
|
||||
var steam_id: int = Steam.getFriendByIndex(i, Steam.FRIEND_FLAG_IMMEDIATE)
|
||||
var game_info: Dictionary = Steam.getFriendGamePlayed(steam_id)
|
||||
|
||||
if game_info.is_empty():
|
||||
# This friend is not playing a game
|
||||
continue
|
||||
else:
|
||||
# They are playing a game, check if it's the same game as ours
|
||||
var app_id: int = game_info['id']
|
||||
var lobby = game_info['lobby']
|
||||
|
||||
if app_id != Steam.getAppID() or lobby is String:
|
||||
# Either not in this game, or not in a lobby
|
||||
continue
|
||||
|
||||
results[steam_id] = lobby
|
||||
|
||||
return results
|
||||
|
||||
|
||||
func join_lobby(lobby_id: int):
|
||||
NetworkManager.join_lobby(lobby_id)
|
||||
|
||||
|
||||
func _on_lobby_created(lobby_id: int):
|
||||
print("Lobby created")
|
||||
host_button.disabled = true
|
||||
leave_button.disabled = false
|
||||
start_button.disabled = false
|
||||
user_list_box.visible = true
|
||||
|
||||
|
||||
func _on_lobby_joined(lobby_id: int):
|
||||
host_button.disabled = true
|
||||
leave_button.disabled = false
|
||||
user_list_box.visible = true
|
||||
lobby_list_box.visible = false
|
||||
|
||||
|
||||
func _on_user_joined_lobby(user_id: int):
|
||||
if user_id in added_users:
|
||||
return # User already added, skip
|
||||
added_users.append(user_id)
|
||||
update_ui()
|
||||
|
||||
|
||||
func _on_user_left_lobby(user_id: int):
|
||||
added_users.erase(user_id)
|
||||
update_ui()
|
||||
|
||||
|
||||
func _on_start():
|
||||
NetworkManager.start_game()
|
||||
visible = false
|
||||
|
||||
|
||||
func _on_game_started():
|
||||
visible = false
|
||||
|
||||
|
||||
func reset_ui():
|
||||
host_button.disabled = false
|
||||
leave_button.disabled = true
|
||||
start_button.disabled = true
|
||||
user_list_box.visible = false
|
||||
lobby_list_box.visible = false
|
||||
show_lobbies_button.disabled = false
|
||||
added_users.clear()
|
||||
for child in user_list_box.get_children():
|
||||
child.queue_free()
|
||||
|
||||
for child in lobby_list_box.get_children():
|
||||
child.queue_free()
|
||||
|
||||
|
||||
func update_ui() -> void:
|
||||
for child in user_list_box.get_children():
|
||||
child.queue_free()
|
||||
|
||||
for user_id in added_users:
|
||||
var user_box = user_box_prefab.instantiate()
|
||||
user_box.get_node("Username").text = str(Steam.getFriendPersonaName(user_id))
|
||||
user_list_box.add_child(user_box)
|
||||
|
||||
var avatar_texture = await NetworkManager.get_player_avatar(user_id)
|
||||
user_box.get_node("PFP").texture = avatar_texture
|
Before Width: | Height: | Size: 4.4 KiB |
@ -1,8 +0,0 @@
|
||||
shader_type canvas_item;
|
||||
|
||||
uniform sampler2D SCREEN_TEXTURE : hint_screen_texture, filter_linear_mipmap;
|
||||
|
||||
void fragment(){
|
||||
vec4 color = vec4(texture(SCREEN_TEXTURE, SCREEN_UV).rgb, texture(TEXTURE, UV).a);
|
||||
COLOR = vec4(1.0 - color.rgb, color.a);
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
[gd_scene load_steps=5 format=3 uid="uid://c6w0ivy4hetrl"]
|
||||
|
||||
[ext_resource type="Script" path="res://assets/core/player-controller/scripts/player.gd" id="1_bv7t4"]
|
||||
[ext_resource type="Script" path="res://assets/core/player-controller/scripts/player_interacter.gd" id="2_wvu3d"]
|
||||
|
||||
[sub_resource type="CapsuleMesh" id="CapsuleMesh_v7b3h"]
|
||||
radius = 0.35
|
||||
|
||||
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_qlkab"]
|
||||
radius = 0.35
|
||||
|
||||
[node name="Player" type="CharacterBody3D" node_paths=PackedStringArray("camera")]
|
||||
script = ExtResource("1_bv7t4")
|
||||
camera = NodePath("Neck/Camera3D")
|
||||
|
||||
[node name="PlayerTag" type="Label3D" parent="."]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.26654, 0)
|
||||
billboard = 1
|
||||
text = "username"
|
||||
|
||||
[node name="MeshInstance3D" type="MeshInstance3D" parent="."]
|
||||
mesh = SubResource("CapsuleMesh_v7b3h")
|
||||
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
|
||||
shape = SubResource("CapsuleShape3D_qlkab")
|
||||
|
||||
[node name="Neck" type="Node3D" parent="."]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.65, 0)
|
||||
|
||||
[node name="Camera3D" type="Camera3D" parent="Neck"]
|
||||
|
||||
[node name="RayCast3D" type="RayCast3D" parent="Neck/Camera3D" node_paths=PackedStringArray("player")]
|
||||
target_position = Vector3(0, 0, -2.5)
|
||||
script = ExtResource("2_wvu3d")
|
||||
player = NodePath("../../..")
|
@ -1,130 +0,0 @@
|
||||
[gd_scene load_steps=12 format=3 uid="uid://c6w0ivy4hetrl"]
|
||||
|
||||
[ext_resource type="Script" path="res://assets/core/player-controller/scripts/player.gd" id="1_bv7t4"]
|
||||
[ext_resource type="Texture2D" uid="uid://fwub8fvl2u4i" path="res://addons/ingameconsole/ps1hagrid.png" id="2_omgn1"]
|
||||
[ext_resource type="Script" path="res://assets/core/player-controller/scripts/player_interacter.gd" id="2_wvu3d"]
|
||||
[ext_resource type="Script" path="res://assets/core/player-controller/scripts/player_hud.gd" id="3_02ne1"]
|
||||
[ext_resource type="Shader" path="res://assets/core/player-controller/scenes/inverted_crosshair.gdshader" id="3_rakxt"]
|
||||
[ext_resource type="Texture2D" uid="uid://c3lblkyavjtol" path="res://assets/core/player-controller/2d_crosshair_dot.png" id="4_3uwxe"]
|
||||
|
||||
[sub_resource type="CapsuleMesh" id="CapsuleMesh_v7b3h"]
|
||||
radius = 0.35
|
||||
height = 1.5
|
||||
|
||||
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_qlkab"]
|
||||
radius = 0.35
|
||||
|
||||
[sub_resource type="SphereMesh" id="SphereMesh_vu2l8"]
|
||||
radius = 0.35
|
||||
height = 0.7
|
||||
|
||||
[sub_resource type="ShaderMaterial" id="ShaderMaterial_wmxrr"]
|
||||
shader = ExtResource("3_rakxt")
|
||||
|
||||
[sub_resource type="ShaderMaterial" id="ShaderMaterial_0n7pd"]
|
||||
shader = ExtResource("3_rakxt")
|
||||
|
||||
[node name="Player" type="CharacterBody3D" node_paths=PackedStringArray("camera", "neck", "player_avatar_face", "body", "head", "player_hud")]
|
||||
collision_mask = 242
|
||||
script = ExtResource("1_bv7t4")
|
||||
camera = NodePath("Neck/Camera3D")
|
||||
neck = NodePath("Neck")
|
||||
player_avatar_face = NodePath("Neck/Camera3D/PlayerAvatarFace")
|
||||
body = NodePath("Body")
|
||||
head = NodePath("Neck/Head")
|
||||
player_hud = NodePath("PlayerHUD")
|
||||
|
||||
[node name="PlayerTag" type="Label3D" parent="."]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.26654, 0)
|
||||
visibility_range_begin = 0.7
|
||||
visibility_range_end = 15.0
|
||||
billboard = 1
|
||||
text = "username"
|
||||
|
||||
[node name="Body" type="MeshInstance3D" parent="."]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.5, 0)
|
||||
mesh = SubResource("CapsuleMesh_v7b3h")
|
||||
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
|
||||
shape = SubResource("CapsuleShape3D_qlkab")
|
||||
|
||||
[node name="Neck" type="Node3D" parent="."]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.65, 0)
|
||||
|
||||
[node name="Camera3D" type="Camera3D" parent="Neck"]
|
||||
|
||||
[node name="RayCast3D" type="RayCast3D" parent="Neck/Camera3D" node_paths=PackedStringArray("player")]
|
||||
target_position = Vector3(0, 0, -2.5)
|
||||
collision_mask = 256
|
||||
script = ExtResource("2_wvu3d")
|
||||
player = NodePath("../../..")
|
||||
|
||||
[node name="PlayerAvatarFace" type="Sprite3D" parent="Neck/Camera3D"]
|
||||
transform = Transform3D(-0.1, 0, -8.74228e-09, 0, 0.1, 0, 8.74228e-09, 0, -0.1, 0, 0, -0.349588)
|
||||
flip_h = true
|
||||
shaded = true
|
||||
double_sided = false
|
||||
texture = ExtResource("2_omgn1")
|
||||
|
||||
[node name="Head" type="MeshInstance3D" parent="Neck"]
|
||||
mesh = SubResource("SphereMesh_vu2l8")
|
||||
|
||||
[node name="PlayerHUD" type="CanvasLayer" parent="." node_paths=PackedStringArray("fps_label")]
|
||||
script = ExtResource("3_02ne1")
|
||||
fps_label = NodePath("Debug/HBoxContainer/VBoxContainer/MarginContainer/FPSLabel")
|
||||
|
||||
[node name="Overlay" type="Control" parent="PlayerHUD"]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
mouse_filter = 1
|
||||
|
||||
[node name="CenterContainer" type="CenterContainer" parent="PlayerHUD/Overlay"]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="TextureRect" type="TextureRect" parent="PlayerHUD/Overlay/CenterContainer"]
|
||||
material = SubResource("ShaderMaterial_wmxrr")
|
||||
layout_mode = 2
|
||||
texture = ExtResource("4_3uwxe")
|
||||
|
||||
[node name="Debug" type="Control" parent="PlayerHUD"]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
mouse_filter = 1
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="PlayerHUD/Debug"]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
alignment = 2
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="PlayerHUD/Debug/HBoxContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="PlayerHUD/Debug/HBoxContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/margin_top = 5
|
||||
theme_override_constants/margin_right = 5
|
||||
|
||||
[node name="FPSLabel" type="Label" parent="PlayerHUD/Debug/HBoxContainer/VBoxContainer/MarginContainer"]
|
||||
material = SubResource("ShaderMaterial_0n7pd")
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 35
|
||||
text = "999 FPS"
|
||||
horizontal_alignment = 2
|
||||
vertical_alignment = 1
|
@ -1,202 +0,0 @@
|
||||
class_name Player extends CharacterBody3D
|
||||
|
||||
var player_tag: Label3D
|
||||
var username: String = ""
|
||||
var is_piloting: bool = false
|
||||
|
||||
var is_network_authority: bool = false
|
||||
var network_uuid: String = ""
|
||||
var steam_id: int = 0
|
||||
|
||||
@export_category("Player")
|
||||
@export_range(1, 35, 1) var speed: float = 5.0
|
||||
@export_range(10, 400, 1) var acceleration: float = 100.0
|
||||
|
||||
@export_range(0.1, 3.0, 0.1) var jump_height: float = 1.0
|
||||
@export_range(0.1, 3.0, 0.1, "or_greater") var camera_sens: float = 1.0
|
||||
@export_range(0.1, 3.0, 0.1, "or_greater") var joystick_camera_sens_multiplier: float = 5.0
|
||||
@export var camera: Camera3D
|
||||
@export var neck: Node3D
|
||||
@export var player_avatar_face: Sprite3D
|
||||
@export var body: MeshInstance3D
|
||||
@export var head: MeshInstance3D
|
||||
@export var player_hud: CanvasLayer
|
||||
|
||||
|
||||
var jumping: bool = false
|
||||
var is_using_joystick: bool = false
|
||||
var gravity: float = ProjectSettings.get_setting("physics/3d/default_gravity")
|
||||
|
||||
var move_dir: Vector2 # Input direction for movement
|
||||
var look_dir: Vector2 # Input direction for look/aim
|
||||
var walk_vel: Vector3 # Walking velocity
|
||||
var grav_vel: Vector3 # Gravity velocity
|
||||
var jump_vel: Vector3 # Jumping velocity
|
||||
|
||||
var current_ship: Ship
|
||||
|
||||
var previous_global_position: Vector3
|
||||
var previous_global_rotation: Vector3
|
||||
var previous_head_vert_rotation: Vector3
|
||||
|
||||
var neck_rotation_sync: Vector3
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
NetworkManager.property_update_received.connect(_on_property_update)
|
||||
network_uuid = NetworkManager.register_node(self)
|
||||
|
||||
player_tag = get_node("PlayerTag")
|
||||
player_tag.text = username
|
||||
player_avatar_face.texture = await NetworkManager.get_player_avatar(steam_id)
|
||||
|
||||
if is_network_authority:
|
||||
camera.make_current()
|
||||
body.hide()
|
||||
head.hide()
|
||||
player_avatar_face.hide()
|
||||
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
|
||||
else:
|
||||
player_hud.hide()
|
||||
|
||||
|
||||
func _input(event):
|
||||
if !is_network_authority: return
|
||||
if event.is_action_pressed("esc") and Input.mouse_mode == Input.MOUSE_MODE_CAPTURED:
|
||||
release_mouse()
|
||||
elif event.is_action_pressed("esc") and not Input.mouse_mode == Input.MOUSE_MODE_CAPTURED:
|
||||
capture_mouse()
|
||||
|
||||
if Input.mouse_mode == Input.MOUSE_MODE_CAPTURED:
|
||||
if event is InputEventMouseMotion:
|
||||
is_using_joystick = false
|
||||
look_dir = event.relative * 0.001
|
||||
_rotate_camera()
|
||||
elif event is InputEventJoypadMotion:
|
||||
is_using_joystick = true
|
||||
|
||||
if Input.is_action_just_pressed("jump") and !is_piloting:
|
||||
jumping = true
|
||||
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
if !is_network_authority: return
|
||||
if is_piloting and current_ship != null:
|
||||
velocity = Vector3.ZERO + _gravity(delta)
|
||||
else:
|
||||
velocity = walk(delta) + _gravity(delta) + _jump(delta)
|
||||
|
||||
global_rotation.x = 0.0
|
||||
global_rotation.z = 0.0
|
||||
|
||||
if Input.mouse_mode == Input.MOUSE_MODE_CAPTURED:
|
||||
_handle_joypad_camera_rotation(delta)
|
||||
move_and_slide()
|
||||
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
if !is_network_authority: return
|
||||
|
||||
# If the global_position has changed, notify the NetworkManager
|
||||
if previous_global_position != global_position:
|
||||
NetworkManager.sync_property_to_all(network_uuid, "global_position", global_position)
|
||||
previous_global_position = global_position
|
||||
|
||||
if previous_global_rotation != global_rotation:
|
||||
NetworkManager.sync_property_to_all(network_uuid, "global_rotation", global_rotation)
|
||||
previous_global_rotation = global_rotation
|
||||
|
||||
if previous_head_vert_rotation != neck.rotation:
|
||||
NetworkManager.sync_property_to_all(network_uuid, "neck_rotation_sync", neck_rotation_sync)
|
||||
previous_global_rotation = neck.rotation
|
||||
|
||||
|
||||
func capture_mouse() -> void:
|
||||
if !is_network_authority: return
|
||||
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
|
||||
|
||||
|
||||
func release_mouse() -> void:
|
||||
if !is_network_authority: return
|
||||
Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
|
||||
|
||||
|
||||
func _rotate_camera(sens_mod: float = 1.0) -> void:
|
||||
if !is_network_authority: return
|
||||
var camera_sens_final = camera_sens
|
||||
if is_using_joystick:
|
||||
camera_sens_final = camera_sens * joystick_camera_sens_multiplier
|
||||
|
||||
rotation.y -= look_dir.x * camera_sens_final * sens_mod
|
||||
neck.rotation.x = clamp(neck.rotation.x - look_dir.y * camera_sens_final * sens_mod, -1.5, 1.5)
|
||||
neck_rotation_sync = neck.rotation
|
||||
|
||||
|
||||
func _handle_joypad_camera_rotation(delta: float, sens_mod: float = 1.0) -> void:
|
||||
var joypad_dir: Vector2 = Input.get_vector("look_left","look_right","look_up","look_down")
|
||||
if joypad_dir.length() > 0:
|
||||
look_dir += joypad_dir * delta
|
||||
_rotate_camera(sens_mod)
|
||||
look_dir = Vector2.ZERO
|
||||
|
||||
|
||||
func walk(delta: float) -> Vector3:
|
||||
if Input.mouse_mode == Input.MOUSE_MODE_CAPTURED:
|
||||
move_dir = Input.get_vector("move_left", "move_right", "move_forwards", "move_backwards")
|
||||
var _forward: Vector3 = neck.global_transform.basis * Vector3(move_dir.x, 0, move_dir.y)
|
||||
var walk_dir: Vector3 = Vector3(_forward.x, 0, _forward.z).normalized()
|
||||
walk_vel = walk_vel.move_toward(walk_dir * speed * move_dir.length(), acceleration * delta)
|
||||
return walk_vel
|
||||
|
||||
|
||||
func _gravity(delta: float) -> Vector3:
|
||||
grav_vel = Vector3.ZERO if is_on_floor() else grav_vel.move_toward(Vector3(0, velocity.y - gravity, 0), gravity * delta)
|
||||
return grav_vel
|
||||
|
||||
|
||||
func _jump(delta: float) -> Vector3:
|
||||
if jumping:
|
||||
if is_on_floor(): jump_vel = Vector3(0, sqrt(4 * jump_height * gravity), 0)
|
||||
jumping = false
|
||||
return jump_vel
|
||||
jump_vel = Vector3.ZERO if is_on_floor() else jump_vel.move_toward(Vector3.ZERO, gravity * delta)
|
||||
return jump_vel
|
||||
|
||||
|
||||
func player_entered_ship(ship_global_position: Vector3, ship: Ship):
|
||||
if !is_network_authority: return
|
||||
current_ship = ship
|
||||
print(ship.ship_id)
|
||||
print(global_position)
|
||||
|
||||
|
||||
func player_exited_ship(ship_global_position: Vector3, ship: Ship):
|
||||
if !is_network_authority: return
|
||||
current_ship = null
|
||||
print(ship.ship_id)
|
||||
print(global_position)
|
||||
|
||||
|
||||
func set_is_piloting(state: bool):
|
||||
if !is_network_authority: return
|
||||
is_piloting = state
|
||||
print("player is piloting: ", str(is_piloting))
|
||||
|
||||
|
||||
func ship_entered(_ship: Ship):
|
||||
current_ship = _ship
|
||||
|
||||
|
||||
func ship_exited():
|
||||
current_ship = null
|
||||
|
||||
|
||||
func _on_property_update(node_id: String, property_name: String, value: Variant) -> void:
|
||||
if NetworkManager.node_map.has(node_id):
|
||||
if property_name == "neck_rotation_sync" and !is_network_authority:
|
||||
neck.rotation = value
|
||||
else:
|
||||
var node = NetworkManager.node_map[node_id]
|
||||
node.set(property_name, value)
|
||||
else:
|
||||
printerr("Received property update but node_id is wrong? Expected " + str(network_uuid) + " but got " + str(node_id))
|
@ -1,7 +0,0 @@
|
||||
extends CanvasLayer
|
||||
|
||||
|
||||
@export var fps_label: Label
|
||||
|
||||
func _process(delta):
|
||||
fps_label.text = str(Engine.get_frames_per_second()) + " FPS"
|
@ -1,16 +0,0 @@
|
||||
extends RayCast3D
|
||||
|
||||
@export var player: Player
|
||||
|
||||
var current_interactable: Interactable
|
||||
var has_processed_interact_frame: bool = false
|
||||
|
||||
func _process(delta):
|
||||
if is_colliding() and get_collider() is Interactable:
|
||||
if Input.is_action_just_pressed("interact") and !has_processed_interact_frame:
|
||||
current_interactable = get_collider() as Interactable
|
||||
GameConsole.log_debug("interacted with interactable: " + current_interactable.name)
|
||||
current_interactable.player_reference = player
|
||||
current_interactable.interact()
|
||||
has_processed_interact_frame = true
|
||||
has_processed_interact_frame = false
|
@ -1,168 +0,0 @@
|
||||
class_name Ship
|
||||
|
||||
extends RigidBody3D
|
||||
|
||||
@export var player_detection_area: Area3D
|
||||
@export var helm_location_marker: Marker3D
|
||||
@export var base_turn_speed: float = 50.0
|
||||
@export var max_turn_speed: float = 50.0
|
||||
@export var base_lift_speed: float = 50.0
|
||||
@export var top_speed: float = 50.0
|
||||
@export var move_speed: float = 50.0
|
||||
@export var acceleration: float = 5.0 # Increased acceleration for smoother interpolation
|
||||
@export var interpolation_damp: float = 0.1 # Damping for smoother rotation
|
||||
|
||||
var piloting_player: Player = null
|
||||
var ship_is_piloted: bool = false
|
||||
var target_position: Vector3 = Vector3.ZERO
|
||||
var target_rotation: Quaternion = Quaternion()
|
||||
var predicted_position: Vector3
|
||||
var predicted_rotation: Quaternion
|
||||
var last_input_time: float = 0.0
|
||||
|
||||
var network_uuid: String = ""
|
||||
|
||||
func _ready():
|
||||
network_uuid = NetworkManager.register_node(self)
|
||||
NetworkManager.property_update_received.connect(_on_property_update)
|
||||
|
||||
# Preserve current position and rotation at the start
|
||||
target_position = global_position
|
||||
target_rotation = global_transform.basis.get_rotation_quaternion()
|
||||
predicted_position = global_position
|
||||
predicted_rotation = global_transform.basis.get_rotation_quaternion()
|
||||
|
||||
player_detection_area.body_entered.connect(_on_player_entered)
|
||||
player_detection_area.body_exited.connect(_on_player_exited)
|
||||
|
||||
func _process(delta: float):
|
||||
if ship_is_piloted:
|
||||
if piloting_player.steam_id == NetworkManager.steam_id:
|
||||
# This player is piloting
|
||||
handle_input(delta)
|
||||
send_network_update()
|
||||
else:
|
||||
# Another player is piloting, interpolate
|
||||
interpolate_position_and_rotation(delta)
|
||||
else:
|
||||
# No one is piloting, just interpolate
|
||||
interpolate_position_and_rotation(delta)
|
||||
|
||||
func handle_input(delta: float):
|
||||
var forward_input = 0.0
|
||||
var turn_input = 0.0
|
||||
var vertical_input = 0.0
|
||||
|
||||
# Forward and backward movement
|
||||
if Input.is_action_pressed("move_forwards"):
|
||||
forward_input += 1
|
||||
if Input.is_action_pressed("move_backwards"):
|
||||
forward_input -= 1
|
||||
|
||||
# Left and right turning
|
||||
if Input.is_action_pressed("move_left"):
|
||||
turn_input += 1
|
||||
if Input.is_action_pressed("move_right"):
|
||||
turn_input -= 1
|
||||
|
||||
# Up and down movement
|
||||
if Input.is_action_pressed("jump"):
|
||||
vertical_input += 1
|
||||
if Input.is_action_pressed("crouch"):
|
||||
vertical_input -= 1
|
||||
|
||||
# Apply torque for turning
|
||||
if turn_input != 0:
|
||||
var torque = Vector3.UP * turn_input * base_turn_speed
|
||||
apply_torque(torque)
|
||||
|
||||
# Clamp the angular velocity to the maximum turn speed
|
||||
if angular_velocity.length() > max_turn_speed:
|
||||
angular_velocity = angular_velocity.normalized() * max_turn_speed
|
||||
|
||||
# Apply force for forward and backward movement
|
||||
if forward_input != 0:
|
||||
var forward_direction = -global_transform.basis.z.normalized()
|
||||
var forward_force = forward_direction * forward_input * move_speed
|
||||
apply_central_force(forward_force)
|
||||
|
||||
# Apply force for vertical movement
|
||||
if vertical_input != 0:
|
||||
var vertical_force = Vector3.UP * vertical_input * base_lift_speed
|
||||
apply_central_force(vertical_force)
|
||||
|
||||
# Update predicted state
|
||||
predicted_position = predict_position(delta)
|
||||
predicted_rotation = predict_rotation(delta)
|
||||
last_input_time = Time.get_ticks_msec()
|
||||
|
||||
func predict_position(delta: float):
|
||||
var velocity = linear_velocity
|
||||
return global_position + velocity * delta
|
||||
|
||||
func predict_rotation(delta: float):
|
||||
var angular_velocity_quat = Quaternion(Vector3.UP, angular_velocity.y * delta)
|
||||
return global_transform.basis.get_rotation_quaternion() * angular_velocity_quat
|
||||
|
||||
func interpolate_position_and_rotation(delta: float):
|
||||
# Smooth interpolation with damping
|
||||
global_position = lerp(global_position, target_position, 1.0 - exp(-acceleration * delta))
|
||||
global_transform.basis = Basis(slerp_damp(global_transform.basis.get_rotation_quaternion(), target_rotation, interpolation_damp, 1.0))
|
||||
|
||||
func slerp_damp(from: Quaternion, to: Quaternion, damping: float, dt: float):
|
||||
var t = 1.0 - exp(-damping * dt)
|
||||
return from.slerp(to, t)
|
||||
|
||||
func send_network_update():
|
||||
if NetworkManager.is_host and ship_is_piloted and piloting_player.steam_id == NetworkManager.steam_id:
|
||||
# Host is piloting, synchronize with clients
|
||||
NetworkManager.sync_property_to_all_except_host(network_uuid, "global_position", predicted_position)
|
||||
NetworkManager.sync_property_to_all_except_host(network_uuid, "global_rotation", predicted_rotation)
|
||||
|
||||
elif !NetworkManager.is_host and ship_is_piloted and piloting_player.steam_id == NetworkManager.steam_id:
|
||||
# Client is piloting, send updates to the host
|
||||
NetworkManager.sync_property_to_all_except_host(network_uuid, "global_position", predicted_position)
|
||||
NetworkManager.sync_property_to_all_except_host(network_uuid, "global_rotation", predicted_rotation)
|
||||
|
||||
func receive_global_position_update(value: Vector3):
|
||||
target_position = value
|
||||
|
||||
func receive_global_rotation_update(value: Quaternion):
|
||||
target_rotation = value
|
||||
|
||||
func _on_player_entered(body):
|
||||
if body is Player and body.get_parent() != self:
|
||||
if !body.is_network_authority: return
|
||||
body.call_deferred("reparent", self, true)
|
||||
body.ship_entered(self)
|
||||
print("Player entered the ship area.")
|
||||
|
||||
func _on_player_exited(body):
|
||||
if body is Player and body.get_parent() != get_node("/root/DevLevel/"):
|
||||
if !body.is_network_authority: return
|
||||
body.call_deferred("reparent", get_node("/root/DevLevel/"), true)
|
||||
body.ship_exited()
|
||||
print("Player exited the ship area.")
|
||||
|
||||
func set_piloting_player(player: Player):
|
||||
piloting_player = player
|
||||
ship_is_piloted = piloting_player != null
|
||||
if piloting_player:
|
||||
print("Player", piloting_player.username, "is now piloting the ship.")
|
||||
else:
|
||||
print("The ship is no longer piloted.")
|
||||
|
||||
func remove_piloting_player():
|
||||
piloting_player = null
|
||||
ship_is_piloted = false
|
||||
print("The ship is no longer piloted.")
|
||||
|
||||
func _on_property_update(node_id: String, property_name: String, value: Variant) -> void:
|
||||
if NetworkManager.node_map.has(node_id) and node_id == network_uuid:
|
||||
if property_name == "global_position":
|
||||
receive_global_position_update(value)
|
||||
elif property_name == "global_rotation":
|
||||
receive_global_rotation_update(value)
|
||||
else:
|
||||
var node = NetworkManager.node_map[node_id]
|
||||
node.set(property_name, value)
|
@ -1,8 +1,6 @@
|
||||
[gd_scene load_steps=8 format=3 uid="uid://y0xb2vktsr1k"]
|
||||
[gd_scene load_steps=6 format=3 uid="uid://y0xb2vktsr1k"]
|
||||
|
||||
[ext_resource type="Script" path="res://assets/core/ships/ship_script.gd" id="1_ghft7"]
|
||||
[ext_resource type="Texture2D" uid="uid://gymb0tju4y67" path="res://addons/kennysprototypetextures/Dark/texture_black (1).png" id="2_3koq8"]
|
||||
[ext_resource type="PackedScene" uid="uid://dm31ddavxv5gt" path="res://assets/core/interactables/ship-helm/ship-helm.tscn" id="3_eyiku"]
|
||||
|
||||
[sub_resource type="BoxShape3D" id="BoxShape3D_6gbwt"]
|
||||
size = Vector3(5, 0.15, 20)
|
||||
@ -17,15 +15,11 @@ size = Vector3(5, 0.15, 20)
|
||||
[sub_resource type="BoxShape3D" id="BoxShape3D_7o08p"]
|
||||
size = Vector3(4.99609, 3.11252, 20.0022)
|
||||
|
||||
[node name="ShuttleClass" type="RigidBody3D" node_paths=PackedStringArray("player_detection_area", "helm_location_marker")]
|
||||
[node name="ShuttleClass" type="RigidBody3D"]
|
||||
collision_layer = 8
|
||||
collision_mask = 200
|
||||
mass = 700.0
|
||||
gravity_scale = 0.0
|
||||
script = ExtResource("1_ghft7")
|
||||
player_detection_area = NodePath("PlayerDetectionArea")
|
||||
helm_location_marker = NodePath("HelmLocationMarker")
|
||||
base_lift_speed = 5.0
|
||||
|
||||
[node name="ShipToShipCol" type="CollisionShape3D" parent="."]
|
||||
shape = SubResource("BoxShape3D_6gbwt")
|
||||
@ -50,7 +44,3 @@ shape = SubResource("BoxShape3D_7o08p")
|
||||
|
||||
[node name="HelmLocationMarker" type="Marker3D" parent="."]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.1, 0)
|
||||
|
||||
[node name="ShipHelm" parent="." node_paths=PackedStringArray("parent_ship") instance=ExtResource("3_eyiku")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -7.54699)
|
||||
parent_ship = NodePath("..")
|
||||
|
@ -1,71 +0,0 @@
|
||||
|
||||
extends RigidBody3D
|
||||
|
||||
@export var ship_area: Area3D
|
||||
@export var ship_helm: ShipHelm
|
||||
|
||||
var ship_id: int = 0
|
||||
var ship_is_piloted: bool = false
|
||||
var base_turn_speed: float = 1
|
||||
var top_turn_speed: float = 50.0
|
||||
var ship_lift_speed: float = 20 # Adjust this value as needed
|
||||
var acceleration: float = 50
|
||||
var deceleration: float = acceleration * 0.5
|
||||
var ship_top_speed: float = 100
|
||||
var current_speed: float = 0.0
|
||||
var max_force: float = 1000.0
|
||||
|
||||
var piloting_player: Player = null
|
||||
|
||||
|
||||
func _ready():
|
||||
ship_area.body_entered.connect(_on_area_3d_body_entered)
|
||||
ship_area.body_exited.connect(_on_area_3d_body_exited)
|
||||
ship_id = randi_range(1000, 9999)
|
||||
GameConsole.log_debug("Ship ID: " + str(ship_id))
|
||||
|
||||
|
||||
func _physics_process(delta):
|
||||
global_rotation.x = lerpf(global_rotation.x, 0, 0.5)
|
||||
global_rotation.z = lerpf(global_rotation.z, 0, 0.5)
|
||||
|
||||
if ship_is_piloted:
|
||||
var turn_speed = base_turn_speed / max(mass, 1) # changes speeds based on how THICC the ship is
|
||||
if Input.is_action_pressed("move_left"):
|
||||
angular_velocity.y += turn_speed
|
||||
elif Input.is_action_pressed("move_right"):
|
||||
angular_velocity.y -= turn_speed
|
||||
|
||||
if Input.is_action_pressed("jump"):
|
||||
# Apply an impulse to achieve instantaneous upward velocity
|
||||
apply_impulse(Vector3.UP * ship_lift_speed)
|
||||
|
||||
if Input.is_action_pressed("move_forwards"):
|
||||
current_speed = min(current_speed + acceleration * delta, ship_top_speed)
|
||||
else:
|
||||
current_speed = max(current_speed - deceleration * delta, 0.0)
|
||||
|
||||
var desired_velocity = -transform.basis.z * current_speed
|
||||
var forward_force = (desired_velocity - linear_velocity) * mass
|
||||
forward_force = forward_force.normalized() * min(forward_force.length(), max_force)
|
||||
apply_central_force(forward_force)
|
||||
|
||||
if Input.is_action_pressed("crouch"):
|
||||
apply_impulse(Vector3.DOWN * ship_lift_speed)
|
||||
|
||||
angular_velocity.y = clamp(angular_velocity.y, -top_turn_speed, top_turn_speed)
|
||||
|
||||
|
||||
func _on_area_3d_body_entered(body):
|
||||
if body is Player:
|
||||
piloting_player = body
|
||||
body.player_entered_ship(global_position, self)
|
||||
body.set_block_signals(true)
|
||||
|
||||
|
||||
func _on_area_3d_body_exited(body):
|
||||
if body is Player:
|
||||
ship_helm.is_being_piloted = false
|
||||
body.player_exited_ship(global_position, self)
|
||||
piloting_player = null
|
||||
body.set_block_signals(false)
|
176
godotsteam_sync_example/Test.tscn
Normal file
@ -0,0 +1,176 @@
|
||||
[gd_scene load_steps=13 format=3 uid="uid://dhi5myb4u2mij"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/godot_steam_sync/Network/RadomeSteamSync/NetworkPlayerSpawner.gd" id="1_a26tm"]
|
||||
[ext_resource type="Script" path="res://addons/godot_steam_sync/Network/RadomeSteamSync/SyncNode/RigidBodySync3D.gd" id="2_io2do"]
|
||||
|
||||
[sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_ilft6"]
|
||||
sky_horizon_color = Color(0.64625, 0.65575, 0.67075, 1)
|
||||
ground_horizon_color = Color(0.64625, 0.65575, 0.67075, 1)
|
||||
|
||||
[sub_resource type="Sky" id="Sky_ryjhm"]
|
||||
sky_material = SubResource("ProceduralSkyMaterial_ilft6")
|
||||
|
||||
[sub_resource type="Environment" id="Environment_lmp12"]
|
||||
background_mode = 2
|
||||
sky = SubResource("Sky_ryjhm")
|
||||
tonemap_mode = 2
|
||||
glow_enabled = true
|
||||
|
||||
[sub_resource type="BoxMesh" id="BoxMesh_li3io"]
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_r06tq"]
|
||||
albedo_color = Color(0.321728, 0.321728, 0.321728, 1)
|
||||
|
||||
[sub_resource type="ConcavePolygonShape3D" id="ConcavePolygonShape3D_io4rt"]
|
||||
data = PackedVector3Array(-0.5, 0.5, 0.5, 0.5, 0.5, 0.5, -0.5, -0.5, 0.5, 0.5, 0.5, 0.5, 0.5, -0.5, 0.5, -0.5, -0.5, 0.5, 0.5, 0.5, -0.5, -0.5, 0.5, -0.5, 0.5, -0.5, -0.5, -0.5, 0.5, -0.5, -0.5, -0.5, -0.5, 0.5, -0.5, -0.5, 0.5, 0.5, 0.5, 0.5, 0.5, -0.5, 0.5, -0.5, 0.5, 0.5, 0.5, -0.5, 0.5, -0.5, -0.5, 0.5, -0.5, 0.5, -0.5, 0.5, -0.5, -0.5, 0.5, 0.5, -0.5, -0.5, -0.5, -0.5, 0.5, 0.5, -0.5, -0.5, 0.5, -0.5, -0.5, -0.5, 0.5, 0.5, 0.5, -0.5, 0.5, 0.5, 0.5, 0.5, -0.5, -0.5, 0.5, 0.5, -0.5, 0.5, -0.5, 0.5, 0.5, -0.5, -0.5, -0.5, 0.5, 0.5, -0.5, 0.5, -0.5, -0.5, -0.5, 0.5, -0.5, 0.5, 0.5, -0.5, -0.5, -0.5, -0.5, -0.5)
|
||||
|
||||
[sub_resource type="PhysicsMaterial" id="PhysicsMaterial_iltko"]
|
||||
bounce = 1.0
|
||||
|
||||
[sub_resource type="SphereMesh" id="SphereMesh_jxxnq"]
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_cdfuw"]
|
||||
albedo_color = Color(0.415686, 1, 1, 1)
|
||||
|
||||
[sub_resource type="SphereShape3D" id="SphereShape3D_ktxia"]
|
||||
|
||||
[node name="Test" type="Node"]
|
||||
|
||||
[node name="NetworkPlayerSpawner" type="Node" parent="." node_paths=PackedStringArray("spawn_pos", "players_parent_node")]
|
||||
script = ExtResource("1_a26tm")
|
||||
spawn_pos = NodePath("../SpawnPos")
|
||||
players_parent_node = NodePath("../Players")
|
||||
|
||||
[node name="WorldEnvironment" type="WorldEnvironment" parent="."]
|
||||
environment = SubResource("Environment_lmp12")
|
||||
|
||||
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."]
|
||||
transform = Transform3D(-0.866023, -0.433016, 0.250001, 0, 0.499998, 0.866027, -0.500003, 0.749999, -0.43301, 0, 0, 0)
|
||||
shadow_enabled = true
|
||||
|
||||
[node name="Players" type="Node3D" parent="."]
|
||||
|
||||
[node name="SpawnPos" type="Marker3D" parent="."]
|
||||
|
||||
[node name="Level" type="Node3D" parent="."]
|
||||
|
||||
[node name="MeshInstance3D" type="MeshInstance3D" parent="Level"]
|
||||
transform = Transform3D(49.9588, 0, 0, 0, 1, 0, 0, 0, 61.4249, 0, -3.98574, 0)
|
||||
mesh = SubResource("BoxMesh_li3io")
|
||||
skeleton = NodePath("../..")
|
||||
surface_material_override/0 = SubResource("StandardMaterial3D_r06tq")
|
||||
|
||||
[node name="StaticBody3D" type="StaticBody3D" parent="Level/MeshInstance3D"]
|
||||
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="Level/MeshInstance3D/StaticBody3D"]
|
||||
shape = SubResource("ConcavePolygonShape3D_io4rt")
|
||||
|
||||
[node name="MeshInstance3D6" type="MeshInstance3D" parent="Level"]
|
||||
transform = Transform3D(-0.506593, 0.999949, 0, -49.9562, -0.0101402, 0, 0, 0, 61.4249, -18.7014, -3.98574, 0)
|
||||
mesh = SubResource("BoxMesh_li3io")
|
||||
skeleton = NodePath("../..")
|
||||
|
||||
[node name="StaticBody3D" type="StaticBody3D" parent="Level/MeshInstance3D6"]
|
||||
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="Level/MeshInstance3D6/StaticBody3D"]
|
||||
shape = SubResource("ConcavePolygonShape3D_io4rt")
|
||||
|
||||
[node name="MeshInstance3D8" type="MeshInstance3D" parent="Level"]
|
||||
transform = Transform3D(-0.0344473, 0.0679944, 61.2827, -49.9562, -0.0101402, 0, 0.505421, -0.997635, 4.17676, -0.748235, -3.98574, -26.874)
|
||||
mesh = SubResource("BoxMesh_li3io")
|
||||
skeleton = NodePath("../..")
|
||||
|
||||
[node name="StaticBody3D" type="StaticBody3D" parent="Level/MeshInstance3D8"]
|
||||
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="Level/MeshInstance3D8/StaticBody3D"]
|
||||
shape = SubResource("ConcavePolygonShape3D_io4rt")
|
||||
|
||||
[node name="MeshInstance3D9" type="MeshInstance3D" parent="Level"]
|
||||
transform = Transform3D(-0.0344473, 0.0679944, 61.2827, -49.9562, -0.0101402, 0, 0.505421, -0.997635, 4.17676, -6.63549, -3.98574, 19.7729)
|
||||
mesh = SubResource("BoxMesh_li3io")
|
||||
skeleton = NodePath("../..")
|
||||
|
||||
[node name="StaticBody3D" type="StaticBody3D" parent="Level/MeshInstance3D9"]
|
||||
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="Level/MeshInstance3D9/StaticBody3D"]
|
||||
shape = SubResource("ConcavePolygonShape3D_io4rt")
|
||||
|
||||
[node name="MeshInstance3D7" type="MeshInstance3D" parent="Level"]
|
||||
transform = Transform3D(-0.506593, 0.999949, 0, -49.9562, -0.0101402, 0, 0, 0, 61.4249, 12.2278, -3.98574, 0)
|
||||
mesh = SubResource("BoxMesh_li3io")
|
||||
skeleton = NodePath("../..")
|
||||
|
||||
[node name="StaticBody3D" type="StaticBody3D" parent="Level/MeshInstance3D7"]
|
||||
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="Level/MeshInstance3D7/StaticBody3D"]
|
||||
shape = SubResource("ConcavePolygonShape3D_io4rt")
|
||||
|
||||
[node name="Physics" type="Node3D" parent="."]
|
||||
|
||||
[node name="RigidBody3D" type="RigidBody3D" parent="Physics"]
|
||||
physics_interpolation_mode = 1
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.20984, 0)
|
||||
collision_layer = 4
|
||||
collision_mask = 7
|
||||
physics_material_override = SubResource("PhysicsMaterial_iltko")
|
||||
|
||||
[node name="MeshInstance3D" type="MeshInstance3D" parent="Physics/RigidBody3D"]
|
||||
mesh = SubResource("SphereMesh_jxxnq")
|
||||
surface_material_override/0 = SubResource("StandardMaterial3D_cdfuw")
|
||||
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="Physics/RigidBody3D"]
|
||||
shape = SubResource("SphereShape3D_ktxia")
|
||||
|
||||
[node name="RigidBodySync3D" type="Node" parent="Physics/RigidBody3D"]
|
||||
script = ExtResource("2_io2do")
|
||||
|
||||
[node name="RigidBody3D2" type="RigidBody3D" parent="Physics"]
|
||||
physics_interpolation_mode = 1
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -3.5851, 1.20984, -2.08695)
|
||||
collision_layer = 4
|
||||
collision_mask = 7
|
||||
physics_material_override = SubResource("PhysicsMaterial_iltko")
|
||||
|
||||
[node name="MeshInstance3D" type="MeshInstance3D" parent="Physics/RigidBody3D2"]
|
||||
mesh = SubResource("SphereMesh_jxxnq")
|
||||
surface_material_override/0 = SubResource("StandardMaterial3D_cdfuw")
|
||||
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="Physics/RigidBody3D2"]
|
||||
shape = SubResource("SphereShape3D_ktxia")
|
||||
|
||||
[node name="RigidBodySync3D" type="Node" parent="Physics/RigidBody3D2"]
|
||||
script = ExtResource("2_io2do")
|
||||
|
||||
[node name="RigidBody3D3" type="RigidBody3D" parent="Physics"]
|
||||
physics_interpolation_mode = 1
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.84777, 1.20984, 4.17028)
|
||||
collision_layer = 4
|
||||
collision_mask = 7
|
||||
physics_material_override = SubResource("PhysicsMaterial_iltko")
|
||||
|
||||
[node name="MeshInstance3D" type="MeshInstance3D" parent="Physics/RigidBody3D3"]
|
||||
mesh = SubResource("SphereMesh_jxxnq")
|
||||
surface_material_override/0 = SubResource("StandardMaterial3D_cdfuw")
|
||||
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="Physics/RigidBody3D3"]
|
||||
shape = SubResource("SphereShape3D_ktxia")
|
||||
|
||||
[node name="RigidBodySync3D" type="Node" parent="Physics/RigidBody3D3"]
|
||||
script = ExtResource("2_io2do")
|
||||
|
||||
[node name="RigidBody3D4" type="RigidBody3D" parent="Physics"]
|
||||
physics_interpolation_mode = 1
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.84777, 1.8173, -2.41229)
|
||||
collision_layer = 4
|
||||
collision_mask = 7
|
||||
physics_material_override = SubResource("PhysicsMaterial_iltko")
|
||||
|
||||
[node name="MeshInstance3D" type="MeshInstance3D" parent="Physics/RigidBody3D4"]
|
||||
mesh = SubResource("SphereMesh_jxxnq")
|
||||
surface_material_override/0 = SubResource("StandardMaterial3D_cdfuw")
|
||||
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="Physics/RigidBody3D4"]
|
||||
shape = SubResource("SphereShape3D_ktxia")
|
||||
|
||||
[node name="RigidBodySync3D" type="Node" parent="Physics/RigidBody3D4"]
|
||||
script = ExtResource("2_io2do")
|
49
godotsteam_sync_example/fpc/EditorModule.gd
Normal file
@ -0,0 +1,49 @@
|
||||
@tool
|
||||
extends Node
|
||||
|
||||
# This does not effect runtime yet but will in the future.
|
||||
|
||||
@export_category("Controller Editor Module")
|
||||
@export_range(-360.0, 360.0, 0.01, "or_greater", "or_less") var head_y_rotation : float = 0.0:
|
||||
set(new_rotation):
|
||||
if HEAD:
|
||||
head_y_rotation = new_rotation
|
||||
HEAD.rotation.y = deg_to_rad(head_y_rotation)
|
||||
update_configuration_warnings()
|
||||
@export_range(-90.0, 90.0, 0.01, "or_greater", "or_less") var head_x_rotation : float = 0.0:
|
||||
set(new_rotation):
|
||||
if HEAD:
|
||||
head_x_rotation = new_rotation
|
||||
HEAD.rotation.x = deg_to_rad(head_x_rotation)
|
||||
update_configuration_warnings()
|
||||
|
||||
@export_group("Nodes")
|
||||
@export var CHARACTER : CharacterBody3D
|
||||
@export var head_path : String = "Head" # Relative to the parent node
|
||||
#@export var CAMERA : Camera3D
|
||||
#@export var HEADBOB_ANIMATION : AnimationPlayer
|
||||
#@export var JUMP_ANIMATION : AnimationPlayer
|
||||
#@export var CROUCH_ANIMATION : AnimationPlayer
|
||||
#@export var COLLISION_MESH : CollisionShape3D
|
||||
|
||||
@onready var HEAD = get_node("../" + head_path)
|
||||
|
||||
|
||||
func _ready():
|
||||
if !Engine.is_editor_hint():
|
||||
print("not editor")
|
||||
HEAD.rotation.y = deg_to_rad(head_y_rotation)
|
||||
HEAD.rotation.x = deg_to_rad(head_x_rotation)
|
||||
|
||||
|
||||
func _get_configuration_warnings():
|
||||
var warnings = []
|
||||
|
||||
if head_y_rotation > 360:
|
||||
warnings.append("The head rotation is greater than 360")
|
||||
|
||||
if head_y_rotation < -360:
|
||||
warnings.append("The head rotation is less than -360")
|
||||
|
||||
# Returning an empty array gives no warnings
|
||||
return warnings
|
854
godotsteam_sync_example/fpc/character.tscn
Normal file
@ -0,0 +1,854 @@
|
||||
[gd_scene load_steps=22 format=3 uid="uid://cc1m2a1obsyn4"]
|
||||
|
||||
[ext_resource type="Script" path="res://godotsteam_sync_example/fpc/EditorModule.gd" id="3_v3ckk"]
|
||||
[ext_resource type="Script" path="res://godotsteam_sync_example/fpc/debug.gd" id="3_x1wcc"]
|
||||
[ext_resource type="Script" path="res://addons/godot_steam_sync/Network/RadomeSteamSync/SyncNode/TransformSync3D.gd" id="4_ch187"]
|
||||
|
||||
[sub_resource type="GDScript" id="GDScript_r0lhh"]
|
||||
script/source = "
|
||||
# COPYRIGHT Colormatic Studios
|
||||
# MIT licence
|
||||
# Quality Godot First Person Controller v2
|
||||
|
||||
|
||||
extends CharacterBody3D
|
||||
|
||||
var IS_OWNER : bool = false
|
||||
|
||||
func make_owner():
|
||||
IS_OWNER = true
|
||||
$Head/Camera.current = true
|
||||
|
||||
## The settings for the character's movement and feel.
|
||||
@export_category(\"Character\")
|
||||
## The speed that the character moves at without crouching or sprinting.
|
||||
@export var base_speed : float = 3.0
|
||||
## The speed that the character moves at when sprinting.
|
||||
@export var sprint_speed : float = 6.0
|
||||
## The speed that the character moves at when crouching.
|
||||
@export var crouch_speed : float = 1.0
|
||||
|
||||
## How fast the character speeds up and slows down when Motion Smoothing is on.
|
||||
@export var acceleration : float = 10.0
|
||||
## How high the player jumps.
|
||||
@export var jump_velocity : float = 4.5
|
||||
## How far the player turns when the mouse is moved.
|
||||
@export var mouse_sensitivity : float = 0.1
|
||||
## Invert the Y input for mouse and joystick
|
||||
@export var invert_mouse_y : bool = false # Possibly add an invert mouse X in the future
|
||||
## Wether the player can use movement inputs. Does not stop outside forces or jumping. See Jumping Enabled.
|
||||
@export var immobile : bool = false
|
||||
## The reticle file to import at runtime. By default are in res://addons/fpc/reticles/. Set to an empty string to remove.
|
||||
@export_file var default_reticle
|
||||
|
||||
@export_group(\"Nodes\")
|
||||
## The node that holds the camera. This is rotated instead of the camera for mouse input.
|
||||
@export var HEAD : Node3D
|
||||
@export var CAMERA : Camera3D
|
||||
@export var HEADBOB_ANIMATION : AnimationPlayer
|
||||
@export var JUMP_ANIMATION : AnimationPlayer
|
||||
@export var CROUCH_ANIMATION : AnimationPlayer
|
||||
@export var COLLISION_MESH : CollisionShape3D
|
||||
|
||||
@export_group(\"Controls\")
|
||||
# We are using UI controls because they are built into Godot Engine so they can be used right away
|
||||
@export var JUMP : String = \"ui_accept\"
|
||||
@export var LEFT : String = \"ui_left\"
|
||||
@export var RIGHT : String = \"ui_right\"
|
||||
@export var FORWARD : String = \"ui_up\"
|
||||
@export var BACKWARD : String = \"ui_down\"
|
||||
## By default this does not pause the game, but that can be changed in _process.
|
||||
@export var PAUSE : String = \"ui_cancel\"
|
||||
@export var CROUCH : String = \"crouch\"
|
||||
@export var SPRINT : String = \"sprint\"
|
||||
|
||||
# Uncomment if you want controller support
|
||||
#@export var controller_sensitivity : float = 0.035
|
||||
#@export var LOOK_LEFT : String = \"look_left\"
|
||||
#@export var LOOK_RIGHT : String = \"look_right\"
|
||||
#@export var LOOK_UP : String = \"look_up\"
|
||||
#@export var LOOK_DOWN : String = \"look_down\"
|
||||
|
||||
@export_group(\"Feature Settings\")
|
||||
## Enable or disable jumping. Useful for restrictive storytelling environments.
|
||||
@export var jumping_enabled : bool = true
|
||||
## Wether the player can move in the air or not.
|
||||
@export var in_air_momentum : bool = true
|
||||
## Smooths the feel of walking.
|
||||
@export var motion_smoothing : bool = true
|
||||
@export var sprint_enabled : bool = true
|
||||
@export var crouch_enabled : bool = true
|
||||
@export_enum(\"Hold to Crouch\", \"Toggle Crouch\") var crouch_mode : int = 0
|
||||
@export_enum(\"Hold to Sprint\", \"Toggle Sprint\") var sprint_mode : int = 0
|
||||
## Wether sprinting should effect FOV.
|
||||
@export var dynamic_fov : bool = true
|
||||
## If the player holds down the jump button, should the player keep hopping.
|
||||
@export var continuous_jumping : bool = true
|
||||
## Enables the view bobbing animation.
|
||||
@export var view_bobbing : bool = true
|
||||
## Enables an immersive animation when the player jumps and hits the ground.
|
||||
@export var jump_animation : bool = true
|
||||
## This determines wether the player can use the pause button, not wether the game will actually pause.
|
||||
@export var pausing_enabled : bool = true
|
||||
## Use with caution.
|
||||
@export var gravity_enabled : bool = true
|
||||
|
||||
|
||||
# Member variables
|
||||
var speed : float = base_speed
|
||||
var current_speed : float = 0.0
|
||||
# States: normal, crouching, sprinting
|
||||
var state : String = \"normal\"
|
||||
var low_ceiling : bool = false # This is for when the cieling is too low and the player needs to crouch.
|
||||
var was_on_floor : bool = true # Was the player on the floor last frame (for landing animation)
|
||||
|
||||
# The reticle should always have a Control node as the root
|
||||
var RETICLE : Control
|
||||
|
||||
# Get the gravity from the project settings to be synced with RigidBody nodes
|
||||
var gravity : float = ProjectSettings.get_setting(\"physics/3d/default_gravity\") # Don't set this as a const, see the gravity section in _physics_process
|
||||
|
||||
# Stores mouse input for rotating the camera in the phyhsics process
|
||||
var mouseInput : Vector2 = Vector2(0,0)
|
||||
|
||||
func _ready():
|
||||
if IS_OWNER:
|
||||
#It is safe to comment this line if your game doesn't start with the mouse captured
|
||||
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
|
||||
|
||||
# If the controller is rotated in a certain direction for game design purposes, redirect this rotation into the head.
|
||||
HEAD.rotation.y = rotation.y
|
||||
rotation.y = 0
|
||||
|
||||
if default_reticle:
|
||||
change_reticle(default_reticle)
|
||||
|
||||
# Reset the camera position
|
||||
# If you want to change the default head height, change these animations.
|
||||
HEADBOB_ANIMATION.play(\"RESET\")
|
||||
JUMP_ANIMATION.play(\"RESET\")
|
||||
CROUCH_ANIMATION.play(\"RESET\")
|
||||
|
||||
check_controls()
|
||||
|
||||
func check_controls(): # If you add a control, you might want to add a check for it here.
|
||||
# The actions are being disabled so the engine doesn't halt the entire project in debug mode
|
||||
if IS_OWNER:
|
||||
if !InputMap.has_action(JUMP):
|
||||
push_error(\"No control mapped for jumping. Please add an input map control. Disabling jump.\")
|
||||
jumping_enabled = false
|
||||
if !InputMap.has_action(LEFT):
|
||||
push_error(\"No control mapped for move left. Please add an input map control. Disabling movement.\")
|
||||
immobile = true
|
||||
if !InputMap.has_action(RIGHT):
|
||||
push_error(\"No control mapped for move right. Please add an input map control. Disabling movement.\")
|
||||
immobile = true
|
||||
if !InputMap.has_action(FORWARD):
|
||||
push_error(\"No control mapped for move forward. Please add an input map control. Disabling movement.\")
|
||||
immobile = true
|
||||
if !InputMap.has_action(BACKWARD):
|
||||
push_error(\"No control mapped for move backward. Please add an input map control. Disabling movement.\")
|
||||
immobile = true
|
||||
if !InputMap.has_action(PAUSE):
|
||||
push_error(\"No control mapped for pause. Please add an input map control. Disabling pausing.\")
|
||||
pausing_enabled = false
|
||||
if !InputMap.has_action(CROUCH):
|
||||
push_error(\"No control mapped for crouch. Please add an input map control. Disabling crouching.\")
|
||||
crouch_enabled = false
|
||||
if !InputMap.has_action(SPRINT):
|
||||
push_error(\"No control mapped for sprint. Please add an input map control. Disabling sprinting.\")
|
||||
sprint_enabled = false
|
||||
|
||||
|
||||
func change_reticle(reticle): # Yup, this function is kinda strange
|
||||
if IS_OWNER:
|
||||
if RETICLE:
|
||||
RETICLE.queue_free()
|
||||
|
||||
RETICLE = load(reticle).instantiate()
|
||||
RETICLE.character = self
|
||||
$UserInterface.add_child(RETICLE)
|
||||
|
||||
|
||||
func _physics_process(delta):
|
||||
if IS_OWNER:
|
||||
# Big thanks to github.com/LorenzoAncora for the concept of the improved debug values
|
||||
current_speed = Vector3.ZERO.distance_to(get_real_velocity())
|
||||
$UserInterface/DebugPanel.add_property(\"Speed\", snappedf(current_speed, 0.001), 1)
|
||||
$UserInterface/DebugPanel.add_property(\"Target speed\", speed, 2)
|
||||
var cv : Vector3 = get_real_velocity()
|
||||
var vd : Array[float] = [
|
||||
snappedf(cv.x, 0.001),
|
||||
snappedf(cv.y, 0.001),
|
||||
snappedf(cv.z, 0.001)
|
||||
]
|
||||
var readable_velocity : String = \"X: \" + str(vd[0]) + \" Y: \" + str(vd[1]) + \" Z: \" + str(vd[2])
|
||||
$UserInterface/DebugPanel.add_property(\"Velocity\", readable_velocity, 3)
|
||||
|
||||
# Gravity
|
||||
#gravity = ProjectSettings.get_setting(\"physics/3d/default_gravity\") # If the gravity changes during your game, uncomment this code
|
||||
if not is_on_floor() and gravity and gravity_enabled:
|
||||
velocity.y -= gravity * delta
|
||||
|
||||
handle_jumping()
|
||||
|
||||
var input_dir = Vector2.ZERO
|
||||
if !immobile: # Immobility works by interrupting user input, so other forces can still be applied to the player
|
||||
input_dir = Input.get_vector(LEFT, RIGHT, FORWARD, BACKWARD)
|
||||
handle_movement(delta, input_dir)
|
||||
|
||||
handle_head_rotation()
|
||||
|
||||
# The player is not able to stand up if the ceiling is too low
|
||||
low_ceiling = $CrouchCeilingDetection.is_colliding()
|
||||
|
||||
handle_state(input_dir)
|
||||
if dynamic_fov: # This may be changed to an AnimationPlayer
|
||||
update_camera_fov()
|
||||
|
||||
if view_bobbing:
|
||||
headbob_animation(input_dir)
|
||||
|
||||
if jump_animation:
|
||||
if !was_on_floor and is_on_floor(): # The player just landed
|
||||
match randi() % 2: #TODO: Change this to detecting velocity direction
|
||||
0:
|
||||
JUMP_ANIMATION.play(\"land_left\", 0.25)
|
||||
1:
|
||||
JUMP_ANIMATION.play(\"land_right\", 0.25)
|
||||
|
||||
was_on_floor = is_on_floor() # This must always be at the end of physics_process
|
||||
|
||||
|
||||
func handle_jumping():
|
||||
if IS_OWNER:
|
||||
if jumping_enabled:
|
||||
if continuous_jumping: # Hold down the jump button
|
||||
if Input.is_action_pressed(JUMP) and is_on_floor() and !low_ceiling:
|
||||
if jump_animation:
|
||||
JUMP_ANIMATION.play(\"jump\", 0.25)
|
||||
velocity.y += jump_velocity # Adding instead of setting so jumping on slopes works properly
|
||||
else:
|
||||
if Input.is_action_just_pressed(JUMP) and is_on_floor() and !low_ceiling:
|
||||
if jump_animation:
|
||||
JUMP_ANIMATION.play(\"jump\", 0.25)
|
||||
velocity.y += jump_velocity
|
||||
|
||||
|
||||
func handle_movement(delta, input_dir):
|
||||
if IS_OWNER:
|
||||
var direction = input_dir.rotated(-HEAD.rotation.y)
|
||||
direction = Vector3(direction.x, 0, direction.y)
|
||||
move_and_slide()
|
||||
|
||||
if in_air_momentum:
|
||||
if is_on_floor():
|
||||
if motion_smoothing:
|
||||
velocity.x = lerp(velocity.x, direction.x * speed, acceleration * delta)
|
||||
velocity.z = lerp(velocity.z, direction.z * speed, acceleration * delta)
|
||||
else:
|
||||
velocity.x = direction.x * speed
|
||||
velocity.z = direction.z * speed
|
||||
else:
|
||||
if motion_smoothing:
|
||||
velocity.x = lerp(velocity.x, direction.x * speed, acceleration * delta)
|
||||
velocity.z = lerp(velocity.z, direction.z * speed, acceleration * delta)
|
||||
else:
|
||||
velocity.x = direction.x * speed
|
||||
velocity.z = direction.z * speed
|
||||
|
||||
func handle_head_rotation():
|
||||
if IS_OWNER:
|
||||
HEAD.rotation_degrees.y -= mouseInput.x * mouse_sensitivity
|
||||
if invert_mouse_y:
|
||||
HEAD.rotation_degrees.x -= mouseInput.y * mouse_sensitivity * -1.0
|
||||
else:
|
||||
HEAD.rotation_degrees.x -= mouseInput.y * mouse_sensitivity
|
||||
|
||||
# Uncomment for controller support
|
||||
#var controller_view_rotation = Input.get_vector(LOOK_DOWN, LOOK_UP, LOOK_RIGHT, LOOK_LEFT) * controller_sensitivity # These are inverted because of the nature of 3D rotation.
|
||||
#HEAD.rotation.x += controller_view_rotation.x
|
||||
#if invert_mouse_y:
|
||||
#HEAD.rotation.y += controller_view_rotation.y * -1.0
|
||||
#else:
|
||||
#HEAD.rotation.y += controller_view_rotation.y
|
||||
|
||||
|
||||
mouseInput = Vector2(0,0)
|
||||
HEAD.rotation.x = clamp(HEAD.rotation.x, deg_to_rad(-90), deg_to_rad(90))
|
||||
|
||||
|
||||
func handle_state(moving):
|
||||
if IS_OWNER:
|
||||
if sprint_enabled:
|
||||
if sprint_mode == 0:
|
||||
if Input.is_action_pressed(SPRINT) and state != \"crouching\":
|
||||
if moving:
|
||||
if state != \"sprinting\":
|
||||
enter_sprint_state()
|
||||
else:
|
||||
if state == \"sprinting\":
|
||||
enter_normal_state()
|
||||
elif state == \"sprinting\":
|
||||
enter_normal_state()
|
||||
elif sprint_mode == 1:
|
||||
if moving:
|
||||
# If the player is holding sprint before moving, handle that cenerio
|
||||
if Input.is_action_pressed(SPRINT) and state == \"normal\":
|
||||
enter_sprint_state()
|
||||
if Input.is_action_just_pressed(SPRINT):
|
||||
match state:
|
||||
\"normal\":
|
||||
enter_sprint_state()
|
||||
\"sprinting\":
|
||||
enter_normal_state()
|
||||
elif state == \"sprinting\":
|
||||
enter_normal_state()
|
||||
|
||||
if crouch_enabled:
|
||||
if crouch_mode == 0:
|
||||
if Input.is_action_pressed(CROUCH) and state != \"sprinting\":
|
||||
if state != \"crouching\":
|
||||
enter_crouch_state()
|
||||
elif state == \"crouching\" and !$CrouchCeilingDetection.is_colliding():
|
||||
enter_normal_state()
|
||||
elif crouch_mode == 1:
|
||||
if Input.is_action_just_pressed(CROUCH):
|
||||
match state:
|
||||
\"normal\":
|
||||
enter_crouch_state()
|
||||
\"crouching\":
|
||||
if !$CrouchCeilingDetection.is_colliding():
|
||||
enter_normal_state()
|
||||
|
||||
|
||||
# Any enter state function should only be called once when you want to enter that state, not every frame.
|
||||
|
||||
func enter_normal_state():
|
||||
if IS_OWNER:
|
||||
#print(\"entering normal state\")
|
||||
var prev_state = state
|
||||
if prev_state == \"crouching\":
|
||||
CROUCH_ANIMATION.play_backwards(\"crouch\")
|
||||
state = \"normal\"
|
||||
speed = base_speed
|
||||
|
||||
func enter_crouch_state():
|
||||
if IS_OWNER:
|
||||
#print(\"entering crouch state\")
|
||||
var prev_state = state
|
||||
state = \"crouching\"
|
||||
speed = crouch_speed
|
||||
CROUCH_ANIMATION.play(\"crouch\")
|
||||
|
||||
func enter_sprint_state():
|
||||
if IS_OWNER:
|
||||
#print(\"entering sprint state\")
|
||||
var prev_state = state
|
||||
if prev_state == \"crouching\":
|
||||
CROUCH_ANIMATION.play_backwards(\"crouch\")
|
||||
state = \"sprinting\"
|
||||
speed = sprint_speed
|
||||
|
||||
|
||||
func update_camera_fov():
|
||||
if IS_OWNER:
|
||||
if state == \"sprinting\":
|
||||
CAMERA.fov = lerp(CAMERA.fov, 85.0, 0.3)
|
||||
else:
|
||||
CAMERA.fov = lerp(CAMERA.fov, 75.0, 0.3)
|
||||
|
||||
|
||||
func headbob_animation(moving):
|
||||
if IS_OWNER:
|
||||
if moving and is_on_floor():
|
||||
var use_headbob_animation : String
|
||||
match state:
|
||||
\"normal\",\"crouching\":
|
||||
use_headbob_animation = \"walk\"
|
||||
\"sprinting\":
|
||||
use_headbob_animation = \"sprint\"
|
||||
|
||||
var was_playing : bool = false
|
||||
if HEADBOB_ANIMATION.current_animation == use_headbob_animation:
|
||||
was_playing = true
|
||||
|
||||
HEADBOB_ANIMATION.play(use_headbob_animation, 0.25)
|
||||
HEADBOB_ANIMATION.speed_scale = (current_speed / base_speed) * 1.75
|
||||
if !was_playing:
|
||||
HEADBOB_ANIMATION.seek(float(randi() % 2)) # Randomize the initial headbob direction
|
||||
# Let me explain that piece of code because it looks like it does the opposite of what it actually does.
|
||||
# The headbob animation has two starting positions. One is at 0 and the other is at 1.
|
||||
# randi() % 2 returns either 0 or 1, and so the animation randomly starts at one of the starting positions.
|
||||
# This code is extremely performant but it makes no sense.
|
||||
|
||||
else:
|
||||
if HEADBOB_ANIMATION.current_animation == \"sprint\" or HEADBOB_ANIMATION.current_animation == \"walk\":
|
||||
HEADBOB_ANIMATION.speed_scale = 1
|
||||
HEADBOB_ANIMATION.play(\"RESET\", 1)
|
||||
|
||||
|
||||
func _process(delta):
|
||||
if IS_OWNER:
|
||||
$UserInterface/DebugPanel.add_property(\"FPS\", Performance.get_monitor(Performance.TIME_FPS), 0)
|
||||
var status : String = state
|
||||
if !is_on_floor():
|
||||
status += \" in the air\"
|
||||
$UserInterface/DebugPanel.add_property(\"State\", status, 4)
|
||||
|
||||
if pausing_enabled:
|
||||
if Input.is_action_just_pressed(PAUSE):
|
||||
# You may want another node to handle pausing, because this player may get paused too.
|
||||
match Input.mouse_mode:
|
||||
Input.MOUSE_MODE_CAPTURED:
|
||||
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
|
||||
#get_tree().paused = false
|
||||
Input.MOUSE_MODE_VISIBLE:
|
||||
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
|
||||
#get_tree().paused = false
|
||||
|
||||
|
||||
func _unhandled_input(event : InputEvent):
|
||||
if IS_OWNER:
|
||||
if event is InputEventMouseMotion and Input.mouse_mode == Input.MOUSE_MODE_CAPTURED:
|
||||
mouseInput.x += event.relative.x
|
||||
mouseInput.y += event.relative.y
|
||||
# Toggle debug menu
|
||||
elif event is InputEventKey:
|
||||
if event.is_released():
|
||||
# Where we're going, we don't need InputMap
|
||||
if event.keycode == 4194338: # F7
|
||||
$UserInterface/DebugPanel.visible = !$UserInterface/DebugPanel.visible
|
||||
"
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_kp17n"]
|
||||
albedo_color = Color(0.909804, 0.596078, 0, 1)
|
||||
clearcoat_enabled = true
|
||||
clearcoat_roughness = 0.2
|
||||
|
||||
[sub_resource type="CapsuleMesh" id="CapsuleMesh_jw1de"]
|
||||
material = SubResource("StandardMaterial3D_kp17n")
|
||||
|
||||
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_uy03j"]
|
||||
|
||||
[sub_resource type="Animation" id="Animation_j8cx7"]
|
||||
resource_name = "RESET"
|
||||
length = 0.001
|
||||
tracks/0/type = "value"
|
||||
tracks/0/imported = false
|
||||
tracks/0/enabled = true
|
||||
tracks/0/path = NodePath("Mesh:scale")
|
||||
tracks/0/interp = 2
|
||||
tracks/0/loop_wrap = true
|
||||
tracks/0/keys = {
|
||||
"times": PackedFloat32Array(0),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"update": 0,
|
||||
"values": [Vector3(1, 1, 1)]
|
||||
}
|
||||
tracks/1/type = "value"
|
||||
tracks/1/imported = false
|
||||
tracks/1/enabled = true
|
||||
tracks/1/path = NodePath("Collision:scale")
|
||||
tracks/1/interp = 2
|
||||
tracks/1/loop_wrap = true
|
||||
tracks/1/keys = {
|
||||
"times": PackedFloat32Array(0),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"update": 0,
|
||||
"values": [Vector3(1, 1, 1)]
|
||||
}
|
||||
tracks/2/type = "value"
|
||||
tracks/2/imported = false
|
||||
tracks/2/enabled = true
|
||||
tracks/2/path = NodePath("Mesh:position")
|
||||
tracks/2/interp = 2
|
||||
tracks/2/loop_wrap = true
|
||||
tracks/2/keys = {
|
||||
"times": PackedFloat32Array(0),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"update": 0,
|
||||
"values": [Vector3(0, 1, 0)]
|
||||
}
|
||||
tracks/3/type = "value"
|
||||
tracks/3/imported = false
|
||||
tracks/3/enabled = true
|
||||
tracks/3/path = NodePath("Collision:position")
|
||||
tracks/3/interp = 2
|
||||
tracks/3/loop_wrap = true
|
||||
tracks/3/keys = {
|
||||
"times": PackedFloat32Array(0),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"update": 0,
|
||||
"values": [Vector3(0, 1, 0)]
|
||||
}
|
||||
tracks/4/type = "value"
|
||||
tracks/4/imported = false
|
||||
tracks/4/enabled = true
|
||||
tracks/4/path = NodePath("Head:position")
|
||||
tracks/4/interp = 2
|
||||
tracks/4/loop_wrap = true
|
||||
tracks/4/keys = {
|
||||
"times": PackedFloat32Array(0),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"update": 0,
|
||||
"values": [Vector3(0, 1.5, 0)]
|
||||
}
|
||||
|
||||
[sub_resource type="Animation" id="Animation_5ec5e"]
|
||||
resource_name = "crouch"
|
||||
length = 0.2
|
||||
tracks/0/type = "value"
|
||||
tracks/0/imported = false
|
||||
tracks/0/enabled = true
|
||||
tracks/0/path = NodePath("Mesh:scale")
|
||||
tracks/0/interp = 2
|
||||
tracks/0/loop_wrap = true
|
||||
tracks/0/keys = {
|
||||
"times": PackedFloat32Array(0, 0.2),
|
||||
"transitions": PackedFloat32Array(1, 1),
|
||||
"update": 0,
|
||||
"values": [Vector3(1, 1, 1), Vector3(1, 0.75, 1)]
|
||||
}
|
||||
tracks/1/type = "value"
|
||||
tracks/1/imported = false
|
||||
tracks/1/enabled = true
|
||||
tracks/1/path = NodePath("Collision:scale")
|
||||
tracks/1/interp = 2
|
||||
tracks/1/loop_wrap = true
|
||||
tracks/1/keys = {
|
||||
"times": PackedFloat32Array(0, 0.2),
|
||||
"transitions": PackedFloat32Array(1, 1),
|
||||
"update": 0,
|
||||
"values": [Vector3(1, 1, 1), Vector3(1, 0.75, 1)]
|
||||
}
|
||||
tracks/2/type = "value"
|
||||
tracks/2/imported = false
|
||||
tracks/2/enabled = true
|
||||
tracks/2/path = NodePath("Mesh:position")
|
||||
tracks/2/interp = 2
|
||||
tracks/2/loop_wrap = true
|
||||
tracks/2/keys = {
|
||||
"times": PackedFloat32Array(0, 0.2),
|
||||
"transitions": PackedFloat32Array(1, 1),
|
||||
"update": 0,
|
||||
"values": [Vector3(0, 1, 0), Vector3(0, 0.75, 0)]
|
||||
}
|
||||
tracks/3/type = "value"
|
||||
tracks/3/imported = false
|
||||
tracks/3/enabled = true
|
||||
tracks/3/path = NodePath("Collision:position")
|
||||
tracks/3/interp = 2
|
||||
tracks/3/loop_wrap = true
|
||||
tracks/3/keys = {
|
||||
"times": PackedFloat32Array(0, 0.2),
|
||||
"transitions": PackedFloat32Array(1, 1),
|
||||
"update": 0,
|
||||
"values": [Vector3(0, 1, 0), Vector3(0, 0.75, 0)]
|
||||
}
|
||||
tracks/4/type = "value"
|
||||
tracks/4/imported = false
|
||||
tracks/4/enabled = true
|
||||
tracks/4/path = NodePath("Head:position")
|
||||
tracks/4/interp = 2
|
||||
tracks/4/loop_wrap = true
|
||||
tracks/4/keys = {
|
||||
"times": PackedFloat32Array(0, 0.2),
|
||||
"transitions": PackedFloat32Array(1, 1),
|
||||
"update": 0,
|
||||
"values": [Vector3(0, 1.5, 0), Vector3(0, 1.12508, 0)]
|
||||
}
|
||||
|
||||
[sub_resource type="AnimationLibrary" id="AnimationLibrary_5e5t5"]
|
||||
_data = {
|
||||
"RESET": SubResource("Animation_j8cx7"),
|
||||
"crouch": SubResource("Animation_5ec5e")
|
||||
}
|
||||
|
||||
[sub_resource type="Animation" id="Animation_gh776"]
|
||||
resource_name = "RESET"
|
||||
length = 0.001
|
||||
loop_mode = 1
|
||||
tracks/0/type = "bezier"
|
||||
tracks/0/imported = false
|
||||
tracks/0/enabled = true
|
||||
tracks/0/path = NodePath("Camera:position:x")
|
||||
tracks/0/interp = 1
|
||||
tracks/0/loop_wrap = true
|
||||
tracks/0/keys = {
|
||||
"handle_modes": PackedInt32Array(0),
|
||||
"points": PackedFloat32Array(0, -0.25, 0, 0.25, 0),
|
||||
"times": PackedFloat32Array(0)
|
||||
}
|
||||
tracks/1/type = "bezier"
|
||||
tracks/1/imported = false
|
||||
tracks/1/enabled = true
|
||||
tracks/1/path = NodePath("Camera:position:y")
|
||||
tracks/1/interp = 1
|
||||
tracks/1/loop_wrap = true
|
||||
tracks/1/keys = {
|
||||
"handle_modes": PackedInt32Array(0),
|
||||
"points": PackedFloat32Array(0, -0.25, 0, 0.25, 0),
|
||||
"times": PackedFloat32Array(0)
|
||||
}
|
||||
|
||||
[sub_resource type="Animation" id="Animation_8ku67"]
|
||||
resource_name = "sprint"
|
||||
length = 2.0
|
||||
loop_mode = 1
|
||||
tracks/0/type = "bezier"
|
||||
tracks/0/imported = false
|
||||
tracks/0/enabled = true
|
||||
tracks/0/path = NodePath("Camera:position:x")
|
||||
tracks/0/interp = 1
|
||||
tracks/0/loop_wrap = true
|
||||
tracks/0/keys = {
|
||||
"handle_modes": PackedInt32Array(0, 1, 0, 1, 0),
|
||||
"points": PackedFloat32Array(0.06, -0.25, 0, 0.25, -0.01, 0, 0, 0, 0, 0, -0.06, -0.25, 0.01, 0.25, 0.01, 0, 0, 0, 0, 0, 0.06, -0.25, -0.01, 0.25, 0),
|
||||
"times": PackedFloat32Array(0, 0.5, 1, 1.5, 2)
|
||||
}
|
||||
tracks/1/type = "bezier"
|
||||
tracks/1/imported = false
|
||||
tracks/1/enabled = true
|
||||
tracks/1/path = NodePath("Camera:position:y")
|
||||
tracks/1/interp = 1
|
||||
tracks/1/loop_wrap = true
|
||||
tracks/1/keys = {
|
||||
"handle_modes": PackedInt32Array(0, 0, 0, 0, 0),
|
||||
"points": PackedFloat32Array(0.05, -0.25, 0, 0.2, -0.01, 0, -0.2, 0.000186046, 0.2, 0.000186046, 0.05, -0.2, -0.01, 0.2, -0.01, 0, -0.2, 0, 0.2, 0, 0.05, -0.2, -0.01, 0.25, 0),
|
||||
"times": PackedFloat32Array(0, 0.5, 1, 1.5, 2)
|
||||
}
|
||||
|
||||
[sub_resource type="Animation" id="Animation_lrqmv"]
|
||||
resource_name = "walk"
|
||||
length = 2.0
|
||||
loop_mode = 1
|
||||
tracks/0/type = "bezier"
|
||||
tracks/0/imported = false
|
||||
tracks/0/enabled = true
|
||||
tracks/0/path = NodePath("Camera:position:x")
|
||||
tracks/0/interp = 1
|
||||
tracks/0/loop_wrap = true
|
||||
tracks/0/keys = {
|
||||
"handle_modes": PackedInt32Array(0, 1, 0, 1, 0),
|
||||
"points": PackedFloat32Array(0.04, -0.25, 0, 0.25, 0, 0, 0, 0, 0, 0, -0.04, -0.25, 0, 0.25, 0, 0, 0, 0, 0, 0, 0.04, -0.25, 0, 0.25, 0),
|
||||
"times": PackedFloat32Array(0, 0.5, 1, 1.5, 2)
|
||||
}
|
||||
tracks/1/type = "bezier"
|
||||
tracks/1/imported = false
|
||||
tracks/1/enabled = true
|
||||
tracks/1/path = NodePath("Camera:position:y")
|
||||
tracks/1/interp = 1
|
||||
tracks/1/loop_wrap = true
|
||||
tracks/1/keys = {
|
||||
"handle_modes": PackedInt32Array(0, 0, 0, 0, 0),
|
||||
"points": PackedFloat32Array(-0.05, -0.25, 0, 0.2, 0.005, 0, -0.2, 0.000186046, 0.2, 0.000186046, -0.05, -0.2, 0.005, 0.2, 0.005, 0, -0.2, 0, 0.2, 0, -0.05, -0.2, 0.005, 0.25, 0),
|
||||
"times": PackedFloat32Array(0, 0.5, 1, 1.5, 2)
|
||||
}
|
||||
|
||||
[sub_resource type="AnimationLibrary" id="AnimationLibrary_o0unb"]
|
||||
_data = {
|
||||
"RESET": SubResource("Animation_gh776"),
|
||||
"sprint": SubResource("Animation_8ku67"),
|
||||
"walk": SubResource("Animation_lrqmv")
|
||||
}
|
||||
|
||||
[sub_resource type="Animation" id="Animation_fvvjq"]
|
||||
length = 0.001
|
||||
tracks/0/type = "value"
|
||||
tracks/0/imported = false
|
||||
tracks/0/enabled = true
|
||||
tracks/0/path = NodePath("Camera:rotation")
|
||||
tracks/0/interp = 1
|
||||
tracks/0/loop_wrap = true
|
||||
tracks/0/keys = {
|
||||
"times": PackedFloat32Array(0),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"update": 0,
|
||||
"values": [Vector3(0, 0, 0)]
|
||||
}
|
||||
tracks/1/type = "value"
|
||||
tracks/1/imported = false
|
||||
tracks/1/enabled = true
|
||||
tracks/1/path = NodePath("Camera:position")
|
||||
tracks/1/interp = 1
|
||||
tracks/1/loop_wrap = true
|
||||
tracks/1/keys = {
|
||||
"times": PackedFloat32Array(0),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"update": 0,
|
||||
"values": [Vector3(0, 0, 0)]
|
||||
}
|
||||
|
||||
[sub_resource type="Animation" id="Animation_s07ye"]
|
||||
resource_name = "jump"
|
||||
length = 3.0
|
||||
tracks/0/type = "value"
|
||||
tracks/0/imported = false
|
||||
tracks/0/enabled = true
|
||||
tracks/0/path = NodePath("Camera:rotation")
|
||||
tracks/0/interp = 2
|
||||
tracks/0/loop_wrap = true
|
||||
tracks/0/keys = {
|
||||
"times": PackedFloat32Array(0, 0.6, 3),
|
||||
"transitions": PackedFloat32Array(1, 1, 1),
|
||||
"update": 0,
|
||||
"values": [Vector3(0, 0, 0), Vector3(0.0349066, 0, 0), Vector3(0, 0, 0)]
|
||||
}
|
||||
|
||||
[sub_resource type="Animation" id="Animation_l1rph"]
|
||||
resource_name = "land_left"
|
||||
length = 1.5
|
||||
tracks/0/type = "value"
|
||||
tracks/0/imported = false
|
||||
tracks/0/enabled = true
|
||||
tracks/0/path = NodePath("Camera:rotation")
|
||||
tracks/0/interp = 2
|
||||
tracks/0/loop_wrap = true
|
||||
tracks/0/keys = {
|
||||
"times": PackedFloat32Array(0, 0.5, 1.5),
|
||||
"transitions": PackedFloat32Array(1, 1, 1),
|
||||
"update": 0,
|
||||
"values": [Vector3(0, 0, 0), Vector3(-0.0349066, 0, 0.0174533), Vector3(0, 0, 0)]
|
||||
}
|
||||
tracks/1/type = "value"
|
||||
tracks/1/imported = false
|
||||
tracks/1/enabled = true
|
||||
tracks/1/path = NodePath("Camera:position")
|
||||
tracks/1/interp = 1
|
||||
tracks/1/loop_wrap = true
|
||||
tracks/1/keys = {
|
||||
"times": PackedFloat32Array(0, 0.5, 1.5),
|
||||
"transitions": PackedFloat32Array(1, 1, 1),
|
||||
"update": 0,
|
||||
"values": [Vector3(0, 0, 0), Vector3(0, -0.1, 0), Vector3(0, 0, 0)]
|
||||
}
|
||||
|
||||
[sub_resource type="Animation" id="Animation_vsknp"]
|
||||
resource_name = "land_right"
|
||||
length = 1.5
|
||||
tracks/0/type = "value"
|
||||
tracks/0/imported = false
|
||||
tracks/0/enabled = true
|
||||
tracks/0/path = NodePath("Camera:rotation")
|
||||
tracks/0/interp = 2
|
||||
tracks/0/loop_wrap = true
|
||||
tracks/0/keys = {
|
||||
"times": PackedFloat32Array(0, 0.5, 1.5),
|
||||
"transitions": PackedFloat32Array(1, 1, 1),
|
||||
"update": 0,
|
||||
"values": [Vector3(0, 0, 0), Vector3(-0.0349066, 0, -0.0174533), Vector3(0, 0, 0)]
|
||||
}
|
||||
tracks/1/type = "value"
|
||||
tracks/1/imported = false
|
||||
tracks/1/enabled = true
|
||||
tracks/1/path = NodePath("Camera:position")
|
||||
tracks/1/interp = 1
|
||||
tracks/1/loop_wrap = true
|
||||
tracks/1/keys = {
|
||||
"times": PackedFloat32Array(0, 0.5, 1.5),
|
||||
"transitions": PackedFloat32Array(1, 1, 1),
|
||||
"update": 0,
|
||||
"values": [Vector3(0, 0, 0), Vector3(0, -0.1, 0), Vector3(0, 0, 0)]
|
||||
}
|
||||
|
||||
[sub_resource type="AnimationLibrary" id="AnimationLibrary_qeg5r"]
|
||||
_data = {
|
||||
"RESET": SubResource("Animation_fvvjq"),
|
||||
"jump": SubResource("Animation_s07ye"),
|
||||
"land_left": SubResource("Animation_l1rph"),
|
||||
"land_right": SubResource("Animation_vsknp")
|
||||
}
|
||||
|
||||
[sub_resource type="Theme" id="Theme_wdf0f"]
|
||||
MarginContainer/constants/margin_bottom = 10
|
||||
MarginContainer/constants/margin_left = 10
|
||||
MarginContainer/constants/margin_right = 10
|
||||
MarginContainer/constants/margin_top = 10
|
||||
|
||||
[sub_resource type="SphereShape3D" id="SphereShape3D_k4wwl"]
|
||||
|
||||
[node name="Character" type="CharacterBody3D" node_paths=PackedStringArray("HEAD", "CAMERA", "HEADBOB_ANIMATION", "JUMP_ANIMATION", "CROUCH_ANIMATION", "COLLISION_MESH")]
|
||||
collision_layer = 2
|
||||
collision_mask = 3
|
||||
script = SubResource("GDScript_r0lhh")
|
||||
default_reticle = "res://godotsteam_sync_example/fpc/reticles/reticle_1.tscn"
|
||||
HEAD = NodePath("Head")
|
||||
CAMERA = NodePath("Head/Camera")
|
||||
HEADBOB_ANIMATION = NodePath("Head/HeadbobAnimation")
|
||||
JUMP_ANIMATION = NodePath("Head/JumpAnimation")
|
||||
CROUCH_ANIMATION = NodePath("CrouchAnimation")
|
||||
COLLISION_MESH = NodePath("Collision")
|
||||
JUMP = "jump"
|
||||
LEFT = "left"
|
||||
RIGHT = "right"
|
||||
FORWARD = "forward"
|
||||
BACKWARD = "backward"
|
||||
PAUSE = "pause"
|
||||
|
||||
[node name="Mesh" type="MeshInstance3D" parent="."]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0)
|
||||
mesh = SubResource("CapsuleMesh_jw1de")
|
||||
|
||||
[node name="Collision" type="CollisionShape3D" parent="."]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0)
|
||||
shape = SubResource("CapsuleShape3D_uy03j")
|
||||
|
||||
[node name="CrouchAnimation" type="AnimationPlayer" parent="."]
|
||||
libraries = {
|
||||
"": SubResource("AnimationLibrary_5e5t5")
|
||||
}
|
||||
|
||||
[node name="Head" type="Node3D" parent="."]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.5, 0)
|
||||
|
||||
[node name="Camera" type="Camera3D" parent="Head"]
|
||||
|
||||
[node name="HeadbobAnimation" type="AnimationPlayer" parent="Head"]
|
||||
libraries = {
|
||||
"": SubResource("AnimationLibrary_o0unb")
|
||||
}
|
||||
blend_times = [&"RESET", &"RESET", 0.5, &"RESET", &"walk", 0.5, &"walk", &"RESET", 0.5]
|
||||
|
||||
[node name="JumpAnimation" type="AnimationPlayer" parent="Head"]
|
||||
libraries = {
|
||||
"": SubResource("AnimationLibrary_qeg5r")
|
||||
}
|
||||
speed_scale = 4.0
|
||||
|
||||
[node name="UserInterface" type="Control" parent="."]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
mouse_filter = 1
|
||||
|
||||
[node name="DebugPanel" type="PanelContainer" parent="UserInterface"]
|
||||
visible = false
|
||||
layout_mode = 0
|
||||
offset_left = 10.0
|
||||
offset_top = 10.0
|
||||
offset_right = 453.0
|
||||
offset_bottom = 50.0
|
||||
theme = SubResource("Theme_wdf0f")
|
||||
script = ExtResource("3_x1wcc")
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="UserInterface/DebugPanel"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="UserInterface/DebugPanel/MarginContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="CrouchCeilingDetection" type="ShapeCast3D" parent="."]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0)
|
||||
shape = SubResource("SphereShape3D_k4wwl")
|
||||
target_position = Vector3(0, 0.5, 0)
|
||||
|
||||
[node name="EditorModule" type="Node" parent="."]
|
||||
script = ExtResource("3_v3ckk")
|
||||
|
||||
[node name="TransformSync3D" type="Node" parent="." node_paths=PackedStringArray("object_player")]
|
||||
script = ExtResource("4_ch187")
|
||||
object_player = NodePath("..")
|
18
godotsteam_sync_example/fpc/debug.gd
Normal file
@ -0,0 +1,18 @@
|
||||
extends PanelContainer
|
||||
|
||||
|
||||
func _process(delta):
|
||||
if visible:
|
||||
pass
|
||||
|
||||
func add_property(title : String, value, order : int): # This can either be called once for a static property or called every frame for a dynamic property
|
||||
var target
|
||||
target = $MarginContainer/VBoxContainer.find_child(title, true, false) # I have no idea what true and false does here, the function should be more specific
|
||||
if !target:
|
||||
target = Label.new() # Debug lines are of type Label
|
||||
$MarginContainer/VBoxContainer.add_child(target)
|
||||
target.name = title
|
||||
target.text = title + ": " + str(value)
|
||||
elif visible:
|
||||
target.text = title + ": " + str(value)
|
||||
$MarginContainer/VBoxContainer.move_child(target, order)
|
37
godotsteam_sync_example/fpc/reticles/reticle_0.tscn
Normal file
@ -0,0 +1,37 @@
|
||||
[gd_scene load_steps=2 format=3 uid="uid://coqpusufa8a6k"]
|
||||
|
||||
[sub_resource type="GDScript" id="GDScript_10f85"]
|
||||
script/source = "extends CenterContainer
|
||||
|
||||
|
||||
@export_category(\"Reticle\")
|
||||
@export_group(\"Nodes\")
|
||||
@export var character : CharacterBody3D
|
||||
|
||||
@export_group(\"Settings\")
|
||||
@export var dot_size : int = 1
|
||||
@export var dot_color : Color = Color.WHITE
|
||||
|
||||
|
||||
func _process(_delta):
|
||||
if visible: # If the reticle is disabled (not visible), don't bother updating it
|
||||
update_reticle_settings()
|
||||
|
||||
func update_reticle_settings():
|
||||
$dot.scale.x = dot_size
|
||||
$dot.scale.y = dot_size
|
||||
$dot.color = dot_color
|
||||
"
|
||||
|
||||
[node name="Reticle" type="CenterContainer"]
|
||||
anchors_preset = 8
|
||||
anchor_left = 0.5
|
||||
anchor_top = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 0.5
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
script = SubResource("GDScript_10f85")
|
||||
|
||||
[node name="dot" type="Polygon2D" parent="."]
|
||||
polygon = PackedVector2Array(-1, -1, 1, -1, 1, 1, -1, 1)
|
104
godotsteam_sync_example/fpc/reticles/reticle_1.tscn
Normal file
@ -0,0 +1,104 @@
|
||||
[gd_scene load_steps=2 format=3 uid="uid://3mij3cjhkwsm"]
|
||||
|
||||
[sub_resource type="GDScript" id="GDScript_a8kpl"]
|
||||
script/source = "extends CenterContainer
|
||||
|
||||
|
||||
@export_category(\"Reticle\")
|
||||
@export_group(\"Nodes\")
|
||||
@export var reticle_lines : Array[Line2D]
|
||||
@export var character : CharacterBody3D
|
||||
|
||||
@export_group(\"Animate\")
|
||||
@export var animated_reticle : bool = true
|
||||
@export var reticle_speed : float = 0.5
|
||||
@export var reticle_spread : float = 4.0
|
||||
|
||||
@export_group(\"Dot Settings\")
|
||||
@export var dot_size : int = 1
|
||||
@export var dot_color : Color = Color.WHITE
|
||||
|
||||
@export_group(\"Line Settings\")
|
||||
@export var line_color : Color = Color.WHITE
|
||||
@export var line_width : int = 2
|
||||
@export var line_length : int = 10
|
||||
@export var line_distance : int = 5
|
||||
@export_enum(\"None\", \"Round\") var cap_mode : int = 0
|
||||
|
||||
|
||||
func _process(_delta):
|
||||
if visible: # If the reticle is disabled (not visible), don't bother updating it
|
||||
update_reticle_settings()
|
||||
if animated_reticle:
|
||||
animate_reticle_lines()
|
||||
|
||||
|
||||
func animate_reticle_lines():
|
||||
var vel = character.get_real_velocity()
|
||||
var origin = Vector3(0,0,0)
|
||||
var pos = Vector2(0,0)
|
||||
var speed = origin.distance_to(vel)
|
||||
|
||||
reticle_lines[0].position = lerp(reticle_lines[0].position, pos + Vector2(0, -speed * reticle_spread), reticle_speed)
|
||||
reticle_lines[1].position = lerp(reticle_lines[1].position, pos + Vector2(-speed * reticle_spread, 0), reticle_speed)
|
||||
reticle_lines[2].position = lerp(reticle_lines[2].position, pos + Vector2(speed * reticle_spread, 0), reticle_speed)
|
||||
reticle_lines[3].position = lerp(reticle_lines[3].position, pos + Vector2(0, speed * reticle_spread), reticle_speed)
|
||||
|
||||
|
||||
func update_reticle_settings():
|
||||
# Dot
|
||||
$dot.scale.x = dot_size
|
||||
$dot.scale.y = dot_size
|
||||
$dot.color = dot_color
|
||||
|
||||
# Lines
|
||||
for line in reticle_lines:
|
||||
line.default_color = line_color
|
||||
line.width = line_width
|
||||
if cap_mode == 0:
|
||||
line.begin_cap_mode = Line2D.LINE_CAP_NONE
|
||||
line.end_cap_mode = Line2D.LINE_CAP_NONE
|
||||
elif cap_mode == 1:
|
||||
line.begin_cap_mode = Line2D.LINE_CAP_ROUND
|
||||
line.end_cap_mode = Line2D.LINE_CAP_ROUND
|
||||
|
||||
# Please someone find a better way to do this
|
||||
reticle_lines[0].points[0].y = -line_distance
|
||||
reticle_lines[0].points[1].y = -line_length - line_distance
|
||||
reticle_lines[1].points[0].x = -line_distance
|
||||
reticle_lines[1].points[1].x = -line_length - line_distance
|
||||
reticle_lines[2].points[0].x = line_distance
|
||||
reticle_lines[2].points[1].x = line_length + line_distance
|
||||
reticle_lines[3].points[0].y = line_distance
|
||||
reticle_lines[3].points[1].y = line_length + line_distance
|
||||
"
|
||||
|
||||
[node name="Reticle" type="CenterContainer" node_paths=PackedStringArray("reticle_lines")]
|
||||
anchors_preset = 8
|
||||
anchor_left = 0.5
|
||||
anchor_top = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 0.5
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
script = SubResource("GDScript_a8kpl")
|
||||
reticle_lines = [NodePath("top"), NodePath("left"), NodePath("right"), NodePath("bottom")]
|
||||
|
||||
[node name="dot" type="Polygon2D" parent="."]
|
||||
polygon = PackedVector2Array(-1, -1, 1, -1, 1, 1, -1, 1)
|
||||
|
||||
[node name="top" type="Line2D" parent="."]
|
||||
points = PackedVector2Array(0, -5, 0, -15)
|
||||
width = 2.0
|
||||
|
||||
[node name="left" type="Line2D" parent="."]
|
||||
points = PackedVector2Array(-5, 0, -15, 0)
|
||||
width = 2.0
|
||||
|
||||
[node name="right" type="Line2D" parent="."]
|
||||
points = PackedVector2Array(5, 0, 15, 0)
|
||||
width = 2.0
|
||||
|
||||
[node name="bottom" type="Line2D" parent="."]
|
||||
points = PackedVector2Array(0, 5, 0, 15)
|
||||
width = 2.0
|
1
godotsteam_sync_example/test.gd
Normal file
@ -0,0 +1 @@
|
||||
extends Node
|
@ -11,14 +11,16 @@ config_version=5
|
||||
[application]
|
||||
|
||||
config/name="Steamforged Skies"
|
||||
run/main_scene="res://assets/core/networking/scenes/lobby.tscn"
|
||||
run/main_scene="res://addons/godot_steam_sync/Lobby/main_menu.tscn"
|
||||
config/features=PackedStringArray("4.3", "Forward Plus")
|
||||
config/icon="res://assets/icon.png"
|
||||
|
||||
[autoload]
|
||||
|
||||
GameConsole="*res://addons/ingameconsole/GameConsole.tscn"
|
||||
NetworkManager="*res://assets/core/networking/scripts/NetworkManager.gd"
|
||||
NetworkManager="*res://addons/godot_steam_sync/Network/RadomeSteamSync/NetworkManager.gd"
|
||||
P2P="*res://addons/godot_steam_sync/Network/RadomeSteamSync/P2P.gd"
|
||||
SceneManager="*res://assets/core/networking/scripts/SceneManager.gd"
|
||||
|
||||
[debug]
|
||||
|
||||
@ -36,7 +38,7 @@ project/assembly_name="Steamforged Skies"
|
||||
|
||||
[editor_plugins]
|
||||
|
||||
enabled=PackedStringArray("res://addons/ingameconsole/plugin.cfg")
|
||||
enabled=PackedStringArray("res://addons/godot_steam_sync/plugin.cfg", "res://addons/ingameconsole/plugin.cfg")
|
||||
|
||||
[input]
|
||||
|
||||
|