如何使用 Slick2D 渲染最后一个字符字形的一部分

How do I render a portion of the last character glyph using Slick2D

我目前正在使用 Slick 在游戏中呈现自定义聊天。

我想做的是,我希望能够为聊天创建自定义 "fading" 效果,平滑地 从右向左淡化.我不太擅长用文字解释事情,所以我会展示一个我想要的例子。

示例:

我想渲染文本 bernhardkiv: hello world!,运行 我的常规字体渲染器,结果如下:

而我希望能够做到的,就是顺畅的截断文字,如下;

而且我不知道该怎么做!我相信使用 glScissoring 可以做到这一点,但我一直在尝试使用它,但似乎没有任何效果(什么也没有发生,一切都保持不变)。

我将不胜感激任何人的帮助,我本质上希望能够,而不是在 x、y 处渲染,我希望能够在 x、y 处以宽度 string_width 渲染 - smooth_cut, 能做一点渐变效果

免责声明:

  1. 这对我来说是一个新领域,但由于似乎没有其他人回答,我会尝试一下,因为这个问题对我来说似乎很有趣。我还没有彻底测试这些代码修改,所以可能 bugs/side-effects 我不知道。使用风险自负。 :-)

  2. Slick2D 库中的 org.newdawn.slick.Font 实现 类(并支持 类)似乎对扩展不友好(私有成员和方法) .据我所知,为了达到预期的效果,需要对这些内部 Slick2D 类 中的一些进行更改。 (另一种方法是复制整个 类 和包。)据我从提问者的评论中理解,Slick2D 源代码包含在提问者的项目中,因此对内部 Slick2D 类 进行更改应该是在这种情况下可行。

  3. 可以说,上述 类 的扩展不友好是有原因的,并且可能有更好的方法来进行必要的更改以使其可扩展。如果有一个实际的项目和更多的时间,我什至可能会以不同的方式自己去做。 然而,这个答案的目的是向提问者展示我如何通过对内部 Slick2D 进行最少的代码更改来达到预期效果的本质 类。 提问者可以his/her 自己决定如何处理将这些更改应用到 his/her 项目的细节。

此代码使用 Slick build 237。


您可以在此 github repo 查看完整的代码更改。下面我只显示与实现所需淡入淡出效果最相关的代码更改。

将每个字符想象成 GL_QUAD 中绘制的每个字符,每个角都有一个 RGBA 值(如所描述的 here 此图片的来源):

 (x,x,x,1)       (x,x,x,0)
     -----------------
     |               |
     |               |
     |               |
     |               |
     |               |
     |               |
     -----------------
  (x,x,x,1)      (x,x,x,0)

我们正在做的是保持左上角和左下角的颜色不变,并将右上角和右下角的颜色更改为 alpha 为 0(透明)。 OpenGL 会逐渐为我们淡化透明度。为此,我们扩展 org.newdawn.slick.Image 以使用以下方法创建 FadableImage(这与 ImagedrawEmbedded 方法非常相似,但不会覆盖它):

public class FadableImage extends Image {

   //.....

    public void drawEmbeddedFaded(float x,float y,float width,float height, Color color, float fadePercentWidth) {
        init();

        //if percent width is set to an invalid amount, default it to 1.
        if(fadePercentWidth < 0.0f || fadePercentWidth > 1.0f)
            fadePercentWidth = 1.0f;

        if (corners == null) {
            GL.glTexCoord2f(textureOffsetX, textureOffsetY);
            GL.glVertex3f(x, y, 0);
            GL.glTexCoord2f(textureOffsetX, textureOffsetY + textureHeight);
            GL.glVertex3f(x, y + height, 0);

            //set the color of x2 and y2 of the quad to 0 alpha
            GL.glColor4f(color.r, color.g, color.b, 0.0f);
            GL.glTexCoord2f(textureOffsetX + textureWidth*fadePercentWidth, textureOffsetY
                    + textureHeight);
            GL.glVertex3f(x + width*fadePercentWidth, y + height, 0);
            GL.glTexCoord2f(textureOffsetX + textureWidth*fadePercentWidth, textureOffsetY);
            GL.glVertex3f(x + width*fadePercentWidth, y, 0);
            //reset the color
            GL.glColor4f(color.r, color.g, color.b, color.a);
        } else {
            corners[TOP_LEFT].bind();
            GL.glTexCoord2f(textureOffsetX, textureOffsetY);
            GL.glVertex3f(x, y, 0);
            corners[BOTTOM_LEFT].bind();
            GL.glTexCoord2f(textureOffsetX, textureOffsetY + textureHeight);
            GL.glVertex3f(x, y + height, 0);
            //set the color of x2 and y2 of the quad to 0 alpha
            GL.glColor4f(color.r, color.g, color.b, 0.0f);
            //corners[BOTTOM_RIGHT].bind();
            GL.glTexCoord2f(textureOffsetX + textureWidth*fadePercentWidth, textureOffsetY
                    + textureHeight);
            GL.glVertex3f(x + width*fadePercentWidth, y + height, 0);
            //corners[TOP_RIGHT].bind();
            GL.glTexCoord2f(textureOffsetX + textureWidth*fadePercentWidth, textureOffsetY);
            GL.glVertex3f(x + width*fadePercentWidth, y, 0);
            //reset the color
            GL.glColor4f(color.r, color.g, color.b, color.a);
        }
    }

}

