Our First System
In this tutorial, we will create our first
engo. We will also be using basic input methods such as the
mouse and keyboard.
Remember what we did in the last tutorial?
We created a
Scenewhich renders a huge City icon onto the screen.
The final code for tutorial 2 is available here at GitHub.
Step 1: The Concept
Before creating any
System, it is often good to ask yourself: what is the purpose (or task) of this
we are building a traffic-managing game which has multiple cities, it would make sense to create a system which creates
To keep it simple for the sake of this tutorial, we shall create a
System which creates a new City at the location
of the cursor, whenever someone presses F1.
Step 2: Defining and Adding our System
Defining the System
Before adding any functions, let’s start by creating a dummy
System and adding it to our game.
System is anything that implements all these functions:
Let’s start off with a very simple implementation of this interface:
CityBuildingSystem builds Cities on top of the cursor. This requires use of the keyboard and mouse. At first,
we don’t need to worry about entities, so we’ll ignore the
Remove function for now. We haven’t added any entities,
so we can safely ignore removing them.
Let’s keep code quality in mind, and create a separate package for our Systems, called
systems. This means we create
$GOPATH/github.com/EngoEngine/TrafficManager/systems. Within this, we create a new file, with the
exact contents of the
CityBuildingSystem described above. However, we do add
package systems to the top of the file.
Adding the System
Now that we’ve created the
System, let’s add it to our game. The usual way to add
Systems to a game, is by doing
it within the
Setup function of the
Scene you’re going to use. Since we don’t want this to interfere with the
big City icon we created in the previous tutorial, we are not only going to add the
System, but also remove
Entity we created in the last tutorial.
How do we know it works?
At the moment, we can’t be sure it actually works, right? Each system can optionally implement
New(*ecs.World), which will be called whenever the System is added to the scene.
Let’s create a
Newfunction for our
When we now run our game, we can see the white background, no City icon (because we removed it), and the console outputs “CityBuildingSystem was added to the Scene”.
Step 3: Figuring out when F1 is pressed
We want to spawn a City whenever someone presses F1, so it makes sense that we want to know when this happens.
First, we need to tell the Engo to listen for the F1 key press. We’ll do this by using the
engo.Input.RegisterButton(name string, keys Key...) function. Multiple keys can be assigned to one identifier.
Add the following line to the
Setup function for your
We will check “did the gamer press F1”, on every frame. The
Update function of our
gets called every frame by the
World. So our checking-code needs to be written there. Engo has a neat feature which
allows you to lookup the state of keys, such as:
Down(): when the button is pressed; will be true as long as the user holds the button,
JustPressed(): when the button was just pressed; will be true for one frame, and cannot become true again unless the button was released first,
JustReleased(): same as
JustPressed(), but with releasing the button instead of pressing it,
We don’t want to risk placing two cities on top of each other in a very short time period (it’s very likely that the
key is pressed longer than one frame, because a frame usually lasts between 16.6ms and 6.94ms). Therefore, we shall use
How do we know it works?
Let’s run the game, and press F1 and find out! It should print “The gamer pressed F1” just once, when we hold the F1-key. It should print it twice when we double-tap it. It shouldn’t print it as long as we don’t press F1.
Spawning a City
Now we know when to spawn a City, we can actually write the code to do so.
Remember the code we used for the large City-icon we removed?
There are a few things we want to change, before using this in our
- The size: 303 by 641 is a bit too big. Let’s change this to 30 by 64.
- The location: we want to spawn it at the location of the cursor
worldis unknown in the
Updatefunction, so we have to work around that
As stated, we can easily change the size, by changing the numbers. However, changing the values at the
SpaceComponent isn’t enough. The texture is still way too big; we can change this by setting the scale to
In order to spawn them at the correct location, we need to know where the cursor is. Our first guess might be to use
engo.Input.Mouse struct which is available. However, this one returns the actual
(X, Y) location relative to the
screen size, not the in-game grid system. We have a special
MouseSystem available for just that.
The first thing you want to do, is add the
MouseSystem to your
Note that we added the other systems before the
CityBuildingSystem. This is to ensure any systems we might depend
upon, are already initialized when we’re initializing the
MouseSystem is mainly written to keep track of mouse-events for entities; you can check whether your
has been hovered, clicked, dragged, etc. In order to use it, we therefore need an
Entity which uses the
This one needs to hold a
MouseComponent, at which the results/data will be saved.
We first will update our
CityBuildingSystem to contain the new
Then, we want to ensure this
mouseTracker entity gets initialized and added to the
World whenever we start using
Note that we added the
Track: true variable to the
MouseComponent. This allows us to know the position of the
mouse, regardless of where it is. If we were to leave it at
false (the default), it would only contain anything
useful if the mouse was hovering the
Entity. We are also giving two parameters
nil within the
MouseSystem. That is because (as described in the documentation of that
Add function), one can also provide
RenderComponent, if one wants to know specifics about that particular entity (like
hover-events). This is not our intention at the moment, we just want to know about the position of the cursor. Therefore
we can safely pass
nil for those two parameters.
Getting information from the MouseSystem
MouseSystem updates the
mouseTracker.MouseComponent every frame. Everything we need to know, is in there.
But we still don’t have a
world reference within our
Update function. We are given one inside our
so let’s save that reference.
Now, we can reference it by saying
cb.world, within our
Now, if we were to run this whole project, and place a few cities using the F1-key, we would get something like this: