Advertisement

Proper Component Decoupling

Started by April 10, 2016 04:37 PM
3 comments, last by frob 8 years, 8 months ago

So recently, I've started to abandoned an inheritance heavy game structure to a more component based one. My problem is, I'm getting confused about what code belongs in which component. For example, I created a mover component to handle the basic movement of the player & enemies, here is an outline of what occurs:


class CharacterMover(abstracts.Mover):
	...

        def update(self, character, ...)
            #1. move character appropriate direction.
   
            #2. if character collides with wall or screen edge move
            #   character opposite the direction just moved -

            #3. or if character is on edge of wall move left/right
            #   to align with opening.

My question is, #2 and #3 require collision detection. Is it appropriate to have the collision detection code in a mover component given that the player is moving in response to the collision? Should all collision be set in a different component? Also, this would require the component to have knowledge about objects it could collide with and where the screen edges are. This seems to defeat the purpose of separate components. I understand there's no one right way to do something, but would like some advice on my general approach and some insight. Thanks!


this would require the component to have knowledge about objects it could collide with

The only knowledge it requires is the region that these other objects occupy. It also needs to know the region that the current object will occupy after moving. This means that collision detection is actually just a function testing for intersections of two regions of space - it's region logic.
The only purpose of a mover component is to change the object's region in a correct fashion - it's just a function modifying region - it's region logic.

So both problems are in the domain of a region component. That's probably where the logic for both should be placed, with a region component.
Advertisement

Is it appropriate to have the collision detection code in a mover component given that the player is moving in response to the collision?

Absolutely not. Throwing in the note about it being a player that is moving in response to the collision is a straw man.
#1: It’s not even a valid framing of the situation. If the player bumps a tire, the player bounces back and the tire bounces forward. Is the player moving in response to the tire, or is the tire moving in response to the player?
Collision must be handled by something that doesn’t have a frame of reference. They bounce off each other.

#2: What about when a monster hits a wall? Or a car hits a monster? Or a rock hits a car into a monster into a wall?
Are you going to duplicate your collision code for every case? Clearly collision and physics don’t belong inside an update meant for a specific type of entity.

Also, this would require the component to have knowledge about objects it could collide with and where the screen edges are.

Correct, which is another clue as to why this is not the way to go. “God” has knowledge regarding all the objects in the scene, the walls, and how they should interact. The player is one of “God’s” puppets.
The player knows about itself, and some higher-level part of the game engine knows about all objects and how they interact.


The way it should be:
#1: A high-level system reads input and applies them to the correct player. For most games this means applying a force vector on the player, but for ultra-simple games it could mean an actual change to the character’s position.
#2 Normal: For normal games that use force vectors, a high-level run over objects is done to perform collision detection and physics. This results in a new set of force vectors which have been created to keep the player out of walls. This is also where item pick-up would occur.
#2 Simple: For ultra-simple games that modify the position directly, a high-level collision-detection routine is run over all objects (this could be done in the ECS fashion) and objects are sanitized (moved out of walls) and items are gotten, bullets hit enemies, etc.
#3 Normal: For normal games that use force vectors, an ECS-style run over the objects would be implemented to update their positions based on their force vectors.

Strictly adhering to the ECS fashion of running over objects and doing some kind of update on each component will lead you to extremely poor design.
ECS itself isn’t generally the best way to go anyway, and when you do go that way you still have to think about what makes sense as a per-entity update via some updating function going over each entity in turn vs. a normal update where objects are collected via some specific method and parts of them updated there.
Even if you are using ECS you still need to handle collision/physics in the normal way of gathering the minimal set of potential colliders (usually through a spatial partitioning scheme) and handling only them.
Even if your game is as simple as Pong it doesn’t make sense to run over every entity to handle collisions. It still has to be done at a higher level where knowledge of the game rules, objects, and boundaries exists.


L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid

So currently I have a collision component that is a member of my player class. The component creates a list as a member of the player class (player.collisions) containing all objects that the player collided with. What I gather is that this is the wrong approach? Because it creates the frame of reference of the player colliding with something. Also, if this component was part of an enemy as well we could have the same collision in both the enemy and the player (in the case they collide with each other for example).

So all my collision should be detected and handled in an area that has access to all knowledge of the game world and it's entities (enough to handle collisions that is)? And only deal with potential colliders to improve performance?

And the mover component should simply only move an entity?

Also, what are good alternatives to look into besides ECS?

Thanks again.

The Single Responsibility Principle still applies.

A class should do one thing, handle a single responsibility.

It looks like you are combining:

* Storage of physics data and physics object shape

* Application of physics force by applying movement

* Collision detection between physics objects

* Collision response between physics objects

If you are using a physics driven game, usually physics systems work by adding a force or a torque or resistance to a physics object. The physics system computes all the collisions between all the systems, handles most of the physics response between all the physics objects, and provides callbacks back for physics objects if they choose to listen to certain events.

Break each responsibility out. Physics objects like a mesh or cube or sphere or capsule, with event handlers for various events. Movement controllers that apply forces to the physics objects. Physics updates that runs the physics simulation among all physics objects in the world.

This topic is closed to new replies.

Advertisement