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.

123 lines
4.0 KiB

  1. using Microsoft.Xna.Framework;
  2. using Microsoft.Xna.Framework.Graphics;
  3. using System;
  4. using System.Collections.Generic;
  5. namespace SemiColinGames {
  6. public sealed class ShmupWorld : IWorld {
  7. public class ShmupPlayer {
  8. public TextureRef Texture = Textures.Yellow2;
  9. // Center of player sprite.
  10. public Vector2 Position = new Vector2(48, 1080 / 8);
  11. // TODO: use a bounds rect instead of HalfSize.
  12. public Vector2 HalfSize = new Vector2(16, 10);
  13. private float speed = 150f;
  14. private float shotCooldown = 0f;
  15. private ProfilingList<Shot> shots = new ProfilingList<Shot>(10, "playerShots");
  16. public ProfilingList<Shot> Update(
  17. float modelTime, History<Input> input, Rectangle worldBounds) {
  18. // Movement update.
  19. Vector2 motion = Vector2.Multiply(input[0].Motion, modelTime * speed);
  20. Position = Vector2.Add(Position, motion);
  21. Position.X = Math.Max(Position.X, HalfSize.X);
  22. Position.X = Math.Min(Position.X, worldBounds.Size.X - HalfSize.X);
  23. Position.Y = Math.Max(Position.Y, HalfSize.Y);
  24. Position.Y = Math.Min(Position.Y, worldBounds.Size.Y - HalfSize.Y);
  25. // Check whether we need to add new shots.
  26. shotCooldown -= modelTime;
  27. shots.Clear();
  28. if (input[0].Attack && shotCooldown <= 0) {
  29. shotCooldown = 0.2f;
  30. Vector2 shotOffset = new Vector2(12, 2);
  31. Vector2 shotPosition = Vector2.Add(Position, shotOffset);
  32. shots.Add(new Shot(shotPosition, new Vector2(300, 0)));
  33. }
  34. return shots;
  35. }
  36. public void Draw(SpriteBatch spriteBatch) {
  37. Texture2D texture = Texture.Get;
  38. Vector2 spriteCenter = new Vector2(texture.Width / 2, texture.Height / 2);
  39. Vector2 drawPos = Vector2.Floor(Vector2.Subtract(Position, spriteCenter));
  40. spriteBatch.Draw(texture, drawPos, Color.White);
  41. }
  42. }
  43. public class Shot {
  44. static int color = 0;
  45. public TextureRef Texture;
  46. public Vector2 Position;
  47. public Vector2 HalfSize = new Vector2(11, 4);
  48. public Rectangle Bounds;
  49. public Vector2 Velocity;
  50. public Shot(Vector2 position, Vector2 velocity) {
  51. Texture = (color % 5) switch {
  52. 0 => Textures.Projectile1,
  53. 1 => Textures.Projectile2,
  54. 2 => Textures.Projectile3,
  55. 3 => Textures.Projectile4,
  56. _ => Textures.Projectile5
  57. };
  58. color++;
  59. Position = position;
  60. Velocity = velocity;
  61. Update(0); // set Bounds
  62. }
  63. public void Update(float modelTime) {
  64. Position = Vector2.Add(Position, Vector2.Multiply(Velocity, modelTime));
  65. Bounds = new Rectangle(
  66. (int) (Position.X - HalfSize.X),
  67. (int) (Position.Y - HalfSize.Y),
  68. (int) HalfSize.X * 2,
  69. (int) HalfSize.Y * 2);
  70. }
  71. public void Draw(SpriteBatch spriteBatch) {
  72. Texture2D texture = Texture.Get;
  73. Vector2 center = new Vector2(texture.Width / 2, texture.Height / 2);
  74. spriteBatch.Draw(texture, Vector2.Floor(Vector2.Subtract(Position, center)), Color.White);
  75. }
  76. }
  77. public readonly Rectangle Bounds;
  78. public readonly ShmupPlayer Player;
  79. public readonly ProfilingList<Shot> Shots;
  80. public ShmupWorld() {
  81. Bounds = new Rectangle(0, 0, 1920 / 4, 1080 / 4);
  82. Player = new ShmupPlayer();
  83. Shots = new ProfilingList<Shot>(100, "shots");
  84. }
  85. ~ShmupWorld() {
  86. Dispose();
  87. }
  88. public void Dispose() {
  89. GC.SuppressFinalize(this);
  90. }
  91. public void Update(float modelTime, History<Input> input) {
  92. // Update player & shots.
  93. ProfilingList<Shot> newPlayerShots = Player.Update(modelTime, input, Bounds);
  94. foreach (Shot shot in Shots) {
  95. shot.Update(modelTime);
  96. }
  97. // Add new shots.
  98. Shots.AddRange(newPlayerShots);
  99. // Reap off-screen objects.
  100. Rectangle paddedBounds = Bounds;
  101. paddedBounds.Inflate(16, 16);
  102. Shots.RemoveAll(shot => !paddedBounds.Intersects(shot.Bounds));
  103. Debug.AddToast("shots: " + Shots.Count);
  104. }
  105. }
  106. }