Add "ghost" bullets & networking

main
copygirl 4 years ago
parent e835057241
commit a974149c9e
  1. 16
      scene/Player.tscn
  2. 45
      src/Items/Weapon.cs
  3. 72
      src/Objects/Bullet.cs
  4. 14
      src/Utility/Extensions.cs

@ -68,10 +68,11 @@ texture = ExtResource( 7 )
offset = Vector2( 8, 0 ) offset = Vector2( 8, 0 )
script = ExtResource( 8 ) script = ExtResource( 8 )
Knockback = 50.0 Knockback = 50.0
Spread = 3.0 Spread = 1.5
SpreadIncrease = 1.0 SpreadIncrease = 1.0
RecoilMin = 3.0 RecoilMin = 3.0
RecoilMax = 5.0 RecoilMax = 5.0
BulletSpeed = 1200
[node name="Tip" type="Node2D" parent="Items/Revolver"] [node name="Tip" type="Node2D" parent="Items/Revolver"]
position = Vector2( 15, -2.5 ) position = Vector2( 15, -2.5 )
@ -87,10 +88,13 @@ script = ExtResource( 8 )
EffectiveRange = 240 EffectiveRange = 240
MaximumRange = 360 MaximumRange = 360
Knockback = 135.0 Knockback = 135.0
Spread = 14.0 Spread = 8.0
SpreadIncrease = 20.0 SpreadIncrease = 10.0
RecoilMin = 6.0 RecoilMin = 6.0
RecoilMax = 12.0 RecoilMax = 12.0
BulletSpeed = 1000
BulletsPetShot = 8
BulletOpacity = 0.1
[node name="Tip" type="Node2D" parent="Items/Shotgun"] [node name="Tip" type="Node2D" parent="Items/Shotgun"]
position = Vector2( 22, -1.5 ) position = Vector2( 22, -1.5 )
@ -109,6 +113,8 @@ Knockback = 100.0
SpreadIncrease = 2.0 SpreadIncrease = 2.0
RecoilMin = 8.0 RecoilMin = 8.0
RecoilMax = 8.0 RecoilMax = 8.0
BulletSpeed = 4000
BulletOpacity = 0.4
[node name="Tip" type="Node2D" parent="Items/Rifle"] [node name="Tip" type="Node2D" parent="Items/Rifle"]
position = Vector2( 24, -1.5 ) position = Vector2( 24, -1.5 )
@ -122,8 +128,8 @@ texture = ExtResource( 12 )
offset = Vector2( 8, 0 ) offset = Vector2( 8, 0 )
script = ExtResource( 8 ) script = ExtResource( 8 )
Knockback = 30.0 Knockback = 30.0
Spread = 1.0 Spread = 0.6
SpreadIncrease = 1.2 SpreadIncrease = 0.8
RecoilMin = 1.0 RecoilMin = 1.0
RecoilMax = 2.5 RecoilMax = 2.5

@ -13,6 +13,10 @@ public class Weapon : Sprite
[Export] public float RecoilMin { get; set; } = 0.0F; [Export] public float RecoilMin { get; set; } = 0.0F;
[Export] public float RecoilMax { get; set; } = 0.0F; [Export] public float RecoilMax { get; set; } = 0.0F;
[Export] public int BulletSpeed { get; set; } = 2000;
[Export] public int BulletsPetShot { get; set; } = 1;
[Export] public float BulletOpacity { get; set; } = 0.2F;
public Cursor Cursor { get; private set; } public Cursor Cursor { get; private set; }
public Player Player { get; private set; } public Player Player { get; private set; }
@ -34,14 +38,45 @@ public class Weapon : Sprite
if (!(Player is LocalPlayer localPlayer)) return; if (!(Player is LocalPlayer localPlayer)) return;
if (ev.IsActionPressed("interact_primary")) { if (ev.IsActionPressed("interact_primary")) {
var seed = unchecked((int)GD.Randi());
ShootInternal(AimDirection, Scale.y > 0, seed);
RpcId(1, nameof(Shoot), AimDirection, Scale.y > 0, seed);
localPlayer.Velocity -= Mathf.Polar2Cartesian(Knockback, Rotation);
}
}
[Remote]
private void Shoot(float aimDirection, bool toRight, int seed)
{
if (this.GetGame() is Server) {
if (Player.NetworkID != GetTree().GetRpcSenderId()) return;
// TODO: Verify input.
Rpc(nameof(Shoot), aimDirection, toRight, seed);
} else if (Player is LocalPlayer) return;
ShootInternal(aimDirection, toRight, seed);
}
private void ShootInternal(float aimDirection, bool toRight, int seed)
{
if (this.GetGame() is Client)
GetNodeOrNull<AudioStreamPlayer2D>("Fire")?.Play(); GetNodeOrNull<AudioStreamPlayer2D>("Fire")?.Play();
// TODO: Spawn bullet or something.
// TODO: Tell server (and other clients) we shot.
_currentSpreadInc += Mathf.Deg2Rad(SpreadIncrease);
_currentRecoil += Mathf.Deg2Rad((float)GD.RandRange(RecoilMin, RecoilMax));
localPlayer.Velocity -= Mathf.Polar2Cartesian(Knockback, Rotation); var random = new Random(seed);
var angle = aimDirection - _currentRecoil * (toRight ? 1 : -1);
var tip = GetNode<Node2D>("Tip").Position;
if (!toRight) tip.y *= -1;
tip = tip.Rotated(angle);
for (var i = 0; i < BulletsPetShot; i++) {
var spread = (Mathf.Deg2Rad(Spread) + _currentSpreadInc) * Mathf.Clamp(random.NextGaussian(0.4F), -1, 1);
var dir = Mathf.Polar2Cartesian(1, angle + spread);
var color = new Color(Player.Color, BulletOpacity);
var bullet = new Bullet(Player.Position + tip, dir, EffectiveRange, MaximumRange, BulletSpeed, color);
this.GetWorld().AddChild(bullet);
} }
_currentSpreadInc += Mathf.Deg2Rad(SpreadIncrease);
_currentRecoil += Mathf.Deg2Rad(random.NextFloat(RecoilMin, RecoilMax));
} }
public override void _Process(float delta) public override void _Process(float delta)

