为什么画到slick.Image.getGraphics()的速度那么慢?

问题描述 投票:0回答:2

我使用Java和Slick2D库做了一个游戏,我有一个按钮类,它的渲染方法看起来像这样。

public void render(Graphics g, int xOffset, int yOffset){
    int renderX = x+xOffset;
    int renderY = y+yOffset;

    if(hasFocus()){
        g.setColor(HIGHLIGHTEDBUTTONCOLOR);
    }else{
        g.setColor(BUTTONCOLOR);
    }
    g.fillRect(renderX, renderY, width, height);

    g.setColor(BUTTONTEXTCOLOR);
    g.drawString(text, renderX+(width/2) - (g.getFont().getWidth(text)/2), renderY+(height/2) - (g.getFont().getLineHeight()/2));    
}

我有一个按钮类,它的渲染方法是这样的: 这工作很好,直到我需要只渲染按钮的一部分,而另一部分在我的可滚动容器的边界之外。我试过这个方法。

Image temp = new Image(myButton.getWidth(), myButton.getHeight());
myButton.render(temp.getGraphics(),this.x, this.y);
temp.getSubImage(0, 0, int temp.getWidth(), temp.getHeight()/2);
myGraphics.drawImage(temp, myButton.getX(), myButton.getY());

但这让我的帧数从4000下降到了10.

我试过事先制作一张空白图片,然后在渲染时复制到图片上,但这并没有帮助。我试过预先渲染图片,它又以4000帧/秒的速度运行,但每当我改变按钮的文字或是否突出显示时,我不得不重新绘制图片,这导致了冻结。

我如何才能在每一帧中只渲染这个按钮的一部分,而不至于太慢.还记得我需要渲染的按钮的部分是变化的,如果我在滚动,它可能会改变每一帧。

java opengl lwjgl slick2d
2个回答
1
投票

从来没有 在一个经常被调用且对性能敏感的方法中,如render(...)或update(...),曾经绘制到图像的内部图形。

首先,这就是为什么这个

Image temp = new Image(myButton.getWidth(), myButton.getHeight());
myButton.render(temp.getGraphics(),this.x, this.y);
temp.getSubImage(0, 0, int temp.getWidth(), temp.getHeight()/2);
myGraphics.drawImage(temp, myButton.getX(), myButton.getY());

是如此之慢。

  1. 你应该避免在频繁调用的方法中使用new操作符,因为它很慢,尤其是在创建新的图像上下文时(必须创建纹理和内部缓冲区)。如果你真的要做这样的事情,可以尝试实现一些缓冲,比如在初始化时创建一个图像,然后在方法中重复使用,而不是重新创建。

  2. getGraphics()方法 创造 为图像创建一个新的Graphics实例,并且在您的代码中每一帧都会调用这个实例。如上所述,在对性能敏感的调用中,应尽可能避免使用new操作符创建,而且在render(..)这样的方法中创建一个新的Graphics实例可能是导致应用程序中帧数大幅下降的原因。

此外,与一般人的想法相反,getSubImage(..)方法其实并不影响性能,如果有必要的话,可以在频繁调用的方法中使用,因为它基本上只是将缓冲区的信息剪辑成给定的大小(但保留了对原始图像的引用,所以如果你修改了子图像,例如:在上面画画,原始图像也会被修改!)。

为了解决你的问题,你可以手动夹住按钮,然后自己计算按钮框的边界。在你的情况下,你可以这样做(由于你没有说明你指的是哪种容器,我假设它是某种GUI元素,而且是一个简单的矩形,但这可以很容易地改变以满足你的要求)。

//some pseudo-code as a help

if (buttonX within container bounds) buttonStartX = buttonX
else if (buttonX smaller than container bounds) buttonStartX = containerMinX

if (buttonX + buttonWidth within container bounds) buttonBoxWidth = buttonWidth
else if (buttonX + buttonWidth greater than container bounds) buttonBoxWidth = containerMaxX - buttonStartX

//... and so on... (yes the horizontal logic is not complete, but I dont want you to just copy this 
// without thinking but rather that you finish this yourself with the given approach :)

0
投票

解决办法是为游戏容器的图形设置一个世界剪辑(可以渲染的区域)。

g.setWorldClip(this.getX()+xOffset, this.getY()+yOffset, this.getWidth(), this.getHeight());

然后在渲染组件后将其清除

g.clearWorldClip();
© www.soinside.com 2019 - 2024. All rights reserved.