18

私は最近、小さな Java ゲームに Canvas をよく使用していますが、多くの奇妙なことが起こる可能性があることに気付きました。たとえば、今日、敵の船を撃ってポイントを獲得することを目的とした小さなゲームを作成しました。ゲームは小さな 32x32 の画像を画面に描画し (場合によってはそれより少し大きくなります)、私が気付いていない理由で、ゲームが奇妙にレンダリングされます。

たとえば、私の船には頭上にヘルスバーがあります。

ここに画像の説明を入力

画像からわかるように、テクスチャは非常に小さいです。それにもかかわらず、ゲームは遅れ、時には次のように正しくレンダリングされません:

ここに画像の説明を入力

ヘルス バーの上部をよく見ると、わずかに上に移動していることがわかります。すべてのレンダリングがバッファリングされているため、ヘルス バーがどのように変化するのかがわかりません。

私のレンダリングコード:

public void render(){
    BufferStrategy bs = getBufferStrategy();
    if(bs == null){
        createBufferStrategy(3);
        return;
    }
    Graphics2D g = (Graphics2D)bs.getDrawGraphics();
    toDrawG.setColor(new Color(0x222222));
    toDrawG.fillRect(0, 0, WIDTH, HEIGHT);
    draw((Graphics2D)toDrawG);
    g.drawImage(toDraw, 0, 0, null);
    g.dispose();
    bs.show();
}

public void draw(Graphics2D g){
    if(Settings.planets){
    renderer.renderPlanets();
    }
    if(level != null){
    for(int i = 0 ; i < level.entityList.size(); i++){
        if(level.entityList.get(i) != null){
        level.entityList.get(i).render(renderer);
        }
        }
    }
    renderer.overlayString("Space Game", 20, 20, 24, 0xFFFFFF);
    renderer.overlayString(VERSION, 20, 50, 24, 0xFFFFFF);
    renderer.overlayString("FPS: " + renderer.fps, 20, 70, 24, 0xFFFFFF);
    renderer.overlayString("Ships spawned: " + level.shipsSpawned, 20, 90, 24,     0xFFFFFF);
    renderer.overlayString("Time Survived: " + level.time / 100 + "s", 20, 110,     24, 0xFFFFFF);
    renderer.overlayString("Physics FPS: " + fps, 20, 130, 24, 0xFFFFFF);

    if(currentGui != null){
        currentGui.render(renderer);
    }else{
        map.drawMinimap(SpaceGame.WIDTH-Minimap.WIDTH-20, SpaceGame.HEIGHT-    Minimap.HEIGHT-30);
    }

}

あなたがそれを勉強する必要があるなら、そして私の「Render.class」:

package com.maestrum;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.awt.image.ConvolveOp;
import java.awt.image.Kernel;
import java.util.Random;

