1

最近、CS の授業で得た知識を使って小さなゲーム (2D で、大したことはありません) を作ろうとしています。これらのグラフィック関連クラスのドキュメントを 2 週間ほど読んだ後、私はこの状況に陥りました。60 ゲーム ロジック更新/60 フレーム/秒で動作する実行中のシステムがあります (これは非常にうまく機能します:D)。最初の小さなテストとして、画面上で画像を動かしたいと考えました。それがコードです(一部は私のもので、一部はいくつかのチュートリアルからのものです):

import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.Color;

import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Dimension;
import java.awt.Graphics;

import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.io.File;
import java.net.URL;



import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;


public class Game extends Canvas implements Runnable{
private static final long serialVersionUID = 1L;

public static final String NAME= "PokeCraft PRE-ALPHA";
public static final int HEIGHT=720;
public static final int WIDTH=HEIGHT*16/9;
public static final int SCALE=1;

private int fps=0;
private int tps=0;



private boolean running;
private int tickCount;
public void start(){
    running = true;
    new Thread(this).start();
}

public void stop(){
    running = false;

}

public void render(){
    BufferStrategy bufferStrategy =getBufferStrategy();
    if(bufferStrategy==null){
        this.createBufferStrategy(3);
        return;
    }

    /* render function */

      Graphics g = (Graphics) bufferStrategy.getDrawGraphics();
      g.clearRect(0, 0, super.getWidth(), super.getHeight());

      Image img = null;
      try{
          String imgPath = "data/MF.png";
          img = ImageIO.read(getClass().getResourceAsStream(imgPath));


      } catch(Exception e){
          System.out.println(e);
      }


        g.drawImage(img, tickCount, 0, null);
        Font font = new Font("Verdana",0,11);
        g.setFont(font);
        g.setColor(Color.RED);
        g.drawString(NAME+" / "+fps+" fps, "+tps+"tps", 5, 15);
      g.dispose();
      bufferStrategy.show();
}


public void run() {
    long lastTime= System.nanoTime();
    double unprocessed = 0;
    double nsPerTick = 1000000000.0/60.0;
    int frames = 0;
    int ticks = 0;
    long lastTimer1 = System.currentTimeMillis();

    while(running){
        long now = System.nanoTime();
        unprocessed += (now-lastTime)/nsPerTick;
        lastTime= now;
        boolean shouldRender= false;
        while(unprocessed >= 1){
            ticks++;
            tick();
            unprocessed -= 1;
            shouldRender = true;
        }
        if(shouldRender){
        frames++;
        render();
        }

        if(System.currentTimeMillis()-lastTimer1 > 1000){

            lastTimer1 += 1000;
            System.out.println(ticks+" ticks, "+frames + " fps");
            fps=frames;
            tps=ticks;
            ticks = 0;
            frames = 0;
        }
    }
}



public void tick(){
    tickCount++;
}

public static void main(String[] args){
    Game game= new Game();
    game.setPreferredSize(new Dimension(WIDTH*SCALE, HEIGHT*SCALE));
    game.setMinimumSize(new Dimension(WIDTH*SCALE, HEIGHT*SCALE));
    game.setMaximumSize(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);
    frame.pack();
    frame.setResizable(true);
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);

    game.start();
    }
}

ImageIO.read(...) は、パフォーマンスに非常に大きな影響を与えています (VisualVM によると、1 回の実行で約 200 ミリ秒かかります)。どうすればその問題に取り組むことができますか?

4

3 に答える 3

10

画像の読み取りは、本質的にコストのかかる操作です。

したがって、ゲームの開始時に一度イメージを読み取り、後でアクセスできるようにメモリに保持する必要があります。

于 2012-12-12T21:54:42.003 に答える
5

レンダリングするたびに画像をロードすることは避けてください。クラス変数にして、一度だけロードします。このような:

import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.Color;

import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Dimension;
import java.awt.Graphics;

import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.io.File;
import java.net.URL;

import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;

public class Game extends Canvas implements Runnable {
    private static final long serialVersionUID = 1L;

    public static final String NAME = "PokeCraft PRE-ALPHA";
    public static final int HEIGHT = 720;
    public static final int WIDTH = HEIGHT * 16 / 9;
    public static final int SCALE = 1;

    private int fps = 0;
    private int tps = 0;
    private Image img = null;

    private boolean running;
    private int tickCount;

    public void start() {
        running = true;
        new Thread(this).start();
    }

    public void stop() {
        running = false;

    }

    public void render() {
        BufferStrategy bufferStrategy = getBufferStrategy();
        if (bufferStrategy == null) {
            this.createBufferStrategy(3);
            return;
        }

        /* render function */

        Graphics g = (Graphics) bufferStrategy.getDrawGraphics();
        g.clearRect(0, 0, super.getWidth(), super.getHeight());

        if (img == null) {
            try {
                String imgPath = "data/MF.png";
                img = ImageIO.read(getClass().getResourceAsStream(imgPath));
            } catch (Exception e) {
                System.out.println(e);
            }
        }

        g.drawImage(img, tickCount, 0, null);
        Font font = new Font("Verdana", 0, 11);
        g.setFont(font);
        g.setColor(Color.RED);
        g.drawString(NAME + " / " + fps + " fps, " + tps + "tps", 5, 15);
        g.dispose();
        bufferStrategy.show();
    }

    public void run() {
        long lastTime = System.nanoTime();
        double unprocessed = 0;
        double nsPerTick = 1000000000.0 / 60.0;
        int frames = 0;
        int ticks = 0;
        long lastTimer1 = System.currentTimeMillis();

        while (running) {
            long now = System.nanoTime();
            unprocessed += (now - lastTime) / nsPerTick;
            lastTime = now;
            boolean shouldRender = false;
            while (unprocessed >= 1) {
                ticks++;
                tick();
                unprocessed -= 1;
                shouldRender = true;
            }
            if (shouldRender) {
                frames++;
                render();
            }

            if (System.currentTimeMillis() - lastTimer1 > 1000) {

                lastTimer1 += 1000;
                System.out.println(ticks + " ticks, " + frames + " fps");
                fps = frames;
                tps = ticks;
                ticks = 0;
                frames = 0;
            }
        }
    }

    public void tick() {
        tickCount++;
    }

    public static void main(String[] args) {
        Game game = new Game();
        game.setPreferredSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));
        game.setMinimumSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));
        game.setMaximumSize(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);
        frame.pack();
        frame.setResizable(true);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);

        game.start();
    }
}

フォントにも同じことが言えますが、画像の読み込みほどパフォーマンスに影響を与えていない可能性があります.

于 2012-12-12T21:58:37.653 に答える
1

ディスク操作は信じられないほど遅く、ループごとにファイルにアクセスする必要はありません。これは現在行っていることです。run() メソッドで while(running) ループに入る前に、img 変数をクラス変数にしてインスタンス化します。

于 2012-12-12T22:06:27.453 に答える