1
0
mirror of https://github.com/Anuken/Mindustry.git synced 2024-11-10 15:05:23 +03:00

Better physics system

This commit is contained in:
Anuken 2020-09-24 15:39:34 -04:00
parent 1682e3b996
commit 0dccbf8598
8 changed files with 102 additions and 135 deletions

View File

@ -200,7 +200,6 @@ project(":desktop"){
dependencies{ dependencies{
implementation project(":core") implementation project(":core")
implementation arcModule("natives:natives-box2d-desktop")
implementation arcModule("natives:natives-desktop") implementation arcModule("natives:natives-desktop")
implementation arcModule("natives:natives-freetype-desktop") implementation arcModule("natives:natives-freetype-desktop")
implementation 'com.github.MinnDevelopment:java-discord-rpc:v2.0.1' implementation 'com.github.MinnDevelopment:java-discord-rpc:v2.0.1'
@ -239,7 +238,6 @@ project(":ios"){
implementation arcModule("natives:natives-ios") implementation arcModule("natives:natives-ios")
implementation arcModule("natives:natives-freetype-ios") implementation arcModule("natives:natives-freetype-ios")
implementation arcModule("natives:natives-box2d-ios")
implementation arcModule("backends:backend-robovm") implementation arcModule("backends:backend-robovm")
compileOnly project(":annotations") compileOnly project(":annotations")
@ -282,7 +280,6 @@ project(":core"){
api "org.lz4:lz4-java:1.4.1" api "org.lz4:lz4-java:1.4.1"
api arcModule("arc-core") api arcModule("arc-core")
api arcModule("extensions:freetype") api arcModule("extensions:freetype")
api arcModule("extensions:box2d")
api arcModule("extensions:g3d") api arcModule("extensions:g3d")
api arcModule("extensions:fx") api arcModule("extensions:fx")
api arcModule("extensions:arcnet") api arcModule("extensions:arcnet")
@ -299,7 +296,6 @@ project(":server"){
dependencies{ dependencies{
implementation project(":core") implementation project(":core")
implementation arcModule("natives:natives-box2d-desktop")
implementation arcModule("backends:backend-headless") implementation arcModule("backends:backend-headless")
} }
} }
@ -312,7 +308,6 @@ project(":tests"){
testImplementation "org.junit.jupiter:junit-jupiter-params:5.3.1" testImplementation "org.junit.jupiter:junit-jupiter-params:5.3.1"
testImplementation "org.junit.jupiter:junit-jupiter-api:5.3.1" testImplementation "org.junit.jupiter:junit-jupiter-api:5.3.1"
testImplementation arcModule("backends:backend-headless") testImplementation arcModule("backends:backend-headless")
testImplementation arcModule("natives:natives-box2d-desktop")
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:5.3.1" testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:5.3.1"
} }
@ -334,7 +329,6 @@ project(":tools"){
implementation arcModule("natives:natives-desktop") implementation arcModule("natives:natives-desktop")
implementation arcModule("natives:natives-freetype-desktop") implementation arcModule("natives:natives-freetype-desktop")
implementation arcModule("natives:natives-box2d-desktop")
implementation arcModule("backends:backend-headless") implementation arcModule("backends:backend-headless")
} }
} }

View File

@ -73,7 +73,7 @@ public class FormationAI extends AIController implements FormationMember{
//TODO return formation size //TODO return formation size
//eturn ((Commanderc)unit).formation(). //eturn ((Commanderc)unit).formation().
} }
return unit.hitSize * 1f; return unit.hitSize * 0.65f;
} }
@Override @Override

View File

