重力与predetermined目的地 [英] Gravity with predetermined destination

查看:279
本文介绍了重力与predetermined目的地的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用的重力为我的游戏在2D环境。 我在我的游戏的所有使用的对象有x和y坐标,它们可以被抛到一个水平,意思是实例化,然后给出一个特定原点位置,然后给予新坐标每一帧具有以下比重:

I am using gravity in a 2d environment for my game. The objects I use in my game all have x and y coordinates, they can be "thrown" into a level, meaning instantiated, then given a specific origin location and then given new coordinates each frame with the following gravity:

public void throwObject(float delta) {
    setX(getX() + velocity.x * delta);
    setY(getY() + velocity.y * delta);

velocity.y += gravity.y * delta;
}

为以下内容:

with the following:

Vector2 GRAVITY = new Vector2(0, -10);     

这是一个给定的originx和originy,对象移动因此,这个效果很好。

From a given originx and originy, the objects "move" accordingly, this works well.

现在,我想使物体在移动到AA给定的目标,例如:

Now I would like to make the objects move to a a given destination, for example at:

destinationx = 50; 
destinationy = 350;

如果我用一个静态的起源X和originy,我怎么能计算velocity.x和velocity.y使物体被抛出一个弹丸曲线到指定的目标坐标?

If I use a static origin x and originy, how can I calculate velocity.x and velocity.y so that the object is thrown with a projectile curve to the specified destination coordinates?

编辑: 我已经取得了一些进展确定计算velocity.x:

I have made some progress determining the calculation for velocity.x:

 velocity.x = (destinationx - originx) / 100; 

其中100是我已经设置为静态的帧数。这个效果很好。

where 100 is the number of frames I have set as static. This works well.

有关velocity.y,我曾尝试:

For velocity.y, I have tried:

velocity.y = (destinationy - originy) / 100 * delta + Math.sqrt(gravity) * 2;

它给出​​的结果,看起来接近从右侧的公式,但不完全是。

it gives a result that looks close from the right formula, but not exactly it

推荐答案

首先:有无穷多解。包括平凡之一,其中速度是在源和目标位置,由增量(即,这里的目标将在单一步骤中达成,由于速度是的溶液除以刚差无限高)。

First of all: There are infinitely many solutions. Including the "trivial" one, where the velocity is just the difference of the source and the target position, divided by "delta" (that is, the solution where the target would be reached in a single step, due to the velocity being "infinitely high").

但有直觉能想象你想要达到的目标,所以我会停下来吹毛求疵:

But one intuitively can imagine what you want to achieve, so I'll stop nitpicking:

您想在指定的速度的直角的坐标:(DELTAX,移动deltaY)。这是更方便的认为这速度而言的极性的坐标:(角,功率),其中功率再presents所述的量值的初始速度的。 (有关转换规则,请参见百科:极坐标系)。

You want to specify the velocity in cartesian coordinates: (deltaX, deltaY). It is more convenient to think of this velocity in terms of polar coordinates: (angle, power), where "power" represents the magnitude of the initial velocity. (For conversion rules, see Wikipedia: Polar coordinate system).

给定一个固定的初始速度,有任一零个,一个或两个角度,其中将弹丸击中目标。给定一个固定的初始角,还有可以是零个或一个初始速度,其中弹丸将命中目标。但是,如果你可以选择的两个的,速度和角度,再有就是弹道轨迹开始在原点,并通过目标无限多的。

Given a fixed initial velocity, there are either zero, one or two angles where the projectile will hit the target. Given a fixed initial angle, there may either be zero or one initial velocity where the projectile will hit the target. But if you can choose both, the velocity and the angle, then there is an infinite number of ballistic trajectories starting at the origin and passing through the target.

我稍微延伸的<一个href="http://stackoverflow.com/questions/21783858/how-to-$c$c-the-projectile-of-a-ball-of-different-force-and-angle-in-java-swing/21785654#21785654">Projectile从我的previous回答中的一个射手例如的,使用一些维基百科式的,主要是从的维基百科:一个弹丸的轨迹:

I slightly extended a Projectile Shooter example from one of my previous answers, using some of the formulas from wikipedia, mainly from Wikipedia: Trajectory of a projectile:

您可以使用滑块来改变角度和力量,用鼠标拖动的起源和目标。文本包含了两个重要成果:

