- Appearance (name and color) can be changed - Appearance is synced to other players - Split tabs into multiple scripts - Use nameof() where applicablemain
parent
d302a56855
commit
7b76d44b43
8 changed files with 386 additions and 158 deletions
@ -1,15 +1,34 @@ |
|||||||
[gd_scene load_steps=3 format=2] |
[gd_scene load_steps=4 format=2] |
||||||
|
|
||||||
|
[ext_resource path="res://ui_theme.tres" type="Theme" id=1] |
||||||
[ext_resource path="res://gfx/player.png" type="Texture" id=2] |
[ext_resource path="res://gfx/player.png" type="Texture" id=2] |
||||||
|
|
||||||
[sub_resource type="CircleShape2D" id=1] |
[sub_resource type="CircleShape2D" id=1] |
||||||
radius = 8.0 |
radius = 8.0 |
||||||
|
|
||||||
[node name="Player" type="KinematicBody2D"] |
[node name="Player" type="KinematicBody2D"] |
||||||
|
z_index = 10 |
||||||
collision_layer = 0 |
collision_layer = 0 |
||||||
|
|
||||||
[node name="CircleShape" type="CollisionShape2D" parent="."] |
[node name="CircleShape" type="CollisionShape2D" parent="."] |
||||||
shape = SubResource( 1 ) |
shape = SubResource( 1 ) |
||||||
|
|
||||||
[node name="Sprite" type="Sprite" parent="."] |
[node name="Sprite" type="Sprite" parent="."] |
||||||
|
z_index = -5 |
||||||
texture = ExtResource( 2 ) |
texture = ExtResource( 2 ) |
||||||
|
|
||||||
|
[node name="Name" type="Label" parent="."] |
||||||
|
modulate = Color( 1, 1, 1, 0.501961 ) |
||||||
|
anchor_left = 0.5 |
||||||
|
anchor_right = 0.5 |
||||||
|
margin_left = -80.0 |
||||||
|
margin_top = -24.0 |
||||||
|
margin_right = 80.0 |
||||||
|
margin_bottom = -9.0 |
||||||
|
rect_min_size = Vector2( 160, 15 ) |
||||||
|
theme = ExtResource( 1 ) |
||||||
|
align = 1 |
||||||
|
valign = 1 |
||||||
|
__meta__ = { |
||||||
|
"_edit_use_anchors_": false |
||||||
|
} |
||||||
|
@ -0,0 +1,103 @@ |
|||||||
|
using System.Text.RegularExpressions; |
||||||
|
using Godot; |
||||||
|
|
||||||
|
public class EscapeMenuAppearance : CenterContainer |
||||||
|
{ |
||||||
|
[Export] public NodePath PlayerNamePath { get; set; } |
||||||
|
[Export] public NodePath ColorPreviewPath { get; set; } |
||||||
|
[Export] public NodePath ColorSliderPath { get; set; } |
||||||
|
|
||||||
|
public LineEdit PlayerName { get; private set; } |
||||||
|
public TextureRect ColorPreview { get; private set; } |
||||||
|
public Slider ColorSlider { get; private set; } |
||||||
|
|
||||||
|
public Network Network { get; private set; } |
||||||
|
public Player LocalPlayer { get; private set; } |
||||||
|
|
||||||
|
public override void _EnterTree() |
||||||
|
{ |
||||||
|
PlayerName = GetNode<LineEdit>(PlayerNamePath); |
||||||
|
ColorPreview = GetNode<TextureRect>(ColorPreviewPath); |
||||||
|
ColorSlider = GetNode<Slider>(ColorSliderPath); |
||||||
|
|
||||||
|
CallDeferred(nameof(Initialize)); |
||||||
|
} |
||||||
|
|
||||||
|
private void Initialize() |
||||||
|
{ |
||||||
|
Network = GetNode<Network>("/root/Game/Network"); |
||||||
|
LocalPlayer = GetNode<Player>("/root/Game/LocalPlayer"); |
||||||
|
|
||||||
|
ColorSlider.Value = GD.RandRange(0.0, 1.0); |
||||||
|
var color = Color.FromHsv((float)ColorSlider.Value, 1.0F, 1.0F); |
||||||
|
LocalPlayer.GetNode<Sprite>("Sprite").Modulate = color; |
||||||
|
ColorPreview.Modulate = color; |
||||||
|
|
||||||
|
Network.Connect(nameof(Network.StatusChanged), this, nameof(OnNetworkStatusChanged)); |
||||||
|
GetTree().Connect("network_peer_connected", this, nameof(OnPeerConnected)); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
private void OnNetworkStatusChanged(Network.Status status) |
||||||
|
{ |
||||||
|
if (status == Network.Status.ConnectedToServer) |
||||||
|
SendAppearance(); |
||||||
|
} |
||||||
|
|
||||||
|
private void OnPeerConnected(int id) |
||||||
|
{ |
||||||
|
// TODO: See if we can do something with syncing these directly? |
||||||
|
var name = LocalPlayer.GetNode<Label>("Name").Text; |
||||||
|
var hue = LocalPlayer.GetNode<Sprite>("Sprite").Modulate.h; |
||||||
|
RpcId(id, nameof(AppearanceChanged), name, hue); |
||||||
|
} |
||||||
|
|
||||||
|
private void SendAppearance() |
||||||
|
{ |
||||||
|
// TODO: See if we can do something with syncing these directly? |
||||||
|
var name = LocalPlayer.GetNode<Label>("Name").Text; |
||||||
|
var hue = LocalPlayer.GetNode<Sprite>("Sprite").Modulate.h; |
||||||
|
Rpc(nameof(AppearanceChanged), name, hue); |
||||||
|
} |
||||||
|
|
||||||
|
[Remote] |
||||||
|
private void AppearanceChanged(string name, float hue) |
||||||
|
{ |
||||||
|
// TODO: Clear out invalid characters from name. |
||||||
|
hue = Mathf.Clamp(hue, 0.0F, 1.0F); |
||||||
|
|
||||||
|
var id = GetTree().GetRpcSenderId(); |
||||||
|
var player = Network.GetOrCreatePlayerWithId(id); |
||||||
|
player.GetNode<Label>("Name").Text = name; |
||||||
|
player.GetNode<Sprite>("Sprite").Modulate = Color.FromHsv(hue, 1.0F, 1.0F); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
#pragma warning disable IDE0051 |
||||||
|
#pragma warning disable IDE1006 |
||||||
|
|
||||||
|
private static readonly Regex INVALID_CHARS = new Regex(@"\s"); |
||||||
|
private void _on_Name_text_changed(string text) |
||||||
|
{ |
||||||
|
var validText = INVALID_CHARS.Replace(text, ""); |
||||||
|
if (validText != text) { |
||||||
|
var previousCaretPos = PlayerName.CaretPosition; |
||||||
|
PlayerName.Text = validText; |
||||||
|
PlayerName.CaretPosition = previousCaretPos - (text.Length - validText.Length); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void _on_HSlider_value_changed(float value) |
||||||
|
{ |
||||||
|
var color = Color.FromHsv(value, 1.0F, 1.0F); |
||||||
|
ColorPreview.Modulate = color; |
||||||
|
} |
||||||
|
|
||||||
|
private void _on_Appearance_visibility_changed() |
||||||
|
{ |
||||||
|
if (IsVisibleInTree()) return; |
||||||
|
LocalPlayer.GetNode<Label>("Name").Text = PlayerName.Text; |
||||||
|
LocalPlayer.GetNode<Sprite>("Sprite").Modulate = ColorPreview.Modulate; |
||||||
|
if (GetTree().NetworkPeer != null) SendAppearance(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,94 @@ |
|||||||
|
using Godot; |
||||||
|
|
||||||
|
public class EscapeMenuMultiplayer : Container |
||||||
|
{ |
||||||
|
[Export] public NodePath StatusPath { get; set; } |
||||||
|
[Export] public NodePath ServerStartStopPath { get; set; } |
||||||
|
[Export] public NodePath ServerPortPath { get; set; } |
||||||
|
[Export] public NodePath ClientDisConnectPath { get; set; } |
||||||
|
[Export] public NodePath ClientAddressPath { get; set; } |
||||||
|
|
||||||
|
public Label Status { get; private set; } |
||||||
|
public Button ServerStartStop { get; private set; } |
||||||
|
public LineEdit ServerPort { get; private set; } |
||||||
|
public Button ClientDisConnect { get; private set; } |
||||||
|
public LineEdit ClientAddress { get; private set; } |
||||||
|
|
||||||
|
public Network Network { get; private set; } |
||||||
|
|
||||||
|
public override void _EnterTree() |
||||||
|
{ |
||||||
|
Status = GetNode<Label>(StatusPath); |
||||||
|
ServerStartStop = GetNode<Button>(ServerStartStopPath); |
||||||
|
ServerPort = GetNode<LineEdit>(ServerPortPath); |
||||||
|
ClientDisConnect = GetNode<Button>(ClientDisConnectPath); |
||||||
|
ClientAddress = GetNode<LineEdit>(ClientAddressPath); |
||||||
|
|
||||||
|
Network = GetNode<Network>("/root/Game/Network"); |
||||||
|
Network.Connect(nameof(Network.StatusChanged), this, nameof(OnNetworkStatusChanged)); |
||||||
|
ServerPort.PlaceholderText = Network.DefaultPort.ToString(); |
||||||
|
ClientAddress.PlaceholderText = $"{Network.DefaultAddress}:{Network.DefaultPort}"; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
private void OnNetworkStatusChanged(Network.Status status) |
||||||
|
{ |
||||||
|
switch (status) { |
||||||
|
case Network.Status.NoConnection: |
||||||
|
Status.Text = "No Connection"; |
||||||
|
Status.Modulate = Colors.Red; |
||||||
|
break; |
||||||
|
case Network.Status.ServerRunning: |
||||||
|
Status.Text = "Server Running"; |
||||||
|
Status.Modulate = Colors.Green; |
||||||
|
break; |
||||||
|
case Network.Status.Connecting: |
||||||
|
Status.Text = "Connecting ..."; |
||||||
|
Status.Modulate = Colors.Yellow; |
||||||
|
break; |
||||||
|
case Network.Status.ConnectedToServer: |
||||||
|
Status.Text = "Connected to Server"; |
||||||
|
Status.Modulate = Colors.Green; |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
ServerPort.Editable = status == Network.Status.NoConnection; |
||||||
|
ServerStartStop.Text = (status == Network.Status.ServerRunning) ? "Stop Server" : "Start Server"; |
||||||
|
ClientAddress.Editable = status == Network.Status.NoConnection; |
||||||
|
ClientDisConnect.Text = (status < Network.Status.Connecting) ? "Connect" : "Disconnect"; |
||||||
|
ClientDisConnect.Disabled = status == Network.Status.ServerRunning; |
||||||
|
if (Visible) GetTree().Paused = status == Network.Status.NoConnection; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
#pragma warning disable IDE0051 |
||||||
|
#pragma warning disable IDE1006 |
||||||
|
|
||||||
|
private void _on_ServerStartStop_pressed() |
||||||
|
{ |
||||||
|
if (GetTree().NetworkPeer == null) { |
||||||
|
var port = Network.DefaultPort; |
||||||
|
if (ServerPort.Text.Length > 0) |
||||||
|
port = ushort.Parse(ServerPort.Text); |
||||||
|
Network.StartServer(port); |
||||||
|
} else Network.StopServer(); |
||||||
|
} |
||||||
|
|
||||||
|
private void _on_ClientDisConnect_pressed() |
||||||
|
{ |
||||||
|
if (GetTree().NetworkPeer == null) { |
||||||
|
var address = Network.DefaultAddress; |
||||||
|
var port = Network.DefaultPort; |
||||||
|
if (ClientAddress.Text.Length > 0) { |
||||||
|
// TODO: Verify input some more, support IPv6? |
||||||
|
var split = address.Split(':'); |
||||||
|
address = (split.Length > 1) ? split[0] : address; |
||||||
|
port = (split.Length > 1) ? ushort.Parse(split[1]) : port; |
||||||
|
} |
||||||
|
Network.ConnectToServer(address, port); |
||||||
|
} else Network.DisconnectFromServer(); |
||||||
|
} |
||||||
|
|
||||||
|
private void _on_HideAddress_toggled(bool pressed) |
||||||
|
=> ClientAddress.Secret = pressed; |
||||||
|
} |
Loading…
Reference in new issue