Triggering damage to enemies in a specified radius

Is there a way to get a list of all gameobjects within a certain radius of another gameobject?

Or list all game objects currently colliding with a collision object?

I’m working on a game with a “nuke” powerup and I would like it to inflict damage to all enemy gameobjects within a radius of the powerup when it’s picked up.

I’m not really sure how to accomplish that, so any ideas would be welcomed :slight_smile: !

There are several ways to do it indeed (Lua modules, message passing etc.)

Here is an approach I would consider (using Lua modules) if I had to implement such a system (maybe others would think of better options) - hope this will be clear enough to give you some leads :slight_smile:
(this is probably how I’ll implement AoE spells in my game, but not 100% sure so at this point this is just an opinion)

1/ In a shared Lua module, you have the following nuke parameters:
=> “is_nuking” boolean / set to false by default (until it’s launched)
=> the nuke world position (can be set to “empty” position until it’s launched - depends if the bomb already exists before exploding… up to you)
=> damage, radius, and any other nuke attribute (in a “constants” module or not, up to you again / depends on your game and how you structured your project)

2/ When the bomb explodes, you set the boolean to true and update the nuke position (via the nuke script).

3/ At this moment, all the enemies (that were waiting for the shared “is_nuking” to be set to true, in the enemy script) know that the bomb is exploding.

4/ In the same enemy script, each enemy checks the distance between the nuke world position (shared via the Lua module) and its own world position.

5/ If an enemy is within the explosion radius => is hit by the explosion => takes the damage
(you can even make the damage proportional to the distance etc.)



Note
If you want to be able to launch not only 1 nuke at a time but multiple nukes, then you should have a shared table, where 1 entry = 1 nuke. Each entry would contain all the values mentioned above. (boolean, position and anything you want) And the enemy would not only check 1 boolean but traverse the table, check each boolean and take the damage of any nuke set to true.*

Once a nuke is used, you need to update/clean/remove its entry in the table, but it really depends on your game works, how you manage your nukes, whether they are consumed after being used, or just reloaded etc.)

2 Likes

Hey! The ways I’ve know:

1 Default List

  • Add the enemies (during factory.create()) into a list
  • Calculate the distance between the objects (maybe vmath.length) during the explosion

2 Hitboxes

  • Create a hitbox during the explosion
  • everything it hits will get damage ( you can use a physics body)

But depends on your game. Are you making a “vampire survivor-like” game? Maybe it’s a better option using a list of enemies for a target system late on. Is it a simple top down game with fewer enemies on screen? Maybe the hitbox solution could be a good option here.
Anyway, good luck on your project.

2 Likes

Strongly recommend AABB for Vampire Survivor-like games.

3 Likes

I would personally either use an always active collision object of type trigger and keep track of all enemies that are inside.

Or use a kinematic object that you enable when the nuke power up is triggered.

2 Likes

Thanks everyone for the suggestions.

I ended up going with @britzl 's second suggestion - using a kinematic object which gets enabled when the nuke is triggered.

I’ve included some working code below, thought I would share. Any input/suggestions :slight_smile:?

It’s the code file (powerupBomb.script) attached to my powerupBomb.go.

The code works with two collision objects:

  1. #collision_powerup - the collision by the player on the powerup icon itself to trigger the explosion
  2. #collision_explosion - which is used to damage colliding enemies
local function enemy_needs_damage(self, id)
	if self.enemies == nil then
		return true
	end

	for i = 1, self.enemies_count do
		if self.enemies[i] == id then
			return false
		end
	end

	return true
end

function init(self)
	self.enemies = {}
	self.enemies_count = 0
	
	msg.post("#sprite_explosion_animation", "disable")
	msg.post("#collision_explosion", "disable")
end

function on_message(self, message_id, message, sender)
	if message_id == hash("animation_done") then
		msg.post("#sprite_explosion_animation", "disable")
		go.delete()
		
	elseif message_id == hash("explode") then
		msg.post("#collision_explosion", "enable")
		msg.post("#collision_powerup", "disable")
		msg.post("#sprite_powerup", "disable")
		
		msg.post("#sprite_explosion_animation", "enable")
		msg.post("#sprite_explosion_animation", "play_animation", { id = hash("explosion") })
		msg.post("#screenFlash", "flash")

	elseif message_id == hash("collision_response") then
		-- If an enemy collision is detected, incflict damage (only once)
		if message.own_group == hash("bomb") and message.other_group == hash("enemy") then
			if enemy_needs_damage(self, message.other_id) then
				print("Bomb - Take Damage!")
				table.insert(self.enemies, message.other_id)
				self.enemies_count = #self.enemies

				msg.post(message.other_id, "take_damage", { damage = 50 } )
			end
		end
	end
end
2 Likes