You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
346 lines
14 KiB
C#
346 lines
14 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
|
|
namespace Otter {
|
|
/// <summary>
|
|
/// Grid Collider. Can be mainly used to create collision to correspond to a Tilemap.
|
|
/// </summary>
|
|
public class GridCollider : Collider {
|
|
|
|
#region Private Fields
|
|
|
|
List<bool> collisions = new List<bool>();
|
|
|
|
#endregion
|
|
|
|
#region Public Properties
|
|
|
|
/// <summary>
|
|
/// The width of the tiles.
|
|
/// </summary>
|
|
public int TileWidth { get; private set; }
|
|
|
|
/// <summary>
|
|
/// The height of the tiles.
|
|
/// </summary>
|
|
public int TileHeight { get; private set; }
|
|
|
|
/// <summary>
|
|
/// The total number of rows on the grid.
|
|
/// </summary>
|
|
public int TileRows { get; private set; }
|
|
|
|
/// <summary>
|
|
/// The total number of columns on the grid.
|
|
/// </summary>
|
|
public int TileColumns { get; private set; }
|
|
|
|
/// <summary>
|
|
/// The width of the grid. (TileColumns * TileWidth)
|
|
/// </summary>
|
|
public override float Width {
|
|
get { return TileColumns * TileWidth; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// The height of the grid (TileRows * TileHeight)
|
|
/// </summary>
|
|
public override float Height {
|
|
get { return TileRows * TileHeight; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Convert an X position to a tile on the grid.
|
|
/// </summary>
|
|
/// <param name="x">The position to convert.</param>
|
|
/// <returns>The X position of the tile that was found at that position.</returns>
|
|
public int GridX(float x) {
|
|
return (int)(Util.SnapToGrid(x, TileWidth) / TileWidth);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Convert an Y position to a tile on the grid.
|
|
/// </summary>
|
|
/// <param name="y">The position to convert.</param>
|
|
/// <returns>The Y position of the tile that was found at that position.</returns>
|
|
public int GridY(float y) {
|
|
return (int)(Util.SnapToGrid(y, TileHeight) / TileHeight);
|
|
}
|
|
|
|
/// <summary>
|
|
/// The area in tile size.
|
|
/// </summary>
|
|
public int TileArea {
|
|
get { return TileRows * TileHeight; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// The area in pixels.
|
|
/// </summary>
|
|
public int Area {
|
|
get { return (int)(Width * Height); }
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Constructors
|
|
|
|
/// <summary>
|
|
/// Create a new GridCollider.
|
|
/// </summary>
|
|
/// <param name="width">The width in pixels of the GridCollider.</param>
|
|
/// <param name="height">The height in pixels of the GridCollider.</param>
|
|
/// <param name="tileWidth">The width of each tile on the grid.</param>
|
|
/// <param name="tileHeight">The heigth of each tile on the grid.</param>
|
|
/// <param name="tags">The tags to register for the collider.</param>
|
|
public GridCollider(int width, int height, int tileWidth, int tileHeight, params int[] tags) {
|
|
if (width < 0) throw new ArgumentOutOfRangeException("Width must be greater than 0.");
|
|
if (height < 0) throw new ArgumentOutOfRangeException("Height must be greater than 0.");
|
|
|
|
TileColumns = (int)Util.Ceil((float)width / tileWidth);
|
|
TileRows = (int)Util.Ceil((float)height / tileHeight);
|
|
TileWidth = tileWidth;
|
|
TileHeight = tileHeight;
|
|
|
|
for (int i = 0; i < TileRows * TileColumns; i++) {
|
|
collisions.Add(false);
|
|
}
|
|
|
|
AddTag(tags);
|
|
}
|
|
|
|
public GridCollider(int width, int height, int tileWidth, int tileHeight, Enum tag, params Enum[] tags) : this(width, height, tileWidth, tileHeight) {
|
|
AddTag(tag);
|
|
AddTag(tags);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Public Methods
|
|
|
|
/// <summary>
|
|
/// Set the collision status of a tile on the GridCollider.
|
|
/// </summary>
|
|
/// <param name="x">The X position of the tile.</param>
|
|
/// <param name="y">The Y position of the tile.</param>
|
|
/// <param name="collidable">True if collidable.</param>
|
|
public void SetTile(int x, int y, bool collidable = true) {
|
|
if (x < 0 || y < 0) return;
|
|
if (x >= TileColumns || y >= TileRows) return;
|
|
collisions[Util.OneDee((int)TileColumns, (int)x, (int)y)] = collidable;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set the collision status of a rectangle area on the GridCollider.
|
|
/// </summary>
|
|
/// <param name="x">The X position of the top left corner of the rectangle.</param>
|
|
/// <param name="y">The Y position of the top left corner of the rectangle.</param>
|
|
/// <param name="width">The width in tiles of the rectangle.</param>
|
|
/// <param name="height">The height in tiles of the rectangle.</param>
|
|
/// <param name="collidable">True if collidable.</param>
|
|
public void SetRect(int x, int y, int width, int height, bool collidable = true) {
|
|
for (int i = x; i < x + width; i++) {
|
|
for (int j = y; j < y + height; j++) {
|
|
SetTile(i, j, collidable);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Clears the entire grid.
|
|
/// </summary>
|
|
/// <param name="collidable">Optionally set to true to clear grid with collidable cells.</param>
|
|
public void Clear(bool collidable = false) {
|
|
for (var i = 0; i < collisions.Count; i++) {
|
|
collisions[i] = collidable;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the collision status of a tile on the GridCollider.
|
|
/// </summary>
|
|
/// <param name="x">The X position of the tile.</param>
|
|
/// <param name="y">The Y position of the tile.</param>
|
|
/// <returns>True if the tile is collidable.</returns>
|
|
public bool GetTile(int x, int y) {
|
|
if (x < 0 || y < 0) return false;
|
|
if (x >= TileColumns || y >= TileRows) return false;
|
|
var index = Util.OneDee((int)TileColumns, (int)x, (int)y);
|
|
if (index >= collisions.Count) return false;
|
|
return collisions[index];
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the collision status of a tile at a position in the Scene.
|
|
/// </summary>
|
|
/// <param name="x">The X position in the Scene.</param>
|
|
/// <param name="y">The Y position in the Scene.</param>
|
|
/// <returns>True if the tile at that position is collidable.</returns>
|
|
public bool GetTileAtPosition(float x, float y) {
|
|
var ox = x;
|
|
var oy = y;
|
|
|
|
x -= Left;
|
|
y -= Top;
|
|
|
|
x = (float)Math.Floor(x / TileWidth);
|
|
y = (float)Math.Floor(y / TileHeight);
|
|
|
|
return GetTile((int)x, (int)y);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Check for a collidable tile in a rectangle on the GridCollider.
|
|
/// </summary>
|
|
/// <param name="x">The X position of the top left corner of the rectangle.</param>
|
|
/// <param name="y">The Y position of the top left corner of the rectangle.</param>
|
|
/// <param name="width">The width in tiles of the rectangle.</param>
|
|
/// <param name="height">The height in tiles of the rectangle.</param>
|
|
/// <param name="collidable">True if any tile in the rectangle is collidable.</param>
|
|
public bool GetRect(float x, float y, float x2, float y2, bool usingGrid = true) {
|
|
//adjust for grid position
|
|
x -= Left;
|
|
x2 -= Left;
|
|
y -= Top;
|
|
y2 -= Top;
|
|
|
|
if (!usingGrid) {
|
|
x = (int)(Util.SnapToGrid(x, TileWidth) / TileWidth);
|
|
y = (int)(Util.SnapToGrid(y, TileHeight) / TileHeight);
|
|
x2 = (int)(Util.SnapToGrid(x2, TileWidth) / TileWidth);
|
|
y2 = (int)(Util.SnapToGrid(y2, TileHeight) / TileHeight);
|
|
}
|
|
|
|
for (int i = (int)x; i <= x2; i++) {
|
|
for (int j = (int)y; j <= y2; j++) {
|
|
if (GetTile(i, j)) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Convert a string into tiles on the GridCollider.
|
|
/// </summary>
|
|
/// <param name="source">The source string data.</param>
|
|
/// <param name="empty">The character representing an empty space on the grid.</param>
|
|
/// <param name="filled">The character representing a collidable space on the grid.</param>
|
|
public void LoadString(string source, char empty = '0', char filled = '1') {
|
|
int xx = 0, yy = 0;
|
|
|
|
for (int i = 0; i < source.Length; i++) {
|
|
if (source[i] != empty && source[i] != filled) continue;
|
|
|
|
if (xx == TileColumns) {
|
|
xx = 0;
|
|
yy++;
|
|
}
|
|
|
|
SetTile(xx, yy, source[i] == filled);
|
|
|
|
xx++;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Convert a CSV file into tiles on the GridCollider.
|
|
/// </summary>
|
|
/// <param name="source">The source CSV data.</param>
|
|
/// <param name="empty">String representing an empty space on the grid.</param>
|
|
/// <param name="filled">String representing a collidable space on the grid.</param>
|
|
/// <param name="columnSep">The separator character for columns.</param>
|
|
/// <param name="rowSep">The separator character for rows.</param>
|
|
public void LoadCSV(string source, string empty = "0", string filled = "1", char columnSep = ',', char rowSep = '\n') {
|
|
string[] row = source.Split(rowSep);
|
|
int rows = row.Length;
|
|
string[] col;
|
|
int cols;
|
|
int x;
|
|
int y;
|
|
|
|
for (y = 0; y < rows; y++) {
|
|
if (row[y] == "") {
|
|
continue;
|
|
}
|
|
|
|
col = row[y].Split(columnSep);
|
|
cols = col.Length;
|
|
for (x = 0; x < cols; x++) {
|
|
if (col[x].Equals("") || !col[x].Equals(filled)) {
|
|
continue;
|
|
}
|
|
|
|
SetTile(x, y, col[x].Equals(filled));
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Load collision data from a Tilemap.
|
|
/// </summary>
|
|
/// <param name="source">The source Tilemap.</param>
|
|
/// <param name="layerDepth">The layer of tiles to use to mark collisions.</param>
|
|
public void LoadTilemap(Tilemap source, int layerDepth) {
|
|
if (source.Width != Width) throw new ArgumentException("Tilemap size and tile size must match grid size and tile size.");
|
|
if (source.Height != Height) throw new ArgumentException("Tilemap size and tile size must match grid size and tile size.");
|
|
if (source.TileWidth != TileWidth) throw new ArgumentException("Tilemap size and tile size must match grid size and tile size.");
|
|
if (source.TileWidth != TileWidth) throw new ArgumentException("Tilemap size and tile size must match grid size and tile size.");
|
|
foreach(var t in source.GetTiles(layerDepth)) {
|
|
SetTile(t.X / TileWidth, t.Y / TileHeight, true);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Load collision data from a Tilemap.
|
|
/// </summary>
|
|
/// <param name="source">The source Tilemap.</param>
|
|
/// <param name="layerName">The layer of tiles to use to mark collisions.</param>
|
|
public void LoadTilemap(Tilemap source, string layerName) {
|
|
LoadTilemap(source, source.LayerDepth(layerName));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Load collision data from a Tilemap.
|
|
/// </summary>
|
|
/// <param name="source">The source Tilemap.</param>
|
|
/// <param name="layerName">The layer of tiles to use to mark collisions.</param>
|
|
public void LoadTilemap(Tilemap source, Enum layerName) {
|
|
LoadTilemap(source, source.LayerDepth(layerName));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Draw the collider for debug purposes.
|
|
/// </summary>
|
|
public override void Render(Color color = null) {
|
|
base.Render(color);
|
|
if (color == null) color = Color.Red;
|
|
|
|
if (Entity == null) return;
|
|
|
|
float viewLeft = Game.Instance.Scene.CameraX - TileWidth;
|
|
float viewRight = Game.Instance.Scene.CameraX + Game.Instance.Scene.CameraWidth;
|
|
float viewTop = Game.Instance.Scene.CameraY - TileHeight;
|
|
float viewBottom = Game.Instance.Scene.CameraY + Game.Instance.Scene.CameraHeight;
|
|
|
|
for (int i = 0; i < TileColumns; i++) {
|
|
for (int j = 0; j < TileRows; j++) {
|
|
|
|
if (Left + i * TileWidth > viewLeft && Left + i * TileWidth < viewRight && Top + j * TileHeight > viewTop && Top + j * TileHeight < viewBottom )
|
|
{
|
|
if (GetTile(i, j))
|
|
{
|
|
Draw.Rectangle(Left + i * TileWidth + 1, Top + j * TileHeight + 1, TileWidth - 2, TileHeight - 2, Color.None, color, 1f);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
}
|
|
}
|