为什么要绘制到slick.Image.getGraphics()那么慢?

时间:2014-07-14 04:48:45

标签: java opengl lwjgl slick

我正在使用Java和Slick2D库制作游戏。 我有一个带有render方法的按钮类,如下所示:

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

但是这使我的fps从4000降到了10。

我尝试先制作一张空白图像,然后将其复制到渲染时绘制,但这并没有帮助。 我尝试预渲染图像并再次以4000 fps运行,但每当我更改按钮文本或是否突出显示时,我都不得不重绘图像,导致冻结。

如何在没有太慢的情况下每帧渲染此按钮的一部分。 还记得我需要渲染更改的按钮部分,如果我滚动它可能会改变每一帧。

2 个答案:

答案 0 :(得分:1)

从不曾经在经常调用且性能敏感的方法(例如渲染(...)或更新(...))中绘制到图像的内部图形。

首先,这就是为什么这个

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()方法为图像创建图形的新实例,并在代码中的每一帧调用。如上所述,如果可能,在性能敏感的调用中应该避免使用new运算符进行创建,并且在render(..)等方法中创建新的Graphics实例可能是应用程序中大量fps-drop的原因。

  3. 此外,有点普遍的看法是,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 :)
    

答案 1 :(得分:0)

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

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

然后在渲染组件后清除它

g.clearWorldClip();