So now we've got Tomasso, he's going to tell us how to build a snake game, and we're going
to build it together.
Hopefully.
Hi all.
Today we are here to talk about snake, obviously, rust and wasm.
In particular, we will see how to build a snake game written in rust, and Shepard has
a wasm module.
Before doing that, I would like to introduce myself.
Hi, I'm Tomasso, I have two cats.
And commonly, I used to be a software architect in a web application environment.
So probably the games is not my best stuff I can do build, but I try.
So let's start to talk about what wasm is.
Powerless is a stack-based virtual machine that allows to be portable.
So we can build applications and bring where you want most mostly.
And the main target is web, but not linked to the web only.
We will see later.
So we have four concepts, efficient and fast, memory safety, safeness, open the bug able,
and part of the web platform.
For this reason, we have four parts, the core one, JavaScript API that allows us to interact
with the JavaScript world, like browser nodes and so on.
Web API that allows us to interact with DOM events and so on.
And wasm, this is a standard for web assembly system integration, if I remember correctly,
that allows us to interact with the file system, networking, and so on.
Obviously, was is not allowed in browser context for some reason.
So how we can write a was module?
Well, wasm actually supports two kind of format, text and binary, but probably you don't want
to write directly into wasm.
For example, assembly probably in this year, no one write in assembly directly.
But if you want, you can do that.
But probably you want to leverage on different languages, for example, cc++, raster, go and
so on.
But if you remember the previous slide, we talked about the memory safeness of the web
assembly, and which is the other language that remember has a similar capability, raster.
Because raster guarantee memory safety.
And this is why we are here to talk about REST plus web assembly.
So which is the constraint we have for building wasm in REST?
Unfortunately, we are not so free to use what we want.
We need to put an attribute wasm and gen in all type and exported, so structure and
arms and so on, but not linked to that.
Also the implementation block, we need to treat like that.
So put the same attribute on top of the implementation block.
And unfortunately wasm doesn't understand all the types available in REST.
So bytes are integer, but not all the integer has supported floating points and vector.
We have some limitation about that.
As consequence, for example, enumeration need to be treated as 80.
And all returned values from methods need to be casted to some wasm types or return
a wasm and gen structure.
So we are here to talk about snake, who play at least one snake, at least one, OK.
For the other, snake is a simplest game, two grid, there is a two dimension grid.
Your aim is to win, for winning, avoid and go through walls and eat yourself.
You are able to eat foods that give you scores and so on.
Your aim is to drive the snake through walls and try to eating the food, more or less.
Anyway, we will see you later.
So our code is here, is a cargo workspace with three members.
The first one is just plain REST implementation of game logic without wasm stuff, without
any other part.
The second one is handmade snake that is just a wrapper on the previous one in order to
let a JavaScript world to import it and use it.
So we implement the web interface manually through JavaScript and DOMs.
And the last is baby plug-in that allows us to create a proficiency, more proficiency
than manual ones, a game.
The last two members use the first one and we will see how.
So conceptually we have a bunch of stuff, a direction that allows us to describe which
is the direction the snake have.
The point, because we live inside a grid, so we have to somehow describe the points.
The game itself, private stuff, skip it, but mainly we have two members, tick and get the
last snapshot.
Tick allows us to move the snake in the direction specified there, has attributes.
And the last snapshot allows us to know what happened in the last tick.
For example, I eat a food, I go through the wall, which is my score, which is the position,
and last but not least, the period duration.
Because in the game the interval between the ticks changes accordingly with your score.
So the game more gone, the period decrease.
So how we can use hopefully you read the code, because the finger, anyway, I describe it.
We have a level described that has a simplest, in simple way, through a string.
We can parse it, creating a game.
We invoke a tick method on the game, describing which is the direction we want to use.
Get the last snapshot, check status, for example, in this case I eat a food, because the H goes
on the food.
We are not yet on the wall, and the game over is none.
Instead the code below goes through the wall, so on wall is true, and the game over is some
with the reason.
And finally, we have two public levels, snake one is the two, the difference probably you
know, but for repeating, the difference is the frame.
So in snake one we have a frame with all the walls, instead snake two is more like a toroids,
so you can go left and appear on the right and upper and bound.
So how we can use these?
We have a snake core, again without any dependency.
We need to wrap it, because we already seen, we have some deficit about that.
We have some custom JavaScript code that interact with DOM in order to update the UI.
This is more like what happened.
And at compilation time, after the compilation actually, we have a process for compilation,
the Rust code into a wasm, and this compilation generates two artifacts actually, the wasm
itself and an auto-generated JavaScript module that allow us to simplify the interaction
with the wasm module.
After that the same JavaScript code and the same DOM, so more or less what we have is
wasm and auto-generated JavaScript that allow us a lot.
So shortly, we need to wrap all the stuff, all the stuff.
So the direction, the point, the game, the snapshot, and so on.
So definitely, we are not able to do that for a large project.
Obviously, this works for little ones like snake, but if you want to build a bigger one,
probably it's not the best solution.
But if you want instead create a cryptographic library, hashing library, something like that,
this is really amazing, is sufficient.
But for the gaming, probably not so much.
So before seeing which is our alternative, we have, I have prepared a demo that obviously
you can find in the code.
So let's see if it is work properly.
Ah, yes, here.
OK.
So because I haven't the framework that helped me to build a better user UI, I choose this
one.
Sorry, I'm not a UI expert.
So for our proposal, it's sufficient.
As you can see here, there is some bootstrap, webpack, blah, blah, blah, we don't care at
all.
But at a certain point with a lot of wasm, wasm is a few kilobytes, so not so big.
The user is able to choose which game he would like to play, click on here, and move
with the arrow key.
Not whoa, but why not whoa.
Thanks.
OK.
Obviously, when I go through the wall, the game is hence put on wall.
Again, not the best user experience we have, but sorry.
So we will see the code together in the final Q&A session, so sorry, time-restrainted.
So which is our alternative?
Our alternative, there are many alternatives, obviously.
I choose a baby engine because I like it, I didn't find, again, I'm a web developer,
so for gaming stuff, I don't understand nothing.
But baby engine allow me to put something in a short time, so good stuff, guys, good
stuff team.
And support across platform as well, Windows Mac, Windows, and obviously web.
The pattern used is ACS, entity component system, entity is just an ID that you can
put on the world, and component is a tag, something you can attach to an entity, like
image, like a position, like something like that.
And system is a function that work on those stuff, can add entity, remove, add component,
remove component, move existing component, and so on.
So more or less how baby works, each frame invokes, baby runs our function, call it system,
let change, add, remove components and entities, that allow us to change our worlds, and finally
baby render some of them, obviously, on the screen.
So conceptually, what I understand in two years at night, obviously, is not my job again.
So it's almost simplest to understand.
Last stuff to introduce of baby, we have two other concept, I need to introduce event,
is a plane rest object that allow us to be fired and to be listened, so we can inform
other functions, other systems, that something happens.
And the resources is just a global instance, because system is allowed to access only on
the world, not our custom objects.
So you need to put your resources on the world and fetch it inside the system.
And a nice feature, baby tracks when a resource change, we will see later.
So how can you stem a system, for example, firing events that is listed by system two,
that on that event change accordingly a resource A, and system three, more or less, react on
that change, for example, moving the snake.
So let's have a look to a more detailed example on the code, skipping the arguments because
it's not important to understand better what I would like to show.
Firstly, because systems runs every frame, we don't want to tick on every frame.
We want to wait a timer.
So also for test proposing, this is nice.
So we need to wait a tick event.
So only when the tick event is fired, we call tick method on game.
Game is on the third arguments that the game resources, obviously, is the game we saw before.
After that, we get the snapshot, check if the game over is, we are in game over.
If yes, we send, we fire a game over event.
After that, we update the snake position, update the resource score, update the food,
and the duration of the timer because, you know, the period can change.
The nice stuff to be focused on is the if because we don't want to change if the real
value is changed.
Bevy leveraged on the ref mute trait.
So it is important to not the ref mute before the real change.
So have a look at a quick demo, after the demo, we can see the code, I promise.
So again, the demo, the demo, I propose you to show the native, the native part.
So cargo run, blah, blah, blah.
Okay, this is our windows that is created natively.
Okay, again, I can choose snake one as the shoe with button this time.
Thanks.
And again, it's not my job, but this is what I implemented.
So as you can see, under the hood, there is some locks, and in front of you should be
at least the snake that runs through the table.
And this is the way I handle the game over.
So, and obviously, quit close the windows.
So, we have three different states in our game, and in our code, I treat these in three
different packages, sub packages, choose game, play game, and game over.
And as you probably understand, we can leverage on event system to bring the user from one
state to another one.
So let's focus on the play state because probably is the most important one.
So what we need to do in the play state, probably we need to, surely when we enter in that state,
we create the resource, the dedicated resource, and make the initial draw.
After that, we already saw we need to wait the tick event called tick game methods, update
the position, update the food position, and the score number.
And surely we do not forget them handling the press key and the game tick.
So these are the last slides, after that we will see the output and the code.
So the graphical representation, we have in red the systems, so the function, handle
keyboard input that update the direction resource.
We will see it before, when I press the key, the direction resource changes.
Send game tick is the function that wait X seconds.
So after X seconds sends game tick event, listed by tick system, that update after calling
tick method on the game, all the resources.
Because the resource changes, I can update accordingly the score, the snake and the food.
Why I structure like that?
Because the last three systems I mentioned, the score, the snake, the foods, can be parallelized
by bevy.
Bevy has a parallelization system that allows you to automatically parallelize the system
if he understood that is parallelizable.
For example, it does not access a mutable way on the same stuff.
So show me the code, but probably show me the result also.
If you want, you want, okay, okay.
So I built it in release mode, and this is important, refresh the page, okay.
As you can see here, we have 60 megabytes, not kilo, mega, but not in release mode.
This heavier up to 70 mega, if I remember correctly, so crazy.
Take one, obviously, are the same user experience, the same user experience, and as you can see
here, there is the logs also.
Nice feature is that he also linked to the particular lines, and this is amazing, at
least from my point of view.
So let's dig into the code.
So we have time.
Apparently, yes.
So here we have the handmade snake.
I remember that this is just a wrapper around our core implementation.
As you can see here, there is a JSS that is a JavaScript API from the WebAssembly package
we described before.
The other part is just merely the tracing, for example, a different allocation.
The second dependency allows us to print a message on panic, for example, and the first
is the waspingen.
So because I don't lie, not now, at least, here we have all the bingen attributes.
With all the enumeration, the structures, and so on.
And here, under this folder, we have the classic webpack, the webpack front-end stuff.
I really don't know what is this.
For building it, I use a webpack that allows us to translate rust in wasm, and used in
the handmade package.
Instead, the baby snake is built using a truck that allows us to somehow transform all the
rust plus index.html into a web application directly.
And if you are questioning how it works, why we made handmade snake and baby snake, which
is the main difference under the hood, the answer is this.
This is the public repository on GitHub.
And here, as you can see, there is a web system, another API that allows us to interact
with DOM world.
So at the rust side, we can change the canvas.
Because under the hood, there is the canvas, it's displayed inside the canvas.
So more or less, I have done.
Thank you.
If there is any questions, I will be happy.
Be kind.
We have about five minutes for questions.
Be kind.
We have a show of hands for questions.
Have you ever played around with much more entities like 100,000 or 1 million entities?
Good question.
No, I didn't.
I know that the limitation here is the thread number.
We have in JavaScript, we have in browser.
If you don't use WebWorker, for example, you don't able to scale on this part, baby
is not using WebWorker, at least for the time being, so he is not able to parallelize.
And for this reason, probably, you can find a limitation.
There is no internet, but in the baby engine website, there is a dedicated example.
Also Shippe has a wasm, so you can find it and give me the answer, please.
Thanks.
Thank you very much.