mirror of
https://github.com/Rundll86/Dog-Lynx-And-HCN.git
synced 2026-06-26 13:32:29 +08:00
feat(多人游戏): 实现基础多人游戏功能
- 添加玩家位置同步功能 - 实现服务器和客户端连接管理 - 添加玩家名称输入和生成逻辑 - 完善多人游戏UI界面 - 移除单机模式下的预设玩家角色
This commit is contained in:
@@ -65,13 +65,36 @@ unique_name_in_owner = true
|
|||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "0 ∈ [0, 0]"
|
text = "0 ∈ [0, 0]"
|
||||||
|
|
||||||
[node name="startBtn" type="Button" parent="content/wrapper/starter/singleplayer" index="1"]
|
[node name="start" type="HBoxContainer" parent="content/wrapper/starter/singleplayer" index="1"]
|
||||||
|
layout_mode = 2
|
||||||
|
alignment = 1
|
||||||
|
|
||||||
|
[node name="playerNameInput" type="LineEdit" parent="content/wrapper/starter/singleplayer/start" index="0"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_vertical = 4
|
||||||
|
theme = ExtResource("4_lfxcn")
|
||||||
|
text = "公鸡"
|
||||||
|
placeholder_text = "角色名"
|
||||||
|
expand_to_text_length = true
|
||||||
|
virtual_keyboard_type = 7
|
||||||
|
select_all_on_focus = true
|
||||||
|
|
||||||
|
[node name="startBtn" type="Button" parent="content/wrapper/starter/singleplayer/start" index="1"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_horizontal = 4
|
size_flags_horizontal = 4
|
||||||
theme = ExtResource("4_lfxcn")
|
theme = ExtResource("4_lfxcn")
|
||||||
text = "单人游戏"
|
text = "单人游戏"
|
||||||
|
|
||||||
|
[node name="startMultiplayerBtn" type="Button" parent="content/wrapper/starter/singleplayer/start" index="2"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 4
|
||||||
|
theme = ExtResource("4_lfxcn")
|
||||||
|
disabled = true
|
||||||
|
text = "多人游戏"
|
||||||
|
|
||||||
[node name="multiplayer" type="VBoxContainer" parent="content/wrapper/starter" index="1"]
|
[node name="multiplayer" type="VBoxContainer" parent="content/wrapper/starter" index="1"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
theme_override_constants/separation = 15
|
theme_override_constants/separation = 15
|
||||||
@@ -153,26 +176,31 @@ size_flags_horizontal = 4
|
|||||||
disabled = true
|
disabled = true
|
||||||
text = "断开连接"
|
text = "断开连接"
|
||||||
|
|
||||||
[node name="serverConfig" type="VBoxContainer" parent="content/wrapper/starter/multiplayer" index="1"]
|
[node name="HBoxContainer" type="HBoxContainer" parent="content/wrapper/starter/multiplayer" index="1"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
theme_override_constants/separation = 30
|
||||||
alignment = 1
|
alignment = 1
|
||||||
|
|
||||||
[node name="title" type="Label" parent="content/wrapper/starter/multiplayer/serverConfig" index="0"]
|
[node name="serverConfig" type="VBoxContainer" parent="content/wrapper/starter/multiplayer/HBoxContainer" index="0"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="title" type="Label" parent="content/wrapper/starter/multiplayer/HBoxContainer/serverConfig" index="0"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_horizontal = 4
|
size_flags_horizontal = 4
|
||||||
text = "服务器配置"
|
text = "服务器配置"
|
||||||
|
|
||||||
[node name="maxPlayer" type="HBoxContainer" parent="content/wrapper/starter/multiplayer/serverConfig" index="1"]
|
[node name="maxPlayer" type="HBoxContainer" parent="content/wrapper/starter/multiplayer/HBoxContainer/serverConfig" index="1"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
alignment = 1
|
alignment = 1
|
||||||
|
|
||||||
[node name="title" type="Label" parent="content/wrapper/starter/multiplayer/serverConfig/maxPlayer" index="0"]
|
[node name="title" type="Label" parent="content/wrapper/starter/multiplayer/HBoxContainer/serverConfig/maxPlayer" index="0"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_horizontal = 4
|
size_flags_horizontal = 4
|
||||||
text = "最大玩家数"
|
text = "最大玩家数"
|
||||||
label_settings = SubResource("LabelSettings_i7qv0")
|
label_settings = SubResource("LabelSettings_i7qv0")
|
||||||
|
|
||||||
[node name="maxPlayerInput" type="LineEdit" parent="content/wrapper/starter/multiplayer/serverConfig/maxPlayer" index="1"]
|
[node name="maxPlayerInput" type="LineEdit" parent="content/wrapper/starter/multiplayer/HBoxContainer/serverConfig/maxPlayer" index="1"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_horizontal = 4
|
size_flags_horizontal = 4
|
||||||
@@ -182,3 +210,26 @@ expand_to_text_length = true
|
|||||||
emoji_menu_enabled = false
|
emoji_menu_enabled = false
|
||||||
virtual_keyboard_type = 2
|
virtual_keyboard_type = 2
|
||||||
select_all_on_focus = true
|
select_all_on_focus = true
|
||||||
|
|
||||||
|
[node name="VBoxContainer" type="VBoxContainer" parent="content/wrapper/starter/multiplayer/HBoxContainer" index="1"]
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="Label" type="Label" parent="content/wrapper/starter/multiplayer/HBoxContainer/VBoxContainer" index="0"]
|
||||||
|
layout_mode = 2
|
||||||
|
text = "玩家名"
|
||||||
|
|
||||||
|
[node name="Label2" type="Label" parent="content/wrapper/starter/multiplayer/HBoxContainer/VBoxContainer" index="1"]
|
||||||
|
layout_mode = 2
|
||||||
|
text = "玩家名"
|
||||||
|
|
||||||
|
[node name="Label3" type="Label" parent="content/wrapper/starter/multiplayer/HBoxContainer/VBoxContainer" index="2"]
|
||||||
|
layout_mode = 2
|
||||||
|
text = "玩家名"
|
||||||
|
|
||||||
|
[node name="Label4" type="Label" parent="content/wrapper/starter/multiplayer/HBoxContainer/VBoxContainer" index="3"]
|
||||||
|
layout_mode = 2
|
||||||
|
text = "玩家名"
|
||||||
|
|
||||||
|
[node name="Label5" type="Label" parent="content/wrapper/starter/multiplayer/HBoxContainer/VBoxContainer" index="4"]
|
||||||
|
layout_mode = 2
|
||||||
|
text = "玩家名"
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
[gd_scene load_steps=10 format=3 uid="uid://dmxi1ikn6avig"]
|
[gd_scene load_steps=9 format=3 uid="uid://dmxi1ikn6avig"]
|
||||||
|
|
||||||
[ext_resource type="Script" path="res://scripts/Tools/Managers/WorldManager.gd" id="1_lxsxj"]
|
[ext_resource type="Script" uid="uid://d2oyyyg0b4qqd" path="res://scripts/Tools/Managers/WorldManager.gd" id="1_lxsxj"]
|
||||||
[ext_resource type="PackedScene" uid="uid://dfwg750a47ggx" path="res://components/Scenes/UI.tscn" id="2_04cdd"]
|
[ext_resource type="PackedScene" uid="uid://dfwg750a47ggx" path="res://components/Scenes/UI.tscn" id="2_04cdd"]
|
||||||
[ext_resource type="PackedScene" uid="uid://bm7ymrri6pykb" path="res://components/Characters/Rooster.tscn" id="3_5ui6q"]
|
|
||||||
[ext_resource type="Texture2D" uid="uid://c33c8mtm4x3e3" path="res://resources/maps/Galaxy.png" id="4_oy4jj"]
|
[ext_resource type="Texture2D" uid="uid://c33c8mtm4x3e3" path="res://resources/maps/Galaxy.png" id="4_oy4jj"]
|
||||||
[ext_resource type="Script" path="res://scripts/Tools/Managers/CameraManager.gd" id="5_mk7bv"]
|
[ext_resource type="Script" uid="uid://bs45p8w83d4b4" path="res://scripts/Tools/Managers/CameraManager.gd" id="5_mk7bv"]
|
||||||
|
|
||||||
[sub_resource type="Animation" id="Animation_ykpvi"]
|
[sub_resource type="Animation" id="Animation_ykpvi"]
|
||||||
length = 0.001
|
length = 0.001
|
||||||
@@ -60,8 +59,8 @@ tracks/1/keys = {
|
|||||||
|
|
||||||
[sub_resource type="AnimationLibrary" id="AnimationLibrary_44ixa"]
|
[sub_resource type="AnimationLibrary" id="AnimationLibrary_44ixa"]
|
||||||
_data = {
|
_data = {
|
||||||
"RESET": SubResource("Animation_ykpvi"),
|
&"RESET": SubResource("Animation_ykpvi"),
|
||||||
"bigLaser": SubResource("Animation_kii8h")
|
&"bigLaser": SubResource("Animation_kii8h")
|
||||||
}
|
}
|
||||||
|
|
||||||
[sub_resource type="CircleShape2D" id="CircleShape2D_4hkht"]
|
[sub_resource type="CircleShape2D" id="CircleShape2D_4hkht"]
|
||||||
@@ -85,7 +84,7 @@ script = ExtResource("5_mk7bv")
|
|||||||
[node name="animator" type="AnimationPlayer" parent="camera"]
|
[node name="animator" type="AnimationPlayer" parent="camera"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
libraries = {
|
libraries = {
|
||||||
"": SubResource("AnimationLibrary_44ixa")
|
&"": SubResource("AnimationLibrary_44ixa")
|
||||||
}
|
}
|
||||||
|
|
||||||
[node name="map" type="StaticBody2D" parent="." groups=["map"]]
|
[node name="map" type="StaticBody2D" parent="." groups=["map"]]
|
||||||
@@ -103,5 +102,3 @@ shape = SubResource("CircleShape2D_4hkht")
|
|||||||
|
|
||||||
[node name="shan2" type="CollisionPolygon2D" parent="map"]
|
[node name="shan2" type="CollisionPolygon2D" parent="map"]
|
||||||
polygon = PackedVector2Array(-2419, 1803, 2429, 1825, 2392, -366, 2867, -318, 2723, 2241, -2879, 2193, -2797, -2582, 2959, -2528, 2858, -347, 2420, -337, 2441, -1834, -2438, -1792)
|
polygon = PackedVector2Array(-2419, 1803, 2429, 1825, 2392, -366, 2867, -318, 2723, 2241, -2879, 2193, -2797, -2582, 2959, -2528, 2858, -347, 2420, -337, 2441, -1834, -2438, -1792)
|
||||||
|
|
||||||
[node name="rooster" parent="." groups=["players"] instance=ExtResource("3_5ui6q")]
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ extends FullscreenPanelBase
|
|||||||
|
|
||||||
@onready var diffEdit: HSlider = $"%diffEdit"
|
@onready var diffEdit: HSlider = $"%diffEdit"
|
||||||
@onready var startBtn: Button = $"%startBtn"
|
@onready var startBtn: Button = $"%startBtn"
|
||||||
|
@onready var startMultiplayerBtn: Button = $"%startMultiplayerBtn"
|
||||||
@onready var levelShow: Label = $"%levelShow"
|
@onready var levelShow: Label = $"%levelShow"
|
||||||
@onready var hostInput: LineEdit = $"%hostInput"
|
@onready var hostInput: LineEdit = $"%hostInput"
|
||||||
@onready var portInput: LineEdit = $"%portInput"
|
@onready var portInput: LineEdit = $"%portInput"
|
||||||
@@ -11,15 +12,30 @@ extends FullscreenPanelBase
|
|||||||
@onready var maxPlayerInput: LineEdit = $"%maxPlayerInput"
|
@onready var maxPlayerInput: LineEdit = $"%maxPlayerInput"
|
||||||
@onready var connectionState: Label = $"%connectionState"
|
@onready var connectionState: Label = $"%connectionState"
|
||||||
@onready var disconnectBtn: Button = $"%disconnectBtn"
|
@onready var disconnectBtn: Button = $"%disconnectBtn"
|
||||||
|
@onready var playerNameInput: LineEdit = $"%playerNameInput"
|
||||||
|
@onready var serverConfig: VBoxContainer = $"%serverConfig"
|
||||||
|
|
||||||
func _ready():
|
func _ready():
|
||||||
diffEdit.min_value = GameRule.difficultyRange.x
|
diffEdit.min_value = GameRule.difficultyRange.x
|
||||||
diffEdit.max_value = GameRule.difficultyRange.y
|
diffEdit.max_value = GameRule.difficultyRange.y
|
||||||
|
multiplayer.connection_failed.connect(
|
||||||
|
func():
|
||||||
|
setState(MultiplayerState.ConnectionState.DISCONNECTED)
|
||||||
|
)
|
||||||
|
multiplayer.peer_connected.connect(
|
||||||
|
func():
|
||||||
|
setState(MultiplayerState.ConnectionState.CONNECTED_CLIENT)
|
||||||
|
)
|
||||||
startBtn.pressed.connect(
|
startBtn.pressed.connect(
|
||||||
func():
|
func():
|
||||||
|
EntityBase.generatePlayer(playerNameInput.text)
|
||||||
Wave.next()
|
Wave.next()
|
||||||
UIState.closeCurrentPanel()
|
UIState.closeCurrentPanel()
|
||||||
)
|
)
|
||||||
|
startMultiplayerBtn.pressed.connect(
|
||||||
|
func():
|
||||||
|
pass
|
||||||
|
)
|
||||||
maxPlayerInput.text_changed.connect(
|
maxPlayerInput.text_changed.connect(
|
||||||
func(text):
|
func(text):
|
||||||
MultiplayerState.maxPlayer = int(text)
|
MultiplayerState.maxPlayer = int(text)
|
||||||
@@ -27,6 +43,16 @@ func _ready():
|
|||||||
launchBtn.pressed.connect(
|
launchBtn.pressed.connect(
|
||||||
func():
|
func():
|
||||||
MultiplayerState.launchServer(int(portInput.text))
|
MultiplayerState.launchServer(int(portInput.text))
|
||||||
|
setState(MultiplayerState.ConnectionState.CONNECTED_HOST)
|
||||||
|
)
|
||||||
|
connectBtn.pressed.connect(
|
||||||
|
func():
|
||||||
|
multiplayer.multiplayer_peer = MultiplayerState.connectClient(hostInput.text, int(portInput.text))
|
||||||
|
setState(MultiplayerState.ConnectionState.CONNECTING)
|
||||||
|
)
|
||||||
|
disconnectBtn.pressed.connect(
|
||||||
|
func():
|
||||||
|
setState(MultiplayerState.ConnectionState.DISCONNECTED)
|
||||||
)
|
)
|
||||||
setState(MultiplayerState.ConnectionState.DISCONNECTED)
|
setState(MultiplayerState.ConnectionState.DISCONNECTED)
|
||||||
func _physics_process(_delta):
|
func _physics_process(_delta):
|
||||||
@@ -38,3 +64,5 @@ func setState(state: MultiplayerState.ConnectionState):
|
|||||||
connectionState.text = "状态:%s" % MultiplayerState.stateTextMap[state]
|
connectionState.text = "状态:%s" % MultiplayerState.stateTextMap[state]
|
||||||
connectionState.modulate = MultiplayerState.stateColorMap[state]
|
connectionState.modulate = MultiplayerState.stateColorMap[state]
|
||||||
disconnectBtn.disabled = not MultiplayerState.isConnected()
|
disconnectBtn.disabled = not MultiplayerState.isConnected()
|
||||||
|
startMultiplayerBtn.disabled = not MultiplayerState.isConnected()
|
||||||
|
serverConfig.visible = MultiplayerState.state == MultiplayerState.ConnectionState.CONNECTED_HOST
|
||||||
|
|||||||
@@ -175,6 +175,7 @@ func _physics_process(_delta: float) -> void:
|
|||||||
move_and_slide()
|
move_and_slide()
|
||||||
storeEnergy(randf_range(0.01, 0.05 + fields.get(FieldStore.Entity.ENERGY_REGENERATION) - 1), true)
|
storeEnergy(randf_range(0.01, 0.05 + fields.get(FieldStore.Entity.ENERGY_REGENERATION) - 1), true)
|
||||||
trailParticle.emitting = trailing
|
trailParticle.emitting = trailing
|
||||||
|
rpc("syncPosition", displayName, position)
|
||||||
|
|
||||||
# 通用方法
|
# 通用方法
|
||||||
func rebuildWeaponIcons():
|
func rebuildWeaponIcons():
|
||||||
@@ -393,11 +394,26 @@ func summon(who: PackedScene, syncFields: bool = true, lockValue: bool = true) -
|
|||||||
get_parent().add_child(instance)
|
get_parent().add_child(instance)
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
|
# 多人游戏数据同步
|
||||||
|
@rpc("any_peer")
|
||||||
|
func syncPosition(player: String, newPosition: Vector2):
|
||||||
|
if player == displayName:
|
||||||
|
position = newPosition
|
||||||
|
@rpc("any_peer")
|
||||||
|
func syncHealth(player: String, newHealth: float):
|
||||||
|
if player == displayName:
|
||||||
|
health = newHealth
|
||||||
|
healthChanged.emit(health)
|
||||||
|
@rpc("any_peer")
|
||||||
|
func syncAttack(player: String, index: int):
|
||||||
|
if player == displayName:
|
||||||
|
tryAttack(index)
|
||||||
|
|
||||||
# 关于追踪
|
# 关于追踪
|
||||||
func getTrackingAnchor() -> Vector2:
|
func getTrackingAnchor() -> Vector2:
|
||||||
return hurtbox.get_node("hitbox").global_position
|
return hurtbox.get_node("hitbox").global_position
|
||||||
|
|
||||||
# 关于分组
|
# 关于实体类型
|
||||||
func isPlayer() -> bool:
|
func isPlayer() -> bool:
|
||||||
return is_in_group("players")
|
return is_in_group("players")
|
||||||
func isSummon() -> bool:
|
func isSummon() -> bool:
|
||||||
@@ -430,6 +446,10 @@ func enterStage(_stage: int):
|
|||||||
func kill():
|
func kill():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
static func generatePlayer(playerName: String):
|
||||||
|
var player = generate(ComponentManager.getCharacter("Rooster"), Vector2.ZERO, false, false, true)
|
||||||
|
player.displayName = playerName
|
||||||
|
return player
|
||||||
static func generate(
|
static func generate(
|
||||||
entity: PackedScene,
|
entity: PackedScene,
|
||||||
spawnPosition: Vector2,
|
spawnPosition: Vector2,
|
||||||
@@ -443,6 +463,8 @@ static func generate(
|
|||||||
instance.level = clamp((round(Wave.current * (1 + GameRule.entityLevelOffsetByWave * randf_range(-1, 1)))), 1, INF)
|
instance.level = clamp((round(Wave.current * (1 + GameRule.entityLevelOffsetByWave * randf_range(-1, 1)))), 1, INF)
|
||||||
if isMob:
|
if isMob:
|
||||||
instance.add_to_group("mobs")
|
instance.add_to_group("mobs")
|
||||||
|
else:
|
||||||
|
instance.add_to_group("players")
|
||||||
if addToWorld:
|
if addToWorld:
|
||||||
WorldManager.rootNode.add_child(instance)
|
WorldManager.rootNode.add_child(instance)
|
||||||
return instance
|
return instance
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ enum ConnectionState {
|
|||||||
}
|
}
|
||||||
static var stateTextMap = {
|
static var stateTextMap = {
|
||||||
ConnectionState.DISCONNECTED: "未连接到服务器。",
|
ConnectionState.DISCONNECTED: "未连接到服务器。",
|
||||||
ConnectionState.CONNECTING: "连接中...",
|
ConnectionState.CONNECTING: "正在连接到服务器...",
|
||||||
ConnectionState.CONNECTED_HOST: "服务器启动成功!",
|
ConnectionState.CONNECTED_HOST: "服务器启动成功!",
|
||||||
ConnectionState.CONNECTED_CLIENT: "已连接到服务器!",
|
ConnectionState.CONNECTED_CLIENT: "已连接到服务器!",
|
||||||
}
|
}
|
||||||
@@ -21,18 +21,17 @@ static var stateColorMap = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static var state: ConnectionState = ConnectionState.DISCONNECTED
|
static var state: ConnectionState = ConnectionState.DISCONNECTED
|
||||||
static var isMultiplayer: bool = false
|
|
||||||
|
|
||||||
static var maxPlayer: int = 10
|
static var maxPlayer: int = 10
|
||||||
|
|
||||||
static func isConnected():
|
static func isConnected():
|
||||||
return [ConnectionState.CONNECTED_HOST, ConnectionState.CONNECTED_CLIENT].has(state)
|
return [ConnectionState.CONNECTED_HOST, ConnectionState.CONNECTED_CLIENT].has(state)
|
||||||
static func launchServer(port: int):
|
static func launchServer(port: int) -> ENetMultiplayerPeer:
|
||||||
var peer = ENetMultiplayerPeer.new()
|
var peer = ENetMultiplayerPeer.new()
|
||||||
peer.create_server(port, maxPlayer)
|
peer.create_server(port, maxPlayer)
|
||||||
state = ConnectionState.CONNECTED_HOST
|
state = ConnectionState.CONNECTED_HOST
|
||||||
return peer
|
return peer
|
||||||
static func connectClient(host: String, port: int):
|
static func connectClient(host: String, port: int) -> ENetMultiplayerPeer:
|
||||||
var peer = ENetMultiplayerPeer.new()
|
var peer = ENetMultiplayerPeer.new()
|
||||||
peer.create_client(host, port)
|
peer.create_client(host, port)
|
||||||
state = ConnectionState.CONNECTED_CLIENT
|
state = ConnectionState.CONNECTED_CLIENT
|
||||||
|
|||||||
Reference in New Issue
Block a user