Explorar o código

engine improvements, basic map renderer with scrolling / zooming, tile renderer registry, basic tile with automatic registry, test tiles, tile map, documentation, tile texture atlas base

Kajetan Johannes Hammerle %!s(int64=5) %!d(string=hai) anos
pai
achega
46e685a687

BIN=BIN
lib/SnuviEngine.jar


BIN=BIN
resources/tiles.png


BIN=BIN
resources/tiles.xcf


+ 3 - 1
src/pathgame/Main.java

@@ -7,7 +7,9 @@ public class Main
     public static void main(String[] args)
     {
         Engine.init("Path Game", 1024, 768);
-        Engine.setNanosPerTick(50000000);
+        
+        Engine.setNanosPerTick(50_000_000);
+        
         PathGame game = new PathGame();
         Engine.start(game);
     }    

+ 43 - 21
src/pathgame/PathGame.java

@@ -5,18 +5,25 @@ import me.hammerle.snuviengine.api.KeyBinding;
 import me.hammerle.snuviengine.api.KeyHandler;
 import me.hammerle.snuviengine.api.Renderer;
 import org.lwjgl.glfw.GLFW;
+import pathgame.rendering.TileMapRenderer;
+import pathgame.tilemap.TileMap;
+import pathgame.utils.TestUtils;
 
 public class PathGame implements IGame
 {
-    private float oldX = 0;
-    private float oldY = 0;
-    private float x = 0;
-    private float y = 0;
+    private final TileMapRenderer mapRenderer = new TileMapRenderer();
+    private final TileMap map = TestUtils.getTestMap(25, 15);
     
-    private static final KeyBinding UP_KEY = KeyHandler.register(GLFW.GLFW_KEY_W);
-    private static final KeyBinding DOWN_KEY = KeyHandler.register(GLFW.GLFW_KEY_S);
-    private static final KeyBinding LEFT_KEY = KeyHandler.register(GLFW.GLFW_KEY_A);
-    private static final KeyBinding RIGHT_KEY = KeyHandler.register(GLFW.GLFW_KEY_D);
+    private final static KeyBinding ZOOM_IN = KeyHandler.register(GLFW.GLFW_KEY_UP);
+    private final static KeyBinding ZOOM_OUT = KeyHandler.register(GLFW.GLFW_KEY_DOWN);
+    
+    private final static KeyBinding UP = KeyHandler.register(GLFW.GLFW_KEY_W);
+    private final static KeyBinding DOWN = KeyHandler.register(GLFW.GLFW_KEY_S);
+    private final static KeyBinding LEFT = KeyHandler.register(GLFW.GLFW_KEY_A);
+    private final static KeyBinding RIGHT = KeyHandler.register(GLFW.GLFW_KEY_D);
+    
+    private float x = 0.0f;
+    private float y = 0.0f;
     
     public PathGame()
     {
@@ -25,31 +32,46 @@ public class PathGame implements IGame
     @Override
     public void tick()
     {
-        oldX = x;
-        oldY = y;
+        mapRenderer.tick();
+        
+        if(ZOOM_IN.isDown())
+        {
+            mapRenderer.setScale(mapRenderer.getScale() * 1.05f);
+        }
+        if(ZOOM_OUT.isDown())
+        {
+            mapRenderer.setScale(mapRenderer.getScale() / 1.05f);
+        }
         
-        if(LEFT_KEY.isDown())
+        if(UP.isDown())
         {
-            x -= 10; 
+            y -= 20;
         }
-        if(RIGHT_KEY.isDown())
+        if(DOWN.isDown())
         {
-            x += 10; 
+            y += 20;
+        }
+        if(LEFT.isDown())
+        {
+            x -= 20;
+        }
+        if(RIGHT.isDown())
+        {
+            x += 20;
         }
     }
     
     @Override
     public void renderTick(Renderer r, float lag)
     {
-        r.setMixColorEnabled(false);
-     
+        mapRenderer.renderTick(map, r, false, x, y);
+        
         r.translateTo(0.0f, 0.0f);
         r.updateMatrix();
-        
-        float ix = oldX + (x - oldX) * lag;
-        float iy = oldY + (y - oldY) * lag;
-        
-        r.getColorRenderer().drawRectangle(ix, iy, ix + 20, iy + 20, 0xFF0000FF);
+        r.setTextureEnabled(false);
+        r.setColorEnabled(true);
+        r.getColorRenderer().drawRectangle(0, 0, mapRenderer.getWidth(map), 10, 0xFF0000FF);
+        r.getColorRenderer().drawRectangle(0, 0, 10, mapRenderer.getHeight(map), 0xFF0000FF);
     }
 
     @Override

+ 29 - 0
src/pathgame/rendering/StaticTextureProvider.java

@@ -0,0 +1,29 @@
+package pathgame.rendering;
+
+import pathgame.tilemap.Tile;
+import pathgame.tilemap.TileMap;
+
+/** A static {@link  pathgame.rendering.TileTextureProvider TileTextureProvider},
+ * which always returns the same {@link  pathgame.rendering.TileTexture TileTexture}.
+ *
+ * @author kajetan
+ */
+public class StaticTextureProvider implements TileTextureProvider
+{
+    private final TileTexture tt;
+    
+    /** Creates a new static texture provider.
+     *
+     * @param tt a tile texture
+     */
+    public StaticTextureProvider(TileTexture tt)
+    {
+        this.tt = tt;
+    }
+    
+    @Override
+    public TileTexture getTexture(TileMap map, Tile t, int x, int y)
+    {
+        return tt;
+    }
+}

+ 119 - 0
src/pathgame/rendering/TileMapRenderer.java

@@ -0,0 +1,119 @@
+package pathgame.rendering;
+
+import me.hammerle.snuviengine.api.Renderer;
+import me.hammerle.snuviengine.api.Texture;
+import me.hammerle.snuviengine.api.TextureRenderer;
+import pathgame.tilemap.TileMap;
+
+/** A renderer for tile maps.
+ *
+ * @author kajetan
+ */
+public class TileMapRenderer
+{
+    private final Texture tileTexture = new Texture("resources/tiles.png");
+    private final TextureRenderer textureRenderer = new TextureRenderer(20 * 20 * 2); // default to 20x20 map
+    private float scale = 1.0f;
+    
+    /** Creates a new tile map renderer.
+     *
+     */
+    public TileMapRenderer()
+    {
+    }
+
+    /** Sets the scale of the map.
+     *
+     * @param scale the scale of the map
+     */
+    public void setScale(float scale)
+    {
+        this.scale = scale;
+    }
+
+    /** Returns the scale of the map.
+     *
+     * @return the scale of the map
+     */
+    public float getScale()
+    {
+        return scale;
+    }
+    
+    /** Returns the scaled render width of a map.
+     *
+     * @param map a map
+     * @return the scaled render width of a map
+     */
+    public float getWidth(TileMap map)
+    {
+        return map.getWidth() * TileRenderer.TILE_SIZE * scale;
+    }
+    
+    /** Returns the scaled render height of a map.
+     *
+     * @param map a map
+     * @return the scaled render height of a map
+     */
+    public float getHeight(TileMap map)
+    {
+        return map.getHeight() * TileRenderer.TILE_SIZE * scale;
+    }
+    
+    private void updateData(TileMap map)
+    {
+        textureRenderer.clear();
+        for(int x = 0; x < map.getWidth(); x++)
+        {
+            for(int y = 0; y < map.getHeight(); y++)
+            {
+                TileTexture tt = TileRenderer.getTileTexture(map, map.getTile(x, y), x, y);
+                if(tt == null)
+                {
+                    continue;
+                }
+                textureRenderer.addRectangle(
+                        x * TileRenderer.TILE_SIZE, y * TileRenderer.TILE_SIZE,
+                        (x + 1) * TileRenderer.TILE_SIZE, (y + 1) * TileRenderer.TILE_SIZE,
+                        tt.getMinX(), tt.getMinY(), tt.getMaxX(), tt.getMaxY());
+            }
+        }
+        textureRenderer.build();
+    }
+    
+    /** Ticks the renderer. Used for animated tiles.
+     *
+     */
+    public void tick()
+    {
+        // tick tile animations here
+    }
+    
+    /** Draws the given map at the given offset.
+     *
+     * @param map a map
+     * @param r the renderer given by the engine
+     * @param forceUpdate whether an update of the render data should be forced
+     * @param offX the x coordinate of the offset
+     * @param offY the y coordinate of the offset
+     */
+    public void renderTick(TileMap map, Renderer r, boolean forceUpdate, float offX, float offY)
+    {
+        r.setTextureEnabled(true);
+        r.setColorEnabled(false);
+        r.setMixColorEnabled(false);
+        
+        tileTexture.bind();
+        
+        if(forceUpdate || map.isDirty())
+        {
+            updateData(map);
+            map.clean();
+        }
+        
+        r.translateTo(offX, offY);
+        r.scale(scale, scale);
+        r.updateMatrix();
+        textureRenderer.draw();
+    }
+}

+ 45 - 0
src/pathgame/rendering/TileRenderer.java

@@ -0,0 +1,45 @@
+package pathgame.rendering;
+
+import pathgame.tilemap.Tile;
+import pathgame.tilemap.TileMap;
+import pathgame.tilemap.Tiles;
+
+public class TileRenderer
+{
+    /** The unscaled render size of a map tile. */
+    public final static float TILE_SIZE = 16;
+    
+    private static TileTextureProvider[] tProviders = new TileTextureProvider[8];
+    
+    private static void register(Tile t, TileTextureProvider provider)
+    {
+        if(t.getId() >= tProviders.length)
+        {
+            TileTextureProvider[] newTProviders = new TileTextureProvider[t.getId()];
+            System.arraycopy(tProviders, 0, newTProviders, 0, tProviders.length);
+            tProviders = newTProviders;
+        }
+        tProviders[t.getId()] = provider;
+    }
+    
+    static
+    {
+        register(Tiles.GRASS, new StaticTextureProvider(TileTexture.fromTextureId(0)));
+        register(Tiles.HILL, new StaticTextureProvider(TileTexture.fromTextureId(1)));
+        register(Tiles.MOUNTAIN, new StaticTextureProvider(TileTexture.fromTextureId(2)));
+    }
+    
+    public static TileTexture getTileTexture(TileMap map, Tile t, int x, int y)
+    {
+        if(t == null)
+        {
+            return null;
+        }
+        int id = t.getId();
+        if(id < 0 || id >= tProviders.length || tProviders[id] == null)
+        {
+            return null;
+        }
+        return tProviders[id].getTexture(map, t, x, y);
+    }
+}

+ 82 - 0
src/pathgame/rendering/TileTexture.java

@@ -0,0 +1,82 @@
+package pathgame.rendering;
+
+/** A container for tile texture coordinates.
+ *
+ * @author kajetan
+ */
+public class TileTexture
+{
+    private final static float TEXTURE_SIZE = 512;
+    private final static float TILE_TEXTURE_PERCENT = TileRenderer.TILE_SIZE / TEXTURE_SIZE;
+    private final static int TILES_PER_LINE = (int) (TEXTURE_SIZE / TileRenderer.TILE_SIZE);
+    
+    /** Returns a tile texture constructed from a given texture id.
+     *
+     * @param textureId a texture id
+     * @return a tile texture constructed from a given texture id
+     */
+    public static TileTexture fromTextureId(int textureId)
+    {
+        float tMinX = (textureId % TILES_PER_LINE) * TILE_TEXTURE_PERCENT;
+        float tMinY = (textureId/ TILES_PER_LINE) * TILE_TEXTURE_PERCENT;
+        float tMaxX = tMinX + TILE_TEXTURE_PERCENT;
+        float tMaxY = tMinY + TILE_TEXTURE_PERCENT;
+        return new TileTexture(tMinX, tMinY, tMaxX, tMaxY);
+    }
+    
+    private final float tMinX;
+    private final float tMinY;
+    private final float tMaxX;
+    private final float tMaxY;
+    
+    /** Creates a new tile texture.
+     *
+     * @param tMinX the left x coordinate in the texture atlas
+     * @param tMinY the upper y coordinate in the texture atlas
+     * @param tMaxX the right x coordinate in the texture atlas
+     * @param tMaxY the lower y coordinate in the texture atlas
+     */
+    public TileTexture(float tMinX, float tMinY, float tMaxX, float tMaxY)
+    {
+        this.tMinX = tMinX;
+        this.tMinY = tMinY;
+        this.tMaxX = tMaxX;
+        this.tMaxY = tMaxY;
+    }
+
+    /** Returns the the left x coordinate in the texture atlas.
+     *
+     * @return the left x coordinate in the texture atlas
+     */
+    public float getMinX()
+    {
+        return tMinX;
+    }
+
+    /** Returns the upper y coordinate in the texture atlas.
+     *
+     * @return the upper y coordinate in the texture atlas
+     */
+    public float getMinY()
+    {
+        return tMinY;
+    }
+             
+    /** Returns the right x coordinate in the texture atlas.
+     *
+     * @return the right x coordinate in the texture atlas
+     */
+    public float getMaxX()
+    {
+        return tMaxX;
+    }
+
+    /** Returns the lower y coordinate in the texture atlas.
+     *
+     * @return the lower y coordinate in the texture atlas
+     */
+    public float getMaxY()
+    {
+        return tMaxY;
+    }
+}

+ 22 - 0
src/pathgame/rendering/TileTextureProvider.java

@@ -0,0 +1,22 @@
+package pathgame.rendering;
+
+import pathgame.tilemap.Tile;
+import pathgame.tilemap.TileMap;
+
+/** Represents an operation that accepts tile map data and returns a tile texture.
+ *
+ * @author kajetan
+ */
+@FunctionalInterface
+public interface TileTextureProvider
+{
+    /** Returns a tile texture corresponding to the given tile at the given position.
+     *
+     * @param map a map
+     * @param t a tile
+     * @param x the x coordinate of the tile
+     * @param y the y coordinate of the tile
+     * @return a tile texture corresponding to the given tile at the given position
+     */
+    public TileTexture getTexture(TileMap map, Tile t, int x, int y);
+}

+ 56 - 0
src/pathgame/tilemap/Tile.java

@@ -0,0 +1,56 @@
+package pathgame.tilemap;
+
+/** Base class for tiles. Tiles are registered on construction.
+ *
+ * @author kajetan
+ */
+public class Tile
+{
+    private static int idCounter = 0;
+    private static Tile[] tiles = new Tile[8];
+    
+    private static int addTile(Tile t)
+    {
+        if(idCounter >= tiles.length)
+        {
+            Tile[] newTiles = new Tile[tiles.length * 2];
+            System.arraycopy(tiles, 0, newTiles, 0, tiles.length);
+            tiles = newTiles;
+        }
+        tiles[idCounter] = t;
+        return idCounter++;
+    }
+    
+    /** Returns a tile from the given id or null if the id is invalid.
+     *
+     * @param id the id of the tile
+     * @return a tile from the given id or null if the id is invalid
+     */
+    public static Tile fromId(int id)
+    {
+        if(id < 0 || id >= idCounter)
+        {
+            return null;
+        }
+        return tiles[id];
+    }
+    
+    private final int id;
+    
+    /** Creates a new tile, which is automatically registered.
+     *
+     */
+    public Tile()
+    {
+        id = addTile(this);
+    }
+
+    /** Returns the id of the tile.
+     *
+     * @return the id of the tile
+     */
+    public final int getId()
+    {
+        return id;
+    }
+}

+ 85 - 0
src/pathgame/tilemap/TileMap.java

@@ -0,0 +1,85 @@
+package pathgame.tilemap;
+
+/** Fixed size container for tile maps. Changes are stored in a dirty flag,
+ * which can be used for rendering.
+ *
+ * @author kajetan
+ */
+public class TileMap
+{
+    private final int width;
+    private final int height;
+    private final int[][] tiles;
+    private boolean dirty = true;
+    
+    /** Creates a new tile map of the given size.
+     *
+     * @param width the width of the map
+     * @param height the height of the map
+     */
+    public TileMap(int width, int height)
+    {
+        this.width = width;
+        this.height = height;
+        tiles = new int[width][height];
+    }
+    
+    /** Returns the width of the map.
+     *
+     * @return the width of the map
+     */
+    public int getWidth()
+    {
+        return width;
+    }
+
+    /** Returns the height of the map.
+     *
+     * @return the height of the map
+     */
+    public int getHeight()
+    {
+        return height;
+    }
+    
+    /** Sets the tile at the given map position. The dirty flag is set.
+     *
+     * @param x the x coordinate of the tile
+     * @param y the y coordinate of the tile
+     * @param tile a tile
+     */
+    public void setTile(int x, int y, Tile tile)
+    {
+        dirty = true;
+        tiles[x][y] = tile.getId();
+    }
+    
+    /** Returns the tile at the given map position or null if the tile is invalid.
+     *
+     * @param x the x coordinate of the tile
+     * @param y the y coordinate of the tile
+     * @return the tile at the given map position or null if the tile is invalid
+     */
+    public Tile getTile(int x, int y)
+    {
+        return Tile.fromId(tiles[x][y]);
+    }
+
+    /** Returns true if the map was modified. This is used for rendering.
+     *
+     * @return true if the map was modified
+     */
+    public boolean isDirty()
+    {
+        return dirty;
+    }
+    
+    /** Clears the dirty flag of the map.
+     *
+     */
+    public void clean()
+    {
+        dirty = false;
+    }
+}
+

+ 8 - 0
src/pathgame/tilemap/Tiles.java

@@ -0,0 +1,8 @@
+package pathgame.tilemap;
+
+public class Tiles
+{
+    public final static Tile GRASS = new Tile();
+    public final static Tile HILL = new Tile();
+    public final static Tile MOUNTAIN = new Tile();
+}

+ 34 - 0
src/pathgame/utils/TestUtils.java

@@ -0,0 +1,34 @@
+package pathgame.utils;
+
+import java.util.Random;
+import pathgame.tilemap.TileMap;
+import pathgame.tilemap.Tiles;
+
+public class TestUtils
+{
+    /** Returns a random test map.
+     *
+     * @param width the width of the map
+     * @param height the height of the map
+     * @return a random test map
+     */
+    public static TileMap getTestMap(int width, int height)
+    {
+        Random r = new Random();
+        
+        TileMap map = new TileMap(width, height);
+        for(int x = 0; x < width; x++)
+        {
+            for(int y = 0; y < height; y++)
+            {
+                switch(r.nextInt(3))
+                {
+                    case 0: map.setTile(x, y, Tiles.GRASS); break;
+                    case 1: map.setTile(x, y, Tiles.HILL); break;
+                    case 2: map.setTile(x, y, Tiles.MOUNTAIN); break;
+                }
+            }
+        }
+        return map;
+    }
+}