DEADSOFTWARE

Support different block texture sizes and animation
authorfred-boy <fredboy@protonmail.com>
Mon, 15 Jun 2020 17:49:23 +0000 (00:49 +0700)
committerfredboy <fredboy@protonmail.com>
Tue, 16 Jun 2020 11:57:48 +0000 (18:57 +0700)
android/assets/json/game_items.json
core/src/ru/deadsoftware/cavedroid/game/GameItems.java
core/src/ru/deadsoftware/cavedroid/game/GameRenderer.java
core/src/ru/deadsoftware/cavedroid/game/objects/Block.kt

index b3394e69c4632f1ceed6e93d6a284e9cbf678248..aa7bb3e9854a0c2d9ace9153361b90065a28a07d 100644 (file)
       "background": false,
       "transparent": true,
       "fluid": true,
-      "meta": "water"
+      "texture": "water_still",
+      "meta": "water",
+      "animated": true,
+      "frames": 16
     },
     "lava": {
       "hp": -1,
       "collision": false,
       "background": false,
       "fluid": true,
-      "animated": true,
-      "frames": 16,
       "texture": "lava_still",
-      "meta": "lava"
+      "meta": "lava",
+      "animated": true,
+      "frames": 16
     },
     "sand": {
       "hp": 45
     "stone_slab": {
       "top": 8,
       "hp": 600,
-      "transparent": true
+      "transparent": true,
+      "sprite_top": 8
     },
     "double_stone_slab": {
       "hp": 600,
       "collision": false,
       "background": false,
       "transparent": true,
-      "texture": "water",
       "fluid": true,
-      "meta": "water"
+      "texture": "water_still",
+      "meta": "water",
+      "animated": true,
+      "frames": 16
     },
     "water_12": {
+      "top": 4,
       "hp": -1,
       "collision": false,
       "background": false,
       "transparent": true,
       "fluid": true,
-      "meta": "water"
+      "texture": "water_still",
+      "meta": "water",
+      "animated": true,
+      "frames": 16,
+      "sprite_top": 4
     },
     "water_8": {
+      "top": 8,
       "hp": -1,
       "collision": false,
       "background": false,
       "transparent": true,
       "fluid": true,
-      "meta": "water"
+      "texture": "water_still",
+      "meta": "water",
+      "animated": true,
+      "frames": 16,
+      "sprite_top": 8
     },
     "water_4": {
+      "top": 12,
       "hp": -1,
       "collision": false,
       "background": false,
       "transparent": true,
       "fluid": true,
-      "meta": "water"
+      "texture": "water_still",
+      "meta": "water",
+      "animated": true,
+      "frames": 16,
+      "sprite_top": 12
+    },
+    "lava_16": {
+      "hp": -1,
+      "collision": false,
+      "background": false,
+      "fluid": true,
+      "texture": "lava_still",
+      "meta": "lava",
+      "animated": true,
+      "frames": 16
+    },
+    "lava_12": {
+      "top": 4,
+      "hp": -1,
+      "collision": false,
+      "background": false,
+      "fluid": true,
+      "texture": "lava_still",
+      "meta": "lava",
+      "animated": true,
+      "frames": 16,
+      "sprite_top": 4
+    },
+    "lava_8": {
+      "top": 8,
+      "hp": -1,
+      "collision": false,
+      "background": false,
+      "fluid": true,
+      "texture": "lava_still",
+      "meta": "lava",
+      "animated": true,
+      "frames": 16,
+      "sprite_top": 8
+    },
+    "lava_4": {
+      "top": 12,
+      "hp": -1,
+      "collision": false,
+      "background": false,
+      "fluid": true,
+      "texture": "lava_still",
+      "meta": "lava",
+      "animated": true,
+      "frames": 16,
+      "sprite_top": 12
     },
     "obsidian": {
       "hp": 1500
index 64fba5add7c89326fb65c70a63df094cc894eb85..85f4b9c765401ba0c2cf851d0ece119043b4d19c 100644 (file)
@@ -105,19 +105,46 @@ public class GameItems {
                 int right = Assets.getIntFromJson(block, "right", 0);
                 int top = Assets.getIntFromJson(block, "top", 0);
                 int bottom = Assets.getIntFromJson(block, "bottom", 0);
+                int clipX = Assets.getIntFromJson(block, "sprite_left", 0);
+                int clipY = Assets.getIntFromJson(block, "sprite_top", 0);
+                int clipWidth = Assets.getIntFromJson(block, "sprite_right", 0);
+                int clipHeight = Assets.getIntFromJson(block, "sprite_bottom", 0);
                 int hp = Assets.getIntFromJson(block, "hp", -1);
-                boolean coll = Assets.getBooleanFromJson(block, "collision", true);
-                boolean bg = Assets.getBooleanFromJson(block, "background", false);
-                boolean tp = Assets.getBooleanFromJson(block, "transparent", false);
-                boolean br = Assets.getBooleanFromJson(block, "block_required", false);
+                boolean collision = Assets.getBooleanFromJson(block, "collision", true);
+                boolean background = Assets.getBooleanFromJson(block, "background", false);
+                boolean transparent = Assets.getBooleanFromJson(block, "transparent", false);
+                boolean requiresBlock = Assets.getBooleanFromJson(block, "block_required", false);
                 boolean fluid = Assets.getBooleanFromJson(block, "fluid", false);
                 String drop = Assets.getStringFromJson(block, "drop", key);
                 String meta = Assets.getStringFromJson(block, "meta", "");
                 String tex = Assets.getStringFromJson(block, "texture", key);
-                Sprite sprite = tex.equals("none") ? null :
-                        new Sprite(new Texture(Gdx.files.internal("textures/blocks/" + tex + ".png")));
+                Texture texture = tex.equals("none") ? null :
+                        new Texture(Gdx.files.internal("textures/blocks/" + tex + ".png"));
+                boolean animated = Assets.getBooleanFromJson(block, "animated", false);
+                int frames = Assets.getIntFromJson(block, "frames", 0);
+
+                Block newBlock = new Block(
+                        left,
+                        top,
+                        right,
+                        bottom,
+                        hp,
+                        drop,
+                        collision,
+                        background,
+                        transparent,
+                        requiresBlock,
+                        fluid,
+                        meta,
+                        texture,
+                        animated,
+                        frames,
+                        clipX,
+                        clipY,
+                        clipWidth,
+                        clipHeight
+                );
 
-                Block newBlock = new Block(left, top, right, bottom, hp, drop, coll, bg, tp, br, fluid, meta, sprite);
                 blocksIds.put(key, blocks.size);
                 blocks.put(key, newBlock);
             } catch (GdxRuntimeException e) {
index 864938d83b35db74a152d17f90ef4480a30897d4..19f2681b0df65020285bcdb4572076bc089fb969 100644 (file)
@@ -67,14 +67,14 @@ public class GameRenderer extends Renderer {
         if (drawBG) {
             if ((!mGameWorld.hasForeAt(x, y) || mGameWorld.getForeMapBlock(x, y).isTransparent())
                     && mGameWorld.hasBackAt(x, y)) {
-                spriter.draw(mGameWorld.getBackMapBlock(x, y).getTexture(), drawX(x), drawY(y));
+                mGameWorld.getBackMapBlock(x, y).draw(spriter, drawX(x), drawY(y));
                 if (!mGameWorld.hasForeAt(x, y) && x == mGameInput.getCurX() && y == mGameInput.getCurY()) {
                     drawWreck(mGameWorld.getBackMap(mGameInput.getCurX(), mGameInput.getCurY()));
                 }
             }
         }
         if (mGameWorld.hasForeAt(x, y) && mGameWorld.getForeMapBlock(x, y).isBackground() == drawBG) {
-            spriter.draw(mGameWorld.getForeMapBlock(x, y).getTexture(), drawX(x), drawY(y));
+            mGameWorld.getForeMapBlock(x, y).draw(spriter, drawX(x), drawY(y));
             if (x == mGameInput.getCurX() && y == mGameInput.getCurY()) {
                 drawWreck(mGameWorld.getForeMap(mGameInput.getCurX(), mGameInput.getCurY()));
             }
index e8ed1e752cd33e1990bd6c08a0dc12345bab5248..d44f017e0647016afb767fba663815f63a699063 100644 (file)
@@ -2,9 +2,12 @@
 
 package ru.deadsoftware.cavedroid.game.objects
 
+import com.badlogic.gdx.graphics.Texture
 import com.badlogic.gdx.graphics.g2d.Sprite
+import com.badlogic.gdx.graphics.g2d.SpriteBatch
 import com.badlogic.gdx.math.Rectangle
 
+private const val ANIMATION_FRAME_DURATION = 100L
 private const val DEPRECATION_MESSAGE =
         "Deprecated since moved to Kotlin. Use generated getter or kotlin property access."
 
@@ -21,7 +24,13 @@ private const val DEPRECATION_MESSAGE =
  * @param requiresBlock true if block should break when there is no block with collision under it
  * @param fluid         true if fluid
  * @param meta          extra info for storing
- * @param sprite        block's texture
+ * @param texture       block's texture
+ * @param animated      indicates if block has animation
+ * @param frames        number of animation frames. ignored if animated is false
+ * @param spriteLeft    block's sprite x on texture
+ * @param spriteTop     block's sprite y on texture
+ * @param spriteRight   block's sprite right edge on texture
+ * @param spriteBottom  block's sprite bottom on texture
  */
 data class Block(
         val left: Int,
@@ -36,21 +45,80 @@ data class Block(
         val requiresBlock: Boolean,
         val fluid: Boolean,
         val meta: String,
-        val sprite: Sprite?
+        private val texture: Texture?,
+        val animated: Boolean,
+        val frames: Int,
+        private val spriteLeft: Int,
+        private val spriteTop: Int,
+        private val spriteRight: Int,
+        private val spriteBottom: Int
 ) {
 
+    val width = 16 - right - left
+    val height = 16 - top - bottom
+
+    private val spriteWidth = 16 - spriteLeft - spriteRight
+    private val spriteHeight = 16 - spriteTop - spriteBottom
+
+    private val sprite: Sprite?
+        get() {
+            return if (animated) {
+                animation[currentFrame()]
+            } else {
+                field
+            }
+        }
+
+
+    private val animation: Array<Sprite>
+
     init {
-        sprite?.flip(false, true)
+        if (frames !in 0..Int.MAX_VALUE) {
+            throw IllegalArgumentException("Animation frames must be in range [0, ${Int.MAX_VALUE}]")
+        }
+
+        animation = if (animated) {
+            if (texture == null) {
+                throw IllegalArgumentException("Cannot derive animation frames from null sprite")
+            }
+            Array(frames) { y ->
+                Sprite(texture, spriteLeft, 16 * y + spriteTop, spriteWidth, spriteHeight).apply {
+                    flip(false, true)
+                }
+            }
+        } else {
+            emptyArray()
+        }
+
+        sprite = if (animated) { animation[0] } else {
+            if (texture != null) {
+                Sprite(texture, spriteLeft, spriteTop, spriteWidth, spriteHeight).apply {
+                    flip(false, true)
+                }
+            } else {
+                null
+            }
+        }
     }
 
-    val width = 16 - right - left
-    val height = 16 - top - bottom
+    private fun currentFrame() = if (animated) {
+        ((System.currentTimeMillis() / ANIMATION_FRAME_DURATION) % frames).toInt()
+    } else {
+        0
+    }
+
+    fun requireSprite() = sprite ?: throw IllegalStateException("Sprite is null")
+
+    fun draw(spriter: SpriteBatch, x: Float, y: Float) {
+        requireSprite().apply {
+            setBounds(x + spriteLeft, y + spriteTop, spriteWidth.toFloat(), spriteHeight.toFloat())
+            draw(spriter)
+        }
+    }
 
     fun getRectangle(x: Int, y: Int) =
             Rectangle(x * 16f + left, y * 16f + this.top, width.toFloat(), height.toFloat())
 
-    fun requireSprite() = sprite ?: throw IllegalStateException("Sprite is null")
-
     fun hasDrop() = drop != "none"
 
     fun toJump() = top < 8 && collision
@@ -70,7 +138,7 @@ data class Block(
     @Deprecated(DEPRECATION_MESSAGE)
     fun requiresBlock() = requiresBlock
 
-    @Deprecated("Was renamed to Sprite to comply with variable type.", ReplaceWith("getSprite()"))
+    @Deprecated("Was renamed to Sprite to comply with variable type.", ReplaceWith("requireSprite()"))
     fun getTexture() = sprite
 
 }
\ No newline at end of file