@ -1,31 +1,19 @@
package mindustry.async; package mindustry.async;
import arc.box2d.*; import arc.math.*;
import arc.box2d.BodyDef.*;
import arc.math.geom.*; import arc.math.geom.*;
import arc.math.geom.QuadTree.*;
import arc.struct.*; import arc.struct.*;
import mindustry.*;
import mindustry.entities.*; import mindustry.entities.*;
import mindustry.async.PhysicsProcess.PhysicsWorld.*;
import mindustry.gen.*; import mindustry.gen.*;
public class PhysicsProcess implements AsyncProcess{ public class PhysicsProcess implements AsyncProcess{
private Physics physics; private PhysicsWorld physics;
private Seq<PhysicRef> refs = new Seq<>(false); private Seq<PhysicRef> refs = new Seq<>(false);
private BodyDef def; //currently only enabled for units
private EntityGroup<? extends Physicsc> group = Groups.unit;
private EntityGroup<? extends Physicsc> group;
private Filter flying = new Filter(){{
maskBits = categoryBits = 2;
}}, ground = new Filter(){{
maskBits = categoryBits = 1;
}};
public PhysicsProcess(){
def = new BodyDef();
def.type = BodyType.dynamicBody;
//currently only enabled for units
group = Groups.unit;
}
@Override @Override
public void begin(){ public void begin(){
@ -34,7 +22,7 @@ public class PhysicsProcess implements AsyncProcess{
//remove stale entities //remove stale entities
refs.removeAll(ref -> { refs.removeAll(ref -> {
if(!ref.entity.isAdded()){ if(!ref.entity.isAdded()){
physics.destroyBody(ref.body); physics.remove(ref.body);
ref.entity.physref(null); ref.entity.physref(null);
return true; return true;
} }
@ -44,37 +32,29 @@ public class PhysicsProcess implements AsyncProcess{
//find entities without bodies and assign them //find entities without bodies and assign them
for(Physicsc entity : group){ for(Physicsc entity : group){
boolean grounded = entity.isGrounded(); boolean grounded = entity.isGrounded();
int bits = grounded ? ground.maskBits : flying.maskBits;
if(entity.physref() == null){ if(entity.physref() == null){
//add bodies to entities that have none PhysicsBody body = new PhysicsBody();
FixtureDef fd = new FixtureDef(); body.x = entity.x();
fd.shape = new CircleShape(entity.hitSize() / 2f); body.y = entity.y();
fd.density = 5f; body.mass = entity.mass();
fd.restitution = 0.0f; body.radius = entity.hitSize() / 2f;
fd.filter.maskBits = fd.filter.categoryBits = (grounded ? ground : flying).maskBits; body.flag = grounded ? 1 : 0;
def.position.set(entity);
Body body = physics.createBody(def);
body.createFixture(fd);
PhysicRef ref = new PhysicRef(entity, body); PhysicRef ref = new PhysicRef(entity, body);
refs.add(ref); refs.add(ref);
entity.physref(ref); entity.physref(ref);
physics.add(body);
} }
//save last position //save last position
PhysicRef ref = entity.physref(); PhysicRef ref = entity.physref();
if(ref.body.getFixtureList().any() && ref.body.getFixtureList().first().getFilterData().categoryBits != bits){ ref.body.flag = grounded ? 1 : 0;
//set correct filter ref.x = entity.x();
ref.body.getFixtureList().first().setFilterData(grounded ? ground : flying); ref.y = entity.y();
}
ref.velocity.set(entity.deltaX(), entity.deltaY());
ref.position.set(entity);
} }
} }
@ -85,26 +65,11 @@ public class PhysicsProcess implements AsyncProcess{
//get last position vectors before step //get last position vectors before step
for(PhysicRef ref : refs){ for(PhysicRef ref : refs){
//force set target position //force set target position
ref.body.setPosition(ref.position.x, ref.position.y); ref.body.x = ref.x;
ref.body.y = ref.y;
//save last position for delta
ref.lastPosition.set(ref.body.getPosition());
//write velocity
ref.body.setLinearVelocity(ref.velocity);
ref.lastVelocity.set(ref.velocity);
} }
physics.step(1f/45f, 5, 8); physics.update();
//get delta vectors
for(PhysicRef ref : refs){
//get delta vector
ref.delta.set(ref.body.getPosition()).sub(ref.lastPosition);
ref.velocity.set(ref.body.getLinearVelocity());
}
} }
@Override @Override
@ -115,13 +80,8 @@ public class PhysicsProcess implements AsyncProcess{
for(PhysicRef ref : refs){ for(PhysicRef ref : refs){
Physicsc entity = ref.entity; Physicsc entity = ref.entity;
entity.move(ref.delta.x, ref.delta.y); //move by delta
entity.move(ref.body.x - ref.x, ref.body.y - ref.y);
//save last position
ref.position.set(entity);
//add delta velocity - this doesn't work very well yet
//entity.vel().add(ref.velocity).sub(ref.lastVelocity);
} }
} }
@ -129,7 +89,6 @@ public class PhysicsProcess implements AsyncProcess{
public void reset(){ public void reset(){
if(physics != null){ if(physics != null){
refs.clear(); refs.clear();
physics.dispose();
physics = null; physics = null;
} }
} }
@ -138,17 +97,83 @@ public class PhysicsProcess implements AsyncProcess{
public void init(){ public void init(){
reset(); reset();
physics = new Physics(new Vec2(), true); physics = new PhysicsWorld(Vars.world.getQuadBounds(new Rect()));
} }
public static class PhysicRef{ public static class PhysicRef{
public Physicsc entity; public Physicsc entity;
public Body body; public PhysicsBody body;
public Vec2 lastPosition = new Vec2(), delta = new Vec2(), velocity = new Vec2(), lastVelocity = new Vec2(), position = new Vec2(); public float x, y;
public PhysicRef(Physicsc entity, Body body){ public PhysicRef(Physicsc entity, PhysicsBody body){
this.entity = entity; this.entity = entity;
this.body = body; this.body = body;
} }
} }
//world for simulating physics in a different thread
public static class PhysicsWorld{
//how much to soften movement by
private static final float scl = 1.25f;
private final QuadTree<PhysicsBody> tree;
private final Seq<PhysicsBody> bodies = new Seq<>(false, 16, PhysicsBody.class);
private final Rect rect = new Rect();
private final Vec2 vec = new Vec2();
public PhysicsWorld(Rect bounds){
tree = new QuadTree<>(new Rect(bounds));
}
public void add(PhysicsBody body){
bodies.add(body);
}
public void remove(PhysicsBody body){
bodies.remove(body);
}
public void update(){
tree.clear();
for(int i = 0; i < bodies.size; i++){
PhysicsBody body = bodies.items[i];
body.collided = false;
tree.insert(body);
}
for(int i = 0; i < bodies.size; i++){
PhysicsBody body = bodies.items[i];
body.hitbox(rect);
tree.intersect(rect, other -> {
if(other.flag != body.flag || other == body || other.collided) return;
float rs = body.radius + other.radius;
float dst = Mathf.dst(body.x, body.y, other.x, other.y);
if(dst < rs){
vec.set(body.x - other.x, body.y - other.y).setLength(rs - dst);
float ms = body.mass + other.mass;
float m1 = other.mass / ms, m2 = body.mass / ms;
body.x += vec.x * m1 / scl;
body.y += vec.y * m1 / scl;
other.x -= vec.x * m2 / scl;
other.y -= vec.y * m2 / scl;
}
});
body.collided = true;
}
}
public static class PhysicsBody implements QuadTreeObject{
public float x, y, radius, mass;
public int flag = 0;
public boolean collided = false;
@Override
public void hitbox(Rect out){
out.setCentered(x, y, radius * 2, radius * 2);
}
}
}
} }

View File

@ -304,7 +304,7 @@ public class UnitTypes implements ContentList{
mineTier = 2; mineTier = 2;
mineSpeed = 5f; mineSpeed = 5f;
commandLimit = 15; commandLimit = 8;
abilities.add(new ShieldFieldAbility(20f, 40f, 60f * 5, 60f)); abilities.add(new ShieldFieldAbility(20f, 40f, 60f * 5, 60f));
ammoType = AmmoTypes.power; ammoType = AmmoTypes.power;
@ -347,7 +347,7 @@ public class UnitTypes implements ContentList{
armor = 9f; armor = 9f;
landShake = 2f; landShake = 2f;
commandLimit = 18; commandLimit = 10;
mechFrontSway = 0.55f; mechFrontSway = 0.55f;
ammoType = AmmoTypes.power; ammoType = AmmoTypes.power;
@ -402,7 +402,7 @@ public class UnitTypes implements ContentList{
canBoost = true; canBoost = true;
landShake = 4f; landShake = 4f;
commandLimit = 20; commandLimit = 8;
weapons.add(new Weapon("vela-weapon"){{ weapons.add(new Weapon("vela-weapon"){{
mirror = false; mirror = false;
@ -452,7 +452,7 @@ public class UnitTypes implements ContentList{
landShake = 1.5f; landShake = 1.5f;
rotateSpeed = 1.5f; rotateSpeed = 1.5f;
commandLimit = 20; commandLimit = 8;
legCount = 4; legCount = 4;
legLength = 14f; legLength = 14f;
@ -1312,7 +1312,7 @@ public class UnitTypes implements ContentList{
payloadCapacity = (5.3f * 5.3f) * tilePayload; payloadCapacity = (5.3f * 5.3f) * tilePayload;
buildSpeed = 4f; buildSpeed = 4f;
drawShields = false; drawShields = false;
commandLimit = 25; commandLimit = 6;
abilities.add(new ForceFieldAbility(140f, 4f, 7000f, 60f * 8), new HealFieldAbility(130f, 60f * 2, 140f)); abilities.add(new ForceFieldAbility(140f, 4f, 7000f, 60f * 8), new HealFieldAbility(130f, 60f * 2, 140f));
}}; }};

View File

@ -1,48 +0,0 @@
package mindustry.entities;
import arc.math.geom.*;
import arc.math.geom.QuadTree.*;
import arc.struct.*;
public class PhysicsWorld{
private QuadTree<PhysicsBody> tree;
private Seq<PhysicsBody> bodies = new Seq<>(false, 16, PhysicsBody.class);
private Rect rect = new Rect();
public PhysicsWorld(Rect bounds){
tree = new QuadTree<>(new Rect(bounds));
}
public void add(PhysicsBody body){
bodies.add(body);
}
public void remove(PhysicsBody body){
bodies.remove(body);
}
public void update(){
tree.clear();
for(int i = 0; i < bodies.size; i++){
tree.insert(bodies.items[i]);
}
for(int i = 0; i < bodies.size; i++){
PhysicsBody body = bodies.items[i];
body.hitbox(rect);
tree.intersect(rect, other -> {
});
}
}
public static class PhysicsBody implements QuadTreeObject{
public float x, y, xv, yv, radius, mass;
public int flag = 0;
@Override
public void hitbox(Rect out){
out.setCentered(x, y, radius * 2, radius * 2);
}
}
}

View File

@ -73,7 +73,7 @@ abstract class CommanderComp implements Unitc{
void command(Formation formation, Seq<Unit> units){ void command(Formation formation, Seq<Unit> units){
clearCommand(); clearCommand();
float spacing = hitSize() * 1f; float spacing = hitSize() * 0.65f;
minFormationSpeed = type().speed; minFormationSpeed = type().speed;
controlling.addAll(units); controlling.addAll(units);

View File

@ -317,7 +317,7 @@ public class UnitType extends UnlockableContent{
public void draw(Unit unit){ public void draw(Unit unit){
Mechc mech = unit instanceof Mechc ? (Mechc)unit : null; Mechc mech = unit instanceof Mechc ? (Mechc)unit : null;
float z = unit.elevation > 0.5f ? (lowAltitude ? Layer.flyingUnitLow : Layer.flyingUnit) : groundLayer + Mathf.clamp(hitSize /4000f, 0, 0.01f); float z = unit.elevation > 0.5f ? (lowAltitude ? Layer.flyingUnitLow : Layer.flyingUnit) : groundLayer + Mathf.clamp(hitSize / 4000f, 0, 0.01f);
if(unit.controller().isBeingControlled(player.unit())){ if(unit.controller().isBeingControlled(player.unit())){
drawControl(unit); drawControl(unit);

View File

@ -33,7 +33,6 @@ if(!hasProperty("release")){
':Arc:extensions:recorder', ':Arc:extensions:recorder',
':Arc:extensions:arcnet', ':Arc:extensions:arcnet',
':Arc:extensions:packer', ':Arc:extensions:packer',
':Arc:extensions:box2d',
':Arc:extensions:g3d', ':Arc:extensions:g3d',
':Arc:extensions:fx', ':Arc:extensions:fx',
':Arc:natives', ':Arc:natives',
@ -43,9 +42,6 @@ if(!hasProperty("release")){
':Arc:natives:natives-freetype-desktop', ':Arc:natives:natives-freetype-desktop',
':Arc:natives:natives-freetype-android', ':Arc:natives:natives-freetype-android',
':Arc:natives:natives-freetype-ios', ':Arc:natives:natives-freetype-ios',
':Arc:natives:natives-box2d-desktop',
':Arc:natives:natives-box2d-android',
':Arc:natives:natives-box2d-ios',
':Arc:backends', ':Arc:backends',
':Arc:backends:backend-sdl', ':Arc:backends:backend-sdl',
':Arc:backends:backend-android', ':Arc:backends:backend-android',