Last active
October 10, 2019 00:13
-
-
Save antopilo/e359f2e9f3b811721b352ea143b3a9b2 to your computer and use it in GitHub Desktop.
Player.cs class in my game Robin.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Godot; | |
using System; | |
using System.Collections.Generic; | |
public class Player : KinematicBody2D | |
{ | |
public CollisionShape2D CollisionBox { get; private set; } | |
public AnimatedSprite Sprite { get; private set; } | |
public Camera Camera { get; private set; } | |
public Arrow Arrow { get; set; } | |
private Particles2D RunDust; | |
private const int GRAVITY = 4; | |
private const int ACCELERATION = 5; | |
private const int DECELERATION = 4; | |
private const float JUMP_TIMING_TOLERANCE = 0.1f; | |
private const int JUMP_FORCE = 190; | |
private const int SUPER_JUMP_FORCE = 220; | |
private const int WALL_JUMP_HEIGHT = 140; | |
private const int WALL_JUMP_FORCE = 120; | |
private const int JUMP_PAD_FORCE = 260; | |
private const int MAX_SPEED = 90; | |
private const int MAX_AIR_SPEED = 100; | |
private const int MAX_FALL_SPEED = 300; | |
private float NextJumpTime = 0f; | |
private float CurrentMaxSpeed = MAX_SPEED; | |
private float GravityMult = 1f; | |
public Vector2 Velocity = new Vector2(); | |
public bool CanControl = true; | |
public int LastDirectionX = 0; | |
private int InputDirectionX = 0; | |
private int InputDirectionY = 0; // For later use maybe. | |
public States State { get; private set; } | |
public bool Alive = true; | |
public bool IsCrouching { get; private set; } = false; | |
public bool IsWallJumping { get; private set; } = false; | |
public bool WasOnGround { get; private set; } = false; | |
public bool CanWallJump { get; private set; } = false; | |
public bool CanJump { get; private set; } = false; | |
public bool ArrowExist { get; set; } = false; | |
public bool IsCeilling { get; private set; } = false; | |
public Particles2D RunDust1, WallJumpDust; | |
private float DeltaTime = 0; | |
//SFX | |
[Export] float FootStepRate = 0f; | |
float FootStepTimer = 0; | |
public List<Node2D> Following = new List<Node2D>(); // List of following entities. | |
// Init. | |
public override void _Ready() | |
{ | |
base._Ready(); | |
CollisionBox = (CollisionShape2D)GetNode("Collision"); | |
Sprite = (AnimatedSprite)GetNode("AnimatedSprite"); | |
Camera = (Camera)GetNode("Camera2D"); | |
RunDust = (Particles2D)GetNode("Particles/RunDust"); | |
WallJumpDust = (Particles2D)GetNode("Particles/WallJump"); | |
} | |
// Jumping. | |
public override void _Input(InputEvent e) | |
{ | |
if (Root.Console.Visible) | |
return; | |
if (e.IsActionPressed("jump") && CanControl) | |
if (State == States.Ground || (DeltaTime <= NextJumpTime && CanJump)) | |
{ | |
CanJump = false; | |
WasOnGround = true; | |
// Jump sound. | |
//(GetNode("SFX/Jump") as AudioStreamPlayer).Play(0); | |
if (IsCrouching) | |
SuperJump(); | |
else | |
Jump(); | |
} | |
else if (CanWallJump) | |
{ | |
WasOnGround = false; | |
WallJump(); | |
WallJumpDust.Emitting = true; | |
} | |
if (e.IsActionReleased("jump") && Velocity.y < 0 && WasOnGround) // Tap jump | |
Velocity.y /= 1.75f; | |
} | |
// Main loop. | |
public override void _PhysicsProcess(float delta) | |
{ | |
GetInput(); | |
UpdateVelocity(); | |
UpdateState(); | |
CanWalljump(); | |
SpeedLimits(); | |
Sounds(delta); // Handles player sounds | |
Particles(); // Handles player particles | |
MoveAndSlide(Velocity); | |
ApplyGravity(); | |
GetArrow(); | |
DeltaTime += delta; | |
FootStepTimer += delta; | |
} | |
/// <summary> | |
/// Update the direction of the player with the input. | |
/// Must be called every frame. | |
/// Also plays the animation. | |
/// </summary> | |
private void GetInput() | |
{ | |
// Input disabling | |
if (!CanControl || !Alive || Root.Console.Visible) | |
{ | |
InputDirectionX = 0; | |
InputDirectionY = 0; | |
return; | |
} | |
// Horizontal Inputs | |
if (Godot.Input.IsActionPressed("Left")) | |
{ | |
Sprite.Play("Running"); | |
Sprite.FlipH = true; | |
InputDirectionX = -1; | |
LastDirectionX = -1; | |
} | |
else if (Godot.Input.IsActionPressed("Right")) | |
{ | |
Sprite.Play("Running"); | |
Sprite.FlipH = false; | |
InputDirectionX = 1; | |
LastDirectionX = 1; | |
} | |
else | |
{ | |
InputDirectionX = 0; | |
if (State == States.Ground) | |
Sprite.Play("idle"); | |
} | |
// Looking up | |
if (Godot.Input.IsActionPressed("Up")) | |
InputDirectionY = -1; | |
else | |
InputDirectionY = 0; | |
// Crouching | |
if (Godot.Input.IsActionPressed("Down") && State == States.Ground) | |
{ | |
IsCrouching = true; | |
InputDirectionY = 1; | |
if (InputDirectionX == 0) | |
Sprite.Play("Crouch"); | |
} | |
else | |
{ | |
InputDirectionY = 0; | |
IsCrouching = false; | |
} | |
} | |
private void Particles() | |
{ | |
WallJumpDust.Emitting = IsWallJumping; | |
RunDust.Emitting = InputDirectionX != 0 && State == States.Ground; | |
} | |
private void Sounds(float delta) | |
{ | |
if (InputDirectionX != 0 && State == States.Ground && FootStepTimer >= 0.125) | |
{ | |
(GetNode("SFX/Footstep") as AudioStreamPlayer).Play(0); | |
FootStepTimer = 0; | |
} | |
} | |
#region States | |
/// <summary> | |
/// Decide what state is the player in. See States.cs for all of the possible states. | |
/// </summary> | |
private void UpdateState() | |
{ | |
var CollisionCount = GetSlideCount() - 1; | |
if (CollisionCount <= -1) | |
{ | |
EnterAirState(); | |
return; | |
} | |
var Collision = GetSlideCollision(CollisionCount); | |
var NormalGround = new Vector2(0, -1); | |
var NormalLeft = new Vector2(1, 0); | |
var NormalRight = new Vector2(-1, 0); | |
var NormalCeiling = new Vector2(0, 1); | |
if (Collision.Normal == NormalGround && State != States.Ground) | |
EnterGroundState(); | |
if (Collision.Normal == NormalLeft || Collision.Normal == NormalRight) | |
EnterWallState(Collision.Normal == NormalLeft ? 1 : 0); | |
if (Collision.Normal == NormalCeiling) | |
Velocity.y = -Velocity.y / 4; | |
} | |
public void EnterGroundState() // Ground state. | |
{ | |
if (State == States.Air) | |
Velocity.y = 3; | |
State = States.Ground; | |
CurrentMaxSpeed = MAX_SPEED; | |
CanControl = true; | |
CanJump = true; | |
IsCeilling = false; | |
} | |
public void EnterWallState(int direction) // Wall State | |
{ | |
State = States.Wall; | |
GravityMult = 1; | |
if (State == States.Ground) | |
CanWallJump = true; | |
IsCeilling = false; | |
if (Velocity.y > 0) | |
{ | |
Velocity.y /= 1.1f; | |
Sprite.Play("Wall"); | |
Sprite.FlipH = Convert.ToBoolean(direction); | |
} | |
else | |
{ | |
Sprite.Play("falling"); | |
} | |
} | |
public void EnterAirState() // Air State. | |
{ | |
if (State != States.Air) | |
NextJumpTime = DeltaTime + JUMP_TIMING_TOLERANCE; | |
if (Velocity.y > 0) | |
Sprite.Play("falling"); | |
else | |
Sprite.Play("Jump"); | |
State = States.Air; | |
} | |
#endregion | |
#region Physics | |
/// <summary> | |
/// Pretty much just adds the acceleration. | |
/// </summary> | |
private void UpdateVelocity() | |
=> Velocity.x += InputDirectionX * ACCELERATION; | |
/// <summary> | |
/// Apply Constant force on the player. if the player is on the ground. Stop the gravity. | |
/// </summary> | |
private void ApplyGravity() | |
{ | |
if (State == States.Ground) | |
GravityMult = 0; | |
else | |
GravityMult = 1; | |
Velocity.y += GRAVITY * GravityMult; | |
} | |
/// <summary> | |
/// Define speed limits. | |
/// </summary> | |
private void SpeedLimits() | |
{ | |
if (IsWallJumping && Mathf.Abs(Velocity.x) > MAX_AIR_SPEED) | |
Velocity.x = MAX_AIR_SPEED * Mathf.Sign(Velocity.x); | |
if (Mathf.Abs(Velocity.x) > CurrentMaxSpeed) | |
Velocity.x = MAX_SPEED * Mathf.Sign(Velocity.x); | |
if (Mathf.Abs(Velocity.y) > MAX_FALL_SPEED) | |
Velocity.y = MAX_FALL_SPEED * Mathf.Sign(Velocity.y); | |
if (InputDirectionX == 0 && CanControl) | |
Velocity.x -= DECELERATION * Mathf.Sign(Velocity.x); | |
if (Mathf.Abs(Velocity.x) < 10 && InputDirectionX == 0) | |
Velocity.x = 0; | |
} | |
#endregion | |
#region Abilities | |
/// <summary> | |
/// Decide if the player can Wall jump or not. | |
/// </summary> | |
private void CanWalljump() | |
{ | |
var Left = (RayCast2D)GetNode("Raycasts/Left"); | |
var Right = (RayCast2D)GetNode("Raycasts/Right"); | |
CanWallJump = (Left.IsColliding() || Right.IsColliding()) || State == States.Wall; | |
} | |
/// <summary> | |
/// Normal jump | |
/// </summary> | |
public void Jump() | |
{ | |
CurrentMaxSpeed = MAX_SPEED; | |
Velocity.y = -JUMP_FORCE; | |
} | |
/// <summary> | |
/// Crouch jump | |
/// </summary> | |
public void SuperJump() | |
{ | |
CurrentMaxSpeed = MAX_SPEED; | |
Velocity.y = -SUPER_JUMP_FORCE; | |
} | |
/// <summary> | |
/// Wall jump | |
/// </summary> | |
public void WallJump() | |
{ | |
var JumpDirection = 0; | |
IsWallJumping = true; | |
CanControl = false; | |
CurrentMaxSpeed = MAX_AIR_SPEED; | |
(GetNode("Timers/DisableInput") as Timer).Start(); | |
var CollisionCount = GetSlideCount() - 1; | |
if (CollisionCount > -1) | |
{ | |
var Collision = GetSlideCollision(CollisionCount); | |
if (Collision.Normal == new Vector2(1, 0)) | |
{ | |
JumpDirection = 1; | |
Sprite.FlipH = false; | |
} | |
else if (Collision.Normal == new Vector2(-1, 0)) | |
{ | |
JumpDirection = -1; | |
Sprite.FlipH = true; | |
} | |
} | |
else if (State != States.Ground) | |
{ | |
var RayLeft = GetNode("Raycasts/Left") as RayCast2D; | |
var RayRight = GetNode("Raycasts/Right") as RayCast2D; | |
if (RayLeft.IsColliding()) | |
{ | |
JumpDirection = 1; | |
Sprite.FlipH = false; | |
} | |
else if (RayRight.IsColliding()) | |
{ | |
JumpDirection = -1; | |
Sprite.FlipH = true; | |
} | |
} | |
Velocity.x = WALL_JUMP_FORCE * JumpDirection; | |
Velocity.y = -WALL_JUMP_HEIGHT; | |
} | |
/// <summary> | |
/// Boost the player in a specified direction. | |
/// TODO: Improve and define forces better. | |
/// </summary> | |
/// <param name="pDirection"></param> | |
public void JumpPad(Vector2 pDirection) | |
{ | |
if (pDirection.x != 0) | |
{ | |
Velocity.x = Mathf.Sign(pDirection.x) * JUMP_PAD_FORCE * 5; | |
Velocity.y = -JUMP_PAD_FORCE / 1.5f; | |
IsWallJumping = true; | |
CurrentMaxSpeed = MAX_AIR_SPEED; | |
CanControl = false; | |
(GetNode("Timers/DisableInput") as Timer).Start(); | |
} | |
else if (pDirection.y != 0) | |
{ | |
Velocity.y = Mathf.Sign(pDirection.y) * JUMP_PAD_FORCE; | |
} | |
WasOnGround = false; | |
} | |
public void Spawn(bool WithAnimation) | |
{ | |
(GetParent() as GameController).Spawn(WithAnimation); | |
Velocity = new Vector2(); | |
Camera.Shake(3f, 0.05f); | |
} | |
/// <summary> | |
/// Check every frame if there is an arrow and get it as a reference. | |
/// </summary> | |
public void GetArrow() | |
{ | |
if (HasNode("arrow")) | |
{ | |
ArrowExist = true; | |
Arrow = GetNode("../arrow") as Arrow; | |
} | |
else | |
{ | |
ArrowExist = false; | |
Arrow = null; | |
} | |
} | |
#endregion | |
private void _on_DisableInput_timeout() | |
{ | |
CanControl = true; | |
IsWallJumping = false; | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment