3

Swingで簡単なタワーディフェンスゲームを作成していますが、多くのスプライト(20以上)を画面に表示しようとすると、パフォーマンスの問題が発生しました。

ゲーム全体は、setIgnoreRepaint(true)を持つJPanelで行われます。次に、paintComponentメソッドを示します(conはコントローラーです)。

public void paintComponent(Graphics g){
    super.paintComponent(g);
    //Draw grid
    g.drawImage(background, 0, 0, null);
    if (con != null){
        //Draw towers
        for (Tower t : con.getTowerList()){
            t.paintTower(g);
        }
        //Draw targets
        if (con.getTargets().size() != 0){
            for (Target t : con.getTargets()){
                t.paintTarget(g);
            }
            //Draw shots
            for (Shot s : con.getShots()){
                s.paintShot(g);
            }
        }
    }
}

Targetクラスは、現在の場所にBufferedImageを単純にペイントします。getImageメソッドは、新しいBufferedImageを作成せず、Controllerクラスのインスタンスを返すだけです。

public void paintTarget(Graphics g){
    g.drawImage(con.getImage("target"), getPosition().x - 20, getPosition().y - 20, null);
}

各ターゲットはスイングタイマーを実行してその位置を計算します。これは、それが呼び出すActionListenerです。

public void actionPerformed(ActionEvent e) {
    if (!waypointReached()){
        x += dx;
        y += dy;
        con.repaintArea((int)x - 25, (int)y - 25, 50, 50);
    }
    else{
        moving = false;
        mover.stop();
    }
}

private boolean waypointReached(){
    return Math.abs(x - currentWaypoint.x) <= speed && Math.abs(y - currentWaypoint.y) <= speed;
}

それ以外は、repaint()は新しいタワーを配置するときにのみ呼び出されます。

どうすればパフォーマンスを向上させることができますか?

4

5 に答える 5

5

各ターゲットはスイングタイマーを実行してその位置を計算します。これは、それが呼び出すActionListenerです。

これはあなたの問題かもしれません-各ターゲット/弾丸(私は推測しますか?)に、それ自体を更新してそれ自体を描画するタイミングを追跡する責任があるのは、かなりの作業のように聞こえます。より一般的なアプローチは、次の行に沿ってループを作成することです。

while (gameIsRunning) {
  int timeElapsed = timeSinceLastUpdate();
  for (GameEntity e : entities) {
    e.update(timeElapsed);
  }
  render(); // or simply repaint in your case, I guess
  Thread.sleep(???); // You don't want to do this on the main Swing (EDT) thread though
}

基本的に、チェーンのさらに上のオブジェクトには、ゲーム内のすべてのエンティティを追跡し、それらに更新するように指示し、それらをレンダリングする責任があります。

于 2012-07-11T12:45:52.310 に答える
3

私は当初、あまりにも多くのタイマーの理論に警戒していました。使用例javax.swing.Timer「単一の共有スレッド(実行される最初のTimerオブジェクトによって作成される)」。数十またはスコアでさえ完全に問題ありませんが、通常、数百は遅くなり始めます。期間とデューティサイクルに応じて、EventQueue最終的には飽和します。私はあなたがあなたのデザインを批判的に調べる必要があるという他の人たちに同意します、しかしあなたはで実験したいかもしれませんsetCoalesce()。参考までに、ここにプロファイリングしたいsscceがあります。

import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.Timer;

/**
* @see http://stackoverflow.com/a/11436660/230513
*/
public class TimerTest extends JPanel {

    private static final int N = 25;

    public TimerTest() {
        super(new GridLayout(N, N));
        for (int i = 0; i < N * N; i++) {
            this.add(new TimedLabel());
        }
    }

    private static class TimedLabel extends JLabel {

        private static final Random r = new Random();

        public TimedLabel() {
            super("000", JLabel.CENTER);
            // period 100 to 1000 ms; frequency 1 to 10 Hz.
            Timer timer = new Timer(r.nextInt(900) + 100, new ActionListener() {

                @Override
                public void actionPerformed(ActionEvent e) {
                    TimedLabel.this.setText(next());
                }
            });
            timer.setCoalesce(true);
            timer.start();
        }

        private String next() {
            return String.valueOf(r.nextInt(900) + 100);
        }
    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(640, 480);
    }

    private void display() {
        JFrame f = new JFrame("TimerTet");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.add(new JScrollPane(this));
        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

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

            @Override
            public void run() {
                new TimerTest().display();
            }
        });
    }
}
于 2012-07-11T15:58:06.260 に答える
3

ここで問題になるのは、ゲームのセットアップのロジック全体(攻撃は意図されていません)だと思います。別の回答で述べられているように、各エンティティの動きを処理するタイマーが異なりますが、これは良くありません。いくつかのゲームループの例を見て、これに合わせて調整することをお勧めします。読みやすさとパフォーマンスの大幅な向上に、いくつかの優れたリンクがあります。

于 2012-07-11T13:03:51.420 に答える
2
  1. Swingでペイントする方が良いです(すべての場合> = Java5)Swing Timer排他的に使用します

  2. この塗装プロセスは1つだけ必要でしたSwing Timer

  3. たくさんの星と1つのスイングタイマーについての例

于 2012-07-11T13:25:59.087 に答える
0

すべてのターゲットに1つのタイマーを使用してみてください。

20個のターゲットがある場合は、同時に20個のタイマーも実行されます(約1000個のターゲットを考えてみてください)。いくつかの費用がかかり、最も重要なことは、それぞれが同様の仕事をしていることです-位置を計算するために-あなたはそれらを分割する必要はありません。20回走っても瞬くことがない簡単な作業だと思います。

私がポイントを得た場合、あなたがしたいのは、すべてのターゲットの位置を同時に変更しようとすることです。これは、1つのスレッドで実行される1つのメソッドですべてを変更することで実現できます。

于 2012-12-08T09:33:21.923 に答える