Camera super slow but way more efficient

master
BraydonKains 5 years ago
parent d90bf6dffe
commit 15612f660a

@ -1,21 +1,26 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace SeeNoEvil.Level {
public struct Camera {
public int width;
public int height;
public int x;
public int y;
}
public class Camera {
public Matrix Transform {get; private set;}
public Viewport Viewport {get; private set;}
public Vector2 Centre {get; set;}
// public class Camera {
// private Viewport m_dimension;
private Vector3 TranslationVector =>
new Vector3(-Centre.X + (Viewport.Width / 2),
-Centre.Y + (Viewport.Height / 2),
0);
// public Camera(int x, int y) {
// m_dimension.x = x;
// m_dimension.y = x;
// }
public Camera(Viewport _viewport) {
Viewport = _viewport;
//TODO This is to experiment
Centre = new Vector2(Viewport.Width / 2, Viewport.Height / 2);
}
// public Viewport GetDimension() {
// return m_dimension;
// }
// }
public void Update(Vector2 position, Vector2 velocity) {
Centre = Vector2.Add(Centre, velocity);
Transform = Matrix.CreateTranslation(TranslationVector);
}
}
}

@ -3,32 +3,41 @@ using System.Linq;
using SeeNoEvil.Tiled;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
namespace SeeNoEvil.Level {
public class TilemapLevel {
private readonly string MapName;
private TiledMap Map;
private Dictionary<string, Texture2D> tilesetTextures;
public TilemapLevel(string tilemapName) {
MapName = tilemapName;
}
public void LoadMap() {
Map = TiledParser.ReadMapJson(MapName);
public void LoadMap(ContentManager Content) {
Map = new TiledMap(TiledParser.ReadMapJson(MapName));
Map.LoadView();
tilesetTextures = Map.GetTilesetNames().Aggregate(new Dictionary<string, Texture2D>(),
(content, contentName) => {
content.Add(contentName, Content.Load<Texture2D>(contentName));
return content;
});
}
public IEnumerable<string> GetTilesetNames() {
return Map.GetTilesetNames();
}
public void Draw(Camera viewport, SpriteBatch spritebatch, IDictionary<string, Texture2D> this_sucks) {
IEnumerable<TileLocation> locations = Map.DrawView(viewport);
locations.ToList().ForEach(tile => {
Texture2D this_one;
if(tile.tile.gid > 0 && this_sucks.TryGetValue(tile.tile.setName, out this_one)) {
spritebatch.Draw(this_one, tile.location, tile.tile.srcRectangle, Color.White);
}
});
public void Draw(SpriteBatch spritebatch, string layer) {
List<TileLocation> locations;
if(Map.View.TryGetValue(layer, out locations))
locations.ForEach(tile => {
Texture2D layerTexture;
if(tile.tile.gid > 0 && tilesetTextures.TryGetValue(tile.tile.setName, out layerTexture)) {
spritebatch.Draw(layerTexture, tile.location, tile.tile.srcRectangle, Color.White);
}
});
}
}
}

@ -7,7 +7,7 @@ namespace SeeNoEvil
[STAThread]
static void Main()
{
using (var game = new Game1())
using (var game = new SeeNoEvilGame())
game.Run();
}
}

@ -10,30 +10,25 @@ using SeeNoEvil.Level;
namespace SeeNoEvil
{
public class Game1 : Game
public class SeeNoEvilGame : Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
TilemapLevel level;
Dictionary<string, Texture2D> tilesets;
Camera viewport;
Camera camera;
public Game1()
public SeeNoEvilGame()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
IsMouseVisible = true;
viewport.height = 720;
viewport.width = 1280;
viewport.x = 0;
viewport.y = 0;
}
protected override void Initialize()
{
// TODO: Add your initialization logic here
level = new TilemapLevel("./Maps/MagicLandCsv.json");
level.LoadMap();
// level = new TilemapLevel("./Maps/test.json");
base.Initialize();
}
@ -41,12 +36,8 @@ namespace SeeNoEvil
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
tilesets = new Dictionary<string, Texture2D>();
tilesets = level.GetTilesetNames().Aggregate(new Dictionary<string, Texture2D>(),
(content, contentName) => {
content.Add(contentName, Content.Load<Texture2D>(contentName));
return content;
});
level.LoadMap(Content);
camera = new Camera(GraphicsDevice.Viewport);
}
protected override void Update(GameTime gameTime)
@ -54,24 +45,29 @@ namespace SeeNoEvil
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
Exit();
int xVelocity = 0, yVelocity = 0;
if(Keyboard.GetState().IsKeyDown(Keys.Down))
viewport.y += 16;
yVelocity = 1;
if(Keyboard.GetState().IsKeyDown(Keys.Up))
viewport.y -= 16;
yVelocity = -1;
if(Keyboard.GetState().IsKeyDown(Keys.Left))
viewport.x -= 16;
xVelocity = -1;
if(Keyboard.GetState().IsKeyDown(Keys.Right))
viewport.x += 16;
xVelocity = 1;
camera.Update(Vector2.Zero, new Vector2(xVelocity, yVelocity));
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
GraphicsDevice.Clear(Color.Black);
spriteBatch.Begin();
level.Draw(viewport, spriteBatch, tilesets);
spriteBatch.Begin(
transformMatrix: camera.Transform
);
level.Draw(spriteBatch, "background");
// level.Draw(spriteBatch, "Tile Layer 1");
spriteBatch.End();
base.Draw(gameTime);

