using System;
namespace Otter {
///
/// Collider that can use an image as a mask. This is not recommended to use for most cases as it can
/// be pretty expensive to process.
///
public class PixelCollider : Collider {
#region Public Fields
///
/// The amount of Alpha a pixel needs to exceed to register as a collision.
/// If 0, any pixel with an alpha above 0 will register as collidable.
///
public float Threshold = 0;
#endregion
#region Public Properties
///
/// The byte array of pixels.
///
public byte[] Pixels {
get { return collideImage.Pixels; }
}
#endregion
#region Private Fields
SFML.Graphics.Image collideImage;
SFML.Graphics.Texture texture;
Texture visibleTexture;
Graphic visibleImage;
bool rendered;
#endregion
#region Constructors
///
/// Creates a pixel collider.
///
/// The source image to create the collider from.
/// The tags to register the collider with.
public PixelCollider(string source, params int[] tags) {
texture = Textures.Load(source);
collideImage = texture.CopyToImage();
Width = texture.Size.X;
Height = texture.Size.Y;
AddTag(tags);
}
public PixelCollider(Texture texture, params int[] tags) {
this.texture = texture.SFMLTexture;
collideImage = this.texture.CopyToImage();
Width = this.texture.Size.X;
Height = this.texture.Size.Y;
AddTag(tags);
}
public PixelCollider(string source, Enum tag, params Enum[] tags) : this(source) {
AddTag(tag);
AddTag(tags);
}
public PixelCollider(Texture texture, Enum tag, params Enum[] tags) : this(texture) {
AddTag(tag);
AddTag(tags);
}
#endregion
#region Private Methods
void InitializeTexture() {
visibleTexture = new Texture((int)Width, (int)Height);
for (var x = 0; x < Width; x++) {
for (var y = 0; y < Height; y++) {
if (PixelAt(x, y)) {
visibleTexture.SetPixel(x, y, Color.Red);
}
else {
visibleTexture.SetPixel(x, y, Color.None);
}
}
}
visibleImage = new Image(visibleTexture);
}
#endregion
#region Public Methods
///
/// Check if a pixel is collidable at x, y.
///
/// The X position of the pixel to check.
/// The Y position of the pixel to check.
/// True if the pixel collides.
public bool PixelAt(int x, int y) {
if (x < 0 || y < 0 || x > Width || y > Height) return false;
if (collideImage.GetPixel((uint)x, (uint)y).A > Threshold) return true;
return false;
}
///
/// Check if a pixel is collidable at X, Y.
///
/// The X position of the pixel to check.
/// The Y position of the pixel to check.
/// The alpha threshold that should register a collision.
/// True if the pixel collides.
public bool PixelAt(int x, int y, float threshold) {
if (x < 0 || y < 0 || x > Width || y > Height) return false;
if (collideImage.GetPixel((uint)x, (uint)y).A > threshold) return true;
return false;
}
///
/// Check if a pixel is collideable at X - Left, Y - Top.
///
/// The X position of the pixel to check.
/// The Y position of the pixel to check.
/// True if the pixel collides.
public bool PixelAtRelative(int x, int y) {
x -= (int)Left;
y -= (int)Top;
return PixelAt(x, y);
}
///
/// Check if a pixel is collideable at X - Left, Y - Top.
///
/// The X position of the pixel to check.
/// The Y position of the pixel to check.
/// The alpha threshold that should register a collision.
/// True if the pixel collides.
public bool PixelAtRelative(int x, int y, float threshold) {
x -= (int)Left;
y -= (int)Top;
return PixelAt(x, y, threshold);
}
///
/// Check if any pixels in the area defined by X, Y, X2, Y2 are collideable.
///
/// The left of the area to check.
/// The top of the area to check.
/// The right of the area to check.
/// The bottom of the area to check.
/// True if the pixel collides.
public bool PixelArea(int x, int y, int x2, int y2) {
for (var i = x; i < x2; i++) {
for (var j = y; j < y2; j++) {
if (PixelAt(i, j)) return true;
}
}
return false;
}
///
/// Check if any pixels in the area defined by X, Y, X2, Y2 are collideable.
///
/// The left of the area to check.
/// The top of the area to check.
/// The right of the area to check.
/// The bottom of the area to check.
/// The alpha threshold that should register a collision.
/// True if the pixel collides.
public bool PixelArea(int x, int y, int x2, int y2, float threshold) {
for (var i = x; i < x2; i++) {
for (var j = y; j < y2; j++) {
if (PixelAt(i, j, threshold)) return true;
}
}
return false;
}
///
/// Draw the collider for debug purposes.
///
public override void Render(Color color = null) {
base.Render(color);
if (color == null) color = Color.Red;
if (Entity == null) return;
if (!rendered) {
rendered = true;
InitializeTexture();
}
visibleImage.Color = color;
Draw.Graphic(visibleImage, Left, Top);
}
#endregion
}
}