diff --git a/include/hsm.h b/include/hsm.h index 30d0527..57958d5 100644 --- a/include/hsm.h +++ b/include/hsm.h @@ -11,6 +11,8 @@ // Required includes #include +#include +#include #pragma once #ifndef __HSM_H__ @@ -189,156 +191,6 @@ StateTypeId GetStateType() #pragma endregion "RTTI" #endif -#ifdef _MSC_VER -#pragma region "Utils" -#endif -/////////////////////////////////////////////////////////////////////////////////////////////////// -// Utils -/////////////////////////////////////////////////////////////////////////////////////////////////// - -namespace hsm { -namespace util { - -// IntrusivePtr is a ref-counting smart pointer that deletes the pointed-to object when its -// ref count reaches 0. The pointed-to object is expected to implement AddRef/RemoveRef -// (see IntrusivePtrClient below for a default implementation that can be used). -// @NOTE: object is required to be allocated using HSM_NEW. -template -class IntrusivePtr -{ -public: - typedef IntrusivePtr ThisType; - - IntrusivePtr(T* object = 0) : mObject(object) - { - InvokeAddRef(); - } - - ~IntrusivePtr() - { - InvokeRemoveRef(); - } - - IntrusivePtr(const ThisType& rhs) : mObject(rhs.Get()) - { - InvokeAddRef(); - } - - template - IntrusivePtr(const IntrusivePtr& rhs) : mObject(rhs.Get()) - { - InvokeAddRef(); - } - - ThisType& operator=(const ThisType& rhs) - { - // This neat "swap" trick uses the constructor/destructor to do the work. - ThisType(rhs).swap(*this); - return *this; - } - - template - ThisType& operator=(const IntrusivePtr& rhs) - { - ThisType(rhs).swap(*this); - return *this; - } - - // Since the object holds its own ref count, we can safely assign an object - // directly to an IntrusivePtr, even if another IntrusivePtr already points - // to it. Note that this is not possible with shared_ptr, so this assignment - // operator is not made available for shared_ptr. - ThisType& operator=(T* object) - { - Reset(object); - return *this; - } - - void Reset(T* object = 0) - { - ThisType(object).swap(*this); - } - - T* Get() const { return mObject; } - - T* operator->() - { - return mObject; - } - - const T* operator->() const - { - return mObject; - } - - T& operator*() - { - return *mObject; - } - - const T& operator*() const - { - return *mObject; - } - - void swap(ThisType& rhs) - { - T* lhsObject = mObject; - mObject = rhs.mObject; - rhs.mObject = lhsObject; - } - -private: - void InvokeAddRef() - { - if (mObject) - { - mObject->AddRef(); - } - } - - void InvokeRemoveRef() - { - if (mObject && mObject->RemoveRef() == 0) - { - HSM_DELETE(mObject); - mObject = 0; - } - } - - T* mObject; -}; - -// Optional class that T can derive from to add required functionality for IntrusivePtr -class IntrusivePtrClient -{ -public: - IntrusivePtrClient() : mRefCount(0) {} - virtual ~IntrusivePtrClient() {} - - void AddRef() const - { - ++mRefCount; - } - - int RemoveRef() const - { - HSM_ASSERT(mRefCount > 0); - --mRefCount; - return mRefCount; - } - -private: - mutable int mRefCount; -}; - -} // namespace util -} // namespace hsm - -#ifdef _MSC_VER -#pragma endregion "Utils" -#endif - #ifdef _MSC_VER #pragma region "Transition" #endif @@ -351,13 +203,6 @@ namespace hsm { struct State; struct StateFactory; -// StateArgs: For states that wish to receive arguments via OnEnter, implement an inner struct named 'Args' that derives -// from StateArgs, and implement State::OnEnter(const Args& args). -struct StateArgs : util::IntrusivePtrClient -{ - virtual ~StateArgs() {} // Make sure destructors get called in derived types -}; - // Returns the one StateFactory instance for the input state. Note that this type can be used to effectively store // a state in a variable at runtime, which can subsequently be passed to a Transition function. template @@ -368,45 +213,11 @@ struct StateFactory { virtual StateTypeId GetStateType() const = 0; virtual State* AllocateState() const = 0; - virtual void InvokeStateOnEnter(State* state, const StateArgs* stateArgs) const = 0; }; inline bool operator==(const StateFactory& lhs, const StateFactory& rhs) { return lhs.GetStateType() == rhs.GetStateType(); } inline bool operator!=(const StateFactory& lhs, const StateFactory& rhs) { return !(lhs == rhs); } -namespace detail -{ - template - struct Select - { - typedef TrueType Type; - }; - - template - struct Select - { - typedef FalseType Type; - }; - - template - struct IsSame - { - static const bool value = false; - }; - - template - struct IsSame - { - static const bool value = true; - }; - - template - struct IsDifferent - { - static const bool value = !IsSame::value; - }; -} - // ConcreteStateFactory is the actual state creator; these are allocated statically in the transition // functions (below) and stored within Transition instances. template @@ -422,45 +233,80 @@ struct ConcreteStateFactory : StateFactory return HSM_NEW TargetState(); } - // See implementation of this function after class State is defined - virtual void InvokeStateOnEnter(State* state, const StateArgs* stateArgs) const; - private: // Only GetStateFactory can create this type friend const StateFactory& GetStateFactory(); ConcreteStateFactory() {} - - struct InvokeStateOnEnterNoArgsFunctor - { - static void Execute(State* state, const StateArgs* stateArgs) - { - (void)stateArgs; - HSM_ASSERT_MSG(stateArgs == 0, "Target state does not expect args, yet args were passed in via the transition"); - - //@NOTE: Compiler will fail here if TargetState defines OnEnter(const Args&) - static_cast(state)->OnEnter(); - } - }; - - struct InvokeStateOnEnterWithArgsFunctor - { - static void Execute(State* state, const StateArgs* stateArgs) - { - HSM_ASSERT_MSG(stateArgs != 0, "Target state expects args, make sure to pass them in in via the transition"); - - //@NOTE: Compiler will fail here if TargetState does not define OnEnter(const Args&) - static_cast(state)->OnEnter( static_cast(*stateArgs) ); - } - }; }; template const StateFactory& GetStateFactory() { + static_assert(std::is_convertible::value, "TargetState must derive from hsm::State"); static ConcreteStateFactory instance; return instance; } +// Small wrapper used to carry the SourceState along with the StateFactory for a state override. +// This allows us to provide an overload of transition functions that accept a state override with args. +template +struct StateOverride +{ + explicit StateOverride(const StateFactory& stateFactory) : mStateFactory(stateFactory) {} + operator const StateFactory& () const { return mStateFactory; } + const StateFactory& mStateFactory; +}; + +typedef std::function OnEnterArgsFunc; + +// MSVC 14 (VS 2015) doesn't handle generating lambdas that capture C-style arrays ("const T(&)[n]") +// by value, emitting error "array initialization requires a brace-enclosed initializer list". +// The most common case that this affects are immediate strings, so we work around this issue by +// detecting this case and forwarding them as "const char*", which is safe since immediate strings +// have global lifetime. +#ifdef _MSC_VER +#define DECAY_IMMEDIATE_STRINGS_WHEN_FORWARDING +#endif + +namespace detail +{ + // Generates a lambda that will invoke TargetState::OnEnter with matching args. + // We get a compiler-time error if a matching OnEnter is not found. + template + OnEnterArgsFunc DoGenerateOnEnterArgsFunc(Args&&... args) + { + static_assert(std::is_convertible::value, "TargetState must derive from hsm::State"); + + // Purposely capture args by copy rather than by reference in case args are + // created on the stack. Use std::ref() to wrap args that do not need to be copied. + return [args...](State* state) { (static_cast(state))->OnEnter(std::move(args)...); }; + } + + // Base case: do nothing + template + T&& DecayIfImmediateString(T&& arg1) + { + return std::forward(arg1); + } + + // If immediate string, decay to const char* + template + const char* DecayIfImmediateString(const char(&arr)[_Nx]) + { + return static_cast(arr); + } + + template + OnEnterArgsFunc GenerateOnEnterArgsFunc(Args&&... args) + { +#ifdef DECAY_IMMEDIATE_STRINGS_WHEN_FORWARDING + return DoGenerateOnEnterArgsFunc(DecayIfImmediateString(args)...); +#else + return DoGenerateOnEnterArgsFunc(std::forward(args)...); +#endif // DECAY_IMMEDIATE_STRINGS_WHEN_FORWARDING + } +} // namespace detail + // Transition objects are created via the free-standing transition functions below, and typically returned by // GetTransition. They can also be stored as data members, passed around, and returned later. They are meant @@ -484,31 +330,27 @@ struct Transition } // Transition with state args - template - Transition(Transition::Type transitionType, const StateFactory& stateFactory, const StateArgsType& stateArgs) + Transition(Transition::Type transitionType, const StateFactory& stateFactory, OnEnterArgsFunc&& onEnterArgsFunc) : mTransitionType(transitionType) , mStateFactory(&stateFactory) + , mOnEnterArgsFunc(std::move(onEnterArgsFunc)) { - // Copy-construct new instance of state args, stored in intrusive_ptr for ref counting - mStateArgs.Reset( HSM_NEW StateArgsType(stateArgs) ); } Transition::Type GetTransitionType() const { return mTransitionType; } StateTypeId GetTargetStateType() const { HSM_ASSERT(mStateFactory != 0); return mStateFactory->GetStateType(); } const StateFactory& GetStateFactory() const { HSM_ASSERT(mStateFactory != 0); return *mStateFactory; } + const OnEnterArgsFunc& GetOnEnterArgsFunc() const { return mOnEnterArgsFunc; } hsm_bool IsSibling() const { return mTransitionType == Sibling; } hsm_bool IsInner() const { return mTransitionType == Inner; } hsm_bool IsInnerEntry() const { return mTransitionType == InnerEntry; } hsm_bool IsNo() const { return mTransitionType == No; } - //@NOTE: Do not cache returned pointer - const StateArgs* GetStateArgs() const { return mStateArgs.Get(); } - private: Transition::Type mTransitionType; const StateFactory* mStateFactory; // Bald pointer is safe for shallow copying because StateFactory instances are always statically allocated - util::IntrusivePtr mStateArgs; // Reference counted pointer so we can safely copy Transitions without leaking + OnEnterArgsFunc mOnEnterArgsFunc; // Optional: set if transition specifies arguments }; @@ -521,24 +363,23 @@ inline Transition SiblingTransition(const StateFactory& stateFactory) return Transition(Transition::Sibling, stateFactory); } -template -Transition SiblingTransition(const StateFactory& stateFactory, const StateArgsType& stateArgs) -{ - return Transition(Transition::Sibling, stateFactory, stateArgs); -} - template Transition SiblingTransition() { return Transition(Transition::Sibling, GetStateFactory()); } -template -Transition SiblingTransition(const StateArgsType& stateArgs) +template +Transition SiblingTransition(Args&&... args) { - return Transition(Transition::Sibling, GetStateFactory(), stateArgs); + return Transition(Transition::Sibling, GetStateFactory(), detail::GenerateOnEnterArgsFunc(std::forward(args)...)); } +template +Transition SiblingTransition(const StateOverride& stateOverride, T1&& arg1, Args&&... args) +{ + return Transition(Transition::Sibling, stateOverride.mStateFactory, detail::GenerateOnEnterArgsFunc(std::forward(arg1), std::forward(args)...)); +} // InnerTransition @@ -547,24 +388,23 @@ inline Transition InnerTransition(const StateFactory& stateFactory) return Transition(Transition::Inner, stateFactory); } -template -Transition InnerTransition(const StateFactory& stateFactory, const StateArgsType& stateArgs) -{ - return Transition(Transition::Inner, stateFactory, stateArgs); -} - template Transition InnerTransition() { return Transition(Transition::Inner, GetStateFactory()); } -template -Transition InnerTransition(const StateArgsType& stateArgs) +template +Transition InnerTransition(Args&&... args) { - return Transition(Transition::Inner, GetStateFactory(), stateArgs); + return Transition(Transition::Inner, GetStateFactory(), detail::GenerateOnEnterArgsFunc(std::forward(args)...)); } +template +Transition InnerTransition(const StateOverride& stateOverride, T1&& arg1, Args&&... args) +{ + return Transition(Transition::Inner, stateOverride.mStateFactory, detail::GenerateOnEnterArgsFunc(std::forward(arg1), std::forward(args)...)); +} // InnerEntryTransition @@ -573,22 +413,22 @@ inline Transition InnerEntryTransition(const StateFactory& stateFactory) return Transition(Transition::InnerEntry, stateFactory); } -template -Transition InnerEntryTransition(const StateFactory& stateFactory, const StateArgsType& stateArgs) -{ - return Transition(Transition::InnerEntry, stateFactory, stateArgs); -} - template Transition InnerEntryTransition() { return Transition(Transition::InnerEntry, GetStateFactory()); } -template -Transition InnerEntryTransition(const StateArgsType& stateArgs) +template +Transition InnerEntryTransition(Args&&... args) +{ + return Transition(Transition::InnerEntry, GetStateFactory(), detail::GenerateOnEnterArgsFunc(std::forward(args)...)); +} + +template +Transition InnerEntryTransition(const StateOverride& stateOverride, T1&& arg1, Args&&... args) { - return Transition(Transition::InnerEntry, GetStateFactory(), stateArgs); + return Transition(Transition::InnerEntry, stateOverride.mStateFactory, detail::GenerateOnEnterArgsFunc(std::forward(arg1), std::forward(args)...)); } @@ -768,18 +608,19 @@ struct State return stateValue.mValue; } - // Child states are expected to hide this type with their own struct named Args that derives from StateArgs - typedef StateArgs Args; - // Overridable functions // OnEnter is invoked when a State is created; Note that GetStateMachine() is valid in OnEnter. // Also note that the function does not need to be virtual as the state machine invokes it - // directly on the most-derived type (not polymorphically); however, we make it virtual for consistency. + // directly on the most-derived type (not polymorphically); however, we make it virtual to avoid + // compiler warnings about hiding base class functions. virtual void OnEnter() {} - // If state expects StateArgs, the overridden version should look like this - //virtual void OnEnter(const Args& args); + // You can also pass args to OnEnter via any of the transition functions. Just make sure to declare an + // OnEnter that matches the argument types. Since the OnEnter call is delayed, the arguments will be copied. + // Use std::ref() to avoid the copy (but make sure the object is still valid by the time OnEnter is called!). + // + //void OnEnter(...) // OnExit is invoked just before a State is destroyed virtual void OnExit() {} @@ -788,8 +629,8 @@ struct State // the state stack has settled (i.e. all states return NoTransition). Override this function to return // a state to transition to, or NoTransition to remain in this state. Generally, this function should avoid // side-effects (updating state) as it may be called several times on the same state per ProcessStateTransitions. - // Instead, it should read state to determine whether a transition should be made. For udpating, override - // the Update function. + // Instead, it should read state to determine whether a transition should be made. Override Update instead + // to update the current state. virtual Transition GetTransition() { return NoTransition(); @@ -800,7 +641,7 @@ struct State virtual void Update(HSM_STATE_UPDATE_ARGS) {} template - const hsm::StateFactory& GetStateOverride(); + StateOverride GetStateOverride(); private: @@ -886,17 +727,6 @@ struct StateWithOwner : StateBaseType OwnerType* const& mOwner; }; -// Implemented here because this function requires State to be fully defined (i.e. a complete type) -template -void ConcreteStateFactory::InvokeStateOnEnter(State* state, const StateArgs* stateArgs) const -{ - // We select which functor to call at compile-time so that only states that expect StateArgs are required to implement - // an OnEnter(const Args& args) where Args is a struct derived from StateArgs defined within TargetState. - const bool expectsStateArgs = detail::IsDifferent::value; - typedef typename detail::Select::Type Functor; - Functor::Execute(state, stateArgs); -} - } // namespace hsm #ifdef _MSC_VER @@ -1149,9 +979,9 @@ inline const StateType* State::GetImmediateInnerState() const } template -inline const StateFactory& State::GetStateOverride() +inline StateOverride State::GetStateOverride() { - return GetStateMachine().GetStateOverride(); + return StateOverride(GetStateMachine().GetStateOverride()); } // Inline StateMachine function implementations @@ -1211,7 +1041,14 @@ namespace detail inline void InvokeStateOnEnter(const Transition& transition, State* state) { - transition.GetStateFactory().InvokeStateOnEnter(state, transition.GetStateArgs()); + if (const auto& onEnterArgsFunc = transition.GetOnEnterArgsFunc()) + { + onEnterArgsFunc(state); + } + else + { + state->OnEnter(); + } } inline void InvokeStateOnExit(State* state) diff --git a/samples/hsm_book_samples/source/ch4/restarting_states.cpp b/samples/hsm_book_samples/source/ch4/restarting_states.cpp index bfcbd58..c24898d 100644 --- a/samples/hsm_book_samples/source/ch4/restarting_states.cpp +++ b/samples/hsm_book_samples/source/ch4/restarting_states.cpp @@ -57,7 +57,7 @@ struct CharacterStates if (Owner().mAttack) { // Start attack sequence with combo index 0 - return SiblingTransition( Attack::Args(0) ); + return SiblingTransition(0); } return InnerEntryTransition(); @@ -88,17 +88,11 @@ struct CharacterStates struct Attack : BaseState { - struct Args : StateArgs - { - Args(int comboIndex) : mComboIndex(comboIndex) {} - int mComboIndex; - }; - - virtual void OnEnter(const Args& args) + virtual void OnEnter(int comboIndex) { Owner().mAttack = false; - mComboIndex = args.mComboIndex; + mComboIndex = comboIndex; static const char* AttackAnim[] = { @@ -119,7 +113,7 @@ struct CharacterStates && Owner().mAnimComponent.PollEvent("CanChainCombo")) { // Restart state with next combo index - return SiblingTransition( Attack::Args(mComboIndex + 1) ); + return SiblingTransition(mComboIndex + 1); } if (Owner().mAnimComponent.IsFinished()) diff --git a/samples/hsm_book_samples/source/ch4/reusable_states.cpp b/samples/hsm_book_samples/source/ch4/reusable_states.cpp index aa735a7..665053a 100644 --- a/samples/hsm_book_samples/source/ch4/reusable_states.cpp +++ b/samples/hsm_book_samples/source/ch4/reusable_states.cpp @@ -48,26 +48,13 @@ struct CharacterStates struct PlayAnim : BaseState { - struct Args : StateArgs + virtual void OnEnter(const char* animName + , bool loop = true + , const Transition& doneTransition = SiblingTransition() + ) { - Args(const char* animName - , bool loop = true - , const Transition& doneTransition = SiblingTransition() - ) - : animName(animName) - , loop(loop) - , doneTransition(doneTransition) - {} - - const char* animName; - bool loop; - Transition doneTransition; - }; - - virtual void OnEnter(const Args& args) - { - Owner().mAnimComponent.PlayAnim(args.animName, args.loop); - mDoneTransition = args.doneTransition; + Owner().mAnimComponent.PlayAnim(animName, loop); + mDoneTransition = doneTransition; } virtual Transition GetTransition() @@ -103,6 +90,9 @@ struct CharacterStates } }; + template + struct PrintType; + struct Attack : BaseState { virtual Transition GetTransition() @@ -110,9 +100,10 @@ struct CharacterStates if (IsInInnerState()) return SiblingTransition(); - return InnerEntryTransition(PlayAnim::Args("Attack_1", false, - SiblingTransition(PlayAnim::Args("Attack_2", false, - SiblingTransition(PlayAnim::Args("Attack_3", false)))))); + return InnerEntryTransition( + "Attack_1", false, SiblingTransition( + "Attack_2", false, SiblingTransition( + "Attack_3", false))); } }; }; diff --git a/samples/hsm_book_samples/source/ch4/shared_states_related_owners.cpp b/samples/hsm_book_samples/source/ch4/shared_states_related_owners.cpp index 2836e15..0e11fa3 100644 --- a/samples/hsm_book_samples/source/ch4/shared_states_related_owners.cpp +++ b/samples/hsm_book_samples/source/ch4/shared_states_related_owners.cpp @@ -54,26 +54,13 @@ struct SharedStates struct PlayAnim : BaseState { - struct Args : StateArgs + virtual void OnEnter(const char* animName + , bool loop = true + , const Transition& doneTransition = SiblingTransition() + ) { - Args(const char* animName - , bool loop = true - , const Transition& doneTransition = SiblingTransition() - ) - : animName(animName) - , loop(loop) - , doneTransition(doneTransition) - {} - - const char* animName; - bool loop; - Transition doneTransition; - }; - - virtual void OnEnter(const Args& args) - { - Owner().mAnimComponent.PlayAnim(args.animName, args.loop); - mDoneTransition = args.doneTransition; + Owner().mAnimComponent.PlayAnim(animName, loop); + mDoneTransition = doneTransition; } virtual Transition GetTransition() @@ -141,7 +128,7 @@ struct HeroStates if (IsInInnerState()) return SiblingTransition(); - return InnerEntryTransition(PlayAnim::Args("Attack_1", false)); + return InnerEntryTransition("Attack_1", false); } }; }; @@ -206,7 +193,7 @@ struct EnemyStates if (IsInInnerState()) return SiblingTransition(); - return InnerEntryTransition(PlayAnim::Args("Attack_1", false)); + return InnerEntryTransition("Attack_1", false); } }; }; diff --git a/samples/hsm_book_samples/source/ch4/shared_states_unrelated_owners.cpp b/samples/hsm_book_samples/source/ch4/shared_states_unrelated_owners.cpp index b943c06..ebbab4b 100644 --- a/samples/hsm_book_samples/source/ch4/shared_states_unrelated_owners.cpp +++ b/samples/hsm_book_samples/source/ch4/shared_states_unrelated_owners.cpp @@ -16,26 +16,13 @@ struct SharedStates { using BaseState::Owner; - struct Args : StateArgs + virtual void OnEnter(const char* animName + , bool loop = true + , const Transition& doneTransition = SiblingTransition< PlayAnim_Done >() + ) { - Args(const char* animName - , bool loop = true - , const Transition& doneTransition = SiblingTransition< PlayAnim_Done >() - ) - : animName(animName) - , loop(loop) - , doneTransition(doneTransition) - {} - - const char* animName; - bool loop; - Transition doneTransition; - }; - - virtual void OnEnter(const Args& args) - { - Owner().mAnimComponent.PlayAnim(args.animName, args.loop); - mDoneTransition = args.doneTransition; + Owner().mAnimComponent.PlayAnim(animName, loop); + mDoneTransition = doneTransition; } virtual Transition GetTransition() @@ -124,7 +111,7 @@ struct HeroStates if (IsInInnerState()) return SiblingTransition(); - return InnerEntryTransition(PlayAnim::Args("Attack_1", false)); + return InnerEntryTransition("Attack_1", false); } }; }; @@ -200,7 +187,7 @@ struct EnemyStates if (IsInInnerState()) return SiblingTransition(); - return InnerEntryTransition(PlayAnim::Args("Attack_1", false)); + return InnerEntryTransition("Attack_1", false); } }; }; diff --git a/samples/hsm_book_samples/source/ch4/state_args.cpp b/samples/hsm_book_samples/source/ch4/state_args.cpp index 946cb8d..7669371 100644 --- a/samples/hsm_book_samples/source/ch4/state_args.cpp +++ b/samples/hsm_book_samples/source/ch4/state_args.cpp @@ -45,20 +45,9 @@ struct CharacterStates struct PlayAnim : BaseState { - struct Args : StateArgs + virtual void OnEnter(const char* animName, bool loop = true, float blendTime = 0.2f, float rate = 1.0f) { - Args(const char* animName, bool loop = true, float blendTime = 0.2f, float rate = 1.0f) - : animName(animName), loop(loop), blendTime(blendTime), rate(rate) {} - - const char* animName; - bool loop; - float blendTime; - float rate; - }; - - virtual void OnEnter(const Args& args) - { - Owner().mAnimComponent.PlayAnim(args.animName, args.loop, args.blendTime, args.rate); + Owner().mAnimComponent.PlayAnim(animName, loop, blendTime, rate); } virtual Transition GetTransition() @@ -90,7 +79,7 @@ struct CharacterStates if (Owner().mMove) return SiblingTransition(); - return InnerEntryTransition(PlayAnim::Args("Anim_Stand")); + return InnerEntryTransition("Anim_Stand"); } }; @@ -107,7 +96,7 @@ struct CharacterStates return SiblingTransition(); } - return InnerEntryTransition(PlayAnim::Args("Anim_Move")); + return InnerEntryTransition("Anim_Move"); } }; @@ -118,7 +107,7 @@ struct CharacterStates if (IsInInnerState()) return SiblingTransition(); - return InnerEntryTransition(PlayAnim::Args("Anim_Jump", false)); + return InnerEntryTransition("Anim_Jump", false); } }; }; diff --git a/samples/hsm_book_samples/source/ch4/state_overrides.cpp b/samples/hsm_book_samples/source/ch4/state_overrides.cpp index 5d588ac..995fb49 100644 --- a/samples/hsm_book_samples/source/ch4/state_overrides.cpp +++ b/samples/hsm_book_samples/source/ch4/state_overrides.cpp @@ -52,7 +52,7 @@ struct CharacterStates if (Owner().mJump) { Owner().mJump = false; - return SiblingTransition(GetStateOverride()); + return SiblingTransition(GetStateOverride(), 20); } return NoTransition(); @@ -67,9 +67,19 @@ struct CharacterStates } }; - struct Jump : BaseState + struct JumpBase { - virtual Transition GetTransition() + virtual void OnEnter(int height) = 0; + }; + + struct Jump : BaseState, JumpBase + { + void OnEnter(int height) override + { + (void)height; + } + + Transition GetTransition() override { return SiblingTransition(); } @@ -118,9 +128,14 @@ struct HeroStates } }; - struct Jump : BaseState + struct Jump : BaseState, CharacterStates::JumpBase { - virtual Transition GetTransition() + void OnEnter(int height) override + { + (void)height; + } + + Transition GetTransition() override { return SiblingTransition(); }