我已经为不同的力和角度的射弹运动编写了以下功能,但它不能正常工作。我哪里出错了?我想要一些像愤怒的小鸟游戏。
代码:
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();
}
我希望球能从左下角投出。
答案 0 :(得分:2)
正如评论中所指出的那样(以及答案https://stackoverflow.com/a/21785385):为了实现弹丸的“现实”弹道轨迹,重要的是要考虑速度 - 以及变化给定加速度的速度(基于重力)。不可否认,我并不完全理解您想要在当前位置更新中使用sin / cos计算得到什么。但是我已经在这里找到了一些接近你想要达到的SSCE,所以我稍微调整了一下。其中大部分是q& d-样板代码,但您可能需要查看Projectile
类以及如何在performTimeStep
方法中更新速度和位置。
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);
}
}
}
答案 1 :(得分:1)
您为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)+ ...
我引用了Wikipedia's equation作为弧度函数的位移来回答你的问题。
答案 2 :(得分:0)
非常重要的通知:在Java中(与许多其他语言一样)图像中的位置(0,0)位于左上角。因此,为了模拟您的对象“倒下”,您实际上需要增加其Y坐标(并检查它是否已“触地” - > if (position.Y == GROUND) stop();
)。起始位置也不是(0,0)而是(0,起始Y)。
除此之外,我的建议是:
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
调用开始经过的时间。请记住此答案顶部的通知。另外:正确初始化velocity
和position
,如下所示:
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(开始时速度矢量的长度)。