| Safe Haskell | None |
|---|---|
| Language | GHC2024 |
Mischief.Assets.Tutorial.Introduction
Description
Synopsis
Chapter 1: Introduction to Assets
This chapter will walk you through creating and loading your first asset, as well as hot-reloading it and integrating it into the ECS.
We'll start by creating an app:
import Mischief.Assets import Mischief.ECS main ::IO() main = do app <-newAppaddPlugin@MyPlugin apprunAppapp data MyPlugin instancePluginMyPlugin
It's good practice to set an asset source at the start of your program. This will be the place where you'll be loading and saving assets.
app <-newAppsetAssetSource"assets/" app ...
Make sure to create an assets folder. This is generally relative to where you're executing the program from.
Defining an Asset
For this tutorial we'll use .kdl (pronounced 'cuddle'), as it is the official file format used by Mischief. You can read more about it here.
We'll define an asset that looks like this:
heal {
amount 5
}
Just a simple heal node with an amount child containing an integer.
Create a heal.kdl file in your assets folder and copy the above content into it.
Next, we need a type in Haskell to represent that data. Component is necessary for all assets.
KdlAsset is a tool we use to conver the kdl into this form for us.
newtype Heal = Heal {amount :: Int}
deriving (Generic, KdlAsset, Show, Component)
Now to write the actual Asset instance:
instanceAssetHeal where typeSaveableHeal =FalseloadAsset=loadKdl"heal"
We do two things here: First, we set Saveable to False. We just don't care about saving this asset. Second,
we tell it how to actually load the asset. We use the loadKdl function provided by the earlier KdlAsset instance.
We need to also give it the name of the node we want it to load. In our case that's "heal".
Loading the Asset
We can load an asset using the load function! Let's write a system that does that.
loadHeal ::System() loadHeal =load_@Heal "heal.kdl"
Since assets are loaded aysnchronously, we also need a way to react to the asset being loaded if we want to print it and
check that everything's working. We can do that using the OnLoad event:
onLoadHeal ::OnLoadHeal ->System() onLoadHeal (OnLoade) = [q|e. Heal|] &qinfo(\h -> [i|Loaded #{h}|]) &query_
And of course, we need to set these to run:
instancePluginMyPlugin whereinit= dosystemsloadHeal &schedule@Startupspawn_$ObserveronLoadHeal
When running your app, you should now see this printed to your screen:
[INFO] Loaded Heal {amount = 5}
Hot-Reloading the Asset
Pretty annoying that we need to restart our program after modifying our asset in order to load it again..
But that's why we have hot-reloading! Just add the FileWatcherPlugin as a dependency to your plugin:
instancePluginMyPlugin wheredeps= [dep@FileWatcherPlugin]
Note that, on Windows, you may need to run your project with the -fthreaded flag for this to work properly.
Now, while running your app, try changing the amount in the heal.kdl file. You'll see the new value printed
to the screen immediately!
Integrating Assets Into a Game
Let's spawn a few players, each having a Health component:
newtype Health = HealthIntderiving (Component) spawnPlayers ::System() spawnPlayers = dospawn_(Name "A", Health 20)spawn_(Name "B", Health 30)spawn_(Name "C", Health 60)
Now let's write a system that, when it runs, increases each player's health by the value currently loaded inside Heal:
healPlayers ::System() healPlayers =query_$ do Heal heal <-qasset@Heal "heal.kdl" (player, name, Health hp) <- [q|Entity, Name, Health|]qlift$ doinsert(Health $ hp + heal) playerinfo[i|Player #{name} is now at #{hp + heal} hp|]
Inside a query, we are grabbing each player's name and health, along with the Heal asset. We are then inserting the updated health on the player and printing it.
All that's left is scheduling this to run at a fixed interval:
import Mischief.ECS.Interval qualified as Interval
instancePluginMyPlugin whereinit= dosystemsspawnPlayers &schedule@Startupvoid$ Interval.start1000000 healPlayers
Note that we don't need to manually load the file anymore. Asset queries are active by default, meaning they will trigger a load for us if the asset we're asking for is not currently loaded.
You should see a bunch of these printed to your screen:
[Info] Player A is now at 24 hp [Info] Player B is now at 34 hp [Info] Player C is now at 64 hp
Try toying with the heal amount in the file, and see how that affects the amount players heal each second!