From 3352518f0829d29bdc0f184bb6a91fe19d84d0b5 Mon Sep 17 00:00:00 2001 From: copygirl Date: Sat, 29 May 2021 16:52:09 +0200 Subject: [PATCH] Remove LocalPlayer ... again - Add PlayerMovement, child node of Player which handles movement - PlayerMovement and Camera are added through code when spawned - Add Player.IsLocal property - Move Velocity property to Player - Remove ResetPosition, instead just use RsetId on Position and Velocity --- scene/LocalPlayer.tscn | 11 ------ src/EscapeMenuWorld.cs | 3 +- src/Items/CreativeBuilding.cs | 7 ++-- src/Items/Weapon.cs | 18 ++++----- src/Objects/Player.cs | 8 +++- .../{LocalPlayer.cs => PlayerMovement.cs} | 37 ++++++++----------- src/Scenes/Client.cs | 14 +++---- src/World.cs | 10 +++-- 8 files changed, 48 insertions(+), 60 deletions(-) delete mode 100644 scene/LocalPlayer.tscn rename src/Objects/{LocalPlayer.cs => PlayerMovement.cs} (61%) diff --git a/scene/LocalPlayer.tscn b/scene/LocalPlayer.tscn deleted file mode 100644 index 9bb3ce7..0000000 --- a/scene/LocalPlayer.tscn +++ /dev/null @@ -1,11 +0,0 @@ -[gd_scene load_steps=3 format=2] - -[ext_resource path="res://scene/Player.tscn" type="PackedScene" id=1] -[ext_resource path="res://src/Objects/LocalPlayer.cs" type="Script" id=2] - -[node name="LocalPlayer" instance=ExtResource( 1 )] -script = ExtResource( 2 ) - -[node name="Camera" type="Camera2D" parent="." index="0"] -pause_mode = 2 -current = true diff --git a/src/EscapeMenuWorld.cs b/src/EscapeMenuWorld.cs index 55ddfdf..efbf5de 100644 --- a/src/EscapeMenuWorld.cs +++ b/src/EscapeMenuWorld.cs @@ -106,7 +106,8 @@ public class EscapeMenuWorld : CenterContainer foreach (var player in server.GetWorld().Players) { // Reset players' positions. // Can't use RPC helper method here since player is not a LocalPlayer here. - player.RpcId(player.NetworkID, nameof(LocalPlayer.ResetPosition), Vector2.Zero); + player.RsetId(player.NetworkID, "position", Vector2.Zero); + player.RsetId(player.NetworkID, nameof(Player.Velocity), Vector2.Zero); // Reset the visbility tracker so the client will receive new chunks. player.VisibilityTracker.Reset(); } diff --git a/src/Items/CreativeBuilding.cs b/src/Items/CreativeBuilding.cs index 78966c9..7db3ac0 100644 --- a/src/Items/CreativeBuilding.cs +++ b/src/Items/CreativeBuilding.cs @@ -35,7 +35,7 @@ public class CreativeBuilding : Node2D public override void _UnhandledInput(InputEvent ev) { - if (!Visible || !(Player is LocalPlayer) || !Player.IsAlive) return; + if (!Visible || !Player.IsLocal || !Player.IsAlive) return; if (ev.IsActionPressed("interact_primary")) { GetTree().SetInputAsHandled(); @@ -49,7 +49,7 @@ public class CreativeBuilding : Node2D public override void _Process(float delta) { - if (!(Player is LocalPlayer)) return; + if (!Player.IsLocal) return; if (!Visible || !Player.IsAlive) _currentMode = null; if (_currentMode == BuildMode.Placing) { @@ -86,8 +86,7 @@ public class CreativeBuilding : Node2D public override void _Draw() { - if (!(Player is LocalPlayer) || !Cursor.Visible || - EscapeMenu.Instance.Visible) return; + if (!Player.IsLocal || !Cursor.Visible || EscapeMenu.Instance.Visible) return; var green = Color.FromHsv(1.0F / 3, 1.0F, 1.0F, 0.4F); var red = Color.FromHsv(0.0F, 1.0F, 1.0F, 0.4F); diff --git a/src/Items/Weapon.cs b/src/Items/Weapon.cs index 5d2cca3..8de2004 100644 --- a/src/Items/Weapon.cs +++ b/src/Items/Weapon.cs @@ -60,7 +60,7 @@ public class Weapon : Sprite public override void _UnhandledInput(InputEvent ev) { - if (!(Player is LocalPlayer)) return; + if (!Player.IsLocal) return; if (ev.IsActionPressed("interact_primary")) { HoldingTrigger = TimeSpan.Zero; OnTriggerPressed(); } if (ev.IsActionPressed("interact_reload")) Reload(); @@ -82,7 +82,7 @@ public class Weapon : Sprite _reloadDelay = 0.0F; Rounds = Capacity; HoldingTrigger = null; - if (Player is LocalPlayer) Update(); + if (Player.IsLocal) Update(); } else if (Visible) { if (HoldingTrigger is TimeSpan holding) HoldingTrigger = holding + TimeSpan.FromSeconds(delta); @@ -101,7 +101,7 @@ public class Weapon : Sprite _fireDelay = 0; } - if (Player is LocalPlayer) { + if (Player.IsLocal) { // Automatically reload when out of rounds. if (Rounds <= 0) Reload(); @@ -165,7 +165,7 @@ public class Weapon : Sprite if (float.IsNaN(value = Mathf.PosMod(value, Mathf.Tau))) return; RPC.Unreliable(SendAimAngle, value); - } else if (!(Player is LocalPlayer)) + } else if (!Player.IsLocal) AimDirection = value; } @@ -175,7 +175,7 @@ public class Weapon : Sprite var seed = unchecked((int)GD.Randi()); if (!FireInternal(AimDirection, Scale.y > 0, seed)) return; RPC.Reliable(1, SendFire, AimDirection, Scale.y > 0, seed); - ((LocalPlayer)Player).Velocity -= Mathf.Polar2Cartesian(Knockback, Rotation); + Player.Velocity -= Mathf.Polar2Cartesian(Knockback, Rotation); } protected virtual bool FireInternal(float aimDirection, bool toRight, int seed) @@ -209,7 +209,7 @@ public class Weapon : Sprite _currentSpreadInc += Mathf.Deg2Rad(SpreadIncrease); _currentRecoil += Mathf.Deg2Rad(random.NextFloat(RecoilMin, RecoilMax)); - if (isServer || (Player is LocalPlayer)) { + if (isServer || Player.IsLocal) { // Do not keep track of fire rate or ammo for other players. _fireDelay += 60.0F / RateOfFire; Rounds -= 1; @@ -227,7 +227,7 @@ public class Weapon : Sprite // TODO: Only send to players who can see the full path of the bullet. if (FireInternal(aimDirection, toRight, seed)) RPC.Reliable(SendFire, aimDirection, toRight, seed); - } else if (!(Player is LocalPlayer)) + } else if (!Player.IsLocal) FireInternal(aimDirection, toRight, seed); } @@ -250,14 +250,14 @@ public class Weapon : Sprite if (this.GetGame() is Server) { if (Player.NetworkID != GetTree().GetRpcSenderId()) return; if (ReloadInternal()) RPC.Reliable(SendReload); - } else if (!(Player is LocalPlayer)) + } else if (!Player.IsLocal) ReloadInternal(); } public override void _Draw() { - if (!(Player is LocalPlayer) || !Player.IsAlive || _lowered) return; + if (!Player.IsLocal || !Player.IsAlive || _lowered) return; // Draws an "aiming cone" to show where bullets might travel. var tip = TipOffset + new Vector2(4, 0); diff --git a/src/Objects/Player.cs b/src/Objects/Player.cs index 2cba187..d315e53 100644 --- a/src/Objects/Player.cs +++ b/src/Objects/Player.cs @@ -16,9 +16,12 @@ public class Player : KinematicBody2D, IInitializable public IItems Items { get; private set; } public int NetworkID { get => int.Parse(Name); set => Name = value.ToString(); } + public bool IsLocal => NetworkID == GetTree().GetNetworkUniqueId(); public string DisplayName { get => DisplayNameLabel.Text; set => DisplayNameLabel.Text = value; } public Color Color { get => Sprite.SelfModulate; set => Sprite.SelfModulate = value; } + public Vector2 Velocity { get; set; } + public float Health { get; set; } = 1.0F; public bool IsAlive => Health > 0.0F; private float _previousHealth; @@ -37,6 +40,7 @@ public class Player : KinematicBody2D, IInitializable RsetConfig("modulate", MultiplayerAPI.RPCMode.Puppetsync); RsetConfig(nameof(DisplayName), MultiplayerAPI.RPCMode.Puppetsync); RsetConfig(nameof(Color), MultiplayerAPI.RPCMode.Puppetsync); + RsetConfig(nameof(Velocity), MultiplayerAPI.RPCMode.Puppet); RsetConfig(nameof(Health), MultiplayerAPI.RPCMode.Puppet); } @@ -59,8 +63,8 @@ public class Player : KinematicBody2D, IInitializable if (!IsAlive && ((_respawnDelay += delta) > RESPAWN_TIMER.TotalSeconds)) { // TODO: Move respawning related code to its own method. - // Can't use RPC helper method here since player is not a LocalPlayer here. - RpcId(NetworkID, nameof(LocalPlayer.ResetPosition), Vector2.Zero); + RsetId(NetworkID, "position", Vector2.Zero); + RsetId(NetworkID, nameof(Velocity), Vector2.Zero); Rset("modulate", Colors.White); Health = 1.0F; _respawnDelay = 0.0F; diff --git a/src/Objects/LocalPlayer.cs b/src/Objects/PlayerMovement.cs similarity index 61% rename from src/Objects/LocalPlayer.cs rename to src/Objects/PlayerMovement.cs index 88a33df..0441180 100644 --- a/src/Objects/LocalPlayer.cs +++ b/src/Objects/PlayerMovement.cs @@ -1,7 +1,7 @@ using System; using Godot; -public class LocalPlayer : Player +public class PlayerMovement : Node { // TODO: Implement "low jumps" activated by releasing the jump button early. public TimeSpan JumpEarlyTime { get; } = TimeSpan.FromSeconds(0.2F); @@ -15,47 +15,40 @@ public class LocalPlayer : Player public float GroundFriction { get; set; } = 0.2F; public float AirFriction { get; set; } = 0.05F; - - public Vector2 Velocity = Vector2.Zero; + private Player _player; private DateTime? _jumpPressed = null; private DateTime? _lastOnFloor = null; + public override void _Ready() + => _player = GetParent(); + public override void _Process(float delta) - { - base._Process(delta); - RPC.Unreliable(1, Move, Position); - } + => RPC.Unreliable(1, _player.Move, _player.Position); public override void _PhysicsProcess(float delta) { var moveDir = 0.0F; var jumpPressed = false; - if (!EscapeMenu.Instance.Visible && IsAlive) { + if (!EscapeMenu.Instance.Visible && _player.IsAlive) { moveDir = Input.GetActionStrength("move_right") - Input.GetActionStrength("move_left"); jumpPressed = Input.IsActionJustPressed("move_jump"); } - var friction = IsOnFloor() ? GroundFriction : AirFriction; - Velocity.x = (moveDir != 0) ? Mathf.Lerp(Velocity.x, moveDir * MovementSpeed, Acceleration) - : Mathf.Lerp(Velocity.x, 0, friction); - Velocity.y += Gravity * delta; - Velocity = MoveAndSlide(Velocity, Vector2.Up); + var velocity = _player.Velocity; + var friction = _player.IsOnFloor() ? GroundFriction : AirFriction; + velocity.x = (moveDir != 0) ? Mathf.Lerp(_player.Velocity.x, moveDir * MovementSpeed, Acceleration) + : Mathf.Lerp(_player.Velocity.x, 0, friction); + velocity.y += Gravity * delta; + _player.Velocity = _player.MoveAndSlide(velocity, Vector2.Up); if (jumpPressed) _jumpPressed = DateTime.Now; - if (IsOnFloor()) _lastOnFloor = DateTime.Now; + if (_player.IsOnFloor()) _lastOnFloor = DateTime.Now; if (((DateTime.Now - _jumpPressed) <= JumpEarlyTime) && ((DateTime.Now - _lastOnFloor) <= JumpCoyoteTime)) { - Velocity.y = -JumpVelocity; + _player.Velocity -= new Vector2(0, JumpVelocity); _jumpPressed = null; _lastOnFloor = null; } } - - [Puppet] - public void ResetPosition(Vector2 position) - { - Position = position; - Velocity = Vector2.Zero; - } } diff --git a/src/Scenes/Client.cs b/src/Scenes/Client.cs index 8d0808a..589de4c 100644 --- a/src/Scenes/Client.cs +++ b/src/Scenes/Client.cs @@ -12,15 +12,15 @@ public class Client : Game public NetworkedMultiplayerENet Peer => (NetworkedMultiplayerENet)Multiplayer.NetworkPeer; public ConnectionStatus Status => Peer?.GetConnectionStatus() ?? ConnectionStatus.Disconnected; - public LocalPlayer LocalPlayer => (LocalPlayer)this.GetWorld().GetPlayer(GetTree().GetNetworkUniqueId()); - private LocalPlayer _storedLocalPlayer; + public Player LocalPlayer => this.GetWorld().GetPlayer(GetTree().GetNetworkUniqueId()); + private Player _storedLocalPlayer; public event Action Connected; public event Action Disconnected; - public event Action LocalPlayerSpawned; + public event Action LocalPlayerSpawned; public event Action StatusChanged; - internal void FireLocalPlayerSpawned(LocalPlayer player) + internal void FireLocalPlayerSpawned(Player player) => LocalPlayerSpawned?.Invoke(player); @@ -57,9 +57,9 @@ public class Client : Game if (IntegratedServer.Server.IsRunning) { foreach (var player in this.GetWorld().Players) { // Store the local player for later restoration, but remove it from the scene. - if (player is LocalPlayer localPlayer) { - _storedLocalPlayer = localPlayer; - localPlayer.GetParent().RemoveChild(localPlayer); + if (player.IsLocal) { + _storedLocalPlayer = player; + player.GetParent().RemoveChild(player); // Do NOT call QueueFree - like RemoveFromParent does. } else player.RemoveFromParent(); } diff --git a/src/World.cs b/src/World.cs index 2d00690..b2cc960 100644 --- a/src/World.cs +++ b/src/World.cs @@ -61,14 +61,16 @@ public class World : Node [PuppetSync] public void SpawnPlayer(int networkID, Vector2 position) { - var isLocal = networkID == GetTree().GetNetworkUniqueId(); - var player = (isLocal ? LOCAL_PLAYER : PLAYER).Init(); + var player = SceneCache.Instance(); player.NetworkID = networkID; player.Position = position; PlayerContainer.AddChild(player); - if (player is LocalPlayer localPlayer) - this.GetClient().FireLocalPlayerSpawned(localPlayer); + if (player.IsLocal) { + player.AddChild(new PlayerMovement { Name = "PlayerMovement" }); + player.AddChild(new Camera2D { Name = "Camera", Current = true }); + this.GetClient().FireLocalPlayerSpawned(player); + } if (this.GetGame() is Server) { player.VisibilityTracker.ChunkTracked += (chunkPos) => {