如何在Java Swing中编写不同力和角度球的弹丸?

时间:2014-02-14 15:59:44

标签: java swing applet physics japplet

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

代码:

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();
}

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

enter image description here

3 个答案:

答案 0 :(得分:2)

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

BTW:这种方法有一个很好的优势,它可以很容易地扩展到像 wind 这样的模型:只需使用不同的加速度。例如,不是(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);
        }
    }
}

答案 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)。

除此之外,我的建议是:

  1. 将球的当前位置存储在单独的结构中(可能只是2个变量,x和y。)
  2. 让你的速度由两个元素组成:X方向的速度和Y方向的速度。
  3. 创建一个move方法,该方法会根据速度更改当前位置。
  4. 请注意,您的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(开始时速度矢量的长度)。