IO Image & view function
nikita-leonov opened this issue · 8 comments
It is more a Haskell question, but not a Helm question but here is a problem. withImage returns IO, so can be converted only to IO Collage and afterward to IO Graphics. Meanwhile view function expects an actual Graphics value as return. It is unclear how to stay in monad context and still provide a view function with images. What would be a right way to load an image and use for drawing with Helm?
Ok, it seems it should be just loaded through Cmd execute and transformed to Action. Still in progress of implementation, but seems like a way to go...
You should use withImage
in your main
and then pass the image to your initial function before using Helm.run
.
This is what I tried to do:
initial :: SDLEngine -> (Model, Cmd SDLEngine Action)
initial engine = (Model { pos = V2 0 0, tileset = Nothing }, Cmd.execute (withTileset engine) LoadTileset)
withTileset :: SDLEngine -> (IO (Image SDLEngine))
withTileset engine = withImage engine "tileset.png" return
update :: Model -> Action -> (Model, Cmd SDLEngine Action)
update model Idle = (model, Cmd.none)
update model (ChangePosition pos) = (model { pos = pos }, Cmd.none)
update model (LoadTileset img) = (model { tileset = Just img }, Cmd.none)
subscriptions :: Sub SDLEngine Action
subscriptions = Mouse.moves (\(V2 x y) -> ChangePosition $ V2 (fromIntegral x) (fromIntegral y))
view :: Model -> Graphics SDLEngine
view Model { pos = pos, tileset = Just img } = Graphics2D $ collage [move pos $ fittedImage (V2 100 100) img]
Main looks as following:
do engine <- SDL.startup
run engine GameConfig {
initialFn = initial engine
, updateFn = update
, subscriptionsFn = subscriptions
, viewFn = view
}
The resulting app produce three possible different outputs:
Assertion failed: (status < CAIRO_STATUS_LAST_STATUS), function _cairo_pattern_set_error, file cairo-pattern.c, line 187.
Abort trap: 6
Assertion failed: (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&surface->ref_count)), function cairo_surface_reference, file cairo-surface.c, line 931.
Abort trap: 6
game-exe: user error (out of memory)
Obviously I expected non of them :) Any ideas?
withImage
cleans up the image as soon as the lambda provided to it's result finishes computing.
So when the command is executed the image is destroyed and unusable.
Instead rework run to use it like I was talking about:
do engine <- SDL.startup
withImage engine "tileset.png" $ \image ->
run engine GameConfig {
initialFn = initial image
, updateFn = update
, subscriptionsFn = subscriptions
, viewFn = view
}
This way withImage will properly cleanup the image when the engine exits.
Oh wow. It is working :) Thanks! Looks like I have a first ever in the Internet example of helm with image asset. Really disappointing that it is so simple to write a code that compiles but fails with some unclear reason. It will be really hard to trace such issue for unexperienced user.
How would you implement the dynamic load of resources while engine is running, with lambda constraint taken into account?
Hmm, seems like the behaviour of withImage is fairly clear to me from the docs https://github.com/z0w0/helm/blob/master/src/Helm/Engine/SDL/Engine.hs#L103
If you think it could be improved, could you open a pull request with some revisions?
The reason there's no documentation online is because Helm is mainly a toy project. I don't use it for any of my current projects and I doubt anyone is using it for anything important. So it's quite unloved now (both in the feature and documentation areas).
Dynamic loading isn't currently supported. You could revise withImage to a 'loadImage' call and then have the engine automatically cleanup those resources at exit. Personally I think a different paradigm would have to be investigated.
You did a great job with it. I wish people give it more love so it will be in a little bit more mature state, but foundation indeed promising. Keep going with your "hobby". 👍
Actually, current withImage
implementation makes it difficult to load multiple different assets and run the engine with them. I would expect that mapping function for withImage
returns IO
and afterward, it is possible to zip IO
s and use the result. As of today, there is not much use of IO
returned by withImage
, since its image reference will be already released if I try to return it. Am I right, or am I missing something here?