You can use the sliders to change the angle and the power, and use the mouse to drag the origin and the target. The text contains two important outputs:

  • 要求的角度:这是两个拍摄角度在其目标可能受到较大冲击,假设电源的没有的修改。如果没有溶液(例如,当目标超出范围,而不管角度的),那么这些角可以的NaN
  • 所需的电力:这是必需的击中目标,假设角度的没有的修改的权力。如果没有解决方案(例如,在当前的角度简单地指向了错误的方向),那么这种权力可能是 NaN的
  • Required angle: These are the two shooting angles at which the target may be hit, assuming that the power is not modified. If there is no solution (for example, when the target is out of range, regardless of the angle), then these angles may be NaN.
  • Required power: This is the power that is required for hitting the target, assuming that the angle is not modified. If there is no solution (for example, when the current angle simply points into the wrong direction), then this power may be NaN.

根据不同的自由度,你想为你的速度,你可以大概提取相关的公式。 (道歉缺少的意见,这种SSCCE(短,自成一体,可编译,例)已经逐渐变成了LSCCE......)

Depending on the degrees of freedom that you would like for your velocity, you may probably extract the relevant formulas. (Apologies for the missing comments, this "SSCCE" (Short, Self Contained, Compilable, Example) has gradually turned into a "LSCCE"...)

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Path2D;
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.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

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

        final ProjectileShooter projectileShooter = 
            new ProjectileShooter();
        final ProjectileShooterPanel projectileShooterPanel = 
            new ProjectileShooterPanel(projectileShooter);
        projectileShooter.setPaintingComponent(projectileShooterPanel);

        ProjectileShooterMouseControl mouseControl = 
            new ProjectileShooterMouseControl(
                projectileShooter, projectileShooterPanel);
        projectileShooterPanel.addMouseMotionListener(mouseControl);
        projectileShooterPanel.addMouseListener(mouseControl);

        JPanel controlPanel = new JPanel(new GridLayout(1,0));

        controlPanel.add(new JLabel("Angle: ", SwingConstants.RIGHT));
        final JSlider angleSlider = new JSlider(0, 180, 45);
        angleSlider.addChangeListener(new ChangeListener()
        {
            @Override
            public void stateChanged(ChangeEvent e)
            {
                int angleDeg = angleSlider.getValue();
                projectileShooter.setAngle(Math.toRadians(angleDeg));
            }
        });
        controlPanel.add(angleSlider);

        controlPanel.add(new JLabel("Power:", SwingConstants.RIGHT));
        final JSlider powerSlider = new JSlider(0, 50, 25);
        controlPanel.add(powerSlider);
        powerSlider.addChangeListener(new ChangeListener()
        {
            @Override
            public void stateChanged(ChangeEvent e)
            {
                int power = powerSlider.getValue();
                projectileShooter.setPower(power);
            }
        });

        JButton shootButton = new JButton("Shoot");
        shootButton.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                projectileShooter.shoot();
            }
        });
        controlPanel.add(shootButton);

        f.getContentPane().setLayout(new BorderLayout());
        f.getContentPane().add(controlPanel, BorderLayout.NORTH);
        f.getContentPane().add(projectileShooterPanel, BorderLayout.CENTER);
        f.setSize(1200,800);
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }
}

class ProjectileShooterMouseControl 
    implements MouseListener, MouseMotionListener
{
    private final ProjectileShooter projectileShooter;
    private final ProjectileShooterPanel projectileShooterPanel;

    private boolean draggingOrigin = false;
    private boolean draggingTarget = false;

    ProjectileShooterMouseControl(
        ProjectileShooter projectileShooter, 
        ProjectileShooterPanel projectileShooterPanel)
    {
        this.projectileShooter = projectileShooter;
        this.projectileShooterPanel = projectileShooterPanel;
    }

    private Point2D toWorld(Point2D screen)
    {
        return new Point2D.Double(screen.getX(), 
            projectileShooterPanel.getHeight() - screen.getY());
    }

    @Override
    public void mouseMoved(MouseEvent e)
    {
    }

    @Override
    public void mouseDragged(MouseEvent e)
    {
        Point2D p = toWorld(e.getPoint());
        if (draggingOrigin)
        {
            projectileShooter.setOrigin(p);
        }
        else if (draggingTarget)
        {
            projectileShooter.setTarget(p);
        }
    }

    @Override
    public void mousePressed(MouseEvent e)
    {
        Point2D p = toWorld(e.getPoint());

        Point2D origin = projectileShooter.getOrigin();
        Point2D target = projectileShooter.getTarget();
        if (origin.distance(p) < 10)
        {
            draggingOrigin = true;
        }
        else if (target.distance(p) < 10)
        {
            draggingTarget = true;
        }
    }

    @Override
    public void mouseReleased(MouseEvent e)
    {
        draggingOrigin = false;
        draggingTarget = false;
    }

    @Override
    public void mouseClicked(MouseEvent e)
    {
    }

    @Override
    public void mouseEntered(MouseEvent e)
    {
    }

    @Override
    public void mouseExited(MouseEvent e)
    {
    }

}


