Advertisement
learnerdev

Character controller

Apr 28th, 2025
178
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 8.48 KB | Source Code | 0 0
  1. using Godot;
  2. using System;
  3.  
  4. public partial class PlayerController : CharacterBody2D
  5. {
  6.     // --- Movement ---
  7.     [Export] public float MoveSpeed = 200f;
  8.     [Export] public float Gravity = 1200f;
  9.     [Export] public float MaxFallSpeed = 1000f;
  10.     [Export] public float FastFallSpeed = 1500f;
  11.  
  12.     // --- Jump ---
  13.     [Export] public float JumpVelocity = -400f;
  14.     [Export] public float JumpCutMultiplier = 0.5f; // How much to cut jump if button released early
  15.     [Export] public int MaxAirJumps = 1;
  16.     private int remainingAirJumps;
  17.  
  18.     // --- Coyote Time / Jump Buffer ---
  19.     [Export] public float CoyoteTime = 0.15f;
  20.     private float coyoteTimer = 0f;
  21.     [Export] public float JumpBufferTime = 0.15f;
  22.     private float jumpBufferTimer = 0f;
  23.  
  24.     // --- Dash ---
  25.     [Export] public float DashSpeed = 600f;
  26.     [Export] public float DashTime = 0.2f;
  27.     [Export] public float DashCooldown = 0.5f;
  28.     [Export] public float DashInvincibilityTime = 0.2f;
  29.     private bool isDashing = false;
  30.     private bool hasDashed = false;
  31.     private float dashTimer = 0f;
  32.     private float dashCooldownTimer = 0f;
  33.     private Vector2 dashDirection = Vector2.Zero;
  34.  
  35.     // --- Wall ---
  36.     [Export] public float WallSlideSpeed = 100f;
  37.     [Export] public float WallJumpVelocityX = 250f;
  38.     [Export] public float WallJumpVelocityY = -400f;
  39.     private bool isWallSliding = false;
  40.     private int wallDirection = 0;
  41.  
  42.     // --- FX ---
  43.     private Timer trailTimer;
  44.     [Export] public PackedScene DashTrailScene;
  45.     [Export] public PackedScene DoubleJumpParticlesScene;
  46.  
  47.     // --- Hurtbox ---
  48.     private bool isInvincible = false;
  49.     private float invincibilityTimer = 0f;
  50.  
  51.     private Vector2 velocity = Vector2.Zero;
  52.  
  53.     public override void _Ready()
  54.     {
  55.         trailTimer = new Timer();
  56.         trailTimer.WaitTime = 0.05f;
  57.         trailTimer.OneShot = false;
  58.         trailTimer.Timeout += SpawnTrail;
  59.         AddChild(trailTimer);
  60.     }
  61.  
  62.     public override void _PhysicsProcess(double delta)
  63.     {
  64.         float dt = (float)delta;
  65.         HandleTimers(dt);
  66.  
  67.         if (!isDashing)
  68.         {
  69.             HandleInput(dt);
  70.             ApplyGravity(dt);
  71.             HandleWallSlide();
  72.             HandleJumpBuffer();
  73.             HandleFastFall();
  74.         }
  75.         else
  76.         {
  77.             velocity = dashDirection * DashSpeed;
  78.             dashTimer -= dt;
  79.             if (dashTimer <= 0)
  80.             {
  81.                 isDashing = false;
  82.                 isInvincible = false;
  83.                 trailTimer.Stop();
  84.             }
  85.         }
  86.  
  87.         HandleDash(dt);
  88.         MoveAndSlide();
  89.     }
  90.  
  91.     private void HandleTimers(float dt)
  92.     {
  93.         if (IsOnFloor())
  94.         {
  95.             coyoteTimer = CoyoteTime;
  96.             hasDashed = false;
  97.             remainingAirJumps = MaxAirJumps;
  98.         }
  99.         else
  100.         {
  101.             coyoteTimer -= dt;
  102.         }
  103.  
  104.         if (jumpBufferTimer > 0)
  105.             jumpBufferTimer -= dt;
  106.  
  107.         if (invincibilityTimer > 0)
  108.             invincibilityTimer -= dt;
  109.     }
  110.  
  111.     private void HandleInput(float dt)
  112.     {
  113.         Vector2 input = new Vector2(
  114.             Input.GetActionStrength("ui_right") - Input.GetActionStrength("ui_left"),
  115.             0
  116.         );
  117.         velocity.X = input.X * MoveSpeed;
  118.     }
  119.  
  120.     private void ApplyGravity(float dt)
  121.     {
  122.         if (!IsOnFloor() && !isWallSliding)
  123.         {
  124.             velocity.Y += Gravity * dt;
  125.             if (velocity.Y > MaxFallSpeed)
  126.                 velocity.Y = MaxFallSpeed;
  127.         }
  128.     }
  129.  
  130.     private void HandleJumpBuffer()
  131.     {
  132.         if (Input.IsActionJustPressed("jump"))
  133.         {
  134.             jumpBufferTimer = JumpBufferTime;
  135.         }
  136.  
  137.         if (jumpBufferTimer > 0)
  138.         {
  139.             if (coyoteTimer > 0)
  140.             {
  141.                 Jump();
  142.             }
  143.             else if (isWallSliding)
  144.             {
  145.                 WallJump();
  146.             }
  147.             else if (remainingAirJumps > 0)
  148.             {
  149.                 DoubleJump();
  150.             }
  151.         }
  152.  
  153.         // Variable jump height
  154.         if (Input.IsActionJustReleased("jump") && velocity.Y < 0)
  155.         {
  156.             velocity.Y *= JumpCutMultiplier;
  157.         }
  158.     }
  159.  
  160.     private void Jump()
  161.     {
  162.         velocity.Y = JumpVelocity;
  163.         jumpBufferTimer = 0;
  164.         coyoteTimer = 0;
  165.     }
  166.  
  167.     private void DoubleJump()
  168.     {
  169.         velocity.Y = JumpVelocity;
  170.         remainingAirJumps--;
  171.         jumpBufferTimer = 0;
  172.         SpawnDoubleJumpParticles();
  173.     }
  174.  
  175.     private void WallJump()
  176.     {
  177.         velocity.X = -wallDirection * WallJumpVelocityX;
  178.         velocity.Y = WallJumpVelocityY;
  179.         isWallSliding = false;
  180.         jumpBufferTimer = 0;
  181.     }
  182.  
  183.     private void HandleFastFall()
  184.     {
  185.         if (Input.IsActionPressed("ui_down") && !IsOnFloor())
  186.         {
  187.             velocity.Y = Mathf.Min(velocity.Y + Gravity * 1.5f * (float)GetPhysicsProcessDeltaTime(), FastFallSpeed);
  188.         }
  189.     }
  190.  
  191.     private void HandleDash(float dt)
  192.     {
  193.         if (dashCooldownTimer > 0)
  194.             dashCooldownTimer -= dt;
  195.  
  196.         if (Input.IsActionJustPressed("dash") && !isDashing && !hasDashed && dashCooldownTimer <= 0)
  197.         {
  198.             StartDash();
  199.         }
  200.     }
  201.  
  202.     private void StartDash()
  203.     {
  204.         isDashing = true;
  205.         dashTimer = DashTime;
  206.         dashCooldownTimer = DashCooldown;
  207.         hasDashed = true;
  208.         isInvincible = true;
  209.         invincibilityTimer = DashInvincibilityTime;
  210.  
  211.         Vector2 input = new Vector2(
  212.             Input.GetActionStrength("ui_right") - Input.GetActionStrength("ui_left"),
  213.             Input.GetActionStrength("ui_down") - Input.GetActionStrength("ui_up")
  214.         );
  215.  
  216.         dashDirection = Get8Direction(input);
  217.  
  218.         if (dashDirection == Vector2.Zero)
  219.         {
  220.             dashDirection = new Vector2(FacingRight() ? 1 : -1, 0);
  221.         }
  222.  
  223.         trailTimer.Start();
  224.     }
  225.  
  226.     private void HandleWallSlide()
  227.     {
  228.         wallDirection = 0;
  229.         isWallSliding = false;
  230.  
  231.         if (!IsOnFloor() && Velocity.Y > 0)
  232.         {
  233.             if (IsOnWall())
  234.             {
  235.                 wallDirection = GetWallNormal().X > 0 ? -1 : 1;
  236.                 isWallSliding = true;
  237.                 velocity.Y = WallSlideSpeed;
  238.             }
  239.         }
  240.     }
  241.  
  242.     private Vector2 GetWallNormal()
  243.     {
  244.         for (int i = 0; i < GetSlideCollisionCount(); i++)
  245.         {
  246.             var col = GetSlideCollision(i);
  247.             if (Mathf.Abs(col.Normal.X) > 0.9)
  248.             {
  249.                 return col.Normal;
  250.             }
  251.         }
  252.         return Vector2.Zero;
  253.     }
  254.  
  255.     private bool FacingRight()
  256.     {
  257.         return Input.GetActionStrength("ui_right") >= Input.GetActionStrength("ui_left");
  258.     }
  259.  
  260.     private Vector2 Get8Direction(Vector2 input)
  261.     {
  262.         if (input == Vector2.Zero)
  263.             return Vector2.Zero;
  264.  
  265.         input = input.Normalized();
  266.         float angle = Mathf.RadToDeg(Mathf.Atan2(input.Y, input.X));
  267.         angle = (angle + 360f) % 360f;
  268.  
  269.         if (angle >= 337.5f || angle < 22.5f) return Vector2.Right;
  270.         if (angle >= 22.5f && angle < 67.5f) return new Vector2(1, -1).Normalized();
  271.         if (angle >= 67.5f && angle < 112.5f) return Vector2.Up;
  272.         if (angle >= 112.5f && angle < 157.5f) return new Vector2(-1, -1).Normalized();
  273.         if (angle >= 157.5f && angle < 202.5f) return Vector2.Left;
  274.         if (angle >= 202.5f && angle < 247.5f) return new Vector2(-1, 1).Normalized();
  275.         if (angle >= 247.5f && angle < 292.5f) return Vector2.Down;
  276.         if (angle >= 292.5f && angle < 337.5f) return new Vector2(1, 1).Normalized();
  277.  
  278.         return Vector2.Zero;
  279.     }
  280.  
  281.     private void SpawnTrail()
  282.     {
  283.         if (DashTrailScene == null) return;
  284.  
  285.         Node2D trail = (Node2D)DashTrailScene.Instantiate();
  286.         trail.GlobalPosition = GlobalPosition;
  287.         GetParent().AddChild(trail);
  288.     }
  289.  
  290.     private void SpawnDoubleJumpParticles()
  291.     {
  292.         if (DoubleJumpParticlesScene == null) return;
  293.  
  294.         Node2D particles = (Node2D)DoubleJumpParticlesScene.Instantiate();
  295.         particles.GlobalPosition = GlobalPosition;
  296.         GetParent().AddChild(particles);
  297.     }
  298.  
  299.     public void TakeDamage(int amount)
  300.     {
  301.         if (isInvincible)
  302.         {
  303.             GD.Print("Damage ignored (invincible!)");
  304.             return;
  305.         }
  306.  
  307.         GD.Print("Player took " + amount + " damage!");
  308.         // TODO: Add health reduction
  309.     }
  310. }
  311.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement