我们正处于从JavaFX过渡到libGDX的过程中。想知道libGDX中是否有等价的PathTransition。
PathTransition的特别之处在于它在遍历Path时会相应地旋转Node。关于PathTransition的糟糕之处在于你需要自己计算速度,也就是说你只提供完成遍历所需的时间,但你需要弄清楚路径的长度以便你可以设置适当的持续时间
希望libGDX有类似或更好的改进版本的PathTransition。
答案 0 :(得分:1)
根据您发布的示例,我认为libGDX中没有 direct 等效项,但我们可以制作一个。
libGDX附带一个名为Scene2d的场景图,它允许您将动作应用于Actors或Actors组。内置了一系列操作(MoveTo,RotateBy),您可以相互并行或顺序执行它们。您还可以应用Interpolation类进行补间。
libGDX还带有Path接口,但从1.9.4开始没有PathAction,但这并不意味着你不能构建类似下面的那个:
package tech.otter.gdxsandbox.demos;
import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.math.CatmullRomSpline;
import com.badlogic.gdx.math.Path;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.scenes.scene2d.Action;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.Stage;
public class PathDemo extends Demo {
private ShapeRenderer sr;
private Stage stage;
public PathDemo() {
super("Action Demo");
stage = new Stage();
}
@Override
public void show() {
sr = new ShapeRenderer();
Actor ourCircle = new SimpleCircleActor(sr, 10f);
stage.addActor(ourCircle);
ourCircle.addAction(new PathAction(5f, 50f, 50f, 100f, 60f, 300f, 200f, 100f, 400f));
}
@Override
public void render(float delta) {
sr.setAutoShapeType(true);
sr.begin();
sr.set(ShapeRenderer.ShapeType.Filled);
stage.act(delta);
stage.draw();
sr.end();
}
@Override
public void dispose() {
stage.dispose();
sr.dispose();
}
private class SimpleCircleActor extends Actor {
private ShapeRenderer renderer;
private float radius;
public SimpleCircleActor(ShapeRenderer renderer, float radius) {
this.renderer = renderer;
this.radius = radius;
}
/**
* Assumes something outside is setting the ShapeRenderer type/begin/end.
* Don't do this at home, kids.
*/
@Override
public void draw(Batch batch, float delta) {
renderer.circle(this.getX(), this.getY(), this.radius);
}
}
private class PathAction extends Action {
private Path<Vector2> path;
private float current;
private float duration;
public PathAction(float duration, float... coordinates) {
this.duration = duration;
this.current = 0;
Vector2[] dataSet = new Vector2[coordinates.length / 2];
for(int i = 0; i < dataSet.length; i++) {
dataSet[i] = new Vector2(coordinates[2*i], coordinates[2*i+1]);
}
this.path = new CatmullRomSpline<Vector2>(dataSet, true);
}
@Override
public boolean act(float delta) {
if(duration == current) return true; // The action has already completed.
if(current + delta >= duration) {
current = duration;
} else {
current += delta;
}
Vector2 out = new Vector2(); // Get our position on the path.
this.path.valueAt(out, current / duration);
this.actor.setPosition(out.x, out.y);
return duration == current; // Return whether we are now complete.
}
}
}
答案 1 :(得分:0)
所以我开始工作了。下面的类创建了一个Action
实例,确切地说是SequenceAction
。它创建的Action
将通过用户提供的点遍历Actor
。它会考虑速度,持续时间,偏移Actor
在路径上的位置,旋转Actor
,甚至在离开和进入屏幕时缩进Actor
。
import com.badlogic.gdx.scenes.scene2d.actions.Actions;
import com.badlogic.gdx.scenes.scene2d.actions.SequenceAction;
import java.util.ArrayList;
import java.util.List;
/**
*
* @author jmart
*/
public class PathActionMaker {
private final float[][] points;
private float speed = 0;
private final float rotationSpeed = 600;
private float[] screenSize;
/**
*
* @param points - all the path points that must be traversed
* @param duration - how long it should take to traverse the path points
* @param screenSize - the size of the screen
*/
public PathActionMaker(float[][] points, float duration, float[] screenSize) {
this.points = points;
this.speed = getSpeed(points, duration);
this.screenSize = screenSize;
}
/**
*
* @param speed - the speed at which the Actor should move traverse through
* all the points
* @param points - all the path points that must be traversed
* @param screenSize - the size of the screen
*/
public PathActionMaker(float speed, float[][] points, float[] screenSize) {
this.points = points;
this.speed = speed;
this.screenSize = screenSize;
}
/**
* The offsets are used to allow centering of an actor on the provided
* points.
*
* @param xOffset
* @param yOffset
* @param offScreenIndent number of pixels to move the actor out of screen
* when entering and leaving
* @return
*/
public SequenceAction getAction(float xOffset, float yOffset, float offScreenIndent) {
float[] prev = null;
float prevTheta = 0;
int i = 0;
SequenceAction seqAction = Actions.sequence();
boolean firstRotation = true;
float[][] fixedPoints = getOffSetFixedPoints(points, xOffset, yOffset, offScreenIndent);
for (float[] point : fixedPoints) {
float[] p = point;
if (prev == null) {
seqAction.addAction(Actions.moveTo(p[0], p[1]));
} else {
float dist = MathUtils.getDistance(prev[0], prev[1], p[0], p[1]);
float time = dist / speed;
float theta = MathUtils.angle(prev[0], prev[1], p[0], p[1]) + 90;
if (theta > 360) {
theta -= 360;
}
float dTheta = Math.abs(prevTheta - theta);
float rotateDuration = dTheta / rotationSpeed;
// the first rotation should be immediate.
if (firstRotation) {
firstRotation = false;
seqAction.addAction(Actions.parallel(
Actions.moveTo(p[0], p[1], time),
Actions.rotateTo(theta)));//...rotation duration is 0, aka immediate
} else {
seqAction.addAction(Actions.parallel(
Actions.moveTo(p[0], p[1], time),
Actions.rotateTo(theta, rotateDuration)));
}
prevTheta = theta;
}
prev = p;
}
return seqAction;
}
public float[][] getOffSetFixedPoints(float[][] points, float xOffset, float yOffset, float offScreenIndent) {
List<Float[]> list = new ArrayList<>();
//apply offset indent at beginning of path
float[] p1 = points[0];
float[] p2 = points[1];
float slope = (p1[1] - p2[1]) / (p1[0] - p2[0]);
float c = p1[1] - slope * p1[0];
if (p1[0] == 0) {
float x = -offScreenIndent;
float y = slope * x + c;
list.add(new Float[]{x - xOffset, y - yOffset});
} else if (p1[0] == screenSize[0]) {
float x = screenSize[0] + offScreenIndent;
float y = slope * x + c;
list.add(new Float[]{x - xOffset, y - yOffset});
} else if (p1[1] == 0) {
float y = -offScreenIndent;
float x = (y - c) / slope;
list.add(new Float[]{x - xOffset, y - yOffset});
} else if (p1[1] == screenSize[1]) {
float y = screenSize[1] + offScreenIndent;
float x = (y - c) / slope;
list.add(new Float[]{x - xOffset, y - yOffset});
}
//apply the remaining points
for (float[] point : points) {
float[] p = {point[0] - xOffset, point[1] - yOffset};
addPointToList(p, list);
}
//apply offscreen indent at end of path
float[] p4 = points[points.length - 1];
float[] p3 = points[points.length - 2];
slope = (p3[1] - p4[1]) / (p3[0] - p4[0]);
c = p3[1] - slope * p3[0];
if (p4[0] <= 0) {
float x = -offScreenIndent;
float y = slope * x + c;
list.add(new Float[]{x - xOffset, y - yOffset});
} else if (p4[0] >= screenSize[0]) {
float x = screenSize[0] + offScreenIndent;
float y = slope * x + c;
list.add(new Float[]{x - xOffset, y - yOffset});
} else if (p4[1] <= 0) {
float y = -offScreenIndent;
float x = (y - c) / slope;
list.add(new Float[]{x - xOffset, y - yOffset});
} else if (p4[1] >= screenSize[1]) {
float y = screenSize[1] + offScreenIndent;
float x = (y - c) / slope;
list.add(new Float[]{x - xOffset, y - yOffset});
}
//convert list to float[][]
float[][] rv = new float[list.size()][];
int i = 0;
for (Float[] p : list) {
float[] j = new float[2];
j[0] = p[0];
j[1] = p[1];
rv[i++] = j;
}
return rv;
}
private static void addPointToList(float[] point, List<Float[]> list) {
list.add(new Float[]{point[0], point[1]});
}
private float getSpeed(float[][] points, float duration) {
//speed = distance/time
float distance = MathUtils.getDistance(points);
return distance / duration;
}
}