• Register

Help Patchman rescue the Sheeple from enslavement by the Drone army!

Post feature Report RSS Designing the Drones

Debugging, developing, and duplicating these adorable robots.

Posted by on


The primary enemies in our game that Patchman faces are robotic drones, programmed to protect and guard the Sheeple. Why are they gaurding them? The Sheeple are their energy source, obviously!

While there are a number of high-level psychological tactics that are used to keep the Sheeple in line, without resorting to force, we won't be discussing them here.

The Drones were programmed over five major iterations, each of which successively added more abstraction and overall robustness. For example, in the first iteration, the Drones were only able to target Patchman, and alert other Drones. After a round of abstraction, the Drones are able to target any object, such as a decoy blow-up doll of Patchman, and alert other Drones to that blow-up doll's presence, and shoot at it violently until it is successfully eliminated. Drones REALLY don't like blow-up dolls, but if they see a moving Patchman they'll figure out what's what and go after the moving target instead.


This is all predicated on the ye-old basic desires system. Desires increase in rank depending on various factors, and the top desire becomes the dominant goal. Whereas the Sheeple have three levels of goal planning, the Drones have only two levels: goal and action. The reason being is that the Drones really aren't that complex compared to the Sheeple, even though the Sheeple *seem* to be doing less-complex behaviors. Hunting, pursuit, patrolling, investigating, and offensive things of that nature are not rocket science. It's hard work but easy plans. Vice versa for the Sheeple, who have easy work but hard plans, deciding whether to watching the Repeater in order to get Symbols for the Vending machine and ultimately solve their hunger problem.

What follows below is some sample Lua code, that has been heavily pruned so that it might fit in this article and be more readable as pseudo-code:

lua code:
--------------------------------------------
-- AI --------------------------------------
--------------------------------------------
function drone:ai()
  -- =====================================
  -- A) KNOWLEDGE / INIT
  -- =====================================
  local x,y = drone.body:getWorldCenter()
  local know = drone.know or {}
  -- =====================================
  -- A1) TARGETS
  -- =====================================
  drone.visual = false
  -- 1 -> Patchman Target
  drone:look_for_patchman()
  -- 2 -> Alert Target
  if know['alert'] then drone:alert_target() end
  -- 3 -> Interests Target
  if not drone.visual and not drone.short_circuit then drone:look_for_interests(200) end
  -- 4 -> Area Targets
  if not drone.know['visual'] then drone:look_in_area() end
  -- 5 -> remove dead targets
  for object,target in pairs(drone.targets) do end
  -- look for top available targets
  local top_target = false
  local top_target_value = 0
  for object,target in pairs(drone.targets) do
    local value = target['value']
    if target['visual'] or target['alert'] then
      if value > top_target_value then top_target = object; top_target_value = value end
    end
  end
  local new_target = (top_target ~= drone.current_target)
 
  -- =====================================
  -- B) DESIRES
  -- =====================================
  local desires = {}
  -- basic patrol desire
  desires[500] = {'patrol'}
  -- targets
  for object,target in pairs(drone.targets) do
    desires[target['value']] = {'pursuit',object}
  end
  -- =====================================
  -- B2) GOAL CHECK -> ADDITIONAL DESIRES
  -- =====================================
  if drone.goal == 'track' then
    if drone.current_target then
      if drone.travel then
        desires[900] = {'track', drone.current_target}
      else
        desires[900] = {'hunt', drone.current_target}
      end
    end
  end
  -- =====================================
  -- B3) TOP DESIRE -> GOAL
  -- =====================================
  for value,data in pairs(desires) do
    if value > top_desire_value then top_desire = desire; top_desire_value = value end
  end
  local goal = top_desire
  local new_goal = (goal~=prev_goal) or drone.goal_fail or drone.goal_success
  local prev_plan = drone.plan
  if new_goal then
    drone.plan = false
  end

  -- =====================================
  -- C) GOAL -> PLAN
  -- =====================================
  -- PURSUIT: follow and attack visible target
  if goal == 'pursuit' then
  -- PATROL: move around waypoints
  elseif goal == 'patrol' then
  -- TRACK: go to lost target prediction coords
  elseif goal == 'track' then
  -- INVESTIGATE: go to alert location
  elseif goal == 'investigate' then
  -- HUNT: search area
  elseif goal == 'hunt' then
  -- ENERGY: go to charge station, wait to charge, recharge
  elseif goal == 'energy' then
  -- TWIRL: twirl around
  elseif goal == 'twirl' then
  -- RANDOM: random movement when stuck
  elseif goal == 'random' then
  end
  -- finish
  drone.goal = goal
 
  -- =====================================
  -- D) ATTACK
  -- =====================================
  drone.attack = false
  if drone.current_target then
    local target = drone.targets[drone.current_target]
    if target['visual'] then drone.attack = true end
  end
end

--------------------------------------------
-- STEP ------------------------------------
--------------------------------------------
function drone:step()
    Map:CalculateIsometricPriority(self)
    local x, y = drone.body:getWorldCenter()
  -- =====================================
  -- A0) MODIFIERS
  -- =====================================
  if drone.electrical_damage > 0 then
    drone.electrical_damage = drone.electrical_damage - 1
  end
  -- =====================================
  -- PLAN -> ACTION
  -- =====================================
  if drone.plan == 'follow' then
  -- STATION: turn to face target
  elseif drone.plan == 'station' then
  -- TWIRL: turn around
  elseif drone.plan == 'twirl' then
  end
  -- =====================================
  -- TRAVEL
  -- =====================================
    if drone.travel and not drone.travel_fail then
  end
  -- =====================================
  -- E) AVOID
  -- =====================================
    if drone.travel then
  end
  -- =====================================
  -- ATTACK
  -- =====================================
    if drone.attack then
    end
end

The ":ai()" routine is called only on one object per-frame in a round-robin fashion to avoid the simulation slowing down. If a faster platform is detected, then more ai routines will be called every frame. The ":step()" routine is called once-per-frame for all objects.

We hope you learned *something* about game development here, if not, maybe inspired you to see that it is, in some ways, easier and more accessible than you might expect. AI can be very easy to imagine but very difficult to implement because debugging AI is a tough problem when it fails to work as you want it to.

If you want access to the full source code, let's make it happen. We're including a smash goal tier in our crowdfunding campaign to totally opensource our engine and beyond that, creative commons the entire game!

Stay tuned because it's happening soon!
We will launch a Kickstarter this month (February 2015).

Please subscribe, follow, like, and share:

Post a comment

Your comment will be anonymous unless you join the community. Or sign in with your social account: