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.

170 lines
5.7 KiB

3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
  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. public void Update(float modelTime, History<Input> input, Rectangle worldBounds,
  16. ProfilingList<Shot> newShots) {
  17. // Movement update.
  18. Vector2 motion = Vector2.Multiply(input[0].Motion, modelTime * speed);
  19. Position = Vector2.Add(Position, motion);
  20. Position.X = Math.Max(Position.X, HalfSize.X);
  21. Position.X = Math.Min(Position.X, worldBounds.Width - HalfSize.X);
  22. Position.Y = Math.Max(Position.Y, HalfSize.Y);
  23. Position.Y = Math.Min(Position.Y, worldBounds.Height - HalfSize.Y);
  24. // Check whether we need to add new shots.
  25. shotCooldown -= modelTime;
  26. if (input[0].Attack && shotCooldown <= 0) {
  27. shotCooldown = 0.2f;
  28. Vector2 shotOffset = new Vector2(12, 2);
  29. Vector2 shotPosition = Vector2.Add(Position, shotOffset);
  30. newShots.Add(new Shot(shotPosition, new Vector2(300, 0)));
  31. }
  32. }
  33. public void Draw(SpriteBatch spriteBatch) {
  34. Texture2D texture = Texture.Get;
  35. Vector2 spriteCenter = new Vector2(texture.Width / 2, texture.Height / 2);
  36. Vector2 drawPos = Vector2.Floor(Vector2.Subtract(Position, spriteCenter));
  37. spriteBatch.Draw(texture, drawPos, Color.White);
  38. }
  39. }
  40. public class Shot {
  41. static int color = 0;
  42. public TextureRef Texture;
  43. public Vector2 Position;
  44. public Vector2 HalfSize = new Vector2(11, 4);
  45. public Rectangle Bounds;
  46. public Vector2 Velocity;
  47. public Shot(Vector2 position, Vector2 velocity) {
  48. Texture = (color % 5) switch {
  49. 0 => Textures.Projectile1,
  50. 1 => Textures.Projectile2,
  51. 2 => Textures.Projectile3,
  52. 3 => Textures.Projectile4,
  53. _ => Textures.Projectile5
  54. };
  55. color++;
  56. Position = position;
  57. Velocity = velocity;
  58. Update(0); // set Bounds
  59. }
  60. public void Update(float modelTime) {
  61. Position = Vector2.Add(Position, Vector2.Multiply(Velocity, modelTime));
  62. Bounds = new Rectangle(
  63. (int) (Position.X - HalfSize.X),
  64. (int) (Position.Y - HalfSize.Y),
  65. (int) HalfSize.X * 2,
  66. (int) HalfSize.Y * 2);
  67. }
  68. public void Draw(SpriteBatch spriteBatch) {
  69. Texture2D texture = Texture.Get;
  70. Vector2 center = new Vector2(texture.Width / 2, texture.Height / 2);
  71. spriteBatch.Draw(texture, Vector2.Floor(Vector2.Subtract(Position, center)), Color.White);
  72. }
  73. }
  74. public interface IMoveBehavior {
  75. public Vector2 Velocity(float modelTime);
  76. }
  77. public class MoveLeft : IMoveBehavior {
  78. public Vector2 Velocity(float modelTime) {
  79. return new Vector2(-100, 0);
  80. }
  81. }
  82. public class Enemy {
  83. public TextureRef Texture = Textures.Blue1;
  84. // Center of sprite.
  85. public Vector2 Position = new Vector2(1920 / 4 - 48, 1080 / 8);
  86. // TODO: use a bounds rect instead of HalfSize.
  87. public Vector2 HalfSize = new Vector2(16, 10);
  88. public Rectangle Bounds;
  89. private IMoveBehavior moveBehavior = new MoveLeft();
  90. public void Update(float modelTime) {
  91. Vector2 velocity = moveBehavior.Velocity(modelTime);
  92. Position = Vector2.Add(Position, Vector2.Multiply(velocity, modelTime));
  93. Bounds = new Rectangle(
  94. (int) (Position.X - HalfSize.X),
  95. (int) (Position.Y - HalfSize.Y),
  96. (int) HalfSize.X * 2,
  97. (int) HalfSize.Y * 2);
  98. }
  99. public void Draw(SpriteBatch spriteBatch) {
  100. Texture2D texture = Texture.Get;
  101. Vector2 spriteCenter = new Vector2(texture.Width / 2, texture.Height / 2);
  102. Vector2 drawPos = Vector2.Floor(Vector2.Subtract(Position, spriteCenter));
  103. spriteBatch.Draw(texture, drawPos, null, Color.White, 0f,
  104. spriteCenter, Vector2.One, SpriteEffects.FlipHorizontally, 0f);
  105. }
  106. }
  107. public readonly Rectangle Bounds;
  108. public readonly ShmupPlayer Player;
  109. public readonly ProfilingList<Enemy> Enemies;
  110. public readonly ProfilingList<Shot> Shots;
  111. private ProfilingList<Shot> newShots;
  112. public ShmupWorld() {
  113. Bounds = new Rectangle(0, 0, 1920 / 4, 1080 / 4);
  114. Player = new ShmupPlayer();
  115. Enemies = new ProfilingList<Enemy>(100, "enemies");
  116. Enemies.Add(new Enemy());
  117. Shots = new ProfilingList<Shot>(100, "shots");
  118. newShots = new ProfilingList<Shot>(100, "newShots");
  119. }
  120. ~ShmupWorld() {
  121. Dispose();
  122. }
  123. public void Dispose() {
  124. GC.SuppressFinalize(this);
  125. }
  126. public void Update(float modelTime, History<Input> input) {
  127. // Update player, enemies, & shots.
  128. newShots.Clear();
  129. Player.Update(modelTime, input, Bounds, newShots);
  130. foreach (Enemy enemy in Enemies) {
  131. enemy.Update(modelTime);
  132. }
  133. foreach (Shot shot in Shots) {
  134. shot.Update(modelTime);
  135. }
  136. // Add new shots.
  137. Shots.AddRange(newShots);
  138. // Reap off-screen objects.
  139. Rectangle paddedBounds = Bounds;
  140. paddedBounds.Inflate(16, 16);
  141. Shots.RemoveAll(shot => !paddedBounds.Intersects(shot.Bounds));
  142. Enemies.RemoveAll(enemy => !paddedBounds.Intersects(enemy.Bounds));
  143. Debug.AddToast("shots: " + Shots.Count + " enemies: " + Enemies.Count);
  144. }
  145. }
  146. }