Plusieurs graphiques mobiles dans Java JFrame


Je suis assez nouveau pour Java et j'apprends la conception de jeux à la minute. Je suis un peu au début, donc ce n'est pas vraiment un jeu. J'ai affaire à un seul thread et à un tableau de nombres.Je dois obtenir plusieurs formes carrées se déplaçant autour d'un écran en même temps. J'ai le code qui fonctionne bien pour un carré mais j'ai des problèmes lorsque j'essaie de l'implémenter sur plusieurs carrés. Je travaille avec deux interfaces et je ne sais pas comment utiliser le tableau pour créer plusieurs cas d'un seul carré aléatoire. Toute aide serait vraiment appréciée. Cheers les gars.

import java.awt.*;
import javax.swing.*;

public class MovingSquares extends JFrame implements Runnable
{
    private static final Dimension WindowSize = new Dimension(600, 600);    
    private static final int NUMGAMEOBJECTS = 30;
    private GameObject[] GameObjectsArray = new GameObject[NUMGAMEOBJECTS];
    static int strtCoX = (int) (Math.random() * 600);
    static int strtCoY = (int) (Math.random() * 600);


    public MovingSquares() {
        this.setTitle("Crazy squares");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        Dimension screensize = java.awt.Toolkit.getDefaultToolkit().getScreenSize();
        int x = screensize.width/2 - WindowSize.width/2;
        int y = screensize.height/2 - WindowSize.height/2;
        setBounds(x, y, WindowSize.width, WindowSize.height);
        setVisible(true);

        Thread t = new Thread(this);
        t.start();      
    }

    public void run() {
        while (true) {
            try {
                Thread.sleep(20);
            } catch (InterruptedException e) { }
            GameObject.move();
            this.repaint();
        }   
    }

    public void paint(Graphics g) {
        g.setColor(Color.WHITE);
        g.fillRect(0, 0, WindowSize.width, WindowSize.height);

        int size = 50;
        int R = (int) (Math.random() * 256);
        int G = (int) (Math.random() * 256);        
        int B = (int) (Math.random() * 256);
        Color c = new Color(R, G, B);
        g.setColor(c);
        g.fillRect(strtCoX, strtCoY, size, size);
    }

    public static void main(String[] args) {
        MovingSquares M = new MovingSquares();
    }
}

Deuxième interface - classe GameObject.

import java.awt.*;

public class GameObject 
{
    private static double x;
    private static double y;
    private Color c;
    public static int ranCoX = (int) (Math.random() * 20);
    public static int ranCoY = (int) (Math.random() * 20);

    public GameObject() {
        int strtX = (int) x;
        int strtY = (int) y;
        int speedX = (int) (Math.random() * 10);
        int speedY = (int) (Math.random() * 10);
    }

    public static void move() {
        int velX = (int) (Math.random() * ranCoX);
        int velY = (int) (Math.random() * ranCoY);

        if (MovingSquares.strtCoY > 600)
            MovingSquares.strtCoY = MovingSquares.strtCoY - 550;
        else if (MovingSquares.strtCoX > 600)
            MovingSquares.strtCoX = MovingSquares.strtCoX - 550;

        MovingSquares.strtCoX += velX;
        MovingSquares.strtCoY += velY;
    }

    public static void paint(Graphics g) {
        int size = 50;
        x = (Math.random() * 600);
        y = (Math.random() * 600);
        int R = (int) (Math.random() * 256);
        int G = (int) (Math.random() * 256);        
        int B = (int) (Math.random() * 256);
        Color c = new Color(R, G, B);
        g.setColor(c);
        g.fillRect((int)x, (int) y, size, size);
    }
}
Author: Giulio Biagini, 2015-01-29

2 answers

Les variables statiques ne seront disponibles qu'une seule fois par Runtime. Pour créer plusieurs GameObject, vous devez éviter les static mots clés.

Puis appelez new GameObject() plusieurs fois pour créer des instances serveral de GameObject, chacune avec son propre ensemble de variables.


Modifier:

  1. déplacer les variables strtCoX et strtCoY dans GameObject (comme suggéré par @andrew thomson )
  2. corriger toutes les références à strtCoX et strtCoY à l'intérieur de GameObject
  3. changer g.fillRect(strtCoX, strtCoY, size, size); en for (GameObject currentObject : GameObjectsArray) g.fillRect(currentObject.strtCoX, currentObject.strtCoY, size, size);

Explication: Les coordonnées sont des attributs de GameObject. Chaque GameObject doit avoir ses propres coordonnées.

=> Votre code devrait fonctionner comme avant

  1. supprimer tous les mots-clés static dans GameObject
  2. remplacer GameObject.move(); par for (GameObject currentObject : GameObjectsArray) currentObject.move();
  3. initialisez votre GameObjectArray dans public MovingSquares() constructeur comme ceci: for (int i = 0; i < GameObjectsArray.length; i++) GameObjectsArray[i] = new GameObject();

Explication: avec new GameObject() nous créons 30 instances (dans cette boucle à l'intérieur du constructeur). Par conséquent nous avons également pour appeler move() 30 fois (et c'est pourquoi cela est également imbriqué dans une boucle)

=> Portez des lunettes de soleil!

 2
Author: slartidan, 2015-01-29 17:12:31

Voici une version de Moving Squares qui, espérons-le, vous aidera à comprendre comment faire une animation.

Carrés Fous

L'une des premières choses que j'ai faites a été de séparer les classes en une classe model, une classe view ou une classe controller. Le modèle modèle / vue / contrôleur aide à séparer les préoccupations.

Regardons la version révisée de votre classe model, la classe GameObject.

package com.ggl.moving.squares;

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

public class GameObject {

    private static final int size = 50;

    private double x;
    private double y;

    private double dx;
    private double dy;

    private int drawingWidth;

    public GameObject(int drawingWidth) {
        x = Math.random() * drawingWidth;
        y = Math.random() * drawingWidth;
        dx = Math.random() * 30D - 15D;
        dy = Math.random() * 30D - 15D;
        this.drawingWidth = drawingWidth;
    }

    public void move() {
        int lowerLimit = size;
        int upperLimit = drawingWidth - size;

        x += dx;
        if (x < lowerLimit) {
            x += upperLimit;
        } else if (x > upperLimit) {
            x -= upperLimit;
        }

        y += dy;
        if (y < lowerLimit) {
            y += upperLimit;
        } else if (y > upperLimit) {
            y -= upperLimit;
        }
    }

    public void draw(Graphics g) {
        g.setColor(generateRandomColor());
        g.fillRect((int) x, (int) y, size, size);
    }

    private Color generateRandomColor() {
        int R = (int) (Math.random() * 256);
        int G = (int) (Math.random() * 256);
        int B = (int) (Math.random() * 256);
        Color c = new Color(R, G, B);
        return c;
    }
}

Il ne reste qu'un seul champ statique, la taille du carré. Tout le reste est une variable ou une méthode dynamique. Cela nous permet de créer plus d'une instance du GameObject.

J'ai changé le nom de la méthode de dessin de peinture pour dessiner. C'est pour que nous ne soyons pas confus quant aux méthodes qui sont des méthodes Swing, et quelles méthodes sont nos méthodes.

Nous passons la largeur du panneau de dessin dans le constructeur. De cette façon, nous n'avons qu'à définir la largeur en un seul endroit. Vous pouvez voir dans la méthode move que nous autorisons une marge taille du carré dans la zone de dessin.

Le constructeur définit une position initiale aléatoire et une vitesse initiale. La méthode de déplacement maintient simplement le carré en mouvement en ligne droite.

Ensuite, regardons la version révisée de votre classe principale, la classe MovingSquares.

package com.ggl.moving.squares;

import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import javax.swing.JFrame;
import javax.swing.SwingUtilities;

public class MovingSquares implements Runnable {
    private static final int DRAWING_WIDTH = 600;
    private static final int NUMGAMEOBJECTS = 30;
    private GameObject[] gameObjectsArray = new GameObject[NUMGAMEOBJECTS];

    private JFrame frame;

    private MovingPanel movingPanel;

    private ObjectsRunnable objectsRunnable;

