{-# OPTIONS_GHC -Wno-unused-imports #-} -- | -- [Previous Chapter: Monadic Queries]("Mischief.ECS.Tutorial.Guides.MonadicQueries") -- -- [Next Chapter: Keeping Track of Time]("Mischief.ECS.Tutorial.Guides.Time") -- -- [Main Page]("Mischief.ECS") module Mischief.ECS.Tutorial.Guides.Scheduling ( -- * Learn You an ECS for Great Mischief! - 2.7. Scheduling and Running Systems -- $intro -- * Automatic Scheduling -- $auto_scheduling -- * Manual Scheduling -- $manual_scheduling -- * Creating and Running Schedules -- $schedules -- * Deferring Systems -- $defer -- * Fixed Schedules -- $fixed -- * [Next Chapter: Keeping Track of Time]("Mischief.ECS.Tutorial.Guides.Time") ) where import Control.Monad (void) import Data.Foldable (for_) import Data.Traversable (for) import Mischief.ECS import Mischief.ECS.App.Schedules (FixedSchedule) import Mischief.ECS.App.Systems (ScheduledIn, SystemFunction) import Mischief.ECS.Fixed qualified as Fixed import Mischief.ECS.Schedules qualified as Schedules import Mischief.ECS.Systems qualified as Systems -- $intro -- A @System@ in Mischief is a Monad that executes operations on a World. -- -- Unlike other ECS's, systems here are fully composable, In fact, most of the functions discussed in this tutorial so far were systems. -- For instance, the type of @spawn@ is: -- -- @ -- 'spawn' :: ('Bundle' b) => b -> 'System' ()@ -- @ -- -- Systems can either be ran directly, or they can be added scheduled. -- $auto_scheduling -- In order to schedule a system (or a tuple of systems) you can just use @systems@ to put your hand on them, and @schedule@ to add it to a given Schedule. -- -- @ -- 'systems' (foo, bar) -- & 'schedule' \@SomeSchedule -- @ -- -- Systems scheduled this way are stores as a combination of the actual system and the schedule. -- If we were to add @foo@ to /another/ schedule, it would be considered a different system. -- -- The @aftr@ and @before@ functions can be used to explicitly add ordering with other systems when registering them: -- -- @ -- 'systems' foo -- & 'before' foo -- & 'after' baz -- & 'schedule' \@SomeSchedule -- @ -- -- -- A system that's been scheduled this way can be unscheduled with @unschedule@ or @unschedule'@. -- -- @unschedule@ will keep the system alive but disconnect it from the given schedule. If re-scheduled later, -- the system will keep its previous ordering and other configuration: -- -- @ -- 'unschedule' \@SomeSchedule foo -- @ -- -- @unschedule'@ will completely wipe a system, erasing all of its configuration. If re-scheduled later, -- the system will be completely new and it won't have its previous ordering. -- -- @ -- 'Systems.unschedule'' \@SomeSchedule foo -- @ -- $manual_scheduling -- In order to manually create a schedules system, you spawn an entity with the @SystemFunction@ component: -- -- @ -- foo :: 'System' () -- @ -- -- @ -- fooEntity <- 'spawn' $ 'SystemFunction' foo -- @ -- -- You can get a schedule's entity using the following function: -- -- @ -- scheduleEntity <- Schedules.'Schedules.get' \@SomeSchedule -- @ -- -- Where: -- -- @ -- import "Mischief.ECS.Schedules" qualified as Schedules -- @ -- -- To add the system to the schedule you insert a @ScheduledIn@ relationship between the two: -- -- @ -- 'insert' ('Rel' 'ScheduledIn' scheduleEntity) fooEntity -- @ -- -- This will make make your system run along with @SomeSchedule@. Compared to using @Systems.add@, this method will not do any sort of bookkeeping for you. -- Is is your job to keep track of the spawned system's entity. -- -- Two systems can be ordered by using the @Before@ relationship: -- -- @ -- fooEntity <- 'spawn' $ 'SystemFunction' foo -- barEntity <- 'spawn' $ 'SystemFunction' bar -- -- 'insert' ('Rel' 'Before' fooEntity) barEntity -- @ -- -- The above will order @bar@ to happen before @foo@. -- $schedules -- Same as systems, @Schedules@ are entities. Each schedule has an associated type: -- -- @ -- data Update deriving ('Schedule') -- @ -- -- You can both create and get the the entity of a schedule using @Schedules.get@: -- -- @ -- update <- Schedules.'Schedules.get' \@Update -- @ -- -- You can run a schedule using @Schedules.run@: -- -- @ -- Schedules.'Schedules.run' \@Update -- @ -- -- This will run all systems currently linked to that Schedule, respecting their ordering. -- -- Mischief has two components: @'StartupSchedule'@ and @'UpdateSchedule'@ which you can add to a schedule to make it automatically run on app startup, respectively each frame. -- Additionally, 'FixedPlugin' allows you to use 'FixedSchedule'. -- -- Schedules can also be ordered via @'Before'@ (same relationship used for ordering systems). -- $defer -- @defer@ is a very important primitive. -- -- Normally every systems has its effect applied immediately. When you write @insert (Name \"Bob\") bob@, -- you are /immediately/ inserting the respective component. When you do @e <- 'spawn' ()@, you are /immediately/ spawning that entity into the World. -- -- @'defer'@ takes a system and stores it in an internal queue instead of directly applying it. -- -- @ -- 'defer' $ 'spawn' () -- @ -- -- @ -- 'defer' $ do -- e <- 'spawn' () -- 'insert' ('Name' \"Name\") e -- @ -- -- The queue of deferred systems will be flushed (running all the systems in it) at a sync point, usually in-between systems. -- -- You can also flush the queue manually using @'flush'@. -- $fixed -- Mischief offers Fixed schedules mirroring the normal Update schedules. They are called @'FixedUpdate'@, @'FixedPostUpdate'@ and so on. -- -- Systems added to them will be ran at the fixed frequency set in the @'FixedFrequency'@ resource. By default 0.03 (30fps). -- -- Make sure to add the @'FixedPlugin'@ to your App if you wish to use fixed systems.