using System; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.Graphics; namespace LunaLightXMG { public class Player { private float hsp; // Horizontal speed public float vsp; // Vertical speed private float hsp_frac; // Hold fractional position data private float vsp_frac; // Hold fractional position data private float wallJumpDelay; // Delay for wall jumping public bool grounded; // Is the player grounded? public bool walled; // Is the player wall sliding? private bool wasWalled; // Remember is the player was on a wall or ground. private float hspAcc; // Horizontal acceleration private float hspFrictionGround; // Ground friction private float hspFrictionAir; // Air friction private float hspWalk; // Maximum horizontal speed private float grv; // Gravity private float vspMax; // Maximum vertical speed private float grvWall; // Gravity when wall sliding private float vspMaxWall; // Maximum vertical speed when wall sliding private float vspJump; // Jump speed private Texture2D texture; public Vector2 position; public BoundingBox boundingBox { get; private set; } private CollisionManager collisionManager; public Player(Vector2 initPosition) { hsp = 0; vsp = 0; hsp_frac = 0; vsp_frac = 0; wallJumpDelay = 0; grounded = false; walled = false; wasWalled = false; hspAcc = 0.2f; hspFrictionGround = 0.3f; hspFrictionAir = 0.3f; hspWalk = 3f; grv = 0.2f; vspMax = 10f; grvWall = 0.1f; vspMaxWall = 5f; vspJump = -4f; position = initPosition; boundingBox = new BoundingBox(new Rectangle((int)position.X, (int)position.Y, 8, 16)); collisionManager = new CollisionManager(); } public void LoadContent(ContentManager content) { texture = content.Load("player"); } public void Update(GameTime gameTime, PlayerInput input, Platform[] platforms) { Movement(input); boundingBox.Update(position, boundingBox.Bounds.Width, boundingBox.Bounds.Height); DumpFractions(); CollisionChecks(platforms); } public void Draw(SpriteBatch spriteBatch) { // Snap position to integer values Vector2 drawPosition = new Vector2((int)position.X, (int)position.Y); spriteBatch.Draw(texture, drawPosition, Color.White); } // Method Implementation ------------------------------------------------------------------------------- public void Reset(Vector2 initialPosition) { position = initialPosition; // Reset other player-specific states later } private void Movement(PlayerInput input) { if (input.KeyJump && grounded) { vsp = vspJump; } // Horizontal movement with acceleration + wall jump delay wallJumpDelay = Math.Max(wallJumpDelay - 1, 0); // counts down every frame if (wallJumpDelay == 0) // no left-right control until delay is 0, prevents rapid wall climb { float moveH = input.KeyRight - input.KeyLeft; hsp += moveH * hspAcc; if (moveH == 0) { float hspFrictionFinal = grounded ? hspFrictionGround : hspFrictionAir; hsp = Approach(hsp, 0, hspFrictionFinal); } hsp = MathHelper.Clamp(hsp, -hspWalk, hspWalk); } // Calculate vertical movement float grvFinal = grv; float vspMaxFinal = vspMax; if (walled && vsp > 0) { grvFinal = grvWall; vspMaxFinal = vspMaxWall; } vsp += vsp == 0 ? grvFinal * 2 : grvFinal; vsp = MathHelper.Clamp(vsp, vspJump, vspMaxFinal); } private void CollisionChecks(Platform[] platforms) { // Horizontal collision prediction float onePixh = Math.Sign(hsp); if (CollisionManager.IsColliding(position + new Vector2(hsp, 0), boundingBox, platforms)) { while (!CollisionManager.IsColliding(position + new Vector2(onePixh, 0), boundingBox, platforms)) { position.X += onePixh; } hsp = 0; } position.X += hsp; // Horizontal move // Vertical collision prediction float onePixv = Math.Sign(vsp); if (CollisionManager.IsColliding(position + new Vector2(0, vsp), boundingBox, platforms)) { while (!CollisionManager.IsColliding(position + new Vector2(0, onePixv), boundingBox, platforms)) { position.Y += onePixv; } vsp = 0; } position.Y += vsp; // Vertical move // Grounded? grounded = CollisionManager.IsColliding(position + new Vector2(0, 1), boundingBox, platforms); // Walled? walled = CollisionManager.WallCheck(position, boundingBox, platforms); if (walled) wasWalled = walled; } private float Approach(float start, float end, float shift) { if (start < end) { start += shift; if (start > end) return end; } else { start -= shift; if (start < end) return end; } return start; } private void DumpFractions() { if (grounded) { float adjustedPosX = (float)Math.Round(position.X); float adjustedPosY = (float)Math.Round(position.Y); position.X = adjustedPosX; position.Y = adjustedPosY; } if (vsp >= 0) { hsp += hsp_frac; vsp += vsp_frac; hsp_frac = hsp - (float)Math.Floor(hsp); vsp_frac = vsp - (float)Math.Floor(vsp); hsp -= hsp_frac; vsp -= vsp_frac; } } } }