@ -0,0 +1,72 @@
using System;
using Godot;
public class Bullet : Node2D
{
private static readonly TimeSpan TRAIL_DURATION = TimeSpan.FromSeconds(0.6);
public Vector2 Direction { get; }
public int EffectiveRange { get; }
public int MaximumRange { get; }
public int Speed { get; }
public Color Color { get; }
private readonly Vector2 _startPosition;
private TimeSpan _age;
private float _distance;
public Bullet(Vector2 position, Vector2 direction,
int effectiveRange, int maximumRange, int speed, Color color)
{
_startPosition = Position = position;
Direction = direction;
EffectiveRange = effectiveRange;
MaximumRange = maximumRange;
Speed = speed;
Color = color;
}
public override void _Ready()
{ if (this.GetGame() is Server) Visible = false; }
public override void _Process(float delta)
{
_age += TimeSpan.FromSeconds(delta);
if (_distance < MaximumRange) {
_distance = Mathf.Min(MaximumRange, Speed * (float)_age.TotalSeconds);
Position = _startPosition + Direction * _distance;
Update();
}
if (_age > TRAIL_DURATION) {
Modulate = new Color(Modulate, Modulate.a - delta * 2);
if (Modulate.a <= 0) this.RemoveFromParent();
}
}
public override void _Draw()
{
var numPoints = 2
+ ((_distance > 16) ? 1 : 0)
+ ((_distance > EffectiveRange) ? 1 : 0);
var points = new Vector2[numPoints];
var colors = new Color[numPoints];
if (_distance > 16)
colors[0] = new Color(Color, Color.a * Mathf.Min(1.0F, 1.0F - (_distance - EffectiveRange) / (MaximumRange - EffectiveRange)));
if (_distance > EffectiveRange) {
points[1] = Direction * -(_distance - EffectiveRange);
colors[1] = Color;
}
points[points.Length - 2] = Direction * -Mathf.Max(0.0F, _distance - 16);
points[points.Length - 1] = Direction * -_distance;
colors[colors.Length - 2] = Color;
colors[colors.Length - 1] = new Color(Color, 0.0F);
DrawPolylineColors(points, colors, 1.5F, true);
}
}

@ -1,3 +1,4 @@
using System;
using Godot; using Godot;
public static class Extensions public static class Extensions
@ -24,6 +25,19 @@ public static class Extensions
node.GetParent().RemoveChild(node); node.GetParent().RemoveChild(node);
node.QueueFree(); node.QueueFree();
} }
public static float NextFloat(this Random random)
=> (float)random.NextDouble();
public static float NextFloat(this Random random, float min, float max)
=> min + NextFloat(random) * (max - min);
public static float NextGaussian(this Random random, float stdDev = 1.0F, float mean = 0.0F)
{
var u1 = 1.0F - random.NextFloat();
var u2 = 1.0F - random.NextFloat();
var normal = Mathf.Sqrt(-2.0F * Mathf.Log(u1)) * Mathf.Sin(2.0F * Mathf.Pi * u2);
return mean + stdDev * normal;
}
} }
public interface IInitializable public interface IInitializable

Loading…
Cancel
Save