0
\$\begingroup\$

I'm having trouble implementing Flock Behaviour. What I expect is that the boids should move towards the mouse coordinate but they dont.

Thanks in advance.

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import javax.swing.JPanel;

public class MainPanel extends JPanel implements Runnable, MouseMotionListener{
    private Target mouse;
    private List<Boid> boids;
    int delay = 20;
    private int width = 900, height = 900;
    Thread thread;



    public MainPanel() {
        setPreferredSize(new Dimension(width, height));
        setBackground(Color.BLACK);
        setFocusable(true);
        addMouseMotionListener(this);
        thread = new Thread(this);
        mouse = new Target(20, 40);;

        boids = new ArrayList<>();


        boids.add(new Boid(320, 30, this));
        boids.add(new Boid(330, 80, this));
        boids.add(new Boid(360, 130, this));
        boids.add(new Boid(420, 20, this));
        boids.add(new Boid(390, 30, this));

        thread.start();
    }

    @Override
    public void run() {
        while (true) {
            mouse.update();

            for (Boid b : boids) {
                b.run();
            }

            repaint();

            try {
                thread.sleep(delay);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g;
        mouse.paint(g2d);

        for (Boid b : boids) {
            b.paint(g2d);
        }

        g2d.setColor(Color.GREEN);
        String s1 = "MX: %8.2f         MY: %8.2f";
        String s2 = "CX: %8.2f         CY: %8.2f";
        g2d.drawString(String.format(s1, mouse.getX(), mouse.getY()), 10, 20);
    }

    public List<Boid> getBoids() {
        return boids;
    }

    public double getTargetX() {
        return mouse.getX();
    }
    public double getTargetY() {
        return mouse.getY();
    }

    @Override
    public void mouseDragged(MouseEvent e) {
        // TODO Auto-generated method stub
    }

    @Override
    public void mouseMoved(MouseEvent e) {
        mouse.setPos(e.getX(), e.getY());
    }
}


-----------------------------------------------------------

import java.awt.Color;
import java.awt.Graphics;
import java.util.List;

public class Boid {
    private double x, y;
    private double accX, accY; // acceleration
    private double vx, vy; // velocity
    private MainPanel panel;
    double maxSpeed = 16;
    //double maxForce = 3;
    double mass = 2;
    double weightingFactor = 5. / 6;
    int r = 40;


    // separation
    double sepX;
    double sepY;

    // alignment
    double aliX;
    double aliY;

    // cohesion
    double cohX;
    double cohY;

    // seek
    double seeX;
    double seeY;

    public Boid(double x, double y, MainPanel panel) {
        // current coordinate
        this.x = x;
        this.y = y;

        // acceleration
        accX = 0;
        accY = 0;

        // velocity
        vx = 3;
        vy = 3;


        sepX = 0;
        sepY = 0;
        aliX = 0;
        aliY = 0;
        cohX = 0;
        cohY = 0;
        seeX = 0;
        seeY = 0;

        this.panel = panel;
    }

    public void run() {
        flock();
        update();
    }

    private void update() {
        // update velocity
        //vx = vx + accX;
        //vy = vy + accY;

        // limit speed
        //vx = Math.min(vx, maxSpeed);
        //vy = Math.min(vy, maxSpeed);

        x = x + vx;
        y = y + vy;

        // reset acceleration to 0 each cycle
        accX = 0;
        accY = 0;
    }

    private void flock() {
        seek();


        List<Boid> boids = panel.getBoids();
        // accumulate a new acceleration each time based on three rules
        separete(boids);
        align(boids);
        cohesion(boids);

        // arbitrarily weight these forces
        sepX = sepX * 1.5;
        sepY = sepY * 1.5;

        aliX = aliX * 1;
        aliY = aliY * 1;

        cohX = cohX * 1;
        cohY = cohY * 1;

        // add the force vectors to acceleration
        // We could add mass here if we want A = F / M

        accX = seeX + sepX + aliX + cohX;
        accY = seeY + sepY + aliY + cohY;
        vx = accX;
        vy = accY;
    }

    // Checks for nearby boids and steers away
    private void separete(List<Boid> boids) {
        double desiredseparation = 25.0f;
        double steeringX = 0;
        double steeringY = 0;
        int count = 0;

        // For every boid in the system, check if it's too close
        double ed = 0;
        double dx = 0;
        double dy = 0;
        for (Boid other : boids) {
            dx = other.getX() - x;
            dy = other.getY() - y;
            ed = Math.sqrt(dx * dx + dy * dy);
            if (ed > 0 && ed < desiredseparation) {
                // calculate vector pointing away from neighbour
                double diffX = other.getX() - x;
                double diffY = other.getY() - y;

                // normalise
                diffX = diffX / ed;
                diffY = diffY / ed;

                // weight by distance
                diffX = diffX / ed;
                diffY = diffY / ed;

                steeringX = steeringX + diffX;
                steeringY = steeringY + diffY;
                count++; // count how many
            }
        }

        // average -- ??
        if (count > 0) {
            steeringX = steeringX / count;
            steeringY = steeringY / count;
        }

        ed = Math.sqrt(steeringX * steeringX + steeringY * steeringY);
        if (ed > 0) {
            double desiredVX = steeringX / ed * maxSpeed;
            double desiredVY = steeringY / ed * maxSpeed;

            double steeringVX = desiredVX - vx;
            double steeringVY = desiredVY - vy;

            double tvx = ((vx * weightingFactor) + (steeringVX * (1 - weightingFactor) / mass));
            double tvy = ((vy * weightingFactor) + (steeringVY * (1 - weightingFactor) / mass));
            double tve = Math.sqrt(tvx * tvx + tvy * tvy);
            double tvlen = Math.min(tve, maxSpeed);
            vx = tvx / tve * tvlen;
            vy = tvy / tve * tvlen;

            sepX = vx;
            sepY = vy;
        }

        sepX = steeringX;
        sepY = steeringY;
    }

    //Calculate the average velocity for every nearby boid
    private void align(List<Boid> boids) {
        double neighborDistance = 50;
        double sumX = 0;
        double sumY = 0;
        int count = 0;

        double ed = 0;
        double dx = 0;
        double dy = 0;
        for (Boid other : boids) {
            dx = other.getX() - x;
            dy = other.getY() - y;
            ed = Math.sqrt(dx * dx + dy * dy);
            if (ed > 0 && ed < neighborDistance) {
                sumX = sumX + other.getVX();
                sumY = sumY + other.getVY();
                count++;
            }
        }

        if (count > 0) {
            sumX = sumX / count;
            sumY = sumY / count;
            ed = Math.sqrt(sumX * sumX + sumY * sumY);

            double desiredVX = sumX / ed * maxSpeed;
            double desiredVY = sumY / ed * maxSpeed;

            double steeringVX = desiredVX - vx;
            double steeringVY = desiredVY - vy;

            double tvx = ((vx * weightingFactor) + (steeringVX * (1 - weightingFactor) / mass));
            double tvy = ((vy * weightingFactor) + (steeringVY * (1 - weightingFactor) / mass));
            double tve = Math.sqrt(tvx * tvx + tvy * tvy);
            double tvlen = Math.min(tve, maxSpeed);
            vx = tvx / tve * tvlen;
            vy = tvy / tve * tvlen;

            aliX = vx;
            aliY = vy;
        } else {
            aliX = 0;
            aliY = 0;
        }
    }

    // Calculate steering vector towards that position for the average position (i.e. center) of all nearby boids
    private void cohesion(List<Boid> boids) {
        double neighborDistance = 50;
        double sumX = 0;
        double sumY = 0;
        int count = 0;

        double ed = 0;
        double dx = 0;
        double dy = 0;
        for (Boid other : boids) {
            dx = other.getX() - x;
            dy = other.getY() - y;
            ed = Math.sqrt(dx * dx + dy * dy);
            if (ed > 0 && ed < neighborDistance) {
                sumX = sumX + other.getVX();
                sumY = sumY + other.getVY();
                count++;
            }
        }

        if (count > 0) {
            sumX = sumX / count;
            sumY = sumY / count;
            seek(sumX, sumY);
        } else {
            cohX = 0;
            cohY = 0;
        }
    }

    private void seek(double tx, double ty) {
        double dx = tx - x;
        double dy = ty - y;
        double ed = Math.sqrt(dx * dx + dy * dy); // distance to the target
        if (ed > 0) {
            double desiredVX = dx / ed * maxSpeed;
            double desiredVY = dy / ed * maxSpeed;
            // double steeringVX = desiredVX + vx;
            // double steeringVY = desiredVY + vy;
            double steeringVX = desiredVX - vx;
            double steeringVY = desiredVY - vy;

            double v = Math.sqrt(steeringVX * steeringVX + steeringVY * steeringVY);
            if (v != 0.0) {
                // truncate with max speed
                steeringVX = steeringVX / v * Math.min(v, maxSpeed);
                steeringVY = steeringVY / v * Math.min(v, maxSpeed);
            }

            double tvx = ((vx * weightingFactor) + (steeringVX * (1 - weightingFactor) / mass));
            double tvy = ((vy * weightingFactor) + (steeringVY * (1 - weightingFactor) / mass));
            double tve = Math.sqrt(tvx * tvx + tvy * tvy);
            double tvlen = Math.min(tve, maxSpeed);
            vx = tvx / tve * tvlen;
            vy = tvy / tve * tvlen;

            cohX = vx;
            cohY = vy;
        }
    }

    private void seek() {
        double dx = panel.getTargetX() - x;
        double dy = panel.getTargetY() - y;
        double ed = Math.sqrt(dx * dx + dy * dy); // distance to the target
        if (ed > 0) {
            double desiredVX = dx / ed * maxSpeed;
            double desiredVY = dy / ed * maxSpeed;
            // double steeringVX = desiredVX + vx;
            // double steeringVY = desiredVY + vy;
            double steeringVX = desiredVX - seeX;
            double steeringVY = desiredVY - seeY;

            double v = Math.sqrt(steeringVX * steeringVX + steeringVY * steeringVY);
            if (v != 0.0) {
                // truncate with max speed
                steeringVX = steeringVX / v * Math.min(v, maxSpeed);
                steeringVY = steeringVY / v * Math.min(v, maxSpeed);
            }

            double tvx = ((seeX * weightingFactor) + (steeringVX * (1 - weightingFactor) / mass));
            double tvy = ((seeY * weightingFactor) + (steeringVY * (1 - weightingFactor) / mass));
            double tve = Math.sqrt(tvx * tvx + tvy * tvy);
            double tvlen = Math.min(tve, maxSpeed);
            seeX = tvx / tve * tvlen;
            seeY = tvy / tve * tvlen;
        }
    }

    public void paint(Graphics g) {
        g.setColor(Color.YELLOW);
        g.fillRect((int) (x + .5), (int) (y + .5), 20, 20);
        g.setColor(Color.RED);
        g.drawOval((int)x - r / 2, (int)y - r / 2, r, r);
    }

    public double getX() {
        return x;
    }

    public double getY() {
        return y;
    }

    // Get velocity 
    public double getVX() {
        return vx;
    }

    public double getVY() {
        return vy;
    }
}


-----------------------------------------------------------

import java.awt.Color;
import java.awt.Graphics;

public class Target {
    private int x, y, vx, vy, r;

    public Target(int x, int y) {
        this.x = x;
        this.y = y;
        r = 100;
    }

    public void setPos(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public void update() {
    }

    public void stop() {
        vx = 0;
        vy = 0;
    }

    public void paint(Graphics g) {
        g.setColor(Color.BLUE);
        g.fillRect(x, y, 10, 10);
        g.setColor(Color.RED);
        g.drawOval(x - r / 2, y - r / 2, r, r);
    }

    public double getX() {
        return x;
    }

    public double getY() {
        return y;
    }
}

-----------------------------------------------------------

import java.awt.Container;

import javax.swing.JFrame;

public class Test extends JFrame {
    public Test() {
        Container container = getContentPane();
        container.add(new MainPanel());
        pack();
    }

    public static void main(String[] args) {
        Test gameFrame = new Test();
        gameFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        gameFrame.setVisible(true);
    }
}
\$\endgroup\$
5
  • 1
    \$\begingroup\$ mouse is used solely inside MainPanel, but the flock behavior is calculated inside Boid which does not make use of MainPanel.mouse. Thus the mouse coordinates have no influence on the swarm behavior in your code. \$\endgroup\$
    – Thomas
    Apr 10, 2017 at 5:15
  • 1
    \$\begingroup\$ I see from a deleted answer that you seem to have created a duplicate account. You can follow the instructions here to merge your accounts, which will allow you to post comments on this page and edit your question if you need to, as well as mark an answer as accepted. \$\endgroup\$
    – DMGregory
    Apr 10, 2017 at 16:32
  • \$\begingroup\$ @DMGregory, I merged the accounts. Thanks for the instructions. \$\endgroup\$
    – hau
    Apr 14, 2017 at 16:01
  • \$\begingroup\$ @Thomas, Thanks for pointing out. I fixed the code to use mouse coordinate. \$\endgroup\$
    – hau
    Apr 16, 2017 at 0:11
  • \$\begingroup\$ You could checkout github.com/ClickerMonkey/Steerio to see how steering behaviors are implemented in Java. \$\endgroup\$ Apr 16, 2017 at 1:37

1 Answer 1

1
\$\begingroup\$

I believe the problem is that the boids do not know where the mouse is. I searched your source for calls to getTargetX or getTargetY and didn't find any.

The standard boids algorithm doesn't use a target. I believe you can modify your implementation to include one by adding another influence in Boids.run that draws the boid toward the mouse position.

Something along these lines would make sense to me:

Add a function to find the target position. This can start out as a copy of seek. Instead of taking in parameters tx and ty it will read them from the panel, and instead of saving results to cohX and cohY it will save them to two new members.

Call this new function from flock, and then add the values of new members to accX and accY.

\$\endgroup\$
1
  • \$\begingroup\$ T,I implemented the instructions but still need some adjustments because the boids do not smoothly flock. \$\endgroup\$
    – hau
    Apr 16, 2017 at 0:09

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .