From 273f4590df72f4836fda8704f1fcc46819e77308 Mon Sep 17 00:00:00 2001 From: Anuken Date: Thu, 30 Apr 2020 21:58:48 -0400 Subject: [PATCH] Physics --- core/src/mindustry/async/AsyncLogic.java | 40 +++-- core/src/mindustry/async/AsyncProcess.java | 8 +- core/src/mindustry/async/PhysicsProcess.java | 142 ++++++++++++++++++ core/src/mindustry/entities/AllDefs.java | 2 +- .../mindustry/entities/def/HitboxComp.java | 4 +- core/src/mindustry/entities/def/UnitComp.java | 17 --- .../entities/units/AIController.java | 9 +- core/src/mindustry/io/SaveVersion.java | 2 +- gradle.properties | 2 +- 9 files changed, 189 insertions(+), 37 deletions(-) diff --git a/core/src/mindustry/async/AsyncLogic.java b/core/src/mindustry/async/AsyncLogic.java index b3cf6757fd..c5f7b40b79 100644 --- a/core/src/mindustry/async/AsyncLogic.java +++ b/core/src/mindustry/async/AsyncLogic.java @@ -1,6 +1,8 @@ package mindustry.async; +import arc.*; import arc.struct.*; +import mindustry.game.EventType.*; import java.util.concurrent.*; @@ -12,15 +14,28 @@ public class AsyncLogic{ private Array> futures = new Array<>(); private ExecutorService executor = Executors.newFixedThreadPool(processes.size, r -> { - Thread thread = new Thread(r, "AsyncExecutor-Thread"); + Thread thread = new Thread(r, "AsyncLogic-Thread"); thread.setDaemon(true); - thread.setUncaughtExceptionHandler((t, e) -> { - e.printStackTrace(); - //TODO crash! - }); + thread.setUncaughtExceptionHandler((t, e) -> Core.app.post(() -> { throw new RuntimeException(e); })); return thread; }); + public AsyncLogic(){ + Events.on(WorldLoadEvent.class, e -> { + complete(); + for(AsyncProcess p : processes){ + p.init(); + } + }); + + Events.on(ResetEvent.class, e -> { + complete(); + for(AsyncProcess p : processes){ + p.reset(); + } + }); + } + public void begin(){ //sync begin for(AsyncProcess p : processes){ @@ -36,6 +51,15 @@ public class AsyncLogic{ } public void end(){ + complete(); + + //sync end (flush data) + for(AsyncProcess p : processes){ + p.end(); + } + } + + private void complete(){ //wait for all threads to stop processing for(Future future : futures){ try{ @@ -45,11 +69,7 @@ public class AsyncLogic{ } } + //clear processed futures futures.clear(); - - //sync end (flush data) - for(AsyncProcess p : processes){ - p.end(); - } } } diff --git a/core/src/mindustry/async/AsyncProcess.java b/core/src/mindustry/async/AsyncProcess.java index c8a644108f..08c6d86a53 100644 --- a/core/src/mindustry/async/AsyncProcess.java +++ b/core/src/mindustry/async/AsyncProcess.java @@ -2,7 +2,13 @@ package mindustry.async; public interface AsyncProcess{ - /** Synchronous. Called at the beginning of the main loop. */ + /** Sync. Called when the world loads. */ + void init(); + + /** Sync. Called when the world resets. */ + void reset(); + + /** Sync. Called at the beginning of the main loop. */ void begin(); /** Async. Called in a separate thread. */ diff --git a/core/src/mindustry/async/PhysicsProcess.java b/core/src/mindustry/async/PhysicsProcess.java index f1718b5de7..b3765976d4 100644 --- a/core/src/mindustry/async/PhysicsProcess.java +++ b/core/src/mindustry/async/PhysicsProcess.java @@ -1,19 +1,161 @@ package mindustry.async; +import arc.*; +import arc.box2d.*; +import arc.box2d.BodyDef.*; +import arc.math.geom.*; +import arc.struct.*; +import mindustry.entities.*; +import mindustry.gen.*; + public class PhysicsProcess implements AsyncProcess{ + private Physics physics; + private Array refs = new Array<>(false); + private BodyDef def; + + private EntityGroup group; + private Filter flying = new Filter(){{ + maskBits = categoryBits = 2; + }}, ground = new Filter(){{ + maskBits = categoryBits = 1; + }}; + + public PhysicsProcess(){ + def = new BodyDef(); + def.allowSleep = true; + def.bullet = false; + def.type = BodyType.DynamicBody; + + //currently only enabled for units + group = Groups.unit; + } @Override public void begin(){ + if(physics == null) return; + //remove stale entities + refs.removeAll(ref -> { + if(!ref.entity.isAdded()){ + physics.destroyBody(ref.body); + return true; + } + return false; + }); + + //find entities without bodies and assign them + for(Hitboxc entity : group){ + boolean grounded = ((Flyingc)entity).isGrounded(); + + if(entity.body() == null){ + //add bodies to entities that have none + CircleShape shape = new CircleShape(); + shape.setRadius(entity.hitSize() * 0.46f); + + FixtureDef fd = new FixtureDef(); + fd.shape = shape; + fd.density = 10.0f; + fd.restitution = 0.05f; + fd.filter.maskBits = fd.filter.categoryBits = (grounded ? ground : flying).maskBits; + + def.position.set(entity); + + Body body = physics.createBody(def); + body.createFixture(fd); + + body.setUserData(entity); + + PhysicRef ref = new PhysicRef(entity, body); + refs.add(ref); + + entity.body(ref); + } + + //save last position + PhysicRef ref = entity.body(); + + if(ref.wasGround != grounded){ + //set correct filter + ref.body.getFixtureList().first().setFilterData(grounded ? ground : flying); + ref.wasGround = grounded; + } + + ref.velocity.set(entity.deltaX(), entity.deltaY()); + ref.position.set(entity); + } } @Override public void process(){ + if(physics == null) return; + //get last position vectors before step + for(PhysicRef ref : refs){ + //force set target position + ref.body.setPosition(ref.position.x, ref.position.y); + + //save last position for delta + ref.lastPosition.set(ref.body.getPosition()); + + //write velocity + ref.body.setLinearVelocity(ref.velocity); + } + + physics.step(Core.graphics.getDeltaTime(), 3, 3); + + //get delta vectors + for(PhysicRef ref : refs){ + //get delta vector + ref.delta.set(ref.body.getPosition()).sub(ref.lastPosition); + } } @Override public void end(){ + if(physics == null) return; + //move entities + for(PhysicRef ref : refs){ + Hitboxc entity = ref.entity; + + if(entity instanceof Velc){ + //move using velocity component move method TODO hack + ((Velc)entity).move(ref.delta.x, ref.delta.y); + }else{ + //move directly + entity.trns(ref.delta.x, ref.delta.y); + } + + //save last position + ref.position.set(entity); + } + } + + @Override + public void reset(){ + if(physics != null){ + refs.clear(); + physics.dispose(); + physics = null; + } + } + + @Override + public void init(){ + reset(); + + physics = new Physics(new Vec2(), true); + } + + public static class PhysicRef{ + Hitboxc entity; + Body body; + boolean wasGround = true; + Vec2 lastPosition = new Vec2(), delta = new Vec2(), velocity = new Vec2(), position = new Vec2(); + + public PhysicRef(Hitboxc entity, Body body){ + this.entity = entity; + this.body = body; + } } } diff --git a/core/src/mindustry/entities/AllDefs.java b/core/src/mindustry/entities/AllDefs.java index ca88a8b688..cc1d816d91 100644 --- a/core/src/mindustry/entities/AllDefs.java +++ b/core/src/mindustry/entities/AllDefs.java @@ -20,7 +20,7 @@ class AllDefs{ } - @GroupDef(value = Unitc.class, spatial = true, collide = {gunit.class}, mapping = true) + @GroupDef(value = Unitc.class, spatial = true, mapping = true) class gunit{ } diff --git a/core/src/mindustry/entities/def/HitboxComp.java b/core/src/mindustry/entities/def/HitboxComp.java index cd0a6659b6..94163555b3 100644 --- a/core/src/mindustry/entities/def/HitboxComp.java +++ b/core/src/mindustry/entities/def/HitboxComp.java @@ -1,8 +1,9 @@ package mindustry.entities.def; -import arc.math.geom.*; import arc.math.geom.QuadTree.*; +import arc.math.geom.*; import mindustry.annotations.Annotations.*; +import mindustry.async.PhysicsProcess.*; import mindustry.gen.*; @Component @@ -10,6 +11,7 @@ abstract class HitboxComp implements Posc, QuadTreeObject{ @Import float x, y; transient float lastX, lastY, hitSize; + transient PhysicRef body; @Override public void update(){ diff --git a/core/src/mindustry/entities/def/UnitComp.java b/core/src/mindustry/entities/def/UnitComp.java index f1d27b5757..22c983ba8b 100644 --- a/core/src/mindustry/entities/def/UnitComp.java +++ b/core/src/mindustry/entities/def/UnitComp.java @@ -74,23 +74,6 @@ abstract class UnitComp implements Healthc, Velc, Statusc, Teamc, Itemsc, Hitbox controller(controller); } - @Override - public void collision(Hitboxc other, float x, float y){ - if(!(other instanceof Unitc)) return; - Unitc unit = (Unitc)other; - - if(isGrounded() != unit.isGrounded()) return; - - float scale = 2f; - hitbox(Tmp.r1); - other.hitbox(Tmp.r2); - Vec2 v = Geometry.overlap(Tmp.r1, Tmp.r2, true); - float tm = mass() + unit.mass(); - float s1 = mass() / tm, s2 = unit.mass() / tm; - move(v.x*s2/scale, v.y*s2/scale); - unit.move(-v.x*s1/scale, -v.y*s1/scale); - } - @Override public void type(UnitType type){ this.type = type; diff --git a/core/src/mindustry/entities/units/AIController.java b/core/src/mindustry/entities/units/AIController.java index 4cf22a0350..75ff20cd21 100644 --- a/core/src/mindustry/entities/units/AIController.java +++ b/core/src/mindustry/entities/units/AIController.java @@ -3,7 +3,6 @@ package mindustry.entities.units; import arc.math.*; import arc.math.geom.*; import arc.util.*; -import mindustry.entities.*; import mindustry.gen.*; import mindustry.world.*; import mindustry.world.meta.*; @@ -37,10 +36,10 @@ public class AIController implements UnitController{ } protected void targetClosest(){ - Teamc newTarget = Units.closestTarget(unit.team(), unit.x(), unit.y(), Math.max(unit.range(), unit.type().range), u -> (unit.type().targetAir && u.isFlying()) || (unit.type().targetGround && !u.isFlying())); - if(newTarget != null){ - target = newTarget; - } + //Teamc newTarget = Units.closestTarget(unit.team(), unit.x(), unit.y(), Math.max(unit.range(), unit.type().range), u -> (unit.type().targetAir && u.isFlying()) || (unit.type().targetGround && !u.isFlying())); + //if(newTarget != null){ + // target = newTarget; + //} } @Override diff --git a/core/src/mindustry/io/SaveVersion.java b/core/src/mindustry/io/SaveVersion.java index 7f1aad622d..9a558d976f 100644 --- a/core/src/mindustry/io/SaveVersion.java +++ b/core/src/mindustry/io/SaveVersion.java @@ -92,7 +92,7 @@ public abstract class SaveVersion extends SaveFileReader{ Core.camera.position.set(Tmp.v1); player.set(Tmp.v1); - control.input.controlledType = content.getByName(ContentType.unit, map.get("controlledType")); + control.input.controlledType = content.getByName(ContentType.unit, map.get("controlledType", "")); } Map worldmap = maps.byName(map.get("mapname", "\\\\\\")); diff --git a/gradle.properties b/gradle.properties index 17ec201c07..973f5c2699 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,3 @@ org.gradle.daemon=true org.gradle.jvmargs=-Xms256m -Xmx1024m -archash=7ef1821dfd7725c97d0dbcc6a6da5c96b1fbb4b2 +archash=3533846d0009cda47c9416c4cb105b8e79518099