Disclaimer:
This is new territory for me, but since no one else seems to be answering, I will make an attempt since this question seems kind of interesting to me. I haven't thoroughly tested these code modifications, so there may be bugs/side-effects I am unaware of. Use at your own risk. :-)
The org.newdawn.slick.Font
implementation classes (and supporting classes) in the Slick2D library don't appear to be extension-friendly (private members and methods). As far as I can tell, in order to achieve the desired effect, changes are required to be made to some of the these internal Slick2D classes. (The alternative being to copy entire classes and packages.) To my understanding from the question asker's comments, the Slick2D source code is contained within the question asker's project, so making changes to internal Slick2D classes should be workable in this case.
It can be argued that the said classes were not made extension-friendly for a reason, and there may better ways to go about making the changes necessary to make them extensible. I might even go about it differently myself given an actual project and more time. However, the goal of this answer is to show the question asker the essence of how I achieved the desired effect with minimal code changes to the internal Slick2D classes. The question asker can make his/her own decisions how to go about the details of applying these changes to his/her project.
This code uses Slick build 237.
You can see the complete code changes at this github repo. Below I show only the code changes most relevant to achieve the desired fade effect.
Think of each character as being drawn in an GL_QUAD with the four corners each having an RGBA value (as described here where this picture is taken from):
(x,x,x,1) (x,x,x,0)
-----------------
| |
| |
| |
| |
| |
| |
-----------------
(x,x,x,1) (x,x,x,0)
What we are doing is leaving the top left and bottom left corner colors unchanged and changing the top right and bottom right corner colors to have an alpha of 0 (transparent). OpenGL will gradually fade the transparency for us. To do this we extend org.newdawn.slick.Image
to create a FadableImage
with the following method (which is very similar to Image
's drawEmbedded
method but does not override it):
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);
}
}
}
You can see the diff of what I am about to describe.
In order to use FadableImage
, we also need to extend org.newdawn.slick.font.GlyphPage
to create FadableGlyphPage
. To support FadableGlyphPage
we need to modify org.newdawn.slick.font.GlyphPage
's constructor and add a new one.
Then we need to modify org.newdawn.slick.UnicodeFont
to use our FadableGlyphPage
when UnicodeFont
loads the glyphs in loadGlyphs
.
We change UnicodeFont
's drawDisplayList(...)
method to call our FadableImage
's special drawEmbeddedFaded(...)
method for the character at the specified end index of the given string. We also have to make modifications so that the DisplayList
caching will be handled correctly.
We add a convenience method to UnicodeFont
to draw a string with the character at endIndex-1 faded and drawn to the width percentage specified by fadePercentWidth:
public void drawString(float x, float y, String text, int endIndex, float fadePercentWidth) {
drawDisplayList(x, y, text, Color.white, 0, endIndex, fadePercentWidth);
}
So the idea is that the last character rendered will be the character at endIndex-1. The fade width percentage is the percentage of the width that will be drawn of the quad containing the character at endIndex-1. So you can play with that parameter to fine tune the effect you want (0.0f
to 1.0f
). With the fade width percentage set to 0.5f
, 50% of the total width of the quad will be drawn. And the fade will be to that 50% width:
(x,x,x,1) (x,x,x,0)
------------|------------
| | |
| drawn | not |
| | drawn |
| | |
| | |
| | |
------------|------------
(x,x,x,1) (x,x,x,0)
Now let's test it with this code:
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);
}
}
}
Which gives this result:
