Menu and a second level
The menu
The project's first scene, Main.scene, becomes the menu. The menu's UI is Menu.slxml, a title and a Play button.
- Create the file
Assets/UI/Menu.slxmlwith this text, the way you created the HUD:
<?xml version="1.0" encoding="UTF-8"?>
<ui version="1">
<panel id="menu" style="position: absolute; left: 0; top: 0; width: 100%; height: 100%; flex-direction: column; align-items: center; justify-content: center">
<panel id="title" style="margin-bottom: 32px">Coin Collector</panel>
<!-- MainMenu finds this button by its id and loads the first level when it is clicked -->
<button id="play" style="width: 200px" text="Play"/>
</panel>
</ui>- Create a component named
MainMenuand replace its two files:
#pragma once
#include "Shalltear.h"
// the menu's Play button. It sits below the UIPanelRenderer that draws the
// menu, on the same object, so the button exists when OnEnable looks for it
class MainMenu : public Behavior {
public:
// None: the menu has no per-frame code, only OnEnable and OnDisable
MainMenu() : Behavior(BehaviorTick::None) {}
SLT_REFLECT_COMPONENT(MainMenu, Behavior)
SLT_REFLECT_FIELD(firstLevel)
SLT_END_REFLECT()
// the scene Play loads
Handle<SceneAsset> firstLevel;
protected:
void OnEnable() override;
void OnDisable() override;
};#include "MainMenu.h"
#include "Shalltear/Core/UI/Context.h"
#include "Shalltear/Core/UI/UIPanelRenderer.h"
SLT_REGISTER_TYPE(MainMenu)
// runs when the game starts, not while you edit the scene: a component's hooks
// run only while the game plays. It wires the Play button, found by the id it
// has in Menu.slxml, the same way LevelController wires the pause menu
void MainMenu::OnEnable() {
UIPanelRenderer* menu = GetOwner()->GetComponent<UIPanelRenderer>().Get();
if (!menu || !menu->GetContext()) {
SLT_LOG_WARN("CoinCollector", "MainMenu needs a UIPanelRenderer above it on the same object");
return;
}
Handle<MainMenu> self(this);
menu->GetContext()->Events(menu->FindElement("play")).onClick = [self] {
MainMenu* mainMenu = self.Get();
if (SceneAsset* level = mainMenu ? mainMenu->firstLevel.Get() : nullptr) {
SceneSystem::ReplaceScenes(level->assetId);
}
};
}
void MainMenu::OnDisable() {
UIPanelRenderer* menu = GetOwner()->GetComponent<UIPanelRenderer>().Get();
if (menu && menu->GetContext()) {
menu->GetContext()->Events(menu->FindElement("play")).onClick = nullptr;
}
}- Save both files. Save the level too, then double-click
Main.sceneinAssets/Scenesto open it. - Create an empty object named
Menu. Add a UIPanelRenderer withMenu.slxmlas its document andGame.slpanelas its panelSettings. - Add MainMenu below it, and drag
Level1.sceneonto its firstLevel field. - Save the scene.
Press Ctrl+P, then Play, and the first level starts. When it ends, you are back at the menu.
A second level
- Double-click
Level1.sceneto open it, and select File ▸ Save Scene As.... Change the path toproject://Assets/Scenes/Level2.sceneand select OK. You are now editing the copy. - Select a coin and press Ctrl+D twice, so there are seven, and move them:
| Coin | Position |
|---|---|
| 1 | -7, 0.75, 7 |
| 2 | 0, 0.75, 8 |
| 3 | 7, 0.75, 7 |
| 4 | -8, 0.75, 0 |
| 5 | 8, 0.75, 0 |
| 6 | -7, 0.75, -7 |
| 7 | 7, 0.75, -7 |
- Create a Level Definition named
Level2.level, with timeLimit25and coinsToWin7, and select Save. Leave its nextLevel empty: winning the last level returns to the menu. - Select the
Levelobject and dragLevel2.levelonto its level field. Save the scene. - Select
Level1.level, dragLevel2.sceneonto its nextLevel field and select Save.
Open Main.scene and press Ctrl+P. Play starts the first level, collecting its coins starts the second, and finishing the second returns to the menu.
Next, build the game.