如何同时滚动多个对象?

时间:2014-02-05 20:37:21

标签: java swing jframe awt flappy-bird-clone

在此问题之后提出了新问题,找到了here

我是Java的新手,但我正在开发“Flappy Bird”,以了解有关java和图形显示方式的更多信息。我非常感谢任何解决方案或建议。谢谢!

现在,我的程序制作一个随机管道并滚动它,但我不需要它在x1-3 = -83时继续滚动(这是管道完全离开屏幕的时候不再需要)。

问题

如何在我的Game.class滚动多个Pipes.class实例的同时添加预设距离?我可以找出它们之间的距离,但就显示不止一个,我不知道该怎么做。最多只能同时显示3个管道。

如何显示主菜单的面板,然后在按下开始按钮后切换到管道面板?

Game.java

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

public class Game {

    Pipes panel = new Pipes();

    public Game() {
        JFrame f = new JFrame();

        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.add(panel);
        f.setTitle("Pipe Game");
        f.setResizable(false);
        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);

        Timer timer = new Timer(10, new ActionListener() {  //pipe speed
            @Override
            public void actionPerformed(ActionEvent e) {
                panel.move();
            }
        });
        timer.start();

        Timer refresh = new Timer(30, new ActionListener() {    //refresh rate
            @Override
            public void actionPerformed(ActionEvent e) {
                panel.repaint();
            }
        });
        refresh.start();


    }

    public static void main(String args[]) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                new Game();
            }
        });
    }
}

Pipes.java

import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JPanel;

public class Pipes extends JPanel {
    //Declare and initialiaze variables
    int x1 = 754;               //xVal start
    int x2 = 75;                //pipe width
                                //total width is 83
    int y1 = -1;                //yVal start
    int y2 = setHeightVal();    //pipe height
    int gap = 130;              //gap height

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

        g.clearRect(0,0,750,500);                       //Clear screen
        g.drawRect(x1,y1,x2,y2);                        //Draw part 1
        g.drawRect(x1-3,y2-1,x2+6,25);                  //Draw part 2
        g.drawRect(x1-3,y2+25+gap,x2+6,25);             //Draw part 3
        g.drawRect(x1,y2+25+gap+25,x2,500-y2-49-gap);   //Draw part 4
    }

    public void move() {
        x1--;
    }

    public int getMyX() {   //To determine where the pipe is horizontally
        return x1-3;
    }

    public int getMyY() {   //To determine where the pipe is vertically
        return y2+25;
    }

    public int setHeightVal() {     //Get a random number and select a preset height
        int num = (int)(9*Math.random() + 1);
        int val = 0;
        if (num == 9)
        {
            val = 295;
        }
        else if (num == 8)
        {
            val = 246;
        }
        else if (num == 7)
        {
            val = 216;
        }
        else if (num == 6)
        {
            val = 185;
        }
        else if (num == 5)
        {
            val = 156;
        }
        else if (num == 4)
        {
            val = 125;
        }
        else if (num == 3)
        {
            val = 96;
        }
        else if (num == 2)
        {
            val = 66;
        }
        else
        {
            val = 25;
        }
        return val;
    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(751, 501);
    }
}

1 个答案:

答案 0 :(得分:4)

  

“我如何让我的Game.class滚动多个Pipes.class实例,同时在它们之间添加预设距离?”

这是一些简单的逻辑。您希望使用数据结构来保存管道。这个数据结构将保留的是当时需要绘制的任何数据,如x,y,坐标。对于这个任务,我更喜欢用它自己的draw方法创建一个新类,我将paintComponent的Graphics上下文传递给。例如

public class Pipe {
    int x;
    int y;
    public class Pipe(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public void drawPipe(Graphics g) {
        g.fillRect(x, y, 50, 100);
    }
}

现在这只是一个示例类。上面只绘制一个矩形,但这只是为了向您展示您应该做什么。

接下来,您希望数据结构能够容纳三个Pipe对象,就像数组一样。我更喜欢使用List。您需要List类中的Pipes,并向其添加三个Pipe对象。您可以将x指定为您喜欢的任何内容,以使它们保持相同的距离

public class Pipes extends JPanel {
    List<Pipe> pipes = new ArrayList<Pipe>();

