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.

192 lines
7.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 TreeScene : IScene {
  7. const int MAX_SEGMENTS = 1000;
  8. const int MAX_VERTICES = MAX_SEGMENTS * 6; // 2 triangles per segment
  9. private readonly Color backgroundColor = Color.SkyBlue;
  10. private readonly GraphicsDevice graphics;
  11. private readonly BasicEffect basicEffect;
  12. private VertexPositionColor[] vertices;
  13. private VertexBuffer vertexBuffer;
  14. public TreeScene(GraphicsDevice graphics) {
  15. this.graphics = graphics;
  16. basicEffect = new BasicEffect(graphics) {
  17. World = Matrix.CreateTranslation(0, 0, 0),
  18. View = Matrix.CreateLookAt(Vector3.Backward, Vector3.Zero, Vector3.Up),
  19. VertexColorEnabled = true,
  20. Projection = Matrix.CreateOrthographicOffCenter(
  21. -1920 / 2, 1920 / 2, -1080 / 4, 1080 * 3 / 4, -1, 1)
  22. };
  23. vertices = new VertexPositionColor[MAX_VERTICES];
  24. vertexBuffer = new VertexBuffer(
  25. graphics, typeof(VertexPositionColor), MAX_VERTICES, BufferUsage.WriteOnly);
  26. }
  27. ~TreeScene() {
  28. Dispose();
  29. }
  30. public void Dispose() {
  31. vertexBuffer.Dispose();
  32. GC.SuppressFinalize(this);
  33. }
  34. public struct Trapezoid {
  35. public Vector2 p1, p2, p3, p4;
  36. public void Rotate(float angle) {
  37. p1 = p1.Rotate(angle);
  38. p2 = p2.Rotate(angle);
  39. p3 = p3.Rotate(angle);
  40. p4 = p4.Rotate(angle);
  41. }
  42. public void Translate(Vector2 position) {
  43. p1 = Vector2.Add(p1, position);
  44. p2 = Vector2.Add(p2, position);
  45. p3 = Vector2.Add(p3, position);
  46. p4 = Vector2.Add(p4, position);
  47. }
  48. }
  49. public class TreeNode {
  50. // Ideal orientation, relative to its parent.
  51. public readonly float Orientation;
  52. // Orientation in world space.
  53. public float WorldOrientation;
  54. public readonly float Length;
  55. public readonly float InWidth;
  56. public readonly float OutWidth;
  57. public readonly List<TreeNode> Children;
  58. // Position of in-vertex in world space.
  59. public Vector2 Position;
  60. public TreeNode(float orientation, float length, float inWidth, float outWidth) :
  61. this(orientation, length, inWidth, outWidth, new List<TreeNode>()) {
  62. }
  63. public TreeNode(float orientation, float length, float inWidth, float outWidth,
  64. TreeNode child) :
  65. this(orientation, length, inWidth, outWidth, new List<TreeNode>() { child }) {
  66. }
  67. public TreeNode(float orientation, float length, float inWidth, float outWidth,
  68. TreeNode child1, TreeNode child2) :
  69. this(orientation, length, inWidth, outWidth, new List<TreeNode>() { child1, child2 }) {
  70. }
  71. public TreeNode(float orientation, float length, float inWidth, float outWidth,
  72. TreeNode child1, TreeNode child2, TreeNode child3) :
  73. this(orientation, length, inWidth, outWidth,
  74. new List<TreeNode>() { child1, child2, child3 }) {
  75. }
  76. public TreeNode(float orientation, float length, float inWidth, float outWidth,
  77. List<TreeNode> children) {
  78. Orientation = orientation;
  79. WorldOrientation = orientation;
  80. Length = length;
  81. InWidth = inWidth;
  82. OutWidth = outWidth;
  83. Children = children;
  84. Position = Vector2.Zero;
  85. }
  86. }
  87. public void Draw(bool isRunningSlowly, IWorld iworld, bool paused) {
  88. var tree =
  89. new TreeNode(0.0f, 100, 10, 6,
  90. new TreeNode(0.0f, 100, 10, 6,
  91. new TreeNode(-0.2f, 100, 10, 6,
  92. new TreeNode(-0.3f, 100, 6, 4,
  93. new TreeNode(-0.1f, 100, 6, 4,
  94. new TreeNode(-0.3f, 150, 4, 2),
  95. new TreeNode(0.2f, 200, 4, 2),
  96. new TreeNode(0.5f, 100, 4, 2))),
  97. new TreeNode(0.5f, 100, 6, 4,
  98. new TreeNode(-0.1f, 100, 6, 4,
  99. new TreeNode(-0.1f, 150, 4, 2),
  100. new TreeNode(0.2f, 200, 4, 2))))));
  101. graphics.Clear(backgroundColor);
  102. var segments = new List<Trapezoid>();
  103. LinkedList<TreeNode> queue = new LinkedList<TreeNode>();
  104. queue.AddLast(tree);
  105. Debug.WriteLine("---------------------");
  106. while (queue.Count > 0) {
  107. TreeNode parent = queue.First.Value;
  108. queue.RemoveFirst();
  109. Vector2 outVector = new Vector2(0, parent.Length).Rotate(parent.WorldOrientation);
  110. Vector2 outPosition = Vector2.Add(parent.Position, outVector);
  111. outVector.Normalize();
  112. Vector2 wind = new Vector2(1.0f, 0.0f);
  113. float windAmount = Vector2.Dot(wind, outVector);
  114. Debug.WriteLine("" + windAmount);
  115. // We want a trapezoid with 4 points. A is the in position, B is the out position.
  116. // The TreeNode.Length is the distance from A to B.
  117. //
  118. // We come up with the points relative to A being the origin, then rotate the trapezoid
  119. // by its orientation, and translate the result to A's actual position.
  120. //
  121. // 3---B---4 <-- length = outWidth
  122. // / | \
  123. // / | \
  124. // 1------A------2 <-- length = inWidth
  125. // This fudge factor lengthens the sides a bit longer to prevent small discontinuities
  126. // in the rendered result.
  127. // TODO: remove this sideLengthFudge.
  128. float sideLengthFudge = 1.05f;
  129. Trapezoid t = new Trapezoid();
  130. t.p1 = new Vector2(-parent.InWidth, 0);
  131. t.p2 = new Vector2(parent.InWidth, 0);
  132. t.p3 = new Vector2(-parent.OutWidth, parent.Length * sideLengthFudge);
  133. t.p4 = new Vector2(parent.OutWidth, parent.Length * sideLengthFudge);
  134. t.Rotate(parent.WorldOrientation);
  135. t.Translate(parent.Position);
  136. segments.Add(t);
  137. foreach (TreeNode child in parent.Children) {
  138. child.Position = outPosition;
  139. float orientation = parent.WorldOrientation + child.Orientation;
  140. child.WorldOrientation = orientation;
  141. queue.AddLast(child);
  142. }
  143. }
  144. Color color = Color.SaddleBrown;
  145. for (int i = 0; i < segments.Count; i++) {
  146. Trapezoid t = segments[i];
  147. vertices[i * 6] = new VertexPositionColor(new Vector3(t.p1.X, t.p1.Y, 0), color);
  148. vertices[i * 6 + 1] = new VertexPositionColor(new Vector3(t.p2.X, t.p2.Y, 0), color);
  149. vertices[i * 6 + 2] = new VertexPositionColor(new Vector3(t.p3.X, t.p3.Y, 0), color);
  150. vertices[i * 6 + 3] = new VertexPositionColor(new Vector3(t.p2.X, t.p2.Y, 0), color);
  151. vertices[i * 6 + 4] = new VertexPositionColor(new Vector3(t.p3.X, t.p3.Y, 0), color);
  152. vertices[i * 6 + 5] = new VertexPositionColor(new Vector3(t.p4.X, t.p4.Y, 0), color);
  153. }
  154. graphics.SetVertexBuffer(vertexBuffer);
  155. vertexBuffer.SetData(vertices);
  156. foreach (EffectPass pass in basicEffect.CurrentTechnique.Passes) {
  157. pass.Apply();
  158. graphics.DrawPrimitives(PrimitiveType.TriangleList, 0, segments.Count * 2);
  159. }
  160. }
  161. }
  162. }