Skip to content

Instantly share code, notes, and snippets.

@espeon
Created July 1, 2020 02:42
Show Gist options
  • Select an option

  • Save espeon/c44e8702b28885d91fe4ac791855491f to your computer and use it in GitHub Desktop.

Select an option

Save espeon/c44e8702b28885d91fe4ac791855491f to your computer and use it in GitHub Desktop.
using Microsoft.Xna.Framework;
using Nez;
using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Nez.Textures;
using Nez.Sprites;
using Nez.Particles;
using Nez.Tiled;
namespace fnaGame.Controllers
{
public class PlayerController : Component, ITriggerListener, IUpdatable
{
public float MoveSpeed = 75f;
public float RunSpeed = 150f;
public float FallSpeed = 15f; // for going left and right while falling
public float Gravity = 900f;
public float GravityWhileDashing = 100f;
public float JumpHeight = 8 * 10;
public float JumpSpeed = 8;
public float JumpHorizontalBoost = 10f;
public float CanJump = 3;
public float DashSpeed = 8 * 10;
public bool CanInput = true;
public bool CanGravity = true;
public bool CanDash = false;
public bool CanMove = true;
public bool IsRunning = false;
public bool IsDashing = false;
public string LatestAnimation;
string[] _particleConfigs;
// the ParticleEmitter component
ParticleEmitter _particleEmitter;
// The currently load particle emitter configuration
ParticleEmitterConfig _particleEmitterConfig;
Camera _camera;
SpriteAnimator _animator;
TiledMapMover _mover;
BoxCollider _boxCollider;
TiledMapMover.CollisionState _collisionState = new TiledMapMover.CollisionState();
Vector2 _velocity;
VirtualButton _jumpInput;
VirtualButton _runInput;
VirtualButton _dashInput;
VirtualIntegerAxis _xAxisInput;
VirtualIntegerAxis _yAxisInput;
public override void OnAddedToEntity()
{
var texture = Entity.Scene.Content.LoadTexture("Content/Tilesets/caveman.png");
var sprites = Sprite.SpritesFromAtlas(texture, 32, 32);
_camera = Entity.Scene.Camera;
_boxCollider = Entity.GetComponent<BoxCollider>();
_mover = Entity.GetComponent<TiledMapMover>();
_animator = Entity.AddComponent(new SpriteAnimator(sprites[0]));
_particleConfigs = Directory.GetFiles("Content/Particles", "*.pex");
// extract the animations from the atlas. they are setup in rows with 8 columns
_animator.AddAnimation("Walk", new[]
{
sprites[0],
sprites[1],
sprites[2],
sprites[3],
sprites[4],
sprites[5]
});
_animator.AddAnimation("Run", new[]
{
sprites[8 + 0],
sprites[8 + 1],
sprites[8 + 2],
sprites[8 + 3],
sprites[8 + 4],
sprites[8 + 5],
sprites[8 + 6]
});
_animator.AddAnimation("Idle", new[]
{
sprites[16]
});
_animator.AddAnimation("Attack", new[]
{
sprites[24 + 0],
sprites[24 + 1],
sprites[24 + 2],
sprites[24 + 3]
});
_animator.AddAnimation("Death", new[]
{
sprites[40 + 0],
sprites[40 + 1],
sprites[40 + 2],
sprites[40 + 3]
});
_animator.AddAnimation("Falling", new[]
{
sprites[48]
});
_animator.AddAnimation("Hurt", new[]
{
sprites[64],
sprites[64 + 1]
});
_animator.AddAnimation("Jumping", new[]
{
sprites[72 + 0],
sprites[72 + 1],
sprites[72 + 2],
sprites[72 + 3]
});
SetupInput();
}
public override void OnRemovedFromEntity()
{
// deregister virtual input
_jumpInput.Deregister();
_xAxisInput.Deregister();
_yAxisInput.Deregister();
_dashInput.Deregister();
_runInput.Deregister();
}
// TODO: move to separate file
void LoadParticleSystem()
{
// load up the config then add a ParticleEmitter
_particleEmitterConfig = Entity.Scene.Content.LoadParticleEmitterConfig(_particleConfigs[0]);
ResetEmitter();
}
// TODO: move to separate file
void ResetEmitter()
{
// kill the ParticleEmitter if we already have one
if (_particleEmitter != null)
Entity.RemoveComponent(_particleEmitter);
_particleEmitterConfig.EmissionRate = 10000;
_particleEmitterConfig.MaxParticles = 16;
_particleEmitterConfig.BlendFuncSource = Blend.One;
_particleEmitterConfig.BlendFuncDestination = Blend.One;
_particleEmitter = Entity.AddComponent(new ParticleEmitter(_particleEmitterConfig));
_particleEmitter.CollisionConfig.Enabled = true;
_particleEmitter.CollisionConfig.Elasticity = 0.5f;
_particleEmitter.CollisionConfig.CollidesWithLayers = 1;
_particleEmitter.CollisionConfig.Gravity = new Vector2(0, Gravity);
_particleEmitter.UpdateOrder = 0;
_particleEmitter.SimulateInWorldSpace = true;
}
void SetupInput()
{
// setup input for jumping. we will allow z on the keyboard or a on the gamepad
_jumpInput = new VirtualButton();
_jumpInput.Nodes.Add(new VirtualButton.KeyboardKey(Keys.Z));
_jumpInput.Nodes.Add(new VirtualButton.GamePadButton(0, Buttons.A));
// horizontal input from dpad, left stick or keyboard left/right
_xAxisInput = new VirtualIntegerAxis();
_xAxisInput.Nodes.Add(new VirtualAxis.GamePadDpadLeftRight());
_xAxisInput.Nodes.Add(new VirtualAxis.GamePadLeftStickX());
_xAxisInput.Nodes.Add(new VirtualAxis.KeyboardKeys(VirtualInput.OverlapBehavior.TakeNewer, Keys.Left,
Keys.Right));
// vertical input of ^, used for dashing
_yAxisInput = new VirtualIntegerAxis();
_yAxisInput.Nodes.Add(new VirtualAxis.GamePadDpadUpDown());
_yAxisInput.Nodes.Add(new VirtualAxis.GamePadLeftStickY());
_yAxisInput.Nodes.Add(new VirtualAxis.KeyboardKeys(VirtualInput.OverlapBehavior.TakeNewer, Keys.Up,
Keys.Down));
// running input, left shift on keyboard and b on gamepad
_runInput = new VirtualButton();
_runInput.Nodes.Add(new VirtualButton.KeyboardKey(Keys.LeftShift));
_runInput.Nodes.Add(new VirtualButton.GamePadButton(0, Buttons.B));
//dash input, X on keyboard and X on gamepad
_dashInput = new VirtualButton();
_dashInput.Nodes.Add(new VirtualButton.KeyboardKey(Keys.X));
_dashInput.Nodes.Add(new VirtualButton.GamePadButton(0, Buttons.X));
}
void IUpdatable.Update()
{
// handle movement and animations
var moveDir = new Vector2(_xAxisInput.Value, 0);
string animation = null;
if (IsRunning == true && _collisionState.Below && !_runInput.IsDown)
IsRunning = false;
if (CanMove == false)
return;
if (CanInput == false)
{
if (!_collisionState.Below && _velocity.Y > 0)
animation = "Falling";
// apply gravity
if (CanGravity)
_velocity.Y += Gravity * Time.DeltaTime;
// move
_mover.Move(_velocity * Time.DeltaTime, _boxCollider, _collisionState);
if (animation != null && !_animator.IsAnimationActive(animation))
_animator.Play(animation);
return;
}
// moving right
if (moveDir.X < 0 && Math.Abs(_velocity.X) < RunSpeed)
{
if (_collisionState.Below)
{
// running
if (_runInput.IsDown)
{
animation = "Run";
_velocity.X = -RunSpeed;
IsRunning = true;
}
// walking
else
{
animation = "Walk";
_velocity.X = (MoveSpeed / moveDir.X);
Debug.Log(moveDir.X);
}
}
//moving when jumping
else
{
if (_velocity.X <= MoveSpeed)
{
if (IsRunning && (_velocity.X <= RunSpeed))
{
_velocity.X = -RunSpeed;
}
else
_velocity.X = (MoveSpeed / moveDir.X);
}
else
{
_velocity.X = _velocity.X + -FallSpeed;
}
}
_animator.FlipX = true;
}
// moving left
else if (moveDir.X > 0 && Math.Abs(_velocity.X) < RunSpeed)
{
if (_collisionState.Below)
{
if (_runInput.IsDown)
{
animation = "Run";
_velocity.X = RunSpeed;
IsRunning = true;
}
else
{
animation = "Walk";
_velocity.X = (MoveSpeed / moveDir.X);
}
}
else
{
if (_velocity.X >= MoveSpeed)
{
if (IsRunning && (_velocity.X <= RunSpeed))
{
_velocity.X = RunSpeed;
}
else
_velocity.X = (MoveSpeed / moveDir.X);
}
else
{
_velocity.X = _velocity.X + FallSpeed;
}
}
_animator.FlipX = false;
}
//slowing down
else
{
if (_velocity.X >= 15)
{
_velocity.X = _velocity.X - 15;
Debug.Log(_velocity.X);
}
else if (_velocity.X <= -15)
{
_velocity.X = _velocity.X + 15;
}
else
_velocity.X = 0;
if(Math.Abs(_velocity.X) >= 15 && Math.Abs(_velocity.X) <= MoveSpeed){
animation = "Hurt";
}
if (_collisionState.Below && animation != "Hurt" && Math.Abs(_velocity.X) <= MoveSpeed)
animation = "Idle";
}
// jump
if ((_collisionState.Below || CanJump != 0) && _jumpInput.IsPressed)
{
animation = "Jumping";
_velocity.Y = -Mathf.Sqrt(2f * JumpHeight * Gravity);
Debug.Log(_velocity.Y);
CanJump = 0;
}
//dash
if (_dashInput.IsPressed && CanDash)
{
CanDash = false;
Debug.Log(Math.Abs(_xAxisInput.Value) * DashSpeed);
Debug.Log(Math.Abs(_yAxisInput.Value) * DashSpeed);
float VeloX;
float VeloY;
if(_yAxisInput.Value == 0)
{
Debug.Log("hello");
VeloX = Mathf.Sqrt(2f * DashSpeed * Gravity);
VeloY = 0;//-Mathf.Sqrt(1.5f * DashSpeed * GravityWhileDashing);
Core.StartCoroutine(DisableGravity(.50f)); //disable gravity for a bit
Core.StartCoroutine(DisableInputs(.50f)); //dash should last ~.75sec
}
else
{
VeloX = Mathf.Sqrt(2f * (Math.Abs(_xAxisInput.Value) * DashSpeed) * Gravity);
VeloY = -Mathf.Sqrt(2f * (Math.Abs(_yAxisInput.Value) * DashSpeed) * Gravity);
Core.StartCoroutine(DisableGravity(.25f)); //disable gravity for a bit
}
if (_xAxisInput.Value < 0 || _animator.FlipX == true)
VeloX = -VeloX;
if (_yAxisInput.Value > 0)
VeloY = -VeloY;
_velocity.X = VeloX;
_velocity.Y = VeloY;
if(_yAxisInput.Value < -.02f)
animation = "Death";
//shake camera cause we can :sunglasses:
Entity.GetComponent<CameraShake>().Shake(10, 1.5f, new Vector2(-VeloX, -VeloY));
Core.StartCoroutine(DashEffects(new Vector2().AngleBetween(new Vector2(1, 0), new Vector2(-VeloX, -VeloY))));
Core.StartCoroutine(DisableMovement(.15f)); //dash should last ~.40sec
}
if (!_collisionState.Below && _velocity.Y > 0)
animation = "Falling";
// apply gravity
if (CanGravity)
_velocity.Y += Gravity * Time.DeltaTime;
// move
_mover.Move(_velocity * Time.DeltaTime, _boxCollider, _collisionState);
//reset dashes and jump grace thingy
if (_collisionState.Below)
{
_velocity.Y = 0;
CanJump = 5;
CanDash = true;
}
// snow particles when land
if (_collisionState.BecameGroundedThisFrame && !_collisionState.IsGroundedOnOneWayPlatform)
{
LoadParticleSystem();
_particleEmitterConfig.Angle = 90;
_particleEmitterConfig.AngleVariance = 100;
_particleEmitter.Play();
}
if (LatestAnimation != animation)
{
Debug.Log(animation);
}
if (animation != null && !_animator.IsAnimationActive(animation))
_animator.Play(animation);
// jump grace countdown (-1 per frame)
if (CanJump > 0 && !_collisionState.Below)
{
CanJump--;
Debug.Log($"CanJump {CanJump}");
}
LatestAnimation = animation;
}
private IEnumerator DisableMovement(float seconds)
{
CanMove = false;
yield return Coroutine.WaitForSeconds(seconds);
CanMove = true;
}
private IEnumerator DisableGravity(float seconds)
{
CanGravity = false;
yield return Coroutine.WaitForSeconds(seconds);
CanGravity = true;
}
private IEnumerator DisableInputs(float seconds)
{
yield return Coroutine.WaitForSeconds(.05f);
CanGravity = false;
yield return Coroutine.WaitForSeconds(seconds - .05f);
CanGravity = true;
}
private IEnumerator DashEffects(float angle)
{
yield return Coroutine.WaitForSeconds(.25f);
LoadParticleSystem();
_particleEmitterConfig.Angle = -angle;
_particleEmitterConfig.AngleVariance = 75;
_particleEmitter.Play();
_camera.SetZoom(0.01f);
yield return Coroutine.WaitForSeconds(0.5f);
Debug.Log("coroutine finished?");
_camera.SetZoom(0f);
}
#region ITriggerListener implementation
void ITriggerListener.OnTriggerEnter(Collider other, Collider self)
{
Debug.Log("triggerEnter: {0}", other.Entity.Name);
}
void ITriggerListener.OnTriggerExit(Collider other, Collider self)
{
Debug.Log("triggerExit: {0}", other.Entity.Name);
}
#endregion
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment