{-# OPTIONS_GHC -Wno-unused-imports #-} -- | -- [Previous Chapter: Organizing your Project]("Mischief.ECS.Tutorial.Guides.Organizing") -- -- [Next Chapter: Query Basics]("Mischief.ECS.Tutorial.Guides.QueryBasics") -- -- [Main Page]("Mischief.ECS") module Mischief.ECS.Tutorial.Guides.Basics ( -- * Learn You an ECS for Great Mischief! - 2.2. Entities and Components Basics -- $intro -- * What's in a Bundle? -- $bundle -- * Manipulating Data -- $manipulating -- * Reading Data -- $reading -- * Required Components -- $required -- * Metadata -- $metadata -- * Resources -- $resources -- * From -- $from -- * Registering Components -- $reg -- * [Next Chapter: Query Basics]("Mischief.ECS.Tutorial.Guides.QueryBasics") ) where import Data.Default (Default) import Mischief.ECS -- $intro -- Data in an ECS is manipulated (stored, mutated, destroyed), by performing operations on entities. These operations usually involve bundles of components. -- $bundle -- A bundle is a tuple formed of /"things/" that can be /attached/ (or, inserted) to an Entity. -- -- A bundle can contain: -- -- * Component Values: Will attach the component. -- -- @ -- (Name \"A\", Health 100) bob -- @ -- -- * Relationships: Will attach a relationship with the given entity, containing the given component. -- -- @ -- ('Rel' Likes charlie) bob -- @ -- -- * Resources: Will insert the given resource into the world. Is agnostic to the entity it's inserted on. -- -- @ -- ('Res' (FooRes \"A\")) -- @ -- -- * From: Will insert the given component on the given entity. Is agnostic to the entity it's inserted on. -- -- @ -- ('From' alice (Name \"Alice\")) -- @ -- $manipulating -- In order to create new data, you spawn an entity with the given data stored in components (through a bundle): -- -- @ -- bob <- 'spawn' (Name \"Bob\", Player) -- @ -- -- You can also attach new data to an entity: -- -- @ -- 'insert' (Health 100, 'Rel' Likes alice) bob -- @ -- -- To change data, you re-insert the relevant components to the entity: -- -- @ -- 'insert' (Name \"Bob's New Name\") bob -- @ -- -- To erase data, you remove the components from the entity: -- -- @ -- 'remove' ('C' \@Health, 'R' Likes alice) bob -- @ -- -- You can also @despawn@ an entity, erasing all its data: -- -- @ -- 'despawn' bob -- @ -- $reading -- Data can be retrieved through queries. -- -- You can get Bob's name and health like this: -- -- @ -- 'Just' (name, health) \<- 'single' ['q'|bob. Name, Health|] -- @ -- -- You can grab each entity's name and health like this: -- -- @ -- x \<- 'query' ['q'|Name, Health|] -- @ -- -- More on queries in [the next chapter]("Mischief.ECS.Tutorial.Guides.QueryBasics"). -- $required -- A component @A@ can require any number of other components, as long as they all instance the 'Default' typeclass. -- -- @ -- data Player -- -- instance 'Component' Player where -- required = 'require' \@Health -- -- data Health = Health 'Int' deriving ('Component') -- -- instance 'Default' Health where -- def = Health 100 -- @ -- -- When inserting @Player@ on an entity, @Health 100@ will be immediately inserted as well, as long as the @Health@ component -- wasn't already inserted. -- $resources -- @Resources@ are singleton components that can be easily accessed and modified from any system. -- -- Any component can be used as a resource. -- -- @ -- data MyRes = MyRes 'Int' deriving ('Component') -- @ -- -- You can insert a resource into the World using @insertRes@. -- -- @ -- 'insertRes' $ MyRes 5 -- @ -- -- And you can query for the value of a resource using @res@: -- -- @ -- 'Just' myRes <- 'res' \@MyRes -- @ -- -- Resources are implemented by inserting a component's value on its own meta entity. Which means: -- -- @ -- 'res' \@MyRes -- @ -- -- Is equivalent to: -- -- @ -- m <- 'meta' \@MyRes -- 'single' $ [q|m. MyRes|] -- @ -- -- Besides using @insertRes@, resources can be inserted as part of a bundle through the @Res@ type. -- -- The following will spawn an entity, insert a @Name@ on it, and additionally insert a resource into the world. -- -- @ -- a <- 'spawn' () -- 'insert' (Name "A", 'Res' $ SomeRes 5) a -- @ -- -- It's equivalent to: -- -- @ -- a <- 'spawn' () -- 'insert' (Name \"A\") a -- 'insertRes' (SomeRes 5) -- @ -- -- @Res@ can also be used in queries to grab a resource: -- -- @ -- 'mkQuery' ('C' \@Name, 'Res' \@SomeRes) -- @ -- -- Or -- -- @ -- ['q'|Name, Res SomeRes|] -- @ -- -- This will query the name of all entities, and attach @Res SomeRes@ to all of them. -- $from -- From is a special type in Mischief: -- -- @ -- data From c = From {entity :: 'Entity', comp :: c} -- @ -- -- It symbolizes the idea of a foreign component. A component belonging to an external entity that we store alongside it. -- -- From can be inserted in any bundle, inserting the component @c@ on the entity stored /inside/ it. For instance, the following code -- will insert the name \"B2\" on @b@ and the name \"A2\" on @a@: -- -- @ -- a \<- 'spawn' (Name \"A\") -- b \<- 'spawn' (Name \"B\") -- -- 'insert' (Name \"B2\", 'From' a (Name \"A2\")) b -- @ -- -- Equivalent to: -- -- @ -- a \<- 'spawn' (Name \"A\") -- b \<- 'spawn' (Name \"B\") -- -- 'insert' (Name \"B2\") b -- 'insert' (Name \"A2\") a -- @ -- -- From is generally returned from traversal queries such as: -- -- @ -- x \<- 'query' ['q'|Name, ChildOf -\> (Name)|] -- @ -- -- @ -- x :: [Name, From Name] -- @ -- -- Where @Name@ will be the name of each entity and @From Name@ will be the name of their parent. -- $metadata -- Each component has a corresponding entity in the World. -- The components on that entity store information about the component itself. Such as which archetypes it is part of. -- -- A component's entity can be accessed by using @meta@. -- -- Getting the entities of the @Name@ and @Player@ components: -- -- @ -- x <- 'meta' \@Name -- y <- 'meta' \@Player -- @ -- -- Most users should avoid tinkering with Meta Components unless they have a good reason to, -- and should absolutely never remove or change any components added to them by the @ECS@. -- $reg -- @Registering@ a component involves spawning its meta entity and adding the corresponding data. -- -- Each component is registered automatically the first time it is inserted on an Entity, so you don't usually -- have to worry about registration. -- -- Queries are also smart about components; if you query or filter for a component that hasn't been registered yet, they will just -- assume that component can't be be on any Entity. Queries can't perform registration themselves, because they're not allowed to mutate -- the world in any way. -- -- However, there may be /extremely/ niche situations where you want to register components earlier than normal, which is where manual registration comes in: -- -- @ -- 'register' \@(Player, Health, Position) -- @ -- -- One such situation could be wanting to check the requirements between multiple components on runtime. -- If a component hasn't been registered yet, it won't show up when you query for components that require a specific component.