Improve entity spawner functionality and fix some spawner bugs
This commit is contained in:
@@ -1,5 +1,7 @@
|
|||||||
package com.bartlomiejpluta.base.api.camera;
|
package com.bartlomiejpluta.base.api.camera;
|
||||||
|
|
||||||
|
import com.bartlomiejpluta.base.api.context.Context;
|
||||||
|
import com.bartlomiejpluta.base.api.move.Movable;
|
||||||
import com.bartlomiejpluta.base.api.screen.Screen;
|
import com.bartlomiejpluta.base.api.screen.Screen;
|
||||||
import com.bartlomiejpluta.base.internal.object.Placeable;
|
import com.bartlomiejpluta.base.internal.object.Placeable;
|
||||||
import com.bartlomiejpluta.base.internal.render.ShaderManager;
|
import com.bartlomiejpluta.base.internal.render.ShaderManager;
|
||||||
@@ -10,5 +12,7 @@ public interface Camera extends Placeable {
|
|||||||
|
|
||||||
boolean insideFrustum(float x, float y, float radius);
|
boolean insideFrustum(float x, float y, float radius);
|
||||||
|
|
||||||
|
boolean insideFrustum(Context context, float x, float y);
|
||||||
|
|
||||||
void render(Screen screen, ShaderManager shaderManager);
|
void render(Screen screen, ShaderManager shaderManager);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package com.bartlomiejpluta.base.util.world;
|
package com.bartlomiejpluta.base.util.world;
|
||||||
|
|
||||||
|
import com.bartlomiejpluta.base.api.camera.Camera;
|
||||||
|
import com.bartlomiejpluta.base.api.context.Context;
|
||||||
import com.bartlomiejpluta.base.api.entity.Entity;
|
import com.bartlomiejpluta.base.api.entity.Entity;
|
||||||
import com.bartlomiejpluta.base.api.event.Event;
|
import com.bartlomiejpluta.base.api.event.Event;
|
||||||
import com.bartlomiejpluta.base.api.event.EventType;
|
import com.bartlomiejpluta.base.api.event.EventType;
|
||||||
@@ -26,28 +28,30 @@ public class EntitySpawner implements Updatable {
|
|||||||
private final List<Entity> spawnedEntities = new LinkedList<>();
|
private final List<Entity> spawnedEntities = new LinkedList<>();
|
||||||
private final List<Supplier<Entity>> spawners = new ArrayList<>();
|
private final List<Supplier<Entity>> spawners = new ArrayList<>();
|
||||||
private final Vector2ic origin;
|
private final Vector2ic origin;
|
||||||
|
private final Context context;
|
||||||
|
private final Camera camera;
|
||||||
private final GameMap map;
|
private final GameMap map;
|
||||||
private final ObjectLayer layer;
|
private final ObjectLayer layer;
|
||||||
private float maxTime = 10000f;
|
private DiceRoller interval = DiceRoller.of("90d2");
|
||||||
private int range = 4;
|
private int range = 4;
|
||||||
private int spawnChance = 50;
|
private float spawnChance = 50;
|
||||||
private DiceRoller countRoller = DiceRoller.of("1d4");
|
private DiceRoller countRoller = DiceRoller.of("1d4");
|
||||||
private Entity awayEntity;
|
|
||||||
private EventType<? extends Event> entityRemoveEvent;
|
private EventType<? extends Event> entityRemoveEvent;
|
||||||
|
|
||||||
private float accumulator = 10000f;
|
private float accumulator = 10000f;
|
||||||
|
private boolean spawnOutsideViewport = false;
|
||||||
private float threshold;
|
private float threshold;
|
||||||
|
|
||||||
public EntitySpawner(int x, int y, @NonNull GameMap map, @NonNull ObjectLayer layer) {
|
public EntitySpawner(int x, int y, @NonNull Context context, @NonNull GameMap map, @NonNull ObjectLayer layer) {
|
||||||
this.origin = new Vector2i(x, y);
|
this.origin = new Vector2i(x, y);
|
||||||
|
this.context = context;
|
||||||
|
this.camera = context.getCamera();
|
||||||
this.map = map;
|
this.map = map;
|
||||||
this.layer = layer;
|
this.layer = layer;
|
||||||
drawThreshold();
|
drawThreshold();
|
||||||
}
|
}
|
||||||
|
|
||||||
public EntitySpawner maxTime(float maxTime) {
|
public EntitySpawner interval(@NonNull String interval) {
|
||||||
this.maxTime = maxTime;
|
this.interval = DiceRoller.of(interval);
|
||||||
this.accumulator = maxTime;
|
|
||||||
drawThreshold();
|
drawThreshold();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -57,8 +61,8 @@ public class EntitySpawner implements Updatable {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public EntitySpawner spawnChance(int percent) {
|
public EntitySpawner spawnChance(float change) {
|
||||||
this.spawnChance = percent;
|
this.spawnChance = change;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,13 +71,18 @@ public class EntitySpawner implements Updatable {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public EntitySpawner entityAway(@NonNull Entity awayEntity) {
|
public EntitySpawner trackEntities(@NonNull EventType<? extends Event> entityRemoveEvent) {
|
||||||
this.awayEntity = awayEntity;
|
this.entityRemoveEvent = entityRemoveEvent;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public EntitySpawner trackEntities(@NonNull EventType<? extends Event> entityRemoveEvent) {
|
public EntitySpawner spawnOutsideViewport() {
|
||||||
this.entityRemoveEvent = entityRemoveEvent;
|
this.spawnOutsideViewport = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public EntitySpawner spawnOnCreate() {
|
||||||
|
this.threshold = 0;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,15 +92,11 @@ public class EntitySpawner implements Updatable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void drawThreshold() {
|
private void drawThreshold() {
|
||||||
threshold = random.nextFloat(maxTime);
|
threshold = interval.roll() * 1000f;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void update(float dt) {
|
public void update(float dt) {
|
||||||
if (awayEntity != null && awayEntity.manhattanDistance(origin) <= range) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
accumulator += dt * 1000;
|
accumulator += dt * 1000;
|
||||||
if (accumulator >= threshold) {
|
if (accumulator >= threshold) {
|
||||||
spawn();
|
spawn();
|
||||||
@@ -101,36 +106,61 @@ public class EntitySpawner implements Updatable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void spawn() {
|
private void spawn() {
|
||||||
if (random.nextInt(100) < spawnChance) {
|
if (random.nextFloat() > spawnChance) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var count = Math.max(0, countRoller.roll() - spawnedEntities.size());
|
// Spawn multiple entities at the time
|
||||||
|
var roll = countRoller.roll();
|
||||||
|
var count = Math.max(0, roll - spawnedEntities.size());
|
||||||
for (int i = 0; i < count; ++i) {
|
for (int i = 0; i < count; ++i) {
|
||||||
var attempts = 0;
|
var attempts = 0;
|
||||||
var coordinates = new Vector2i();
|
var coordinates = new Vector2i();
|
||||||
|
|
||||||
do {
|
while (true) {
|
||||||
|
// Give up if too many fails during drawing the proper coordinates
|
||||||
|
if (attempts > MAX_REPOSITION_ATTEMPTS) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw the coordinates and make sure they are inside the current map boundaries
|
||||||
coordinates.x = MathUtil.clamp(origin.x() + random.nextInt(2 * range) - range, 0, map.getColumns() - 1);
|
coordinates.x = MathUtil.clamp(origin.x() + random.nextInt(2 * range) - range, 0, map.getColumns() - 1);
|
||||||
coordinates.y = MathUtil.clamp(origin.y() + random.nextInt(2 * range) - range, 0, map.getRows() - 1);
|
coordinates.y = MathUtil.clamp(origin.y() + random.nextInt(2 * range) - range, 0, map.getRows() - 1);
|
||||||
|
|
||||||
|
// If tile is not reachable, draw the coordinates again
|
||||||
if (!layer.isTileReachable(coordinates)) {
|
if (!layer.isTileReachable(coordinates)) {
|
||||||
++attempts;
|
++attempts;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (attempts > MAX_REPOSITION_ATTEMPTS) {
|
// If we want to keep spawning entities outside the camera view
|
||||||
return;
|
// check if the drawn coordinates are inside frustum.
|
||||||
|
// If so, draw it again.
|
||||||
|
if (spawnOutsideViewport && camera.insideFrustum(context, coordinates.x, coordinates.y)) {
|
||||||
|
++attempts;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
} while (Distance.manhattan(origin, coordinates) <= range);
|
// We need also to drop the coordinates that are too far from the spawner origin
|
||||||
|
if (Distance.manhattan(origin, coordinates) > range) {
|
||||||
|
++attempts;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We found the proper coordinates
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw the entity spawner
|
||||||
var spawner = spawners.get(random.nextInt(spawners.size()));
|
var spawner = spawners.get(random.nextInt(spawners.size()));
|
||||||
var entity = spawner.get();
|
|
||||||
|
|
||||||
|
// Create the entity and push it onto the map layer
|
||||||
|
var entity = spawner.get();
|
||||||
entity.setCoordinates(coordinates);
|
entity.setCoordinates(coordinates);
|
||||||
layer.addEntity(entity);
|
layer.addEntity(entity);
|
||||||
|
|
||||||
|
// If we want to keep the number of spawned entities per spawner almost constant
|
||||||
|
// we need to know when the entity should be removed (i.e. it has been killed by player).
|
||||||
if (entityRemoveEvent != null) {
|
if (entityRemoveEvent != null) {
|
||||||
spawnedEntities.add(entity);
|
spawnedEntities.add(entity);
|
||||||
entity.addEventListener(entityRemoveEvent, e -> spawnedEntities.remove(entity));
|
entity.addEventListener(entityRemoveEvent, e -> spawnedEntities.remove(entity));
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.bartlomiejpluta.base.engine.world.camera;
|
package com.bartlomiejpluta.base.engine.world.camera;
|
||||||
|
|
||||||
import com.bartlomiejpluta.base.api.camera.Camera;
|
import com.bartlomiejpluta.base.api.camera.Camera;
|
||||||
|
import com.bartlomiejpluta.base.api.context.Context;
|
||||||
import com.bartlomiejpluta.base.api.screen.Screen;
|
import com.bartlomiejpluta.base.api.screen.Screen;
|
||||||
import com.bartlomiejpluta.base.engine.core.gl.shader.constant.UniformName;
|
import com.bartlomiejpluta.base.engine.core.gl.shader.constant.UniformName;
|
||||||
import com.bartlomiejpluta.base.engine.world.object.Model;
|
import com.bartlomiejpluta.base.engine.world.object.Model;
|
||||||
@@ -25,6 +26,13 @@ public class DefaultCamera extends Model implements Camera {
|
|||||||
return frustum.testSphere(x, y, 0.0f, radius);
|
return frustum.testSphere(x, y, 0.0f, radius);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean insideFrustum(Context context, float x, float y) {
|
||||||
|
var map = context.getMap();
|
||||||
|
var stepSize = map.getStepSize();
|
||||||
|
return insideFrustum(stepSize.x() * x, stepSize.y() * y, stepSize.get(stepSize.maxComponent()));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void render(Screen screen, ShaderManager shaderManager) {
|
public void render(Screen screen, ShaderManager shaderManager) {
|
||||||
// Update matrices
|
// Update matrices
|
||||||
|
|||||||
Reference in New Issue
Block a user