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:
parent
1682e3b996
commit
0dccbf8598
@ -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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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));
|
||||||
}};
|
}};
|
||||||
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
@ -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',
|
||||||
|
Loading…
Reference in New Issue
Block a user