    public MovingSquares() {
        for (int i = 0; i < gameObjectsArray.length; i++) {
            gameObjectsArray[i] = new GameObject(DRAWING_WIDTH);
        }
    }

    @Override
    public void run() {
        frame = new JFrame();
        frame.setTitle("Crazy squares");
        frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
        frame.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent event) {
                exitProcedure();
            }
        });

        movingPanel = new MovingPanel(gameObjectsArray, DRAWING_WIDTH);
        frame.add(movingPanel);

        frame.pack();
        frame.setLocationByPlatform(true);
        frame.setVisible(true);

        objectsRunnable = new ObjectsRunnable(this, gameObjectsArray);
        new Thread(objectsRunnable).start();
    }

    private void exitProcedure() {
        objectsRunnable.setRunning(false);
        frame.dispose();
        System.exit(0);
    }

    public void repaintMovingPanel() {
        movingPanel.repaint();
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new MovingSquares());
    }
}

Nous définissons ici la largeur du panneau de dessin, ainsi qu'un tableau pour contenir les objets du jeu.

Nous démarrons l'application Swing sur le Event Dispatch thread (EDT) en invoquant la méthode SwingUtilities invokeLater. Une application Swing doit toujours démarrer sur l'EDT.

Nous créons les objets de jeu dans le constructeur et créons les composants Swing dans la méthode run. J'ai déplacé le panneau de dessin dans sa propre classe, dont nous parlerons plus tard.

Nous utilisons un écouteur de fenêtre afin que nous puissions arrêter le thread lorsque nous avons terminé avec l'application.

Nous emballons le JFrame. Le seul endroit où nous spécifions une taille est lorsque nous créons le panneau de dessin. Nous utilisons un JFrame. La seule fois où vous étendez un composant Swing, ou une classe Java, est lorsque vous souhaitez remplacer l'une des méthodes.

Regardons la classe drawing panel, la classe MovingPanel.

package com.ggl.moving.squares;

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

import javax.swing.JPanel;

public class MovingPanel extends JPanel {

    private static final long serialVersionUID = -6291233936414618049L;

    private GameObject[] gameObjectsArray;

    public MovingPanel(GameObject[] gameObjectsArray, int width) {
        this.gameObjectsArray = gameObjectsArray;
        setPreferredSize(new Dimension(width, width));
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);

        g.setColor(Color.WHITE);
        g.fillRect(0, 0, getWidth(), getHeight());

        for (int i = 0; i < gameObjectsArray.length; i++) {
            gameObjectsArray[i].draw(g);
        }
    }

}

Nous remplaçons la méthode paintComponent pour dessiner sur le JPanel. Puisque les objets du jeu se dessinent eux-mêmes, nous appelons la méthode draw sur chaque objet du jeu.

Enfin, regardons l'animation runnable, la classe ObjectsRunnable.

package com.ggl.moving.squares;

import javax.swing.SwingUtilities;

public class ObjectsRunnable implements Runnable {

    private volatile boolean running;

    private GameObject[] gameObjectsArray;

    private MovingSquares movingSquares;

    public ObjectsRunnable(MovingSquares movingSquares,
            GameObject[] gameObjectsArray) {
        this.movingSquares = movingSquares;
        this.gameObjectsArray = gameObjectsArray;
        this.running = true;
    }

    @Override
    public void run() {
        while (running) {
            updateObjects();
            xxx();
            sleep();
        }
    }

    private void updateObjects() {
        for (int i = 0; i < gameObjectsArray.length; i++) {
            gameObjectsArray[i].move();
        }
    }

    private void xxx() {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                movingSquares.repaintMovingPanel();
            }
        });
    }

    private void sleep() {
        try {
            Thread.sleep(200L);
        } catch (InterruptedException e) {
        }
    }

    public synchronized void setRunning(boolean running) {
        this.running = running;
    }

}

Ceci est une animation simple ou boucle de jeu. J'ai mis l'appel repaint dans une autre méthode SwingUtilities invokeLater pour m'assurer que les composants Swing sont mis à jour sur l'EDT.

J'espère que cela vous a été utile.

 2
Author: Gilbert Le Blanc, 2015-01-29 17:46:49