Mobs

How can I make a mob of enemies that shoot projectiles at their nearest player?
To make mobs of enemies, Spawn an entity to represent each enemy,
and give it a behavior that finds the nearest player and shoots every tick.
Step 1: Spawn some mobs
In this example, our mobs of enemies are defined by a function called Mob.
We will define what Mob does in the next step.
repeat 20 {
Spawn unit {
Mob
}
}
Step 2: Make mobs attack
To make our Mob attack,
we have simply given them a behavior that runs every few ticks and finds the nearest player to shoot at.
pub fn unit.Mob() {
use body=this, radius=TileRadius, shape=Circle
let image=PickRandom(@enemy-*.png)
Body(pos=@(SignedRandom*BoundaryRadius, SignedRandom*BoundaryRadius), noRotation=true)
ImageSprite(image=)
PolygonCollider(category=Category:Mob)
DecaySpeed(1)
// Shoot towards nearest hero every so often
on Tick(10s * Random) {
let enemy = QueryNearest(filter=Category:Hero)
if !enemy { continue }
Spawn projectile {
Bullet(heading=Angle(enemy.Pos - Pos), parent=unit)
}
}
// ...
}
- QueryNearest finds the nearest player.
- The
on Tick(10s * Random)means it will rerun this block somewhere between every0sand10s, chosen randomly
Step 3: Make mobs move
In a similar way, we can make our mobs move towards the nearest player by finding the nearest player and moving a small step in that direction every tick.
// Take 50 steps towards the hero every so often
on Tick(0.5s * Random) {
let target = QueryNearest(filter=Category:Hero).Pos
if !target { continue }
let step = 0.025 * (target - Pos).Direction
repeat 50 {
Move(step)
await Tick
}
}
Full Code Listing
pub tangible category Category:Hero
pub tangible category Category:Mob
pub tangible category Category:Projectile
pub const TileRadius = 0.5
pub const Tile = 1
pub const BoundaryRadius = 12
pub game fn World.Main() {
TopContent {
InsetPanel { "Arrow keys to move, click to fire" }
}
SolidBackground(#edceb0)
Camera(@(0, 0), radius=BoundaryRadius)
SpawnEachPlayer owner {
Spawn unit {
Hero
}
}
with Tick(1s) {
if QueryAny(filter=Category:Mob) { continue }
repeat 40 {
Spawn unit {
Mob
}
}
}
}
pub fn unit.Hero([owner]) {
use body=this, radius=TileRadius, shape=Circle
Body(pos=@(0, 0), noRotation=true)
ImageSprite(@hero-*.png, radius=1.5*radius)
PolygonCollider(category=Category:Hero)
DecaySpeed(1)
on BeforePhysics {
Move(0.05 * Joystick)
}
// Shoot towards pointer on click
on ButtonDown(Click) {
Spawn projectile {
Bullet(heading=Angle(Pointer - Pos), parent=unit)
}
}
}
pub fn unit.Mob() {
use body=this, radius=TileRadius, shape=Circle
let image=PickRandom(@enemy-*.png)
Body(pos=@(SignedRandom*BoundaryRadius, SignedRandom*BoundaryRadius), noRotation=true)
ImageSprite(image=, radius=1.5*radius)
PolygonCollider(category=Category:Mob)
DecaySpeed(1)
// Shoot towards nearest hero every so often
on Tick(10s * Random) {
let enemy = QueryNearest(filter=Category:Hero)
if !enemy { continue }
Spawn projectile {
Bullet(heading=Angle(enemy.Pos - Pos), parent=unit, owner=null)
}
}
// Take 50 steps towards the hero every so often
on Tick(0.5s * Random) {
let target = QueryNearest(filter=Category:Hero).Pos
if !target { continue }
let step = 0.025 * (target - Pos).Direction
repeat 50 {
Move(step)
await Tick
}
}
on BeforeCollide that {
if that.Category.Overlaps(Category:Projectile) && that.Owner {
Expire
}
}
}
// Bullet flies towards a target with a speed of 30
pub fn projectile.Bullet(heading, parent, [owner]) {
use body=this, radius=0.1, shape=Circle
Body(pos=parent.Pos, velocity=5*heading.Direction, heading=velocity.Angle)
ImageSprite(@bullet.png, radius=1.5*radius, angleOffset=0.25rev)
PolygonCollider(
category=Category:Projectile, parent=,
collideWith=(Category:Tangible ^ Category:Projectile),
)
on BeforeCollide that { Expire }
once Tick(5s) { Expire }
}