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.

193 lines
6.1 KiB

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