From 06fc93a2c4b03150abf569de5c6d991ef8b35f46 Mon Sep 17 00:00:00 2001 From: falsycat Date: Sun, 13 Oct 2019 00:00:00 +0000 Subject: [PATCH] [update] Implemented animations for scene transition. --- src/sj/SelectScene.d | 133 ++++++++++++++++++++++++++++++++++++++-- src/sj/TitleScene.d | 65 +++++++++++++++----- src/sj/util/Animation.d | 30 +++++++++ src/sj/util/Easing.d | 37 +++++++++++ 4 files changed, 243 insertions(+), 22 deletions(-) create mode 100644 src/sj/util/Animation.d create mode 100644 src/sj/util/Easing.d diff --git a/src/sj/SelectScene.d b/src/sj/SelectScene.d index 9318fc5..08890ad 100644 --- a/src/sj/SelectScene.d +++ b/src/sj/SelectScene.d @@ -1,13 +1,20 @@ /// License: MIT module sj.SelectScene; +import std.math, + std.variant; + import derelict.sfml2.audio; +import gl4d; + import sj.KeyInput, sj.LobbyWorld, sj.ProgramSet, sj.SceneInterface, sj.TitleScene, + sj.util.Animation, + sj.util.Easing, sj.util.audio; /// @@ -20,6 +27,9 @@ class SelectScene : SceneInterface { sound_ = sfSound_create(); soundres_.Load(); + + first_state_ = new FirstSetupState(this); + status_ = first_state_; } ~this() { sfSound_destroy(sound_); @@ -33,13 +43,19 @@ class SelectScene : SceneInterface { /// void Initialize() { + first_state_.Initialize(); + status_ = first_state_; } override SceneInterface Update(KeyInput input) { - if (input.up) { - title_scene_.Initialize(); - return title_scene_; - } - return this; + SceneInterface next_scene = this; + AbstractSceneState next_state; + + status_.Update(input).visit!( + (SceneInterface scene) { next_scene = scene; }, + (AbstractSceneState state) { next_state = state; } + ); + status_ = next_state; + return next_scene; } override void Draw() { lobby_.Draw(); @@ -67,4 +83,111 @@ class SelectScene : SceneInterface { sfSound* sound_; SoundResources soundres_; + + FirstSetupState first_state_; + + AbstractSceneState status_; +} + +private abstract class AbstractSceneState { + public: + alias UpdateResult = Algebraic!(AbstractSceneState, SceneInterface); + + this(SelectScene owner) { + owner_ = owner; + } + + abstract UpdateResult Update(KeyInput input); + + @property SelectScene owner() { + return owner_; + } + + protected: + static UpdateResult CreateResult(SceneInterface s) { + return UpdateResult(s); + } + static UpdateResult CreateResult(AbstractSceneState s) { + return UpdateResult(s); + } + private: + SelectScene owner_; +} + +private class FirstSetupState : AbstractSceneState { + public: + this(SelectScene owner) { + super(owner); + stage_appear_state_ = new StageAppearState(owner); + } + + enum AnimeFrames = 30; + enum BgInnerColor = vec4(0.8, 0.6, 0.6, 1); + enum BgOuterColor = vec4(-0.4, -0.4, -0.4, 1); + + enum CubeRotationSpeed = vec3(0, PI/10, 0); + enum CubeInterval = 0.06; + + void Initialize() { + anime_ = Animation(AnimeFrames); + bg_inner_ease_ = Easing!vec4(owner.lobby_.background.inner_color, BgInnerColor); + bg_outer_ease_ = Easing!vec4(owner.lobby_.background.outer_color, BgOuterColor); + + cube_interval_ease_ = Easing!float(owner.lobby_.cube_interval, CubeInterval); + } + override UpdateResult Update(KeyInput input) { + const ratio = anime_.Update(); + + owner.lobby_.cube_matrix.rotation += CubeRotationSpeed * (ratio+0.2); + owner.lobby_.cube_interval = cube_interval_ease_.Calculate(ratio); + + owner.lobby_.background.inner_color = bg_inner_ease_.Calculate(ratio); + owner.lobby_.background.outer_color = bg_outer_ease_.Calculate(ratio); + + if (anime_.isFinished) { + stage_appear_state_.Initialize(); + return CreateResult(stage_appear_state_); + } + return CreateResult(this); + } + + private: + StageAppearState stage_appear_state_; + + Animation anime_; + + Easing!vec4 bg_inner_ease_; + Easing!vec4 bg_outer_ease_; + + Easing!float cube_interval_ease_; +} +private class StageAppearState : AbstractSceneState { + public: + this(SelectScene owner) { + super(owner); + } + + enum AnimeFrames = 30; + enum CubeRotationSpeed = vec3(0, PI/500, 0); + + void Initialize() { // TODO: pass a stage data + anime_ = Animation(AnimeFrames); + cube_interval_ease_ = Easing!float(owner.lobby_.cube_interval, 0.005); + + sfSound_setBuffer(owner.sound_, owner.soundres_.spotlight); + sfSound_play(owner.sound_); + } + override UpdateResult Update(KeyInput input) { + const ratio = anime_.Update(); + + owner.lobby_.cube_matrix.rotation += CubeRotationSpeed + + (FirstSetupState.CubeRotationSpeed - CubeRotationSpeed) * (1-ratio); + owner.lobby_.cube_interval = cube_interval_ease_.Calculate(ratio); + return CreateResult(this); + } + + private: + Animation anime_; + + Easing!float cube_interval_ease_; } diff --git a/src/sj/TitleScene.d b/src/sj/TitleScene.d index 114ae0f..27cb6cf 100644 --- a/src/sj/TitleScene.d +++ b/src/sj/TitleScene.d @@ -1,7 +1,8 @@ /// License: MIT module sj.TitleScene; -import std.math; +import std.conv, + std.math; import gl4d; @@ -10,7 +11,9 @@ import sj.KeyInput, sj.ProgramSet, sj.SelectScene, sj.SceneInterface, - sj.TitleTextProgram; + sj.TitleTextProgram, + sj.util.Animation, + sj.util.Easing; /// class TitleScene : SceneInterface { @@ -23,10 +26,35 @@ class TitleScene : SceneInterface { return m; }(); + /// + enum AnimationFrame = 30; + /// + enum BgInnerColor = vec4(0.9, 0.9, 0.9, 1); + /// + enum BgOuterColor = vec4(-0.1, -0.1, -0.1, 1); + /// + enum CubeInterval = 0.005; + /// this(LobbyWorld lobby, ProgramSet program) { lobby_ = lobby; title_ = program.Get!TitleTextProgram; + + lobby_.view.pos = vec3(0, -0.15, -1); + lobby_.view.target = vec3(0, -0.15, 0); + lobby_.view.up = vec3(0, 1, 0); + + lobby_.background.inner_color = BgInnerColor; + lobby_.background.outer_color = BgOuterColor; + + lobby_.light_pos = vec3(0, 9, -1); + lobby_.cube_material.diffuse_color = vec3(0.1, 0.1, 0.1); + lobby_.cube_material.light_color = vec3(1, 0.8, 0.8); + lobby_.cube_material.light_power = vec3(100, 100, 100); + lobby_.cube_material.ambient_color = vec3(0.2, 0.2, 0.2); + lobby_.cube_material.specular_color = vec3(0.5, 0.2, 0.2); + + lobby_.cube_interval = CubeInterval; } /// @@ -36,25 +64,23 @@ class TitleScene : SceneInterface { /// void Initialize() { - lobby_.view.pos = vec3(0, -0.15, -1); - lobby_.view.target = vec3(0, -0.15, 0); - lobby_.view.up = vec3(0, 1, 0); + anime_ = Animation(AnimationFrame); - lobby_.background.inner_color = vec4(0.9, 0.9, 0.9, 1); - lobby_.background.outer_color = vec4(-0.1, -0.1, -0.1, 1); + bg_inner_ease_ = Easing!vec4(lobby_.background.inner_color, BgInnerColor); + bg_outer_ease_ = Easing!vec4(lobby_.background.outer_color, BgOuterColor); - lobby_.light_pos = vec3(0, 9, -1); - lobby_.cube_material.diffuse_color = vec3(0.1, 0.1, 0.1); - lobby_.cube_material.light_color = vec3(1, 0.8, 0.8); - lobby_.cube_material.light_power = vec3(100, 100, 100); - lobby_.cube_material.ambient_color = vec3(0.2, 0.2, 0.2); - lobby_.cube_material.specular_color = vec3(0.5, 0.2, 0.2); - - frame_ = 0; + cube_interval_ease_ = Easing!float(lobby_.cube_interval, CubeInterval); } override SceneInterface Update(KeyInput input) { + const ratio = anime_.Update(); + lobby_.cube_matrix.rotation += vec3(PI/600, PI/600, PI/600); + lobby_.background.inner_color = bg_inner_ease_.Calculate(ratio); + lobby_.background.outer_color = bg_outer_ease_.Calculate(ratio); + + lobby_.cube_interval = cube_interval_ease_.Calculate(ratio); + if (input.down) { select_scene_.Initialize(); return select_scene_; @@ -63,7 +89,8 @@ class TitleScene : SceneInterface { } override void Draw() { lobby_.Draw(); - title_.Draw(lobby_.Projection, lobby_.view.Create(), TitleMatrix, frame_++); + title_.Draw(lobby_.Projection, lobby_.view.Create(), TitleMatrix, + (anime_.frame%int.max).to!int); } private: @@ -73,5 +100,9 @@ class TitleScene : SceneInterface { TitleTextProgram title_; - int frame_; + Animation anime_; + + Easing!vec4 bg_inner_ease_; + Easing!vec4 bg_outer_ease_; + Easing!float cube_interval_ease_; } diff --git a/src/sj/util/Animation.d b/src/sj/util/Animation.d new file mode 100644 index 0000000..b777b43 --- /dev/null +++ b/src/sj/util/Animation.d @@ -0,0 +1,30 @@ +/// License: MIT +module sj.util.Animation; + +import std.algorithm; + +/// +struct Animation { + public: + /// + this(size_t frame_count) { + frame_count_ = frame_count; + } + /// + float Update() { + return (frame_++ * 1f / frame_count_).clamp(0f, 1f); + } + /// + @property bool isFinished() const { + return frame_ >= frame_count_; + } + /// + @property size_t frame() const { + return frame_; + } + + private: + size_t frame_count_; + + size_t frame_; +} diff --git a/src/sj/util/Easing.d b/src/sj/util/Easing.d new file mode 100644 index 0000000..ce75137 --- /dev/null +++ b/src/sj/util/Easing.d @@ -0,0 +1,37 @@ +/// License: MIT +module sj.util.Easing; + +/// +enum EasingType { + Linear, + LinearMountain, +} + +/// +struct Easing(T) { + public: + /// + this(T st, T ed, EasingType type = EasingType.Linear) { + st_ = st; + ed_ = ed; + type_ = type; + } + + /// + T Calculate(float t) { + final switch (type_) with (EasingType) { + case LinearMountain: + t = t*2; + t = t > 1? 2-t: t; + goto case; + case Linear: + return (ed_ - st_) * t + st_; + } + } + + private: + T st_; + T ed_; + + EasingType type_; +}