Алгоритм нацеливания на частицы - PullRequest
1 голос
/ 30 марта 2011

Я строю системы частиц, одна из функций, которую я хотел бы добавить, - это «цель».То, что я хочу сделать, это установить цель X, Y для каждой частицы и заставить ее двигаться туда, но не по прямой линии (хотя бы), а с учетом всех других эффектов движения, применяемых к частице.

Соответствующие параметры моих частиц имеют:

  • posx, posy : единицы с произвольными значениями.На каждом тике speedx и speedy добавляются к posx и posy соответственно
  • speedx, speedy : единицы с произвольными значениями.На каждом тике Accex и Acly добавляются к SpeedX Speedy соответственно, если таковые имеются)
  • Accelx, Acly : Inits с произвольными значениями.При текущей реализации остается неизменным на протяжении жизни частицы.
  • жизнь : начинается с произвольного значения, и 1 уменьшается с каждым тактом системы.

Чего я хочу добиться, так это чтобы частица достигла цели X, Y на последнем жизненном такте, начиная с ее первоначальных значений (скоростей и ускорений), поэтому движение к цели будет выглядеть «плавным».Я думал об ускорении в направлении цели, при этом пересчитывая необходимую силу ускорения на каждом тике.Это не кажется правильным, хотя, хотел бы услышать некоторые предложения.

Ответы [ 3 ]

5 голосов
/ 30 марта 2011

Для «плавного» движения вы либо сохраняете постоянную скорость, либо постоянную ускорения, либо постоянную рывка. Это зависит от того, что вы называете «гладким» и что вы называете «скучным». Давайте сохраним постоянное ускорение.

С физической точки зрения у вас есть это ограничение

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)

(Чтобы это работало, скорость должна быть в той же единице, что и время, например, «пикселей на тик», а жизнь - это количество «тиков».)

Поскольку вы используете интеграцию Эйлера , это не приведет частицы точно к цели. Но я сомневаюсь, что это будет реальной проблемой.

Работает как шарм:

result

Еще одна картинка, на этот раз с постоянным рывком

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

Похоже, есть еще один изгиб кривой ... enter image description here

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);
    }
  }
}
2 голосов
/ 30 марта 2011

Вы можете считать, что ваша частица изначально «прикладывает» силу (Fv), которая соответствует инерции, которую она имеет от своей начальной скорости. Затем вы применяете силу притяжения (Фа), которая пропорциональна расстоянию до цели. Затем вы можете суммировать эти силы, и, учитывая вес частицы, вы можете вывести ускорение для рассмотрения в момент времени t.

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

Тогда вы можете вычислить v (t) из v (t-1) и a (t) как обычно

Редактировать: я забыл, что жизнь частицы может быть напрямую рассчитана с расстояния до цели (например: life = distance / initialDistance будет идти от 1 в начале и приближаться к 0 возле цели)

Редактировать: Вы могли бы думать об этом как своего рода магнит. См. Википедию для формулы силы .

1 голос
/ 30 марта 2011

Одним из видов движения, которое вы можете использовать, является равномерное ускорение. http://en.wikipedia.org/wiki/Acceleration#Uniform_acceleration

. Ваши частицы сделают небольшое движение в направлении цели и ударит ее с довольно высокой скоростью

.критерии, выполните следующие действия:

  • вычислите расстояние от цели, которое будет иметь частица в конце своего времени жизни, предполагая, что скорость не изменится с этого момента.

  • это расстояние положить в этом уравнении: http://upload.wikimedia.org/math/6/2/9/6295e1819e6bfe1101506caa4b4ec706.png и решить его для

  • использовать это как ваше ускорение

  • Делайте это отдельно для x и y

...