mirror of
https://github.com/Anuken/Mindustry.git
synced 2025-07-14 01:37:36 +07:00
a performance disaster, part 1
This commit is contained in:
@ -14,6 +14,15 @@ public class GroundAI extends AIController{
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void update(){
|
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)){
|
if(Units.invalidateTarget(target, unit.team(), unit.x(), unit.y(), Float.MAX_VALUE)){
|
||||||
target = null;
|
target = null;
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ import java.util.concurrent.*;
|
|||||||
|
|
||||||
public class AsyncLogic{
|
public class AsyncLogic{
|
||||||
//all processes to be executed each frame
|
//all processes to be executed each frame
|
||||||
private Array<AsyncProcess> processes = Array.with(new PhysicsProcess());
|
private Array<AsyncProcess> processes = Array.with(new PhysicsProcess(), new CollisionProcess());
|
||||||
|
|
||||||
//futures to be awaited
|
//futures to be awaited
|
||||||
private Array<Future<?>> futures = new Array<>();
|
private Array<Future<?>> futures = new Array<>();
|
||||||
|
200
core/src/mindustry/async/CollisionProcess.java
Normal file
200
core/src/mindustry/async/CollisionProcess.java
Normal file
@ -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<CollisionRef> pool = Pools.get(CollisionRef.class, CollisionRef::new);
|
||||||
|
|
||||||
|
private Physics physics;
|
||||||
|
private Array<CollisionRef> refs = new Array<>(false);
|
||||||
|
private BodyDef def;
|
||||||
|
private FixtureDef fdef;
|
||||||
|
|
||||||
|
private EntityGroup<? extends Collisionc> group = Groups.collision;
|
||||||
|
private Array<Collisionc> 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -13,7 +13,7 @@ public class PhysicsProcess implements AsyncProcess{
|
|||||||
private Array<PhysicRef> refs = new Array<>(false);
|
private Array<PhysicRef> refs = new Array<>(false);
|
||||||
private BodyDef def;
|
private BodyDef def;
|
||||||
|
|
||||||
private EntityGroup<Unitc> group;
|
private EntityGroup<? extends Physicsc> group;
|
||||||
private Filter flying = new Filter(){{
|
private Filter flying = new Filter(){{
|
||||||
maskBits = categoryBits = 2;
|
maskBits = categoryBits = 2;
|
||||||
}}, ground = new Filter(){{
|
}}, ground = new Filter(){{
|
||||||
@ -22,8 +22,6 @@ public class PhysicsProcess implements AsyncProcess{
|
|||||||
|
|
||||||
public PhysicsProcess(){
|
public PhysicsProcess(){
|
||||||
def = new BodyDef();
|
def = new BodyDef();
|
||||||
def.allowSleep = true;
|
|
||||||
def.bullet = false;
|
|
||||||
def.type = BodyType.DynamicBody;
|
def.type = BodyType.DynamicBody;
|
||||||
|
|
||||||
//currently only enabled for units
|
//currently only enabled for units
|
||||||
@ -44,17 +42,15 @@ public class PhysicsProcess implements AsyncProcess{
|
|||||||
});
|
});
|
||||||
|
|
||||||
//find entities without bodies and assign them
|
//find entities without bodies and assign them
|
||||||
for(Unitc entity : group){
|
for(Physicsc entity : group){
|
||||||
boolean grounded = entity.isGrounded();
|
boolean grounded = entity.isGrounded();
|
||||||
|
|
||||||
if(entity.body() == null){
|
if(entity.physref() == null){
|
||||||
//add bodies to entities that have none
|
//add bodies to entities that have none
|
||||||
CircleShape shape = new CircleShape();
|
|
||||||
shape.setRadius(entity.hitSize() / 2f);
|
|
||||||
|
|
||||||
FixtureDef fd = new FixtureDef();
|
FixtureDef fd = new FixtureDef();
|
||||||
fd.shape = shape;
|
fd.shape = new CircleShape(entity.hitSize() / 2f);
|
||||||
fd.density = 5.0f * entity.mass();
|
//TODO does this scale well?
|
||||||
|
fd.density = 5f; //5.0f * entity.mass();
|
||||||
fd.restitution = 0.05f;
|
fd.restitution = 0.05f;
|
||||||
fd.filter.maskBits = fd.filter.categoryBits = (grounded ? ground : flying).maskBits;
|
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 body = physics.createBody(def);
|
||||||
body.createFixture(fd);
|
body.createFixture(fd);
|
||||||
body.setUserData(entity);
|
|
||||||
|
|
||||||
PhysicRef ref = new PhysicRef(entity, body);
|
PhysicRef ref = new PhysicRef(entity, body);
|
||||||
refs.add(ref);
|
refs.add(ref);
|
||||||
|
|
||||||
entity.body(ref);
|
entity.physref(ref);
|
||||||
}
|
}
|
||||||
|
|
||||||
//save last position
|
//save last position
|
||||||
PhysicRef ref = entity.body();
|
PhysicRef ref = entity.physref();
|
||||||
|
|
||||||
if(ref.wasGround != grounded){
|
if(ref.wasGround != grounded){
|
||||||
//set correct filter
|
//set correct filter
|
||||||
@ -115,15 +110,9 @@ public class PhysicsProcess implements AsyncProcess{
|
|||||||
|
|
||||||
//move entities
|
//move entities
|
||||||
for(PhysicRef ref : refs){
|
for(PhysicRef ref : refs){
|
||||||
Hitboxc entity = ref.entity;
|
Physicsc entity = ref.entity;
|
||||||
|
|
||||||
if(entity instanceof Velc){
|
entity.move(ref.delta.x, ref.delta.y);
|
||||||
//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
|
//save last position
|
||||||
ref.position.set(entity);
|
ref.position.set(entity);
|
||||||
@ -147,12 +136,12 @@ public class PhysicsProcess implements AsyncProcess{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static class PhysicRef{
|
public static class PhysicRef{
|
||||||
Hitboxc entity;
|
Physicsc entity;
|
||||||
Body body;
|
Body body;
|
||||||
boolean wasGround = true;
|
boolean wasGround = true;
|
||||||
Vec2 lastPosition = new Vec2(), delta = new Vec2(), velocity = new Vec2(), position = new Vec2();
|
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.entity = entity;
|
||||||
this.body = body;
|
this.body = body;
|
||||||
}
|
}
|
||||||
|
@ -15,11 +15,16 @@ class AllDefs{
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@GroupDef(value = Bulletc.class, spatial = true, collide = {gunit.class})
|
@GroupDef(value = Bulletc.class, spatial = true)
|
||||||
class gbullet{
|
class gbullet{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GroupDef(value = Collisionc.class)
|
||||||
|
class gcollision{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@GroupDef(value = Unitc.class, spatial = true, mapping = true)
|
@GroupDef(value = Unitc.class, spatial = true, mapping = true)
|
||||||
class gunit{
|
class gunit{
|
||||||
|
|
||||||
|
@ -114,7 +114,7 @@ public class EntityCollisions{
|
|||||||
|
|
||||||
group.each(s -> {
|
group.each(s -> {
|
||||||
s.updateLastPosition();
|
s.updateLastPosition();
|
||||||
tree.insert(s);
|
//tree.insert(s);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ import static mindustry.Vars.*;
|
|||||||
|
|
||||||
@EntityDef(value = {Bulletc.class}, pooled = true)
|
@EntityDef(value = {Bulletc.class}, pooled = true)
|
||||||
@Component
|
@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;
|
Object data;
|
||||||
BulletType type;
|
BulletType type;
|
||||||
float damage;
|
float damage;
|
||||||
|
12
core/src/mindustry/entities/def/CollisionComp.java
Normal file
12
core/src/mindustry/entities/def/CollisionComp.java
Normal file
@ -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;
|
||||||
|
}
|
@ -3,7 +3,6 @@ package mindustry.entities.def;
|
|||||||
import arc.math.geom.QuadTree.*;
|
import arc.math.geom.QuadTree.*;
|
||||||
import arc.math.geom.*;
|
import arc.math.geom.*;
|
||||||
import mindustry.annotations.Annotations.*;
|
import mindustry.annotations.Annotations.*;
|
||||||
import mindustry.async.PhysicsProcess.*;
|
|
||||||
import mindustry.gen.*;
|
import mindustry.gen.*;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
@ -11,7 +10,6 @@ abstract class HitboxComp implements Posc, QuadTreeObject{
|
|||||||
@Import float x, y;
|
@Import float x, y;
|
||||||
|
|
||||||
transient float lastX, lastY, hitSize;
|
transient float lastX, lastY, hitSize;
|
||||||
transient PhysicRef body;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void update(){
|
public void update(){
|
||||||
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
18
core/src/mindustry/entities/def/PhysicsComp.java
Normal file
18
core/src/mindustry/entities/def/PhysicsComp.java
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -18,7 +18,7 @@ import mindustry.world.blocks.environment.*;
|
|||||||
import static mindustry.Vars.*;
|
import static mindustry.Vars.*;
|
||||||
|
|
||||||
@Component
|
@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;
|
@Import float x, y, rotation, elevation, maxHealth;
|
||||||
|
|
||||||
private UnitController controller;
|
private UnitController controller;
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
org.gradle.daemon=true
|
org.gradle.daemon=true
|
||||||
org.gradle.jvmargs=-Xms256m -Xmx1024m
|
org.gradle.jvmargs=-Xms256m -Xmx1024m
|
||||||
archash=ae2bb7c28ac26b26e549cdc42b0fb5ecfaa0f916
|
archash=00ccb60840294db20d561feda12fa739edaaf601
|
||||||
|
Reference in New Issue
Block a user