Spawn + shoot asteroids
* Add AsteroidSpawningSystem which adds asteroids to the game at a regular interval up to a set limit. * Add EventFactory for generating entity-specific Collision events * Detect asteroids being shot with BulletAsteroidCollisionEvent * Add DEBUG output to creating entities * New getPointAlongPolygon utils function
This commit is contained in:
parent
e6356e4210
commit
042282646b
@ -3,6 +3,7 @@ package com.me.asteroids;
|
||||
import com.badlogic.gdx.math.Polygon;
|
||||
import com.badlogic.gdx.math.Vector2;
|
||||
import com.me.asteroids.components.AccelerationComponent;
|
||||
import com.me.asteroids.components.AsteroidComponent;
|
||||
import com.me.asteroids.components.BulletComponent;
|
||||
import com.me.asteroids.components.ModelComponent;
|
||||
import com.me.asteroids.components.PlayerComponent;
|
||||
@ -11,8 +12,20 @@ import com.me.asteroids.components.VelocityComponent;
|
||||
import com.me.common.ecs.Engine;
|
||||
import com.me.common.ecs.Entity;
|
||||
|
||||
import static com.me.asteroids.Constants.rand;
|
||||
|
||||
public class EntityFactory {
|
||||
|
||||
private static final Vector2 tmp = new Vector2();
|
||||
|
||||
private static Entity createEntity(Engine engine) {
|
||||
Entity entity = engine.createEntity();
|
||||
if (Constants.DEBUG) {
|
||||
System.out.println("Spawned entity: " + entity);
|
||||
}
|
||||
return entity;
|
||||
}
|
||||
|
||||
public static Entity createPlayer(Engine engine) {
|
||||
PositionComponent position = new PositionComponent();
|
||||
position.position = new Vector2(Constants.HALF_WIDTH, Constants.HALF_HEIGHT);
|
||||
@ -35,7 +48,7 @@ public class EntityFactory {
|
||||
AccelerationComponent accel = new AccelerationComponent();
|
||||
accel.acceleration = new Vector2(0,1f);
|
||||
|
||||
Entity player = engine.createEntity();
|
||||
Entity player = createEntity(engine);
|
||||
player.addComponent(position);
|
||||
player.addComponent(velocity);
|
||||
player.addComponent(model);
|
||||
@ -45,26 +58,30 @@ public class EntityFactory {
|
||||
}
|
||||
|
||||
public static Entity createBullet(Engine engine, Entity player) {
|
||||
PositionComponent position = new PositionComponent();
|
||||
float[] modelVertices = player.getComponent(ModelComponent.class).model.getTransformedVertices();
|
||||
position.position = new Vector2(modelVertices[0], modelVertices[1]);
|
||||
position.rotation = player.getComponent(PositionComponent.class).rotation;
|
||||
float rotation = player.getComponent(PositionComponent.class).rotation;
|
||||
|
||||
Vector2 direction = Utils.setUnitVectorAngle(tmp, rotation);
|
||||
|
||||
VelocityComponent velocity = new VelocityComponent();
|
||||
velocity.velocity = Utils.setUnitVectorAngle(new Vector2(), position.rotation).scl(500);
|
||||
velocity.velocity = new Vector2(direction).scl(500);
|
||||
|
||||
PositionComponent position = new PositionComponent();
|
||||
position.position = new Vector2(modelVertices[0], modelVertices[1]).add(direction.scl(4));
|
||||
position.rotation = rotation;
|
||||
|
||||
ModelComponent model = new ModelComponent();
|
||||
model.model = new Polygon(new float[] {
|
||||
-1f, 2f,
|
||||
1f, 2f,
|
||||
1f, -2f,
|
||||
-1f, -2f,
|
||||
1f, 0f,
|
||||
-1f, 0f,
|
||||
-1f, -4f,
|
||||
1f, -4f,
|
||||
});
|
||||
model.model.setRotation(position.rotation);
|
||||
model.model.setPosition(position.position.x, position.position.x);
|
||||
model.model.setPosition(position.position.x, position.position.y);
|
||||
|
||||
|
||||
Entity bullet = engine.createEntity();
|
||||
Entity bullet = createEntity(engine);
|
||||
bullet.addComponent(position);
|
||||
bullet.addComponent(velocity);
|
||||
bullet.addComponent(model);
|
||||
@ -72,4 +89,27 @@ public class EntityFactory {
|
||||
return bullet;
|
||||
}
|
||||
|
||||
public static Entity createAsteroid(Engine engine) {
|
||||
// Creates an asteroid entity with position and velocity unset - the AsteroidSpawningSystem
|
||||
// is responsible for setting its position and velocity
|
||||
PositionComponent position = new PositionComponent();
|
||||
VelocityComponent velocity = new VelocityComponent();
|
||||
|
||||
ModelComponent model = new ModelComponent();
|
||||
int size = rand.nextInt(30, 60);
|
||||
model.model = new AsteroidFactory()
|
||||
.setVertexCount(rand.nextInt(16, 24))
|
||||
.setSize(size)
|
||||
.setSizeVariation(size * 0.7f)
|
||||
.generate();
|
||||
model.aabb = model.model.getBoundingRectangle();
|
||||
|
||||
Entity asteroid = createEntity(engine);
|
||||
asteroid.addComponent(position);
|
||||
asteroid.addComponent(velocity);
|
||||
asteroid.addComponent(model);
|
||||
asteroid.addComponent(new AsteroidComponent());
|
||||
return asteroid;
|
||||
}
|
||||
|
||||
}
|
||||
|
36
core/src/com/me/asteroids/EventFactory.java
Normal file
36
core/src/com/me/asteroids/EventFactory.java
Normal file
@ -0,0 +1,36 @@
|
||||
package com.me.asteroids;
|
||||
|
||||
import com.me.asteroids.components.AsteroidComponent;
|
||||
import com.me.asteroids.components.BulletComponent;
|
||||
import com.me.asteroids.components.PlayerComponent;
|
||||
import com.me.asteroids.events.BulletAsteroidCollisionEvent;
|
||||
import com.me.asteroids.events.CollisionEvent;
|
||||
import com.me.asteroids.events.PlayerASteroidCollisionEvent;
|
||||
import com.me.common.ecs.Entity;
|
||||
|
||||
public class EventFactory {
|
||||
|
||||
public static CollisionEvent getNewCollisionEvent(Entity a, Entity b) {
|
||||
boolean isEntityAPlayer = a.hasComponent(PlayerComponent.class);
|
||||
boolean isEntityBPlayer = b.hasComponent(PlayerComponent.class);
|
||||
boolean isEntityABullet = !isEntityAPlayer && a.hasComponent(BulletComponent.class);
|
||||
boolean isEntityBBullet = !isEntityBPlayer && b.hasComponent(BulletComponent.class);
|
||||
boolean isEntityAAsteroid = !isEntityAPlayer && !isEntityABullet && a.hasComponent(AsteroidComponent.class);
|
||||
boolean isEntityBAsteroid = !isEntityBPlayer && !isEntityBBullet && b.hasComponent(AsteroidComponent.class);
|
||||
|
||||
if (isEntityAAsteroid || isEntityBAsteroid) {
|
||||
if (isEntityAPlayer && isEntityBAsteroid) {
|
||||
return new PlayerASteroidCollisionEvent(a, b);
|
||||
} else if (isEntityBPlayer && isEntityAAsteroid) {
|
||||
return new PlayerASteroidCollisionEvent(b, a);
|
||||
} else if (isEntityABullet && isEntityBAsteroid) {
|
||||
return new BulletAsteroidCollisionEvent(a, b);
|
||||
} else if (isEntityBBullet && isEntityAAsteroid) {
|
||||
return new BulletAsteroidCollisionEvent(b, a);
|
||||
}
|
||||
}
|
||||
|
||||
return new CollisionEvent(a, b);
|
||||
}
|
||||
|
||||
}
|
@ -7,6 +7,8 @@ import java.lang.Math;
|
||||
|
||||
public final class Utils {
|
||||
|
||||
private static final Vector2 tmp = new Vector2();
|
||||
|
||||
public static float rotate(float rotation, float degrees) {
|
||||
rotation += degrees;
|
||||
if (rotation < 0) {
|
||||
@ -25,4 +27,21 @@ public final class Utils {
|
||||
return vector.set((float) Math.cos(radians), (float) Math.sin(radians));
|
||||
}
|
||||
|
||||
public static Vector2 getPointAlongPolygon(Vector2[] vertices, float distanceAlongPerimeter) {
|
||||
for (int i = 0, n = vertices.length; i < n; i++) {
|
||||
Vector2 a = vertices[i];
|
||||
Vector2 b = vertices[i + 1 == n ? 0 : i + 1];
|
||||
tmp.set(a).sub(b);
|
||||
float len = tmp.len();
|
||||
|
||||
if (len < distanceAlongPerimeter) {
|
||||
distanceAlongPerimeter -= len;
|
||||
continue;
|
||||
}
|
||||
|
||||
return a.add(tmp.set(tmp.x / len, tmp.y / len).scl(distanceAlongPerimeter));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,7 @@
|
||||
package com.me.asteroids.components;
|
||||
|
||||
import com.me.common.ecs.Component;
|
||||
|
||||
public class AsteroidComponent implements Component {
|
||||
// TODO: See PlayerComponent's TODO
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package com.me.asteroids.events;
|
||||
|
||||
import com.me.common.ecs.Entity;
|
||||
|
||||
public class BulletAsteroidCollisionEvent extends CollisionEvent {
|
||||
|
||||
public BulletAsteroidCollisionEvent(Entity bullet, Entity asteroid) {
|
||||
super(bullet, asteroid);
|
||||
}
|
||||
|
||||
public Entity getBullet() {
|
||||
return entityA;
|
||||
}
|
||||
|
||||
public Entity getAsteroid() {
|
||||
return entityB;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package com.me.asteroids.events;
|
||||
|
||||
import com.me.common.ecs.Entity;
|
||||
|
||||
public class PlayerASteroidCollisionEvent extends CollisionEvent {
|
||||
|
||||
public PlayerASteroidCollisionEvent(Entity player, Entity asteroid) {
|
||||
super(player, asteroid);
|
||||
}
|
||||
|
||||
public Entity getPlayer() {
|
||||
return entityA;
|
||||
}
|
||||
|
||||
public Entity getAsteroid() {
|
||||
return entityB;
|
||||
}
|
||||
|
||||
}
|
@ -4,22 +4,28 @@ import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.graphics.Color;
|
||||
import com.badlogic.gdx.graphics.g2d.BitmapFont;
|
||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
|
||||
import com.badlogic.gdx.math.Polygon;
|
||||
import com.badlogic.gdx.math.Vector2;
|
||||
import com.me.asteroids.Constants;
|
||||
import com.me.asteroids.EntityFactory;
|
||||
import com.me.asteroids.Graphics;
|
||||
import com.me.asteroids.components.AccelerationComponent;
|
||||
import com.me.asteroids.components.AsteroidComponent;
|
||||
import com.me.asteroids.components.BulletComponent;
|
||||
import com.me.asteroids.components.ModelComponent;
|
||||
import com.me.asteroids.components.PlayerComponent;
|
||||
import com.me.asteroids.components.PositionComponent;
|
||||
import com.me.asteroids.components.VelocityComponent;
|
||||
import com.me.asteroids.events.BulletAsteroidCollisionEvent;
|
||||
import com.me.asteroids.events.ScreenWrapEvent;
|
||||
import com.me.asteroids.systems.AsteroidSpawningSystem;
|
||||
import com.me.asteroids.systems.CollisionSystem;
|
||||
import com.me.asteroids.systems.ModelRenderSystem;
|
||||
import com.me.asteroids.systems.MovementSystem;
|
||||
import com.me.asteroids.systems.PlayerInputSystem;
|
||||
import com.me.asteroids.systems.ScreenWrapSystem;
|
||||
import com.me.common.Screen;
|
||||
import com.me.common.ecs.ComponentMapper;
|
||||
import com.me.common.ecs.Engine;
|
||||
import com.me.common.ecs.Entity;
|
||||
import com.me.common.ecs.event.EventHandler;
|
||||
@ -48,18 +54,20 @@ public class GameScreen extends Screen implements Listener {
|
||||
|
||||
engine.registerComponentClass(PlayerComponent.class);
|
||||
engine.registerComponentClass(BulletComponent.class);
|
||||
engine.registerComponentClass(AsteroidComponent.class);
|
||||
engine.registerComponentClass(PositionComponent.class);
|
||||
engine.registerComponentClass(VelocityComponent.class);
|
||||
engine.registerComponentClass(AccelerationComponent.class);
|
||||
engine.registerComponentClass(ModelComponent.class);
|
||||
|
||||
engine.registerSystem(new PlayerInputSystem(engine));
|
||||
engine.registerSystem(new AsteroidSpawningSystem(engine));
|
||||
engine.registerSystem(new MovementSystem(engine));
|
||||
engine.registerSystem(new CollisionSystem(engine));
|
||||
engine.registerSystem(new ScreenWrapSystem(engine));
|
||||
engine.registerSystem(new ModelRenderSystem(engine, graphics));
|
||||
|
||||
engine.registerListener(this);
|
||||
engine.registerListener(this.new EventListener(engine));
|
||||
|
||||
engine.ready();
|
||||
|
||||
@ -84,14 +92,41 @@ public class GameScreen extends Screen implements Listener {
|
||||
|
||||
}
|
||||
|
||||
private class EventListener implements Listener {
|
||||
|
||||
ComponentMapper<PositionComponent> positionMapper;
|
||||
ComponentMapper<ModelComponent> modelMapper;
|
||||
ComponentMapper<BulletComponent> bulletMapper;
|
||||
|
||||
EventListener(Engine engine) {
|
||||
this.positionMapper = engine.getComponentMapper(PositionComponent.class);
|
||||
this.modelMapper = engine.getComponentMapper(ModelComponent.class);
|
||||
this.bulletMapper = engine.getComponentMapper(BulletComponent.class);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onScreenWrap(ScreenWrapEvent event) {
|
||||
if (event.entity.hasComponent(BulletComponent.class)) {
|
||||
if (bulletMapper.has(event.entity)) {
|
||||
// Remove bullets when they leave the screen
|
||||
event.setCancelled(true);
|
||||
//event.setCancelled(true);
|
||||
event.entity.remove();
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onBulletAsteroidCollision(BulletAsteroidCollisionEvent event) {
|
||||
Vector2 bulletPosition = positionMapper.get(event.getBullet()).position;
|
||||
Polygon asteroidModel = modelMapper.get(event.getAsteroid()).model;
|
||||
|
||||
if (asteroidModel.contains(bulletPosition)) {
|
||||
// AABBs intersect but let's only consider it a hit if the bullet's position
|
||||
// is actually inside the asteroid
|
||||
event.getBullet().remove();
|
||||
event.getAsteroid().remove();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,88 @@
|
||||
package com.me.asteroids.systems;
|
||||
|
||||
import com.badlogic.gdx.math.Vector2;
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
import com.me.asteroids.Constants;
|
||||
import com.me.asteroids.EntityFactory;
|
||||
import com.me.asteroids.Utils;
|
||||
import com.me.asteroids.components.AsteroidComponent;
|
||||
import com.me.asteroids.components.ModelComponent;
|
||||
import com.me.asteroids.components.PositionComponent;
|
||||
import com.me.asteroids.components.VelocityComponent;
|
||||
import com.me.common.ecs.BaseSystem;
|
||||
import com.me.common.ecs.ComponentMapper;
|
||||
import com.me.common.ecs.Engine;
|
||||
import com.me.common.ecs.Entity;
|
||||
|
||||
import static com.me.asteroids.Constants.rand;
|
||||
|
||||
public class AsteroidSpawningSystem extends BaseSystem {
|
||||
|
||||
private ComponentMapper<AsteroidComponent> asteroidMapper;
|
||||
private ComponentMapper<PositionComponent> positionMapper;
|
||||
private ComponentMapper<VelocityComponent> velocityMapper;
|
||||
private ComponentMapper<ModelComponent> modelMapper;
|
||||
|
||||
private float asteroidSpawnDelay = 0f;
|
||||
|
||||
public AsteroidSpawningSystem(Engine engine) {
|
||||
super(engine);
|
||||
asteroidMapper = engine.getComponentMapper(AsteroidComponent.class);
|
||||
positionMapper = engine.getComponentMapper(PositionComponent.class);
|
||||
velocityMapper = engine.getComponentMapper(VelocityComponent.class);
|
||||
modelMapper = engine.getComponentMapper(ModelComponent.class);
|
||||
}
|
||||
|
||||
private Vector2 getRandomSpawnLocation(float asteroidWidth, float asteroidHeight) {
|
||||
float halfWidth = asteroidWidth / 2;
|
||||
float halfHeight = asteroidHeight / 2;
|
||||
|
||||
Vector2[] spawnPerimeter = new Vector2[]{
|
||||
new Vector2(-halfWidth, -halfHeight),
|
||||
new Vector2(-halfWidth, Constants.HEIGHT + halfHeight),
|
||||
new Vector2(Constants.WIDTH + halfWidth, Constants.HEIGHT + halfHeight),
|
||||
new Vector2(Constants.WIDTH + halfWidth, -halfHeight),
|
||||
};
|
||||
|
||||
float perimeterLength = (Constants.WIDTH + Constants.HEIGHT + asteroidWidth + asteroidHeight) * 2;
|
||||
float positionAlongPerimeter = rand.nextFloat(0, perimeterLength);
|
||||
|
||||
return Utils.getPointAlongPolygon(spawnPerimeter, positionAlongPerimeter);
|
||||
}
|
||||
|
||||
private void spawnAsteroid() {
|
||||
Entity asteroid = EntityFactory.createAsteroid(engine);
|
||||
|
||||
ModelComponent model = modelMapper.get(asteroid);
|
||||
Vector2 position
|
||||
= positionMapper.get(asteroid).position
|
||||
= getRandomSpawnLocation(model.aabb.getWidth(), model.aabb.getHeight());
|
||||
model.model.setPosition(position.x, position.y);
|
||||
model.aabb = model.model.getBoundingRectangle();
|
||||
|
||||
VelocityComponent velocityComponent = velocityMapper.get(asteroid);
|
||||
velocityComponent.velocity = new Vector2().setToRandomDirection().scl(rand.nextFloat(125, 175));
|
||||
|
||||
asteroid.activate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process(float dt) {
|
||||
Array<Entity> entities = engine.getEntities();
|
||||
int asteroidCount = 0;
|
||||
for (Entity entity : entities) {
|
||||
// It's rather inefficient to have to check our entire entity list every frame
|
||||
// to count how many entities have a specific component. Maybe we should keep a count of
|
||||
// how many entites a given component?
|
||||
if (asteroidMapper.has(entity)) {
|
||||
asteroidCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (asteroidCount++ < 6 && (asteroidSpawnDelay -= dt) <= 0) {
|
||||
spawnAsteroid();
|
||||
asteroidSpawnDelay = Constants.ASTEROID_SPAWN_DELAY;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
package com.me.asteroids.systems;
|
||||
|
||||
import com.badlogic.gdx.math.Rectangle;
|
||||
import com.me.asteroids.EventFactory;
|
||||
import com.me.asteroids.components.ModelComponent;
|
||||
import com.me.asteroids.events.CollisionEvent;
|
||||
import com.me.common.ecs.ComponentMapper;
|
||||
@ -29,8 +30,7 @@ public class CollisionSystem extends EntitySystem {
|
||||
|
||||
Rectangle aabbB = modelMapper.get(entityB).aabb;
|
||||
if (aabbA.overlaps(aabbB)) {
|
||||
CollisionEvent event = new CollisionEvent(entityA, entityB);
|
||||
engine.callEvent(event);
|
||||
engine.callEvent(EventFactory.getNewCollisionEvent(entityA, entityB));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user