public class Render implements Runnable{

public Graphics2D g2d;

public double xScroll,yScroll;

public int frames;
public int fps;
public long lastTime;

public int[] pSequence = new int[40];   
public SpaceGame game;

public Entity trackedEntity;

public Random rand;

public Render(SpaceGame game, Graphics2D g){
    this.game = game;
    this.g2d = g;
    this.rand = new Random();
    for(int i = 0 ; i < 40; i++){
        pSequence[i] = rand.nextInt(15) + 1;
    }
}

@Override
public void run() {     
    renderLoop();
}

public void renderLoop(){
    while(true){
        game.render();
        if(System.currentTimeMillis() - lastTime >= 1000){
            fps = frames;
            frames = 0;
            lastTime = System.currentTimeMillis();
        }
        frames++;
    }
}

public void renderPlanets(){
    overlayImage(ImageHandler.background, 0, 0, 1.5);
    for(int i = 0 ; i < 20; i++){
        overlayImage(ImageHandler.planets[pSequence[i]/4][pSequence[i]%4], i * 400 - xScroll/pSequence[i], i * pSequence[i]*40 - yScroll/pSequence[i]*2, pSequence[i]);
    }
}

private class PlanetRenderer {



}

public void overlayString(String s, double x, double y, int fontSize, int colour){
    drawString(s, x+xScroll, y+yScroll, fontSize, colour);
}

public void overlayRectangle(double x, double y, int xs, int ys, int colour){
    drawRectangle(x+xScroll, y+yScroll, xs, ys, colour);
}

public void overlayBlurred(BufferedImage img, double x, double y, double scale){
    drawImageBlurred(img, x+xScroll, y+yScroll, scale);
}

public void overlayImage(BufferedImage img, double x, double y, double scale){
    drawImage(img, x+xScroll, y+yScroll, scale);
}

public BufferedImage execute(BufferedImage img) {
    // TODO Auto-generated method stub
    float weight = 1.0f/2.0f;

    float [] elements = {weight, weight, weight, weight, weight, weight, weight, weight, weight};

    Kernel k = new Kernel (3,3,elements);
    ConvolveOp op = new ConvolveOp(k);

    BufferedImage dest = new BufferedImage(img.getWidth(), img.getHeight(), img.getType());

    op.filter(img, dest);
    return dest;
}

public void drawImageBlurred(BufferedImage img, double x, double y, double scale){
    x -= xScroll;
    y -= yScroll;
    BufferedImage image = new BufferedImage((int)(img.getWidth()*scale), (int)(img.getHeight()*scale), BufferedImage.TYPE_INT_ARGB);
    Graphics g = image.getGraphics();
    g.drawImage(img, 0, 0, (int)(img.getWidth()*scale), (int)(img.getHeight()*scale), null);
    execute(image);
    g2d.drawImage(image, (int)x, (int)y, null);
    g.dispose();
}

public void drawString(String s, Vector2D pos, int fontSize, int colour){
    drawString(s, pos.x, pos.y, fontSize, colour);
}

public void drawString(String s, double x, double y, int fontSize, int colour){
    if(s == null){
        return;
    }
    x -= xScroll;
    y -= yScroll;
    BufferedImage img = new BufferedImage(s.length()*fontSize+1, fontSize*2, BufferedImage.TYPE_INT_ARGB);
    Graphics g = img.getGraphics();
    g.setColor(new Color(colour));
    g.setFont(new Font("Consolas", Font.BOLD, fontSize));
    g.drawString(s, 0, img.getHeight()/2);
    g2d.drawImage(img, (int)x, (int)y, null);
    g.dispose();
}

public void drawImage(BufferedImage img, Vector2D pos, double scale){
    drawImage(img, pos.x, pos.y, scale);
}

public void drawLine(Vector2D v1, Vector2D v2, int colour, int width){
    drawLine(v1.x, v1.y, v2.x, v2.y, colour, width);
}

public void drawLine(double x1, double y1, double x2, double y2, int colour, int lWidth){
    x1 -= xScroll;
    y1 -= yScroll;
    x2 -= xScroll;
    y2 -= yScroll;
    g2d.setColor(new Color(colour));
    g2d.setStroke(new BasicStroke(lWidth));
    g2d.drawLine((int)x1, (int)y1, (int)x2, (int)y2);
}

public void drawImage(BufferedImage img, double x, double y, double scale){
    x -= xScroll;
    y -= yScroll;
    BufferedImage image = new BufferedImage((int)(img.getWidth()*scale), (int)(img.getHeight()*scale), BufferedImage.TYPE_INT_ARGB);
    Graphics g = image.getGraphics();
    g.drawImage(img, 0, 0, (int)(img.getWidth()*scale), (int)(img.getHeight()*scale), null);
    g2d.drawImage(image, (int)x, (int)y, null);
    g.dispose();
}

public void drawRectangle(Vector2D pos, int xs, int ys, int colour){
    drawRectangle(pos.x, pos.y, xs, ys, colour);
}

public void drawRectangle(double x, double y, int xs, int ys, int colour){
    if(xs <= 0){
        return;
    }
    x -= xScroll;
    y -= yScroll;
    BufferedImage image = new BufferedImage(xs, ys, BufferedImage.TYPE_INT_RGB);
    Graphics2D g = (Graphics2D) image.getGraphics();
    g.setColor(new Color(colour));
    g.fillRect(0, 0, xs, ys);
    g2d.drawImage(image, (int)x, (int)y, null);
    g.dispose();
}

public void drawImageRotated(BufferedImage img, Vector2D pos, double scale, double angle) {
    drawImageRotated(img, pos.x, pos.y, scale, angle);
}

 public void drawImageRotated(BufferedImage img, double x, double y, double scale, double angle) {
        x -= xScroll;
        y -= yScroll;  
        BufferedImage image = new BufferedImage((int)(img.getWidth() * 1.5D), (int)(img.getHeight() * 1.5D), 2);
        Graphics2D g = (Graphics2D)image.getGraphics();
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g.rotate(Math.toRadians(angle), image.getWidth() / 2, image.getHeight() / 2);
        g.drawImage(img, image.getWidth() / 2 - img.getWidth() / 2, image.getHeight() / 2 - image.getHeight() / 2, null);
        g2d.drawImage(image, (int)(x-image.getWidth()*scale/2), (int)(y-image.getHeight()*scale/2), (int)(image.getWidth()*scale), (int)(image.getHeight()*scale), null);
        g.dispose();      
 }

}

レンダリング クラスでわかるように、レンダリング プロセスは 1 秒間にできるだけ多く実行されます。これは、ゲームに可能な限り最高の FPS を与えるためです。私が求めていることの要点を逃した場合; Java を使用してレンダリングする際に考慮すべき適切な方法は何ですか? また、宇宙船のヘルス バーがこのように表示される原因は何ですか?

注: このビデオを見てください。私はそのプログラムを実行し、50000 のパーティクルで 80 FPS を得ましたが、私のレンダリング コード (明らかに品質がはるかに低い) では、物事が混乱し始める前に、わずか 100 程度のパーティクルしかレンダリングできません。上。

http://www.youtube.com/watch?v=6M3Ze4Eu87Y