你可以看到我要描述的diff

为了使用FadableImage,我们还需要扩展org.newdawn.slick.font.GlyphPage来创建FadableGlyphPage。为了支持 FadableGlyphPage 我们需要修改 org.newdawn.slick.font.GlyphPage 的构造函数并添加一个新的。

然后我们需要修改 org.newdawn.slick.UnicodeFont 以在 UnicodeFont 加载 loadGlyphs 中的字形时使用我们的 FadableGlyphPage

我们更改 UnicodeFontdrawDisplayList(...) 方法来调用我们 FadableImage 的特殊 drawEmbeddedFaded(...) 方法来获取给定字符串的指定结束索引处的字符。我们还必须进行修改,以便正确处理 DisplayList 缓存。

我们向 UnicodeFont 添加了一个方便的方法来绘制一个字符串,其中 endIndex-1 处的字符淡入淡出并绘制到由 fadePercentWidth:

指定的宽度百分比
public void drawString(float x, float y, String text, int endIndex, float fadePercentWidth) {
    drawDisplayList(x, y, text, Color.white, 0, endIndex, fadePercentWidth);
}

所以想法是最后呈现的字符将是 endIndex-1 处的字符。渐变宽度百分比是包含 endIndex-1 处字符的四边形的宽度百分比。所以您可以使用该参数来微调您想要的效果(0.0f1.0f)。将渐变宽度百分比设置为 0.5f,将绘制四边形总宽度的 50%。淡入淡出将达到 50% 的宽度:

 (x,x,x,1)       (x,x,x,0)

     ------------|------------
     |           |            |
     | drawn     | not        |
     |           |  drawn     |
     |           |            |
     |           |            |
     |           |            |
     ------------|------------

  (x,x,x,1)      (x,x,x,0)

现在让我们用这段代码来测试它:

import java.awt.Font;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.newdawn.slick.AppGameContainer;
import org.newdawn.slick.BasicGame;
import org.newdawn.slick.Color;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.UnicodeFont;
import org.newdawn.slick.font.effects.ColorEffect;

public class TextFadeTest extends BasicGame {

    private final int Y_OFFSET = 50;
    private final org.newdawn.slick.Color bgColor =  new org.newdawn.slick.Color(126, 166, 240);

    private UnicodeFont unicodeFont;

    public TextFadeTest(String gamename) {
        super(gamename);
    }

    @Override
    public void init(GameContainer gc) throws SlickException {
        gc.setShowFPS(false);
        gc.getGraphics().setBackground(bgColor);

        Font awtFont = new Font(Font.SERIF, Font.PLAIN, 36);
        unicodeFont = new UnicodeFont(awtFont);
        unicodeFont.getEffects().add(new ColorEffect(java.awt.Color.WHITE));
        unicodeFont.addAsciiGlyphs();
        unicodeFont.loadGlyphs();
    }

    @Override
    public void update(GameContainer gc, int i) throws SlickException {}

    @Override
    public void render(GameContainer gc, Graphics g) throws SlickException {

        String str = "bernhardkiv: hello world!";
        int y = 0;
        unicodeFont.drawString(10, y, str);
        unicodeFont.drawString(10, y+=Y_OFFSET, str, Color.white, 0, 22);
        unicodeFont.drawString(10, y+=Y_OFFSET, str, 22, 0f);
        unicodeFont.drawString(10, y+=Y_OFFSET, str, 22, 0.10f);
        unicodeFont.drawString(10, y+=Y_OFFSET, str, 22, 0.25f);
        unicodeFont.drawString(10, y+=Y_OFFSET, str, 22, 0.50f);
        unicodeFont.drawString(10, y+=Y_OFFSET, str, 22, 0.75f);
        unicodeFont.drawString(10, y+=Y_OFFSET, str, 22, 0.90f);
        unicodeFont.drawString(10, y+=Y_OFFSET, str, 22, 1.0f);

    }

    public static void main(String[] args) {
        try {
            AppGameContainer appgc;
            appgc = new AppGameContainer(new TextFadeTest("TextFadeTest"));
            appgc.setDisplayMode(640, 480, false);
            appgc.start();
        }
        catch(SlickException ex) {
            Logger.getLogger(TextFadeTest.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

}

结果如下: