From 4c563f6f74490374b3bad24e0aea6ac7a659194e Mon Sep 17 00:00:00 2001 From: fallahn Date: Fri, 25 Oct 2024 13:58:49 +0100 Subject: [PATCH] update attract state --- samples/golf/src/scrub/ScrubAttractState.cpp | 228 +++++++++++++++++-- samples/golf/src/scrub/ScrubAttractState.hpp | 7 + samples/golf/src/scrub/ScrubGameState.cpp | 6 +- 3 files changed, 218 insertions(+), 23 deletions(-) diff --git a/samples/golf/src/scrub/ScrubAttractState.cpp b/samples/golf/src/scrub/ScrubAttractState.cpp index f6d422299..2fd457c63 100644 --- a/samples/golf/src/scrub/ScrubAttractState.cpp +++ b/samples/golf/src/scrub/ScrubAttractState.cpp @@ -40,21 +40,28 @@ source distribution. #include #include #include +#include #include #include #include #include #include +#include #include #include +#include #include #include #include +#include namespace { + float tabScrollTime = 0.f; + constexpr float TabDisplayTime = 6.f; + struct TabData final { enum @@ -155,9 +162,11 @@ ScrubAttractState::ScrubAttractState(cro::StateStack& ss, cro::State::Context ct m_sharedData (sd), m_sharedScrubData (sc), m_uiScene (ctx.appInstance.getMessageBus()), + m_gameScene (ctx.appInstance.getMessageBus()), m_currentTab (0) { addSystems(); + loadAssets(); buildScene(); } @@ -200,25 +209,38 @@ bool ScrubAttractState::handleEvent(const cro::Event& evt) } + m_gameScene.forwardEvent(evt); m_uiScene.forwardEvent(evt); return false; } void ScrubAttractState::handleMessage(const cro::Message& msg) { + m_gameScene.forwardMessage(msg); m_uiScene.forwardMessage(msg); } bool ScrubAttractState::simulate(float dt) { - //TODO automatically scroll through tabs + //automatically scroll through tabs + tabScrollTime += dt; + if (tabScrollTime > TabDisplayTime) + { + tabScrollTime -= TabDisplayTime; + nextTab(); + } + m_gameScene.simulate(dt); m_uiScene.simulate(dt); return true; } void ScrubAttractState::render() { + m_scrubTexture.clear(cro::Colour::Transparent); + m_gameScene.render(); + m_scrubTexture.display(); + m_uiScene.render(); } @@ -226,9 +248,15 @@ void ScrubAttractState::render() void ScrubAttractState::addSystems() { auto& mb = cro::App::getInstance().getMessageBus(); + m_gameScene.addSystem(mb); + m_gameScene.addSystem(mb); + m_gameScene.addSystem(mb); + + m_uiScene.addSystem(mb); m_uiScene.addSystem(mb); m_uiScene.addSystem(mb); + m_uiScene.addSystem(mb); m_uiScene.addSystem(mb); m_uiScene.addSystem(mb); m_uiScene.addSystem(mb); @@ -236,11 +264,16 @@ void ScrubAttractState::addSystems() void ScrubAttractState::loadAssets() { + m_environmentMap.loadFromFile("assets/images/hills.hdr"); + m_scrubTexture.create(1500, 1500); + //TODO load menu music } void ScrubAttractState::buildScene() { + buildScrubScene(); //loads the 3D model + const auto& largeFont = m_sharedScrubData.fonts->get(sc::FontID::Title); const auto& smallFont = m_sharedScrubData.fonts->get(sc::FontID::Body); @@ -277,7 +310,7 @@ void ScrubAttractState::buildScene() entity.getComponent().setShadowOffset(sc::LargeTextOffset); entity.addComponent().ID = CommandID::UI::UIElement; entity.addComponent().relativePosition = glm::vec2(0.5f); - entity.getComponent().absolutePosition = { 0.f, 60.f }; + entity.getComponent().absolutePosition = { -60.f, 160.f }; entity.getComponent().characterSize = sc::LargeTextSize; entity.getComponent().depth = sc::TextDepth; @@ -346,8 +379,41 @@ void ScrubAttractState::buildScene() auto bgEnt = entity; titleData.spriteNode.getComponent().addChild(entity.getComponent()); + + entity = m_uiScene.createEntity(); + entity.addComponent().setPosition(glm::vec3(size / 2.f, 0.f)); + entity.getComponent().setScale(glm::vec2(0.f)); + entity.addComponent(); + entity.addComponent(m_scrubTexture.getTexture()); + auto bounds = entity.getComponent().getTextureBounds(); + entity.getComponent().setOrigin({ bounds.width / 2.f, bounds.height / 2.f }); + entity.addComponent().ID = CommandID::UI::UIElement; + entity.addComponent().relativePosition = glm::vec2(0.5f); + entity.getComponent().depth = 0.f; + entity.addComponent().active = true; + entity.getComponent().setUserData(0.f); + entity.getComponent().function = + [](cro::Entity e, float dt) + { + auto& progress = e.getComponent().getUserData(); + progress = std::min(1.f, progress + dt); + + const auto scale = cro::Util::Easing::easeInCubic(progress); + e.getComponent().setScale(glm::vec2(scale)); + + if (progress == 1) + { + progress = 0.f; + e.getComponent().active = false; + } + }; + + auto scrubEnt = entity; + titleData.spriteNode.getComponent().addChild(entity.getComponent()); + + titleData.onStartIn = - [textEnt, bgEnt](cro::Entity) mutable + [textEnt, bgEnt, scrubEnt](cro::Entity) mutable { textEnt.getComponent().setScale(glm::vec2(0.f)); textEnt.getComponent().setRotation(0.f); @@ -355,12 +421,16 @@ void ScrubAttractState::buildScene() bgEnt.getComponent().setScale(glm::vec2(0.f)); bgEnt.getComponent().getUserData() = 0.f; + + scrubEnt.getComponent().setScale(glm::vec2(0.f)); + scrubEnt.getComponent().getUserData() = 0.f; }; titleData.onEndIn = - [textEnt, bgEnt](cro::Entity) mutable //to play anim + [textEnt, bgEnt, scrubEnt](cro::Entity) mutable //to play anim { textEnt.getComponent().active = true; bgEnt.getComponent().active = true; + scrubEnt.getComponent().active = true; }; //TODO titleData.onStartOut //to play exit anim @@ -377,35 +447,57 @@ void ScrubAttractState::buildScene() howToData.spriteNode.addComponent(); m_tabs[TabID::HowTo].getComponent().addChild(howToData.spriteNode.getComponent()); - const std::string str = + static const std::string controllerStr = R"( -Use A/D to scrub or right thumb stick -Use Q to insert and E to remove a ball -Press SPACE or Controller A to add more soap +Use right thumb stick to scrub +Use LB to insert and RB to remove a ball +Press A to add more soap -Press ESCAPE or Start to Pause the game. +Press Start to Pause the game. +)"; + static const std::string keyboardStr = + R"( +Use A/D to scrub +Use Q to insert and E to remove a ball +Press SPACE to add more soap -Press SPACE or Controller A to begin. +Press ESCAPE to Pause the game. )"; - entity = m_uiScene.createEntity(); entity.addComponent().setPosition(glm::vec3(size / 2.f, sc::TextDepth)); entity.addComponent(); - entity.addComponent(smallFont).setString(str); - entity.getComponent().setCharacterSize(sc::MediumTextSize * getViewScale()); + entity.addComponent(smallFont).setCharacterSize(sc::MediumTextSize * getViewScale()); entity.getComponent().setAlignment(cro::Text::Alignment::Centre); entity.getComponent().setFillColour(TextNormalColour); entity.getComponent().setShadowColour(LeaderboardTextDark); entity.getComponent().setShadowOffset(sc::MediumTextOffset); - auto bounds = cro::Text::getLocalBounds(entity); + entity.getComponent().setString(controllerStr); //set this once so we can approximate the local bounsd + bounds = cro::Text::getLocalBounds(entity); entity.addComponent().ID = CommandID::UI::UIElement; entity.addComponent().relativePosition = glm::vec2(0.5f); - entity.getComponent().absolutePosition = { 0.f, std::floor(bounds.height / 2.f) }; + entity.getComponent().absolutePosition = { 0.f, std::floor(bounds.height / 2.f) + 40.f }; entity.getComponent().characterSize = sc::MediumTextSize; entity.getComponent().depth = sc::TextDepth; + entity.addComponent().active = true; + entity.getComponent().function = + [](cro::Entity e, float) + { + if (cro::GameController::getControllerCount() + || Social::isSteamdeck()) + { + //TODO use controller appropriate icons + e.getComponent().setString(controllerStr); + } + else + { + //TODO read keybinds and update string as necessary + e.getComponent().setString(keyboardStr); + } + }; + m_tabs[TabID::HowTo].getComponent().addChild(entity.getComponent()); @@ -423,9 +515,8 @@ Press SPACE or Controller A to begin. entity = m_uiScene.createEntity(); entity.addComponent().setPosition(glm::vec3(size / 2.f, sc::TextDepth)); - entity.getComponent().move({ 0.f, 60.f * getViewScale() }); - entity.addComponent(); - entity.addComponent(smallFont).setString("flaps"); + entity.addComponent(); + entity.addComponent(smallFont).setString("High Scores"); entity.getComponent().setCharacterSize(sc::MediumTextSize * getViewScale()); entity.getComponent().setAlignment(cro::Text::Alignment::Centre); entity.getComponent().setFillColour(TextNormalColour); @@ -443,8 +534,51 @@ Press SPACE or Controller A to begin. + //start message + entity = m_uiScene.createEntity(); + entity.addComponent().setPosition(glm::vec3(size / 2.f, sc::TextDepth)); + entity.addComponent(); + entity.addComponent(smallFont).setString("Press Space To Start"); + entity.getComponent().setCharacterSize(sc::MediumTextSize* getViewScale()); + entity.getComponent().setAlignment(cro::Text::Alignment::Centre); + entity.getComponent().setFillColour(TextNormalColour); + entity.getComponent().setShadowColour(LeaderboardTextDark); + entity.getComponent().setShadowOffset(sc::MediumTextOffset); + entity.addComponent().ID = CommandID::UI::UIElement; + entity.addComponent().relativePosition = glm::vec2(0.5f, 0.f); + entity.getComponent().absolutePosition = { 0.f, 50.f }; + entity.getComponent().characterSize = sc::MediumTextSize; + entity.getComponent().depth = sc::TextDepth; + entity.addComponent().active = true; + entity.getComponent().setUserData(0.f); + entity.getComponent().function = + [](cro::Entity e, float dt) + { + auto& t = e.getComponent().getUserData(); + t += dt; + auto f = static_cast(std::floor(t)); + if (f % 2 == 0) + { + e.getComponent().setFillColour(cro::Colour::Transparent); + e.getComponent().setShadowColour(cro::Colour::Transparent); + } + else + { + e.getComponent().setFillColour(TextNormalColour); + e.getComponent().setShadowColour(LeaderboardTextDark); + } + if (cro::GameController::getControllerCount() + || Social::isSteamdeck()) + { + e.getComponent().setString("Press A To Start"); + } + else + { + e.getComponent().setString("Press SPACE To Start"); + } + }; @@ -496,6 +630,55 @@ Press SPACE or Controller A to begin. resize(cam); } +void ScrubAttractState::buildScrubScene() +{ + cro::ModelDefinition md(m_resources, &m_environmentMap); + if (md.loadFromFile("assets/arcade/scrub/models/body.cmt")) + { + auto entity = m_gameScene.createEntity(); + entity.addComponent().setPosition({ 0.f, 0.08f, 0.f }); + md.createModel(entity); + + if (md.loadFromFile("assets/arcade/scrub/models/handle.cmt")) + { + auto handleEnt = m_gameScene.createEntity(); + handleEnt.addComponent().setPosition({ 0.f, -0.1f, 0.f }); + md.createModel(handleEnt); + entity.getComponent().addChild(handleEnt.getComponent()); + } + + entity.addComponent().active = true; + entity.getComponent().function = + [](cro::Entity e, float) + { + static const auto wavetable = cro::Util::Wavetable::sine(0.3f); + static std::size_t idx = 0; + static constexpr float Rotation = cro::Util::Const::PI / 8.f; + + e.getComponent().setRotation(cro::Transform::Y_AXIS, Rotation * wavetable[idx]); + + idx = (idx + 1) % wavetable.size(); + }; + } + + auto resize = [](cro::Camera& cam) + { + glm::vec2 size(cro::App::getWindow().getSize()); + cam.viewport = { 0.f, 0.f, 1.f, 1.f }; + cam.setPerspective(65.f * cro::Util::Const::degToRad, 1.f, 0.1f, 10.f); + }; + + auto camera = m_gameScene.getActiveCamera(); + auto& cam = camera.getComponent(); + cam.resizeCallback = resize; + resize(cam); + + camera.getComponent().setLocalTransform(glm::inverse(glm::lookAt(glm::vec3(-0.04f, 0.07f, 0.36f), glm::vec3(0.f, -0.04f, 0.f), cro::Transform::Y_AXIS))); + + m_gameScene.getSunlight().getComponent().rotate(cro::Transform::X_AXIS, -1.2f); + m_gameScene.getSunlight().getComponent().rotate(cro::Transform::Y_AXIS, -0.6f); +} + void ScrubAttractState::prevTab() { m_tabs[m_currentTab].getComponent().getUserData().hide(m_tabs[m_currentTab]); @@ -512,6 +695,11 @@ void ScrubAttractState::nextTab() void ScrubAttractState::onCachedPush() { - //TODO reset to default tab - //TODO reset tab change timer + //reset to default tab + prevTab(); //this just tidies up existing tab before forcing the index below + m_currentTab = m_tabs.size() - 1; + nextTab(); + + //reset tab change timer + tabScrollTime = 0.f; } \ No newline at end of file diff --git a/samples/golf/src/scrub/ScrubAttractState.hpp b/samples/golf/src/scrub/ScrubAttractState.hpp index e04664879..25f9a9f5f 100644 --- a/samples/golf/src/scrub/ScrubAttractState.hpp +++ b/samples/golf/src/scrub/ScrubAttractState.hpp @@ -35,6 +35,8 @@ source distribution. #include #include +#include +#include #include @@ -56,8 +58,12 @@ class ScrubAttractState final : public cro::State SharedStateData& m_sharedData; SharedScrubData& m_sharedScrubData; cro::Scene m_uiScene; + cro::Scene m_gameScene; cro::ResourceCollection m_resources; + cro::EnvironmentMap m_environmentMap; + cro::RenderTexture m_scrubTexture; + struct TabID final { enum @@ -72,6 +78,7 @@ class ScrubAttractState final : public cro::State void addSystems(); void loadAssets(); void buildScene(); + void buildScrubScene(); void nextTab(); void prevTab(); diff --git a/samples/golf/src/scrub/ScrubGameState.cpp b/samples/golf/src/scrub/ScrubGameState.cpp index a9d11b60a..e85ef8094 100644 --- a/samples/golf/src/scrub/ScrubGameState.cpp +++ b/samples/golf/src/scrub/ScrubGameState.cpp @@ -713,7 +713,7 @@ void ScrubGameState::createScene() { glm::vec2 size(cro::App::getWindow().getSize()); cam.viewport = { 0.f, 0.f, 1.f, 1.f }; - cam.setPerspective(70.f * cro::Util::Const::degToRad, size.x / size.y, 0.1f, 10.f); + cam.setPerspective(74.f * cro::Util::Const::degToRad, size.x / size.y, 0.1f, 10.f); }; auto camera = m_gameScene.getActiveCamera(); @@ -993,7 +993,7 @@ void ScrubGameState::createUI() entity.getComponent().setBlendMode(cro::Material::BlendMode::Additive); entity.addComponent().ID = CommandID::UI::UIElement; entity.addComponent().relativePosition = glm::vec2(0.5f, 1.f); - entity.getComponent().absolutePosition = { -BarHeight / 2.f, -(BarWidth + 12.f) }; + entity.getComponent().absolutePosition = { -BarHeight / 2.f, -((BarWidth / 2.f) + 52.f) }; entity.addComponent().active = true; entity.getComponent().setUserData(0.f); entity.getComponent().function = @@ -1490,7 +1490,7 @@ void ScrubGameState::updateScore() - if (((m_score.ballsWashed - m_score.countAtThreshold) % (m_score.bonusRun > Score::bonusRunThreshold ? 3 : 5)) == 0) + if (((m_score.ballsWashed - m_score.countAtThreshold) % (m_score.bonusRun > Score::bonusRunThreshold ? 4 : 5)) == 0) { //new soap in 3.. 2.. 1.. const auto& font = m_sharedScrubData.fonts->get(sc::FontID::Body);