diff --git a/core/src/mindustry/ai/types/GroundAI.java b/core/src/mindustry/ai/types/GroundAI.java index 2e295bd68b..f305cefbe9 100644 --- a/core/src/mindustry/ai/types/GroundAI.java +++ b/core/src/mindustry/ai/types/GroundAI.java @@ -14,6 +14,15 @@ public class GroundAI extends AIController{ @Override public void update(){ + + //TODO test + if(true){ + unit.controlWeapons(true, true); + unit.aimLook(unit.x(), unit.y() + 10); + + return; + } + if(Units.invalidateTarget(target, unit.team(), unit.x(), unit.y(), Float.MAX_VALUE)){ target = null; diff --git a/core/src/mindustry/async/AsyncLogic.java b/core/src/mindustry/async/AsyncLogic.java index c5f7b40b79..d8ced55084 100644 --- a/core/src/mindustry/async/AsyncLogic.java +++ b/core/src/mindustry/async/AsyncLogic.java @@ -8,7 +8,7 @@ import java.util.concurrent.*; public class AsyncLogic{ //all processes to be executed each frame - private Array processes = Array.with(new PhysicsProcess()); + private Array processes = Array.with(new PhysicsProcess(), new CollisionProcess()); //futures to be awaited private Array> futures = new Array<>(); diff --git a/core/src/mindustry/async/CollisionProcess.java b/core/src/mindustry/async/CollisionProcess.java new file mode 100644 index 0000000000..47b87dbf40 --- /dev/null +++ b/core/src/mindustry/async/CollisionProcess.java @@ -0,0 +1,200 @@ +package mindustry.async; + +import arc.*; +import arc.box2d.*; +import arc.box2d.BodyDef.*; +import arc.math.geom.*; +import arc.struct.*; +import arc.util.*; +import arc.util.pooling.*; +import arc.util.pooling.Pool.*; +import mindustry.entities.*; +import mindustry.gen.*; + +/** + * Processes collisions. + * + * Each entity is assigned a final filter layer, then given a body and inserted into a physics world. + * + * Async: + * The body's position is set to its entity position, and the body velocity is set to the entity delta. + * Collisions are resolved and stored in a list, then processed synchronously. + * + */ +public class CollisionProcess implements AsyncProcess, ContactListener, ContactFilter{ + private Pool pool = Pools.get(CollisionRef.class, CollisionRef::new); + + private Physics physics; + private Array refs = new Array<>(false); + private BodyDef def; + private FixtureDef fdef; + + private EntityGroup group = Groups.collision; + private Array collisions = new Array<>(Collisionc.class); + + public CollisionProcess(){ + def = new BodyDef(); + def.type = BodyType.DynamicBody; + + fdef = new FixtureDef(); + fdef.density = 1; + fdef.isSensor = true; + } + + @Override + public void begin(){ + if(physics == null) return; + + //remove stale entities + refs.removeAll(ref -> { + if(!ref.entity.isAdded()){ + physics.destroyBody(ref.body); + pool.free(ref); + return true; + } + return false; + }); + + collisions.clear(); + + //find entities without bodies and assign them + for(Collisionc entity : group){ + if(entity.colref() == null){ + //add bodies to entities that have none + fdef.shape = new CircleShape(entity.hitSize() / 2f); + + def.position.set(entity); + + Body body = physics.createBody(def); + body.createFixture(fdef); + + CollisionRef ref = pool.obtain().set(entity, body); + refs.add(ref); + + body.setUserData(ref); + entity.colref(ref); + } + + //save last position + CollisionRef ref = entity.colref(); + ref.position.set(entity); + } + } + + @Override + public void process(){ + if(physics == null) return; + + collisions.clear(); + + Time.mark(); + + //get last position vectors before step + for(CollisionRef ref : refs){ + //force set target position + ref.body.setPosition(ref.position.x, ref.position.y); + + //write velocity + ref.body.setLinearVelocity(ref.velocity); + } + + physics.step(Core.graphics.getDeltaTime(), 2, 2); + } + + @Override + public void end(){ + if(physics == null) return; + + //processes anything that happened + for(int i = 0; i < collisions.size; i += 2){ + Collisionc a = collisions.items[i]; + Collisionc b = collisions.items[i + 1]; + + //TODO incorrect + float cx = (a.x() + b.x())/2f, cy = (a.y() + b.y())/2f; + + a.collision(b, cx, cy); + b.collision(a, cx, cy); + } + + //update velocity state based on frame movement + for(CollisionRef ref : refs){ + ref.velocity.set(ref.entity).sub(ref.position); + } + } + + @Override + public void reset(){ + if(physics != null){ + pool.freeAll(refs); + refs.clear(); + physics.dispose(); + physics = null; + } + } + + @Override + public void init(){ + reset(); + + physics = new Physics(new Vec2(), true); + physics.setContactListener(this); + physics.setContactFilter(this); + } + + @Override + public void beginContact(Contact contact){ + CollisionRef a = contact.getFixtureA().getBody().getUserData(); + CollisionRef b = contact.getFixtureB().getBody().getUserData(); + + //save collision + collisions.add(a.entity, b.entity); + } + + @Override + public void endContact(Contact contact){ + + } + + @Override + public void preSolve(Contact contact, Manifold oldManifold){ + + } + + @Override + public void postSolve(Contact contact, ContactImpulse impulse){ + + } + + @Override + public boolean shouldCollide(Fixture fixtureA, Fixture fixtureB){ + CollisionRef a = fixtureA.getBody().getUserData(); + CollisionRef b = fixtureB.getBody().getUserData(); + + //note that this method is called in a different thread, but for simple collision checks state doesn't matter too much + return a != b && a.entity.collides(b.entity) && b.entity.collides(a.entity); + } + + public static class CollisionRef implements Poolable{ + Collisionc entity; + Body body; + Vec2 velocity = new Vec2(), position = new Vec2(); + + public CollisionRef set(Collisionc entity, Body body){ + this.entity = entity; + this.body = body; + + position.set(entity); + return this; + } + + @Override + public void reset(){ + entity = null; + body = null; + + velocity.setZero(); + position.setZero(); + } + } +} diff --git a/core/src/mindustry/async/PhysicsProcess.java b/core/src/mindustry/async/PhysicsProcess.java index 7fcf9a05c2..5b193f4918 100644 --- a/core/src/mindustry/async/PhysicsProcess.java +++ b/core/src/mindustry/async/PhysicsProcess.java @@ -13,7 +13,7 @@ public class PhysicsProcess implements AsyncProcess{ private Array refs = new Array<>(false); private BodyDef def; - private EntityGroup group; + private EntityGroup group; private Filter flying = new Filter(){{ maskBits = categoryBits = 2; }}, ground = new Filter(){{ @@ -22,8 +22,6 @@ public class PhysicsProcess implements AsyncProcess{ public PhysicsProcess(){ def = new BodyDef(); - def.allowSleep = true; - def.bullet = false; def.type = BodyType.DynamicBody; //currently only enabled for units @@ -44,17 +42,15 @@ public class PhysicsProcess implements AsyncProcess{ }); //find entities without bodies and assign them - for(Unitc entity : group){ + for(Physicsc entity : group){ boolean grounded = entity.isGrounded(); - if(entity.body() == null){ + if(entity.physref() == null){ //add bodies to entities that have none - CircleShape shape = new CircleShape(); - shape.setRadius(entity.hitSize() / 2f); - FixtureDef fd = new FixtureDef(); - fd.shape = shape; - fd.density = 5.0f * entity.mass(); + fd.shape = new CircleShape(entity.hitSize() / 2f); + //TODO does this scale well? + fd.density = 5f; //5.0f * entity.mass(); fd.restitution = 0.05f; fd.filter.maskBits = fd.filter.categoryBits = (grounded ? ground : flying).maskBits; @@ -62,16 +58,15 @@ public class PhysicsProcess implements AsyncProcess{ Body body = physics.createBody(def); body.createFixture(fd); - body.setUserData(entity); PhysicRef ref = new PhysicRef(entity, body); refs.add(ref); - entity.body(ref); + entity.physref(ref); } //save last position - PhysicRef ref = entity.body(); + PhysicRef ref = entity.physref(); if(ref.wasGround != grounded){ //set correct filter @@ -115,15 +110,9 @@ public class PhysicsProcess implements AsyncProcess{ //move entities for(PhysicRef ref : refs){ - Hitboxc entity = ref.entity; + Physicsc 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); - } + entity.move(ref.delta.x, ref.delta.y); //save last position ref.position.set(entity); @@ -147,12 +136,12 @@ public class PhysicsProcess implements AsyncProcess{ } public static class PhysicRef{ - Hitboxc entity; + Physicsc 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){ + public PhysicRef(Physicsc 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 cc1d816d91..04680a0fe6 100644 --- a/core/src/mindustry/entities/AllDefs.java +++ b/core/src/mindustry/entities/AllDefs.java @@ -15,11 +15,16 @@ class AllDefs{ } - @GroupDef(value = Bulletc.class, spatial = true, collide = {gunit.class}) + @GroupDef(value = Bulletc.class, spatial = true) class gbullet{ } + @GroupDef(value = Collisionc.class) + class gcollision{ + + } + @GroupDef(value = Unitc.class, spatial = true, mapping = true) class gunit{ diff --git a/core/src/mindustry/entities/EntityCollisions.java b/core/src/mindustry/entities/EntityCollisions.java index 00bf484a6c..79356ecc91 100644 --- a/core/src/mindustry/entities/EntityCollisions.java +++ b/core/src/mindustry/entities/EntityCollisions.java @@ -114,7 +114,7 @@ public class EntityCollisions{ group.each(s -> { s.updateLastPosition(); - tree.insert(s); + //tree.insert(s); }); } diff --git a/core/src/mindustry/entities/def/BulletComp.java b/core/src/mindustry/entities/def/BulletComp.java index 43dd0800cf..6aa5106a64 100644 --- a/core/src/mindustry/entities/def/BulletComp.java +++ b/core/src/mindustry/entities/def/BulletComp.java @@ -12,7 +12,7 @@ import static mindustry.Vars.*; @EntityDef(value = {Bulletc.class}, pooled = true) @Component -abstract class BulletComp implements Timedc, Damagec, Hitboxc, Teamc, Posc, Drawc, Shielderc, Ownerc, Velc, Bulletc, Timerc{ +abstract class BulletComp implements Timedc, Damagec, Collisionc, Teamc, Posc, Drawc, Shielderc, Ownerc, Velc, Bulletc, Timerc{ Object data; BulletType type; float damage; diff --git a/core/src/mindustry/entities/def/CollisionComp.java b/core/src/mindustry/entities/def/CollisionComp.java new file mode 100644 index 0000000000..1814177d2a --- /dev/null +++ b/core/src/mindustry/entities/def/CollisionComp.java @@ -0,0 +1,12 @@ +package mindustry.entities.def; + +import mindustry.annotations.Annotations.*; +import mindustry.async.CollisionProcess.*; +import mindustry.gen.*; + +/** Can be collided with. Collision elibility depends on team. + * TODO merge with hitboxcomp?*/ +@Component +abstract class CollisionComp implements Hitboxc{ + transient CollisionRef colref; +} diff --git a/core/src/mindustry/entities/def/HitboxComp.java b/core/src/mindustry/entities/def/HitboxComp.java index 94163555b3..8e33766f93 100644 --- a/core/src/mindustry/entities/def/HitboxComp.java +++ b/core/src/mindustry/entities/def/HitboxComp.java @@ -3,7 +3,6 @@ package mindustry.entities.def; import arc.math.geom.QuadTree.*; import arc.math.geom.*; import mindustry.annotations.Annotations.*; -import mindustry.async.PhysicsProcess.*; import mindustry.gen.*; @Component @@ -11,7 +10,6 @@ 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/MassComp.java b/core/src/mindustry/entities/def/MassComp.java deleted file mode 100644 index aef77bfa6a..0000000000 --- a/core/src/mindustry/entities/def/MassComp.java +++ /dev/null @@ -1,13 +0,0 @@ -package mindustry.entities.def; - -import mindustry.annotations.Annotations.*; -import mindustry.gen.*; - -@Component -abstract class MassComp implements Velc{ - transient float mass = 1f; - - public void impulse(float x, float y){ - vel().add(x / mass, y / mass); - } -} diff --git a/core/src/mindustry/entities/def/PhysicsComp.java b/core/src/mindustry/entities/def/PhysicsComp.java new file mode 100644 index 0000000000..97ba374fab --- /dev/null +++ b/core/src/mindustry/entities/def/PhysicsComp.java @@ -0,0 +1,18 @@ +package mindustry.entities.def; + +import mindustry.annotations.Annotations.*; +import mindustry.async.PhysicsProcess.*; +import mindustry.gen.*; + +/** Affected by physics. + * Will bounce off of other objects that are at similar elevations. + * Has mass.*/ +@Component +abstract class PhysicsComp implements Velc, Hitboxc, Flyingc{ + transient PhysicRef physref; + transient float mass = 1f; + + public void impulse(float x, float y){ + vel().add(x / mass, y / mass); + } +} diff --git a/core/src/mindustry/entities/def/UnitComp.java b/core/src/mindustry/entities/def/UnitComp.java index 22c983ba8b..9e14eff1d1 100644 --- a/core/src/mindustry/entities/def/UnitComp.java +++ b/core/src/mindustry/entities/def/UnitComp.java @@ -18,7 +18,7 @@ import mindustry.world.blocks.environment.*; import static mindustry.Vars.*; @Component -abstract class UnitComp implements Healthc, Velc, Statusc, Teamc, Itemsc, Hitboxc, Rotc, Massc, Unitc, Weaponsc, Drawc, Boundedc, Syncc, Shieldc{ +abstract class UnitComp implements Healthc, Physicsc, Collisionc, Statusc, Teamc, Itemsc, Rotc, Unitc, Weaponsc, Drawc, Boundedc, Syncc, Shieldc{ @Import float x, y, rotation, elevation, maxHealth; private UnitController controller; diff --git a/gradle.properties b/gradle.properties index 3125de6047..4bb8a161ab 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,3 @@ org.gradle.daemon=true org.gradle.jvmargs=-Xms256m -Xmx1024m -archash=ae2bb7c28ac26b26e549cdc42b0fb5ecfaa0f916 +archash=00ccb60840294db20d561feda12fa739edaaf601