[quote="Barri167":2mh8izmo]I do have a question though, may I see how you did the AI? I can't wrap my head around how to do them half as well as you have them.[/quote:2mh8izmo]
Sure, no problem.
Each of the enemies has six parts:
1. The enemy sprite
2. The hitbox (has platform behavior on it)
3. A detector for walls (the black and yellow striped sprite)
4. A detector for the floor
5. An enemy locator (the M is for 'monster')
6. A separate death sprite
The first five items are all in a container together. The only thing not contained is the death sprite. This is so I can show a dead body on the screen, but still have all the unecessary parts destroyed when the enemy is killed.
When I build my level in the Construct layout editor, I don't place enemies. Instead, there's a seventh sprite that spawns everything. It looks like this:
This is the object I copy/paste around the level where I want my enemies to be. Everything else gets a checkmark next to "Destroy on startup."
At the beginning of the layout I spawn all the enemies:
[code:2mh8izmo]+System: For each enemySpawner
-> enemySpawner: Spawn object enemyBox on layer "actionLayer" (image point 0)
-> enemyBox: Start ignoring user input
-> mLocator: Set position to object enemyBox (image point 0)
-> enemySpawner: Destroy
This creates all the parts needed to run the enemies, and puts the hitbox and locator where the spawning object was. Then it destroys the spawner.
The sprite and detectors get placed on top of the hitbox every cycle, so there's no need to place them here.
Now for the AI:
Each enemy has a variable called "active" that is defaulted to 0. When the enemy is on the screen, this is set to 1. It stays at 1 until either the enemy or the player dies, so the monster can chase you from screen to screen when it sees you.
For the movement, each enemy has the following variables: mySpeed, lastX, lastY. This sets up the speed (capBox is the player hitbox):
+Every 125 MS
-> enemyBox: Add 1 to 'speedTime'
+enemyBox: Value 'active' Equal to 1
+enemyBox: X Greater than capBox .X
+enemyBox: Value 'mySpeed' Greater than -4
-> enemyBox: Subtract 1 from 'mySpeed'
+enemyBox: X Less than capBox .X
+enemyBox: Value 'mySpeed' Less than 4
-> enemyBox: Add 1 to 'mySpeed'[/code:2mh8izmo]
The speed can go from -4 (full speed left) to 4 (full speed right). Notice that we're not setting the speed of the platform behavior directly based on where capBox is, but rather just adding or subtracting a variable. This is so the monster doesn't turn around immediately when you jump over the top. If the monster changes it's X in relation to the player, it takes one full second for the monster to turn around and gain full speed (every 125 ms add 1 from -4 to +4).
But we still haven't actually set the speed yet. There's another variable called "speedMod" that randomizes to give each enemy a slightly different speed, so they don't all bunch together:
[code:2mh8izmo]-> enemyBox Platform: Set speed to (enemyBox.Value('speedMod')+25)*enemyBox.Value('mySpeed')[/code:2mh8izmo]
And this is how we get the speedMod: See the 'speedTime' variable up there? When it reaches 5 this happens:
[code:2mh8izmo]+enemyBox: Value 'speedTime' Equal to 5
-> enemyBox: Set 'speedMod' to random(27)
-> enemyBox: Set 'speedTime' to 0[/code:2mh8izmo]
Dang, that's a lot of info just for one post. We're about halfway done. Here's how to set the direction of the sprite:
[code:2mh8izmo]+enemyBox: X Greater than enemyBox.Value('lastX')
-> monsterSprite: Set angle to 0
+enemyBox: X Less than enemyBox.Value('lastX')
-> monsterSprite: Set angle to 180
+System: Always (every tick)
-> enemyBox: Set 'lastX' to .X
-> enemyBox: Set 'lastY' to .Y[/code:2mh8izmo]
As you might guess, 'lastY' is for setting the jumping and falling animations. I'm sure that's pretty self explanatory. Speaking of jumping, here's how they do it:
+enemyBox: Value 'active' Equal to 1
+enemyBox Platform: is on ground
+enemyWallDetector: overlaps Family Terrain
+System: random(20) Equal to 4
-> enemyBox Platform: Jump
I added in the randomizer so that they don't just immediately jump when they hit the wall. Like the change in speed and direction I wanted it to look a little more natural, like they were thinking about it or something.
Resetting and killing enemies:
When the player dies, his position is set to the last eye stone that he touched. At the same time, there's a loop that sets each enemy to the mLocator sprite in it's container (that was set back when they spawned at Start of Layout). Then the 'active' value for enemyBox is set to 0 again so they don't just run around the level willy-nilly.
When they're killed, the blinking "hit" animation is played. When the animation is finished (and only if the enemy is on the ground) then a dead monster sprite is spawned and the enemyBox is destroyed. Destroying the enemyBox destroys all the other objects in the container. And that's about it.
I didn't cover animations really, so if you want to know how that's done let me know. Though I will be putting this kind of information in my Platform School tutorial, so if you can wait until then that's cool too.