First, I wanted to share a cool Text-based Menu you can use for you Android/Desktop Games (since I haven’t done anything in the way of documentation for the 2D framework, I thought more examples might help).
The menu just displays a list of text links, however… they ripple outward from the point of contact when clicked or touched.
Here is a vid showing how they work… then the classes needed and a usage sample (In the usage sample, you’ll see how to also use ExecuteAction to call a method after a dureation of time has passed):
[video]http://youtu.be/V8nYDnoNyU4[/video]
Here is the scale utility (used to determine basic screen scaling and also has a method for adjust fonts appropriately:
ScaleUtil.java
[java]
import com.jme3.font.BitmapFont;
import com.jme3.font.LineWrapMode;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector2f;
import tonegod.gui.controls.text.TextElement;
import tonegod.gui.core.Screen;
import tonegod.gui.core.utils.UIDUtil;
/**
*
-
@author t0neg0d
*/
public class ScaleUtil {
private static float REF_WIDTH = 480;
private static float REF_HEIGHT = 720;private Main main;
private Screen screen;private float baseFontSize = 30;
private float gameScale = 1;
private float fontScale = 1;public ScaleUtil(Main main, Screen screen) {
this.main = main;
this.screen = screen;gameScale = screen.getWidth()/REF_WIDTH; fontScale = getFontScale(baseFontSize)/baseFontSize;
}
public float getGameScale() { return this.gameScale; }
public float getFontScale() { return this.fontScale; }private float getFontScale(float startSize) {
Vector2f dim1 = new Vector2f(REF_WIDTH, REF_HEIGHT);
TextElement refString = getTestLabel(UIDUtil.getUID(),“Testing”, dim1);
refString.setFontSize(startSize);
float refScale = refString.getAnimText().getLineWidth()/dim1.x;Vector2f dim2 = new Vector2f(screen.getWidth(), REF_HEIGHT); TextElement testString = getTestLabel(UIDUtil.getUID(),"Testing", dim2); startSize = 5; testString.setFontSize(startSize); float testScale = testString.getAnimText().getLineWidth()/dim2.x; while (testScale < refScale) { startSize++; testString.setFontSize(startSize); testScale = testString.getAnimText().getLineWidth()/dim2.x; } return startSize;
}
private TextElement getTestLabel(String UID, String text, Vector2f dim) {
TextElement el = new TextElement(screen, UID, Vector2f.ZERO, new Vector2f(dim), null) {
@Override
public void onUpdate(float tpf) { }
@Override
public void onEffectStart() { }
@Override
public void onEffectStop() { }
};
el.setIsResizable(false);
el.setIsMovable(false);
el.setUseTextClipping(false);
el.setTextWrap(LineWrapMode.NoWrap);
el.setTextVAlign(BitmapFont.VAlign.Center);
el.setTextAlign(BitmapFont.Align.Center);
el.setFont(main.getDefaultFont());
el.setFontColor(ColorRGBA.White);
el.setFontSize(baseFontSize);//*main.getGameScale());
el.setText(text);
el.setIgnoreMouse(true);
el.getAnimText().setIgnoreMouse(true);
return el;
}
}
[/java]
Here are the 3 classes needed:
TextButton.java
[java]
import com.jme3.font.BitmapFont;
import com.jme3.input.event.MouseButtonEvent;
import com.jme3.math.Vector2f;
import tonegod.gui.core.Screen;
import tonegod.gui.framework.core.AnimText;
import tonegod.gui.listeners.MouseButtonListener;
/**
*
-
@author t0neg0d
*/
public class TextButton extends AnimText implements MouseButtonListener {
Screen screen;public TextButton(Screen screen, Vector2f position, BitmapFont font) {
super(screen.getApplication().getAssetManager(), font);
this.screen = screen;
this.setIsMovable(false);
this.setZOrderEffect(ZOrderEffect.None);
}public void onMouseLeftPressed(MouseButtonEvent evt) { }
public void onMouseLeftReleased(MouseButtonEvent evt) { }
public void onMouseRightPressed(MouseButtonEvent evt) { }
public void onMouseRightReleased(MouseButtonEvent evt) { }
}
[/java]
TextMenuItem.java
[java]
import com.jme3.input.event.MouseButtonEvent;
import com.jme3.math.Vector2f;
import tonegod.gui.core.Screen;
import tonegod.gui.core.utils.UIDUtil;
import tonegod.gui.framework.animation.ScaleByAction;
import tonegod.gui.framework.core.QuadData;
/**
*
-
@author t0neg0d
*/
public class TextMenuItem {
private Main main;
private Screen screen;
private TextMenu menu;
private String label;
private TextButton button;
private int index;
private String UID;
private int sQuadIndex = 0;
private int lQuadIndex = 0, rQuadIndex = 0;
private float rippleTime = .05f, rippleCounter = 0;
private float scaleTime = 0.25f;
private boolean ripple = false;
private float executeTime = 0;public TextMenuItem(Main main, Screen screen, TextMenu menu, String label) {
this.main = main;
this.screen = screen;
this.menu = menu;
this.label = label;
this.index = menu.getNextIndex();
this.UID = UIDUtil.getUID();initMenuItem();
}
private void initMenuItem() {
button = new TextButton(screen, Vector2f.ZERO, main.getDefaultFont()) {
@Override
public void animElementUpdate(float tpf) {
super.animElementUpdate(tpf);
rippleText(tpf);
}@Override public void onMouseLeftReleased(MouseButtonEvent evt) { startRippleEffect(screen.getEventQuad().userIndex); menu.handleMenuItemClick(index); } }; button.setFontSize(menu.getDefaultFontSize()); button.setText(label); executeTime = (button.getQuads().size()-1)*rippleTime+scaleTime;
}
public TextButton getButton() { return this.button; }
public String getUID() { return this.UID; }
public float getExecuteTime() {
return this.executeTime;
}private void startRippleEffect(int quadIndex) {
sQuadIndex = quadIndex;
lQuadIndex = quadIndex;
rQuadIndex = quadIndex;ripple = true;
}
public void resetRipple() {
ripple = false;
rippleCounter = 0;
for (QuadData qd : button.getQuads().values()) {
qd.actions.clear();
qd.setScale(1, 1);
qd.setRotation(0);
}
}private void rippleText(float tpf) {
if (ripple) {
rippleCounter += tpf;
if (rippleCounter >= rippleTime) {
if (lQuadIndex == rQuadIndex) {
if (button.getQuadDataAt(sQuadIndex).actions.isEmpty()) {
ScaleByAction lScale = new ScaleByAction();
lScale.setAmount(.6f, .85f);
lScale.setDuration(.25f);
lScale.setAutoReverse(true);
button.getQuadDataAt(sQuadIndex).addAction(lScale);
}
lQuadIndex–;
rQuadIndex++;
} else {
if (lQuadIndex > -1) {
if (button.getQuadDataAt(lQuadIndex).actions.isEmpty()) {
ScaleByAction lScale = new ScaleByAction();
lScale.setAmount(.6f, .85f);
lScale.setDuration(.25f);
lScale.setAutoReverse(true);
button.getQuadDataAt(lQuadIndex).addAction(lScale);
}
lQuadIndex–;
}
if (rQuadIndex < button.getQuads().size()) {
if (button.getQuadDataAt(rQuadIndex).actions.isEmpty()) {
ScaleByAction rScale = new ScaleByAction();
rScale.setAmount(.6f, .85f);
rScale.setDuration(.25f);
rScale.setAutoReverse(true);
button.getQuadDataAt(rQuadIndex).addAction(rScale);
}
rQuadIndex++;
}
if (lQuadIndex == -1 && rQuadIndex == button.getQuads().size()) {
boolean active = false;
for (QuadData qd : button.getQuads().values()) {
if (!qd.actions.isEmpty()) {
active = true;
break;
}
}
if (!active)
resetRipple();
}
}
rippleCounter = 0;
}
}
}
}
[/java]
TextMenu.java
[java]
import com.jme3.math.Vector2f;
import java.util.LinkedList;
import java.util.List;
import tonegod.gui.core.Screen;
/**
*
-
@author t0neg0d
*/
public abstract class TextMenu {
private Main main;
private Screen screen;
private List<TextMenuItem> menuItems = new LinkedList();
private int index = -1;
private float fontSize = 50;
private float spacer = 10;
private Vector2f pos = new Vector2f();public TextMenu(Main main, Screen screen) {
this.main = main;
this.screen = screen;
fontSize *= main.getScaleManager().getFontScale();
spacer *= main.getScaleManager().getGameScale();
}public void addMenuItem(String label) {
index++;
TextMenuItem mi = new TextMenuItem(main, screen, this, label);
menuItems.add(mi);
}public void pack() {
float lineHeight = menuItems.get(0).getButton().getLineHeight();
float totalSize = lineHeightmenuItems.size()+(spacer(menuItems.size()-1));
float halfSize = totalSize/2;pos.set(screen.getWidth()/2,screen.getHeight()/2+(halfSize)); for (TextMenuItem mi : menuItems) { pos.setX(screen.getWidth()/2-(mi.getButton().getLineWidth()/2)); mi.getButton().setPosition(pos); pos.setY(pos.getY()-(lineHeight+spacer)); }
}
public TextMenuItem getMenuItem(int index) {
return menuItems.get(index);
}public int getNextIndex() { return this.index; }
public float getDefaultFontSize() { return this.fontSize; }public void displayMenu() {
for (TextMenuItem mi : menuItems) {
main.getMenuLayer().addAnimElement(mi.getUID(),mi.getButton());
}
}public void hideMenu() {
for (TextMenuItem mi : menuItems) {
main.getMenuLayer().removeAnimElement(mi.getUID());
}
}public abstract void handleMenuItemClick(int index);
}
[/java]
Create an AnimLayer for your Menus to display in in your main class (or alter the source above to reflect where ever you want to put this:
[java]
AnimLayer menuLayer;
ScaleUtil scaleManager;
// in init
scaleMAnager = new ScaleUtil(this, screen);
menuLayer = screen.addAnimLayer(“menuLayer”);
// Somewhere in main
public ScaleUtil getScaleManager() { return this.scaleManager; }
public AnimLayer getMenuLayer() { return this.menuLayer; }
[/java]
And lastly, Usage Sample:
[java]
menu = new TextMenu(main, screen) {
@Override
public void handleMenuItemClick(int index) {
ExecuteAction ea;
switch (index) {
case 0:
System.out.println("0: " + menu.getMenuItem(index).getButton().getText());
break;
case 1:
System.out.println("1: " + menu.getMenuItem(index).getButton().getText());
break;
case 2:
System.out.println("2: " + menu.getMenuItem(index).getButton().getText());
break;
case 3:
ea = new ExecuteAction() {
@Override
public void execute() { System.exit(0); }
};
screen.getAnimManager().addQueuedAction(ea, menu.getMenuItem(index).getButton(), menu.getMenuItem(index).getExecuteTime());
break;
}
}
};
menu.addMenuItem(“New Game”);
menu.addMenuItem(“Options”);
menu.addMenuItem(“Controls”);
menu.addMenuItem(“Quit”);
menu.pack();
// To show the menu
menu.displayMenu();
// To remove the menu:
menu.hideMenu();
[/java]
Anyways, hope someone finds this useful.