Rewrote Direction utility class for accuracy

Rewrote Direction utility class to relate directions based on tile instead of unit circle
MapRenderer will not longer update angles of objects that have no target set
This commit is contained in:
Collin Smith 2019-02-23 03:09:31 -08:00
parent edc4166a8d
commit 5d6a140b54
4 changed files with 274 additions and 135 deletions

View File

@ -2,7 +2,11 @@ package gdx.diablo.entity;
import com.badlogic.gdx.math.MathUtils; import com.badlogic.gdx.math.MathUtils;
import org.apache.commons.lang3.ArrayUtils;
public class Direction { public class Direction {
private Direction() {}
public static final int SOUTH = 0; public static final int SOUTH = 0;
public static final int WEST = 1; public static final int WEST = 1;
public static final int NORTH = 2; public static final int NORTH = 2;
@ -12,172 +16,185 @@ public class Direction {
public static final int UP = 6; public static final int UP = 6;
public static final int RIGHT = 7; public static final int RIGHT = 7;
/* static final float RADIANS_4[] = {
static private final int SIN_BITS[] = {3, 4, 5}; MathUtils.atan2(-2, -4),
static private final int SIN_MASK[] = {~(-1 << SIN_BITS[0]), ~(-1 << SIN_BITS[1]), ~(-1 << SIN_BITS[2])}; MathUtils.atan2(-2, 4),
static private final int SIN_COUNT[] = {SIN_MASK[0] + 1, SIN_MASK[1] + 1, SIN_MASK[2] + 1}; MathUtils.atan2( 2, 4),
MathUtils.atan2( 2, -4),
static private final float radFull = MathUtils.PI2;
static private final float radToIndex[] = {SIN_COUNT[0] / radFull, SIN_COUNT[1] / radFull, SIN_COUNT[2] / radFull};
static final int DIRS[][] = {
{7, 2, 6, 1, 5, 0, 4, 3},
{7, 13, 2, 12, 6, 11, 1, 10, 5, 9, 0, 8, 4, 15, 3, 14},
{7, 27, 13, 26, 2, 25, 12, 24, 6, 23, 11, 22, 1, 21, 10, 20, 5, 19, 9, 18, 0, 17, 8, 16, 4, 31, 15, 30, 3, 29, 14, 28}
}; };
*/ static final int DIRS_4M[] = {0, 3, 2, 1};
static final float RADIANS_4M[];
static {
RADIANS_4M = new float[4];
float min = RADIANS_4[3];
for (int i = 0; i < 4; i++) {
RADIANS_4M[i] = (min + RADIANS_4[i]) / 2;
min = RADIANS_4[i];
}
RADIANS_4M[0] = -MathUtils.PI;
}
static final byte[][] OFFSETS = new byte[][] { static final float RADIANS_8[] = {
{ 0, 2}, RADIANS_4[0],
{-2, 0}, MathUtils.atan2(-4, 0),
{ 0,-2}, RADIANS_4[1],
{ 2, 0}, MathUtils.atan2( 0, 8),
RADIANS_4[2],
{ 2, 2}, MathUtils.atan2( 4, 0),
{-2, 2}, RADIANS_4[3],
{-2,-2}, MathUtils.atan2( 0, -8),
{ 2,-2},
{ 1, 2},
{-1, 2},
{-2, 1},
{-2,-1},
{-1,-2},
{ 1,-2},
{ 2,-1},
{ 2, 1},
}; };
static final int DIRS_8M[] = {5, 0, 4, 3, 7, 2, 6, 1};
static final float RADIANS_8M[];
static {
RADIANS_8M = new float[8];
float min = -RADIANS_8[7];
for (int i = 0; i < 8; i++) {
RADIANS_8M[i] = (min + RADIANS_8[i]) / 2;
min = RADIANS_8[i];
}
}
static final float[] RADIANS = new float[] { static final float RADIANS_16[] = {
MathUtils.PI * 5 / 4, MathUtils.atan2(-1, -6),
MathUtils.PI * 3 / 4, RADIANS_8[0],
MathUtils.PI * 1 / 4, MathUtils.atan2(-3, -2),
MathUtils.PI * 7 / 4, RADIANS_8[1],
MathUtils.atan2(-3, 2),
MathUtils.PI * 3 / 2, RADIANS_8[2],
MathUtils.PI * 2 / 2, MathUtils.atan2(-1, 6),
MathUtils.PI * 1 / 2, RADIANS_8[3],
MathUtils.PI * 0 / 2, MathUtils.atan2(1, 6),
RADIANS_8[4],
MathUtils.PI * 11 / 8, MathUtils.atan2(3, 2),
MathUtils.PI * 9 / 8, RADIANS_8[5],
MathUtils.PI * 7 / 8, MathUtils.atan2(3, -2),
MathUtils.PI * 5 / 8, RADIANS_8[6],
MathUtils.PI * 3 / 8, MathUtils.atan2(1, -6),
MathUtils.PI * 1 / 8, RADIANS_8[7],
MathUtils.PI * 15 / 8,
MathUtils.PI * 13 / 8,
}; };
static final int DIRS_16M[] = {5, 9, 0, 8, 4, 15, 3, 14, 7, 13, 2, 12, 6, 11, 1, 10};
static final float RADIANS_16M[];
static {
RADIANS_16M = new float[16];
float min = -RADIANS_16[15];
for (int i = 0; i < 16; i++) {
RADIANS_16M[i] = (min + RADIANS_16[i]) / 2;
min = RADIANS_16[i];
}
}
private Direction() {} static final float RADIANS_32[] = {
MathUtils.atan2(-0.5f, -7),
RADIANS_16[0],
MathUtils.atan2(-1.5f, -5),
RADIANS_16[1],
MathUtils.atan2(-2.5f, -3),
RADIANS_16[2],
MathUtils.atan2(-3.5f, -1),
RADIANS_16[3],
MathUtils.atan2(-3.5f, 1),
RADIANS_16[4],
MathUtils.atan2(-2.5f, 3),
RADIANS_16[5],
MathUtils.atan2(-1.5f, 5),
RADIANS_16[6],
MathUtils.atan2(-0.5f, 7),
RADIANS_16[7],
MathUtils.atan2(0.5f, 7),
RADIANS_16[8],
MathUtils.atan2(1.5f, 5),
RADIANS_16[9],
MathUtils.atan2(2.5f, 3),
RADIANS_16[10],
MathUtils.atan2(3.5f, 1),
RADIANS_16[11],
MathUtils.atan2(3.5f, -1),
RADIANS_16[12],
MathUtils.atan2(2.5f, -3),
RADIANS_16[13],
MathUtils.atan2(1.5f, -5),
RADIANS_16[14],
MathUtils.atan2(0.5f, -7),
RADIANS_16[15],
};
static final int DIRS_32M[] = {5, 19, 9, 18, 0, 17, 8, 16, 4, 31, 15, 30, 3, 29, 14, 28, 7, 27, 13, 26, 2, 25, 12, 24, 6, 23, 11, 22, 1, 21, 10, 20};
static final float RADIANS_32M[];
static {
RADIANS_32M = new float[32];
float min = -RADIANS_32[31];
for (int i = 0; i < 32; i++) {
RADIANS_32M[i] = (min + RADIANS_32[i]) / 2;
min = RADIANS_32[i];
}
}
public static int radiansToDirection(float radians, int directions) { public static int radiansToDirection(float radians, int directions) {
switch (directions) { switch (directions) {
case 1: return 0; case 1: return 0;
case 4: return radiansToDirection4(radians);
case 8: return radiansToDirection8(radians); case 8: return radiansToDirection8(radians);
case 16: return radiansToDirection16(radians); case 16: return radiansToDirection16(radians);
case 32: return radiansToDirection32(radians);
default: return 0; default: return 0;
} }
} }
static final int DIRS_16[] = {7, 13, 2, 12, 6, 11, 1, 10, 5, 9, 0, 8, 4, 15, 3, 14}; private static int radiansToDirection4(float radians) {
static final float RADIANS_16[]; for (int i = 0; i < 4; i++) {
static { if (radians < RADIANS_4M[i]) {
float r = MathUtils.PI2 / 32; return DIRS_4M[i];
float a = r * 2; }
RADIANS_16 = new float[16];
for (int i = 0; i < 16; i++) {
RADIANS_16[i] = r;
r += a;
} }
//System.out.println(Arrays.toString(RADIANS_16)); return DIRS_4M[0];
} }
static final int DIRS_8[] = {7, 2, 6, 1, 5, 0, 4, 3}; private static int radiansToDirection8(float radians) {
static final float RADIANS_8[];
static {
float r = MathUtils.PI2 / 16;
float a = r * 2;
RADIANS_8 = new float[8];
for (int i = 0; i < 8; i++) { for (int i = 0; i < 8; i++) {
RADIANS_8[i] = r; if (radians < RADIANS_8M[i]) {
r += a; return DIRS_8M[i];
}
} }
//System.out.println(Arrays.toString(RADIANS_8)); return DIRS_8M[0];
} }
public static int radiansToDirection16(float radians) { private static int radiansToDirection16(float radians) {
if (radians < 0) radians += MathUtils.PI2;
for (int i = 0; i < 16; i++) { for (int i = 0; i < 16; i++) {
if (radians < RADIANS_16[i]) return DIRS_16[i]; if (radians < RADIANS_16M[i]) {
return DIRS_16M[i];
}
} }
return DIRS_16[0]; return DIRS_16M[0];
} }
public static int radiansToDirection8(float radians) { private static int radiansToDirection32(float radians) {
if (radians < 0) radians += MathUtils.PI2; for (int i = 0; i < 32; i++) {
for (int i = 0; i < 8; i++) { if (radians < RADIANS_32M[i]) {
if (radians < RADIANS_8[i]) return DIRS_8[i]; return DIRS_32M[i];
}
} }
return DIRS_8[0]; return DIRS_32M[0];
} }
public static int getOffX(float radians) { public static float snapToDirection(float radians, int directions) {
int id = radiansToDirection16(radians);
return OFFSETS[id][0];
}
public static int getOffY(float radians) {
int id = radiansToDirection16(radians);
return OFFSETS[id][1];
}
public static float radiansToDirection16Radians(float radians) {
if (radians < 0) radians += MathUtils.PI2;
for (int i = 0; i < 16; i++) {
if (radians < RADIANS_16[i]) return RADIANS[DIRS_16[i]];
}
return RADIANS[DIRS_16[0]];
}
/*
public static int radiansToDirection(float radians, int directions) {
int d;
switch (directions) { switch (directions) {
case 8: d = 0; break; case 1: return RADIANS_4[0];
case 16: d = 1; break; case 4: return _snapToDirection(radians, directions, DIRS_4M, RADIANS_4);
case 32: d = 2; break; case 8: return _snapToDirection(radians, directions, DIRS_8M, RADIANS_8);
default: throw new GdxRuntimeException("Invalid directions num: " + directions); case 16: return _snapToDirection(radians, directions, DIRS_16M, RADIANS_16);
case 32: return _snapToDirection(radians, directions, DIRS_32M, RADIANS_32);
default: return RADIANS_4[0];
} }
int id = (int) (radians * radToIndex[d]) & SIN_MASK[d];
System.out.println(id);
return DIRS[d][id];
} }
public static int getOffX(float radians) { private static float _snapToDirection(float radians, int dirs, int[] dirsm, float[] rads) {
int id = DIRS[1][(int) (radians * radToIndex[1]) & SIN_MASK[1]]; int dir = radiansToDirection(radians, dirs);
switch (id) { int index = ArrayUtils.indexOf(dirsm, dir);
case 8: id = 0; break; if (--index < 0) index = dirs - 1;
case 9: id = 0; break; return rads[index];
case 10: id = 0; break;
case 11: id = 0; break;
case 12: id = 0; break;
case 13: id = 0; break;
case 14: id = 0; break;
case 15: id = 0; break;
}
//System.out.println(id);
return OFFSETS[id][0];
} }
public static int getOffY(float radians) {
int id = DIRS[0][(int) (radians * radToIndex[0]) & SIN_MASK[0]];
return OFFSETS[id][1];
}
*/
} }

View File

@ -127,7 +127,7 @@ public class Entity {
String weaponClass; String weaponClass;
Vector3 position = new Vector3(); Vector3 position = new Vector3();
Vector3 velocity = new Vector3(); Vector3 velocity = new Vector3();
float angle = MathUtils.atan2(-1, -2);//MathUtils.PI * 3 / 2; float angle = MathUtils.atan2(-1, -2);
boolean running = false; boolean running = false;
float walkSpeed = 6; float walkSpeed = 6;
@ -470,10 +470,12 @@ public class Entity {
shapes.setColor(Color.RED); shapes.setColor(Color.RED);
shapes.line(x, y, x + MathUtils.cos(angle) * R, y + MathUtils.sin(angle) * R); shapes.line(x, y, x + MathUtils.cos(angle) * R, y + MathUtils.sin(angle) * R);
// FIXME: Should be number of direction dependent, not 16, one of 4,8,16,32 if (animation != null) {
float rounded = Direction.radiansToDirection16Radians(angle); int numDirs = animation.getNumDirections();
shapes.setColor(Color.GREEN); float rounded = Direction.snapToDirection(angle, numDirs);
shapes.line(x, y, x + MathUtils.cos(rounded) * R * 0.5f, y + MathUtils.sin(rounded) * R * 0.5f); shapes.setColor(Color.GREEN);
shapes.line(x, y, x + MathUtils.cos(rounded) * R * 0.5f, y + MathUtils.sin(rounded) * R * 0.5f);
}
} }
public void drawDebugTarget(ShapeRenderer shapes) { public void drawDebugTarget(ShapeRenderer shapes) {

View File

@ -436,7 +436,7 @@ public class MapRenderer {
if ((stx <= position.x && position.x < stx + Tile.SUBTILE_SIZE) if ((stx <= position.x && position.x < stx + Tile.SUBTILE_SIZE)
&& (sty <= position.y && position.y < sty + Tile.SUBTILE_SIZE)) { && (sty <= position.y && position.y < sty + Tile.SUBTILE_SIZE)) {
entity.update(Gdx.graphics.getDeltaTime()); entity.update(Gdx.graphics.getDeltaTime());
if (!entity.position().epsilonEquals(entity.target())) { if (!entity.position().epsilonEquals(entity.target()) && !entity.target().isZero()) {
entity.setAngle(angle(entity.position(), entity.target())); entity.setAngle(angle(entity.position(), entity.target()));
} }

View File

@ -0,0 +1,120 @@
package gdx.diablo.entity;
import org.junit.Assert;
import org.junit.Test;
import java.util.Arrays;
public class DirectionTest {
@Test
public void testRadians4() {
System.out.println(Arrays.toString(Direction.RADIANS_4));
double t = Integer.MIN_VALUE;
for (float f : Direction.RADIANS_4) {
Assert.assertTrue(f > t);
t = f;
}
}
@Test
public void testRadians4m() {
System.out.println(Arrays.toString(Direction.RADIANS_4));
System.out.println(Arrays.toString(Direction.RADIANS_4M));
double t = Integer.MIN_VALUE;
for (float f : Direction.RADIANS_4M) {
Assert.assertTrue(f > t);
t = f;
}
}
@Test
public void testRadians8() {
System.out.println(Arrays.toString(Direction.RADIANS_8));
double t = Integer.MIN_VALUE;
for (float f : Direction.RADIANS_8) {
Assert.assertTrue(f > t);
t = f;
}
}
@Test
public void testRadians8m() {
System.out.println(Arrays.toString(Direction.RADIANS_8));
System.out.println(Arrays.toString(Direction.RADIANS_8M));
double t = Integer.MIN_VALUE;
for (float f : Direction.RADIANS_8M) {
Assert.assertTrue(f > t);
t = f;
}
}
@Test
public void testRadians16() {
System.out.println(Arrays.toString(Direction.RADIANS_16));
double t = Integer.MIN_VALUE;
for (float f : Direction.RADIANS_16) {
Assert.assertTrue(f > t);
t = f;
}
}
@Test
public void testRadians16m() {
System.out.println(Arrays.toString(Direction.RADIANS_16));
System.out.println(Arrays.toString(Direction.RADIANS_16M));
double t = Integer.MIN_VALUE;
for (float f : Direction.RADIANS_16M) {
Assert.assertTrue(f > t);
t = f;
}
}
@Test
public void testRadians32() {
System.out.println(Arrays.toString(Direction.RADIANS_32));
double t = Integer.MIN_VALUE;
for (float f : Direction.RADIANS_32) {
Assert.assertTrue(f > t);
t = f;
}
}
@Test
public void testRadians32m() {
System.out.println(Arrays.toString(Direction.RADIANS_32));
System.out.println(Arrays.toString(Direction.RADIANS_32M));
double t = Integer.MIN_VALUE;
for (float f : Direction.RADIANS_32M) {
Assert.assertTrue(f > t);
t = f;
}
}
@Test
public void test_radiansToDirection4() {
for (float f : Direction.RADIANS_8M) {
System.out.println(f + " : " + Direction.radiansToDirection(f, 4));
}
}
@Test
public void test_radiansToDirection8() {
for (float f : Direction.RADIANS_16M) {
System.out.println(f + " : " + Direction.radiansToDirection(f, 8));
}
}
@Test
public void test_radiansToDirection16() {
for (float f : Direction.RADIANS_32M) {
System.out.println(f + " : " + Direction.radiansToDirection(f, 16));
}
}
@Test
public void test_radiansToDirection32() {
for (float f : Direction.RADIANS_32M) {
System.out.println(f + " : " + Direction.radiansToDirection(f, 32));
}
}
}