Search code examples
javajframespritespiral

How to make a sprite spiral to the center (in Java but if you know a formula, it would help too)


I'm making a game in java where enemy sprites spiral to the center and damage the main tower. The only thing I'm having problems with is the formula to make the sprite spiral. All I found on the internet it this: http://scratch.mit.edu/projects/1439249/

That is sort of what I want to do but I want to make them spiral to a point from outside the JFrame, not from a point within the JFrame.

I am in Secondary 4 and I don't have to much knowledge about such formulas yet and sorry if I have problems understanding the formulas. Thanks in advance!


Solution

  • One simple way of making a sprite appear to spiral is to pretend that it is attached to an arm, like the hand of a clock, that rotates around the center of the spiral. As that arm rotates, slowly move the sprite down the arm towards the center. What you end up with is a classic Archimedan Spiral

    I can mock up some code for you, but that's going to take a few minutes.


    Okay, here's the code.

    public static double getArmX(double length, double angle) {
        return Math.cos(angle) * length;
    }
    
    public static double getArmY(double length, double angle) {
        return Math.sin(angle) * length;
    }
    

    These are the core of the math. They return the x and y values of an entity that is at the specified distance from the center (length) and angle from the center (angle).

    Now, I don't know how you have your code set up, but lets pretend we have a double named spiralProgress that represents how far into its spiral your entity is. At spiralProgress == 0, the entity is just starting, and at spiralProgress == 1, the entity is at the center.

    Here is what the code to get the x and y for the entity would look like:

    double startingRadius = 64;
    double rotations = 10;
    
    double x = getArmX(startingRadius * (1-t), t * rotations * Math.PI * 2);
    double y = getArmY(startingRadius * (1-t), t * rotations * Math.PI * 2);
    

    In that snippet, startingRadius is how many units (pixels, if thats what x and y means in your program), the entity should start away from the center, and rotations is how many times the entity should loop around the center before reaching it.

    The coordinates this returns are for a spiral around {0, 0}, so if you want to spiral around some other point, say {screenWidth / 2, screenHeight / 2}, you'd add screenWidth / 2 to x and screenHeight / 2 to y.

    Here is a full Java program that demonstrates this math. Click the mouse anywhere in the window to reset the spiral.

    package net.eonz.stackoverflow.spiral;
    
    import java.awt.BorderLayout;
    import java.awt.Canvas;
    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.Graphics;
    import java.awt.event.MouseEvent;
    import java.awt.event.MouseListener;
    import java.awt.image.BufferStrategy;
    
    import javax.swing.JFrame;
    
    public class Game extends Canvas implements Runnable, MouseListener {
        private static final long serialVersionUID = 1L;
        public static final String NAME = "untitled";
        public static final int HEIGHT = 600;
        public static final int WIDTH = 600;
        public static final int SCALE = 1;
    
        private boolean running = false;
    
        public void start() {
            running = true;
            new Thread(this).start();
            this.addMouseListener(this);
        }
    
        public void stop() {
            running = false;
        }
    
        public void run() {
            long last = System.currentTimeMillis();
    
            while (running) {
                long now = System.currentTimeMillis();
                double dt = (now - last) / 1000.0;
                last = now;
                update(dt);
                render();
            }
        }
    
        double t = 0;
    
        public void update(double dt) {
            t += dt / 16;
            if (t > 1)
                t = 1;
        }
    
        public void render() {
            BufferStrategy bs = getBufferStrategy();
    
            if (bs == null) {
                createBufferStrategy(3);
                return;
            }
    
            Graphics g = bs.getDrawGraphics();
            g.setColor(Color.white);
            g.fillRect(0, 0, this.getWidth(), this.getHeight());
    
            /* SPIRAL MATH IS HERE */
    
            double startingRadius = this.getHeight() * 0.40;
            double rotations = 10;
    
            double x = getArmX(startingRadius * (1 - t), t * rotations * Math.PI
                    * 2);
            double y = getArmY(startingRadius * (1 - t), t * rotations * Math.PI
                    * 2);
    
            g.setColor(Color.black);
            g.fillRect((int) (x - 8) + this.getWidth() / 2,
                    (int) (y - 8) + this.getHeight() / 2, 16, 16);
    
            /* END SPIRAL MATH */
    
            g.dispose();
            bs.show();
        }
    
        public static double getArmX(double length, double angle) {
            return Math.cos(angle) * length;
        }
    
        public static double getArmY(double length, double angle) {
            return Math.sin(angle) * length;
        }
    
        @Override
        public void mouseClicked(MouseEvent e) {
            this.t = 0;
        }
    
        @Override
        public void mousePressed(MouseEvent e) {
    
        }
    
        @Override
        public void mouseReleased(MouseEvent e) {
    
        }
    
        @Override
        public void mouseEntered(MouseEvent e) {
    
        }
    
        @Override
        public void mouseExited(MouseEvent e) {
    
        }
    
        public static void main(String[] args) {
            Game game = new Game();
            game.setMinimumSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));
            game.setMaximumSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));
            game.setPreferredSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));
    
            JFrame frame = new JFrame(Game.NAME);
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setLayout(new BorderLayout());
            frame.add(game, BorderLayout.CENTER);
            frame.pack();
            frame.setLocationRelativeTo(null);
            frame.setResizable(false);
            frame.setVisible(true);
    
            game.start();
        }
    
    }