Created
March 17, 2015 12:41
-
-
Save junkdog/719dbc240578eb500059 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.github.junkdog.shamans.system.spatial; | |
import static com.badlogic.gdx.math.MathUtils.clamp; | |
import static com.github.junkdog.shamans.Constants.HEIGHT; | |
import static com.github.junkdog.shamans.Constants.WIDTH; | |
import static com.github.junkdog.shamans.system.render.NearestNeighborsRenderSystem.FIND_NEIGHBORS; | |
import static java.lang.Math.min; | |
import lombok.Getter; | |
import lombok.Setter; | |
import com.artemis.Aspect; | |
import com.artemis.ComponentMapper; | |
import com.artemis.Entity; | |
import com.artemis.annotations.Profile; | |
import com.artemis.annotations.Wire; | |
import com.artemis.systems.EntityProcessingSystem; | |
import com.badlogic.gdx.graphics.OrthographicCamera; | |
import com.badlogic.gdx.utils.Array; | |
import com.badlogic.gdx.utils.IntArray; | |
import com.badlogic.gdx.utils.IntIntMap; | |
import com.github.junkdog.shamans.component.Owner; | |
import com.github.junkdog.shamans.component.Position; | |
import com.github.junkdog.shamans.component.Repulsor; | |
import com.github.junkdog.shamans.profile.Profiler; | |
import com.github.junkdog.shamans.util.CappedLinkedList; | |
@Profile(using=Profiler.class, enabled=Profiler.PERF_PROFILE) | |
@Wire | |
public class GridSystem extends EntityProcessingSystem { | |
@Getter private int cols; | |
@Getter private int rows; | |
@Getter private float cellSize; | |
@Setter private int player1Id; | |
@Setter private int player2Id; | |
private final PlayerGrid player1Grid; | |
private final PlayerGrid player2Grid; | |
private ComponentMapper<Position> positionMapper; | |
private ComponentMapper<Owner> ownerMapper; | |
@SuppressWarnings("unchecked") | |
public GridSystem(OrthographicCamera camera, float gridSize) { | |
super(Aspect.getAspectForAll(Position.class, Owner.class).exclude(Repulsor.class)); | |
this.cellSize = gridSize; | |
rows = (int)(HEIGHT / gridSize); | |
cols = (int)(WIDTH / gridSize); | |
player1Grid = new PlayerGrid(rows, cols); | |
player2Grid = new PlayerGrid(rows, cols); | |
} | |
@Override | |
protected void inserted(Entity e) { | |
playerGrid(e).inserted(e); | |
} | |
@Override | |
protected void removed(Entity e) { | |
playerGrid(e).removed(e); | |
} | |
@Override | |
protected void process(Entity e) { | |
playerGrid(e).process(e); | |
} | |
public PlayerGrid playerGrid(Entity e) { | |
int id = ownerMapper.has(e) ? ownerMapper.get(e).playerId : e.getId(); | |
if (id == player1Id) return player1Grid; | |
if (id == player2Id) return player2Grid; | |
throw new RuntimeException("Owner does not match player ids: " + id); | |
} | |
public class PlayerGrid { | |
private final IntArray[] grid; | |
private final IntIntMap entityToGridMapping; | |
private final CappedLinkedList<Entity> foundEntities; | |
public PlayerGrid(int rows, int cols) { | |
foundEntities = new CappedLinkedList<Entity>(FIND_NEIGHBORS); | |
entityToGridMapping = new IntIntMap(); | |
grid = new IntArray[rows * cols]; | |
for (int i = 0; grid.length > i; i++) { | |
grid[i] = new IntArray(); | |
} | |
} | |
protected void process(Entity e) { | |
Position pos = positionMapper.get(e); | |
int oldGrid = entityToGridMapping.get(e.getId(), -1); | |
assert oldGrid != -1; | |
int newGrid = grid(pos); | |
if (newGrid != oldGrid) { | |
entityToGridMapping.put(e.getId(), newGrid); | |
boolean removed = grid[oldGrid].removeValue(e.getId()); | |
assert removed; | |
grid[newGrid].add(e.getId()); | |
} | |
} | |
protected void removed(Entity e) { | |
if (entityToGridMapping.containsKey(e.getId())) { | |
int removed = entityToGridMapping.remove(e.getId(), -1); | |
assert removed != -1; | |
grid[grid(positionMapper.get(e))].removeValue(e.getId()); | |
} else { | |
throw new RuntimeException("Tried removing entity: " + e); | |
} | |
} | |
protected void inserted(Entity e) { | |
Position pos = positionMapper.get(e); | |
grid[grid(pos)].add(e.getId()); | |
entityToGridMapping.put(e.getId(), grid(pos)); | |
} | |
public void findNearest(Array<Entity> fillArray, Entity source, int count) { | |
foundEntities.clear(); | |
fillArray.clear(); | |
// + 1 = self reference | |
count = min((count + 1), getActives().size()); | |
if (getActives().size() < count) { | |
count = getActives().size(); | |
} | |
foundEntities.setOrigin(positionMapper.get(source).x, positionMapper.get(source).y); | |
foundEntities.setMaxSize(count); | |
Position pos = positionMapper.get(source); | |
int y = row(pos); | |
int x = col(pos); | |
addEntitiesFromCell(x, y); | |
assert foundEntities.size > 0; | |
if (count < foundEntities.size) { | |
addToFillArray(fillArray, source, (count - 1)); | |
return; | |
} | |
int perimeter = 1; | |
while (count > foundEntities.size || perimeter == 1) { | |
findInPerimeter(x, y, perimeter++); | |
} | |
addToFillArray(fillArray, source, (count - 1)); | |
} | |
public IntArray entitiesInGrid(int x, int y) { | |
return grid[y * cols + x]; | |
} | |
private int row(Position p) { | |
int row = (int)(p.y / cellSize); | |
return clamp(row, 0, rows - 1); | |
} | |
private int col(Position p) { | |
int col = (int)(p.x / cellSize); | |
return clamp(col, 0, cols - 1); | |
} | |
private int grid(Position p) { | |
return row(p) * cols + col(p); | |
} | |
private void addEntitiesFromCell(int x, int y) { | |
x = clamp(x, 0, cols - 1); | |
y = clamp(y, 0, rows - 1); | |
IntArray g = grid[(y * cols) + x]; | |
for (int i = 0; g.size > i; i++) { | |
Entity e = world.getEntity(g.get(i)); | |
foundEntities.offer(e, positionMapper.get(e)); | |
} | |
} | |
private void findInPerimeter(int col, int row, int perimeter) { | |
int x = col - perimeter; | |
int y = row - perimeter; | |
for (int i = 0; (perimeter * 2) >= i; i++) { | |
addEntitiesFromCell(x + i, y); | |
} | |
for (int i = 1; (perimeter * 2) > i; i++) { | |
addEntitiesFromCell(col - perimeter, y + i); | |
addEntitiesFromCell(col + perimeter, y + i); | |
} | |
y = row + perimeter; | |
for (int i = 0; (perimeter * 2) >= i; i++) { | |
addEntitiesFromCell(x + i, y); | |
} | |
} | |
private void addToFillArray(Array<Entity> fillArray, Entity source, int count) { | |
foundEntities.iter(); foundEntities.next(); foundEntities.remove(); | |
foundEntities.iter(); | |
for (Entity e; (e = foundEntities.next()) != null; ) { | |
fillArray.add(e); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment