Expand menu buttons when hovering.

Added hovering capabilities to GUIMenuButton.  The max scale and direction to expand the image can be modified per object.

Signed-off-by: Jeffrey Han <itdelatrisu@gmail.com>
This commit is contained in:
Jeffrey Han 2014-12-23 23:41:37 -05:00
parent ce8ae40f79
commit 33f5df030c
8 changed files with 174 additions and 20 deletions

View File

@ -25,6 +25,7 @@ import org.newdawn.slick.Image;
/**
* A convenience class for menu buttons.
* Consists of an image or animation and coordinates.
* Multi-part images and animations currently do not support hover updates.
*/
public class GUIMenuButton {
/**
@ -48,20 +49,37 @@ public class GUIMenuButton {
private float x, y;
/**
* The x and y radius of the button.
* The x and y radius of the button (scaled).
*/
private float xRadius, yRadius;
/**
* The current and max scale of the button (for hovering).
*/
private float scale, hoverScale = 1.25f;
/**
* The scaled expansion direction for the botton (for hovering).
*/
private Expand dir = Expand.CENTER;
/**
* Scaled expansion directions (for hovering).
*/
public enum Expand {
CENTER, UP_RIGHT, UP_LEFT, DOWN_RIGHT, DOWN_LEFT;
}
/**
* Creates a new button from an Image.
*/
public GUIMenuButton(Image img, float x, float y) {
this.img = img;
this.x = x;
this.y = y;
xRadius = img.getWidth() / 2f;
yRadius = img.getHeight() / 2f;
this.x = x;
this.y = y;
this.xRadius = img.getWidth() / 2f;
this.yRadius = img.getHeight() / 2f;
this.scale = 1f;
}
/**
@ -72,11 +90,11 @@ public class GUIMenuButton {
this.img = imgCenter;
this.imgL = imgLeft;
this.imgR = imgRight;
this.x = x;
this.y = y;
xRadius = (img.getWidth() + imgL.getWidth() + imgR.getWidth()) / 2f;
yRadius = img.getHeight() / 2f;
this.x = x;
this.y = y;
this.xRadius = (img.getWidth() + imgL.getWidth() + imgR.getWidth()) / 2f;
this.yRadius = img.getHeight() / 2f;
this.scale = 1f;
}
/**
@ -84,11 +102,11 @@ public class GUIMenuButton {
*/
public GUIMenuButton(Animation anim, float x, float y) {
this.anim = anim;
this.x = x;
this.y = y;
xRadius = anim.getWidth() / 2f;
yRadius = anim.getHeight() / 2f;
this.x = x;
this.y = y;
this.xRadius = anim.getWidth() / 2f;
this.yRadius = anim.getHeight() / 2f;
this.scale = 1f;
}
/**
@ -110,9 +128,11 @@ public class GUIMenuButton {
*/
public void draw() {
if (img != null) {
if (imgL == null)
img.draw(x - xRadius, y - yRadius);
else {
if (imgL == null) {
Image imgScaled = img.getScaledCopy(scale);
imgScaled.setAlpha(img.getAlpha());
imgScaled.draw(x - xRadius, y - yRadius);
} else {
img.draw(x - xRadius + imgL.getWidth(), y - yRadius);
imgL.draw(x - xRadius, y - yRadius);
imgR.draw(x + xRadius - imgR.getWidth(), y - yRadius);
@ -128,7 +148,7 @@ public class GUIMenuButton {
public void draw(Color filter) {
if (img != null) {
if (imgL == null)
img.draw(x - xRadius, y - yRadius, filter);
img.getScaledCopy(scale).draw(x - xRadius, y - yRadius, filter);
else {
img.draw(x - xRadius + imgL.getWidth(), y - yRadius, filter);
imgL.draw(x - xRadius, y - yRadius, filter);
@ -140,9 +160,77 @@ public class GUIMenuButton {
/**
* Returns true if the coordinates are within the button bounds.
* @param cx the x coordinate
* @param cy the y coordinate
*/
public boolean contains(float cx, float cy) {
return ((cx > x - xRadius && cx < x + xRadius) &&
(cy > y - yRadius && cy < y + yRadius));
}
/**
* Sets the current button scale (for hovering).
* @param scale the new scale (default 1.0f)
*/
public void setScale(float scale) {
this.scale = scale;
setHoverRadius();
}
/**
* Sets the maximum scale factor for the button (for hovering).
* @param scale the maximum scale factor (default 1.25f)
*/
public void setHoverScale(float scale) {
this.hoverScale = scale;
}
/**
* Sets the expansion direction when hovering over the button.
* @param dir the direction
*/
public void setHoverDir(Expand dir) {
this.dir = dir;
}
/**
* Updates the scale of the button depending on whether or not the cursor
* is hovering over the button.
* @param delta the delta interval
* @param cx the x coordinate
* @param cy the y coordinate
*/
public void hoverUpdate(int delta, float cx, float cy) {
boolean isHover = contains(cx, cy);
if (isHover && scale < hoverScale) {
scale += (hoverScale - 1f) * delta / 100f;
if (scale > hoverScale)
scale = hoverScale;
setHoverRadius();
} else if (!isHover && scale > 1f) {
scale -= (hoverScale - 1f) * delta / 100f;
if (scale < 1f)
scale = 1f;
setHoverRadius();
}
}
/**
* Set x and y radius of the button based on current scale factor
* and expansion direction.
*/
private void setHoverRadius() {
int xOffset = 0, yOffset = 0;
if (dir != Expand.CENTER) {
// offset by difference between normal/scaled image dimensions
xOffset = (int) ((scale - 1f) * img.getWidth());
yOffset = (int) ((scale - 1f) * img.getHeight());
if (dir == Expand.DOWN_RIGHT || dir == Expand.UP_RIGHT)
xOffset *= -1; // flip x for right
if (dir == Expand.DOWN_LEFT || dir == Expand.DOWN_RIGHT)
yOffset *= -1; // flip y for down
}
this.xRadius = ((img.getWidth() * scale) + xOffset) / 2f;
this.yRadius = ((img.getHeight() * scale) + yOffset) / 2f;
}
}

View File

@ -107,6 +107,7 @@ public enum GameMod {
// create button
img.setAlpha(0.5f);
this.button = new GUIMenuButton(img, x + (offsetX * id), y);
this.button.setHoverScale(1.15f);
} catch (SlickException e) {
Log.error(String.format("Failed to initialize game mod '%s'.", this), e);
}
@ -177,4 +178,19 @@ public enum GameMod {
* @return true if within bounds
*/
public boolean contains(float x, float y) { return button.contains(x, y); }
/**
* Sets the current button scale (for hovering).
* @param scale the new scale (default 1.0f)
*/
public void setScale(float scale) { button.setScale(scale); }
/**
* Updates the scale of the button depending on whether or not the cursor
* is hovering over the button.
* @param delta the delta interval
* @param x the x coordinate
* @param y the y coordinate
*/
public void hoverUpdate(int delta, float x, float y) { button.hoverUpdate(delta, x, y); }
}

View File

@ -197,6 +197,7 @@ public class Utils {
backButton = new GUIMenuButton(back,
back.getWidth() / 2f,
height - (back.getHeight() / 2f));
backButton.setHoverDir(GUIMenuButton.Expand.UP_RIGHT);
// set default game images
for (GameImage img : GameImage.values())

View File

@ -101,6 +101,10 @@ public class GamePauseMenu extends BasicGameState {
public void update(GameContainer container, StateBasedGame game, int delta)
throws SlickException {
Utils.updateCursor(delta);
int mouseX = input.getMouseX(), mouseY = input.getMouseY();
continueButton.hoverUpdate(delta, mouseX, mouseY);
retryButton.hoverUpdate(delta, mouseX, mouseY);
backButton.hoverUpdate(delta, mouseX, mouseY);
}
@Override
@ -165,6 +169,9 @@ public class GamePauseMenu extends BasicGameState {
SoundController.playSound(SoundController.SOUND_FAIL);
} else
MusicController.pause();
continueButton.setScale(1f);
retryButton.setScale(1f);
backButton.setScale(1f);
}
/**

View File

@ -61,6 +61,7 @@ public class GameRanking extends BasicGameState {
// game-related variables
private StateBasedGame game;
private int state;
private Input input;
public GameRanking(int state) {
this.state = state;
@ -70,6 +71,7 @@ public class GameRanking extends BasicGameState {
public void init(GameContainer container, StateBasedGame game)
throws SlickException {
this.game = game;
this.input = container.getInput();
score = Game.getGameScore();
@ -138,6 +140,8 @@ public class GameRanking extends BasicGameState {
public void update(GameContainer container, StateBasedGame game, int delta)
throws SlickException {
Utils.updateCursor(delta);
int mouseX = input.getMouseX(), mouseY = input.getMouseY();
Utils.getBackButton().hoverUpdate(delta, mouseX, mouseY);
}
@Override
@ -185,6 +189,7 @@ public class GameRanking extends BasicGameState {
public void enter(GameContainer container, StateBasedGame game)
throws SlickException {
Display.setTitle(game.getTitle());
Utils.getBackButton().setScale(1f);
SoundController.playSound(SoundController.SOUND_APPLAUSE);
}
}

View File

@ -103,6 +103,7 @@ public class MainMenu extends BasicGameState {
// game-related variables
private GameContainer container;
private StateBasedGame game;
private Input input;
private int state;
public MainMenu(int state) {
@ -114,6 +115,7 @@ public class MainMenu extends BasicGameState {
throws SlickException {
this.container = container;
this.game = game;
this.input = container.getInput();
osuStartTime = System.currentTimeMillis();
previous = new Stack<Integer>();
@ -126,6 +128,7 @@ public class MainMenu extends BasicGameState {
float buttonScale = (height / 1.2f) / logoImg.getHeight();
Image logoImgScaled = logoImg.getScaledCopy(buttonScale);
logo = new GUIMenuButton(logoImgScaled, width / 2f, height / 2f);
logo.setHoverScale(1.05f);
Image playImg = new Image("menu-play.png");
Image exitImg = new Image("menu-exit.png");
@ -138,6 +141,8 @@ public class MainMenu extends BasicGameState {
exitButton = new GUIMenuButton(exitImg.getScaledCopy(buttonScale),
width * 0.75f - exitOffset, (height / 2) + (exitImg.getHeight() / 2f)
);
playButton.setHoverScale(1.05f);
exitButton.setHoverScale(1.05f);
// initialize music buttons
int musicWidth = 48;
@ -146,6 +151,10 @@ public class MainMenu extends BasicGameState {
musicPause = new GUIMenuButton(new Image("music-pause.png"), width - (2 * musicWidth), musicHeight);
musicNext = new GUIMenuButton(new Image("music-next.png"), width - musicWidth, musicHeight);
musicPrevious = new GUIMenuButton(new Image("music-previous.png"), width - (3 * musicWidth), musicHeight);
musicPlay.setHoverScale(1.5f);
musicPause.setHoverScale(1.5f);
musicNext.setHoverScale(1.5f);
musicPrevious.setHoverScale(1.5f);
// menu background
try {
@ -226,6 +235,14 @@ public class MainMenu extends BasicGameState {
public void update(GameContainer container, StateBasedGame game, int delta)
throws SlickException {
Utils.updateCursor(delta);
int mouseX = input.getMouseX(), mouseY = input.getMouseY();
logo.hoverUpdate(delta, mouseX, mouseY);
playButton.hoverUpdate(delta, mouseX, mouseY);
exitButton.hoverUpdate(delta, mouseX, mouseY);
musicPlay.hoverUpdate(delta, mouseX, mouseY);
musicPause.hoverUpdate(delta, mouseX, mouseY);
musicNext.hoverUpdate(delta, mouseX, mouseY);
musicPrevious.hoverUpdate(delta, mouseX, mouseY);
// fade in background
if (bgAlpha < 0.9f) {
@ -280,6 +297,13 @@ public class MainMenu extends BasicGameState {
logoClicked = false;
logoTimer = 0;
logo.setX(container.getWidth() / 2);
logo.setScale(1f);
playButton.setScale(1f);
exitButton.setScale(1f);
musicPlay.setScale(1f);
musicPause.setScale(1f);
musicNext.setScale(1f);
musicPrevious.setScale(1f);
}
@Override

View File

@ -525,6 +525,10 @@ public class Options extends BasicGameState {
public void update(GameContainer container, StateBasedGame game, int delta)
throws SlickException {
Utils.updateCursor(delta);
int mouseX = input.getMouseX(), mouseY = input.getMouseY();
Utils.getBackButton().hoverUpdate(delta, mouseX, mouseY);
for (GameMod mod : GameMod.values())
mod.hoverUpdate(delta, mouseX, mouseY);
}
@Override
@ -782,6 +786,9 @@ public class Options extends BasicGameState {
public void enter(GameContainer container, StateBasedGame game)
throws SlickException {
currentTab = TAB_DISPLAY;
Utils.getBackButton().setScale(1f);
for (GameMod mod : GameMod.values())
mod.setScale(1f);
}
/**

View File

@ -180,6 +180,7 @@ public class SongMenu extends BasicGameState {
// options button
Image optionsIcon = new Image("options.png").getScaledCopy(iconScale);
optionsButton = new GUIMenuButton(optionsIcon, search.getX() - (optionsIcon.getWidth() * 1.5f), search.getY());
optionsButton.setHoverScale(1.75f);
// music note
int musicNoteDim = (int) (Utils.FONT_LARGE.getLineHeight() * 0.75f + Utils.FONT_DEFAULT.getLineHeight());
@ -281,6 +282,9 @@ public class SongMenu extends BasicGameState {
public void update(GameContainer container, StateBasedGame game, int delta)
throws SlickException {
Utils.updateCursor(delta);
int mouseX = input.getMouseX(), mouseY = input.getMouseY();
Utils.getBackButton().hoverUpdate(delta, mouseX, mouseY);
optionsButton.hoverUpdate(delta, mouseX, mouseY);
// search
search.setFocus(true);
@ -501,6 +505,8 @@ public class SongMenu extends BasicGameState {
public void enter(GameContainer container, StateBasedGame game)
throws SlickException {
Display.setTitle(game.getTitle());
Utils.getBackButton().setScale(1f);
optionsButton.setScale(1f);
// stop playing the theme song
if (MusicController.isThemePlaying() && focusNode != null)