I don't know if this counts as "the first Roc game", since all Roc is doing is adjusting the speed of the ball+paddle... but it works great!
https://github.com/JanCVanB/roc-bevies
oh very cool!
the platform should be able to accept an u16, I can check why that did not work later today
Thanks! This is my first original (not entirely copy-pasted) Rust code, so it's likely that I misunderstood roc_std
's (and Bevy's) constraints.
so bevy does not work for me currently (linker problems), but looking at the LLVM IR I don't see why having the roc code (ultimately speedForHost
) return an I16
. What was the problem you ran into?
I was simply hacking naively until something worked, so this could all be incorrect: The equation it plugs into seems to expect an f32, and Rust told me that it couldn't autocast an i128 to an f32. Instead, it suggested that it could do so for an i16, so I went with that.
are there downsides to roc just giving you an f32?
No, that would be great! I was working under the assumption that the only types I could pass between Roc & Rust were the pub RocFoo
things in roc_std/
, so basically RocStr
, RocDec
, and RocResult
.
oh, no
all the primitive types also work
so ints and floats
🤯
these map 1 to 1 (except Nat
becomes usize
)
Well that's wonderful.
and as I said elsewhere records/structs also work in a predictable but not always intuitive way
I think I skimmed the examples and only found RocStr
used.
so far sending integers was boring
Me: "Passing ints & floats must be hard, somehow. Dec it is!"
we just happen to agree on what the bits mean for number types
for strings for instance that is not true, because we have the small string optimization
Therefore, my next steps for roc-bevies is to (a) simplify int passing and (b) expanding breakout to populate most constant values & primitive formulae with Int/Float/Str/function fields of a big Roc configuration record
I love the idea of Rust/Bevy handling all the heavy rendering logic, then asking its boss Roc "hey, what sprite should I use here, and how many points does the player get for this?"
yes! I think it gets really interesting if the roc app stores some state
How??
Monadically?
I'd love to hear an example :D
No, I think game logic could live in roc. This may be a bit contrived but
Model : { score : U64 }
Msg : [ ScoredPoint ]
update : Model, Msg -> Model
update = \model, msg ->
when msg is
ScoredPoint -> { score: model.score + 1 }
view : Model -> U64
view = .score
you'd also need init = { score: 0 }
then the platform can call init
, and then update the model it has every time a game event happens (e.g. score is increased), and "render" the score when needed
does this make sense in practice? I don't really know, but it's possible
Yes! This update-state-on-event loop is common in games, and Bevy has a managed hyperscalable version of that pattern in its ECS architecture
I suppose that Roc could complement an ECS platform with its own additional state management layer, but I'm not sure why it would
If your app doesn't have a fixed timestep, that could be a fun pattern for discrete event simulations :)
On second thought, since Bevy claims to be modular, moving state management (data layer, ECS, whatever) into Roc could be quite fun
I'm pretty bummed that it looks like Roc can't define Bevy game nouns (entities are defined via Rust struct literals) or verbs (system functions reference entity types), only support them with primitive-passing functions. Is that accurate?
I guess you'd need to have your platform code create rust types (macro?) from roc types.
Mirroring bevy's systems API to expose it to Roc may prove to be difficult ^^'
actrually @Richard Feldman have you thought about this? I remember we discussed writing database plugins in Roc which would run into the same issues: you'd want the database to understand your specific roc data structure. As far as I know, you can only do that with additional code gen
e.g. for postgress extensions in rust you need to derive a bunch of things
and it generates a ton of C code
:nerd::memo:
so what we'd talked about awhile ago (a year or so maybe?) was something along these lines:
.roc
file and it outputs a serialized representation (exact format TBD) of all the types that would get sent to the host, in a format that includes all the necessary low-level information (e.g. struct field ordering would have already taken into account alignment and alphabetizing)we hadn't talked about it in the context of Rust macros needing to get involved, but one idea for a particularly fancy way that could potentially work: in your Rust host's build.rs
, use :point_up: to generate .rs
files from your Roc types, with the (un-annotated) structs and everything, then - still in build.rs
- modify those files to add the annotations (e.g. find/replace struct Player {
with #[derive(BevyStuff)]\nstruct Player {
), and then let the build continue as normal.
so assuming all the generator prerequisites were in place (they don't exist yet, of course!) the workflow would be:
build.rs
stuff will take care of keeping the Rust types in sync with the Roc types, and adding any annotations Bevy needs, so you can also build your Rust project with cargo
as normal and it will stay in syncof course there's some extra setup work involved to make that generation/substitution happen at the right time, but I don't think that's avoidable in the general case whenever the goal is to sync type definitions between two decoupled programming languages :big_smile:
Right, the challenge is then to make the whole rust/c/zig codegen bit hidden from the Roc user
for https://github.com/zombodb/pgx they do this with a custom cargo command
@Richard Feldman That sounds awesome! I had no idea Roc could get that powerful.
That codegen feature addition would enable replacing roc-bevies
(dozens of subgenre-specific platforms) with roc-bevy
(one general-purpose gamedev platform).
cool stuff!
@Folkert de Vries :smiley: https://github.com/JanCVanB/roc-bevies/commit/70fc0e
Last updated: Jul 06 2025 at 12:14 UTC