From c5cddf96ba653e47c37409c3d9f7de07a5a39b62 Mon Sep 17 00:00:00 2001 From: Zukero Date: Fri, 10 Apr 2015 11:40:17 +0200 Subject: [PATCH] Animated movements ! Enhancements in debug map graphics, and more monster to better test animations. Player slides from tile to tile in MIN_INPUT_DEPLAY / 2 ms. Mobs slide at a speed proportional to their Move cost / Max AP ratio. Animation refresh rate is 25ms. --- AndorsTrail/res/raw/monsterlist_debug.json | 2 +- AndorsTrail/res/xml/debugmap.tmx | 10 +-- .../rpg/AndorsTrail/controller/Constants.java | 2 +- .../controller/MonsterMovementController.java | 18 ++-- .../controller/MovementController.java | 23 +++-- .../controller/VisualEffectController.java | 65 ++++++++++++++ .../listeners/VisualEffectFrameListener.java | 3 + .../listeners/VisualEffectFrameListeners.java | 19 ++++ .../rpg/AndorsTrail/model/actor/Actor.java | 4 + .../rpg/AndorsTrail/model/actor/Player.java | 2 - .../gpl/rpg/AndorsTrail/util/CoordRect.java | 20 +++++ .../gpl/rpg/AndorsTrail/view/MainView.java | 89 +++++++++++++++++-- 12 files changed, 231 insertions(+), 26 deletions(-) diff --git a/AndorsTrail/res/raw/monsterlist_debug.json b/AndorsTrail/res/raw/monsterlist_debug.json index f1fa767a2..eb168b5aa 100644 --- a/AndorsTrail/res/raw/monsterlist_debug.json +++ b/AndorsTrail/res/raw/monsterlist_debug.json @@ -62,7 +62,7 @@ "monsterClass": "insect", "maxHP": 10, "maxAP": 10, - "moveCost": 10, + "moveCost": 5, "attackCost": 10, "attackChance": 50, "droplistID": "debuglist1", diff --git a/AndorsTrail/res/xml/debugmap.tmx b/AndorsTrail/res/xml/debugmap.tmx index 4d9b20e2c..1c7683509 100644 --- a/AndorsTrail/res/xml/debugmap.tmx +++ b/AndorsTrail/res/xml/debugmap.tmx @@ -122,12 +122,12 @@ - eJw7ysbAcHSQ4ydUNOsEEqalultkmgcAw35PpQ== + eJw7ysbAcHQQ4wYOBoYnVDTvBBKmpbpbZJoHAISsT2I= - eJxjYMAOktlxSNAYSFDRXlsm6plFLwAA6HwAyQ== + eJyby88AB7FI7JvMDBSDbDLMyOQnrIZYYMtEPbNIBfPI9AcAQ5QDxw== @@ -140,7 +140,7 @@ eJxjYBi+wFVgoF1AOgAAPUQAVg== - + @@ -181,10 +181,10 @@ - + - + diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/Constants.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/Constants.java index b5782ae34..57618f122 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/Constants.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/Constants.java @@ -32,7 +32,7 @@ public final class Constants { public static final int TICKS_PER_FULLROUND = FULLROUND_DURATION / TICK_DELAY; public static final int SPLATTER_DURATION_MS = 20000; - public static final ConstRange monsterWaitTurns = new ConstRange(30,4); + public static final ConstRange monsterWaitTurns = new ConstRange(5,1); public static final long MAP_UNVISITED_RESPAWN_DURATION_MS = 3 * 60 * 1000; // 3 min in milliseconds public static final String PREFERENCE_MODEL_LASTRUNVERSION = "lastversion"; diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/MonsterMovementController.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/MonsterMovementController.java index 2a19631bc..b8b8f68ca 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/MonsterMovementController.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/MonsterMovementController.java @@ -74,7 +74,7 @@ public final class MonsterMovementController implements EvaluateWalkable { private void moveMonster(final Monster m, final MonsterSpawnArea area) { PredefinedMap map = world.model.currentMap; LayeredTileMap tileMap = world.model.currentTileMap; - m.nextActionTime += getMillisecondsPerMove(m); + m.nextActionTime = System.currentTimeMillis() + getMillisecondsPerMove(m); if (m.movementDestination == null) { // Monster has waited and should start to move again. m.movementDestination = new Coord(m.position); @@ -128,7 +128,7 @@ public final class MonsterMovementController implements EvaluateWalkable { private static void cancelCurrentMonsterMovement(final Monster m) { m.movementDestination = null; - m.nextActionTime += getMillisecondsPerMove(m) * Constants.rollValue(Constants.monsterWaitTurns); + m.nextActionTime = System.currentTimeMillis() + (getMillisecondsPerMove(m) * Constants.rollValue(Constants.monsterWaitTurns)); } private static int getMillisecondsPerMove(Monster m) { @@ -151,9 +151,17 @@ public final class MonsterMovementController implements EvaluateWalkable { return monsterCanMoveTo(world.model.currentMap, world.model.currentTileMap, r); } - public void moveMonsterToNextPosition(Monster m, PredefinedMap map) { - CoordRect previousPosition = new CoordRect(new Coord(m.position), m.rectPosition.size); + public void moveMonsterToNextPosition(final Monster m, final PredefinedMap map) { + final CoordRect previousPosition = new CoordRect(new Coord(m.position), m.rectPosition.size); + m.lastPosition.set(previousPosition.topLeft); m.position.set(m.nextPosition.topLeft); - monsterMovementListeners.onMonsterMoved(map, m, previousPosition); + controllers.effectController.startActorMoveEffect(m, previousPosition.topLeft, m.position, getMillisecondsPerMove(m) / 4, new VisualEffectController.VisualEffectCompletedCallback() { + + @Override + public void onVisualEffectCompleted(int callbackValue) { + + monsterMovementListeners.onMonsterMoved(map, m, previousPosition); + } + }, 0); } } diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/MovementController.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/MovementController.java index 66aa575b7..878d40c20 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/MovementController.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/MovementController.java @@ -228,15 +228,24 @@ public final class MovementController implements TimedMessageTask.Callback { player.lastPosition.set(player.position); player.position.set(newPosition); controllers.combatController.setCombatSelection(null, null); - playerMovementListeners.onPlayerMoved(newPosition, player.lastPosition); + + controllers.effectController.startActorMoveEffect(player, player.lastPosition, newPosition, (int) (Constants.MINIMUM_INPUT_INTERVAL / 2), new VisualEffectController.VisualEffectCompletedCallback() { + + @Override + public void onVisualEffectCompleted(int callbackValue) { + playerMovementListeners.onPlayerMoved(newPosition, player.lastPosition); - controllers.mapController.handleMapEventsAfterMovement(currentMap, newPosition, player.lastPosition); + controllers.mapController.handleMapEventsAfterMovement(currentMap, newPosition, player.lastPosition); - if (!world.model.uiSelections.isInCombat) { - //currentMap can be outdated due to mapchange events processed above. - Loot loot = world.model.currentMap.getBagAt(newPosition); - if (loot != null) controllers.itemController.playerSteppedOnLootBag(loot); - } + if (!world.model.uiSelections.isInCombat) { + //currentMap can be outdated due to mapchange events processed above. + Loot loot = world.model.currentMap.getBagAt(newPosition); + if (loot != null) controllers.itemController.playerSteppedOnLootBag(loot); + } + } + }, 0); + + } public void respawnPlayer(Resources res) { diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/VisualEffectController.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/VisualEffectController.java index 63812e7ab..bd422491f 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/VisualEffectController.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/VisualEffectController.java @@ -4,9 +4,11 @@ import android.graphics.Color; import android.graphics.Paint; import android.graphics.Paint.Align; import android.os.Handler; + import com.gpl.rpg.AndorsTrail.context.ControllerContext; import com.gpl.rpg.AndorsTrail.context.WorldContext; import com.gpl.rpg.AndorsTrail.controller.listeners.VisualEffectFrameListeners; +import com.gpl.rpg.AndorsTrail.model.actor.Actor; import com.gpl.rpg.AndorsTrail.model.actor.Monster; import com.gpl.rpg.AndorsTrail.model.actor.MonsterType; import com.gpl.rpg.AndorsTrail.model.map.PredefinedMap; @@ -54,7 +56,70 @@ public final class VisualEffectController { enqueuedEffectID = null; enqueuedEffectValue = 0; } + + public void startActorMoveEffect(Actor actor, Coord origin, Coord destination, int duration, VisualEffectCompletedCallback callback, int callbackValue) { + ++effectCount; + (new SpriteMoveAnimation(origin, destination, duration, actor, callback, callbackValue)) + .start(); + } + public final class SpriteMoveAnimation extends Handler implements Runnable { + + private static final int millisecondsPerFrame=25; + + private final VisualEffectCompletedCallback callback; + private final int callbackValue; + + public final int duration; + public final Actor actor; + public final Coord origin; + public final Coord destination; + + public int timeElapsed; + + @Override + public void run() { + update(); + if (System.currentTimeMillis() - actor.vfxStartTime >= duration) { + onCompleted(); + } else { + postDelayed(this, millisecondsPerFrame); + } + } + + public SpriteMoveAnimation(Coord origin, Coord destination, int duration, Actor actor, VisualEffectCompletedCallback callback, int callbackValue) { + this.callback = callback; + this.callbackValue = callbackValue; + this.duration = duration; + this.actor = actor; + this.origin = origin; + this.destination = destination; + this.timeElapsed = 0; + + } + + private void update() { + + visualEffectFrameListeners.onNewSpriteMoveFrame(this); + } + + private void onCompleted() { + --effectCount; + actor.hasVFXRunning = false; + if (callback != null) callback.onVisualEffectCompleted(callbackValue); + visualEffectFrameListeners.onSpriteMoveCompleted(this); + } + + + public void start() { + actor.hasVFXRunning = true; + actor.vfxDuration = duration; + actor.vfxStartTime = System.currentTimeMillis(); + postDelayed(this, 0); + } + + } + public final class VisualEffectAnimation extends Handler implements Runnable { @Override diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/listeners/VisualEffectFrameListener.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/listeners/VisualEffectFrameListener.java index 4fb6a7d96..c794c8624 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/listeners/VisualEffectFrameListener.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/listeners/VisualEffectFrameListener.java @@ -1,8 +1,11 @@ package com.gpl.rpg.AndorsTrail.controller.listeners; +import com.gpl.rpg.AndorsTrail.controller.VisualEffectController.SpriteMoveAnimation; import com.gpl.rpg.AndorsTrail.controller.VisualEffectController.VisualEffectAnimation; public interface VisualEffectFrameListener { void onNewAnimationFrame(VisualEffectAnimation animation, int tileID, int textYOffset); void onAnimationCompleted(VisualEffectAnimation animation); + void onNewSpriteMoveFrame(SpriteMoveAnimation animation); + void onSpriteMoveCompleted(SpriteMoveAnimation animation); } diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/listeners/VisualEffectFrameListeners.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/listeners/VisualEffectFrameListeners.java index 0801938c7..cec0fcdba 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/listeners/VisualEffectFrameListeners.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/listeners/VisualEffectFrameListeners.java @@ -1,5 +1,6 @@ package com.gpl.rpg.AndorsTrail.controller.listeners; +import com.gpl.rpg.AndorsTrail.controller.VisualEffectController.SpriteMoveAnimation; import com.gpl.rpg.AndorsTrail.controller.VisualEffectController.VisualEffectAnimation; import com.gpl.rpg.AndorsTrail.util.ListOfListeners; @@ -12,6 +13,14 @@ public final class VisualEffectFrameListeners extends ListOfListeners onAnimationCompleted = new Function1() { @Override public void call(VisualEffectFrameListener listener, VisualEffectAnimation animation) { listener.onAnimationCompleted(animation); } }; + + private final Function1 onNewSpriteMoveFrame = new Function1() { + @Override public void call(VisualEffectFrameListener listener, SpriteMoveAnimation animation) { listener.onNewSpriteMoveFrame(animation); } + }; + + private final Function1 onSpriteMoveCompleted = new Function1() { + @Override public void call(VisualEffectFrameListener listener, SpriteMoveAnimation animation) { listener.onSpriteMoveCompleted(animation); } + }; @Override public void onNewAnimationFrame(VisualEffectAnimation animation, int tileID, int textYOffset) { @@ -22,4 +31,14 @@ public final class VisualEffectFrameListeners extends ListOfListeners model.player.vfxDuration) vfxElapsedTime = model.player.vfxDuration; + int x = ((model.player.position.x - mapViewArea.topLeft.x) * tileSize * vfxElapsedTime + ((model.player.lastPosition.x - mapViewArea.topLeft.x) * tileSize * (model.player.vfxDuration - vfxElapsedTime))) / model.player.vfxDuration; + int y = ((model.player.position.y - mapViewArea.topLeft.y) * tileSize * vfxElapsedTime + ((model.player.lastPosition.y - mapViewArea.topLeft.y) * tileSize * (model.player.vfxDuration - vfxElapsedTime))) / model.player.vfxDuration; + tiles.drawTile(canvas, model.player.iconID, x, y, mPaint); + } for (MonsterSpawnArea a : currentMap.spawnAreas) { for (Monster m : a.monsters) { - drawFromMapPosition(canvas, area, m.rectPosition, m.iconID); + if (!m.hasVFXRunning) { + drawFromMapPosition(canvas, area, m.rectPosition, m.iconID); + } else if (area.intersects(m.rectPosition) || area.contains(m.lastPosition)) { + int vfxElapsedTime = (int) (System.currentTimeMillis() - m.vfxStartTime); + if (vfxElapsedTime > m.vfxDuration) vfxElapsedTime = m.vfxDuration; + int x = ((m.position.x - mapViewArea.topLeft.x) * tileSize * vfxElapsedTime + ((m.lastPosition.x - mapViewArea.topLeft.x) * tileSize * (m.vfxDuration - vfxElapsedTime))) / m.vfxDuration; + int y = ((m.position.y - mapViewArea.topLeft.y) * tileSize * vfxElapsedTime + ((m.lastPosition.y - mapViewArea.topLeft.y) * tileSize * (m.vfxDuration - vfxElapsedTime))) / m.vfxDuration; + tiles.drawTile(canvas, m.iconID, x, y, mPaint); + } } } - + } + + private void doDrawRect_Above(Canvas canvas, CoordRect area) { tryDrawMapLayer(canvas, area, currentTileMap.currentLayout.layerAbove); if (model.uiSelections.selectedPosition != null) { @@ -342,6 +405,7 @@ public final class MainView extends SurfaceView ,Math.min(screenSizeTileCount.height, currentMap.size.height) ); mapViewArea = new CoordRect(mapTopLeft, visibleNumberOfTiles); + updateClip(); screenOffset.set( (surfaceSize.width - scaledTileSize * visibleNumberOfTiles.width) / 2 @@ -370,8 +434,13 @@ public final class MainView extends SurfaceView mapTopLeft.y = Math.max(0, playerPosition.y - mapViewArea.size.height/2); mapTopLeft.y = Math.min(mapTopLeft.y, currentMap.size.height - mapViewArea.size.height); } + updateClip(); } } + + private void updateClip() { + worldCoordsToScreenCords(mapViewArea, redrawClip); + } @Override public void onPlayerMoved(Coord newPosition, Coord previousPosition) { @@ -492,6 +561,16 @@ public final class MainView extends SurfaceView public void onAnimationCompleted(VisualEffectAnimation animation) { redrawArea(animation.area, RedrawAreaDebugReason.EffectCompleted); } + + @Override + public void onNewSpriteMoveFrame(SpriteMoveAnimation animation) { + redrawMoveArea_(CoordRect.getBoundingRect(animation.origin, animation.destination), animation); + } + + @Override + public void onSpriteMoveCompleted(SpriteMoveAnimation animation) { + redrawArea(CoordRect.getBoundingRect(animation.origin, animation.destination), RedrawAreaDebugReason.EffectCompleted); + } @Override public void onNewTick() {