Bots
How can I make a bot that shoots projectiles at the nearest player?
Before you start, make sure you understand the difference between a bot and a mob:
-
A bot is a hero controlled by a computer, created using the SpawnBot function. The hero could have been controlled by a human player, but the computer is controlling it instead. The bot normally controls its hero by simulating inputs (button clicks, mouse movement, etc.).
-
A mob is an enemy controlled by the game. It can never be controlled by a human player. In fact, it is not a player at all, it is just an entity in the game world.
It is much more complex to implement a bot than a mob, so make sure that a bot is really what you want to implement, and not a mob. See Mobs for a simpler way to make mobs that shoot at players.
Step 1: Spawn some bots
First, use SpawnBot to spawn some bots in your game world.
You can do this in your Main function.
pub game fn World.Main() {
// ...
repeat 5 {
SpawnBot
}
}
In a real game you might want to wait until the player clicks a Play vs AI button before spawning the bots, but for this example we will just spawn them immediately.
Step 2: Give a BotSystem to your hero
When a Hero is under the control of a bot, it needs to have a BotSystem to simulate inputs for it.
We will define what a BotSystem does in the next step.
pub fn unit.Hero([owner]) {
// ...
if !IsHuman {
BotSystem
}
}
Step 3: Define the BotSystem
A simple BotSystem simply runs every few ticks, finds the nearest player, and simulates a mouse click to shoot at them.
fn unit.BotSystem([owner]) {
use body=unit
on Tick(0.5s) {
let enemy = QueryNearest(filter=Category:Hero, against=Alliance:Enemy)
if !enemy { continue }
Pointer = enemy.Pos
ButtonDown(Click)
ButtonUp(Click)
}
// ...
}
QueryNearestis used to find the nearest enemy player. As the nearestHerois always going to be itself, notice how we useagainst=Alliance:Enemyto only find other heroes.Pointeris used to aim at the enemy player's position.ButtonDown(Click)andButtonUp(Click)simulate a mouse click to shoot at the enemy. Both theButtonDownandButtonUpare needed to simulate a full click, withoutButtonUp, the bot would just hold down the mouse button and it would never shoot again.
Full Code Listing
pub tangible category Category:Hero
pub tangible category Category:Projectile
pub game fn World.Main() {
SpawnEachPlayer owner {
Spawn unit {
Hero
}
}
repeat 5 {
SpawnBot
}
}
pub fn unit.Hero([owner]) {
use body=this, radius=1, shape=Circle
Body(pos=20*RandomVector)
PolygonSprite(ownerColor=true)
PolygonCollider(category=Category:Hero)
DecaySpeed(1)
on BeforePhysics {
Move(0.25 * Joystick)
}
on ButtonDown(Click) {
Spawn projectile {
Bullet(heading=Angle(Pointer - Pos), parent=unit)
}
}
if !IsHuman {
BotSystem
}
}
fn unit.BotSystem([owner]) {
use body=unit
on Tick(0.5s) {
let enemy = QueryNearest(filter=Category:Hero, against=Alliance:Enemy)
if !enemy { continue }
Pointer = enemy.Pos
ButtonDown(Click)
ButtonUp(Click)
}
on Tick(0.25s) {
Joystick = RandomVector
}
}
pub fn projectile.Bullet(heading, parent, [owner]) {
use body=this, radius=0.25, shape=Circle
Body(pos=parent.Pos, velocity=30*heading.Direction)
PolygonSprite(ownerColor=true)
PolygonCollider(category=Category:Projectile, parent=)
on BeforeCollide that {
Expire
}
once Tick(1.5s) { Expire }
}