@ -0,0 +1,57 @@
using System.Collections.Generic;
using System.Linq;
using SeeNoEvil.Level;
namespace SeeNoEvil.Tiled {
public class MapLayer {
public string Type {get; private set;}
public string Name {get; private set;}
public readonly List<DataCoordinate> DataCoordinates = new List<DataCoordinate>();
private int Width;
private int Height;
private List<uint> Data;
public MapLayer(MapLayerModel model) {
Name = model.Name;
Width = model.Width;
Height = model.Height;
Type = model.Type;
if(model.Type == "tilelayer") {
Data = model.Data.ToList();
BuildCoordinates();
}
}
private void BuildCoordinates() {
int row = 0;
int column = 0;
Data.ForEach(gid => {
DataCoordinates.Add(new DataCoordinate(column, row, gid));
if(column == Width-1) {
row++;
column = 0;
} else column++;
});
}
// TODO Remember to delete this if it's garbo
// public IEnumerable<DataCoordinate> GetVisibleTiles(Camera viewport, int tileHeight, int tileWidth) {
// if(!BuiltCoordinates) BuildCoordinates();
// int startColumn = viewport.x / tileWidth;
// int endColumn = startColumn + (viewport.width / tileWidth);
// int startRow = viewport.y / tileHeight;
// int endRow = startRow + (viewport.height / tileHeight);
// return DataCoordinates.Where(coord =>
// startRow <= coord.y &&
// endRow >= coord.y &&
// startColumn <= coord.x &&
// endColumn >= coord.x
// );
// }
}
}

@ -0,0 +1,79 @@
using System.Collections.Generic;
using Microsoft.Xna.Framework;
namespace SeeNoEvil.Tiled {
public class TileSet {
private string Image {get; set;}
private string Name {get; set;}
private int ImageHeight {get; set;}
private int ImageWidth {get; set;}
private int TileHeight {get; set;}
private int TileWidth {get; set;}
private int Spacing {get; set;}
private int Columns {get; set;}
private int FirstGid {get; set;}
private int TileCount {get; set;}
private Dictionary<uint, Tile> Tiles {get; set;}
private bool TilesLoaded {get; set;}
public TileSet(TileSetModel model) {
Image = model.Image;
Name = model.Name;
ImageHeight = model.ImageHeight;
ImageWidth = model.ImageWidth;
TileHeight = model.TileHeight;
TileWidth = model.TileWidth;
Spacing = model.Spacing;
Columns = model.Columns;
FirstGid = model.FirstGid;
TileCount = model.TileCount;
Tiles = new Dictionary<uint, Tile>();
TilesLoaded = false;
}
public bool ContainsTile(uint gid) =>
!(gid < FirstGid ||
gid > (FirstGid + TileCount - 1));
public Tile GetTile(uint gid) {
if(!TilesLoaded) LoadTiles();
Tile result;
Tiles.TryGetValue(gid, out result);
return result;
}
public string GetContentName() {
return Name;
}
private void LoadTiles() {
Tiles = new Dictionary<uint, Tile>();
int row = 0;
int rowPx = 0;
int column = 0;
int columnPx = 0;
for(int i = 0; i < TileCount; i++) {
Rectangle rectangle = new Rectangle(
columnPx,
rowPx,
TileWidth,
TileHeight
);
uint gid = (uint)(i + FirstGid);
Tiles.Add(gid, new Tile(gid, rectangle, Name));
if(column == Columns-1) {
row++;
rowPx += Spacing + TileHeight;
column = 0;
columnPx = 0;
} else {
column++;
columnPx += Spacing + TileWidth;
}
}
TilesLoaded = true;
}
}
}

