In the original game the shape of the "enemies" is defined in a large class and they are created in waves. For this tutorial we keep it simpler. We have some predefined enemy arrangements that repeat themselves.
Let’s first introduce a new type Enemy
:
{% codesnippet "https://raw.githubusercontent.com/macrozone/elm-hexagon-tutorial/chapter/enemies/src/Hexagon.elm", lines="26:30", language="elm" %}{% endcodesnippet %}
-
radius: the distance of the enemies to the center
-
parts: a list of true/false that indicate where the enemy has a "hole"
We add a list of enemies to the game state as well as enemySpeed
which will increase over time.
{% codesnippet "https://raw.githubusercontent.com/macrozone/elm-hexagon-tutorial/chapter/enemies/src/Hexagon.elm", lines="31:45", language="elm" %}{% endcodesnippet %}
We also need to add new initial states for our game:
{% codesnippet "https://raw.githubusercontent.com/macrozone/elm-hexagon-tutorial/chapter/enemies/src/Hexagon.elm", lines="352:371", language="elm" %}{% endcodesnippet %}
We further define the constant values enemyThickness
, enemyDistance
, enemyInitialSpeed
and enemyAcceleration
:
{% codesnippet "https://raw.githubusercontent.com/macrozone/elm-hexagon-tutorial/chapter/enemies/src/Hexagon.elm", lines="67:70", language="elm" %}{% endcodesnippet %}
Our enemies are made out of trapezoids, so that they occupy 1-5 sectors of the hexagon. We configure one enemy with an array of booleans, where True
indicates an occupied sector and False
a hole. We define a fixed set of enemies:
{% codesnippet "https://raw.githubusercontent.com/macrozone/elm-hexagon-tutorial/chapter/enemies/src/Hexagon.elm", lines="72:82", language="elm" %}{% endcodesnippet %}
You can chose whatever configuration you like, just make sure that you keep one side open ;-)
As you may have noticed, we have only defined the parts
property of the Enemy type so far. The radius will represent the distance to the center (and therefore to the player) and we will calculate it in the update-function.
Let’s introduce two new functions in the onFrame
function that will update enemies
and enemySpeed
on the game state:
{% codesnippet "https://raw.githubusercontent.com/macrozone/elm-hexagon-tutorial/chapter/enemies/src/Hexagon.elm", lines="192:213", language="elm" %}{% endcodesnippet %}
updateEnemySpeed
will slowly increase enemySpeed
over time starting with an initial value:
{% codesnippet "https://raw.githubusercontent.com/macrozone/elm-hexagon-tutorial/chapter/enemies/src/Hexagon.elm", lines="160:163", language="elm" %}{% endcodesnippet %}
updateEnemies
is a little bit tricky. Let’s first take a look at the whole function:
{% codesnippet "https://raw.githubusercontent.com/macrozone/elm-hexagon-tutorial/chapter/enemies/src/Hexagon.elm", lines="140:158", language="elm" %}{% endcodesnippet %}
The enemies will approach the player over time. We represent this as the enemyProgress
:
{% codesnippet "https://raw.githubusercontent.com/macrozone/elm-hexagon-tutorial/chapter/enemies/src/Hexagon.elm", lines="143:143", language="elm" %}{% endcodesnippet %}
As we have only a fixed set of enemies, we need them to repeat after all have passed the player. We do this by defining a maxDistance
that is the total radius of all enemies.
{% codesnippet "https://raw.githubusercontent.com/macrozone/elm-hexagon-tutorial/chapter/enemies/src/Hexagon.elm", lines="144:145", language="elm" %}{% endcodesnippet %}
The enemies should repeat whenever enemyProgress
exceeds this maxDistance
. You see this in radiusFor
where we calculate the radius for every enemy modulo maxDistance
:
{% codesnippet "https://raw.githubusercontent.com/macrozone/elm-hexagon-tutorial/chapter/enemies/src/Hexagon.elm", lines="150:152", language="elm" %}{% endcodesnippet %}
offsetForEnemy
represents the distance for a single enemy.
{% codesnippet "https://raw.githubusercontent.com/macrozone/elm-hexagon-tutorial/chapter/enemies/src/Hexagon.elm", lines="147:148", language="elm" %}{% endcodesnippet %}
We subtract the enemyProgress
, so that value will be negative (for most enemies). But because we take the modulo maxDistance
of it, it will always be between 0 and maxDistance
and will decrease for every enemy over time.
First we need a function to create a trapezoid:
{% codesnippet "https://raw.githubusercontent.com/macrozone/elm-hexagon-tutorial/chapter/enemies/src/Hexagon.elm", lines="240:248" %}{% endcodesnippet %}
We will now use this function to create a trapezoid for every occupied part of any enemy:
{% codesnippet "https://raw.githubusercontent.com/macrozone/elm-hexagon-tutorial/chapter/enemies/src/Hexagon.elm", lines="250:262" %}{% endcodesnippet %}
This function returns a Form
that contains a group of these trapezoids. Notice how we filter out parts by creating a tuple of (index:Int, part:Bool)
, removing parts where part is False
and sending the index to makeEnemyPart
. Pure functional magic:
{% codesnippet "https://raw.githubusercontent.com/macrozone/elm-hexagon-tutorial/chapter/enemies/src/Hexagon.elm", lines="261:262", language="elm" %}{% endcodesnippet %}
The following function makeEnemies
will create a List(Form)
for every enemy:
{% codesnippet "https://raw.githubusercontent.com/macrozone/elm-hexagon-tutorial/chapter/enemies/src/Hexagon.elm", lines="264:266", language="elm" %}{% endcodesnippet %}
Finally we add this function to our view
function:
{% codesnippet "https://raw.githubusercontent.com/macrozone/elm-hexagon-tutorial/chapter/enemies/src/Hexagon.elm", lines="320:338", language="elm" %}{% endcodesnippet %}