2013-02-10 21 views
17

Gần đây tôi đã sử dụng Canvas rất nhiều cho các trò chơi Java nhỏ và tôi đã nhận thấy rất nhiều điều kỳ lạ có thể xảy ra. Ví dụ, hôm nay tôi đã tạo ra một trò chơi nhỏ, trong đó mục tiêu là bắn các tàu địch và nhận điểm. Trò chơi thu hút các hình ảnh 32x32 nhỏ trên màn hình (đôi khi hơi lớn hơn) và vì lý do tôi không biết gì, trò chơi ám chỉ một cách kỳ lạ.Canvas - thực tiễn kết xuất tốt?

Ví dụ, tàu của tôi có một quầy bar sức khỏe trên đầu nó:

enter image description here

Như bạn có thể nhìn thấy bằng các hình ảnh, các kết cấu là thực sự nhỏ. Mặc dù vậy, các trò chơi chậm và đôi khi mọi thứ hiển thị không đúng như thế này ví dụ:

enter image description here

Nếu bạn nhìn kỹ ở phía trên cùng của thanh sức khỏe, bạn có thể thấy nó được chuyển lên một chút, nó câu đố tôi làm thế nào anh ta xảy ra như tất cả các rendering của tôi là đệm.

đang vẽ của tôi:

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); 
    } 

} 

Và tôi "Render.class" nếu bạn cần phải nghiên cứu nó:

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();  
} 

}

Như bạn có thể nhìn thấy trong lớp vẽ, quá trình render được thực hiện càng nhiều lần càng tốt. Điều này là để cung cấp cho các trò chơi FPS cao nhất có thể. Nếu bạn bỏ lỡ điểm của những gì tôi yêu cầu; Tôi nên xem xét các phương pháp hay nào khi kết xuất nội dung bằng Java? Và, những gì có thể có thể gây ra không gian tàu thanh sức khỏe để làm như vậy? Lưu ý: hãy xem video này, tôi chạy chương trình đó và nhận 80FPS với 50000 hạt, tuy nhiên với mã hiển thị của tôi (chất lượng rõ ràng là chất lượng thấp hơn nhiều), tôi chỉ có thể hiển thị 100 hạt hoặc hơn, trước khi mọi thứ bắt đầu rối tung lên.

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

Đây là đánh dấu() chức năng của tôi, nó được gọi là mọi trận đấu đánh dấu (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; 
    } 
} 
+1

Tôi không có ý định đăng bài này. Tôi đang chỉnh sửa nó ngay bây giờ. –

+0

có nghĩa là 'kỳ quặc' nghĩa là gì? chậm rãi? nhấp nháy? – mantrid

+0

Tôi đã chỉnh sửa nó, nhìn vào hai hình ảnh. –

Trả lời

8

tôi nghĩ câu trả lời của @mantrid sẽ khắc phục được sự cố của bạn. như xa như hiệu suất đi ... có một số "tội lỗi" rõ ràng trong mã của bạn:

Đừng vẽ một hình ảnh vào một hình ảnh để vẽ một hình ảnh

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(); 
} 

tôi không xem điểm này. Tại sao không chỉ làm điều này:

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); 
} 

AAAAAAAAAAAAA

Người tiếp theo thực sự đốt cháy mắt tôi

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(); 
} 

Tại sao? Đây là đơn giản hơn rất nhiều và nhanh hơn:

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); 
} 

Cũng vậy với các drawImageRotateddrawString. Chỉ cần vẽ trực tiếp đến bộ đệm g2d, bạn sợ điều gì?

drawImageBlurred

Trước hết ... bạn đang làm nó một lần nữa! Thứ hai: Bạn dường như đang áp dụng thao tác xoay vòng cho một hình ảnh trên mỗi khung hình, tại sao không chỉ thực hiện thao tác đó một lần (ví dụ: ở đầu ứng dụng hoặc thậm chí tốt hơn trong chương trình chỉnh sửa hình ảnh).

Tôi không ngụ ý điều này một cách tồi tệ chút nào, nhưng rõ ràng bạn không có kinh nghiệm với lập trình nói chung. Tôi nghĩ bạn có thể xem xét xử lý (http://processing.org). Tôi sắp xếp thành kiến ​​ở đây bởi vì tôi đang sử dụng nó gần như hàng ngày. Nó là một môi trường học tập và tạo mẫu tuyệt vời được viết bằng java cho phép bạn ngừng suy nghĩ về các chi tiết thực hiện (như lật bộ đệm) và tập trung vào những gì bạn thực sự quan tâm (tạo một trò chơi!).

Được rồi, hy vọng những gì tôi nói có ý nghĩa. Chúc may mắn với mã hóa của bạn! :)

+0

Cảm ơn bạn đã gửi phản hồi này. Tôi đã vẽ hình ảnh lên hình ảnh khi tôi lo lắng về các biến đổi Đồ họa ảnh hưởng lẫn nhau, bây giờ tôi biết cách sử dụng AffineTransform để khôi phục một phép biến đổi cũ cho đối tượng Đồ họa. Và bằng cách này, x - = xScroll phải ở đó vì trò chơi là một cuộn bên. Sau khi sửa tất cả mã của tôi từ trạng thái cũ, nó vẫn có vẻ bị trễ nhưng nó trông đẹp hơn rất nhiều. Cảm ơn bạn :) –

+0

@kritzikratzi tôi đã tìm kiếm điều này từ bao giờ, làm thế nào tôi có thể vẽ chính xác cùng một thanh – Kennedy

6

Thanh sức khỏe có thể được chuyển vì khi trò chơi chậm, xScrollyScroll được cập nhật trong khi các thành phần/lớp phủ vẫn đang được đưa vào bộ đệm ẩn. Để khắc phục điều đó:

1) di chuyển đối tượng trò chơi tương ứng với khoảng thời gian trôi qua từ bản cập nhật mới nhất để giữ tốc độ trò chơi luôn không đổi.thêm delta tham số để level.tick() và tất cả các trò chơi đối tượng phương pháp cập nhật

2) đặt level.tick()game.render() trong chuỗi vòng lặp tương tự để đảm bảo các đối tượng game được cập nhật đầu tiên:

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

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

lastTime = currentTime; 

Nói chung, hãy xem xét những bước bổ sung:

1) đặt Component.setIgnoreRepaint(true) để tăng tốc độ hiển thị

2) tối ưu hóa hình ảnh cho hiển thị hiện tại lúc bắt đầu

GraphicsConfiguration gc = GraphicsEnvironment 
.getLocalGraphicsEnvironment() 
.getDefaultScreenDevice() 
.getDefaultConfiguration(); 
Image optimized = gc.createCompatibleImage(img.getWidth(),img.getHeight(),Transparency.BITMASK); 
optimized.getGraphics().drawImage(unoptimized, 0, 0, null); 
+0

Tôi phải không đồng ý với việc đặt chúng trên cùng một vòng lặp; làm như vậy gây ra fps của tôi để giảm xuống khoảng 4, tôi tin rằng điều này là do thực tế là renderer cần phải chờ đợi cho tất cả các vật lý để cập nhật đầu tiên. –

+0

nó là cách nó thường được thực hiện – mantrid

+0

Vâng. Khi tôi làm điều đó như thế, tôi nhận được rất nhiều tụt hậu. Câu trả lời đầu tiên tôi gặp phải là giải quyết vấn đề độc lập, và điều đó dường như đã làm cho trò chơi chạy nhanh hơn rất nhiều. –

Các vấn đề liên quan