Skip to content

Commit

Permalink
fixed up previous tutorials
Browse files Browse the repository at this point in the history
  • Loading branch information
Noofbiz committed May 30, 2018
1 parent b10d77e commit 6b6b57a
Show file tree
Hide file tree
Showing 5 changed files with 251 additions and 112 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
_site
.sass-cache
.jekyll-metadata
Gemfile
Gemfile.lock
46 changes: 22 additions & 24 deletions tutorials/_posts/2016-04-08-foreword.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ number: 0
>
> * An overview of what the tutorial series will be all about;
> * A description of the game we're building during this tutorial series;
> * Information about how to set up your development environment.
> * Information about how to set up your development environment.
### Tutorial Series

Expand All @@ -23,14 +23,14 @@ our way up to a fully-playable game. We will cover topics as:
* Adding **sounds effects** and **ambiance music** to the game
* And probably much more!

In case something wasn't covered that you would very much like explained, drop us a comment at
[Gitter](https://gitter.im/EngoEngine/engo), or [create an issue at
In case something wasn't covered that you would very much like explained, drop us a comment at
[Gitter](https://gitter.im/EngoEngine/engo), or [create an issue at
GitHub](https://github.com/EngoEngine/engoengine.github.io/issues/new) to request improvements of this tutorial series.

### The Game
The game we will be building, looks a lot like [Mini Metro](http://store.steampowered.com/app/287980/). In that game,
The game we will be building, looks a lot like [Mini Metro](http://store.steampowered.com/app/287980/). In that game,
cities are popping up which you will have to connect with metro lines. More and more people will want to travel from
various cities, making the game harder and harder, until the travelers have to wait too long and it's game over.
various cities, making the game harder and harder, until the travelers have to wait too long and it's game over.

In our game, we won't be connecting travelers and cities with metro lines. Instead, we're building highways. This has
quite some interesting features:
Expand All @@ -39,49 +39,47 @@ quite some interesting features:
* You may want to add public transportation, i.e. buses, for them to use instead of the car;
* Trucks and vans might slow down the transportation network, as they usually tend to drive at a slower pace;
* Make up your own laws when it comes to taxes (gas, mileage, etc.), but also when it comes to how fast different
types of vehicles can drive on various lanes.
types of vehicles can drive on various lanes.
* You can connect two highways and get an intersection; there's no need for this to happen in cities.

One can think of much more interesting features, and we could fill a whole page just listing ideas, but for now, let's
teach you how to set up your own development environment.
teach you how to set up your own development environment.

### Development Environment
For the moment, you can use Windows, Linux and OSX. Windows is the poorest-supported of those
at the moment (missing [audio support](https://github.com/EngoEngine/engo/issues/174)) - but that's something we are
working on.
For the moment, you can use Windows, Linux and OSX.

> #### Build-Time Dependencies
> * Linux: `sudo apt-get install libopenal-dev libglu1-mesa-dev freeglut3-dev mesa-common-dev xorg-dev libgl1-mesa-dev`
> * Linux: `sudo apt-get install libasound2-dev libglu1-mesa-dev freeglut3-dev mesa-common-dev xorg-dev libgl1-mesa-dev git-all`
> * Windows: If you're running on Windows you'll need a gcc compiler that the go tool can use and have `gcc.exe` in your PATH environmental variable. We recommend [Mingw](http://mingw-w64.org/doku.php/start) since it has been tested.
> * OSX: No known dependencies.
> * OSX: No additional dependencies.
>
> Note that you will only need these installed when you *build* the game; you will not need these when you want to
> *run* the game. You will probably need the 'runtime' version of those libraries though (instead of the `-dev` ones),
> if you want to run the game.
In any case, you will need to [install the latest version of Go](https://golang.org/dl/). Make sure you
> if you want to run the game.
In any case, you will need to [install the latest version of Go](https://golang.org/dl/). Make sure you
correctly set your `$GOROOT` (which should point to the installation directory of Go, and your `$GOPATH`, which is
basically the root of all your Go projects. Both of these are
[environment variables](https://en.wikipedia.org/wiki/Environment_variable).
basically the root of all your Go projects. Both of these are
[environment variables](https://en.wikipedia.org/wiki/Environment_variable).

Within this `$GOPATH` directory, you will want to create a directory called `src`. What you want to do now, is open
up a terminal / command prompt, go to this location (using the `cd` command), and then type in the following Go command:
`go get -u engo.io/engo`

This command will fetch `engo` from the repository, including all its dependencies.
This command will fetch `engo` from the repository, including all its dependencies.

Now, you will want to create your own project directory for your own game. In Go, it is customary to place the source code
of these projects within code repositories, such as GitHub and Bitbucket. The location of these
of these projects within code repositories, such as GitHub and Bitbucket. The location of these
repositories is the "import path" of such a Go project. We should create this directory structure within the `src`
folder of the `$GOPATH`.
folder of the `$GOPATH`.

> #### Example
> Our GitHub username is `EngoEngine`, and our project is going to be called `TrafficManager`. We are hosting the repository on
> GitHub. This means we want to ensure that this path exists: `$GOPATH/src/github.com/EngoEngine/TrafficManager`.
> Our GitHub username is `EngoEngine`, and our project is going to be called `TrafficManager`. We are hosting the repository on
> GitHub. This means we want to ensure that this path exists: `$GOPATH/src/github.com/EngoEngine/TrafficManager`.
Within this directory, we will be creating our game. All our files will be within this directory, and we will be
Within this directory, we will be creating our game. All our files will be within this directory, and we will be
executing all `go`-commands (such as `go build` and `go run`) from this directory (in other words: this directory
is our working directory).
is our working directory).

<div class="button-group stacked">
<a class="button" href="/tutorials/01-hello-world">Continue to Tutorial 1: <i>Hello World</i> &raquo;</a>
Expand Down
84 changes: 42 additions & 42 deletions tutorials/_posts/2016-04-10-first-system.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,34 +6,34 @@ number: 2
---

In this tutorial, we will create our first `System` in `engo`. We will also be using basic input methods such as the
**mouse and keyboard**.
**mouse and keyboard**.

> #### Recap
> Remember what we did in [the last tutorial](/tutorials/01-hello-world)? <br>
> We created a `Scene` which renders a huge *City* icon onto the screen.
> We created a `Scene` which renders a huge *City* icon onto the screen.
> #### Final Code
> The final code **for tutorial 2** is available
> [here at GitHub](https://github.com/EngoEngine/TrafficManager/tree/02-first-system).
> The final code **for tutorial 2** is available
> [here at GitHub](https://github.com/EngoEngine/TrafficManager/tree/02-first-system).
### Step 1: The Concept
Before creating any `System`, it is often good to ask yourself: what is **the purpose** (or task) of this `System`? As
we are building a traffic-managing game which has multiple cities, it would make sense to create a system which creates
new cities.
new cities.

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**.
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.
Before adding any functions, let's start by creating a dummy `System` and adding it to our game.

A `System` is anything that implements all these functions:
{% highlight go %}
// System is an interface which implements an ECS-System. A System
// should iterate over its Entities on `Update`, in any way
// should iterate over its Entities on `Update`, in any way
// suitable for the current implementation.
type System interface {
// Update is ran every frame, with `dt` being the time
Expand All @@ -59,36 +59,36 @@ func (*CityBuildingSystem) Update(dt float32) {}

{% endhighlight %}

Our `CityBuildingSystem` builds *Cities* on top of the cursor. This requires use of the keyboard and mouse. At first,
Our `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
the directory `$GOPATH/github.com/EngoEngine/TrafficManager/systems`. Within this, we create a new file, with the
the directory `$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 `System`s 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
the `Entity` we created in the last tutorial.
big *City* icon we created in the previous tutorial, we are not only going to add the `System`, but also remove
the `Entity` we created in the last tutorial.

{% highlight go %}
// Setup is called before the main loop starts. It allows you
// Setup is called before the main loop starts. It allows you
// to add entities and systems to your Scene.
func (*myScene) Setup(world *ecs.World) {
common.SetBackground(color.White)
world.AddSystem(&common.RenderSystem{})

world.AddSystem(&systems.CityBuildingSystem{})
}
{% endhighlight %}

> ##### 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.
>
> scene.
>
> Let's create a `New` function for our `CityBuildingSystem` like this:
> {% highlight go %}
// New is the initialisation of the System
Expand All @@ -98,12 +98,12 @@ func (*CityBuildingSystem) New(*ecs.World) {
{% endhighlight %}

> 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"*.
> 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.
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
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 be assigned to one identifier.

Add the following line to the `Setup` function for your `Scene`.
Expand All @@ -112,7 +112,7 @@ func (*myScene) Setup(world *ecs.World) {
engo.Input.RegisterButton("AddCity", engo.KeyF1)
common.SetBackground(color.White)
world.AddSystem(&common.RenderSystem{})

world.AddSystem(&systems.CityBuildingSystem{})
}
{% endhighlight %}
Expand All @@ -121,14 +121,14 @@ We will check "did the gamer press **F1**", on every frame. The `Update` functio
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,
* `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
the `JustPressed()` function.
the `JustPressed()` function.

{% highlight go %}
// Update is ran every frame, with `dt` being the time
Expand All @@ -142,12 +142,12 @@ func (*CityBuildingSystem) Update(dt float32) {

> #### 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.
> 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.
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?
Remember the code we used for the large *City*-icon we removed?
{% highlight go %}
city := City{BasicEntity: ecs.NewBasic()}

Expand Down Expand Up @@ -177,13 +177,13 @@ for _, system := range world.Systems() {

There are a few things we want to change, before using this in our `CityBuildingSystem`:

* The size: 303 by 641 is a bit too big. Let's change this to 30 by 64.
* 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
* The `world` is unknown in the `Update` function, so we have to work around that

#### The size
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 `0.1`
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 `0.1`
instead of `1`:

{% highlight go %}
Expand All @@ -206,8 +206,8 @@ city.RenderComponent = common.NewRenderComponent(

#### The location
In order to spawn them at the correct location, we need to know where the cursor is. Our first guess might be to use
the `common.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 `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 `MouseSystem`
The first thing you want to do, is add the `MouseSystem` to your `Scene`:
Expand All @@ -220,7 +220,7 @@ func (*myGame) Setup(world *ecs.World) {

world.AddSystem(&common.RenderSystem{})
world.AddSystem(&common.MouseSystem{})

world.AddSystem(&systems.CityBuildingSystem{})
}
{% endhighlight %}
Expand All @@ -229,8 +229,8 @@ func (*myGame) Setup(world *ecs.World) {
upon, are already initialized when we're initializing the `CityBuildingSystem`.*

The `MouseSystem` is mainly written to keep track of mouse-events for entities; you can check whether your `Entity`
has been hovered, clicked, dragged, etc. In order to use it, we therefore need an `Entity` which uses the `MouseSystem`.
This one needs to hold a `MouseComponent`, at which the results/data will be saved.
has been hovered, clicked, dragged, etc. In order to use it, we therefore need an `Entity` which uses the `MouseSystem`.
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 `Entity`:

Expand All @@ -252,10 +252,10 @@ this `CityBuildingSystem`:
// New is the initialisation of the System
func (cb *CityBuildingSystem) New(w *ecs.World) {
fmt.Println("CityBuildingSystem was added to the Scene")

cb.mouseTracker.BasicEntity = ecs.NewBasic()
cb.mouseTracker.MouseComponent = common.MouseComponent{Track: true}

for _, system := range w.Systems() {
switch sys := system.(type) {
case *common.MouseSystem:
Expand All @@ -269,12 +269,12 @@ Note that we added the `Track: true` variable to the `MouseComponent`. This allo
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 `Add` function
of the `MouseSystem`. That is because (as described in the documentation of that `Add` function), one can also provide
a `SpaceComponent` and `RenderComponent`, if one wants to know specifics about that particular entity (like
a `SpaceComponent` and `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.
we can safely pass `nil` for those two parameters.

##### Getting information from the MouseSystem
The `MouseSystem` updates the `mouseTracker.MouseComponent` every frame. Everything we need to know, is in there.
The `MouseSystem` updates the `mouseTracker.MouseComponent` every frame. Everything we need to know, is in there.

{% highlight go %}
// Update is ran every frame, with `dt` being the time
Expand Down Expand Up @@ -312,7 +312,7 @@ func (cb *CityBuildingSystem) Update(dt float32) {
{% endhighlight %}

But we still don't have a `world` reference within our `Update` function. We are given one inside our `New` function,
so let's save that reference.
so let's save that reference.

{% highlight go %}
type CityBuildingSystem struct {
Expand All @@ -330,7 +330,7 @@ func (cb *CityBuildingSystem) New(w *ecs.World) {
}
{% endhighlight %}

Now, we can reference it by saying `cb.world`, within our `Update` function.
Now, we can reference it by saying `cb.world`, within our `Update` function.

Now, if we were to run this whole project, and place a few cities using the **F1**-key, we would get something like
this:
Expand Down
Loading

0 comments on commit 6b6b57a

Please sign in to comment.