如何在Java Swing中编码不同力和角度球的抛射物?

我已经为不同的力和角度的弹丸运动编写了以下function,但它不能正常工作。 我哪里出错了? 我想要一些像愤怒的小鸟游戏。

码:

public void shootBall(int timeCounter){ int gravity = 4; double time = timeCounter/40.0; int velocity = force_value; double radians = currentangle*Math.PI/180; ball.setX((int)((ball.getX()+10)*Math.cos(radians) + velocity*Math.cos(radians)*time)); ball.setY((int)((ball.getY()+10)*Math.sin(radians) + velocity*Math.sin(radians)*time - 0.5*gravity*time*time)); updateGame(); } 

我希望球能从左下角投出。

在此处输入图像描述

正如评论中所指出的那样(以及答案https://stackoverflow.com/a/21785385 ):为了实现弹丸的“逼真”弹道轨迹,重要的是要考虑速度 – 以及作为给定加速度的速度变化(基于重力)。 不可否认,我并不完全理解您希望在当前位置更新中使用sin / cos计算得到什么。 但是我已经在这里找到了一些接近你想要达到的SSCE,所以我稍微调整了一下。 其中大部分是q&d-样板代码,但您可能需要查看Projectile类以及如何在performTimeStep方法中更新速度和位置。

顺便说一句:这种方法有一个很好的优势,它可以很容易地扩展到像风一样的模型:只需使用不同的加速度。 例如,不是(0,-9.81)而是(1,-9.81)来模拟左边的微风。

 import java.awt.BorderLayout; import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.geom.AffineTransform; import java.awt.geom.Point2D; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JSlider; import javax.swing.SwingUtilities; public class ProjectileShooterTest { public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { createAndShowGUI(); } }); } private static void createAndShowGUI() { JFrame f = new JFrame(); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.setSize(600,600); final ProjectileShooter projectileShooter = new ProjectileShooter(); ProjectileShooterPanel projectileShooterPanel = new ProjectileShooterPanel(projectileShooter); projectileShooter.setPaintingComponent(projectileShooterPanel); JPanel controlPanel = new JPanel(new GridLayout(1,0)); controlPanel.add(new JLabel("Angle")); final JSlider angleSlider = new JSlider(0, 90, 45); controlPanel.add(angleSlider); controlPanel.add(new JLabel("Power")); final JSlider powerSlider = new JSlider(0, 100, 50); controlPanel.add(powerSlider); JButton shootButton = new JButton("Shoot"); shootButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { int angleDeg = angleSlider.getValue(); int power = powerSlider.getValue(); projectileShooter.setAngle(Math.toRadians(angleDeg)); projectileShooter.setPower(power); projectileShooter.shoot(); } }); controlPanel.add(shootButton); f.getContentPane().setLayout(new BorderLayout()); f.getContentPane().add(controlPanel, BorderLayout.NORTH); f.getContentPane().add(projectileShooterPanel, BorderLayout.CENTER); f.setVisible(true); } } class ProjectileShooter { private double angleRad = Math.toRadians(45); private double power = 50; private Projectile projectile; private JComponent paintingComponent; void setPaintingComponent(JComponent paintingComponent) { this.paintingComponent = paintingComponent; } void setAngle(double angleRad) { this.angleRad = angleRad; } void setPower(double power) { this.power = power; } void shoot() { Thread t = new Thread(new Runnable() { @Override public void run() { executeShot(); } }); t.setDaemon(true); t.start(); } private void executeShot() { if (projectile != null) { return; } projectile = new Projectile(); Point2D velocity = AffineTransform.getRotateInstance(angleRad). transform(new Point2D.Double(1,0), null); velocity.setLocation( velocity.getX() * power * 0.5, velocity.getY() * power * 0.5); projectile.setVelocity(velocity); //System.out.println("Initial "+velocity); long prevTime = System.nanoTime(); while (projectile.getPosition().getY() >= 0) { long currentTime = System.nanoTime(); double dt = 3 * (currentTime - prevTime) / 1e8; projectile.performTimeStep(dt); prevTime = currentTime; paintingComponent.repaint(); try { Thread.sleep(10); } catch (InterruptedException e) { Thread.currentThread().interrupt(); return; } } projectile = null; paintingComponent.repaint(); } Projectile getProjectile() { return projectile; } } class Projectile { private final Point2D ACCELERATION = new Point2D.Double(0, -9.81 * 0.1); private final Point2D position = new Point2D.Double(); private final Point2D velocity = new Point2D.Double(); public Point2D getPosition() { return new Point2D.Double(position.getX(), position.getY()); } public void setPosition(Point2D point) { position.setLocation(point); } public void setVelocity(Point2D point) { velocity.setLocation(point); } void performTimeStep(double dt) { scaleAddAssign(velocity, dt, ACCELERATION); scaleAddAssign(position, dt, velocity); //System.out.println("Now at "+position+" with "+velocity); } private static void scaleAddAssign( Point2D result, double factor, Point2D addend) { double x = result.getX() + factor * addend.getX(); double y = result.getY() + factor * addend.getY(); result.setLocation(x, y); } } class ProjectileShooterPanel extends JPanel { private final ProjectileShooter projectileShooter; public ProjectileShooterPanel(ProjectileShooter projectileShooter) { this.projectileShooter = projectileShooter; } @Override protected void paintComponent(Graphics gr) { super.paintComponent(gr); Graphics2D g = (Graphics2D)gr; Projectile projectile = projectileShooter.getProjectile(); if (projectile != null) { g.setColor(Color.RED); Point2D position = projectile.getPosition(); int x = (int)position.getX(); int y = getHeight() - (int)position.getY(); g.fillOval(x-01, y-10, 20, 20); } } } 

您为x和y位移创建的公式都缺少第一项中的time因子。 下面,我将您的代码放在上面,并在下面输入正确的代码进行比较,以便您可以准确地看到您遗漏的内容。

对于X位移

  • (ball.getX()+10)*Math.cos(radians)+ ...
  • (ball.getX()+10)*time*Math.cos(radians)+ ...

对于Y位移

  • (ball.getY()+10)*Math.sin(radians)+ ...
  • (ball.getY()+10)*time*Math.sin(radians)+ ...

我引用维基百科的位移方程作为弧度的函数来回答你的问题。

非常重要的是要注意:在Java中(与许多其他语言一样) 图像中的位置(0,0)是左上角 。 因此,要模拟您的对象“跌倒”,您实际上需要增加其Y坐标(并检查它是否已经“击中地面” – > if (position.Y == GROUND) stop(); )。 起始位置也不是(0,0)而是(0,起始Y)。

除此之外,我的建议是:

  1. 将球的当前位置存储在一个单独的结构中(可能只是2个变量,x和y。)
  2. 让你的速度由两个元素组成:X方向的速度和Y方向的速度。
  3. 创建一个move方法,根据速度改变当前位置。

请注意,您的x坐标会以恒定的方式变化,因此您的velocity.X将是常量并且可以在开始时计算。 然后,你的velocity.Y应该随时间变化:你计算它的初始值,然后在每次迭代中从它减去与重力相关的量(也是常数)。 move方法可能如下所示:

 public void move(Position position, Velocity velocity) { position.X += velocity.X; position.Y += velocity.Y; velocity.Y -= ACCELERATION*TIME; //TIME is time between calls of move(), approximate. } 

当然这是一个非常简单的例子,但我想它给出了这个想法。 请注意,TIME和ACCELERATION在模拟中都是恒定的,因为TIME不是从开始经过的时间,而是从上一次move调用开始经过的时间。 请记住此答案顶部的通知。 另外:正确初始化您的velocityposition ,如下所示:

 position.X = startingX; //the leftmost pixel of the screen is 0, of course. position.Y = startingY; //the "ground level" of your simulation is probably NOT 0. velocity.X = speed*Math.cos(throwAngle); velocity.Y = speed*Math.sin(throwAngle); 

speed只是一个int(开始时速度矢量的长度)。