    public Pipes() {
        pipes.add(new Pipe(50, 100));
        pipes.add(new Pipe(150, 100));
        pipes.add(new Pipe(250, 100));
    }
}

现在在paintComponent方法中,您需要做的就是遍历它们并使用其drawPipe方法

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

    for ( Pipe pipe : pipes ){
        pipe.drawPipe(g);
    }
}

现在你移动它们你需要做的就是在计时器中移动x个位置,然后调用repaint。您可能还需要检查x以确保它不会离开屏幕,或者如果您向右移动它们,您可以将它们放在最左边然后乳清离开屏幕,就像传送带一样。所以你可以做这样的事情

private static final int X_INC = 5;
...
Timer timer = new Timer(40, new ActionListener(){
    public void actionPerformed(ActionEvent e) {
        for (Pipe pipe : pipes ){
            if (pipe.x >= screenWidth) {
                pipe.x = 0;
            } else {
                pipe.x += X_INC;
            }
        }
        repaint();
    }
});

正如您所看到的,我所做的是遍历List,只需更改所有x坐标,然后repaint()。因此,您可以使用您需要绘制的任何值创建自己的Pipe类,并在循环中移动它们。


对于速度的更改,而不是使用像10这样的硬编码vakue作为计时器,使用变量delay,您可以像点击按钮一样进行更改

int delay = 100;
JButton speedUp = new JButton("Speed UP");
JButton slowDown = new JButton("Slow Down");
Timer timer = null;
public Pipes() {
    timer = new Timer(delay, new ActionListener(){
        ...
    });
    timer.start();

    speedUp.addActionListener(new ActionListener(){
        public void actionPerformed(ActionEvent e) {
            if (!((delay - 20) < 0)) {
                delay -=20;
                timer.setDelay(delay);
            }
        }
    });
    // do the same for slowDown, but decrease the delay
}

测试一下

import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;


public class Mario extends JPanel {

    private static final int D_W = 800;
    private static final int D_H = 300;
    private static final int X_INC = 5;

    BufferedImage bg;
    BufferedImage pipeImg;

    List<Pipe> pipes = new ArrayList<>();

    int delay = 50;

    Timer timer = null;

    public Mario() {

        try {
            bg = ImageIO.read(new URL("http://farm8.staticflickr.com/7341/12338164043_0f68c73fe4_o.png"));
            pipeImg = ImageIO.read(new URL("http://farm3.staticflickr.com/2882/12338452484_7c72da0929_o.png"));
        } catch (IOException ex) {
            Logger.getLogger(Mario.class.getName()).log(Level.SEVERE, null, ex);
        }

        pipes.add(new Pipe(100, 150, pipeImg));
        pipes.add(new Pipe(400, 150, pipeImg));
        pipes.add(new Pipe(700, 150, pipeImg));

        timer = new Timer(delay, new ActionListener(){
            public void actionPerformed(ActionEvent e) {
                for (Pipe pipe : pipes) {
                    if (pipe.x > D_W) {
                        pipe.x = 0;
                    } else {
                        pipe.x += X_INC;
                    }
                }
                repaint();
            }
        });
        timer.start();
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.drawImage(bg, 0, 0, getWidth(), getHeight(), this);
        for (Pipe pipe : pipes) {
            pipe.drawPipe(g);
        }
    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(D_W, D_H);
    }

    public class Pipe {

        int x;
        int y;
        Image pipe;

        public Pipe(int x, int y, Image pipe) {
            this.x = x;
            this.y = y;
            this.pipe = pipe;
        }

        public void drawPipe(Graphics g) {
            g.drawImage(pipe, x, y, 75, 150, Mario.this);
        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                JFrame frame = new JFrame("Mario Pipes");
                frame.add(new Mario());
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }
}

enter image description here