mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-03-26 22:49:01 +08:00
[libgdx] Physics WIP.
This commit is contained in:
parent
f07d1aef43
commit
4a869294ac
@ -29,6 +29,7 @@
|
|||||||
|
|
||||||
package com.esotericsoftware.spine;
|
package com.esotericsoftware.spine;
|
||||||
|
|
||||||
|
import com.badlogic.gdx.math.Vector2;
|
||||||
import com.badlogic.gdx.utils.Array;
|
import com.badlogic.gdx.utils.Array;
|
||||||
|
|
||||||
import com.esotericsoftware.spine.PhysicsConstraintData.Node;
|
import com.esotericsoftware.spine.PhysicsConstraintData.Node;
|
||||||
@ -47,10 +48,15 @@ public class PhysicsConstraint implements Updatable {
|
|||||||
|
|
||||||
boolean active;
|
boolean active;
|
||||||
|
|
||||||
|
private final Skeleton skeleton;
|
||||||
|
float remaining, lastTime;
|
||||||
|
final Vector2 temp = new Vector2();
|
||||||
|
|
||||||
public PhysicsConstraint (PhysicsConstraintData data, Skeleton skeleton) {
|
public PhysicsConstraint (PhysicsConstraintData data, Skeleton skeleton) {
|
||||||
if (data == null) throw new IllegalArgumentException("data cannot be null.");
|
if (data == null) throw new IllegalArgumentException("data cannot be null.");
|
||||||
if (skeleton == null) throw new IllegalArgumentException("skeleton cannot be null.");
|
if (skeleton == null) throw new IllegalArgumentException("skeleton cannot be null.");
|
||||||
this.data = data;
|
this.data = data;
|
||||||
|
this.skeleton = skeleton;
|
||||||
|
|
||||||
nodes = new Array(data.nodes.size);
|
nodes = new Array(data.nodes.size);
|
||||||
for (NodeData nodeData : data.nodes)
|
for (NodeData nodeData : data.nodes)
|
||||||
@ -69,10 +75,10 @@ public class PhysicsConstraint implements Updatable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Copy constructor. */
|
/** Copy constructor. */
|
||||||
public PhysicsConstraint (PhysicsConstraint constraint, Skeleton skeleton) {
|
public PhysicsConstraint (PhysicsConstraint constraint) {
|
||||||
if (constraint == null) throw new IllegalArgumentException("constraint cannot be null.");
|
if (constraint == null) throw new IllegalArgumentException("constraint cannot be null.");
|
||||||
if (skeleton == null) throw new IllegalArgumentException("skeleton cannot be null.");
|
|
||||||
data = constraint.data;
|
data = constraint.data;
|
||||||
|
skeleton = constraint.skeleton;
|
||||||
|
|
||||||
nodes = new Array(constraint.nodes.size);
|
nodes = new Array(constraint.nodes.size);
|
||||||
for (Node node : constraint.nodes)
|
for (Node node : constraint.nodes)
|
||||||
@ -91,6 +97,9 @@ public class PhysicsConstraint implements Updatable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void setToSetupPose () {
|
public void setToSetupPose () {
|
||||||
|
remaining = 0;
|
||||||
|
lastTime = skeleton.time;
|
||||||
|
|
||||||
Object[] nodes = this.nodes.items;
|
Object[] nodes = this.nodes.items;
|
||||||
for (int i = 0, n = this.nodes.size; i < n; i++)
|
for (int i = 0, n = this.nodes.size; i < n; i++)
|
||||||
((Node)nodes[i]).setToSetupPose();
|
((Node)nodes[i]).setToSetupPose();
|
||||||
@ -110,7 +119,29 @@ public class PhysicsConstraint implements Updatable {
|
|||||||
|
|
||||||
/** Applies the constraint to the constrained bones. */
|
/** Applies the constraint to the constrained bones. */
|
||||||
public void update () {
|
public void update () {
|
||||||
// BOZO! - Physics.
|
Object[] nodes = this.nodes.items;
|
||||||
|
int nodeCount = this.nodes.size;
|
||||||
|
Vector2 temp = this.temp;
|
||||||
|
for (int i = 0; i < nodeCount; i++) {
|
||||||
|
Node node = (Node)nodes[i];
|
||||||
|
if (node.parentBone == null) continue;
|
||||||
|
node.parentBone.localToWorld(temp.set(node.data.x, node.data.y));
|
||||||
|
node.x = temp.x;
|
||||||
|
node.y = temp.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object[] springs = this.springs.items;
|
||||||
|
int springCount = this.springs.size;
|
||||||
|
|
||||||
|
remaining += lastTime - skeleton.time;
|
||||||
|
lastTime = skeleton.time;
|
||||||
|
while (remaining > 0.016f) {
|
||||||
|
remaining -= 0.016f;
|
||||||
|
for (int i = 0; i < springCount; i++)
|
||||||
|
((Spring)springs[i]).update();
|
||||||
|
for (int i = 0; i < nodeCount; i++)
|
||||||
|
((Node)nodes[i]).update(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Array<Node> getNodes () {
|
public Array<Node> getNodes () {
|
||||||
|
|||||||
@ -31,6 +31,7 @@ package com.esotericsoftware.spine;
|
|||||||
|
|
||||||
import static com.esotericsoftware.spine.utils.SpineUtils.*;
|
import static com.esotericsoftware.spine.utils.SpineUtils.*;
|
||||||
|
|
||||||
|
import com.badlogic.gdx.math.Vector2;
|
||||||
import com.badlogic.gdx.utils.Array;
|
import com.badlogic.gdx.utils.Array;
|
||||||
import com.badlogic.gdx.utils.Null;
|
import com.badlogic.gdx.utils.Null;
|
||||||
|
|
||||||
@ -111,9 +112,13 @@ public class PhysicsConstraintData extends ConstraintData {
|
|||||||
|
|
||||||
static public class Node {
|
static public class Node {
|
||||||
public final NodeData data;
|
public final NodeData data;
|
||||||
public final @Null Bone parentBone;
|
public @Null Bone parentBone;
|
||||||
public final Bone[] bones;
|
public Bone[] bones;
|
||||||
public float x, y, px, py, ax, ay;
|
public float x, y, vx, vy, ax, ay;
|
||||||
|
|
||||||
|
Node (NodeData data) { // Editor.
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
public Node (NodeData data, Skeleton skeleton) {
|
public Node (NodeData data, Skeleton skeleton) {
|
||||||
this.data = data;
|
this.data = data;
|
||||||
@ -134,8 +139,8 @@ public class PhysicsConstraintData extends ConstraintData {
|
|||||||
arraycopy(node.bones, 0, bones, 0, bones.length);
|
arraycopy(node.bones, 0, bones, 0, bones.length);
|
||||||
x = node.x;
|
x = node.x;
|
||||||
y = node.y;
|
y = node.y;
|
||||||
px = node.px;
|
vx = node.vx;
|
||||||
py = node.py;
|
vy = node.vy;
|
||||||
ax = node.ax;
|
ax = node.ax;
|
||||||
ay = node.ay;
|
ay = node.ay;
|
||||||
}
|
}
|
||||||
@ -143,11 +148,30 @@ public class PhysicsConstraintData extends ConstraintData {
|
|||||||
public void setToSetupPose () {
|
public void setToSetupPose () {
|
||||||
x = data.x;
|
x = data.x;
|
||||||
y = data.y;
|
y = data.y;
|
||||||
px = x;
|
vx = 0;
|
||||||
py = y;
|
vy = 0;
|
||||||
ax = 0;
|
ax = 0;
|
||||||
ay = 0;
|
ay = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void update (PhysicsConstraint constraint) {
|
||||||
|
if (parentBone != null) {
|
||||||
|
vx = 0;
|
||||||
|
vy = 0;
|
||||||
|
ax = 0;
|
||||||
|
ay = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
float friction = 0.98f;
|
||||||
|
vx += ax - constraint.wind / friction;
|
||||||
|
vy += ay - constraint.gravity / friction;
|
||||||
|
ax = 0;
|
||||||
|
ay = 0;
|
||||||
|
x += vx;
|
||||||
|
y += vy;
|
||||||
|
vx *= friction;
|
||||||
|
vy *= friction;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static public class SpringData {
|
static public class SpringData {
|
||||||
@ -159,10 +183,14 @@ public class PhysicsConstraintData extends ConstraintData {
|
|||||||
|
|
||||||
static public class Spring {
|
static public class Spring {
|
||||||
public final SpringData data;
|
public final SpringData data;
|
||||||
public final Node node1, node2;
|
public Node node1, node2;
|
||||||
public final Bone[] bones;
|
public Bone[] bones;
|
||||||
public float length, strength, damping;
|
public float length, strength, damping;
|
||||||
|
|
||||||
|
Spring (SpringData data) { // Editor.
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
public Spring (SpringData data, PhysicsConstraint constraint, Skeleton skeleton) {
|
public Spring (SpringData data, PhysicsConstraint constraint, Skeleton skeleton) {
|
||||||
this.data = data;
|
this.data = data;
|
||||||
|
|
||||||
@ -192,5 +220,34 @@ public class PhysicsConstraintData extends ConstraintData {
|
|||||||
strength = data.strength;
|
strength = data.strength;
|
||||||
damping = data.damping;
|
damping = data.damping;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void update () {
|
||||||
|
float strength = 1f;
|
||||||
|
|
||||||
|
float x = node2.x - node1.x, y = node2.y - node1.y;
|
||||||
|
float d = (float)Math.sqrt(Math.max(x * x + y * y, 0.00001f));
|
||||||
|
if (data.rope && d <= length) return;
|
||||||
|
|
||||||
|
Vector2 unit_vector = new Vector2(x / d, y / d);
|
||||||
|
float distance_error = unit_vector.dot(x, y) - length;
|
||||||
|
float distance_impulse = 0.33f * strength * distance_error;
|
||||||
|
|
||||||
|
float vx = node2.vx - node1.vx, vy = node2.vy - node1.vy;
|
||||||
|
float velocity_error = unit_vector.dot(vx, vy);
|
||||||
|
float velocity_impulse = 0.2f * velocity_error;
|
||||||
|
|
||||||
|
float impulse = -(distance_impulse + velocity_impulse);
|
||||||
|
Vector2 f = unit_vector.scl(impulse);
|
||||||
|
|
||||||
|
node1.ax -= f.x - vx * damping;
|
||||||
|
node1.ay -= f.y - vy * damping;
|
||||||
|
node2.ax += f.x - vx * damping;
|
||||||
|
node2.ay += f.y - vy * damping;
|
||||||
|
|
||||||
|
if (!data.stretch && node2.parentBone == null) {
|
||||||
|
node2.x = node1.x + length * x / d;
|
||||||
|
node2.y = node1.y + length * y / d;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -60,8 +60,9 @@ public class Skeleton {
|
|||||||
final Array<Updatable> updateCache = new Array();
|
final Array<Updatable> updateCache = new Array();
|
||||||
@Null Skin skin;
|
@Null Skin skin;
|
||||||
final Color color;
|
final Color color;
|
||||||
float scaleX = 1, scaleY = 1;
|
|
||||||
float x, y;
|
float x, y;
|
||||||
|
float scaleX = 1, scaleY = 1;
|
||||||
|
float time;
|
||||||
|
|
||||||
public Skeleton (SkeletonData data) {
|
public Skeleton (SkeletonData data) {
|
||||||
if (data == null) throw new IllegalArgumentException("data cannot be null.");
|
if (data == null) throw new IllegalArgumentException("data cannot be null.");
|
||||||
@ -153,12 +154,15 @@ public class Skeleton {
|
|||||||
|
|
||||||
physicsConstraints = new Array(skeleton.physicsConstraints.size);
|
physicsConstraints = new Array(skeleton.physicsConstraints.size);
|
||||||
for (PhysicsConstraint physicsConstraint : skeleton.physicsConstraints)
|
for (PhysicsConstraint physicsConstraint : skeleton.physicsConstraints)
|
||||||
physicsConstraints.add(new PhysicsConstraint(physicsConstraint, this));
|
physicsConstraints.add(new PhysicsConstraint(physicsConstraint));
|
||||||
|
|
||||||
skin = skeleton.skin;
|
skin = skeleton.skin;
|
||||||
color = new Color(skeleton.color);
|
color = new Color(skeleton.color);
|
||||||
|
x = skeleton.x;
|
||||||
|
y = skeleton.y;
|
||||||
scaleX = skeleton.scaleX;
|
scaleX = skeleton.scaleX;
|
||||||
scaleY = skeleton.scaleY;
|
scaleY = skeleton.scaleY;
|
||||||
|
time = skeleton.time;
|
||||||
|
|
||||||
updateCache();
|
updateCache();
|
||||||
}
|
}
|
||||||
@ -791,6 +795,22 @@ public class Skeleton {
|
|||||||
this.y = y;
|
this.y = y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Returns the skeleton's time. This is used for time-based manipulations, such as {@link PhysicsConstraint}.
|
||||||
|
* <p>
|
||||||
|
* See {@link #update(float)}. */
|
||||||
|
public float getTime () {
|
||||||
|
return time;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTime (float time) {
|
||||||
|
this.time = time;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Increments the skeleton's {@link #time}. */
|
||||||
|
public void update (float delta) {
|
||||||
|
time += delta;
|
||||||
|
}
|
||||||
|
|
||||||
public String toString () {
|
public String toString () {
|
||||||
return data.name != null ? data.name : super.toString();
|
return data.name != null ? data.name : super.toString();
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user