mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-02-09 08:38:43 +08:00
[libgdx] Physics update.
This commit is contained in:
parent
2dd17f4228
commit
1e820253d1
@ -29,12 +29,13 @@
|
||||
|
||||
package com.esotericsoftware.spine;
|
||||
|
||||
import static com.esotericsoftware.spine.utils.SpineUtils.*;
|
||||
|
||||
import com.badlogic.gdx.math.Vector2;
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
import com.badlogic.gdx.utils.Null;
|
||||
|
||||
import com.esotericsoftware.spine.PhysicsConstraintData.Node;
|
||||
import com.esotericsoftware.spine.PhysicsConstraintData.NodeData;
|
||||
import com.esotericsoftware.spine.PhysicsConstraintData.Spring;
|
||||
import com.esotericsoftware.spine.PhysicsConstraintData.SpringData;
|
||||
|
||||
/** Stores the current pose for a physics constraint. A physics constraint applies physics to bones.
|
||||
@ -44,7 +45,7 @@ public class PhysicsConstraint implements Updatable {
|
||||
final PhysicsConstraintData data;
|
||||
final Array<Node> nodes;
|
||||
final Array<Spring> springs;
|
||||
float mix, length, strength, damping, gravity, wind;
|
||||
float friction, gravity, wind, length, stiffness, damping, mix;
|
||||
|
||||
boolean active;
|
||||
|
||||
@ -66,12 +67,13 @@ public class PhysicsConstraint implements Updatable {
|
||||
for (SpringData springData : data.springs)
|
||||
springs.add(new Spring(springData, this, skeleton));
|
||||
|
||||
mix = data.mix;
|
||||
length = data.length;
|
||||
strength = data.strength;
|
||||
damping = data.damping;
|
||||
friction = data.friction;
|
||||
gravity = data.gravity;
|
||||
wind = data.wind;
|
||||
length = data.length;
|
||||
stiffness = data.stiffness;
|
||||
damping = data.damping;
|
||||
mix = data.mix;
|
||||
}
|
||||
|
||||
/** Copy constructor. */
|
||||
@ -88,12 +90,13 @@ public class PhysicsConstraint implements Updatable {
|
||||
for (Spring spring : constraint.springs)
|
||||
springs.add(new Spring(spring, this));
|
||||
|
||||
mix = constraint.mix;
|
||||
length = constraint.length;
|
||||
strength = constraint.strength;
|
||||
damping = constraint.damping;
|
||||
friction = constraint.friction;
|
||||
gravity = constraint.gravity;
|
||||
wind = constraint.wind;
|
||||
length = constraint.length;
|
||||
stiffness = constraint.stiffness;
|
||||
damping = constraint.damping;
|
||||
mix = constraint.mix;
|
||||
}
|
||||
|
||||
public void setToSetupPose () {
|
||||
@ -109,12 +112,13 @@ public class PhysicsConstraint implements Updatable {
|
||||
((Spring)springs[i]).setToSetupPose();
|
||||
|
||||
PhysicsConstraintData data = this.data;
|
||||
mix = data.mix;
|
||||
length = data.length;
|
||||
strength = data.strength;
|
||||
damping = data.damping;
|
||||
friction = data.friction;
|
||||
gravity = data.gravity;
|
||||
wind = data.wind;
|
||||
length = data.length;
|
||||
stiffness = data.stiffness;
|
||||
damping = data.damping;
|
||||
mix = data.mix;
|
||||
}
|
||||
|
||||
/** Applies the constraint to the constrained bones. */
|
||||
@ -177,37 +181,12 @@ public class PhysicsConstraint implements Updatable {
|
||||
return springs;
|
||||
}
|
||||
|
||||
/** A percentage (0-1) that controls the mix between the constrained and unconstrained poses. */
|
||||
public float getMix () {
|
||||
return mix;
|
||||
public float getFriction () {
|
||||
return friction;
|
||||
}
|
||||
|
||||
public void setMix (float mix) {
|
||||
this.mix = mix;
|
||||
}
|
||||
|
||||
public float getLength () {
|
||||
return length;
|
||||
}
|
||||
|
||||
public void setLength (float length) {
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
public float getStrength () {
|
||||
return strength;
|
||||
}
|
||||
|
||||
public void setStrength (float strength) {
|
||||
this.strength = strength;
|
||||
}
|
||||
|
||||
public float getDamping () {
|
||||
return damping;
|
||||
}
|
||||
|
||||
public void setDamping (float damping) {
|
||||
this.damping = damping;
|
||||
public void setFriction (float friction) {
|
||||
this.friction = friction;
|
||||
}
|
||||
|
||||
public float getGravity () {
|
||||
@ -226,6 +205,39 @@ public class PhysicsConstraint implements Updatable {
|
||||
this.wind = wind;
|
||||
}
|
||||
|
||||
public float getLength () {
|
||||
return length;
|
||||
}
|
||||
|
||||
public void setLength (float length) {
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
public float getStiffness () {
|
||||
return stiffness;
|
||||
}
|
||||
|
||||
public void setStiffness (float stiffness) {
|
||||
this.stiffness = stiffness;
|
||||
}
|
||||
|
||||
public float getDamping () {
|
||||
return damping;
|
||||
}
|
||||
|
||||
public void setDamping (float damping) {
|
||||
this.damping = damping;
|
||||
}
|
||||
|
||||
/** A percentage (0-1) that controls the mix between the constrained and unconstrained poses. */
|
||||
public float getMix () {
|
||||
return mix;
|
||||
}
|
||||
|
||||
public void setMix (float mix) {
|
||||
this.mix = mix;
|
||||
}
|
||||
|
||||
public boolean isActive () {
|
||||
return active;
|
||||
}
|
||||
@ -238,4 +250,117 @@ public class PhysicsConstraint implements Updatable {
|
||||
public String toString () {
|
||||
return data.name;
|
||||
}
|
||||
|
||||
static public class Node {
|
||||
public final NodeData data;
|
||||
public @Null Bone parentBone;
|
||||
public Bone[] bones;
|
||||
|
||||
/** Position relative to the parent bone, or world position if there is no parent bone. */
|
||||
public float x, y;
|
||||
|
||||
public float massInverse, vx, vy;
|
||||
|
||||
Node (NodeData data) { // Editor.
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public Node (NodeData data, Skeleton skeleton) {
|
||||
this.data = data;
|
||||
|
||||
parentBone = data.parentBone == -1 ? null : skeleton.bones.get(data.parentBone);
|
||||
|
||||
bones = new Bone[data.bones.length];
|
||||
for (int i = 0, n = bones.length; i < n; i++)
|
||||
bones[i] = skeleton.bones.get(data.bones[i]);
|
||||
|
||||
setToSetupPose();
|
||||
}
|
||||
|
||||
public Node (Node node) {
|
||||
this.data = node.data;
|
||||
parentBone = node.parentBone;
|
||||
bones = new Bone[node.bones.length];
|
||||
arraycopy(node.bones, 0, bones, 0, bones.length);
|
||||
x = node.x;
|
||||
y = node.y;
|
||||
vx = node.vx;
|
||||
vy = node.vy;
|
||||
}
|
||||
|
||||
public void setToSetupPose () {
|
||||
x = data.x;
|
||||
y = data.y;
|
||||
vx = 0;
|
||||
vy = 0;
|
||||
}
|
||||
|
||||
public void update (PhysicsConstraint constraint) {
|
||||
if (parentBone != null) {
|
||||
vx = 0;
|
||||
vy = 0;
|
||||
return;
|
||||
}
|
||||
x += vx;
|
||||
y += vy;
|
||||
vx = vx * constraint.friction + constraint.wind;
|
||||
vy = vy * constraint.friction - constraint.gravity;
|
||||
}
|
||||
}
|
||||
|
||||
static public class Spring {
|
||||
public final SpringData data;
|
||||
public Node node1, node2;
|
||||
public Bone[] bones;
|
||||
public float length, stiffness, damping;
|
||||
|
||||
Spring (SpringData data) { // Editor.
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public Spring (SpringData data, PhysicsConstraint constraint, Skeleton skeleton) {
|
||||
this.data = data;
|
||||
|
||||
node1 = constraint.nodes.get(data.node1);
|
||||
node2 = constraint.nodes.get(data.node2);
|
||||
|
||||
bones = new Bone[data.bones.length];
|
||||
for (int i = 0, n = bones.length; i < n; i++)
|
||||
bones[i] = skeleton.bones.get(data.bones[i]);
|
||||
|
||||
setToSetupPose();
|
||||
}
|
||||
|
||||
public Spring (Spring spring, PhysicsConstraint constraint) {
|
||||
this.data = spring.data;
|
||||
node1 = constraint.nodes.get(data.node1);
|
||||
node2 = constraint.nodes.get(data.node2);
|
||||
bones = new Bone[spring.bones.length];
|
||||
arraycopy(spring.bones, 0, bones, 0, bones.length);
|
||||
length = spring.length;
|
||||
stiffness = spring.stiffness;
|
||||
damping = spring.damping;
|
||||
}
|
||||
|
||||
public void setToSetupPose () {
|
||||
length = data.length;
|
||||
stiffness = data.stiffness;
|
||||
damping = data.damping;
|
||||
}
|
||||
|
||||
public void update () {
|
||||
float x = node2.x - node1.x, y = node2.y - node1.y, d = (float)Math.sqrt(Math.max(x * x + y * y, 0.00001f));
|
||||
if (data.rope && d <= length) return;
|
||||
x /= d;
|
||||
y /= d;
|
||||
float m1 = node1.massInverse, m2 = node2.massInverse;
|
||||
float i = (damping * (x * (node2.vx - node1.vx) + y * (node2.vy - node1.vy)) + stiffness * (d - length)) / (m1 + m2);
|
||||
x *= i;
|
||||
y *= i;
|
||||
node1.vx += x * m1;
|
||||
node1.vy += y * m1;
|
||||
node2.vx -= x * m2;
|
||||
node2.vy -= y * m2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -29,11 +29,7 @@
|
||||
|
||||
package com.esotericsoftware.spine;
|
||||
|
||||
import static com.esotericsoftware.spine.utils.SpineUtils.*;
|
||||
|
||||
import com.badlogic.gdx.math.Vector2;
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
import com.badlogic.gdx.utils.Null;
|
||||
|
||||
/** Stores the setup pose for a {@link PhysicsConstraint}.
|
||||
* <p>
|
||||
@ -41,7 +37,7 @@ import com.badlogic.gdx.utils.Null;
|
||||
public class PhysicsConstraintData extends ConstraintData {
|
||||
final Array<NodeData> nodes = new Array();
|
||||
final Array<SpringData> springs = new Array();
|
||||
float mix, length, strength, damping, gravity, wind;
|
||||
float friction, gravity, wind, length, stiffness, damping, mix;
|
||||
|
||||
public PhysicsConstraintData (String name) {
|
||||
super(name);
|
||||
@ -55,37 +51,12 @@ public class PhysicsConstraintData extends ConstraintData {
|
||||
return springs;
|
||||
}
|
||||
|
||||
/** A percentage (0-1) that controls the mix between the constrained and unconstrained poses. */
|
||||
public float getMix () {
|
||||
return mix;
|
||||
public float getFriction () {
|
||||
return friction;
|
||||
}
|
||||
|
||||
public void setMix (float mix) {
|
||||
this.mix = mix;
|
||||
}
|
||||
|
||||
public float getLength () {
|
||||
return length;
|
||||
}
|
||||
|
||||
public void setLength (float length) {
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
public float getStrength () {
|
||||
return strength;
|
||||
}
|
||||
|
||||
public void setStrength (float strength) {
|
||||
this.strength = strength;
|
||||
}
|
||||
|
||||
public float getDamping () {
|
||||
return damping;
|
||||
}
|
||||
|
||||
public void setDamping (float damping) {
|
||||
this.damping = damping;
|
||||
public void setFriction (float friction) {
|
||||
this.friction = friction;
|
||||
}
|
||||
|
||||
public float getGravity () {
|
||||
@ -104,150 +75,49 @@ public class PhysicsConstraintData extends ConstraintData {
|
||||
this.wind = wind;
|
||||
}
|
||||
|
||||
public float getLength () {
|
||||
return length;
|
||||
}
|
||||
|
||||
public void setLength (float length) {
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
public float getStiffness () {
|
||||
return stiffness;
|
||||
}
|
||||
|
||||
public void setStiffness (float stiffness) {
|
||||
this.stiffness = stiffness;
|
||||
}
|
||||
|
||||
public float getDamping () {
|
||||
return damping;
|
||||
}
|
||||
|
||||
public void setDamping (float damping) {
|
||||
this.damping = damping;
|
||||
}
|
||||
|
||||
/** A percentage (0-1) that controls the mix between the constrained and unconstrained poses. */
|
||||
public float getMix () {
|
||||
return mix;
|
||||
}
|
||||
|
||||
public void setMix (float mix) {
|
||||
this.mix = mix;
|
||||
}
|
||||
|
||||
static public class NodeData {
|
||||
public int parentBone = -1;
|
||||
public int[] bones;
|
||||
public float x, y;
|
||||
}
|
||||
|
||||
static public class Node {
|
||||
public final NodeData data;
|
||||
public @Null Bone parentBone;
|
||||
public Bone[] bones;
|
||||
public float x, y, vx, vy, ax, ay;
|
||||
|
||||
Node (NodeData data) { // Editor.
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public Node (NodeData data, Skeleton skeleton) {
|
||||
this.data = data;
|
||||
|
||||
parentBone = data.parentBone == -1 ? null : skeleton.bones.get(data.parentBone);
|
||||
|
||||
bones = new Bone[data.bones.length];
|
||||
for (int i = 0, n = bones.length; i < n; i++)
|
||||
bones[i] = skeleton.bones.get(data.bones[i]);
|
||||
|
||||
setToSetupPose();
|
||||
}
|
||||
|
||||
public Node (Node node) {
|
||||
this.data = node.data;
|
||||
parentBone = node.parentBone;
|
||||
bones = new Bone[node.bones.length];
|
||||
arraycopy(node.bones, 0, bones, 0, bones.length);
|
||||
x = node.x;
|
||||
y = node.y;
|
||||
vx = node.vx;
|
||||
vy = node.vy;
|
||||
ax = node.ax;
|
||||
ay = node.ay;
|
||||
}
|
||||
|
||||
public void setToSetupPose () {
|
||||
x = data.x;
|
||||
y = data.y;
|
||||
vx = 0;
|
||||
vy = 0;
|
||||
ax = 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 {
|
||||
public int node1, node2;
|
||||
public int[] bones;
|
||||
public float length, strength, damping;
|
||||
public boolean rope, stretch;
|
||||
}
|
||||
|
||||
static public class Spring {
|
||||
public final SpringData data;
|
||||
public Node node1, node2;
|
||||
public Bone[] bones;
|
||||
public float length, strength, damping;
|
||||
|
||||
Spring (SpringData data) { // Editor.
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public Spring (SpringData data, PhysicsConstraint constraint, Skeleton skeleton) {
|
||||
this.data = data;
|
||||
|
||||
node1 = constraint.nodes.get(data.node1);
|
||||
node2 = constraint.nodes.get(data.node2);
|
||||
|
||||
bones = new Bone[data.bones.length];
|
||||
for (int i = 0, n = bones.length; i < n; i++)
|
||||
bones[i] = skeleton.bones.get(data.bones[i]);
|
||||
|
||||
setToSetupPose();
|
||||
}
|
||||
|
||||
public Spring (Spring spring, PhysicsConstraint constraint) {
|
||||
this.data = spring.data;
|
||||
node1 = constraint.nodes.get(data.node1);
|
||||
node2 = constraint.nodes.get(data.node2);
|
||||
bones = new Bone[spring.bones.length];
|
||||
arraycopy(spring.bones, 0, bones, 0, bones.length);
|
||||
length = spring.length;
|
||||
strength = spring.strength;
|
||||
damping = spring.damping;
|
||||
}
|
||||
|
||||
public void setToSetupPose () {
|
||||
length = data.length;
|
||||
strength = data.strength;
|
||||
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;
|
||||
}
|
||||
}
|
||||
public float length, stiffness, damping;
|
||||
public boolean rope;
|
||||
}
|
||||
}
|
||||
|
||||
@ -37,7 +37,7 @@ import com.badlogic.gdx.utils.Array;
|
||||
import com.badlogic.gdx.utils.FloatArray;
|
||||
import com.badlogic.gdx.utils.Null;
|
||||
|
||||
import com.esotericsoftware.spine.PhysicsConstraintData.Node;
|
||||
import com.esotericsoftware.spine.PhysicsConstraint.Node;
|
||||
import com.esotericsoftware.spine.Skin.SkinEntry;
|
||||
import com.esotericsoftware.spine.attachments.Attachment;
|
||||
import com.esotericsoftware.spine.attachments.MeshAttachment;
|
||||
|
||||
@ -38,8 +38,8 @@ import com.badlogic.gdx.math.Vector2;
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
import com.badlogic.gdx.utils.FloatArray;
|
||||
|
||||
import com.esotericsoftware.spine.PhysicsConstraintData.Node;
|
||||
import com.esotericsoftware.spine.PhysicsConstraintData.Spring;
|
||||
import com.esotericsoftware.spine.PhysicsConstraint.Node;
|
||||
import com.esotericsoftware.spine.PhysicsConstraint.Spring;
|
||||
import com.esotericsoftware.spine.attachments.Attachment;
|
||||
import com.esotericsoftware.spine.attachments.BoundingBoxAttachment;
|
||||
import com.esotericsoftware.spine.attachments.ClippingAttachment;
|
||||
|
||||
@ -170,16 +170,16 @@ class SkeletonViewerUI {
|
||||
loopCheckbox.setChecked(true);
|
||||
|
||||
loadScaleSlider.setValue(1);
|
||||
loadScaleSlider.setSnapToValues(new float[] {0.5f, 1, 1.5f, 2, 2.5f}, 0.09f);
|
||||
loadScaleSlider.setSnapToValues(0.09f, 0.5f, 1, 1.5f, 2, 2.5f);
|
||||
|
||||
zoomSlider.setValue(1);
|
||||
zoomSlider.setSnapToValues(new float[] {1, 2}, 0.30f);
|
||||
zoomSlider.setSnapToValues(0.30f, 1, 2);
|
||||
|
||||
xScaleSlider.setValue(1);
|
||||
xScaleSlider.setSnapToValues(new float[] {-1.5f, -1, -0.5f, 0.5f, 1, 1.5f}, 0.12f);
|
||||
xScaleSlider.setSnapToValues(0.12f, -1.5f, -1, -0.5f, 0.5f, 1, 1.5f);
|
||||
|
||||
yScaleSlider.setValue(1);
|
||||
yScaleSlider.setSnapToValues(new float[] {-1.5f, -1, -0.5f, 0.5f, 1, 1.5f}, 0.12f);
|
||||
yScaleSlider.setSnapToValues(0.12f, -1.5f, -1, -0.5f, 0.5f, 1, 1.5f);
|
||||
|
||||
skinList.getSelection().setRequired(false);
|
||||
skinList.getSelection().setToggle(true);
|
||||
@ -188,10 +188,10 @@ class SkeletonViewerUI {
|
||||
animationList.getSelection().setToggle(true);
|
||||
|
||||
mixSlider.setValue(0.3f);
|
||||
mixSlider.setSnapToValues(new float[] {1, 1.5f, 2, 2.5f, 3, 3.5f}, 0.12f);
|
||||
mixSlider.setSnapToValues(0.12f, 1, 1.5f, 2, 2.5f, 3, 3.5f);
|
||||
|
||||
speedSlider.setValue(1);
|
||||
speedSlider.setSnapToValues(new float[] {0.5f, 0.75f, 1, 1.25f, 1.5f, 2, 2.5f}, 0.09f);
|
||||
speedSlider.setSnapToValues(0.09f, 0.5f, 0.75f, 1, 1.25f, 1.5f, 2, 2.5f);
|
||||
|
||||
alphaSlider.setValue(1);
|
||||
alphaSlider.setDisabled(true);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user