From c207bb1ed829fce2f70f6eef8663406fe520e218 Mon Sep 17 00:00:00 2001 From: Isaiah Blanks Date: Thu, 1 Apr 2021 22:02:09 -0400 Subject: [PATCH 1/3] Refactored Rubble to use ECS --- .../rubble/components/RubbleMesh.java | 24 ++++ .../systems/RubbleBodyCreationSystem.java | 112 +++++++++++++++++ .../rubble/systems/RubbleCreationSystem.java | 114 ++++++++++++++++-- 3 files changed, 243 insertions(+), 7 deletions(-) create mode 100644 engine/src/main/java/org/destinationsol/rubble/components/RubbleMesh.java create mode 100644 engine/src/main/java/org/destinationsol/rubble/systems/RubbleBodyCreationSystem.java diff --git a/engine/src/main/java/org/destinationsol/rubble/components/RubbleMesh.java b/engine/src/main/java/org/destinationsol/rubble/components/RubbleMesh.java new file mode 100644 index 000000000..f3ac81c88 --- /dev/null +++ b/engine/src/main/java/org/destinationsol/rubble/components/RubbleMesh.java @@ -0,0 +1,24 @@ +/* + * Copyright 2020 The Terasology Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.destinationsol.rubble.components; + +import org.terasology.gestalt.entitysystem.component.EmptyComponent; + +/** + * This class directs the onGenerateBody event to the RubbleBodyCreationSystem + */ +public class RubbleMesh extends EmptyComponent { +} diff --git a/engine/src/main/java/org/destinationsol/rubble/systems/RubbleBodyCreationSystem.java b/engine/src/main/java/org/destinationsol/rubble/systems/RubbleBodyCreationSystem.java new file mode 100644 index 000000000..dce38591c --- /dev/null +++ b/engine/src/main/java/org/destinationsol/rubble/systems/RubbleBodyCreationSystem.java @@ -0,0 +1,112 @@ +/* + * Copyright 2020 The Terasology Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.destinationsol.rubble.systems; + +import com.badlogic.gdx.math.MathUtils; +import com.badlogic.gdx.math.Vector2; +import com.badlogic.gdx.physics.box2d.Body; +import com.badlogic.gdx.physics.box2d.BodyDef; +import com.badlogic.gdx.physics.box2d.FixtureDef; +import com.badlogic.gdx.physics.box2d.World; +import org.destinationsol.Const; +import org.destinationsol.asteroids.components.AsteroidMesh; +import org.destinationsol.body.events.BodyCreatedEvent; +import org.destinationsol.body.events.GenerateBodyEvent; +import org.destinationsol.common.In; +import org.destinationsol.entitysystem.EntitySystemManager; +import org.destinationsol.entitysystem.EventReceiver; +import org.destinationsol.game.CollisionMeshLoader; +import org.destinationsol.game.UpdateAwareSystem; +import org.destinationsol.location.components.Angle; +import org.destinationsol.location.components.Position; +import org.destinationsol.rendering.RenderableElement; +import org.destinationsol.rendering.components.Renderable; +import org.destinationsol.rubble.components.RubbleMesh; +import org.destinationsol.size.components.Size; +import org.terasology.gestalt.entitysystem.entity.EntityRef; +import org.terasology.gestalt.entitysystem.event.EventResult; +import org.terasology.gestalt.entitysystem.event.ReceiveEvent; + +import java.util.ArrayList; + +/** + * This system creates a {@link Body} for an entity with a {@link RubbleMesh} component. + */ +public class RubbleBodyCreationSystem implements EventReceiver { + + private static final float DENSITY = 3f; + + @In + private EntitySystemManager entitySystemManager; + + @In + private World world; + + private final CollisionMeshLoader collisionMeshLoader = new CollisionMeshLoader("engine:miscCollisionMeshes"); + + @ReceiveEvent(components = {RubbleMesh.class, Size.class, Position.class, Angle.class, Renderable.class}) + public EventResult onGenerateBody(GenerateBodyEvent event, EntityRef entity) { + + float size = entity.getComponent(Size.class).get().size; + Vector2 position = entity.getComponent(Position.class).get().position; + float angle = entity.getComponent(Angle.class).get().getAngle(); + ArrayList renderableElements = entity.getComponent(Renderable.class).get().elements; + + //This creates an entity with a generic Body. The fixtures, which provide the collision meshes, are attached later. + BodyDef bd = new BodyDef(); + bd.type = BodyDef.BodyType.DynamicBody; + bd.angle = angle * MathUtils.degRad; + bd.angularDamping = 0; + bd.position.set(position); + bd.linearDamping = 0; + Body body = world.createBody(bd); + + //This sets a reference to an entity in the Body, so that the entity can be retrieved from the body during collision handling. + body.setUserData(entity); + + //This attaches collision meshes to the Body of an entity, based on its graphics. + for (RenderableElement element : renderableElements) { + FixtureDef fixtureDef = new FixtureDef(); + fixtureDef.density = DENSITY; + fixtureDef.friction = Const.FRICTION; + collisionMeshLoader.attachFixture(body, element.texture.name, fixtureDef, size); + + calculateGraphicsOffset(element); + } + + entitySystemManager.sendEvent(new BodyCreatedEvent(body), entity); + + return EventResult.CONTINUE; + } + + /** + * This calculates the offset of the renderable element from "the origin" (as defined in the JSON that the + * CollisionMeshLoader reads from). + * The origin is where the center of the object should be, which is relevant for physics handling. The + * CollisionMeshLoader creates Fixtures (collision meshes) using that information, so the sprites need to be + * adjusted to overlay the mesh properly. + * LibGDX draws sprites from the bottom left corner. Since the position information is from the center, it + * needs to be adjusted to be at the bottom left of the sprite. To do so, (.5, .5) is subtracted from the origin. + * (The coordinates are scaled to range from zero to one, so (.5, .5) represents the center.) + * The originInformation is the information that was read from the JSON, which is used to calculate the graphics + * offset information. + */ + //TODO separate this method into a separate system once CollisionMeshLoader is modular + private void calculateGraphicsOffset(RenderableElement element) { + Vector2 originInformation = collisionMeshLoader.getOrigin(element.texture.name, 1); + element.graphicsOffset = new Vector2(originInformation.x - .5f, originInformation.y - .5f); + } +} diff --git a/engine/src/main/java/org/destinationsol/rubble/systems/RubbleCreationSystem.java b/engine/src/main/java/org/destinationsol/rubble/systems/RubbleCreationSystem.java index b0567cfd1..8135ec083 100644 --- a/engine/src/main/java/org/destinationsol/rubble/systems/RubbleCreationSystem.java +++ b/engine/src/main/java/org/destinationsol/rubble/systems/RubbleCreationSystem.java @@ -15,17 +15,29 @@ */ package org.destinationsol.rubble.systems; +import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.math.Vector2; +import org.destinationsol.assets.Assets; +import org.destinationsol.body.components.BodyLinked; import org.destinationsol.common.In; +import org.destinationsol.common.SolMath; +import org.destinationsol.common.SolRandom; +import org.destinationsol.entitysystem.EntitySystemManager; import org.destinationsol.entitysystem.EventReceiver; import org.destinationsol.game.Rubble; import org.destinationsol.game.RubbleBuilder; import org.destinationsol.game.SolGame; +import org.destinationsol.game.drawables.DrawableLevel; +import org.destinationsol.health.components.Health; +import org.destinationsol.location.components.Angle; import org.destinationsol.location.components.Position; import org.destinationsol.location.components.Velocity; import org.destinationsol.removal.events.DeletionEvent; import org.destinationsol.removal.systems.DestructionSystem; +import org.destinationsol.rendering.RenderableElement; +import org.destinationsol.rendering.components.Renderable; import org.destinationsol.rubble.components.CreatesRubbleOnDestruction; +import org.destinationsol.rubble.components.RubbleMesh; import org.destinationsol.size.components.Size; import org.destinationsol.stasis.components.Stasis; import org.terasology.gestalt.entitysystem.entity.EntityRef; @@ -39,13 +51,76 @@ */ public class RubbleCreationSystem implements EventReceiver { + public static final float MIN_SCALE = .07f; + public static final float MAX_SCALE = .12f; + public static final float SIZE_TO_SHARD_COUNT = 13f; + private static final float MAX_ROT_SPD = 5f; + private static final float MAX_SPD = 40f; + @In private RubbleBuilder rubbleBuilder; @In private SolGame solGame; - //TODO once Shards are entities, this needs to be refactored to replace ShardBuilder + @In + private EntitySystemManager entitySystemManager; + + /** + * This method creates pieces of rubble using values from the object that they are being creating from. It + * initializes entities for each piece of rubble with the relevant component + * @param pos Position component of parent entity + * @param vel Velocity component of parent entity + * @param angle Angle component of parent entity + * @param size Size component of parent entity, used to determine amount of rubble to generate + */ + public void buildRubblePieces(Position pos, Velocity vel, Angle angle, Size size) { + int count = (int) (size.size * SIZE_TO_SHARD_COUNT); + Vector2 basePos = pos.position; + for (int i = 0; i < count; i++) { + + //Create graphics component + RenderableElement element = new RenderableElement(); + element.texture = SolRandom.randomElement(Assets.listTexturesMatching("engine:shard_.*")); + element.drawableLevel = DrawableLevel.PROJECTILES; + element.graphicsOffset = new Vector2(); + + float scale = SolRandom.randomFloat(MIN_SCALE, MAX_SCALE); + element.setSize(scale); + + element.relativePosition = new Vector2(); + element.tint = Color.WHITE; + Renderable graphicsComponent = new Renderable(); + graphicsComponent.elements.add(element); + + //Create position component + float velocityAngle = SolRandom.randomFloat(180); + Vector2 position = new Vector2(); + SolMath.fromAl(position, velocityAngle, SolRandom.randomFloat(size.size)); + position.add(basePos); + Position positionComponent = new Position(); + positionComponent.position = position; + + //Create health component + Health health = new Health(); + health.currentHealth = 1; + + //Create size component + Size sizeComponent = new Size(); + sizeComponent.size = scale; + + //Create velocity component + Velocity velocityComponent = new Velocity(); + Vector2 velocity = SolMath.fromAl(velocityAngle, SolRandom.randomFloat(MAX_SPD)); + velocity.add(vel.velocity); + SolMath.free(velocity); + velocityComponent.velocity = velocity; + + EntityRef entityRef = entitySystemManager.getEntityManager().createEntity(graphicsComponent, positionComponent, + velocityComponent, angle, sizeComponent, new RubbleMesh()); + entityRef.setComponent(new BodyLinked()); + } + } /** * When an entity with a {@link CreatesRubbleOnDestruction} component is destroyed, this creates {@link Rubble}s where @@ -55,15 +130,40 @@ public class RubbleCreationSystem implements EventReceiver { @Before(DestructionSystem.class) public EventResult onDeletion(DeletionEvent event, EntityRef entity) { if (!entity.hasComponent(Stasis.class)) { - Vector2 position = entity.getComponent(Position.class).get().position; - Vector2 velocity; + + Velocity velocityComponent = new Velocity(); + if (entity.hasComponent(Velocity.class)) { - velocity = entity.getComponent(Velocity.class).get().velocity; + velocityComponent.velocity = entity.getComponent(Velocity.class).get().velocity; + } else { + velocityComponent.velocity = new Vector2(); + } + + Angle angleComponent = new Angle(); + + if (entity.hasComponent(Angle.class)) { + angleComponent.setAngle(entity.getComponent(Angle.class).get().getAngle()); } else { - velocity = new Vector2(); + angleComponent.setAngle(0f); } - float size = entity.getComponent(Size.class).get().size; - rubbleBuilder.buildExplosionShards(solGame, position, velocity, size); + + Size sizeComponent = new Size(); + + if (entity.hasComponent(Size.class)) { + sizeComponent.size = entity.getComponent(Size.class).get().size; + } else { + sizeComponent.size = 2f; + } + + Position positionComponent = new Position(); + + if (entity.hasComponent(Position.class)) { + positionComponent.position = entity.getComponent(Position.class).get().position; + } else { + positionComponent.position = new Vector2(); + } + + buildRubblePieces(positionComponent, velocityComponent, angleComponent, sizeComponent); } return EventResult.CONTINUE; } From 70c32cab1b26e6fff33c3998eee15e1e187a0196 Mon Sep 17 00:00:00 2001 From: Isaiah Blanks Date: Mon, 26 Apr 2021 13:28:27 -0400 Subject: [PATCH 2/3] Fixed comments and cleaned up methods for Rubble --- .../rubble/components/RubbleMesh.java | 4 +- .../rubble/systems/RubbleCreationSystem.java | 102 +++++++++--------- 2 files changed, 55 insertions(+), 51 deletions(-) diff --git a/engine/src/main/java/org/destinationsol/rubble/components/RubbleMesh.java b/engine/src/main/java/org/destinationsol/rubble/components/RubbleMesh.java index f3ac81c88..7c8bbd307 100644 --- a/engine/src/main/java/org/destinationsol/rubble/components/RubbleMesh.java +++ b/engine/src/main/java/org/destinationsol/rubble/components/RubbleMesh.java @@ -15,10 +15,12 @@ */ package org.destinationsol.rubble.components; +import org.destinationsol.body.events.GenerateBodyEvent; +import org.destinationsol.rubble.systems.RubbleBodyCreationSystem; import org.terasology.gestalt.entitysystem.component.EmptyComponent; /** - * This class directs the onGenerateBody event to the RubbleBodyCreationSystem + * This class directs the {@link GenerateBodyEvent} to the {@link RubbleBodyCreationSystem} */ public class RubbleMesh extends EmptyComponent { } diff --git a/engine/src/main/java/org/destinationsol/rubble/systems/RubbleCreationSystem.java b/engine/src/main/java/org/destinationsol/rubble/systems/RubbleCreationSystem.java index 8135ec083..95f5830fb 100644 --- a/engine/src/main/java/org/destinationsol/rubble/systems/RubbleCreationSystem.java +++ b/engine/src/main/java/org/destinationsol/rubble/systems/RubbleCreationSystem.java @@ -51,10 +51,9 @@ */ public class RubbleCreationSystem implements EventReceiver { + public static final float SIZE_TO_SHARD_COUNT = 13f; public static final float MIN_SCALE = .07f; public static final float MAX_SCALE = .12f; - public static final float SIZE_TO_SHARD_COUNT = 13f; - private static final float MAX_ROT_SPD = 5f; private static final float MAX_SPD = 40f; @In @@ -66,6 +65,55 @@ public class RubbleCreationSystem implements EventReceiver { @In private EntitySystemManager entitySystemManager; + /** + * When an entity with a {@link CreatesRubbleOnDestruction} component is destroyed, this creates {@link Rubble} where + * the entity was, unless the entity is in {@link Stasis}. + */ + @ReceiveEvent(components = {CreatesRubbleOnDestruction.class, Position.class, Size.class}) + @Before(DestructionSystem.class) + public EventResult onDeletion(DeletionEvent event, EntityRef entity) { + if (!entity.hasComponent(Stasis.class)) { + Velocity velocityComponent; + Angle angleComponent; + Size sizeComponent; + Position positionComponent; + + if (entity.hasComponent(Velocity.class)) { + velocityComponent = entity.getComponent(Velocity.class).get(); + } else { + velocityComponent = new Velocity(); + velocityComponent.velocity = new Vector2(); + } + + + if (entity.hasComponent(Angle.class)) { + angleComponent = entity.getComponent(Angle.class).get(); + } else { + angleComponent = new Angle(); + angleComponent.setAngle(0f); + } + + + if (entity.hasComponent(Size.class)) { + sizeComponent = entity.getComponent(Size.class).get(); + } else { + sizeComponent = new Size(); + sizeComponent.size = 2f; + } + + + if (entity.hasComponent(Position.class)) { + positionComponent = entity.getComponent(Position.class).get(); + } else { + positionComponent = new Position(); + positionComponent.position = new Vector2(); + } + + buildRubblePieces(positionComponent, velocityComponent, angleComponent, sizeComponent); + } + return EventResult.CONTINUE; + } + /** * This method creates pieces of rubble using values from the object that they are being creating from. It * initializes entities for each piece of rubble with the relevant component @@ -74,7 +122,7 @@ public class RubbleCreationSystem implements EventReceiver { * @param angle Angle component of parent entity * @param size Size component of parent entity, used to determine amount of rubble to generate */ - public void buildRubblePieces(Position pos, Velocity vel, Angle angle, Size size) { + private void buildRubblePieces(Position pos, Velocity vel, Angle angle, Size size) { int count = (int) (size.size * SIZE_TO_SHARD_COUNT); Vector2 basePos = pos.position; for (int i = 0; i < count; i++) { @@ -113,58 +161,12 @@ public void buildRubblePieces(Position pos, Velocity vel, Angle angle, Size size Velocity velocityComponent = new Velocity(); Vector2 velocity = SolMath.fromAl(velocityAngle, SolRandom.randomFloat(MAX_SPD)); velocity.add(vel.velocity); - SolMath.free(velocity); velocityComponent.velocity = velocity; EntityRef entityRef = entitySystemManager.getEntityManager().createEntity(graphicsComponent, positionComponent, velocityComponent, angle, sizeComponent, new RubbleMesh()); + SolMath.free(velocity); entityRef.setComponent(new BodyLinked()); } } - - /** - * When an entity with a {@link CreatesRubbleOnDestruction} component is destroyed, this creates {@link Rubble}s where - * the entity was, unless the entity is in {@link Stasis}. - */ - @ReceiveEvent(components = {CreatesRubbleOnDestruction.class, Position.class, Size.class}) - @Before(DestructionSystem.class) - public EventResult onDeletion(DeletionEvent event, EntityRef entity) { - if (!entity.hasComponent(Stasis.class)) { - - Velocity velocityComponent = new Velocity(); - - if (entity.hasComponent(Velocity.class)) { - velocityComponent.velocity = entity.getComponent(Velocity.class).get().velocity; - } else { - velocityComponent.velocity = new Vector2(); - } - - Angle angleComponent = new Angle(); - - if (entity.hasComponent(Angle.class)) { - angleComponent.setAngle(entity.getComponent(Angle.class).get().getAngle()); - } else { - angleComponent.setAngle(0f); - } - - Size sizeComponent = new Size(); - - if (entity.hasComponent(Size.class)) { - sizeComponent.size = entity.getComponent(Size.class).get().size; - } else { - sizeComponent.size = 2f; - } - - Position positionComponent = new Position(); - - if (entity.hasComponent(Position.class)) { - positionComponent.position = entity.getComponent(Position.class).get().position; - } else { - positionComponent.position = new Vector2(); - } - - buildRubblePieces(positionComponent, velocityComponent, angleComponent, sizeComponent); - } - return EventResult.CONTINUE; - } } From 45bd0c6fdb1661ec65cf4fd9e81b7a00f8816da2 Mon Sep 17 00:00:00 2001 From: Isaiah Blanks Date: Sun, 2 May 2021 21:31:54 -0400 Subject: [PATCH 3/3] Remove redundant component creation checks --- .../rubble/systems/RubbleCreationSystem.java | 21 +------------------ 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/engine/src/main/java/org/destinationsol/rubble/systems/RubbleCreationSystem.java b/engine/src/main/java/org/destinationsol/rubble/systems/RubbleCreationSystem.java index 95f5830fb..84ecf725a 100644 --- a/engine/src/main/java/org/destinationsol/rubble/systems/RubbleCreationSystem.java +++ b/engine/src/main/java/org/destinationsol/rubble/systems/RubbleCreationSystem.java @@ -75,8 +75,6 @@ public EventResult onDeletion(DeletionEvent event, EntityRef entity) { if (!entity.hasComponent(Stasis.class)) { Velocity velocityComponent; Angle angleComponent; - Size sizeComponent; - Position positionComponent; if (entity.hasComponent(Velocity.class)) { velocityComponent = entity.getComponent(Velocity.class).get(); @@ -85,7 +83,6 @@ public EventResult onDeletion(DeletionEvent event, EntityRef entity) { velocityComponent.velocity = new Vector2(); } - if (entity.hasComponent(Angle.class)) { angleComponent = entity.getComponent(Angle.class).get(); } else { @@ -93,23 +90,7 @@ public EventResult onDeletion(DeletionEvent event, EntityRef entity) { angleComponent.setAngle(0f); } - - if (entity.hasComponent(Size.class)) { - sizeComponent = entity.getComponent(Size.class).get(); - } else { - sizeComponent = new Size(); - sizeComponent.size = 2f; - } - - - if (entity.hasComponent(Position.class)) { - positionComponent = entity.getComponent(Position.class).get(); - } else { - positionComponent = new Position(); - positionComponent.position = new Vector2(); - } - - buildRubblePieces(positionComponent, velocityComponent, angleComponent, sizeComponent); + buildRubblePieces(entity.getComponent(Position.class).get(), velocityComponent, angleComponent, entity.getComponent(Size.class).get()); } return EventResult.CONTINUE; }