pawns and basic pathfinding
parent
5dd6d8579b
commit
8e1f91d947
BIN
res/textures.png
BIN
res/textures.png
Binary file not shown.
|
Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 7.6 KiB |
|
|
@ -12,6 +12,12 @@ public class Drawing {
|
|||
drawSprite(sprite, x, y, sprite.width, sprite.height);
|
||||
}
|
||||
|
||||
private static float layer = 0f;
|
||||
|
||||
public static void setLayer(float layer) {
|
||||
Drawing.layer = layer;
|
||||
}
|
||||
|
||||
public static void drawSprite(Sprite sprite, int x, int y, int width, int height) {
|
||||
// lazy texture binding
|
||||
if(bound != sprite.atlas) {
|
||||
|
|
@ -21,16 +27,16 @@ public class Drawing {
|
|||
|
||||
glBegin(GL_QUADS);
|
||||
glVertexAttrib2f(SimpleShader.TEX_COORD, sprite.sourceBoxUV.x, sprite.sourceBoxUV.y);
|
||||
glVertex2f(x, y);
|
||||
glVertex3f(x, y, layer);
|
||||
|
||||
glVertexAttrib2f(SimpleShader.TEX_COORD, sprite.sourceBoxUV.x + sprite.sourceBoxUV.z, sprite.sourceBoxUV.y);
|
||||
glVertex2f(x + width, y);
|
||||
glVertex3f(x + width, y, layer);
|
||||
|
||||
glVertexAttrib2f(SimpleShader.TEX_COORD, sprite.sourceBoxUV.x + sprite.sourceBoxUV.z, sprite.sourceBoxUV.y + sprite.sourceBoxUV.w);
|
||||
glVertex2f(x + width, y + height);
|
||||
glVertex3f(x + width, y + height, layer);
|
||||
|
||||
glVertexAttrib2f(SimpleShader.TEX_COORD, sprite.sourceBoxUV.x, sprite.sourceBoxUV.y + sprite.sourceBoxUV.w);
|
||||
glVertex2f(x, y + height);
|
||||
glVertex3f(x, y + height, layer);
|
||||
glEnd();
|
||||
}
|
||||
}
|
||||
|
|
@ -10,6 +10,10 @@ public class Sprite {
|
|||
public final int width;
|
||||
public final int height;
|
||||
|
||||
public Sprite(Texture tex, int x, int y, int w, int h) {
|
||||
this(tex, new Vector4i(x, y, w, h));
|
||||
}
|
||||
|
||||
public Sprite(Texture tex, Vector4i box) {
|
||||
sourceBoxPixels = box;
|
||||
sourceBoxUV = new Vector4f(
|
||||
|
|
|
|||
|
|
@ -1,10 +1,16 @@
|
|||
package xyz.valnet.engine.scenegraph;
|
||||
|
||||
public class GameObject implements IRenderable, ITickable {
|
||||
// private IScene scene;
|
||||
import xyz.valnet.hadean.scenes.GameScene;
|
||||
|
||||
public GameObject(IScene scene) {
|
||||
// this.scene = scene;
|
||||
public class GameObject implements IRenderable, ITickable {
|
||||
private final GameScene scene;
|
||||
|
||||
public GameObject(GameScene scene) {
|
||||
this.scene = scene;
|
||||
}
|
||||
|
||||
protected <T> T get(Class<T> clazz) {
|
||||
return this.scene.get(clazz);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
package xyz.valnet.hadean;
|
||||
|
||||
import xyz.valnet.engine.graphics.Drawing;
|
||||
import xyz.valnet.engine.graphics.Sprite;
|
||||
import xyz.valnet.engine.math.Vector4f;
|
||||
import xyz.valnet.hadean.gameobjects.Terrain;
|
||||
import xyz.valnet.hadean.util.Assets;
|
||||
|
||||
public class Tile {
|
||||
private final int x, y;
|
||||
private final Vector4f color = new Vector4f((float) Math.random() * 0.1f, 0.4f + (float) Math.random() * 0.15f, (float) Math.random() * 0.05f, 1f);
|
||||
private final Sprite sprite = Assets.defaultTerrain[(int)Math.floor(Math.random() * Assets.defaultTerrain.length)];
|
||||
private final boolean obstacle;
|
||||
|
||||
public Tile(int x, int y) {
|
||||
float distanceFromOrigin = 0.9f - (float)Math.sqrt(x * x + y * y) / 42;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.obstacle = Math.random() > 0.8f;
|
||||
}
|
||||
|
||||
public void render() {
|
||||
Assets.flat.pushColor(isWalkable() ? color : new Vector4f(0.1f, 0.1f, 0.1f, 1f));
|
||||
Drawing.drawSprite(sprite, Terrain.left + x * Terrain.TILE_SIZE, Terrain.top + y * Terrain.TILE_SIZE);
|
||||
Assets.flat.popColor();
|
||||
}
|
||||
|
||||
public boolean isWalkable() {
|
||||
return !obstacle;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
package xyz.valnet.hadean.gameobjects;
|
||||
|
||||
import static xyz.valnet.engine.util.Math.lerp;
|
||||
|
||||
import java.util.Stack;
|
||||
|
||||
import xyz.valnet.engine.graphics.Drawing;
|
||||
import xyz.valnet.engine.math.Vector4f;
|
||||
import xyz.valnet.engine.scenegraph.GameObject;
|
||||
import xyz.valnet.hadean.pathfinding.Node;
|
||||
import xyz.valnet.hadean.scenes.GameScene;
|
||||
import xyz.valnet.hadean.util.Assets;
|
||||
|
||||
import static org.lwjgl.opengl.GL20.*;
|
||||
// import org.lwjgl.opengl.GL20;
|
||||
|
||||
public class Pawn extends GameObject {
|
||||
|
||||
private float x, y;
|
||||
private float dx, dy;
|
||||
|
||||
private float counter = 0;
|
||||
|
||||
private Stack<Node> path;
|
||||
|
||||
private final float speed = 20f;
|
||||
|
||||
public Pawn(GameScene scene) {
|
||||
super(scene);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render() {
|
||||
|
||||
Drawing.setLayer(0.15f);
|
||||
|
||||
if(path != null && path.size() > 0) {
|
||||
float cx = (int)(Terrain.left + (x - 0.5f) * Terrain.TILE_SIZE);
|
||||
float cy = (int)(Terrain.top + (y - 0.5f) * Terrain.TILE_SIZE);
|
||||
|
||||
Node n = path.peek();
|
||||
|
||||
float nx = Terrain.left + n.x * Terrain.TILE_SIZE;
|
||||
float ny = Terrain.top + n.y * Terrain.TILE_SIZE;
|
||||
|
||||
System.out.println("" + n.x + " " + n.y);
|
||||
|
||||
Drawing.drawSprite(Assets.pawn, (int)lerp(cx, nx, counter / speed), (int)lerp(cy, ny, counter / speed));
|
||||
|
||||
if(path != null) {
|
||||
for(Node node : path) {
|
||||
glBegin(GL_LINES);
|
||||
if(node.from == null) {
|
||||
glVertex2f(Terrain.left + x * Terrain.TILE_SIZE, Terrain.top + y * Terrain.TILE_SIZE);
|
||||
} else {
|
||||
glVertex2f(Terrain.left + (node.from.x + 0.5f) * Terrain.TILE_SIZE, Terrain.top + (node.from.y + 0.5f) * Terrain.TILE_SIZE);
|
||||
}
|
||||
glVertex2f(Terrain.left + (node.x + 0.5f) * Terrain.TILE_SIZE, Terrain.top + (node.y + 0.5f) * Terrain.TILE_SIZE);
|
||||
glEnd();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int nx = (int)(Terrain.left + (x - 0.5f) * Terrain.TILE_SIZE);
|
||||
int ny = (int)(Terrain.top + (y - 0.5f) * Terrain.TILE_SIZE);
|
||||
|
||||
Drawing.drawSprite(Assets.pawn, nx, ny);
|
||||
}
|
||||
|
||||
// Drawing.setLayer(0.1f);
|
||||
// Assets.flat.pushColor(Vector4f.black);
|
||||
// Drawing.drawSprite(Assets.pawn, (int)(Terrain.left + dx * Terrain.TILE_SIZE) - Assets.pawn.width / 2, (int)(Terrain.top + dy * Terrain.TILE_SIZE) - Assets.pawn.height / 2);
|
||||
|
||||
// Assets.flat.swapColor(new Vector4f(1, 0, 0, 1));
|
||||
// Drawing.setLayer(0.05f);
|
||||
|
||||
// Assets.flat.popColor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick(float dTime) {
|
||||
counter ++;
|
||||
if(counter >= speed) action();
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void action() {
|
||||
if(path == null || path.empty()) {
|
||||
dx = 0.5f + (float)Math.floor(Math.random() * Terrain.WORLD_SIZE);
|
||||
dy = 0.5f + (float)Math.floor(Math.random() * Terrain.WORLD_SIZE);
|
||||
|
||||
int ix = (int)Math.floor(x);
|
||||
int iy = (int)Math.floor(y);
|
||||
|
||||
int idx = (int)Math.floor(dx);
|
||||
int idy = (int)Math.floor(dy);
|
||||
|
||||
path = get(Terrain.class).getPath(ix, iy, idx, idy);
|
||||
if(path != null) {
|
||||
counter = 0;
|
||||
}
|
||||
} else {
|
||||
Node n = path.pop();
|
||||
x = n.x + 0.5f;
|
||||
y = n.y + 0.5f;
|
||||
counter = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,47 +1,178 @@
|
|||
package xyz.valnet.hadean.gameobjects;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Stack;
|
||||
|
||||
import xyz.valnet.engine.graphics.Drawing;
|
||||
import xyz.valnet.engine.graphics.Sprite;
|
||||
import xyz.valnet.engine.math.Vector4f;
|
||||
import xyz.valnet.engine.scenegraph.GameObject;
|
||||
import xyz.valnet.engine.scenegraph.IScene;
|
||||
import xyz.valnet.hadean.util.Assets;
|
||||
import xyz.valnet.hadean.Tile;
|
||||
import xyz.valnet.hadean.pathfinding.Node;
|
||||
import xyz.valnet.hadean.scenes.GameScene;
|
||||
|
||||
// implements IPathable, the thing that has callbacks for interfacing with a pathfinder.
|
||||
public class Terrain extends GameObject {
|
||||
|
||||
private final int WORLD_SIZE = 30;
|
||||
private final int TILE_SIZE = 8;
|
||||
public static final int WORLD_SIZE = 40;
|
||||
public static final int TILE_SIZE = 8;
|
||||
|
||||
private int[][] variation = new int[WORLD_SIZE][WORLD_SIZE];
|
||||
private Vector4f[][] colorVariations = new Vector4f[WORLD_SIZE][WORLD_SIZE];
|
||||
public static int left, top;
|
||||
|
||||
public Terrain(IScene scene) {
|
||||
private Tile[][] tiles = new Tile[WORLD_SIZE][WORLD_SIZE];
|
||||
|
||||
public Terrain(GameScene scene) {
|
||||
super(scene);
|
||||
for (int i = 0; i < WORLD_SIZE; i++) {
|
||||
for (int j = 0; j < WORLD_SIZE; j++) {
|
||||
variation[i][j] = (int) Math.floor(Math.random() * 4);
|
||||
colorVariations[i][j] = new Vector4f((float) Math.random() * 0.2f, 0.4f + (float) Math.random() * 0.2f, (float) Math.random() * 0.05f, 1f);
|
||||
tiles[i][j] = new Tile(i, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Tile getTile(int x, int y) {
|
||||
return tiles[x][y];
|
||||
}
|
||||
|
||||
private Node findNodeInList(List<Node> nodes, int x, int y) {
|
||||
for(Node node : nodes) {
|
||||
if(node.x == x && node.y == y) return node;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Node getPathfindingNode(int x, int y, List<Node> open, List<Node> closed, Node parent, int dstX, int dstY) {
|
||||
if(x < 0 || y < 0 || x >= WORLD_SIZE || y >= WORLD_SIZE) {
|
||||
// * out of bounds
|
||||
return null;
|
||||
}
|
||||
|
||||
Node node = findNodeInList(open, x, y);
|
||||
if(node == null) {
|
||||
node = findNodeInList(closed, x, y);
|
||||
}
|
||||
if(node == null) {
|
||||
node = new Node();
|
||||
node.x = x;
|
||||
node.y = y;
|
||||
node.from = parent;
|
||||
node.g = 0;
|
||||
if(parent != null) {
|
||||
node.g += parent.g;
|
||||
}
|
||||
if(node.from == null) {
|
||||
node.g = 0;
|
||||
} else {
|
||||
node.g += (int)(Math.round(10 * Math.sqrt(Math.pow(node.from.x - x, 2) + Math.pow(node.from.y - y, 2))));
|
||||
}
|
||||
node.h = (int)(Math.round(10 * Math.sqrt(Math.pow(dstX - x, 2) + Math.pow(dstY - y, 2))));
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
private Node[] getNeighbors(Node base, List<Node> open, List<Node> closed, int dstX, int dstY) {
|
||||
Node[] neighbors = new Node[8];
|
||||
neighbors[0] = getPathfindingNode(base.x - 1, base.y - 1, open, closed, base, dstX, dstY);
|
||||
neighbors[1] = getPathfindingNode(base.x, base.y - 1, open, closed, base, dstX, dstY);
|
||||
neighbors[2] = getPathfindingNode(base.x + 1, base.y - 1, open, closed, base, dstX, dstY);
|
||||
neighbors[3] = getPathfindingNode(base.x - 1, base.y, open, closed, base, dstX, dstY);
|
||||
neighbors[4] = getPathfindingNode(base.x + 1, base.y, open, closed, base, dstX, dstY);
|
||||
neighbors[5] = getPathfindingNode(base.x - 1, base.y + 1, open, closed, base, dstX, dstY);
|
||||
neighbors[6] = getPathfindingNode(base.x, base.y + 1, open, closed, base, dstX, dstY);
|
||||
neighbors[7] = getPathfindingNode(base.x + 1, base.y + 1, open, closed, base, dstX, dstY);
|
||||
return neighbors;
|
||||
}
|
||||
|
||||
public Stack<Node> getPath(int x1, int y1, int x2, int y2) {
|
||||
List<Node> open = new ArrayList<Node>();
|
||||
List<Node> closed = new ArrayList<Node>();
|
||||
|
||||
if(getTile(x2, y2).isWalkable() == false) return null;
|
||||
|
||||
open.add(getPathfindingNode(x1, y1, open, closed, null, x2, y2));
|
||||
|
||||
while (open.size() != 0) {
|
||||
open.sort(new Comparator<Node>() {
|
||||
@Override
|
||||
public int compare(Node a, Node b) {
|
||||
int aCost = a.getCost();
|
||||
int bCost = b.getCost();
|
||||
if(aCost > bCost) {
|
||||
return 1;
|
||||
} else if (aCost < bCost) {
|
||||
return -1;
|
||||
} else if (a.h > b.h) {
|
||||
return 1;
|
||||
} else if (a.h < b.h) {
|
||||
return -1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
});
|
||||
Node current = open.get(0);
|
||||
|
||||
open.remove(current);
|
||||
closed.add(current);
|
||||
|
||||
if(current.x == x2 && current.y == y2) {
|
||||
Stack<Node> path = new Stack<Node>();
|
||||
|
||||
Node n = current;
|
||||
while(n != null) {
|
||||
path.push(n);
|
||||
n = n.from;
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
Node[] neighbors = getNeighbors(current, open, closed, x2, y2);
|
||||
|
||||
for(Node node : neighbors) {
|
||||
if(node == null) continue;
|
||||
if(closed.contains(node)) continue;
|
||||
if(!getTile(node.x, node.y).isWalkable()) continue;
|
||||
if(open.contains(node)) {
|
||||
int newGCost = current.g + (int)(Math.round(10 * Math.sqrt(Math.pow(node.x - current.x, 2) + Math.pow(node.y - current.y, 2))));
|
||||
|
||||
if(node.g > newGCost) {
|
||||
if(closed.contains(node)) {
|
||||
closed.remove(node);
|
||||
}
|
||||
if(!open.contains(node)) {
|
||||
open.add(node);
|
||||
}
|
||||
node.g = newGCost;
|
||||
node.from = current;
|
||||
}
|
||||
} else {
|
||||
open.add(node);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// i guess theres no path?!
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render() {
|
||||
int left = 400 - (WORLD_SIZE * TILE_SIZE / 2);
|
||||
int top = 225 - (WORLD_SIZE * TILE_SIZE / 2);
|
||||
left = 400 - (WORLD_SIZE * TILE_SIZE / 2);
|
||||
top = 225 - (WORLD_SIZE * TILE_SIZE / 2);
|
||||
|
||||
|
||||
Sprite s;
|
||||
Assets.flat.pushColor(Vector4f.one);
|
||||
Drawing.setLayer(0f);
|
||||
for (int i = 0; i < WORLD_SIZE; i++) {
|
||||
for (int j = 0; j < WORLD_SIZE; j++) {
|
||||
s = Assets.defaultTerrain[variation[i][j]];
|
||||
Assets.flat.swapColor(colorVariations[i][j]);
|
||||
Drawing.drawSprite(s, left + i * s.width, top + j * s.height);
|
||||
tiles[i][j].render();
|
||||
}
|
||||
}
|
||||
Assets.flat.popColor();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
package xyz.valnet.hadean.pathfinding;
|
||||
|
||||
public class Node {
|
||||
public int x, y, g, h;
|
||||
public Node from;
|
||||
|
||||
public int getCost() {
|
||||
return g + h;
|
||||
}
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ import java.util.List;
|
|||
|
||||
import xyz.valnet.engine.scenegraph.GameObject;
|
||||
import xyz.valnet.engine.scenegraph.IScene;
|
||||
import xyz.valnet.hadean.gameobjects.Pawn;
|
||||
import xyz.valnet.hadean.gameobjects.Terrain;
|
||||
|
||||
public class GameScene implements IScene {
|
||||
|
|
@ -14,12 +15,20 @@ public class GameScene implements IScene {
|
|||
// private List<IRenderable> renderables = new ArrayList<IRenderable>();
|
||||
|
||||
// specific
|
||||
private GameObject terrain;
|
||||
|
||||
public <T> T get(Class<T> clazz) {
|
||||
for(GameObject obj : objects) {
|
||||
if(clazz.isInstance(obj)) {
|
||||
return clazz.cast(obj);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render() {
|
||||
for(GameObject obj : objects) {
|
||||
((Terrain)obj).render();
|
||||
obj.render();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -32,8 +41,10 @@ public class GameScene implements IScene {
|
|||
|
||||
@Override
|
||||
public void enable() {
|
||||
terrain = new Terrain(this);
|
||||
objects.add(terrain);
|
||||
objects.add(new Terrain(this));
|
||||
for(int i = 0; i < 3; i ++) {
|
||||
objects.add(new Pawn(this));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ public class Assets {
|
|||
public static final Tile9 fireFrame;
|
||||
|
||||
public static final Sprite[] defaultTerrain;
|
||||
public static final Sprite pawn;
|
||||
|
||||
public static final SimpleShader flat;
|
||||
|
||||
|
|
@ -34,6 +35,8 @@ public class Assets {
|
|||
new Sprite(atlas, new Vector4i(32, 64, 8, 8))
|
||||
};
|
||||
|
||||
pawn = new Sprite(atlas, 48, 88, 8, 8);
|
||||
|
||||
Map<Character, Sprite> charset = new HashMap<Character, Sprite>();
|
||||
|
||||
charset.put('A', new Sprite(atlas, new Vector4i( 0, 0, 8, 16)));
|
||||
|
|
|
|||
Loading…
Reference in New Issue