mirror of
https://github.com/Anuken/Mindustry.git
synced 2025-02-13 12:16:53 +07:00
Physics
This commit is contained in:
parent
8de0ca7d6e
commit
273f4590df
@ -1,6 +1,8 @@
|
|||||||
package mindustry.async;
|
package mindustry.async;
|
||||||
|
|
||||||
|
import arc.*;
|
||||||
import arc.struct.*;
|
import arc.struct.*;
|
||||||
|
import mindustry.game.EventType.*;
|
||||||
|
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
|
|
||||||
@ -12,15 +14,28 @@ public class AsyncLogic{
|
|||||||
private Array<Future<?>> futures = new Array<>();
|
private Array<Future<?>> futures = new Array<>();
|
||||||
|
|
||||||
private ExecutorService executor = Executors.newFixedThreadPool(processes.size, r -> {
|
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.setDaemon(true);
|
||||||
thread.setUncaughtExceptionHandler((t, e) -> {
|
thread.setUncaughtExceptionHandler((t, e) -> Core.app.post(() -> { throw new RuntimeException(e); }));
|
||||||
e.printStackTrace();
|
|
||||||
//TODO crash!
|
|
||||||
});
|
|
||||||
return thread;
|
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(){
|
public void begin(){
|
||||||
//sync begin
|
//sync begin
|
||||||
for(AsyncProcess p : processes){
|
for(AsyncProcess p : processes){
|
||||||
@ -36,6 +51,15 @@ public class AsyncLogic{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void end(){
|
public void end(){
|
||||||
|
complete();
|
||||||
|
|
||||||
|
//sync end (flush data)
|
||||||
|
for(AsyncProcess p : processes){
|
||||||
|
p.end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void complete(){
|
||||||
//wait for all threads to stop processing
|
//wait for all threads to stop processing
|
||||||
for(Future future : futures){
|
for(Future future : futures){
|
||||||
try{
|
try{
|
||||||
@ -45,11 +69,7 @@ public class AsyncLogic{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//clear processed futures
|
||||||
futures.clear();
|
futures.clear();
|
||||||
|
|
||||||
//sync end (flush data)
|
|
||||||
for(AsyncProcess p : processes){
|
|
||||||
p.end();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,13 @@ package mindustry.async;
|
|||||||
|
|
||||||
public interface AsyncProcess{
|
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();
|
void begin();
|
||||||
|
|
||||||
/** Async. Called in a separate thread. */
|
/** Async. Called in a separate thread. */
|
||||||
|
@ -1,19 +1,161 @@
|
|||||||
package mindustry.async;
|
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{
|
public class PhysicsProcess implements AsyncProcess{
|
||||||
|
private Physics physics;
|
||||||
|
private Array<PhysicRef> refs = new Array<>(false);
|
||||||
|
private BodyDef def;
|
||||||
|
|
||||||
|
private EntityGroup<? extends Hitboxc> 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
|
@Override
|
||||||
public void begin(){
|
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
|
@Override
|
||||||
public void process(){
|
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
|
@Override
|
||||||
public void end(){
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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{
|
class gunit{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
package mindustry.entities.def;
|
package mindustry.entities.def;
|
||||||
|
|
||||||
import arc.math.geom.*;
|
|
||||||
import arc.math.geom.QuadTree.*;
|
import arc.math.geom.QuadTree.*;
|
||||||
|
import arc.math.geom.*;
|
||||||
import mindustry.annotations.Annotations.*;
|
import mindustry.annotations.Annotations.*;
|
||||||
|
import mindustry.async.PhysicsProcess.*;
|
||||||
import mindustry.gen.*;
|
import mindustry.gen.*;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
@ -10,6 +11,7 @@ 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(){
|
||||||
|
@ -74,23 +74,6 @@ abstract class UnitComp implements Healthc, Velc, Statusc, Teamc, Itemsc, Hitbox
|
|||||||
controller(controller);
|
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
|
@Override
|
||||||
public void type(UnitType type){
|
public void type(UnitType type){
|
||||||
this.type = type;
|
this.type = type;
|
||||||
|
@ -3,7 +3,6 @@ package mindustry.entities.units;
|
|||||||
import arc.math.*;
|
import arc.math.*;
|
||||||
import arc.math.geom.*;
|
import arc.math.geom.*;
|
||||||
import arc.util.*;
|
import arc.util.*;
|
||||||
import mindustry.entities.*;
|
|
||||||
import mindustry.gen.*;
|
import mindustry.gen.*;
|
||||||
import mindustry.world.*;
|
import mindustry.world.*;
|
||||||
import mindustry.world.meta.*;
|
import mindustry.world.meta.*;
|
||||||
@ -37,10 +36,10 @@ public class AIController implements UnitController{
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void targetClosest(){
|
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()));
|
//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){
|
//if(newTarget != null){
|
||||||
target = newTarget;
|
// target = newTarget;
|
||||||
}
|
//}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -92,7 +92,7 @@ public abstract class SaveVersion extends SaveFileReader{
|
|||||||
Core.camera.position.set(Tmp.v1);
|
Core.camera.position.set(Tmp.v1);
|
||||||
player.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", "<none>"));
|
||||||
}
|
}
|
||||||
|
|
||||||
Map worldmap = maps.byName(map.get("mapname", "\\\\\\"));
|
Map worldmap = maps.byName(map.get("mapname", "\\\\\\"));
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
org.gradle.daemon=true
|
org.gradle.daemon=true
|
||||||
org.gradle.jvmargs=-Xms256m -Xmx1024m
|
org.gradle.jvmargs=-Xms256m -Xmx1024m
|
||||||
archash=7ef1821dfd7725c97d0dbcc6a6da5c96b1fbb4b2
|
archash=3533846d0009cda47c9416c4cb105b8e79518099
|
||||||
|
Loading…
Reference in New Issue
Block a user