Death & Win Screen Code Implementation - UQdeco2800/2022-studio-2 GitHub Wiki
Death & Win Screen Code Implementation - Sprint 3 & 4
Implemented and written by Elias, supported* by Isaac
[*] Supported referring to any issues with implementation I didn't understand or was stuck on Isaac helped to fix that specific issue.
Introduction
The death screen code implementation is modelled after the Main Menu, it uses DeathScreenActions, DeathScreenDisplay, and DeathScreen classes to display the visual assets and appropriately use eventlisteners.
This was iterated on a non-existent implementation in sprint 2, replacing all previous mentions of the death screen with a working, well-designed class system.
DeathScreen
The relevant death screen images are loaded in as a texture, first created:
private static final String[] deathTextures = {"images/DeathScreens/lvl_1.png", "images/DeathScreens/lvl_2.png"};
Then set out to be loaded in loadAssets()
private void loadAssets() {
logger.debug("Loading assets");
ResourceService resourceService = ServiceLocator.getResourceService();
resourceService.loadTextures(deathTextures);
resourceService.loadMusic(deathMusic);
ServiceLocator.getResourceService().loadAll();
}
And the constructor loads them:
loadAssets();
DeathScreen()
also creates the UI, disposes of the loaded assets, plays music and inherent the relevant parent classes.
DeathScreenActions
Before we display DeathScreen we need to set up the actions it'll do, DeathScreenActions
extends Components, it listens for the relevant actions that will occur due to button presses in DeathScreenDisplay, this holds continueGame
action and exit
action.
/**
* Creates the event listeners relevant to DeathScreen
*/
@Override
public void create() {
entity.getEvents().addListener("continueGame", this::onContinue);
entity.getEvents().addListener("exit", this::onExit);
}
/**
* Restarts the game by reloading Main Game screen.
*/
private void onContinue() {
Sound sound = Gdx.audio.newSound(Gdx.files.internal("sounds/ButtonSoundtrack.wav"));
sound.play(1.0f);
logger.info("Continue playing game after death");
game.setScreen(GdxGame.ScreenType.MAIN_GAME);
}
/**
* Returns to main menu after death when exit is selected.
*/
private void onExit() {
Sound sound = Gdx.audio.newSound(Gdx.files.internal("sounds/ButtonSoundtrack.wav"));
sound.play(1.0f);
logger.info("Return to main menu");
game.setScreen(GdxGame.ScreenType.MAIN_MENU);
}
DeathScreenDisplay
DeathScreenDisplay
is a UI component, it works the exact same as Main Menu, it is triggered when player health is 0. It's main different from MainMenuDisplay
is it takes a level
variable. If level
equals 1, levelBackground()
ensures that the image displayed in the addActors is the level 1 death image, same with level 2, level 1 being the default. This method also has a setButtonDisplay()
class that managed the size of the transparent buttons placed over the image that is staged.
Logic: [deathBackground
is the image then given to the addActor to add it to the table.]
public Image levelBackground (int level) {
switch (level){
case 1:
logger.info("setting level 1 deathscreen from DeathScreenDisplay");
return deathBackground = new Image(ServiceLocator.getResourceService()
.getAsset("images/DeathScreens/lvl_1.png", Texture.class));
case 2:
logger.info("setting level 2 deathscreen from DeathScreenDisplay");
return deathBackground = new Image(ServiceLocator.getResourceService()
.getAsset("images/DeathScreens/lvl_2.png", Texture.class));
}
return deathBackground = new Image(ServiceLocator.getResourceService()
.getAsset("images/DeathScreens/lvl_1.png", Texture.class));
}
addActor()
displays the background image that is then staged. It then calls setButtonDisplay()
to ensure the correct sized buttons are staged as well.
private void addActors() {
// Set's background image based on the level
Image background = levelBackground(level);
// Makes sure the image fills the entire screen
background.setFillParent(true);
// Stages the image
stage.addActor(background);
// stages the buttons
setButtonDisplay();
logger.debug("DeathScreenDisplay background image and buttons has been added to the actor");
}
setButtonDisplay
This class manages the listeners related to the death and win screens. It sets up button displays and adds them to the stage. This is done by setting up textures, assigning them to buttons and assigning listeners to buttons.
If first sets up the texture, texture region and texture drawable:
Group buttons = new Group();
//loads in transparent image for button texture, making button transparent
Texture btnTexture = new Texture(Gdx.files.internal
("images/crafting_assets_sprint2/transparent-texture-buttonClick.png"));
// Texture Region of a given texture
TextureRegion buttonTextureRegion = new TextureRegion(btnTexture);
// Texture drawable of a given texture region
TextureRegionDrawable buttonDrawable = new TextureRegionDrawable(buttonTextureRegion);
Then gets the correct dimensions of the button based on the level/screen it should be showing (level 3 always refers to the win screen). PlayAgain / continue is an example of this logic and dimensions set. Note that getLevel()
is just calling a global variable, it's a more reader-friendly version.
//playAgain
if (getLevel() == 3) {
playAgainBtn.setOrigin(playAgainBtn.getWidth()/3, playAgainBtn.getHeight()/3);
playAgainBtn.setPosition(908, 370);
playAgainBtn.setSize(88, 80);
} else {
playAgainBtn.setOrigin(playAgainBtn.getWidth()/3, playAgainBtn.getHeight()/3);
playAgainBtn.setWidth(340);
playAgainBtn.setHeight(270);
playAgainBtn.setPosition(1565,0);
}
Then the listener is applied, the button is added to the group, the group is staged, and the stage is drawn. This repeats for exit button.
// play again/ restart level 1 listner
playAgainBtn.addListener(
new ChangeListener() {
@Override
public void changed(ChangeEvent changeEvent, Actor actor) {
logger.info("The play again button was clicked");
entity.getEvents().trigger("continueGame");
}
});
buttons.addActor(playAgainBtn);
stage.addActor(buttons);
stage.draw();
Making it Work with GdxGame & MainGameScreen
To make this actually display in the game and logic statement was added to MainGameScreen
's render() method:
if (dead) {
// Could add further player cleanup functionality here
player.getComponent(PlayerActions.class).stopWalking();
if (gameLevel == 1) {
game.setScreen(GdxGame.ScreenType.DEATH_SCREEN_L1);
} else if (gameLevel == 2) {
game.setScreen(GdxGame.ScreenType.DEATH_SCREEN_L2);
}
}
Hence when the game first starts 'dead' is false since the player is alive, there is an event listener added to the player listening for a death, once they die dead
is true, and the above logic is set in place. The enum's DEATH_SCREEN_L1
and DEATH_SCREEN_L2
come from GdxGame
in which there is a switch statement under newScreen that returns a new DeathScreen
for the associated enum, if it's DEATH_SCREEN_L1
then DeathScreen
is given 1 as it's level
and it displays level 1, same for the relevant level 2 processes for DEATH_SCREEN_L2
.
Win Screen
Due to the similarities in death screen and win screen functionality, it was decided to include win screen as apart of death screen, it works as an additional screen under death screen, with the main difference being how it is triggered.
In KeyboardPlayerInputComponent.java
under keyDown()
there is a case that triggers a win event when the button is clicked. In sprint 4 this will be changed to be under Enter
when the character has killed the level 2 boss and entered the level 2 plug, but due to level 2's lack of enemies and delays on plug location this was a quick fix.
case Keys.N:
entity.getEvents().trigger("win");
return true;
Once the event is triggered, like death screen, it triggers player.getEvents().addListener("win", this::winScreenStart);
in MainGameScreen.java
, making the win state true:
/**
- Sets win to true */ public void winScreenStart() { win = true;}
and thus making this logic in render()
true, changing the screen type through GdxGame, and repeating the Death Screen steps.
if (win) {
logger.info("Win screen screen type set");
player.getComponent(PlayerActions.class).stopWalking();
game.setScreen(GdxGame.ScreenType.WIN_SCREEN);
}
UML and Class Structure of Death Screen
Sequence Diagram of DeathScreen:
Below visually represent how the sequence of events for DeathScreen works, this process is very similar for level 2 and win state, only difference being what is displayed and the positioning of the buttons on the screen.
Level 1 Player Die:
This sequence diagram shows the sequence of events when the player dies and DeathScreen is displayed for level 1 when the character dies:
Level 1 Death Screen --> Exit btn pressed
The sequence diagram shows when the exit button is pressed and triggers "exit" event of DeathScreenActions, building on from the previous sequence diagram: