Scalable Approaches to Collision Resolution.
Posted: Mon Oct 02, 2023 12:44 am
In the evolution of my project, more and more objects are interacting and colliding with each other. This is presenting an interesting "philosophical" (architectural?) problem regarding collisions which I can't quite figure out a clean solution to. Not so much a technical "how-to" question, which is why I've chosen to post this in General. Just for the record, I'm using HardonCollider for detection.
A basic description of the problem. Lets say I have four classes/entity types which are all capable of colliding with each other.
1. Player
2. Enemies
3. Projectiles
4. Environment (boundaries, walls, obstacles etc)
In the simplest case of just player and enemies, I might use a simple class method like the following for resolution:
However, the player can collide with multiple objects, so now we need some kind of generic method with validation. (e.g. the player shouldn't be hit by their own projectiles)
This is all fine and well, but not only will this resolution code get increasingly complex, but we run into another, more fundamental problem of how to handle collisions from other instantiated objects. If every object has its own collision resolution method, how do we determine whose takes precedence? It makes sense to handle collisions with a Player object from the Player object's perspective, but sometimes other objects may need their own logic.
It seems like either way, the collide methods necessarily contain side effects. Either player:collidesWith() receives the enemy object and mutates its state, or enemy:collidesWith() receives the player object and mutates its state. If this is unavoidable, how do decide whose resolution function is more "important"?
I just want to quickly note that I am not dogmatic about paradigms; I generally find OOP helpful in game development, but if another approach is better suited I will take it. In prior versions of my game where my code was more procedural/functional the same problem occurs. versus or even are just varying abstractions with the same basic problem. Collision stuff just generally becomes painful with increasing entity types and interactions.
When I started out long ago, I used the bump library, and the author's approach to validation is the idea of "filters", where a collider defines predetermined responses to other colliders (almost the same thing as my class method approach). As for the precedence problem, it still isn't clear:
We can have a playerFilter and an enemyFilter and a projectileFilter but we still have to decide who handles various scenarios. Does playerFilter handle player collisions with enemies, or vice versa? Does projectileFilter handle projectile collisions with enemies and obstacles? Does obstacleFilter handle obstacle collisions with projectiles and enemies and the player?
I figured I'd throw this on the forum to see if anyone has good ideas, because maybe I'm simply thinking about this the wrong way. It's not a major hurdle; usually I just "decide" who handles what arbitrarily and move on. The only issue is, with rising project complexity, sometimes I have to hunt down who is handling which collision scenarios to alter them. I may forget that projectiles are handling collisions with enemies, or the other way around. I'd like to avoid playing "find the collision handler" as much as possible.
A basic description of the problem. Lets say I have four classes/entity types which are all capable of colliding with each other.
1. Player
2. Enemies
3. Projectiles
4. Environment (boundaries, walls, obstacles etc)
In the simplest case of just player and enemies, I might use a simple class method like the following for resolution:
Code: Select all
function Player:collide(enemy)
self.health = self.health - enemy.damage
end
Code: Select all
function Player:collidesWith(object)
--defensive programming can help somewhat
if object.isProjectile then return end --at some point we may also have to distinguish between player/enemy projectiles
--resolve
if object.isEnemy then
object.dead = true --flag the enemy as despawnable for an entity manager
self.health = self.health - object.damage
elseif object.isObstacle then
--perform whatever logic on the player's velocity, position, etc
end
end
Code: Select all
function Enemy:collidesWith(object)
if object.isEnemy then return end
if object.isProjectile then
self.health = self.health - object.damage
end
if object.isObstacle then
--vector/velocity/position transforms
end
if object.isPlayer then
--let the player handle this? Since its just an inversion of the same scenario from the player's collide method?
end
I just want to quickly note that I am not dogmatic about paradigms; I generally find OOP helpful in game development, but if another approach is better suited I will take it. In prior versions of my game where my code was more procedural/functional the same problem occurs.
Code: Select all
function playerCollidesWith(something)
Code: Select all
function enemyCollidesWith(something)
Code: Select all
function collide(object1, object2)
When I started out long ago, I used the bump library, and the author's approach to validation is the idea of "filters", where a collider defines predetermined responses to other colliders (almost the same thing as my class method approach). As for the precedence problem, it still isn't clear:
We can have a playerFilter and an enemyFilter and a projectileFilter but we still have to decide who handles various scenarios. Does playerFilter handle player collisions with enemies, or vice versa? Does projectileFilter handle projectile collisions with enemies and obstacles? Does obstacleFilter handle obstacle collisions with projectiles and enemies and the player?
I figured I'd throw this on the forum to see if anyone has good ideas, because maybe I'm simply thinking about this the wrong way. It's not a major hurdle; usually I just "decide" who handles what arbitrarily and move on. The only issue is, with rising project complexity, sometimes I have to hunt down who is handling which collision scenarios to alter them. I may forget that projectiles are handling collisions with enemies, or the other way around. I'd like to avoid playing "find the collision handler" as much as possible.