我的问题是,我有一个随机生成的地图的游戏,由于块的数量,它只有30-40 fps。(你可以想象我的游戏就像是一个2d的我的游戏)。
首先,我搜索屏幕中的第一个图块。然后启动循环渲染图块下一个图块...直到我到达最后一个图块,你可以看到。
(我不使用任何Java类,如graphics / graphics2d,我使用自己的代码,什么是int [],其中包含teh屏幕的行,当我渲染一个tile时,我更改了int [ x + y * width]屏幕到块的正确像素的位置)
我认为逻辑上这是渲染地图的最佳方式,我不会低估为什么是低fps。我错了或者我需要在我的代码中搜索其他一些问题?或者有更好的渲染方法?
如果我跳过世界的渲染,那里有稳定的120 fps。可能是什么问题?
答案 0 :(得分:1)
我知道你不使用Gaphics函数并选择操纵像素阵列。
尝试将游戏更改为使用Graphics对象,因为没有(简单而有效)的方法。如果您仍然选择不这样做,请尝试添加
System.setProperty("sun.java2d.opengl", "true");
在你的代码刚刚开始之后
public static void main(String[] args) {
我尝试以与你第一次制作简单游戏时相同的方式进行操作,但后来我意识到内置的图形功能在性能和易用性方面都非常出色。
编辑:
关于基本游戏如何使用Graphics对象的简短解释:
假设您创建了一个JFrame。然后,如果您添加一个从Canvas扩展的类并将其添加到它。如果你想使用菜单,你甚至可以先创建一个JPanel,然后将Canvas添加到Jpanel中,这样你就可以更容易地隐藏它。
这里有一个如何创建可用画布的示例:
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.BufferStrategy;
import javax.swing.JFrame;
public class Game extends Canvas implements Runnable{
private static final long serialVersionUID = 1L;
public static final int WIDTH = 1920;
public static final int HEIGHT = WIDTH * 9 / 16;
public static final String TITLE = "YOUR GAMES NAME";
public static final int TICKSPERS = 120;
public static final boolean ISFRAMECAPPED = false;
public static JFrame frame;
private Thread thread;
private boolean running = false;
public int frames;
public int lastFrames;
public int ticks;
public Game(){
Dimension size = new Dimension(WIDTH, HEIGHT);
setPreferredSize(size);
setMaximumSize(size);
setMinimumSize(size);
}
public void render(){
frames++;
BufferStrategy bs = getBufferStrategy();
if (bs == null){
createBufferStrategy(2);
return;
}
Graphics g = bs.getDrawGraphics();
g.setColor(new Color(79,194,232));
g.fillRect(0, 0, getWidth(), getHeight());
//Call your render funtions from here
g.setColor(Color.BLACK);
g.fillRect(120,70,35,90);
g.dispose();
bs.show();
}
public void tick(){
}
public synchronized void start(){
if(running) return;
running = true;
thread = new Thread(this, "Thread");
thread.start();
}
public synchronized void stop(){
if(!running) return;
running = false;
try {
System.exit(1);
frame.dispose();
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void init(){
}
public void run() {
init();
//Tick counter variable
long lastTime = System.nanoTime();
//Nanoseconds per Tick
double nsPerTick = 1000000000D/TICKSPERS;
frames = 0;
ticks = 0;
long fpsTimer = System.currentTimeMillis();
double delta = 0;
boolean shouldRender;
while(running){
shouldRender = !ISFRAMECAPPED;
long now = System.nanoTime();
delta += (now - lastTime) / nsPerTick;
lastTime = now;
//if it should tick it does this
while(delta >= 1 ){
ticks++;
tick();
delta -= 1;
shouldRender = true;
}
if (shouldRender){
render();
}
if (fpsTimer < System.currentTimeMillis() - 1000){
System.out.println(ticks +" ticks, "+ frames+ " frames");
ticks = 0;
lastFrames = frames;
frames = 0;
fpsTimer = System.currentTimeMillis();
}
}
}
public static void main(String[] args){
Game game = new Game();
frame = new JFrame(TITLE);
frame.add(game);
frame.pack();
frame.setResizable(false);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
game.start();
}
}
这门课可以作为你的基础。它的tick方法每秒调用120次,其渲染方法可以将内容渲染到屏幕上。 如果你不熟悉图形对象的功能,我建议你阅读一下它们。
您无法从外部访问Graphics对象。您需要在处理Graphics对象之前从游戏渲染功能中调用渲染函数。尝试从渲染功能中分离游戏逻辑。
答案 1 :(得分:0)
我使用的架构与上面所述略有不同。实际上,我创建了一个单独的Renderer对象,该对象基本上是一个延迟的渲染器。
它的结构是这样的
public class App {
JFrame window;
Renderer renderer;
Engine engine; //implementation is a nested class within App
Dimension window_dimension; //stored for later use
public App() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
window = new JFrame("MyGame");
boolean full_screen = true;
window_dimension = initializeWindow(window, full_screen);
renderer = new Renderer(window_dimension);
window.add(renderer);
engine = new Engine(renderer);
engine.start();
}
});
}
}
Renderer.java:
import java.awt.*;
import java.util.concurrent.CopyOnWriteArrayList;
public class Renderer extends JPanel {
Dimension dim;
private CopyOnWriteArrayList<Drawable> drawables = new CopyOnWriteArrayList<Drawable>();
Renderer(Dimension dim) {
this.dim = dim;
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.clearRect(0, 0, getWidth(), getHeight());
for (Drawable drawable : drawables) {
drawable.paint(g2d);
}
g2d.dispose();
drawables.clear();
}
public synchronized void render() {
repaint();
}
public synchronized void submit(Drawable drawable) {
drawables.add(drawable);
}
public synchronized void submitBackground(Drawable drawable) {
drawables.add(0,drawable);
}
}
Drawable.java:
import java.awt.*;
abstract class Drawable {
protected Stroke stroke;
protected Color color, stroke_color;
public Dimension size;
public float sub_pixel_x;
public float sub_pixel_y;
public Drawable(Color color) {
setColor(color);
setStrokeColor(new Color(0));
sub_pixel_x = 0.0f;
sub_pixel_y = 0.0f;
size = new Dimension(10, 10);
}
public void setStroke(float width) {
stroke = new BasicStroke(width);
}
public void noStroke() {
stroke = null;
}
public void setColor(Color color) {
this.color = color;
}
public void setStrokeColor(Color color) {
this.stroke_color = color;
}
public void setLocation(float x, float y) {
sub_pixel_x = x;
sub_pixel_y = y;
}
protected abstract void paint(Graphics2D g2d);
}
AbstractEngine.java:
import java.awt.*;
abstract class AbstractEngine implements Runnable {
Renderer renderer;
Dimension dimension;
boolean running;
Thread thread;
public AbstractEngine(Renderer renderer) {
this.renderer = renderer;
dimension = renderer.dim;
}
public void start() {
if (running) return;
running = true;
thread = new Thread(this, "Tread");
thread.start();
}
public void stop() {
if(!running) return;
running = false;
try {
System.exit(1);
thread.join();
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
public abstract void handleInput();
public abstract void update();
public abstract void render();
@Override
public void run() {
final int UPS = 120;
final int FPS = 60;
long initialTime = System.nanoTime();
final double timeU = 1000000000 / UPS;
final double timeF = 1000000000 / FPS;
double deltaU = 0, deltaF = 0;
int frames = 0, ticks = 0;
long timer = System.currentTimeMillis();
while (running) {
long currentTime = System.nanoTime();
deltaU += (currentTime - initialTime) / timeU;
deltaF += (currentTime - initialTime) / timeF;
initialTime = currentTime;
if (deltaU >= 1) {
handleInput();
update();
ticks++;
deltaU--;
}
if (deltaF >= 1) {
render();
renderer.render();
frames++;
deltaF--;
}
if (System.currentTimeMillis() - timer > 1000) {
frames = 0;
ticks = 0;
timer += 1000;
}
}
}
}
输入在扩展Engine类中处理。我有一个MouseState对象,该对象保存鼠标位置和按钮状态。然后,我创建一个MouseAdapter来更新MouseState中的日期并将其附加到渲染器。我知道这有点奇怪,但是效果很好。