A stealth-based 2D platformer where you don't have to kill anyone unless you want to. https://www.semicolin.games
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

186 lines
6.5 KiB

  1. using Microsoft.Xna.Framework;
  2. using Microsoft.Xna.Framework.Graphics;
  3. using Microsoft.Xna.Framework.Input;
  4. using System;
  5. using System.Collections.Generic;
  6. namespace Jumpy {
  7. class Player {
  8. enum Facing { Left, Right };
  9. enum Pose { Walking, Standing, Crouching, Stretching, SwordSwing, Jumping };
  10. enum AirState { Jumping, Ground, Falling };
  11. private Texture2D texture;
  12. private const int spriteSize = 48;
  13. private const int spriteWidth = 7;
  14. private const int bottomPadding = 1;
  15. private const int moveSpeed = 200;
  16. private const int jumpSpeed = 600;
  17. private const int gravity = 2000;
  18. private Point position = new Point(Camera.Width / 2, 10);
  19. private Facing facing = Facing.Right;
  20. private Pose pose = Pose.Standing;
  21. private AirState airState = AirState.Ground;
  22. private double swordSwingTime = 0;
  23. private double jumpTime = 0;
  24. private double ySpeed = 0;
  25. public Player(Texture2D texture) {
  26. this.texture = texture;
  27. }
  28. // TODO: refactor input to have a virtual "which directions & buttons were being pressed"
  29. // instead of complicated if-statements in this function.
  30. public void Update(
  31. GameTime time, History<GamePadState> gamePad, List<Rectangle> collisionTargets) {
  32. Point oldPosition = position;
  33. AirState oldAirState = airState;
  34. UpdateFromGamePad(time, gamePad);
  35. bool someIntersection = false;
  36. Rectangle playerBbox = new Rectangle(position.X - spriteWidth, position.Y - 8, spriteWidth * 2, 27);
  37. bool standingOnGround = false;
  38. foreach (var rect in collisionTargets) {
  39. playerBbox = new Rectangle(position.X - spriteWidth, position.Y - 8, spriteWidth * 2, 27);
  40. if (playerBbox.Intersects(rect)) {
  41. someIntersection = true;
  42. if (oldPosition.Y > position.Y) {
  43. int diff = playerBbox.Top - rect.Bottom;
  44. position.Y -= diff;
  45. ySpeed *= 0.9;
  46. } else {
  47. airState = AirState.Ground;
  48. int diff = playerBbox.Bottom - rect.Top;
  49. position.Y -= diff;
  50. }
  51. Debug.AddRect(rect, Color.Yellow);
  52. } else {
  53. playerBbox.Height += 1;
  54. if (playerBbox.Intersects(rect)) {
  55. standingOnGround = true;
  56. Debug.AddRect(rect, Color.Cyan);
  57. } else {
  58. Debug.AddRect(rect, Color.Green);
  59. }
  60. }
  61. }
  62. if (oldAirState != AirState.Ground && standingOnGround) {
  63. airState = AirState.Ground;
  64. ySpeed = 0.0;
  65. }
  66. if (airState == AirState.Ground && !standingOnGround) {
  67. airState = AirState.Falling;
  68. ySpeed = 0.0;
  69. }
  70. if (airState == AirState.Ground) {
  71. Debug.AddRect(playerBbox, Color.Red);
  72. } else if (airState == AirState.Jumping) {
  73. Debug.AddRect(playerBbox, Color.Orange);
  74. } else {
  75. Debug.AddRect(playerBbox, Color.Yellow);
  76. }
  77. }
  78. void UpdateFromGamePad(GameTime time, History<GamePadState> gamePad) {
  79. if (gamePad[0].IsButtonDown(Buttons.A) && airState == AirState.Ground) {
  80. pose = Pose.Jumping;
  81. airState = AirState.Jumping;
  82. jumpTime = 0.5;
  83. ySpeed = -jumpSpeed;
  84. return;
  85. }
  86. if (gamePad[0].IsButtonDown(Buttons.X) && gamePad[1].IsButtonUp(Buttons.X)
  87. && swordSwingTime <= 0) {
  88. pose = Pose.SwordSwing;
  89. swordSwingTime = 0.3;
  90. return;
  91. }
  92. Vector2 leftStick = gamePad[0].ThumbSticks.Left;
  93. if (gamePad[0].IsButtonDown(Buttons.DPadLeft) || leftStick.X < -0.5) {
  94. facing = Facing.Left;
  95. pose = Pose.Walking;
  96. position.X -= (int) (moveSpeed * time.ElapsedGameTime.TotalSeconds);
  97. } else if (gamePad[0].IsButtonDown(Buttons.DPadRight) || leftStick.X > 0.5) {
  98. facing = Facing.Right;
  99. pose = Pose.Walking;
  100. position.X += (int) (moveSpeed * time.ElapsedGameTime.TotalSeconds);
  101. } else if (gamePad[0].IsButtonDown(Buttons.DPadDown) || leftStick.Y < -0.5) {
  102. pose = Pose.Crouching;
  103. } else if (gamePad[0].IsButtonDown(Buttons.DPadUp) || leftStick.Y > 0.5) {
  104. pose = Pose.Stretching;
  105. } else {
  106. pose = Pose.Standing;
  107. }
  108. if (jumpTime > 0) {
  109. jumpTime -= time.ElapsedGameTime.TotalSeconds;
  110. }
  111. if (swordSwingTime > 0) {
  112. swordSwingTime -= time.ElapsedGameTime.TotalSeconds;
  113. pose = Pose.SwordSwing;
  114. }
  115. if (airState == AirState.Jumping || airState == AirState.Falling) {
  116. position.Y += (int) (ySpeed * time.ElapsedGameTime.TotalSeconds);
  117. ySpeed += gravity * (float) time.ElapsedGameTime.TotalSeconds;
  118. }
  119. if (airState == AirState.Jumping && pose != Pose.SwordSwing) {
  120. pose = Pose.Jumping;
  121. }
  122. position.X = Math.Min(Math.Max(position.X, 0 + spriteWidth), Camera.Width - spriteWidth);
  123. }
  124. private Point spritePosition(Pose pose, GameTime time) {
  125. int frameNum = (time.TotalGameTime.Milliseconds / 125) % 4;
  126. if (frameNum == 3) {
  127. frameNum = 1;
  128. }
  129. switch (pose) {
  130. case Pose.Walking:
  131. return new Point(spriteSize * frameNum + spriteSize * 6, 0);
  132. case Pose.Crouching:
  133. return new Point(spriteSize * 7, spriteSize * 2);
  134. case Pose.Stretching:
  135. return new Point(spriteSize * frameNum, spriteSize * 2);
  136. case Pose.Jumping:
  137. if (jumpTime > 0.25) {
  138. return new Point(spriteSize * 6, spriteSize);
  139. } else if (jumpTime > 0) {
  140. return new Point(spriteSize * 7, spriteSize);
  141. } else {
  142. return new Point(spriteSize * 8, spriteSize);
  143. }
  144. case Pose.SwordSwing:
  145. if (swordSwingTime > 0.2) {
  146. return new Point(spriteSize * 3, spriteSize * 0);
  147. } else if (swordSwingTime > 0.1) {
  148. return new Point(spriteSize * 4, spriteSize * 0);
  149. } else {
  150. return new Point(spriteSize * 5, spriteSize * 0);
  151. }
  152. case Pose.Standing:
  153. default:
  154. return new Point(spriteSize * 7, 0);
  155. }
  156. }
  157. public void Draw(GameTime time, SpriteBatch spriteBatch) {
  158. Point source = spritePosition(pose, time);
  159. Rectangle textureSource = new Rectangle(source.X, source.Y, spriteSize, spriteSize);
  160. Vector2 spriteCenter = new Vector2(spriteSize / 2, spriteSize / 2);
  161. SpriteEffects effect = facing == Facing.Right ?
  162. SpriteEffects.FlipHorizontally : SpriteEffects.None;
  163. Vector2 drawPos = new Vector2(position.X, position.Y);
  164. spriteBatch.Draw(texture, drawPos, textureSource, Color.White, 0f, spriteCenter,
  165. Vector2.One, effect, 0f);
  166. }
  167. }
  168. }