1

我正在构建一个粒子系统,我想添加的功能之一是“目标”功能。我想要做的是为每个粒子设置一个 X、Y 目标并让它去那里,虽然不是直线(duh),但考虑到应用于粒子的所有其他运动效果。

我的粒子具有的相关参数:

  • posx, posy:具有任意值的初始化。在每个刻度上 speedx 和 speedy 分别添加到 posx 和 posy
  • speedx, speedy:具有任意值的初始化。在每个刻度上,accelx 和 accely 分别添加到 speedx speedy(如果有)
  • accelx, accely:具有任意值的初始化。当前的实现在粒子的整个生命周期内保持不变。
  • life:以任意值开始,系统每跳一次减 1。

我想要实现的是粒子在它的最后一个生命周期到达目标 X,Y,同时从它的原始值(速度和加速度)开始,因此朝向目标的运动看起来“平滑”。我正在考虑在目标方向上加速它,同时重新计算每个刻度所需的加速力。不过感觉不太对,想听听一些建议。

4

3 回答 3

5

对于“平滑”运动,您要么保持速度恒定,要么保持加速度恒定,要么保持加速度恒定。这取决于你所说的“流畅”和“无聊”。让我们保持加速度不变。

从物理学的角度来看,你有这个约束

targetx - posx = speedx*life + 1/2accelx * life * life
targety - posy = speedy*life + 1/2accely * life * life

因为行驶的距离是v*t+1/2at^2。求解未知加速度给出

accelx = (targetx - posx - speedx*life) / (1/2 * life * life)
accely = (targety - posy - speedy*life) / (1/2 * life * life)

(要使其快速工作,必须与时间采用相同的单位,例如“每滴答的像素数”,而寿命是“滴答”的数量。)

由于您使用euler 积分,这不会将粒子精确地带到目标上。但我怀疑这将是一个真正的问题。

奇迹般有效:

结果

另一张照片,这次是不断的混蛋

jerkx = 6.0f*(targetx-x - speedx*life - 0.5f*accelx*life*life)/(life*life*life) 

看起来曲线还有另一个弯... 在此处输入图像描述

Java 代码

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;

@SuppressWarnings("serial")
public class TargetTest extends JPanel {

  List<Particle> particles = new ArrayList<Particle>();
  float tx, ty; // target position

  public TargetTest() {

    tx = 400;
    ty = 400;
    for (int i = 0; i < 50; i++)
      particles.add(new Particle(tx / 2 + (float) (tx * Math.random()), ty / 2
          + (float) (ty * Math.random())));

    this.setPreferredSize(new Dimension((int) tx * 2, (int) ty * 2));
  }

  @Override
  protected void paintComponent(Graphics g1) {
    Graphics2D g = (Graphics2D) g1;
    g.setColor(Color.black);
    // comment next line to draw curves
    g.fillRect(0, 0, getSize().width, getSize().height);

    for (Particle p : particles) {
      p.update();
      p.draw(g);
    }
  }

  public static void main(String[] args) {
    EventQueue.invokeLater(new Runnable() {
      public void run() {
        JFrame f = new JFrame("Particle tracking");
        final TargetTest world = new TargetTest();
        f.add(world);

        // 1 tick every 50 msec
        new Timer(50, new ActionListener() {
          @Override
          public void actionPerformed(ActionEvent arg0) {
            world.repaint();
          }
        }).start();

        f.pack();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setVisible(true);
      }
    });
  }

  class Particle {
    float x, y;// position
    float vx, vy;// speed
    float ax, ay;// acceleration
    float jx, jy;// jerk

    int life; // life

    float lastx, lasty;// previous position, needed to draw lines
    int maxlife; // maxlife, needed for color

    public Particle(float x, float y) {
      this.x = x;
      this.y = y;
      // pick a random direction to go to
      double angle = 2 * Math.PI * Math.random();
      setVelocity(angle, 2);// 2 pixels per tick = 2 pixels per 50 msec = 40
                            // pixels per second

      // the acceleration direction 'should' be close to being perpendicular to
      // the speed,
      // makes it look interesting, try commenting it if you don't believe me ;)
      if (Math.random() < 0.5)
        angle -= Math.PI / 2;
      else
        angle += Math.PI / 2;
      // add some randomness
      angle += (Math.random() - 0.5) * Math.PI / 10;
      setAcceleration(angle, 0.1);

      life = (int) (100 + Math.random() * 100);
      maxlife = life;
      lastx = x;
      lasty = y;
    }

    public void setVelocity(double angle, double speed) {
      vx = (float) (Math.cos(angle) * speed);
      vy = (float) (Math.sin(angle) * speed);
    }

    public void setAcceleration(double angle, double speed) {
      ax = (float) (Math.cos(angle) * speed);
      ay = (float) (Math.sin(angle) * speed);
    }

    @SuppressWarnings("unused")
    private void calcAcceleration(float tx, float ty) {
      ax = 2 * (tx - x - vx * life) / (life * life);
      ay = 2 * (ty - y - vy * life) / (life * life);
    }

    private void calcJerk(float tx, float ty) {
      jx = 6.0f * (tx - x - vx * life - 0.5f * ax * life * life)
          / (life * life * life);
      jy = 6.0f * (ty - y - vy * life - 0.5f * ay * life * life)
          / (life * life * life);
    }

    public void update() {
      lastx = x;
      lasty = y;
      if (--life <= 0)
        return;

      // calculate jerk
      calcJerk(tx, ty);
      // or uncomment and calculate the acceleration instead
      // calcAcceleration(tx,ty);

      ax += jx;
      ay += jy;// increase acceleration

      vx += ax;
      vy += ay;// increase speed

      x += vx;
      y += vy;// increase position
    }

    public void draw(Graphics2D g) {
      if (life < 0)
        return;
      g.setColor(new Color(255 - 255 * life / maxlife, 
            255 * life / maxlife,0));
      g.drawLine((int) x, (int) y, (int) lastx, (int) lasty);
    }
  }
}
于 2011-03-30T11:11:21.967 回答
2

您可以认为您的粒子最初“施加”了一个力(Fv),该力对应于它从初始速度开始的惯性。然后施加与目标距离成正比的吸引力 (Fa)。然后,您可以将这些力相加,并给定粒子权重,您可以推断出在时间 t 时要考虑的加速度。

Fa(t) = (Constant / distanceToTarget(t))* [direction to target]
Fv(t) = [initialForce] * dampening(t)
a(t) = (Fa(t) + Fv(t)) / mass

然后你可以像往常一样从 v(t-1) 和 a(t) 计算 v(t)

编辑:我忘记了粒子的生命可以直接从到目标的距离计算(例如:生命=距离/初始距离将从开始时的 1 到目标附近的接近 0)

编辑:你可以把它想象成一种磁铁。有关力公式,请参见维基百科。

于 2011-03-30T10:42:56.797 回答
1

您可以使用的一种运动是匀加速http://en.wikipedia.org/wiki/Acceleration#Uniform_acceleration

你的粒子会平滑地向目标移动并以相当高的速度击中它

为了满足您规定的标准,请执行以下操作:

于 2011-03-30T11:01:43.817 回答