class ProjectileShooter
{
    private static final double TIME_SCALE = 20;
    static final double GRAVITY = 9.81 * 0.1;

    private double angleRad = Math.toRadians(45);
    private double power = 25;

    private final Point2D origin = new Point2D.Double(50, 50);
    private final Point2D target = new Point2D.Double(500, 100);

    private Projectile projectile;
    private JComponent paintingComponent;

    void setPaintingComponent(JComponent paintingComponent)
    {
        this.paintingComponent = paintingComponent;
    }

    void setOrigin(Point2D origin)
    {
        this.origin.setLocation(origin);
        this.paintingComponent.repaint();
    }

    Point2D getOrigin()
    {
        return new Point2D.Double(origin.getX(), origin.getY());
    }

    void setTarget(Point2D target)
    {
        this.target.setLocation(target);
        this.paintingComponent.repaint();
    }

    Point2D getTarget()
    {
        return new Point2D.Double(target.getX(), target.getY());
    }

    void setAngle(double angleRad)
    {
        this.angleRad = angleRad;
        this.paintingComponent.repaint();
    }

    double getAngle()
    {
        return angleRad;
    }


    void setPower(double power)
    {
        this.power = power;
        this.paintingComponent.repaint();
    }

    double getPower()
    {
        return power;
    }

    double computeY(double x)
    {
        // http://de.wikipedia.org/wiki/Wurfparabel
        //  #Mathematische_Beschreibung
        double g = GRAVITY;
        double b = angleRad;
        double v0 = power;
        if (b > Math.PI / 2)
        {
            b = Math.PI - b;
        }
        double cb = Math.cos(b);
        return x * Math.tan(b) - g / (2 * v0 * v0 * cb * cb) * x * x;
    }

    double computeRange(double h0)
    {
        // http://de.wikipedia.org/wiki/Wurfparabel
        //  #Reichweite_bei_von_Null_verschiedener_Anfangsh.C3.B6he
        double g = GRAVITY;
        double b = angleRad;
        double v0 = power;
        double sb = Math.sin(b);
        double f0 =(v0 * v0) / (g + g) * Math.sin(b + b);
        double i = 1.0 + (2 * g * h0) / (v0 * v0 * sb * sb);
        double f1 = 1.0 + Math.sqrt(i);
        return f0 * f1;
    }

    Point2D computeRequiredAngles()
    {
        // http://en.wikipedia.org/wiki/Trajectory_of_a_projectile
        //  #Angle_required_to_hit_coordinate_.28x.2Cy.29
        double v0 = power;
        double g = GRAVITY;
        double vv = v0 * v0;
        double dx = target.getX() - origin.getX();
        double dy = target.getY() - origin.getY();
        double radicand = vv * vv - g * (g * dx * dx + 2 * dy * vv);
        double numerator0 = vv + Math.sqrt(radicand);
        double numerator1 = vv - Math.sqrt(radicand);
        double angle0 = Math.atan(numerator0 / (g*dx));
        double angle1 = Math.atan(numerator1 / (g*dx));
        return new Point2D.Double(angle0, angle1);
    }

    double computeRequiredPower()
    {
        // WolframAlpha told me so...
        double R = target.getX() - origin.getX();
        double h0 = origin.getY() - target.getY();
        double g = GRAVITY;
        double b = angleRad;
        double sb = Math.sin(b);
        double isb = 1.0 / sb;
        double v0 = 
            Math.sqrt(2) * Math.sqrt(g) * R * 
            Math.sqrt(1 / Math.sin(2*b)) /
            Math.sqrt(h0 * Math.sin(2*b) * isb * isb + 2*R);
        return v0;
    }



