我有一个我正在制作的自上而下的游戏,我希望我的敌人能够在屏幕上以弧形移动。现在,它们在屏幕的两个边缘之间以直线方式移动。我在一条边上生成一个开始位置,然后在屏幕上的某个位置找到一个随机位置,并计算出移动速度,将角度的sin/cos乘以它们的速度变量。
我想用这些点在它们之间产生一些弧线,然后用它来移动我的敌人。我想也许一些样条线可以做到这一点,但我不完全确定如何创建一个,更重要的是如何使用它来插值我的角色。我认为在这一点上,这更像是一个数学问题,而不是编程,但我希望有人能帮上忙。谢谢。
发布于 2014-03-25 07:16:19
当您打算使用样条线时,可以使用Java语言中已有的Path2D
类。您可以通过以下方式组装任意路径
移动到某一直线段,一条二次曲线,一条三次曲线段,一段三次曲线,一段三次曲线,
因此,组装这条路径应该很简单:您只需创建一条二次曲线,该曲线从屏幕左边界上的任意点开始,到屏幕右边界上的任意点结束。作为控制点(两端),您可以在屏幕中心的任意位置使用一个点。
(顺便说一下:当您设法将路径表示为通用Path2D
时,您可能会想象到,在为敌人设计路径时,您有相当大的自由度。它们可以绕圈或之字形运行,就像你喜欢的那样……)
这里可能更棘手的是让敌人遵循这条道路。
第一步还不是很棘手:您可以使用PathIterator
沿着这条路走下去。但是这个PathIterator
只会返回一个线段,即二次曲线。这可以通过创建一个展平的PathIterator
来缓解。这会将所有曲线转换为线段(分辨率可以很高,因此您不会注意到任何拐角)。
然而,现在来了真正棘手的部分:当迭代这些线段时,移动速度可能会有所不同:根据原始二次曲线的曲率,可能会创建更多或更少的线段。在最坏的情况下,当所有3个点都在一条线上时,将只创建一条线段,并且敌人将在单个步骤中穿过整个屏幕。所以你必须确保以恒定的速度遍历这条路径。当迭代路径时,你必须计算你已经走了多远,并且可能在路径的两点之间插入一个位置。
我很快组装了一个例子。这当然不是万无一失的,但可以作为一个起点。
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Path2D;
import java.awt.geom.PathIterator;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class SplineMovementTest
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
@Override
public void run()
{
createAndShowGUI();
}
});
}
private static PathFollower pathFollower;
private static void createAndShowGUI()
{
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setLayout(new BorderLayout());
final Random random = new Random(0);
final SplineMovementPanel p = new SplineMovementPanel();
JButton generateButton = new JButton("Generate");
generateButton.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
Shape spline = generateSpline(p,
random.nextDouble(),
random.nextDouble(),
random.nextDouble());
p.setSpline(spline);
pathFollower = new PathFollower(spline);
p.repaint();
}
});
frame.getContentPane().add(generateButton, BorderLayout.NORTH);
startAnimation(p);
frame.getContentPane().add(p, BorderLayout.CENTER);
frame.setSize(800, 800);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private static Shape generateSpline(
JComponent c, double yLeft, double yCenter, double yRight)
{
Path2D spline = new Path2D.Double();
double x0 = 0;
double y0 = yLeft * c.getHeight();
double x1 = c.getWidth() / 2;
double y1 = yCenter * c.getHeight();
double x2 = c.getWidth();
double y2 = yRight * c.getHeight();
spline.moveTo(x0, y0);
spline.curveTo(x1, y1, x1, y1, x2, y2);
return spline;
}
private static void startAnimation(final SplineMovementPanel p)
{
Timer timer = new Timer(20, new ActionListener()
{
double position = 0.0;
@Override
public void actionPerformed(ActionEvent e)
{
position += 0.005;
position %= 1.0;
if (pathFollower != null)
{
Point2D point = pathFollower.computePointAt(
position * pathFollower.getPathLength());
p.setObjectLocation(point);
}
}
});
timer.start();
}
}
class PathFollower
{
private final List<Point2D> points;
private final double pathLength;
PathFollower(Shape spline)
{
points = createPointList(spline);
pathLength = computeLength(points);
}
public double getPathLength()
{
return pathLength;
}
Point2D computePointAt(double length)
{
if (length < 0)
{
Point2D p = points.get(0);
return new Point2D.Double(p.getX(), p.getY());
}
if (length > pathLength)
{
Point2D p = points.get(points.size()-1);
return new Point2D.Double(p.getX(), p.getY());
}
double currentLength = 0;
for (int i=0; i<points.size()-1; i++)
{
Point2D p0 = points.get(i);
Point2D p1 = points.get(i+1);
double distance = p0.distance(p1);
double nextLength = currentLength + distance;
if (nextLength > length)
{
double rel = 1 - (nextLength - length) / distance;
double x0 = p0.getX();
double y0 = p0.getY();
double dx = p1.getX() - p0.getX();
double dy = p1.getY() - p0.getY();
double x = x0 + rel * dx;
double y = y0 + rel * dy;
return new Point2D.Double(x,y);
}
currentLength = nextLength;
}
Point2D p = points.get(points.size()-1);
return new Point2D.Double(p.getX(), p.getY());
}
private static double computeLength(List<Point2D> points)
{
double length = 0;
for (int i=0; i<points.size()-1; i++)
{
Point2D p0 = points.get(i);
Point2D p1 = points.get(i+1);
length += p0.distance(p1);
}
return length;
}
private static List<Point2D> createPointList(Shape shape)
{
List<Point2D> points = new ArrayList<Point2D>();
PathIterator pi = shape.getPathIterator(null, 0.1);
double coords[] = new double[6];
while (!pi.isDone())
{
int s = pi.currentSegment(coords);
switch (s)
{
case PathIterator.SEG_MOVETO:
points.add(new Point2D.Double(coords[0], coords[1]));
case PathIterator.SEG_LINETO:
points.add(new Point2D.Double(coords[0], coords[1]));
}
pi.next();
}
return points;
}
}
class SplineMovementPanel extends JPanel
{
void setSpline(Shape shape)
{
this.spline = shape;
}
void setObjectLocation(Point2D objectLocation)
{
this.objectLocation = objectLocation;
repaint();
}
private Shape spline = null;
private Point2D objectLocation = null;
@Override
protected void paintComponent(Graphics gr)
{
super.paintComponent(gr);
Graphics2D g = (Graphics2D)gr;
g.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
if (spline != null)
{
g.setColor(Color.BLACK);
g.draw(spline);
}
if (objectLocation != null)
{
g.setColor(Color.RED);
int x = (int)objectLocation.getX()-15;
int y = (int)objectLocation.getY()-15;
g.fillOval(x, y, 30, 30);
}
}
}
https://stackoverflow.com/questions/22621638
复制相似问题