これは私の tick() 関数で、ゲーム ティック (10ms) ごとに呼び出されます。

public void tick(){
    if(System.currentTimeMillis() - lastTime >= 1000){
        fps = frames;
        frames = 0;
        lastTime = System.currentTimeMillis();
    }
    frames++;
    if(renderer.trackedEntity != null){
        renderer.xScroll = renderer.trackedEntity.pos.x-SpaceGame.WIDTH/2;
        renderer.yScroll = renderer.trackedEntity.pos.y-SpaceGame.HEIGHT/2;
    }
    if(level != null && !paused){
        level.tick();
    }
    if(currentGui != null && currentGui.pausesGame()){
        paused = true;
    }else{
        paused = false;
    }
}
4

2 に答える 2

9

@mantrid の回答で問題が解決すると思います。パフォーマンスに関する限り...コードには明らかな「罪」がいくつかあります。

画像を描画するために画像に画像を描画しないでください

public void drawImage(BufferedImage img, double x, double y, double scale){
    x -= xScroll;
    y -= yScroll;
    BufferedImage image = new BufferedImage((int)(img.getWidth()*scale), (int)(img.getHeight()*scale), BufferedImage.TYPE_INT_ARGB);
    Graphics g = image.getGraphics();
    g.drawImage(img, 0, 0, (int)(img.getWidth()*scale), (int)(img.getHeight()*scale), null);
    g2d.drawImage(image, (int)x, (int)y, null);
    g.dispose();
}

これの要点がわかりません。なぜこれをしないのですか:

public void drawImage(BufferedImage img, double x, double y, double scale){
    g2d.drawImage(img, (int)(x-xScroll), (int)(y-yScroll), (int)(img.getWidth()*scale), (int)(img.getHeight()*scale), null);
}

あああああああああ

次のものは実際に私の目を燃やしました

public void drawRectangle(double x, double y, int xs, int ys, int colour){
    if(xs <= 0){
        return;
    }
    x -= xScroll;
    y -= yScroll;
    BufferedImage image = new BufferedImage(xs, ys, BufferedImage.TYPE_INT_RGB);
    Graphics2D g = (Graphics2D) image.getGraphics();
    g.setColor(new Color(colour));
    g.fillRect(0, 0, xs, ys);
    g2d.drawImage(image, (int)x, (int)y, null);
    g.dispose();
}

なんで?これは非常に簡単で迅速です:

public void drawRectangle(double x, double y, int xs, int ys, int colour){
    if(xs <= 0)
        return;

    g2d.setColor(new Color(colour));
    g2d.fillRect((int)(x-xScroll), (int)(y-yScroll), xs, ys);
}

drawImageRotatedとについても同様drawStringです。g2d バッファに直接描画するだけです。何を恐れていますか?

drawImageBlurred

まず第一に... あなたはそれをもう一度やっている !2番目:各フレームの画像に畳み込み操作を適用しているようです。なぜその操作を1回だけ実行しないのですか(たとえば、アプリの開始時、またはさらに良いことに、画像編集プログラムで)。

決して悪い意味で言っているわけではありませんが、あなたは明らかにプログラミング全般についてあまり経験がありません。処理 ( http://processing.org ) をご覧いただければと思います。私はほぼ毎日自分で使用しているため、ここでは一種の偏見があります。Java で書かれた優れた学習およびプロトタイピング環境であり、実装の詳細 (バッファーの反転など) を考えるのをやめて、本当に必要なこと (ゲームを作る!) に集中することができます。

わかりました、私が言ったことが理にかなっていることを願っています。コーディング頑張ってください!:)

于 2013-02-19T11:46:46.043 に答える
6

コンポーネント/オーバーレイがまだバックバッファにレンダリングされている間にゲームが遅れ、更新されるため、ヘルスバーがシフトする可能性がありますxScrollyScrollそれを修正するには:

1) ゲーム速度を常に一定に保つために、最新の更新からの経過時間に比例してゲーム オブジェクトを移動します。deltaパラメータを追加しlevel.tick()、すべてのゲーム オブジェクト update メソッド

2) と を同じループ シーケンス内に配置level.tick()game.render()て、ゲーム オブジェクトが最初に更新されるようにします。

currentTime = System.currentTimeMillis();
delta = currentTime - lastTime; 

if(level != null && !paused){
    level.tick(delta); // introduce delta here
    game.render();
}

lastTime = currentTime;

一般に、次の追加の手順を検討してください。

1)Component.setIgnoreRepaint(true)レンダリングを高速化するように設定

2) 開始時に現在の表示用に画像を最適化する

GraphicsConfiguration gc = GraphicsEnvironment
.getLocalGraphicsEnvironment()
.getDefaultScreenDevice()
.getDefaultConfiguration();
Image optimized = gc.createCompatibleImage(img.getWidth(),img.getHeight(),Transparency.BITMASK);
optimized.getGraphics().drawImage(unoptimized, 0, 0, null);
于 2013-02-10T15:24:56.257 に答える