More Collisions

Beyond just basic collision between rectangles Otter has a number of Collider classes that may be useful.  This example creates Entities that contain some of the more advanced Collider types and places them in a Scene with a simple Player Entity.  When the Player Entity overlaps any of the Colliders in the Scene it will turn yellow to indicate a positive overlap test.

There is a lot of code here, but hopefully the comments explain what's going on.

using Otter;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MoreCollision {
  class Program {
    static void Main(string[] args) {
      // Create a new Game.
      var game = new Game("More Collisions!");

      // Create a new Scene.
      var scene = new Scene();

      // Add a new Player in the center.
      scene.Add(new Player(game.HalfWidth, game.HalfHeight));

      // Add the GridTest to the Scene.
      scene.Add(new GridTest());

      // Add some PolygonTests to the Scene.
      scene.Add(new PolygonTest(50, 50));
      scene.Add(new PolygonTest(550, 250));
      scene.Add(new PolygonTest(350, 420));

      // Add some LineTests to the Scene.
      scene.Add(new LineTest(20, 440, 180, 400));
      scene.Add(new LineTest(480, 340, 560, 420));

      // Add a CircleTest to the Scene.
      scene.Add(new CircleTest(420, 240));

      // Add soe PointTests to the Scene.
      scene.Add(new PointTest(300, 100));
      scene.Add(new PointTest(350, 100));
      scene.Add(new PointTest(350, 150));
      scene.Add(new PointTest(300, 150));

      // Start the Game using the Scene.
      game.Start(scene);
    }
  }

  // Some simple Enum tags to use for Colliders.
  enum Tags {
    Player,
    Walls
  }

  class GridTest : Entity {
    
    GridCollider gridCollider;

    public GridTest() : base() {
      // Create a GridCollider that is the size of the Game's width and height, and define the tile size as 32 x 32 with the Walls tag.
      gridCollider = new GridCollider(Game.Instance.Width, Game.Instance.Height, 32, 32, Tags.Walls);

      // Set some tiles to be collidable (true)
      gridCollider.SetTile(3, 3, true);
      gridCollider.SetTile(4, 3, true);
      gridCollider.SetTile(5, 3, true);

      // Set some rectangles to be collidable (true)
      gridCollider.SetRect(12, 2, 5, 3, true);
      gridCollider.SetRect(2, 8, 7, 4, true);

      // Add the Collider to the Entity so that collisions with it will register.
      AddCollider(gridCollider);
    }

    public override void Render() {
      base.Render();

      // Render the Collider for debug purposes.
      gridCollider.Render();
    }
  }

  class PolygonTest : Entity {

    PolygonCollider polygonCollider;

    public PolygonTest(float x, float y) : base(x, y) {
      // Create a PolygonCollider and give it some Vector2 points.
      polygonCollider = new PolygonCollider(
        new Vector2(0, 0),
        new Vector2(80, -10),
        new Vector2(30, 90),
        new Vector2(-5, 15),
        new Vector2(0, 5)
        );

      // Add the Walls tag (since this isn't in the constructor.)
      polygonCollider.AddTag(Tags.Walls);

      // Set the rotation to a random angle.
      polygonCollider.Rotation = Rand.Angle;

      // Add the Collider.
      AddCollider(polygonCollider);
      // Center the origin of the Collider.
      polygonCollider.CenterOrigin();
    }

    public override void Render() {
      base.Render();

      // Render the Collider for debug purposes.
      polygonCollider.Render();
    }
  }

  class LineTest : Entity {
    LineCollider lineCollider;

    public LineTest(float x, float y, float x2, float y2) : base(x, y) {
      // Create a new LineCollider using some math to reposition
      // the line based on the positions provided in the Constructor.
      lineCollider = new LineCollider(0, 0, x2 - x, y2 - y, Tags.Walls);

      // Add the Collider.
      AddCollider(lineCollider);
    }

    public override void Render() {
      base.Render();

      // Render the Collider for debug purposes.
      lineCollider.Render();
    }

  }

  class PointTest : Entity {
    PointCollider pointCollider;

    public PointTest(float x, float y) : base(x, y) {
      // Create a PointCollider.
      pointCollider = new PointCollider(0, 0, Tags.Walls);

      // Add the Collider.
      AddCollider(pointCollider);
    }

    public override void Render() {
      base.Render();

      // Render the Collider.
      pointCollider.Render();
    }
  }

  class CircleTest : Entity {
    CircleCollider circleCollider;

    public CircleTest(float x, float y) : base(x, y) {
      // Create a CircleCollider with a Radius of 40.
      circleCollider = new CircleCollider(40, Tags.Walls);

      // Add the Collider.
      AddCollider(circleCollider);

      // Center the origin of the Collider.
      circleCollider.CenterOrigin();
    }

    public override void Render() {
      base.Render();

      // Render the Collider for debug purposes.
      circleCollider.Render();
    }
  }

  class Player : Entity {
    // The Image to use for testing.
    Image image = Image.CreateRectangle(20, Color.White);

    // The movement speed of the Entity.
    float moveSpeed = 3;

    public Player(float x, float y) : base(x, y) {
      // Add the Image to the Graphic list to render.
      AddGraphic(image);
      // Center the origin of the Image.
      image.CenterOrigin();

      // Set the Hitbox of the Entity to a 20 x 20 box with the Player tag.
      SetHitbox(20, 20, Tags.Player);
      // Center the origin of the Hitbox Collider.
      // Note that Hitbox is just a reference to the BoxCollider created with SetHitbox().
      Hitbox.CenterOrigin();
    }

    public override void Update() {
      base.Update();

      // Basic keyboard movement.
      if (Input.KeyDown(Key.Up) || Input.KeyDown(Key.W)) {
        Y -= moveSpeed;
      }
      if (Input.KeyDown(Key.Down) || Input.KeyDown(Key.S)) {
        Y += moveSpeed;
      }
      if (Input.KeyDown(Key.Left) || Input.KeyDown(Key.A)) {
        X -= moveSpeed;
      }
      if (Input.KeyDown(Key.Right) || Input.KeyDown(Key.D)) {
        X += moveSpeed;
      }

      // If the Entity's Collider overlaps any Walls
      if (Overlap(X, Y, Tags.Walls)) {
        // Set the Color to Yellow.
        image.Color = Color.Yellow;
      }
      else {
        // If no overlaps then change the color back to White.
        image.Color = Color.White;
      }
    }

    public override void Render() {
      base.Render();

      // Render the Hitbox for debug purposes.
      Hitbox.Render();
    }
  }
}

Running the code should result in a little demo Scene with different Colliders to move around in.

Examples