Pull Request #75: Visual Glitch Fix

This commit is contained in:
Nut.andor
2024-09-07 12:30:48 +02:00
5 changed files with 162 additions and 8 deletions

View File

@@ -19,11 +19,15 @@ import com.gpl.rpg.AndorsTrail.resource.VisualEffectCollection.VisualEffect;
import com.gpl.rpg.AndorsTrail.resource.tiles.TileManager;
import com.gpl.rpg.AndorsTrail.util.Coord;
import com.gpl.rpg.AndorsTrail.util.CoordRect;
import com.gpl.rpg.AndorsTrail.util.L;
import com.gpl.rpg.AndorsTrail.util.Size;
import java.util.ArrayList;
import java.util.List;
public final class VisualEffectController {
private int effectCount = 0;
private final List<VisualEffectAnimation> activeAnimations = new ArrayList<>();
private final ControllerContext controllers;
private final WorldContext world;
private final VisualEffectCollection effectTypes;
@@ -38,8 +42,24 @@ public final class VisualEffectController {
public void startEffect(Coord position, VisualEffectCollection.VisualEffectID effectID, String displayValue, VisualEffectCompletedCallback callback, int callbackValue) {
++effectCount;
(new VisualEffectAnimation(effectTypes.getVisualEffect(effectID), position, displayValue, callback, callbackValue))
.start();
VisualEffectAnimation animation = new VisualEffectAnimation(effectTypes.getVisualEffect(effectID), position, displayValue, callback, callbackValue);
activeAnimations.add(animation);
animation.start();
}
public void collectAndSendAnimations(VisualEffectAnimation animation) {
List<Integer> tileIDs = new ArrayList<>();
List<Integer> yOffsets = new ArrayList<>();
//There are more elegant ways to fix this, but to get multiple effects to work, this is the quickest fix: increase all other effects that are being played
//TODO: if someone got time to make this look nice, be my guest
for (VisualEffectAnimation vanimation : activeAnimations) {
if (vanimation != animation && vanimation.currentFrame < vanimation.effect.lastFrame) vanimation.currentFrame++;
tileIDs.add(vanimation.effect.frameIconIDs[vanimation.currentFrame]);
yOffsets.add(-2 * vanimation.currentFrame);
L.log("VisualEffectController: collectAndSendAnimations: " + vanimation.currentFrame + " currentFrame, effectCount = " + effectCount);
}
L.log("VisualEffectController: collectAndSendAnimations: " + activeAnimations.size() + " animations to send, effectCount = " + effectCount);
visualEffectFrameListeners.onNewAnimationFrames(activeAnimations, tileIDs, yOffsets);
}
private VisualEffectCollection.VisualEffectID enqueuedEffectID = null;
@@ -111,7 +131,7 @@ public final class VisualEffectController {
if (callback != null) callback.onVisualEffectCompleted(callbackValue);
visualEffectFrameListeners.onSpriteMoveCompleted(this);
}
public void start() {
actor.hasVFXRunning = true;
@@ -134,7 +154,19 @@ public final class VisualEffectController {
textPaint.setAlpha(255);
textPaint.setTextAlign(Align.CENTER);
}
public static class VisualEffectData {
public final VisualEffectAnimation effect;
public final int tileID;
public final int textYOffset;
public VisualEffectData(VisualEffectAnimation effect, int tileID, int textYOffset) {
this.effect = effect;
this.tileID = tileID;
this.textYOffset = textYOffset;
}
}
public final class VisualEffectAnimation extends Handler implements Runnable {
@Override
@@ -151,17 +183,23 @@ public final class VisualEffectController {
++currentFrame;
int frame = currentFrame;
int tileID = effect.frameIconIDs[frame];
int textYOffset = -2 * (frame);
// int tileID = effect.frameIconIDs[frame];
// int textYOffset = -2 * (frame);
if (frame >= beginFadeAtFrame && displayText != null) {
textPaint.setAlpha(255 * (effect.lastFrame - frame) / (effect.lastFrame - beginFadeAtFrame));
}
area.topLeft.y = position.y - 1;
visualEffectFrameListeners.onNewAnimationFrame(this, tileID, textYOffset);
//Fall back to onNewAnimationFrame if the new effect handle process is no longer desired
// visualEffectFrameListeners.onNewAnimationFrame(this, tileID, textYOffset);
collectAndSendAnimations(this);
}
private void onCompleted() {
--effectCount;
activeAnimations.remove(this);
visualEffectFrameListeners.onAnimationCompleted(this);
if (callback != null) callback.onVisualEffectCompleted(callbackValue);
}

View File

@@ -4,8 +4,11 @@ import com.gpl.rpg.AndorsTrail.controller.VisualEffectController.SpriteMoveAnima
import com.gpl.rpg.AndorsTrail.controller.VisualEffectController.VisualEffectAnimation;
import com.gpl.rpg.AndorsTrail.util.CoordRect;
import java.util.List;
public interface VisualEffectFrameListener {
void onNewAnimationFrame(VisualEffectAnimation animation, int tileID, int textYOffset);
void onNewAnimationFrames(List<VisualEffectAnimation> animations, List<Integer> tileIDs, List<Integer> textYOffsets);
void onAnimationCompleted(VisualEffectAnimation animation);
void onSpriteMoveStarted(SpriteMoveAnimation animation);
void onNewSpriteMoveFrame(SpriteMoveAnimation animation);

View File

@@ -5,12 +5,18 @@ import com.gpl.rpg.AndorsTrail.controller.VisualEffectController.VisualEffectAni
import com.gpl.rpg.AndorsTrail.util.CoordRect;
import com.gpl.rpg.AndorsTrail.util.ListOfListeners;
import java.util.List;
public final class VisualEffectFrameListeners extends ListOfListeners<VisualEffectFrameListener> implements VisualEffectFrameListener {
private final Function3<VisualEffectFrameListener, VisualEffectAnimation, Integer, Integer> onNewAnimationFrame = new Function3<VisualEffectFrameListener, VisualEffectAnimation, Integer, Integer>() {
@Override public void call(VisualEffectFrameListener listener, VisualEffectAnimation animation, Integer tileID, Integer textYOffset) { listener.onNewAnimationFrame(animation, tileID, textYOffset); }
};
private final Function3<VisualEffectFrameListener, List<VisualEffectAnimation>, List<Integer>, List<Integer>> onNewAnimationFrames = new Function3<VisualEffectFrameListener, List<VisualEffectAnimation>, List<Integer>, List<Integer>>() {
@Override public void call(VisualEffectFrameListener listener, List<VisualEffectAnimation> animations, List<Integer> tileIDs, List<Integer> textYOffsets) { listener.onNewAnimationFrames(animations, tileIDs, textYOffsets); }
};
private final Function1<VisualEffectFrameListener, VisualEffectAnimation> onAnimationCompleted = new Function1<VisualEffectFrameListener, VisualEffectAnimation>() {
@Override public void call(VisualEffectFrameListener listener, VisualEffectAnimation animation) { listener.onAnimationCompleted(animation); }
};
@@ -36,6 +42,11 @@ public final class VisualEffectFrameListeners extends ListOfListeners<VisualEffe
callAllListeners(this.onNewAnimationFrame, animation, tileID, textYOffset);
}
@Override
public void onNewAnimationFrames(List<VisualEffectAnimation> animations, List<Integer> tileIDs, List<Integer> textYOffsets) {
callAllListeners(this.onNewAnimationFrames, animations, tileIDs, textYOffsets);
}
@Override
public void onAnimationCompleted(VisualEffectAnimation animation) {
callAllListeners(this.onAnimationCompleted, animation);

View File

@@ -30,6 +30,18 @@ public final class CoordRect {
return true;
}
public static CoordRect union(CoordRect r1, CoordRect r2) {
int left = Math.min(r1.topLeft.x, r2.topLeft.x);
int top = Math.min(r1.topLeft.y, r2.topLeft.y);
int right = Math.max(r1.topLeft.x + r1.size.width, r2.topLeft.x + r2.size.width);
int bottom = Math.max(r1.topLeft.y + r1.size.height, r2.topLeft.y + r2.size.height);
int width = right - left;
int height = bottom - top;
return new CoordRect(new Coord(left, top), new Size(width, height));
}
/*
public static boolean contains(final int x, final int y, final Size size, final Coord p) {
if (p.x < x) return false;

View File

@@ -1,6 +1,8 @@
package com.gpl.rpg.AndorsTrail.view;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import com.gpl.rpg.AndorsTrail.AndorsTrailApplication;
import com.gpl.rpg.AndorsTrail.AndorsTrailPreferences;
@@ -8,6 +10,7 @@ import com.gpl.rpg.AndorsTrail.context.ControllerContext;
import com.gpl.rpg.AndorsTrail.context.WorldContext;
import com.gpl.rpg.AndorsTrail.controller.Constants;
import com.gpl.rpg.AndorsTrail.controller.InputController;
import com.gpl.rpg.AndorsTrail.controller.VisualEffectController;
import com.gpl.rpg.AndorsTrail.controller.VisualEffectController.BloodSplatter;
import com.gpl.rpg.AndorsTrail.controller.VisualEffectController.SpriteMoveAnimation;
import com.gpl.rpg.AndorsTrail.controller.VisualEffectController.VisualEffectAnimation;
@@ -29,6 +32,7 @@ import com.gpl.rpg.AndorsTrail.resource.tiles.TileCollection;
import com.gpl.rpg.AndorsTrail.resource.tiles.TileManager;
import com.gpl.rpg.AndorsTrail.util.Coord;
import com.gpl.rpg.AndorsTrail.util.CoordRect;
import com.gpl.rpg.AndorsTrail.util.L;
import com.gpl.rpg.AndorsTrail.util.Size;
import android.content.Context;
@@ -76,6 +80,8 @@ public final class MainView extends SurfaceView
private final int[] debugColors = {Color.MAGENTA, Color.BLUE, Color.CYAN, Color.GREEN, Color.YELLOW, Color.RED, Color.WHITE};
private final CoordRect p1x1 = new CoordRect(new Coord(), new Size(1,1));
private boolean hasSurface = false;
private List<VisualEffectController.VisualEffectData> activeEffects = new ArrayList<>();
//DEBUG
// private Coord touchedTile = null;
@@ -301,6 +307,68 @@ public final class MainView extends SurfaceView
if (c != null) holder.unlockCanvasAndPost(c);
}
}
private void redrawArea_(CoordRect area, final List<VisualEffectAnimation> effects, List<Integer> tileIDs, List<Integer> textYOffsets) {
if (!hasSurface) return;
if (!currentMap.intersects(area)) return;
if (!mapViewArea.intersects(area)) return;
if (shouldRedrawEverything()) {
area = mapViewArea;
}
calculateRedrawRect(area);
redrawRect.intersect(redrawClip);
Canvas c = null;
try {
c = holder.lockCanvas(redrawRect);
if (area != mapViewArea) {
if (isRedrawRectWholeScreen(redrawRect)) {
area = mapViewArea;
}
}
if (area == mapViewArea) {
area = adaptAreaToScrolling(area);
}
synchronized (holder) { synchronized (tiles) {
int xScroll = 0;
int yScroll = 0;
if (scrolling && scrollVector != null) {
xScroll = (int) (tileSize - (tileSize * (System.currentTimeMillis() - scrollStartTime) / SCROLL_DURATION));
xScroll = Math.max(0, Math.min(tileSize, xScroll)) * scrollVector.x;
yScroll = (int) (tileSize - (tileSize * (System.currentTimeMillis() - scrollStartTime) / SCROLL_DURATION));
yScroll = Math.max(0, Math.min(tileSize, yScroll)) * scrollVector.y;
}
c.clipRect(redrawClip);
c.translate(screenOffset.x + xScroll, screenOffset.y + yScroll);
doDrawRect(c, area);
// Render each effect
renderEffects(c);
} }
} finally {
if (c != null) holder.unlockCanvasAndPost(c);
}
}
private void renderEffects(Canvas canvas) {
for (VisualEffectController.VisualEffectData data : activeEffects) {
VisualEffectAnimation effect = data.effect;
int tileID = data.tileID;
int textYOffset = data.textYOffset;
L.log("Rendering effect at position: " + effect.position + " with tileID: " + tileID + " and textYOffset: " + textYOffset);
drawFromMapPosition(canvas, effect.area, effect.position, tileID);
if (effect.displayText != null) {
drawEffectText(canvas, effect.area, effect, textYOffset, effect.getTextPaint());
}
}
L.log("Total effects rendered: " + activeEffects.size());
activeEffects.clear();
}
private boolean isRedrawRectWholeScreen(Rect redrawRect) {
// if (redrawRect.width() < mapViewArea.size.width * scaledTileSize) return false;
@@ -322,6 +390,22 @@ public final class MainView extends SurfaceView
// if (shouldRedrawEverythingForVisualEffect()) area = mapViewArea;
redrawArea_(area, effect, tileID, textYOffset);
}
private void redrawAreaWithEffect(List<VisualEffectAnimation> effects, List<Integer> tileIDs, List<Integer> textYOffsets) {
CoordRect area = null;
for (int i = 0; i < effects.size(); i++) {
VisualEffectAnimation effect = effects.get(i);
activeEffects.add(new VisualEffectController.VisualEffectData(effect, tileIDs.get(i), textYOffsets.get(i)));
if (area == null) {
area = effect.area;
} else {
area = CoordRect.union(area, effect.area);
}
}
if (area != null) {
redrawArea_(area, effects, tileIDs, textYOffsets);
}
}
private void clearCanvas() {
if (!hasSurface) return;
Canvas c = null;
@@ -798,6 +882,12 @@ public final class MainView extends SurfaceView
redrawAreaWithEffect(animation, tileID, textYOffset);
}
@Override
public void onNewAnimationFrames(List<VisualEffectAnimation> animations, List<Integer> tileIDs, List<Integer> textYOffsets) {
L.log("Rendering " + animations.size() + " effects");
redrawAreaWithEffect(animations, tileIDs, textYOffsets);
}
@Override
public void onAnimationCompleted(VisualEffectAnimation animation) {
redrawArea(animation.area, RedrawAreaDebugReason.EffectCompleted);