Add fading transition for changing Screens.

This commit is contained in:
Jan Philipp Timme 2014-11-29 16:33:27 +01:00
parent af9a676cc6
commit 573300ae49
2 changed files with 195 additions and 18 deletions

View File

@ -33,7 +33,7 @@ public class Main {
// Set up the LoadingScreen
final LoadingScreen loadingScreen = new LoadingScreen(superScreen);
superScreen.setOverlay(loadingScreen);
superScreen.setOverlay(loadingScreen, false);
// Initialize the GameFrame properly within the AWT EventQueue
try {

View File

@ -1,7 +1,9 @@
package de.teamteamteam.spacescooter.screen;
import java.awt.Color;
import java.awt.Graphics2D;
import de.teamteamteam.spacescooter.brain.GameConfig;
import de.teamteamteam.spacescooter.datastructure.ConcurrentIterator;
import de.teamteamteam.spacescooter.datastructure.ConcurrentLinkedList;
import de.teamteamteam.spacescooter.entity.Entity;
@ -29,6 +31,18 @@ public abstract class Screen {
* Parent Screen of the current Screen
*/
protected Screen parent;
/**
* Temporary holder of the new Overlay, used by setOverlay() to
* implement the transitions before passing focus to the new overlay.
*/
private Screen newOverlay;
/**
* Marker telling whether this Screen is currently processing a
* setOverlay call.
*/
private boolean processSetOverlayCall;
/**
* List of entities this screen is taking care of
@ -51,10 +65,29 @@ public abstract class Screen {
private ConcurrentIterator<Entity> collisionIteratorOne;
private ConcurrentIterator<Entity> collisionIteratorTwo;
/**
* Transition state, value in [0,1]. Used to implement
* the fade-to-black, fade-to-screen effects.
* Value reflects the "visibility" of the Screen, from
* 0 (not visible) to 1 (visible).
*/
private float transitionState;
/**
* Transition setting, telling whether a transition is currently happening
* and in which direction.
* Values: -1 (fade to black), 0 (nothing happening), 1 (fade to screen)
*/
private int transitionSetting;
/**
* Initialize parent, overlay and the Entity list
*/
public Screen(Screen parent) {
this.transitionState = 1.0F;
this.transitionSetting = 0;
this.newOverlay = null;
this.processSetOverlayCall = false;
this.setOverlay(null);
this.parent = parent;
this.entities = new ConcurrentLinkedList<Entity>();
@ -105,6 +138,8 @@ public abstract class Screen {
this.paint(g);
if(this.overlay != null) {
this.overlay.doPaint(g);
} else if(this.transitionState != 1.0) {
this.paintTransition(g);
}
}
@ -116,46 +151,133 @@ public abstract class Screen {
public final void doUpdate() {
if(this.overlay != null) {
this.overlay.doUpdate();
} else if (this.transitionSetting != 0) {
this.updateTransition();
} else {
this.update();
}
}
/**
* Sets the overlay Screen for the current Screen. In case an overlay is
* being replaced, the old overlays cleanup-method is called. Also, this
* takes care of the static currentScreen value, which is accessible for
* everybody.
* Sets the overlay Screen for the current Screen.
* Triggers a fade out transition.
* After that transition, fadeOutDone() will bubble back up.
* The transition will only trigger if the new overlay is not null
* OR an existing overlay is being removed.
*/
public final void setOverlay(Screen screen) {
if(this.overlay != null) this.overlay.cleanup();
if(screen == null) {
if(screen != null || (this.overlay != null && screen == null)) {
//Trigger the effect if we're fading to the new Screen.
this.processSetOverlayCall = true;
this.newOverlay = screen;
this.fadeOut();
} else if(this.overlay == null) {
this.overlay = null;
Screen.currentScreen = this;
} else {
Screen.currentScreen = screen;
}
this.overlay = screen;
}
/**
* When a Screens life ends, because it is removed, this method will take
* care of existing overlays and remove all references to existing entities.
* There are very few occasions where the transition effect is not neccessary.
* This overloaded method is used for this purpose.
*/
private final void cleanup() {
if(this.overlay != null) this.overlay.cleanup();
//tell all entities to cleanup themselves.
public final void setOverlay(Screen screen, boolean useEffect) {
if(useEffect == true) {
this.setOverlay(screen);
} else {
if(this.overlay != null) this.overlay.cleanup();
this.overlay = screen;
if(screen != null) {
Screen.currentScreen = screen;
} else {
Screen.currentScreen = this;
}
}
}
/**
* A screen will pass the fadeIn() effect up the most upper
* overlay, so the transition will be visible.
* After fadeIn, a call will be bubbled up to the parent, telling
* it that the transition has finished.
*/
private void fadeIn() {
if(this.overlay != null) {
this.overlay.fadeIn();
} else {
this.initializeTransition(1);
}
}
/**
* A screen will pass the fadeOut() effect up the most upper
* overlay, so the transition will be visible.
* After fadeOut, a call will be bubbled up to the parent, telling
* it that the transition has finished.
*/
private void fadeOut() {
if(this.overlay != null) {
this.overlay.fadeOut();
} else {
this.initializeTransition(-1);
}
}
/**
* Notification bubbling upwards telling that the fade in transition is done.
*/
private void fadeInDone() {
if(this.parent != null) {
this.parent.fadeInDone();
}
}
/**
* Notification bubbling upwards telling that the fade out transition is done.
* In case this Screen is processing a setOverlay call, the old overlay will now receive
* the cleanup() call, and the new screen will be put active with a fade in transition.
*/
private void fadeOutDone() {
if(this.processSetOverlayCall) {
if(this.overlay != null) {
this.overlay.cleanup();
}
if(this.newOverlay == null) {
Screen.currentScreen = this;
} else {
Screen.currentScreen = this.newOverlay;
}
this.overlay = this.newOverlay;
this.newOverlay = null;
this.fadeIn();
this.processSetOverlayCall = false;
} else {
if(this.parent != null) {
this.parent.fadeOutDone();
}
}
}
/**
* When a Screens life ends, this method will take care of existing overlays
* and remove all references to existing entities.
*/
private void cleanup() {
if(this.overlay != null) {
this.overlay.cleanup();
}
this.entityUpdateIterator.reset();
while(this.entityUpdateIterator.hasNext()) {
Entity e = this.entityUpdateIterator.next();
e.remove();
}
}
/**
* Returns second collision iterator.
* These are used for collision detection _only_!
*/
public ConcurrentIterator<Entity> getCollisionIteratorOne() {
public final ConcurrentIterator<Entity> getCollisionIteratorOne() {
return this.collisionIteratorOne;
}
@ -163,7 +285,62 @@ public abstract class Screen {
* Returns second collision iterator.
* These are used for collision detection _only_!
*/
public ConcurrentIterator<Entity> getCollisionIteratorTwo() {
public final ConcurrentIterator<Entity> getCollisionIteratorTwo() {
return this.collisionIteratorTwo;
}
/**
* Triggered by doUpdate().
* Updates the transitionState based on the current transitionSetting.
*/
private void updateTransition() {
this.transitionState += (0.03F * this.transitionSetting);
switch(this.transitionSetting) {
case 1:
if(this.transitionState > 1.0F) {
this.transitionState = 1.0F;
this.transitionSetting = 0;
this.fadeInDone();
}
break;
case -1:
if(this.transitionState < 0.0F) {
this.transitionState = 0.0F;
this.transitionSetting = 0;
this.fadeOutDone();
}
break;
default:
break;
}
}
/**
* Kick off a transition.
* Setting tells from where to where the transition will happen.
*/
private void initializeTransition(int setting) {
if(this.transitionSetting != 0 || setting == 0) return;
switch(setting) {
case 1:
this.transitionState = 0.0F;
break;
case -1:
this.transitionState = 1.0F;
break;
default:
break;
}
this.transitionSetting = setting;
}
/**
* Paint the transition effect.
*/
private void paintTransition(Graphics2D g) {
float alpha = (float) (1.0F - this.transitionState);
g.setColor(new Color(0.0F, 0.0F, 0.0F, alpha));
g.fillRect(0, 0, GameConfig.windowWidth, GameConfig.windowHeight);
}
}