Add Model interface to be used for ModelComponent instead of Polygon

Allows shapes other than Polygons to be used a Model
Add PolygonModel and LineModel implementations
This commit is contained in:
Matt Low 2020-01-28 17:58:12 +04:00
parent b9e63dea26
commit 3918f8c1f5
13 changed files with 272 additions and 44 deletions

View File

@ -1,7 +1,6 @@
package com.me.asteroids;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Polygon;
import com.badlogic.gdx.math.Vector2;
import static com.me.asteroids.Constants.rand;
@ -89,7 +88,7 @@ public final class AsteroidFactory {
return size;
}
public Polygon generate() {
public float[] generate() {
validate();
float angleStep = MathUtils.PI2 / vertexCount;
@ -114,7 +113,7 @@ public final class AsteroidFactory {
dir.rotateRad(angleStep);
}
return new Polygon(vertices);
return vertices;
}
}

View File

@ -1,12 +1,13 @@
package com.me.asteroids;
import com.badlogic.gdx.math.Polygon;
import com.badlogic.gdx.graphics.Color;
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;
import com.me.asteroids.components.model.PolygonModel;
import com.me.asteroids.components.PositionComponent;
import com.me.asteroids.components.VelocityComponent;
import com.me.common.ecs.Engine;
@ -36,14 +37,15 @@ public class EntityFactory {
velocity.maxVelocity = 400f;
ModelComponent model = new ModelComponent();
model.model = new Polygon(new float[] {
model.model = new PolygonModel(Color.WHITE);
model.model.setVertices(new float[] {
0f, 4f, // tip
-2.5f, -4f, // bottom left
-1f, -2.5f, // indent
1f, -2.5f, // indent
2.5f, -4f, // bottom right
});
model.model.scale(5);
model.model.setScale(5);
AccelerationComponent accel = new AccelerationComponent();
accel.acceleration = new Vector2(0,1f);
@ -58,7 +60,7 @@ public class EntityFactory {
}
public static Entity createBullet(Engine engine, Entity player) {
float[] modelVertices = player.getComponent(ModelComponent.class).model.getTransformedVertices();
float[] modelVertices = player.getComponent(ModelComponent.class).model.getVertices();
float rotation = player.getComponent(PositionComponent.class).rotation;
Vector2 direction = Utils.setUnitVectorAngle(tmp, rotation);
@ -71,14 +73,15 @@ public class EntityFactory {
position.rotation = rotation;
ModelComponent model = new ModelComponent();
model.model = new Polygon(new float[] {
model.model = new PolygonModel(Color.YELLOW);
model.model.setVertices(new float[] {
1f, 0f,
-1f, 0f,
-1f, -4f,
1f, -4f,
});
model.model.setRotation(position.rotation);
model.model.setPosition(position.position.x, position.position.y);
model.model.setPosition(position.position);
Entity bullet = createEntity(engine);
@ -96,13 +99,14 @@ public class EntityFactory {
VelocityComponent velocity = new VelocityComponent();
ModelComponent model = new ModelComponent();
model.model = new PolygonModel(Color.WHITE);
int size = rand.nextInt(30, 60);
model.model = new AsteroidFactory()
model.model.setVertices(new AsteroidFactory()
.setVertexCount(rand.nextInt(16, 24))
.setSize(size)
.setSizeVariation(size * 0.7f)
.generate();
model.aabb = model.model.getBoundingRectangle();
.sizeRelativeToLast()
.generate());
Entity asteroid = createEntity(engine);
asteroid.addComponent(position);

View File

@ -3,8 +3,6 @@ package com.me.asteroids;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Vector2;
import java.lang.Math;
public final class Utils {
private static final Vector2 tmp = new Vector2();

View File

@ -1,11 +1,10 @@
package com.me.asteroids.components;
import com.badlogic.gdx.math.Polygon;
import com.badlogic.gdx.math.Rectangle;
import com.me.asteroids.components.model.Model;
import com.me.common.ecs.Component;
public class ModelComponent implements Component {
public Polygon model;
public Rectangle aabb;
public Model model;
}

View File

@ -0,0 +1,110 @@
package com.me.asteroids.components.model;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.math.Polyline;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector2;
public class LineModel implements Model {
private Vector2 tmp = new Vector2();
private Color color;
private Polyline model;
private Rectangle aabb;
private boolean dirty = true;
public LineModel(Color color) {
this.color = color;
this.model = new Polyline();
this.aabb = new Rectangle();
}
@Override
public Color getColor() {
return color;
}
@Override
public void setVertices(float[] vertices) {
model.setVertices(vertices);
}
@Override
public float[] getVertices() {
return model.getTransformedVertices();
}
@Override
public void setPosition(Vector2 position) {
model.setPosition(position.x, position.y);
dirty = true;
}
@Override
public Vector2 getPosition() {
return tmp.set(model.getX(), model.getY());
}
@Override
public void setRotation(float degrees) {
model.setRotation(degrees);
dirty = true;
}
@Override
public float getRotation() {
return model.getRotation();
}
@Override
public void setScale(float scale) {
model.setScale(scale, scale);
dirty = true;
}
@Override
public float getScale() {
return model.getScaleX();
}
@Override
public Rectangle getBoundingBox() {
if (dirty) {
updateBoundingBox();
dirty = false;
}
return aabb;
}
@Override
public void render(ShapeRenderer renderer) {
renderer.polyline(getVertices());
}
private void updateBoundingBox() {
float[] vertices = getVertices();
float minX = vertices[0];
float minY = vertices[1];
float maxX = vertices[0];
float maxY = vertices[1];
for (int i = 0, n = vertices.length; i < n; i += 2) {
float x = vertices[i];
float y = vertices[i+1];
minX = x < minX ? x : minX;
maxX = x > maxX ? x : maxX;
minY = y < minY ? y : minY;
maxY = y > maxY ? y : maxY;
}
aabb.x = minX;
aabb.y = minY;
aabb.width = maxX - minX;
aabb.height = maxY - minY;
}
}

View File

@ -0,0 +1,32 @@
package com.me.asteroids.components.model;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector2;
public interface Model {
Color getColor();
void setVertices(float[] vertices);
float[] getVertices();
void setPosition(Vector2 position);
Vector2 getPosition();
void setRotation(float degrees);
float getRotation();
void setScale(float scale);
float getScale();
Rectangle getBoundingBox();
void render(ShapeRenderer render);
}

View File

@ -0,0 +1,91 @@
package com.me.asteroids.components.model;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.math.Polygon;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector2;
public class PolygonModel implements Model {
private Vector2 tmp = new Vector2();
private Color color;
private Polygon model;
private Rectangle aabb;
private boolean dirty = true;
public PolygonModel(Color color) {
this.color = color;
this.model = new Polygon();
}
@Override
public Color getColor() {
return color;
}
@Override
public void setVertices(float[] vertices) {
model.setVertices(vertices);
}
@Override
public float[] getVertices() {
return model.getTransformedVertices();
}
@Override
public void setPosition(Vector2 position) {
model.setPosition(position.x, position.y);
dirty = true;
}
@Override
public Vector2 getPosition() {
return tmp.set(model.getX(), model.getY());
}
@Override
public void setRotation(float degrees) {
model.setRotation(degrees);
dirty = true;
}
@Override
public float getRotation() {
return model.getRotation();
}
@Override
public void setScale(float scale) {
model.setScale(scale, scale);
dirty = true;
}
@Override
public float getScale() {
return model.getScaleX();
}
@Override
public Rectangle getBoundingBox() {
if (dirty) {
aabb = model.getBoundingRectangle();
dirty = false;
}
return aabb;
}
@Override
public void render(ShapeRenderer renderer) {
renderer.polygon(getVertices());
}
public boolean contains(Vector2 point) {
return model.contains(point);
}
}

View File

@ -4,7 +4,6 @@ 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;
@ -14,6 +13,7 @@ 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.model.PolygonModel;
import com.me.asteroids.components.PositionComponent;
import com.me.asteroids.components.VelocityComponent;
import com.me.asteroids.events.BulletAsteroidCollisionEvent;
@ -116,9 +116,9 @@ public class GameScreen extends Screen implements Listener {
@EventHandler
public void onBulletAsteroidCollision(BulletAsteroidCollisionEvent event) {
Vector2 bulletPosition = positionMapper.get(event.getBullet()).position;
Polygon asteroidModel = modelMapper.get(event.getAsteroid()).model;
if (asteroidModel.contains(bulletPosition)) {
PolygonModel model = (PolygonModel) modelMapper.get(event.getAsteroid()).model;
if (model.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();

View File

@ -1,11 +1,13 @@
package com.me.asteroids.systems;
import com.badlogic.gdx.math.Rectangle;
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.model.Model;
import com.me.asteroids.components.ModelComponent;
import com.me.asteroids.components.PositionComponent;
import com.me.asteroids.components.VelocityComponent;
@ -53,12 +55,13 @@ public class AsteroidSpawningSystem extends BaseSystem {
private void spawnAsteroid() {
Entity asteroid = EntityFactory.createAsteroid(engine);
ModelComponent model = modelMapper.get(asteroid);
Model model = modelMapper.get(asteroid).model;
Rectangle aabb = model.getBoundingBox();
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();
= getRandomSpawnLocation(aabb.getWidth(), aabb.getHeight());
model.setPosition(position);
VelocityComponent velocityComponent = velocityMapper.get(asteroid);
velocityComponent.velocity = new Vector2().setToRandomDirection().scl(rand.nextFloat(125, 175));

View File

@ -3,7 +3,6 @@ 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;
import com.me.common.ecs.Engine;
import com.me.common.ecs.Entity;
@ -23,12 +22,12 @@ public class CollisionSystem extends EntitySystem {
Entity[] entities = getEntities().items;
for (int i = 0, n = getEntities().size; i < n-1; i++) {
Entity entityA = entities[i];
Rectangle aabbA = modelMapper.get(entityA).aabb;
Rectangle aabbA = modelMapper.get(entityA).model.getBoundingBox();
for (int j = i + 1; j < n; j++) {
Entity entityB = entities[j];
Rectangle aabbB = modelMapper.get(entityB).aabb;
Rectangle aabbB = modelMapper.get(entityB).model.getBoundingBox();
if (aabbA.overlaps(aabbB)) {
engine.callEvent(EventFactory.getNewCollisionEvent(entityA, entityB));
}

View File

@ -1,9 +1,8 @@
package com.me.asteroids.systems;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.math.Polygon;
import com.me.asteroids.Graphics;
import com.me.asteroids.components.model.Model;
import com.me.asteroids.components.ModelComponent;
import com.me.common.ecs.ComponentMapper;
import com.me.common.ecs.Engine;
@ -24,16 +23,15 @@ public class ModelRenderSystem extends EntitySystem {
@Override
public void preProcess() {
renderer.setColor(Color.WHITE);
renderer.begin(ShapeRenderer.ShapeType.Line);
}
@Override
public void processEntity(Entity entity, float dt) {
ModelComponent modelComponent = modelMapper.get(entity);
Model model = modelMapper.get(entity).model;
Polygon model = modelComponent.model;
renderer.polygon(model.getTransformedVertices());
renderer.setColor(model.getColor());
model.render(renderer);
}
@Override

View File

@ -1,8 +1,8 @@
package com.me.asteroids.systems;
import com.badlogic.gdx.math.Polygon;
import com.badlogic.gdx.math.Vector2;
import com.me.asteroids.components.AccelerationComponent;
import com.me.asteroids.components.model.Model;
import com.me.asteroids.components.ModelComponent;
import com.me.asteroids.components.PositionComponent;
import com.me.asteroids.components.VelocityComponent;
@ -50,10 +50,9 @@ public class MovementSystem extends EntitySystem {
ModelComponent modelComponent = modelMapper.get(entity);
if (modelComponent != null) {
Polygon model = modelComponent.model;
model.setPosition(position.x, position.y);
Model model = modelComponent.model;
model.setPosition(position);
model.setRotation(positionComponent.rotation - 90);
modelComponent.aabb = model.getBoundingRectangle();
}
}

View File

@ -33,11 +33,8 @@ public class ScreenWrapSystem extends EntitySystem {
@Override
public void processEntity(Entity entity, float dt) {
PositionComponent positionComponent = positionMapper.get(entity);
ModelComponent modelComponent = modelMapper.get(entity);
Vector2 position = positionComponent.position;
Rectangle aabb = modelComponent.aabb;
Vector2 position = positionMapper.get(entity).position;
Rectangle aabb = modelMapper.get(entity).model.getBoundingBox();
// Check top/bottom edges
float minY = aabb.y;
@ -57,7 +54,6 @@ public class ScreenWrapSystem extends EntitySystem {
updatePosition(entity, position, Constants.WIDTH + (position.x - minX) + maxX, position.y);
} else if (minX > Constants.WIDTH) {
updatePosition(entity, position, (position.x - maxX) + (minX - Constants.WIDTH), position.y);
}
}