@ -2,56 +2,66 @@ using System.Collections.Generic;
using Microsoft.Xna.Framework;
using System.Linq;
using SeeNoEvil.Level;
using System.Text.Json.Serialization;
namespace SeeNoEvil.Tiled {
public class TiledMap {
public TiledMap() => TileCache = new Dictionary<uint, Tile>();
public Dictionary<string, List<TileLocation>> View {get; private set;}
private bool Infinite;
private List<MapLayer> Layers;
private List<TileSet> TileSets;
private int TileHeight;
private int TileWidth;
private int Rows;
private int Columns;
// TODO Do I really need this?
private Dictionary<uint, Tile> TileCache;
public TiledMap(TiledModel model) {
Infinite = model.Infinite;
Layers = model.Layers.Select(model => new MapLayer(model)).ToList();
TileSets = model.TileSets.Select(model => new TileSet(model)).ToList();
TileHeight = model.TileHeight;
TileWidth = model.TileWidth;
Rows = model.Height;
Columns = model.Width;
TileCache = new Dictionary<uint, Tile>();
View = new Dictionary<string, List<TileLocation>>();
}
public bool Infinite {get; set;}
public IEnumerable<MapLayer> Layers {get; set;}
public IEnumerable<TileSet> TileSets {get; set;}
public int TileHeight {get; set;}
public int TileWidth {get; set;}
private Dictionary<uint, Tile> TileCache {get; set;}
public IEnumerable<string> GetTilesetNames() =>
TileSets.Select(tileset => tileset.GetContentName());
public IEnumerable<TileLocation> DrawView(Camera viewport) {
Dictionary<string, IEnumerable<DataCoordinate>> layerData = new Dictionary<string, IEnumerable<DataCoordinate>>();
// Load Tile Locations for each layer
public void LoadView() {
// Get all tilelayers
Dictionary<string, List<DataCoordinate>> layerData = new Dictionary<string, List<DataCoordinate>>();
Layers.Where(layer => layer.Type == "tilelayer").ToList()
.ForEach(layer => {
layerData.Add(layer.Name, layer.GetVisibleTiles(viewport, TileHeight, TileWidth));
layerData.Add(layer.Name, layer.DataCoordinates);
});
HashSet<uint> gids = layerData.Aggregate(new HashSet<uint>(),
// Get all required unique gids and load to cache
LoadTiles(layerData.Aggregate(new HashSet<uint>(),
(tiles, layer) => {
layer.Value.ToList().ForEach(tile => tiles.Add(tile.gid));
foreach(var tile in layer.Value) {
tiles.Add(tile.gid);
}
return tiles;
});
IDictionary<uint, Tile> tiles = GetTilesetTiles(gids);
// return layerData.Aggregate(new List<TileLocation>(),
// (locations, layer) => {
// layer.Value.ToList().ForEach(coord => {
// Tile currTile;
// if(!tiles.TryGetValue(coord.gid, out currTile)) {
// currTile = new Tile();
// }
// Vector2 pxLocation = new Vector2(coord.x*TileWidth, coord.y*TileHeight);
// locations.Add(new TileLocation(currTile, pxLocation));
// });
// return locations;
// });
int column = 0, row = 0, columnPx = 0, rowPx = 0;
int viewportRows = viewport.height / TileHeight;
int viewportColumns = viewport.width / TileWidth;
return layerData.Aggregate(new List<TileLocation>(),
})
);
// build resulting tile location collection for each layer
View = layerData.Aggregate(new Dictionary<string, List<TileLocation>>(),
(locations, layer) => {
layer.Value.ToList().ForEach(coord => {
Tile currTile;
if(!tiles.TryGetValue(coord.gid, out currTile)) {
currTile = new Tile();
}
int column = 0, row = 0, columnPx = 0, rowPx = 0;
var layerLocations = new List<TileLocation>();
layer.Value.ForEach(coord => {
Tile currTile = new Tile();
if(coord.gid > 0)
TileCache.TryGetValue(coord.gid, out currTile);
Vector2 pxLocation = new Vector2(columnPx, rowPx);
locations.Add(new TileLocation(currTile, pxLocation));
if(column == viewportColumns) {
layerLocations.Add(new TileLocation(currTile, pxLocation));
if(column == (Columns-1)) {
row++;
rowPx += TileHeight;
column = columnPx = 0;
@ -60,175 +70,20 @@ namespace SeeNoEvil.Tiled {
columnPx += TileWidth;
}
});
locations.Add(layer.Key, layerLocations);
return locations;
});
}
public IEnumerable<string> GetTilesetNames() =>
TileSets.Aggregate(new List<string>(),
(names, tileset) => {
names.Add(tileset.GetContentName());
return names;
});
private IDictionary<uint, Tile> GetTilesetTiles(HashSet<uint> tiles) {
Dictionary<uint, Tile> loadedTiles = new Dictionary<uint, Tile>();
tiles.ToList().ForEach(gid => {
TileSet tileset = TileSets.FirstOrDefault(set => set.ContainsTile(gid));
Tile tile = tileset == null ? new Tile() : tileset.GetTile(gid);
loadedTiles.Add(gid, tile);
});
return loadedTiles;
}
}
public struct TileLocation {
public TileLocation(Tile tile, Vector2 location) {
this.tile = tile;
this.location = location;
}
public Tile tile;
public Vector2 location;
}
public struct Tile {
public Tile(long _gid, Rectangle _srcRectangle, string _setName) {
gid = (uint)_gid;
srcRectangle = _srcRectangle;
setName = _setName;
}
public uint gid;
public Rectangle srcRectangle;
public string setName;
}
public class TileSet {
public TileSet() {
TilesLoaded = false;
}
public string Image {get; set;}
public string Name {get; set;}
public int ImageHeight {get; set;}
public int ImageWidth {get; set;}
public int TileHeight {get; set;}
public int TileWidth {get; set;}
public int Spacing {get; set;}
public int Columns {get; set;}
public int FirstGid {get; set;}
public int TileCount {get; set;}
private IDictionary<uint, Tile> Tiles {get; set;}
private bool TilesLoaded {get; set;}
public Tile GetTile(uint gid) {
if(!TilesLoaded) {
LoadTiles();
}
Tile result;
Tiles.TryGetValue(gid, out result);
return result;
}
public bool ContainsTile(uint gid) {
return !(gid < FirstGid ||
gid > (FirstGid + TileCount - 1));
}
public string GetContentName() {
return Name;
}
private void LoadTiles() {
Tiles = new Dictionary<uint, Tile>();
int row = 0;
int rowPx = 0;
int column = 0;
int columnPx = 0;
for(int i = 0; i < TileCount; i++) {
Rectangle rectangle = new Rectangle(
columnPx,
rowPx,
TileWidth,
TileHeight
);
uint gid = (uint)(i + FirstGid);
Tiles.Add(gid, new Tile(gid, rectangle, Name));
if(column == Columns-1) {
row++;
rowPx += Spacing + TileHeight;
column = 0;
columnPx = 0;
} else {
column++;
columnPx += Spacing + TileWidth;
// Load tiles into cache if they don't exist
private void LoadTiles(HashSet<uint> tiles) {
foreach(uint gid in tiles) {
if(!TileCache.ContainsKey(gid)) {
TileSet tileset = TileSets.FirstOrDefault(set => set.ContainsTile(gid));
Tile tile = tileset == null ? new Tile() : tileset.GetTile(gid);
TileCache.Add(gid, tile);
}
}
TilesLoaded = true;
}
}
public struct DataCoordinate {
public DataCoordinate(int _x, int _y, uint _gid) {
x = _x;
y = _y;
gid = _gid;
}
public int x;
public int y;
public uint gid;
}
public class MapLayer {
public MapLayer() { }
public string Name {get; set;}
public int Width {get; set;}
public int Height {get; set;}
public IEnumerable<uint> Data {get; set;}
public string Type {get; set;}
public IEnumerable<DataCoordinate> DataCoordinates {get; set;}
public bool BuiltCoordinates {get; private set;}
public IEnumerable<DataCoordinate> GetVisibleTiles(Camera viewport, int tileHeight, int tileWidth) {
if(!BuiltCoordinates) {
BuildCoordinates();
}
int startColumn = viewport.x / tileWidth;
int endColumn = startColumn + (viewport.width / tileWidth);
int startRow = viewport.y / tileHeight;
int endRow = startRow + (viewport.height / tileHeight);
var coords = DataCoordinates.Where(coord =>
startRow <= coord.y &&
endRow >= coord.y &&
startColumn <= coord.x &&
endColumn >= coord.x
).ToList();
return DataCoordinates.Where(coord =>
startRow <= coord.y &&
endRow >= coord.y &&
startColumn <= coord.x &&
endColumn >= coord.x
);
}
private void BuildCoordinates() {
int row = 0;
int column = 0;
List<DataCoordinate> coordinates = new List<DataCoordinate>();
Data.ToList().ForEach(gid => {
coordinates.Add(new DataCoordinate(column, row, gid));
if(column == Width-1) {
row++;
column = 0;
} else column++;
});
DataCoordinates = coordinates;
BuiltCoordinates = true;
}
}
}
}

@ -0,0 +1,41 @@
using System.Collections.Generic;
namespace SeeNoEvil.Tiled {
public class TiledModel {
public TiledModel() {}
public bool Infinite {get; set;}
public IEnumerable<MapLayerModel> Layers {get; set;}
public IEnumerable<TileSetModel> TileSets {get; set;}
public int TileHeight {get; set;}
public int TileWidth {get; set;}
public int Height {get; set;}
public int Width {get; set;}
}
public class TileSetModel {
public TileSetModel() {}
public string Image {get; set;}
public string Name {get; set;}
public int ImageHeight {get; set;}
public int ImageWidth {get; set;}
public int TileHeight {get; set;}
public int TileWidth {get; set;}
public int Spacing {get; set;}
public int Columns {get; set;}
public int FirstGid {get; set;}
public int TileCount {get; set;}
}
public class MapLayerModel {
public MapLayerModel() { }
public string Name {get; set;}
public int Width {get; set;}
public int Height {get; set;}
public IEnumerable<uint> Data {get; set;}
public string Type {get; set;}
}
}

@ -3,13 +3,13 @@ using System.Text.Json;
namespace SeeNoEvil.Tiled {
public static class TiledParser {
public static TiledMap ReadMapJson(string fileName) {
public static TiledModel ReadMapJson(string fileName) {
StreamReader streamReader = File.OpenText(fileName);
string text = streamReader.ReadToEnd();
var options = new JsonSerializerOptions {
PropertyNameCaseInsensitive = true,
};
return JsonSerializer.Deserialize<TiledMap>(text, options);
return JsonSerializer.Deserialize<TiledModel>(text, options);
}
}
}

@ -0,0 +1,36 @@
using Microsoft.Xna.Framework;
namespace SeeNoEvil.Tiled {
public struct Tile {
public Tile(long _gid, Rectangle _srcRectangle, string _setName) {
gid = (uint)_gid;
srcRectangle = _srcRectangle;
setName = _setName;
}
public uint gid;
public Rectangle srcRectangle;
public string setName;
}
public struct TileLocation {
public TileLocation(Tile tile, Vector2 location) {
this.tile = tile;
this.location = location;
}
public Tile tile;
public Vector2 location;
}
public struct DataCoordinate {
public DataCoordinate(int _x, int _y, uint _gid) {
x = _x;
y = _y;
gid = _gid;
}
public int x;
public int y;
public uint gid;
}
}
Loading…
Cancel
Save