v0.4.2: FOV and shadowcaster optimizations

This commit is contained in:
Evan Debenham 2016-08-22 14:29:51 -04:00 committed by Evan Debenham
parent 9f8981db07
commit f1164af058
7 changed files with 57 additions and 53 deletions

View File

@ -663,8 +663,7 @@ public class Dungeon {
return; return;
} }
level.updateFieldOfView( hero ); level.updateFieldOfView(hero, visible);
System.arraycopy( Level.fieldOfView, 0, visible, 0, visible.length );
BArray.or( level.visited, visible, level.visited ); BArray.or( level.visited, visible, level.visited );

View File

@ -79,7 +79,7 @@ public abstract class Char extends Actor {
@Override @Override
protected boolean act() { protected boolean act() {
Dungeon.level.updateFieldOfView( this ); Dungeon.level.updateFieldOfView( this, Level.fieldOfView );
return false; return false;
} }

View File

@ -65,7 +65,7 @@ public class Guard extends Mob {
@Override @Override
protected boolean act() { protected boolean act() {
Dungeon.level.updateFieldOfView( this ); Dungeon.level.updateFieldOfView( this, Level.fieldOfView );
if (state == HUNTING && if (state == HUNTING &&
paralysed <= 0 && paralysed <= 0 &&

View File

@ -62,7 +62,7 @@ public class Piranha extends Mob {
return true; return true;
} else { } else {
//this causes pirahna to move away when a door is closed on them. //this causes pirahna to move away when a door is closed on them.
Dungeon.level.updateFieldOfView( this ); Dungeon.level.updateFieldOfView( this, Level.fieldOfView );
enemy = chooseEnemy(); enemy = chooseEnemy();
if (state == this.HUNTING && if (state == this.HUNTING &&
!(enemy != null && enemy.isAlive() && Level.fieldOfView[enemy.pos] && enemy.invisible <= 0)){ !(enemy != null && enemy.isAlive() && Level.fieldOfView[enemy.pos] && enemy.invisible <= 0)){

View File

@ -74,6 +74,7 @@ import com.shatteredpixel.shatteredpixeldungeon.plants.Plant;
import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene; import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSprite; import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSprite;
import com.shatteredpixel.shatteredpixeldungeon.ui.CustomTileVisual; import com.shatteredpixel.shatteredpixeldungeon.ui.CustomTileVisual;
import com.shatteredpixel.shatteredpixeldungeon.utils.BArray;
import com.shatteredpixel.shatteredpixeldungeon.utils.GLog; import com.shatteredpixel.shatteredpixeldungeon.utils.GLog;
import com.watabou.noosa.Game; import com.watabou.noosa.Game;
import com.watabou.noosa.Group; import com.watabou.noosa.Group;
@ -544,6 +545,8 @@ public abstract class Level implements Bundlable {
protected void buildFlagMaps() { protected void buildFlagMaps() {
fieldOfView = new boolean[length()];
passable = new boolean[length()]; passable = new boolean[length()];
losBlocking = new boolean[length()]; losBlocking = new boolean[length()];
flamable = new boolean[length()]; flamable = new boolean[length()];
@ -896,10 +899,8 @@ public abstract class Level implements Bundlable {
} }
} }
public boolean[] updateFieldOfView( Char c ) { public void updateFieldOfView( Char c, boolean[] fieldOfView ) {
fieldOfView = new boolean[length()];
int cx = c.pos % width(); int cx = c.pos % width();
int cy = c.pos / width(); int cy = c.pos / width();
@ -907,10 +908,13 @@ public abstract class Level implements Bundlable {
&& c.buff( TimekeepersHourglass.timeStasis.class ) == null && c.isAlive(); && c.buff( TimekeepersHourglass.timeStasis.class ) == null && c.isAlive();
if (sighted) { if (sighted) {
ShadowCaster.castShadow( cx, cy, fieldOfView, c.viewDistance ); ShadowCaster.castShadow( cx, cy, fieldOfView, c.viewDistance );
} else {
BArray.setFalse(fieldOfView);
} }
int sense = 1; int sense = 1;
if (c.isAlive()) { //Currently only the hero can get mind vision
if (c.isAlive() && c == Dungeon.hero) {
for (Buff b : c.buffs( MindVision.class )) { for (Buff b : c.buffs( MindVision.class )) {
sense = Math.max( ((MindVision)b).distance, sense ); sense = Math.max( ((MindVision)b).distance, sense );
} }
@ -926,15 +930,12 @@ public abstract class Level implements Bundlable {
int len = bx - ax + 1; int len = bx - ax + 1;
int pos = ax + ay * width(); int pos = ax + ay * width();
for (int y = ay; y <= by; y++, pos+=width()) { for (int y = ay; y <= by; y++, pos+=width()) {
Arrays.fill( fieldOfView, pos, pos + len, true ); System.arraycopy(discoverable, pos, fieldOfView, pos, len);
}
for (int i=0; i < length(); i++) {
fieldOfView[i] &= discoverable[i];
} }
} }
if (c.isAlive()) { //Currently only the hero can get mind vision or awareness
if (c.isAlive() && c == Dungeon.hero) {
if (c.buff( MindVision.class ) != null) { if (c.buff( MindVision.class ) != null) {
for (Mob mob : mobs) { for (Mob mob : mobs) {
int p = mob.pos; int p = mob.pos;
@ -948,7 +949,7 @@ public abstract class Level implements Bundlable {
fieldOfView[p + width()] = true; fieldOfView[p + width()] = true;
fieldOfView[p - width()] = true; fieldOfView[p - width()] = true;
} }
} else if (c == Dungeon.hero && ((Hero)c).heroClass == HeroClass.HUNTRESS) { } else if (((Hero)c).heroClass == HeroClass.HUNTRESS) {
for (Mob mob : mobs) { for (Mob mob : mobs) {
int p = mob.pos; int p = mob.pos;
if (distance( c.pos, p) == 2) { if (distance( c.pos, p) == 2) {
@ -980,11 +981,12 @@ public abstract class Level implements Bundlable {
} }
} }
for (Heap heap : heaps.values()) if (c == Dungeon.hero) {
if (!heap.seen && fieldOfView[heap.pos] && c == Dungeon.hero) for (Heap heap : heaps.values())
heap.seen = true; if (!heap.seen && fieldOfView[heap.pos])
heap.seen = true;
return fieldOfView; }
} }
public int distance( int a, int b ) { public int distance( int a, int b ) {

View File

@ -22,18 +22,13 @@ package com.shatteredpixel.shatteredpixeldungeon.mechanics;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon; import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.levels.Level; import com.shatteredpixel.shatteredpixeldungeon.levels.Level;
import com.shatteredpixel.shatteredpixeldungeon.utils.BArray;
import java.util.Arrays;
public final class ShadowCaster { public final class ShadowCaster {
private static final int MAX_DISTANCE = 8; private static final int MAX_DISTANCE = 8;
private static int distance; private static boolean[] falseArray;
private static int limits[];
private static boolean[] losBlocking;
private static boolean[] fieldOfView;
private static int[][] rounding; private static int[][] rounding;
static { static {
@ -46,30 +41,28 @@ public final class ShadowCaster {
} }
} }
private static Obstacles obs = new Obstacles();
public static void castShadow( int x, int y, boolean[] fieldOfView, int distance ) { public static void castShadow( int x, int y, boolean[] fieldOfView, int distance ) {
losBlocking = Level.losBlocking; BArray.setFalse(fieldOfView);
ShadowCaster.distance = distance;
limits = rounding[distance];
ShadowCaster.fieldOfView = fieldOfView;
Arrays.fill( fieldOfView, false );
fieldOfView[y * Dungeon.level.width() + x] = true; fieldOfView[y * Dungeon.level.width() + x] = true;
scanSector( x, y, +1, +1, 0, 0 ); boolean[] losBlocking = Level.losBlocking;
scanSector( x, y, -1, +1, 0, 0 ); Obstacles obs = new Obstacles();
scanSector( x, y, +1, -1, 0, 0 );
scanSector( x, y, -1, -1, 0, 0 ); scanSector( distance, fieldOfView, losBlocking, obs, x, y, +1, +1, 0, 0 );
scanSector( x, y, 0, 0, +1, +1 ); scanSector( distance, fieldOfView, losBlocking, obs, x, y, -1, +1, 0, 0 );
scanSector( x, y, 0, 0, -1, +1 ); scanSector( distance, fieldOfView, losBlocking, obs, x, y, +1, -1, 0, 0 );
scanSector( x, y, 0, 0, +1, -1 ); scanSector( distance, fieldOfView, losBlocking, obs, x, y, -1, -1, 0, 0 );
scanSector( x, y, 0, 0, -1, -1 ); scanSector( distance, fieldOfView, losBlocking, obs, x, y, 0, 0, +1, +1 );
scanSector( distance, fieldOfView, losBlocking, obs, x, y, 0, 0, -1, +1 );
scanSector( distance, fieldOfView, losBlocking, obs, x, y, 0, 0, +1, -1 );
scanSector( distance, fieldOfView, losBlocking, obs, x, y, 0, 0, -1, -1 );
} }
private static void scanSector( int cx, int cy, int m1, int m2, int m3, int m4 ) { //TODO: This is still fairly expensive, look into further optimizing this
private static void scanSector( int distance, boolean[] fieldOfView, boolean[] losBlocking, Obstacles obs, int cx, int cy, int m1, int m2, int m3, int m4 ) {
obs.reset(); obs.reset();
@ -77,7 +70,7 @@ public final class ShadowCaster {
float dq2 = 0.5f / p; float dq2 = 0.5f / p;
int pp = limits[p]; int pp = rounding[distance][p];
for (int q=0; q <= pp; q++) { for (int q=0; q <= pp; q++) {
int x = cx + q * m1 + p * m3; int x = cx + q * m1 + p * m3;
@ -112,8 +105,8 @@ public final class ShadowCaster {
private static final class Obstacles { private static final class Obstacles {
private static int SIZE = (MAX_DISTANCE+1) * (MAX_DISTANCE+1) / 2; private static int SIZE = (MAX_DISTANCE+1) * (MAX_DISTANCE+1) / 2;
private static float[] a1 = new float[SIZE]; private float[] a1 = new float[SIZE];
private static float[] a2 = new float[SIZE]; private float[] a2 = new float[SIZE];
private int length; private int length;
private int limit; private int limit;

View File

@ -22,6 +22,16 @@ package com.shatteredpixel.shatteredpixeldungeon.utils;
public class BArray { public class BArray {
private static boolean[] falseArray;
//This is MUCH faster than making a new boolean[] or using Arrays.fill;
public static void setFalse( boolean[] toBeFalse ){
if (falseArray == null || falseArray.length < toBeFalse.length)
falseArray = new boolean[toBeFalse.length];
System.arraycopy(falseArray, 0, toBeFalse, 0, toBeFalse.length);
}
public static boolean[] and( boolean[] a, boolean[] b, boolean[] result ) { public static boolean[] and( boolean[] a, boolean[] b, boolean[] result ) {
int length = a.length; int length = a.length;