using Godot; using System; using System.Diagnostics; public partial class PlayerPhysicsController : CharacterBody2D { [Export] public float FloatingWalkAcceleration { get; set; } = 1f; [Export] public float RunMultiplier { get; set; } = 1.84f; [Export] public float WalkAcceleration { get; set; } = 3f; [Export] public float JumpHeight { get; set; } = 5.5f; private float PeakTime { get; set; } = 0.1f; [Export] public float DragArea { get; set; } = 4f; [Export] public float AirDensity { get; set; } = 37.26982f; [Export] public float DragCoefficient { get; set; } = 1.3f; [Export] public float FloatingDragCoefficientMultiplier { get; set; } = 0.8f; [Export] public Label DebugTextLabel { get; set; } [Export] public AnimatedSprite2D Sprite { get; set; } [Export] public float Gravity { get; set; } = 30.625f; private float TilesToMeters => 9.81f / Gravity; private float PxToMeters => (1 / UnitToPx) * TilesToMeters; [Export] public float UnitToPx { get; set; } = 32; private float _lv = 0.0f; private float _invmass = 1/2f; [Export] public float Mass { get => 1/_invmass; set => _invmass = 1/value; } private float epsilon = 10e-5f; private void RecalculateLv() { _lv = Mathf.Sqrt(Mathf.Pow(Gravity * PeakTime / 2, 2) + 2 * Gravity * JumpHeight); } private float CalculateDrag(float velocity, float delta) { var minopposing = Mathf.Abs(velocity * 0.99f); return Mathf.Clamp( 0.5f * (this.IsOnFloor() ? DragCoefficient : DragCoefficient * FloatingDragCoefficientMultiplier) * (Mathf.Pow(TilesToMeters, 3) * AirDensity) * (Mathf.Pow(TilesToMeters, 2) * DragArea) * Mathf.Pow(velocity * PxToMeters, 2) * _invmass * -Mathf.Sign(velocity) * delta * (1f / TilesToMeters), -minopposing, minopposing); } public override void _Ready() { RecalculateLv(); base._Ready(); } public override void _Process(double delta) { var grounded = this.IsOnFloor(); var absvel = new Vector2(Mathf.Abs(Velocity.X), Mathf.Abs(Velocity.Y)); Sprite.FlipH = Velocity.X < 0; if (grounded && absvel.X > 0) { Sprite.Play("walk", absvel.X switch { < 10 => 1, < 150 => 2, < 300 => 4, _ => 1 }); } else if (grounded && absvel.X < epsilon && absvel.Y < epsilon) { Sprite.Play("idle"); } } private float _timeOffGround = 0.0f; [Export] public float CoyoteTiming { get; set; } = 0.1f; public override void _PhysicsProcess(double delta) { var xAxis = Input.GetAxis("move_left", "move_right"); var yAxis = Input.GetAxis("look_up", "look_down"); // var jumpStrength = -Input.GetActionStrength("jump"); var lastCollision = this.IsOnCeiling(); RecalculateLv(); float velx = (Velocity.X / UnitToPx) + CalculateDrag(Velocity.X, (float)delta); float vely = (Velocity.Y / UnitToPx) + Gravity * (float)delta + CalculateDrag(Velocity.Y, (float)delta); if (Mathf.Abs(velx) < 0.3) velx = 0; var grounded = this.IsOnFloor(); var coyoteGrounded = _timeOffGround < CoyoteTiming; if (grounded || coyoteGrounded) { if (grounded) _timeOffGround = 0; else _timeOffGround += (float)delta; if (Input.IsActionJustPressed("jump")) { vely = -_lv; } if (!grounded && coyoteGrounded && Input.IsActionJustReleased("jump") && vely < 0) { vely *= 1/4f; } if (Input.IsActionPressed("run")) { velx += xAxis * WalkAcceleration * (float)delta * RunMultiplier; } else { velx += xAxis * WalkAcceleration * (float)delta; } } else { if (Input.IsActionJustReleased("jump") && vely < 0) { vely *= 1/4f; } if (Input.IsActionPressed("run")) { velx += xAxis * FloatingWalkAcceleration * (float)delta * RunMultiplier; } else { velx += xAxis * FloatingWalkAcceleration * (float)delta; } } DebugTextLabel.Text = $"grounded: {grounded}\n" + $"vely: {vely}\n" + $"velx: {velx}\n" + $"lv : {_lv:F2} or {_lv/(float)delta:F2}\n" + $"rd : {CalculateDrag(Velocity.X, (float)delta):F2}, {CalculateDrag(Velocity.Y, (float)delta):F2}"; // Force = new Vector2(0, forcey); Velocity = new Vector2(velx, vely) * UnitToPx; MoveAndSlide(); } }