    void shoot()
    {
        Thread t = new Thread(new Runnable()
        {
            @Override
            public void run()
            {
                executeShot();
            }
        });
        t.setDaemon(true);
        t.start();
    }

    private static Point2D polarToCartesian(
        Point2D polar, Point2D cartesian)
    {
        double x = Math.cos(polar.getX()) * polar.getY();
        double y = Math.sin(polar.getX()) * polar.getY();
        if (cartesian == null)
        {
            cartesian = new Point2D.Double();
        }
        cartesian.setLocation(x, y);
        return cartesian;
    }

    private void executeShot()
    {
        if (projectile != null)
        {
            return;
        }
        projectile = new Projectile();
        projectile.setPosition(origin);

        Point2D velocity = polarToCartesian(
            new Point2D.Double(angleRad, power), null); 
        projectile.setVelocity(velocity);

        long prevTime = System.nanoTime();
        while (projectile.getPosition().getY() >= 0)
        {
            long currentTime = System.nanoTime();
            double dt = TIME_SCALE * (currentTime - prevTime) / 1e9;
            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 static final Point2D ACCELERATION = 
        new Point2D.Double(0, -ProjectileShooter.GRAVITY);

    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;

        double angleDeg = Math.toDegrees(projectileShooter.getAngle());
        double power = projectileShooter.getPower();
        Point2D origin = projectileShooter.getOrigin();
        Point2D target = projectileShooter.getTarget();
        double range = projectileShooter.computeRange(origin.getY());
        double impactX = range+origin.getX();

        g.setColor(Color.BLACK);
        int textY = 20;

        g.drawString("Drag origin and target with mouse ", 10, textY+=16);
        g.drawString("Angle: "+angleDeg, 10, textY+=16);
        g.drawString("Power: "+power, 10, textY+=16);
        g.drawString("Range: "+range, 10, textY+=16);
        g.drawString("Origin: "+origin, 10, textY+=16);
        g.drawString("Impact: "+impactX, 10, textY+=16);
        g.drawString("Target: "+target, 10, textY+=16);

        Point2D requiredAngles = projectileShooter.computeRequiredAngles();
        double requiredAngleDeg0 = Math.toDegrees(requiredAngles.getX());
        double requiredAngleDeg1 = Math.toDegrees(requiredAngles.getY());
        g.drawString("Assuming fixed power:", 10, textY+=16);
        g.drawString("Required angle 0: "+requiredAngleDeg0, 10, textY+=16);
        g.drawString("Required angle 1: "+requiredAngleDeg1, 10, textY+=16);

        double requiredPower = projectileShooter.computeRequiredPower();
        g.drawString("Assuming fixed angle:", 10, textY+=16);
        g.drawString("Required power: "+requiredPower, 10, textY+=16);

        Projectile projectile = projectileShooter.getProjectile();
        if (projectile != null)
        {
            g.setColor(Color.RED);
            Point2D position = projectile.getPosition();
            int px = (int)position.getX();
            int py = getHeight() - (int)position.getY();
            g.fillOval(px-10, py-10, 20, 20);
        }

        g.setColor(Color.GREEN);
        int ox = (int)origin.getX();
        int oy = getHeight() - (int)origin.getY();
        g.fillOval(ox-10, oy-10, 20, 20);

        g.setColor(Color.BLUE);
        int tx = (int)target.getX();
        int ty = getHeight() - (int)target.getY();
        g.fillOval(tx-10, ty-10, 20, 20);

        g.setColor(Color.BLACK);
        g.drawLine((int)impactX, 0, (int)impactX, getHeight());

        g.setColor(Color.GRAY);

        Shape trajectory = createTrajectory(
            Math.min(origin.getX(), impactX),
            Math.max(origin.getX(), impactX));
        g.draw(trajectory);
    }

    private Shape createTrajectory(double x0, double x1)
    {
        Path2D path = new Path2D.Double();
        Point2D origin = projectileShooter.getOrigin();
        double y0 = projectileShooter.computeY(Math.abs(origin.getX()-x0));
        path.moveTo(x0, getHeight()-(y0+origin.getY()));
        for (double x=x0; x<=x1; x++)
        {
            double y = projectileShooter.computeY(Math.abs(origin.getX()-x));
            path.lineTo(x, getHeight()-(y+origin.getY()));
        }
        return path;
    }

}

这篇关于重力与predetermined目的地的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