Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- using Microsoft.Xna.Framework;
- using Microsoft.Xna.Framework.Content;
- using Microsoft.Xna.Framework.Graphics;
- using System;
- using System.Collections.Generic;
- namespace Project.TileMaps
- {
- /// <summary>
- /// Identifies rendering options for layers in a <see cref="TileMap"/>.
- /// </summary>
- [Flags] public enum LayerSettings : uint
- {
- Invisible = 0x0,
- BG_Settings = 0xFFFF,
- //--------------------------------------------------------------------
- BG_Opacity = 0x1F,
- //---------------------------------
- BG_OpacitySixteenth = 0x1,
- BG_OpacityEighth = 0x2,
- BG_OpacityQuarter = 0x4,
- BG_OpacityHalf = 0x8,
- BG_OpacityFull = 0x10,
- BG_BlendMode = 0x60,
- //---------------------------------
- BG_InvertBlending = 0x20,
- BG_EnableBlending = 0x40,
- BG_UseLightingTileSet = 0x80,
- BG_DisableColorTransform = 0x100,
- OBJ_Settings = 0xFFFF0000,
- //--------------------------------------------------------------------
- OBJ_Opacity = 0x1F0000,
- //---------------------------------
- OBJ_OpacitySixteenth = 0x10000,
- OBJ_OpacityEighth = 0x20000,
- OBJ_OpacityQuarter = 0x40000,
- OBJ_OpacityHalf = 0x80000,
- OBJ_OpacityFull = 0x100000,
- OBJ_BlendMode = 0x600000,
- //---------------------------------
- OBJ_InvertBlending = 0x200000,
- OBJ_EnableBlending = 0x400000,
- OBJ_DisableColorTransform = 0x1000000,
- }
- public class Entity
- {
- public Vector2 WorldPosition;
- }
- /// <summary>
- /// Represents the drawable aspect of an entity in the game world.
- /// </summary>
- public class SpriteComponent
- {
- public Entity Master;
- public int Layer;
- public Texture2D Texture;
- public Rectangle[] Frames;
- public int FrameIndex;
- }
- public class AnimationSequence
- {
- public int[] Animations;
- public int Progress;
- }
- public struct SpriteAnimation
- {
- public int[] FrameIndices;
- public TimeSpan[] FrameDurations;
- public Vector2[] FramePositions;
- }
- public class AnimationComponent
- {
- public SpriteComponent Target;
- public int CompletedFrameCount;
- public TimeSpan Progress;
- public SpriteAnimation[] AnimationDefinitions;
- public int CurrentDefinitonIndex;
- public AnimationSequence ActiveSequence;
- public AnimationSequence DefaultSequence;
- }
- /// <summary>
- /// Represents a game level constructed from square tiles.
- /// </summary>
- public class TileMap
- {
- #region Constants
- /// <summary>
- /// Interpolates between source and destination color, without blending alpha. The destination alpha is used instead.
- /// </summary>
- public static readonly BlendState AlphaBlend = new BlendState()
- {
- ColorSourceBlend = Blend.SourceAlpha,
- ColorDestinationBlend = Blend.InverseSourceAlpha,
- ColorBlendFunction = BlendFunction.Add,
- AlphaSourceBlend = Blend.Zero,
- AlphaDestinationBlend = Blend.One,
- AlphaBlendFunction = BlendFunction.Add
- };
- /// <summary>
- /// Adds the source color to the destination color, without blending alpha. The destination alpha is used instead.
- /// </summary>
- public static readonly BlendState AdditiveBlend = new BlendState()
- {
- ColorSourceBlend = Blend.SourceAlpha,
- ColorDestinationBlend = Blend.One,
- ColorBlendFunction = BlendFunction.Add,
- AlphaSourceBlend = Blend.Zero,
- AlphaDestinationBlend = Blend.One,
- AlphaBlendFunction = BlendFunction.Add
- };
- /// <summary>
- /// Subtracts the source color from the destination color, without blending alpha. The destination alpha is used instead.
- /// </summary>
- public static readonly BlendState SubtractiveBlend = new BlendState()
- {
- ColorSourceBlend = Blend.SourceAlpha,
- ColorDestinationBlend = Blend.One,
- ColorBlendFunction = BlendFunction.ReverseSubtract,
- AlphaSourceBlend = Blend.Zero,
- AlphaDestinationBlend = Blend.One,
- AlphaBlendFunction = BlendFunction.Add
- };
- static readonly BlendState[] BlendModes =
- {
- AlphaBlend,
- AlphaBlend,
- AdditiveBlend,
- SubtractiveBlend
- };
- /// <summary>
- /// Stores eight bit values for construction of tinting colors when blending layers.
- /// </summary>
- static readonly byte[] OpacityMultipliers =
- {
- 0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70,
- 0x80, 0x90, 0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0,
- // Indices 16..31 refer to full opacity
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
- };
- /// <summary>
- /// Stores the four major angles of rotation in radians.
- /// </summary>
- static readonly float[] Rotations =
- {
- 0f,
- MathHelper.PiOver2,
- MathHelper.Pi,
- MathHelper.PiOver2 * 3
- };
- #endregion
- /// <summary>
- /// Constructs an empty map. For use by the ContentSerializer.
- /// Textures are initialized to null, so a failed texture load crashes the program.
- /// Arrays are initialized as empty, so if the map does not render or animate, one of those failed.
- /// </summary>
- public TileMap()
- {
- tileAliases = new Dictionary<ushort, ushort>();
- background = null;
- lighting = null;
- parallax = null;
- layers = new LayerSettings[0];
- blocks = new ushort[0, 0, 0];
- animDefinitions = new TileAnimation[0];
- }
- public TileMap(Texture2D tileset, Texture2D backdrop)
- {
- background = tileset;
- lighting = null;
- // parallax = backdrop;
- parallaxSpeed = new Vector2(.5f);
- layers = new LayerSettings[]
- {
- LayerSettings.BG_OpacityFull,
- LayerSettings.BG_OpacityFull,
- /*LayerSettings.DisableColorTransform |*/ LayerSettings.BG_EnableBlending | LayerSettings.BG_OpacityHalf,
- LayerSettings.BG_OpacityFull | LayerSettings.OBJ_OpacityFull,
- };
- animDefinitions = new TileAnimation[] {
- new TileAnimation(TileMapView.MakeTileID(14, 26),
- new ushort[] { TileMapView.MakeTileID(14, 26), TileMapView.MakeTileID(15, 26) },
- new TimeSpan[] { TimeSpan.FromMilliseconds(125) })
- };
- tileAliases = new Dictionary<ushort, ushort>();
- blocks = new ushort[4, 32, 18];
- for (int x = 0; x < 32; x++)
- for (int y = 0; y < 18; y++)
- {
- blocks[0, x, y] = 0x485;
- blocks[1, x, y] = blocks[2, x, y] = blocks[3, x, y] = 0xFFF;
- }
- blocks[1, 7, 9] = TileMapView.MakeTileID(14, 26);
- blocks[2, 6, 8] = TileMapView.MakeTileID(0, 49);
- blocks[2, 6, 9] = TileMapView.MakeTileID(0, 50);
- blocks[2, 6, 10] = TileMapView.MakeTileID(0, 51);
- blocks[2, 7, 8] = TileMapView.MakeTileID(1, 49);
- blocks[2, 7, 9] = TileMapView.MakeTileID(1, 50);
- blocks[2, 7, 10] = TileMapView.MakeTileID(1, 51);
- blocks[2, 8, 8] = TileMapView.MakeTileID(2, 49);
- blocks[2, 8, 9] = TileMapView.MakeTileID(2, 50);
- blocks[2, 8, 10] = TileMapView.MakeTileID(2, 51);
- blocks[0, 1, 2] = TileMapView.MakeTileID(3, 25, SpriteEffects.None, 2);
- border = new ushort[1, 2, 2] {
- {
- { TileMapView.MakeTileID(3, 19), TileMapView.MakeTileID(1, 11) },
- { TileMapView.MakeTileID(1, 11), TileMapView.MakeTileID(3, 19) }
- }
- };
- //border = new ushort[0, 0, 0];
- sprites = new List<SpriteComponent>(3);
- sprites.Add(new SpriteComponent());
- sprites.Add(new SpriteComponent());
- sprites.Add(new SpriteComponent());
- sprites[0].Texture = tileset;
- sprites[0].Frames = new Rectangle[] { new Rectangle(48, 0, 16, 32), new Rectangle(64, 0, 16, 32), new Rectangle(80, 0, 16, 32) };
- sprites[0].Master = new Entity();
- sprites[0].Master.WorldPosition = new Vector2(0.5f, 1.5f);
- sprites[0].Layer = 3;
- sprites[1].Texture = tileset;
- sprites[1].Frames = new Rectangle[] { new Rectangle(64, 0, 16, 32) };
- sprites[1].Master = new Entity();
- sprites[1].Master.WorldPosition = new Vector2(1.0f, 1.0f);
- sprites[1].Layer = 0;
- sprites[2].Texture = tileset;
- sprites[2].Frames = new Rectangle[] { new Rectangle(80, 0, 16, 32) };
- sprites[2].Master = new Entity();
- sprites[2].Master.WorldPosition = new Vector2(1.5f, 0.5f);
- sprites[2].Layer = 0;
- spriteAnimations = new List<AnimationComponent>(1);
- spriteAnimations.Add(new AnimationComponent());
- spriteAnimations[0].Target = sprites[0];
- spriteAnimations[0].AnimationDefinitions = new SpriteAnimation[]
- {
- new SpriteAnimation()
- {
- FrameDurations = new TimeSpan[] { TimeSpan.FromSeconds(0.11), TimeSpan.FromSeconds(0.11), TimeSpan.FromSeconds(0.11) },
- FrameIndices = new int[] { 0, 1, 2 },
- FramePositions = new Vector2[] { new Vector2(0.33f, 0), new Vector2(0.34f, 0), new Vector2(0.33f, 0) },
- },
- new SpriteAnimation()
- {
- FrameDurations = new TimeSpan[] { TimeSpan.FromSeconds(0.11), TimeSpan.FromSeconds(0.11), TimeSpan.FromSeconds(0.11) },
- FrameIndices = new int[] { 0, 1, 2 },
- FramePositions = new Vector2[] { new Vector2(0, 0.33f), new Vector2(0, 0.34f), new Vector2(0, 0.33f) },
- },
- new SpriteAnimation()
- {
- FrameDurations = new TimeSpan[] { TimeSpan.FromSeconds(0.11), TimeSpan.FromSeconds(0.11), TimeSpan.FromSeconds(0.11) },
- FrameIndices = new int[] { 0, 1, 2 },
- FramePositions = new Vector2[] { new Vector2(-0.33f, 0), new Vector2(-0.34f, 0), new Vector2(-0.33f, 0) },
- },
- new SpriteAnimation()
- {
- FrameDurations = new TimeSpan[] { TimeSpan.FromSeconds(0.11), TimeSpan.FromSeconds(0.11), TimeSpan.FromSeconds(0.11) },
- FrameIndices = new int[] { 0, 1, 2 },
- FramePositions = new Vector2[] { new Vector2(0, -0.33f), new Vector2(0, -0.34f), new Vector2(0, -0.33f) },
- },
- };
- spriteAnimations[0].ActiveSequence = new AnimationSequence();
- spriteAnimations[0].ActiveSequence.Animations = new int[] { 0, 1, 2, 3 };
- spriteAnimations[0].DefaultSequence = spriteAnimations[0].ActiveSequence;
- }
- [ContentSerializer] Texture2D parallax;
- [ContentSerializer] Vector2 parallaxSpeed;
- [ContentSerializer] Texture2D background;
- [ContentSerializer] Texture2D lighting;
- [ContentSerializer] LayerSettings[] layers;
- [ContentSerializer] ushort[,,] blocks;
- [ContentSerializer] ushort[,,] border;
- [ContentSerializer] TileAnimation[] animDefinitions;
- [ContentSerializer] List<SpriteComponent> sprites;
- [ContentSerializer] List<AnimationComponent> spriteAnimations;
- Dictionary<ushort, ushort> tileAliases;
- #region Properties
- /// <summary>
- /// Gets the rendering options for all individual layers of the TileMap.
- /// </summary>
- public LayerSettings[] Layers { get { return layers; } }
- /// <summary>
- /// Gets the bock data of the TileMap.
- /// </summary>
- public ushort[,,] Blocks { get { return blocks; } }
- /// <summary>
- /// Gets the width of the map in tiles.
- /// </summary>
- public int Width
- {
- get { return blocks.GetLength(1); }
- }
- /// <summary>
- /// Gets the height of the map in tiles.
- /// </summary>
- public int Height
- {
- get { return blocks.GetLength(2); }
- }
- #endregion
- #region Methods
- /// <summary>
- /// Updates animation and map object logic.
- /// </summary>
- public void Update(TimeSpan worldTimeDelta, Profiler profiler)
- {
- profiler.BeginOperation("Update Tile Map Animation Timers ({0})", animDefinitions.Length);
- for (int i = 0; i < animDefinitions.Length; i++)
- tileAliases[animDefinitions[i].TileID] = animDefinitions[i].Update(worldTimeDelta);
- profiler.EndOperation();
- profiler.BeginOperation("Update Sprite Animation Timers ({0})", spriteAnimations.Count);
- foreach (AnimationComponent nextTimer in spriteAnimations)
- {
- if (nextTimer != null && nextTimer.CurrentDefinitonIndex >= 0)
- {
- nextTimer.Progress += worldTimeDelta;
- TimeSpan curFrameDuration = nextTimer
- .AnimationDefinitions[nextTimer.CurrentDefinitonIndex]
- .FrameDurations[nextTimer.CompletedFrameCount];
- float delta = (float)worldTimeDelta.Ticks / curFrameDuration.Ticks; // TODO: Don't divide by zero!
- Vector2 positionOffset = delta * nextTimer
- .AnimationDefinitions[nextTimer.CurrentDefinitonIndex]
- .FramePositions[nextTimer.CompletedFrameCount];
- nextTimer.Target.Master.WorldPosition += positionOffset;
- if (nextTimer.Progress >= curFrameDuration)
- {
- nextTimer.Progress -= curFrameDuration;
- nextTimer.CompletedFrameCount++;
- if (nextTimer.CompletedFrameCount >= nextTimer.AnimationDefinitions[nextTimer.CurrentDefinitonIndex].FrameIndices.Length)
- {
- if (nextTimer.ActiveSequence != null)
- {
- nextTimer.ActiveSequence.Progress++;
- if (nextTimer.ActiveSequence.Progress >= nextTimer.ActiveSequence.Animations.Length)
- {
- if (nextTimer.ActiveSequence == nextTimer.DefaultSequence)
- nextTimer.ActiveSequence.Progress = 0;
- else
- nextTimer.ActiveSequence = nextTimer.DefaultSequence;
- }
- nextTimer.CurrentDefinitonIndex = nextTimer.ActiveSequence.Animations[nextTimer.ActiveSequence.Progress];
- }
- else
- nextTimer.CurrentDefinitonIndex = -1;
- nextTimer.Progress = TimeSpan.Zero;
- nextTimer.CompletedFrameCount = 0;
- }
- else
- {
- nextTimer.Target.FrameIndex = nextTimer
- .AnimationDefinitions[nextTimer.CurrentDefinitonIndex]
- .FrameIndices[nextTimer.CompletedFrameCount];
- }
- }
- }
- }
- profiler.EndOperation();
- }
- /// <summary>
- /// Renders the tile map and all objects on it.
- /// </summary>
- public void Draw(Effect effect, SpriteBatch spriteBatch, TopDownCamera camera, VirtualScreen screen, Profiler profiler)
- {
- profiler.BeginOperation("Draw Tile Map");
- if (parallax != null)
- {
- effect.Parameters["cameraTransform"].SetValue(camera.CreateParallaxView(parallaxSpeed) * screen.CreateSpriteBatchCompatibilityMatrix());
- spriteBatch.Begin(SpriteSortMode.Deferred, null, SamplerState.PointClamp, null, null, effect);
- spriteBatch.Draw(parallax, Vector2.Zero, Color.White);
- spriteBatch.End();
- }
- profiler.BeginOperation("Sort {0} sprites", sprites.Count);
- // Sort sprites by their Y position
- // TODO: Look into getting this to work with SpriteSortMode.FrontToBack, currently it doesn't seem to render anything.
- // TODO: This doesn't support NULLs either. Fix this maybe?
- effect.Parameters["cameraTransform"].SetValue(camera.Transform * screen.CreateSpriteBatchCompatibilityMatrix());
- sprites.Sort((a, b) => Comparer<float>.Default.Compare(a.Master.WorldPosition.Y, b.Master.WorldPosition.Y));
- profiler.EndOperation();
- for (int i = 0; i < blocks.GetLength(0); i++)
- {
- LayerSettings Background = layers[i] & LayerSettings.BG_Settings;
- LayerSettings Objects = layers[i] & LayerSettings.OBJ_Settings;
- if (Background != LayerSettings.Invisible)
- {
- profiler.BeginOperation("Draw Tile Map Layer {0}", i);
- int multiplier = OpacityMultipliers[(int)(Background & LayerSettings.BG_Opacity)];
- DrawBackgroundLayer(spriteBatch, camera, i,
- Background.HasFlag(LayerSettings.BG_DisableColorTransform) ? null : effect,
- Background.HasFlag(LayerSettings.BG_UseLightingTileSet) ? lighting : background,
- BlendModes[(int)(Background & LayerSettings.BG_BlendMode) >> 5],
- Background.HasFlag(LayerSettings.BG_EnableBlending) ?
- new Color(multiplier, multiplier, multiplier) : // Multiply RGB values for additive and subtractive blending
- new Color(0xFF, 0xFF, 0xFF, multiplier) // Multiply Alpha for alpha blending
- );
- profiler.EndOperation();
- }
- if (Objects != LayerSettings.Invisible)
- {
- profiler.BeginOperation("Draw Sprite Layer {0}", i);
- int multiplier = OpacityMultipliers[(int)(Objects & LayerSettings.OBJ_Opacity) >> 16];
- DrawSpriteLayer(spriteBatch, camera, i,
- Objects.HasFlag(LayerSettings.OBJ_DisableColorTransform) ? null : effect,
- BlendModes[(int)(Objects & LayerSettings.OBJ_BlendMode) >> 21],
- Objects.HasFlag(LayerSettings.OBJ_EnableBlending) ?
- new Color(multiplier, multiplier, multiplier) : // Multiply RGB values for additive and subtractive blending
- new Color(0xFF, 0xFF, 0xFF, multiplier) // Multiply Alpha for alpha blending
- );
- profiler.EndOperation();
- }
- }
- profiler.EndOperation();
- }
- void DrawBackgroundLayer(SpriteBatch spriteBatch, TopDownCamera camera, int layerIndex, Effect effect, Texture2D tileset, BlendState blending, Color tint)
- {
- spriteBatch.Begin(SpriteSortMode.Deferred, blending, SamplerState.PointClamp, null, null, effect, camera.Transform);
- if (effect != null) effect.CurrentTechnique.Passes[0].Apply();
- // NOTE: The lo values have to be one less than they theoretically should. Is the math off?
- Vector4 viewport = camera.Viewport;
- int x_lo = (int)viewport.X - 2;
- int x_hi = (int)viewport.Z + 1;
- int y_lo = (int)viewport.Y - 2;
- int y_hi = (int)viewport.W + 1;
- int borderLayers = border.GetLength(0);
- int borderWidth = border.GetLength(1);
- int borderHeight = border.GetLength(2);
- Rectangle destination = new Rectangle(0, 0, 1, 1);
- Rectangle source = new Rectangle(0, 0, 16, 16);
- int angle = 0;
- SpriteEffects effects = SpriteEffects.None;
- ushort id = 0;
- for (int x = x_lo; x <= x_hi; x++)
- {
- for (int y = y_lo; y <= y_hi; y++)
- {
- if (x < 0 || x >= Width || y < 0 || y >= Height)
- {
- if (layerIndex >= borderLayers)
- continue;
- id = border[layerIndex,
- ((x % borderWidth) + borderWidth) % borderWidth,
- ((y % borderHeight) + borderHeight) % borderHeight];
- }
- else
- id = blocks[layerIndex, x, y];
- angle = (id >> 12) & 3;
- effects = (SpriteEffects)((id >> 14) & 3);
- destination.X = x + ((angle & 1) ^ (angle >> 1));
- destination.Y = y + (angle >> 1);
- id &= 0xFFF;
- if (tileAliases.ContainsKey(id))
- id = tileAliases[id];
- source.X = (id & 63) * 16;
- source.Y = (id >> 6) * 16;
- spriteBatch.Draw(tileset, destination, source, tint, Rotations[angle], Vector2.Zero, effects, 0);
- }
- }
- spriteBatch.End();
- }
- void DrawSpriteLayer(SpriteBatch spriteBatch, TopDownCamera camera, int layerIndex, Effect effect, BlendState blending, Color tint)
- {
- spriteBatch.Begin(SpriteSortMode.Deferred, blending, SamplerState.PointClamp, null, null, effect, camera.Transform);
- foreach (SpriteComponent nextSprite in sprites)
- {
- if (nextSprite == null)
- continue;
- if (nextSprite.Layer == layerIndex)
- {
- Rectangle source = nextSprite.Frames[nextSprite.FrameIndex];
- Vector2 position = nextSprite.Master.WorldPosition;
- position.X -= source.Width / 32f;
- position.Y -= source.Height / 16f - 0.5f;
- spriteBatch.Draw(nextSprite.Texture, position, source, tint, 0, Vector2.Zero, 1 / 16f, SpriteEffects.None, 0f);
- }
- }
- spriteBatch.End();
- }
- #endregion
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement