mirror of
https://github.com/Ellpeck/MLEM.git
synced 2024-11-22 12:58:33 +01:00
Added DrawExtendedAutoTile to the AutoTiling class
This commit is contained in:
parent
5cb77be410
commit
ad1d6a864e
4 changed files with 93 additions and 24 deletions
|
@ -14,6 +14,7 @@ Additions
|
||||||
- Added StaticSpriteBatch class
|
- Added StaticSpriteBatch class
|
||||||
- Added missing easing functions Quart and Quint to Easings
|
- Added missing easing functions Quart and Quint to Easings
|
||||||
- Added RotationVector extension methods for Matrix and Quaternion
|
- Added RotationVector extension methods for Matrix and Quaternion
|
||||||
|
- Added DrawExtendedAutoTile to the AutoTiling class
|
||||||
|
|
||||||
Improvements
|
Improvements
|
||||||
- Cache TokenizedString inner offsets for non-Left text alignments to improve performance
|
- Cache TokenizedString inner offsets for non-Left text alignments to improve performance
|
||||||
|
|
|
@ -9,6 +9,7 @@ namespace Demos {
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class AutoTilingDemo : Demo {
|
public class AutoTilingDemo : Demo {
|
||||||
|
|
||||||
|
private const int TileSize = 8;
|
||||||
private Texture2D texture;
|
private Texture2D texture;
|
||||||
private string[] layout;
|
private string[] layout;
|
||||||
|
|
||||||
|
@ -17,23 +18,16 @@ namespace Demos {
|
||||||
|
|
||||||
public override void LoadContent() {
|
public override void LoadContent() {
|
||||||
base.LoadContent();
|
base.LoadContent();
|
||||||
// The layout of the texture is important for auto tiling to work correctly.
|
// The layout of the texture is important for auto tiling to work correctly, and is explained in the XML docs for the methods used
|
||||||
// It needs to be laid out as follows: Five tiles aligned horizontally within the texture file, with the following information
|
|
||||||
// 1. The texture used for filling big areas
|
|
||||||
// 2. The texture used for straight, horizontal borders, with the borders facing away from the center
|
|
||||||
// 3. The texture used for outer corners, with the corners facing away from the center
|
|
||||||
// 4. The texture used for straight, vertical borders, with the borders facing away from the center
|
|
||||||
// 5. The texture used for inner corners, with the corners facing away from the center
|
|
||||||
// Note that the AutoTiling.png texture contains an example of this layout
|
|
||||||
this.texture = LoadContent<Texture2D>("Textures/AutoTiling");
|
this.texture = LoadContent<Texture2D>("Textures/AutoTiling");
|
||||||
|
|
||||||
// in this example, a simple string array is used for layout purposes. As the AutoTiling method allows any kind of
|
// in this example, a simple string array is used for layout purposes. As the AutoTiling method allows any kind of
|
||||||
// comparison, the actual implementation can vary (for instance, based on a more in-depth tile map)
|
// comparison, the actual implementation can vary (for instance, based on a more in-depth tile map)
|
||||||
this.layout = new[] {
|
this.layout = new[] {
|
||||||
|
"XXX X ",
|
||||||
|
"XXXXXX",
|
||||||
"XXX X",
|
"XXX X",
|
||||||
"XXXXX",
|
"XXXXX ",
|
||||||
"XX ",
|
|
||||||
"XXXX ",
|
|
||||||
" X "
|
" X "
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -41,13 +35,9 @@ namespace Demos {
|
||||||
public override void DoDraw(GameTime gameTime) {
|
public override void DoDraw(GameTime gameTime) {
|
||||||
this.GraphicsDevice.Clear(Color.Black);
|
this.GraphicsDevice.Clear(Color.Black);
|
||||||
|
|
||||||
// the texture region supplied to the AutoTiling method should only encompass the first filler tile's location and size
|
|
||||||
const int tileSize = 8;
|
|
||||||
var region = new Rectangle(0, 0, tileSize, tileSize);
|
|
||||||
|
|
||||||
// drawing the auto tiles
|
// drawing the auto tiles
|
||||||
this.SpriteBatch.Begin(SpriteSortMode.Deferred, null, SamplerState.PointClamp, transformMatrix: Matrix.CreateScale(10));
|
this.SpriteBatch.Begin(SpriteSortMode.Deferred, null, SamplerState.PointClamp, transformMatrix: Matrix.CreateScale(10));
|
||||||
for (var x = 0; x < 5; x++) {
|
for (var x = 0; x < 6; x++) {
|
||||||
for (var y = 0; y < 5; y++) {
|
for (var y = 0; y < 5; y++) {
|
||||||
// don't draw non-grass tiles ( )
|
// don't draw non-grass tiles ( )
|
||||||
if (this.layout[y][x] != 'X')
|
if (this.layout[y][x] != 'X')
|
||||||
|
@ -61,13 +51,17 @@ namespace Demos {
|
||||||
// and where there are none ( ).
|
// and where there are none ( ).
|
||||||
bool ConnectsTo(int xOff, int yOff) {
|
bool ConnectsTo(int xOff, int yOff) {
|
||||||
// don't auto-tile out of bounds
|
// don't auto-tile out of bounds
|
||||||
if (x1 + xOff < 0 || y1 + yOff < 0 || x1 + xOff >= 5 || y1 + yOff >= 5)
|
if (x1 + xOff < 0 || y1 + yOff < 0 || x1 + xOff >= 6 || y1 + yOff >= 5)
|
||||||
return false;
|
return false;
|
||||||
// check if the neighboring tile is also grass (X)
|
// check if the neighboring tile is also grass (X)
|
||||||
return this.layout[y1 + yOff][x1 + xOff] == 'X';
|
return this.layout[y1 + yOff][x1 + xOff] == 'X';
|
||||||
}
|
}
|
||||||
|
|
||||||
AutoTiling.DrawAutoTile(this.SpriteBatch, new Vector2(x, y) * tileSize, this.texture, region, ConnectsTo, Color.White);
|
// the texture region supplied to the AutoTiling method should only encompass the first filler tile's location and size
|
||||||
|
AutoTiling.DrawAutoTile(this.SpriteBatch, new Vector2(x + 1, y + 1) * TileSize, this.texture, new Rectangle(0, 0, TileSize, TileSize), ConnectsTo, Color.White);
|
||||||
|
|
||||||
|
// when drawing extended auto-tiles, the same rules apply, but the source texture layout is different
|
||||||
|
AutoTiling.DrawExtendedAutoTile(this.SpriteBatch, new Vector2(x + 8, y + 1) * TileSize, this.texture, new Rectangle(0, TileSize * 2, TileSize, TileSize), ConnectsTo, Color.White);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.SpriteBatch.End();
|
this.SpriteBatch.End();
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 604 B After Width: | Height: | Size: 878 B |
|
@ -3,9 +3,8 @@ using Microsoft.Xna.Framework.Graphics;
|
||||||
|
|
||||||
namespace MLEM.Misc {
|
namespace MLEM.Misc {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This class contains a <see cref="DrawAutoTile"/> method that allows users to easily draw a tile with automatic connections.
|
/// This class contains a <see cref="DrawAutoTile"/> method that allows users to easily draw a tile with automatic connections, as well as a more complex <see cref="DrawExtendedAutoTile"/> method.
|
||||||
/// For auto-tiling in this manner to work, auto-tiled textures have to be laid out in a format described in <see cref="DrawAutoTile"/>.
|
/// Note that <see cref="StaticSpriteBatch"/> can also be used for drawing by using the <see cref="AddAutoTile"/> and <see cref="AddExtendedAutoTile"/> methods instead.
|
||||||
/// Note that <see cref="StaticSpriteBatch"/> can also be used for drawing by using the <see cref="AddAutoTile"/> method instead.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class AutoTiling {
|
public static class AutoTiling {
|
||||||
|
|
||||||
|
@ -21,7 +20,7 @@ namespace MLEM.Misc {
|
||||||
/// <item><description>The texture used for straight, vertical borders, with the borders facing away from the center</description></item>
|
/// <item><description>The texture used for straight, vertical borders, with the borders facing away from the center</description></item>
|
||||||
/// <item><description>The texture used for inner corners, with the corners facing away from the center</description></item>
|
/// <item><description>The texture used for inner corners, with the corners facing away from the center</description></item>
|
||||||
/// </list>
|
/// </list>
|
||||||
/// For more information and an example, see https://github.com/Ellpeck/MLEM/blob/main/Demos/AutoTilingDemo.cs#L20-L28.
|
/// For more information and an example, see https://github.com/Ellpeck/MLEM/blob/main/Demos/AutoTilingDemo.cs and its source texture https://github.com/Ellpeck/MLEM/blob/main/Demos/Content/Textures/AutoTiling.png.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="batch">The sprite batch to use for drawing.</param>
|
/// <param name="batch">The sprite batch to use for drawing.</param>
|
||||||
/// <param name="pos">The position to draw at.</param>
|
/// <param name="pos">The position to draw at.</param>
|
||||||
|
@ -53,6 +52,61 @@ namespace MLEM.Misc {
|
||||||
batch.Add(texture, p4, r4, color, 0, orig, sc, SpriteEffects.None, layerDepth);
|
batch.Add(texture, p4, r4, color, 0, orig, sc, SpriteEffects.None, layerDepth);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This method allows for a tiled texture to be drawn in an auto-tiling mode.
|
||||||
|
/// This allows, for example, a grass patch on a tilemap to have nice looking edges that transfer over into a path without any hard edges between tiles.
|
||||||
|
///
|
||||||
|
/// This method is a more complex version of <see cref="DrawAutoTile"/> that overlays separate border textures on a base texture, which also allows for non-rectangular texture areas to be used easily.
|
||||||
|
/// For auto-tiling in this way to work, the tiles have to be laid out as follows: 17 tiles aligned horizontally within the texture file, with the following information:
|
||||||
|
/// <list type="number">
|
||||||
|
/// <item><description>The texture used for filling big areas</description></item>
|
||||||
|
/// <item><description>The texture used for straight, horizontal borders, with the borders facing away from the center, split up into four parts: top left, then top right, then bottom left, then bottom right</description></item>
|
||||||
|
/// <item><description>The texture used for outer corners, with the corners facing away from the center, split up into four parts: top left, then top right, then bottom left, then bottom right</description></item>
|
||||||
|
/// <item><description>The texture used for straight, vertical borders, with the borders facing away from the center, split up into four parts: top left, then top right, then bottom left, then bottom right</description></item>
|
||||||
|
/// <item><description>The texture used for inner corners, with the corners facing away from the center, split up into four parts: top left, then top right, then bottom left, then bottom right</description></item>
|
||||||
|
/// </list>
|
||||||
|
/// For more information and an example, see https://github.com/Ellpeck/MLEM/blob/main/Demos/AutoTilingDemo.cs and its source texture https://github.com/Ellpeck/MLEM/blob/main/Demos/Content/Textures/AutoTiling.png.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="batch">The sprite batch to use for drawing.</param>
|
||||||
|
/// <param name="pos">The position to draw at.</param>
|
||||||
|
/// <param name="texture">The texture to use for drawing.</param>
|
||||||
|
/// <param name="textureRegion">The location of the first texture region, as described in the summary.</param>
|
||||||
|
/// <param name="connectsTo">A function that determines whether two positions should connect.</param>
|
||||||
|
/// <param name="color">The color to draw with.</param>
|
||||||
|
/// <param name="origin">The origin to draw from.</param>
|
||||||
|
/// <param name="scale">The scale to draw with.</param>
|
||||||
|
/// <param name="layerDepth">The layer depth to draw with.</param>
|
||||||
|
public static void DrawExtendedAutoTile(SpriteBatch batch, Vector2 pos, Texture2D texture, Rectangle textureRegion, ConnectsTo connectsTo, Color color, Vector2? origin = null, Vector2? scale = null, float layerDepth = 0) {
|
||||||
|
var orig = origin ?? Vector2.Zero;
|
||||||
|
var sc = scale ?? Vector2.One;
|
||||||
|
var (p1, r1, p2, r2, p3, r3, p4, r4) = CalculateExtendedAutoTile(pos, textureRegion, connectsTo, sc);
|
||||||
|
batch.Draw(texture, pos, textureRegion, color, 0, orig, sc, SpriteEffects.None, layerDepth);
|
||||||
|
if (r1 != Rectangle.Empty)
|
||||||
|
batch.Draw(texture, p1, r1, color, 0, orig, sc, SpriteEffects.None, layerDepth);
|
||||||
|
if (r2 != Rectangle.Empty)
|
||||||
|
batch.Draw(texture, p2, r2, color, 0, orig, sc, SpriteEffects.None, layerDepth);
|
||||||
|
if (r3 != Rectangle.Empty)
|
||||||
|
batch.Draw(texture, p3, r3, color, 0, orig, sc, SpriteEffects.None, layerDepth);
|
||||||
|
if (r4 != Rectangle.Empty)
|
||||||
|
batch.Draw(texture, p4, r4, color, 0, orig, sc, SpriteEffects.None, layerDepth);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="DrawExtendedAutoTile"/>
|
||||||
|
public static void AddExtendedAutoTile(StaticSpriteBatch batch, Vector2 pos, Texture2D texture, Rectangle textureRegion, ConnectsTo connectsTo, Color color, float rotation = 0, Vector2? origin = null, Vector2? scale = null, float layerDepth = 0) {
|
||||||
|
var orig = origin ?? Vector2.Zero;
|
||||||
|
var sc = scale ?? Vector2.One;
|
||||||
|
var (p1, r1, p2, r2, p3, r3, p4, r4) = CalculateExtendedAutoTile(pos, textureRegion, connectsTo, sc);
|
||||||
|
batch.Add(texture, pos, textureRegion, color, 0, orig, sc, SpriteEffects.None, layerDepth);
|
||||||
|
if (r1 != Rectangle.Empty)
|
||||||
|
batch.Add(texture, p1, r1, color, 0, orig, sc, SpriteEffects.None, layerDepth);
|
||||||
|
if (r2 != Rectangle.Empty)
|
||||||
|
batch.Add(texture, p2, r2, color, 0, orig, sc, SpriteEffects.None, layerDepth);
|
||||||
|
if (r3 != Rectangle.Empty)
|
||||||
|
batch.Add(texture, p3, r3, color, 0, orig, sc, SpriteEffects.None, layerDepth);
|
||||||
|
if (r4 != Rectangle.Empty)
|
||||||
|
batch.Add(texture, p4, r4, color, 0, orig, sc, SpriteEffects.None, layerDepth);
|
||||||
|
}
|
||||||
|
|
||||||
private static (Vector2, Rectangle, Vector2, Rectangle, Vector2, Rectangle, Vector2, Rectangle) CalculateAutoTile(Vector2 pos, Rectangle textureRegion, ConnectsTo connectsTo, Vector2 scale) {
|
private static (Vector2, Rectangle, Vector2, Rectangle, Vector2, Rectangle, Vector2, Rectangle) CalculateAutoTile(Vector2 pos, Rectangle textureRegion, ConnectsTo connectsTo, Vector2 scale) {
|
||||||
var up = connectsTo(0, -1);
|
var up = connectsTo(0, -1);
|
||||||
var down = connectsTo(0, 1);
|
var down = connectsTo(0, 1);
|
||||||
|
@ -73,6 +127,26 @@ namespace MLEM.Misc {
|
||||||
new Vector2(pos.X + w2 * scale.X, pos.Y + h2 * scale.Y), new Rectangle(textureRegion.X + w2 + xDr * w, textureRegion.Y + h2, w2, h2));
|
new Vector2(pos.X + w2 * scale.X, pos.Y + h2 * scale.Y), new Rectangle(textureRegion.X + w2 + xDr * w, textureRegion.Y + h2, w2, h2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static (Vector2, Rectangle, Vector2, Rectangle, Vector2, Rectangle, Vector2, Rectangle) CalculateExtendedAutoTile(Vector2 pos, Rectangle textureRegion, ConnectsTo connectsTo, Vector2 scale) {
|
||||||
|
var up = connectsTo(0, -1);
|
||||||
|
var down = connectsTo(0, 1);
|
||||||
|
var left = connectsTo(-1, 0);
|
||||||
|
var right = connectsTo(1, 0);
|
||||||
|
var xUl = up && left ? connectsTo(-1, -1) ? -1 : 13 : left ? 1 : up ? 9 : 5;
|
||||||
|
var xUr = up && right ? connectsTo(1, -1) ? -1 : 14 : right ? 2 : up ? 10 : 6;
|
||||||
|
var xDl = down && left ? connectsTo(-1, 1) ? -1 : 15 : left ? 3 : down ? 11 : 7;
|
||||||
|
var xDr = down && right ? connectsTo(1, 1) ? -1 : 16 : right ? 4 : down ? 12 : 8;
|
||||||
|
|
||||||
|
var (w, h) = textureRegion.Size;
|
||||||
|
var (w2, h2) = new Point(w / 2, h / 2);
|
||||||
|
|
||||||
|
return (
|
||||||
|
new Vector2(pos.X, pos.Y), xUl < 0 ? Rectangle.Empty : new Rectangle(textureRegion.X + xUl * w, textureRegion.Y, w2, h2),
|
||||||
|
new Vector2(pos.X + w2 * scale.X, pos.Y), xUr < 0 ? Rectangle.Empty : new Rectangle(textureRegion.X + w2 + xUr * w, textureRegion.Y, w2, h2),
|
||||||
|
new Vector2(pos.X, pos.Y + h2 * scale.Y), xDl < 0 ? Rectangle.Empty : new Rectangle(textureRegion.X + xDl * w, textureRegion.Y + h2, w2, h2),
|
||||||
|
new Vector2(pos.X + w2 * scale.X, pos.Y + h2 * scale.Y), xDr < 0 ? Rectangle.Empty : new Rectangle(textureRegion.X + w2 + xDr * w, textureRegion.Y + h2, w2, h2));
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A delegate function that determines if a given offset position connects to an auto-tile location.
|
/// A delegate function that determines if a given offset position connects to an auto-tile location.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
Loading…
Reference in a new issue