Skip to content

Commit 2844675

Browse files
committed
Added a basic message state that allows various other code to
easily present "system" message to the user while in-game. They will scroll up automatically, fade out, and so on. We'll smooth scroll them later.
1 parent 38fdc7e commit 2844675

File tree

2 files changed

+191
-2
lines changed

2 files changed

+191
-2
lines changed

sim-eth-basic/src/main/java/example/GameSessionState.java

+11-2
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
package example;
3838

3939
import com.jme3.app.Application;
40+
import com.jme3.math.ColorRGBA;
4041

4142
import com.simsilica.event.EventBus;
4243
import com.simsilica.lemur.GuiGlobals;
@@ -52,7 +53,8 @@
5253
public class GameSessionState extends CompositeAppState {
5354

5455
public GameSessionState() {
55-
// super(); add normal states on the super-constructor
56+
// add normal states on the super-constructor
57+
super(new MessageState());
5658

5759
// Add states that need to support enable/disable independent of
5860
// the outer state using addChild().
@@ -70,17 +72,24 @@ protected void initialize( Application app ) {
7072

7173
EventBus.publish(GameSessionEvent.sessionStarted, new GameSessionEvent());
7274

75+
getState(MessageState.class).addMessage("> You have joined the game.", ColorRGBA.Yellow);
76+
7377
InputMapper inputMapper = GuiGlobals.getInstance().getInputMapper();
7478
inputMapper.activateGroup(MainGameFunctions.IN_GAME);
7579
}
7680

7781
@Override
7882
protected void cleanup( Application app ) {
79-
super.cleanup(app);
8083

8184
InputMapper inputMapper = GuiGlobals.getInstance().getInputMapper();
8285
inputMapper.deactivateGroup(MainGameFunctions.IN_GAME);
8386

8487
EventBus.publish(GameSessionEvent.sessionEnded, new GameSessionEvent());
88+
89+
// The below will fail because there is no message state anymore... so
90+
// it wouldn't show the message anyway.
91+
// getState(MessageState.class).addMessage("> You have left the game.", ColorRGBA.Yellow);
92+
93+
super.cleanup(app);
8594
}
8695
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
/*
2+
* $Id$
3+
*
4+
* Copyright (c) 2016, Simsilica, LLC
5+
* All rights reserved.
6+
*
7+
* Redistribution and use in source and binary forms, with or without
8+
* modification, are permitted provided that the following conditions
9+
* are met:
10+
*
11+
* 1. Redistributions of source code must retain the above copyright
12+
* notice, this list of conditions and the following disclaimer.
13+
*
14+
* 2. Redistributions in binary form must reproduce the above copyright
15+
* notice, this list of conditions and the following disclaimer in
16+
* the documentation and/or other materials provided with the
17+
* distribution.
18+
*
19+
* 3. Neither the name of the copyright holder nor the names of its
20+
* contributors may be used to endorse or promote products derived
21+
* from this software without specific prior written permission.
22+
*
23+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25+
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26+
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
27+
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
28+
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
29+
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
30+
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31+
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
32+
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
34+
* OF THE POSSIBILITY OF SUCH DAMAGE.
35+
*/
36+
37+
package example;
38+
39+
import java.util.*;
40+
41+
import com.jme3.app.Application;
42+
import com.jme3.app.state.BaseAppState;
43+
import com.jme3.math.*;
44+
import com.jme3.scene.*;
45+
import com.jme3.util.SafeArrayList;
46+
47+
import com.simsilica.lemur.*;
48+
import com.simsilica.lemur.style.ElementId;
49+
50+
/**
51+
* Pops up various types of messages from the bottom of the screen.
52+
*
53+
* @author Paul Speed
54+
*/
55+
public class MessageState extends BaseAppState {
56+
57+
public static final ElementId MESSAGE_LABEL_ID = new ElementId("message.label");
58+
59+
private Node messageRoot;
60+
61+
private SafeArrayList<Message> messages = new SafeArrayList<>(Message.class);
62+
63+
private float fadeRate = 1/8f; // 8 seconds
64+
65+
public MessageState() {
66+
}
67+
68+
public Label addMessage( String message ) {
69+
return addMessage(new Label(message, MESSAGE_LABEL_ID));
70+
}
71+
72+
public Label addMessage( String message, ColorRGBA color ) {
73+
Label result = new Label(message, MESSAGE_LABEL_ID);
74+
result.setColor(color);
75+
addMessage(result);
76+
return result;
77+
}
78+
79+
public <T extends Panel> T addMessage( T label ) {
80+
messages.add(0, new Message(label));
81+
messageRoot.attachChild(label);
82+
refreshLayout();
83+
return label;
84+
}
85+
86+
@Override
87+
protected void initialize( Application app ) {
88+
89+
// We keep a root node that we can slide as new
90+
// messages come in. Initially we can just pop the
91+
// new messages up relative to the messageRoot but eventually
92+
// we can also animate this node to slide up from a lower
93+
// position to make it look like the messages are sliding
94+
// up smoothly.
95+
messageRoot = new Node("MessageRoot");
96+
}
97+
98+
@Override
99+
protected void cleanup( Application app ) {
100+
}
101+
102+
float nextTime = 1;
103+
protected void addTestMessages( float tpf ) {
104+
nextTime -= tpf;
105+
if( nextTime < 0 ) {
106+
nextTime = (float)(Math.random() * 1.9) + 0.1f;
107+
int count = (int)(Math.random() * 4) + 1;
108+
for( int i = 0; i < count; i++ ) {
109+
if( (i % 2) == 0 ) {
110+
addMessage("> Tick " + System.currentTimeMillis());
111+
} else {
112+
addMessage("> Tock " + System.currentTimeMillis());
113+
}
114+
}
115+
}
116+
}
117+
118+
public void update( float tpf ) {
119+
120+
//addTestMessages(tpf);
121+
122+
for( Message m : messages.getArray() ) {
123+
m.update(tpf);
124+
}
125+
}
126+
127+
@Override
128+
protected void onEnable() {
129+
Node gui = ((Main)getApplication()).getGuiNode();
130+
gui.attachChild(messageRoot);
131+
132+
// Set a small margin for the messages
133+
messageRoot.setLocalTranslation(5, 5, 0);
134+
}
135+
136+
@Override
137+
protected void onDisable() {
138+
messageRoot.removeFromParent();
139+
}
140+
141+
protected void refreshLayout() {
142+
int height = getApplication().getCamera().getHeight();
143+
int width = getApplication().getCamera().getWidth();
144+
145+
float y = 0;
146+
for( Message m : messages.getArray() ) {
147+
Vector3f pref = m.label.getPreferredSize();
148+
y += pref.y;
149+
m.label.setLocalTranslation(0, y, 0);
150+
if( y > height ) {
151+
// Off the screen so remove it
152+
m.label.removeFromParent();
153+
messages.remove(m);
154+
}
155+
}
156+
}
157+
158+
private class Message {
159+
private float alpha;
160+
private float overrideAlpha = 0;
161+
private Panel label;
162+
163+
public Message( Panel label ) {
164+
this(label, 1);
165+
}
166+
167+
public Message( Panel label, float initialAlpha ) {
168+
this.alpha = initialAlpha;
169+
this.label = label;
170+
}
171+
172+
public void update( float tpf ) {
173+
alpha -= tpf * fadeRate;
174+
if( alpha < 0 ) {
175+
alpha = 0;
176+
}
177+
label.setAlpha(Math.max(alpha, overrideAlpha));
178+
}
179+
}
180+
}

0 commit comments

Comments
 (0)