我有一个自上而下的游戏,我希望我的敌人能够以弧形移动屏幕。现在,它们在屏幕的两个边缘之间沿直线移动。我在一条边上生成一个起始位置,然后在屏幕的某处找到一个随机位置,并计算移动速度,我将角度的sin / cos乘以它们的速度变量。
我想用它们来指向它们之间产生一些弧线,然后用它来移动我的敌人。我想也许某种样条可以做到这一点,但我不完全确定如何创建一个,也不是更明显如何使用它来插入我的字符。我认为在这一点上它更多的是一个数学问题,而不是编程,但我希望有人可以提供帮助。感谢。
答案 0 :(得分:0)
是的,样条曲线对你有用。具体来说,我会推荐一个三次样条,因为如果你想要做一个不同的形状,也许是街头霸王风格的上勾,你可以重复使用相同的代码。我记得三次样条是一个不错的通用解决方案。
至于求解三次样条函数,我建议您只使用谷歌伪代码,这对您来说很有意义。只有当你真的想要在飞行中一般性地求解合适的三次样条时。
在实践中,我想你想要的形状几乎一直都是一样的?如果是这样,您可以解决一些样条曲线的一般情况,并将其保存到某些快速数据结构中以提高性能。例如,对于y=x
,包含必要信息(预处理)的合适数组将为x[0] = 1,x[1] = 1,x[2] = 2 ... x[n] = n
。
在实践中,您可以想出一个方程来建模一个简单的两点样条曲线。三次方程有4个未知数。所以你至少有两个数据点,你是起点和终点。另外,你可以计算他跳跃时的衍生物。对于你的第四点,你可以使用你希望他跳过的另一个点,或者当他降落时的衍生物。然后使用https://www.wolframalpha.com/为您解决等式。或者使用方程来求解立方体。
您可以做的另一件事就是使用二次方程+重力+风阻来计算弧。 Google再次知道如何解决这个问题。这个页面是我很快发现的,看起来它可以做到这一点。 http://www.physicsclassroom.com/class/vectors/Lesson-2/Non-Horizontally-Launched-Projectiles-Problem-Solv
答案 1 :(得分:0)
当您打算使用样条线时,可以使用Java中已有的Path2D
类。您可以通过
因此,组装此路径应该很简单:您可以创建一个二次曲线,该曲线从屏幕左边框的随机点开始,到屏幕右边框的随机点结束。作为控制点(两端),您可以使用屏幕中心随机位置的点。
(顺便说一句:当你设法将路径表示为通用Path2D
时,你可以想象在为敌人设计路径时你有很大的自由。他们可以在圈子或zig中运行-zag,就像你喜欢的那样......)
这里可能更棘手的是让敌人跟随这条道路。
第一步还不是那么棘手:你可以用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);
}
}
}