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 Label DebugTextLabel { get; set; } [Export] public AnimatedSprite2D Sprite { get; set; } [Export] public float Gravity { get; set; } = 30.625f; private float TilesToMeters => 9.81f / Gravity; [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 CalculateRunDrag(float delta) { return 0.5f * DragCoefficient * (Mathf.Pow(TilesToMeters, 3) * AirDensity) * (Mathf.Pow(TilesToMeters, 2) * DragArea) * Mathf.Pow(Velocity.X * TilesToMeters, 2) * _invmass * -Mathf.Sign(Velocity.X) * delta * (1f / TilesToMeters); } 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 maxOpposingVel = -(Velocity.X * 0.99f / UnitToPx); float velx = (Velocity.X / UnitToPx) + Mathf.Clamp(CalculateRunDrag((float)delta), Math.Min(maxOpposingVel, -maxOpposingVel), Math.Max(maxOpposingVel, -maxOpposingVel)); float vely = (Velocity.Y / UnitToPx) + Gravity * (float)delta; if (Mathf.Abs(velx) < 0.5) 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 : {CalculateRunDrag((float)delta)}"; // Force = new Vector2(0, forcey); Velocity = new Vector2(velx, vely) * UnitToPx; MoveAndSlide(); } }