Decoupling physics

Started by
6 comments, last by Norman Barrows 9 years, 4 months ago

I'm having a problem with my current game architecture design; I'm using the ECS pattern but I'm still having trouble decoupling some of the physics code. Especially in case the player wants to place a block somewhere: this gameplay-related code needs to cast a ray, and aside from that check if that position is free. First, I had casted rays by emitting events, and letting the physics system handle them and invoke a callback function. But since the gameplay code now needs to check whether a position is free, it would need to emit a second event. This method already felt hacky and slow, but better than direct access.

I'm curious to how this is usually handled. I know that you can cast a ray from anywhere when using Unity, but I doubt this is the correct approach. How have you handled this kind of problem?

Advertisement

Why are you doubting that this is the correct approach?

The physics systems acts as a service in this case and the other systems are consumers.

Events or not, your picking system is dependent on the physics system either way, as without it your code would not work.

To add a bit to Madhed's post:


this gameplay-related code needs to cast a ray, and aside from that check if that position is free.

First: it's not clear how (or whether) you've separated collision detection from physics response. That is, why is the determination that a position is "free" not done by an entity attempting to move?


I had casted rays by emitting events, and letting the physics system handle them

Given no information on what "entities" or "positions" are, that's a common approach. Detect collisions (or lack thereof) and let physics determine responses.

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.

You don't forget how to play when you grow old; you grow old when you forget how to play.

"player wants to place a block somewhere"

i assume you mean in a minecraft type game, as opposed to simple entity movement.

i personally prefer non-deferred processing.

"player wants to place a block somewhere"

so your in the process_input part of the game loop.

you need a raycast with collision check that returns space_is_empty: true | false.

if space_is_empty, add_block.

so your parts are a raycaster, a collision checker, and an add_block routine.

" I had casted rays by emitting events, and letting the physics system handle them and invoke a callback function.... This method already felt hacky and slow, but better than direct access."

sounds rather hacky and slow. think of all the unnecessary typing and processor cycles involved.

the non-deferred way:

process_input gets an "addblock" input. calls process_addblock.

process_addblock is the controlling code. it calls the raycaster. raycaster calls collision checker repeatedly as it steps though the scene. raycaster returns intersection point to process_addblock. process_addblock tests intersection point to determine if space_is_empty. if space_is empty, process_addblock calls addblock routine.

after all that input processing continues with the next input tested for or queued.

so your modules are:

process_input

process_addblock

raycaster

collision checker

addblock routine

the collision checker can be part of the physics engine or you can code a simple dedicated raycast and intersection check specifically for the task (often simpler).

Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php

Why are you doubting that this is the correct approach?

The physics systems acts as a service in this case and the other systems are consumers.

Events or not, your picking system is dependent on the physics system either way, as without it your code would not work.

The extra function calls and callback functions add additional overhead and indirection. And in this situation you would be able to replace the physics system, without changing anything to the side.

First: it's not clear how (or whether) you've separated collision detection from physics response. That is, why is the determination that a position is "free" not done by an entity attempting to move?

Collision detection while moving is done by the physics system. The logic code simply sets a velocity and forgets about it. Preventing an object from moving when there is a block in its face is something else than casting a ray, where you need the result instantly to perform a specific action.

Given no information on what "entities" or "positions" are, that's a common approach. Detect collisions (or lack thereof) and let physics determine responses.

I should have given more information; the physics system calculates if and where the ray hit, and then invokes a callback function. I didn't think it would be a good idea to let the physics system handle placing blocks.

...

Thanks for the suggestion! My problem is however, that I want to decouple my code as much as possible. If the logic code needs references to the world etc. to cast rays, it will be harder to maintain, I can't switch out the physics system, and the logic code does things it shouldn't do.


I didn't think it would be a good idea to let the physics system handle placing blocks.

Sorry. I didn't understand the emphasis of the OP as placing blocks.

Not knowing what capabilities your "entities" have, and IF a block is an entity, you can create a block entity (perhaps at a hidden, always "free" location), attempt to move it to the desired position, and, if the entity says "Can't do it," inform the user, and keep the block for later. If the block can be placed, add it to your "world," and create another "for-later-use" block at that hidden location.

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.

You don't forget how to play when you grow old; you grow old when you forget how to play.

Why are you doubting that this is the correct approach?

The physics systems acts as a service in this case and the other systems are consumers.

Events or not, your picking system is dependent on the physics system either way, as without it your code would not work.

The extra function calls and callback functions add additional overhead and indirection. And in this situation you would be able to replace the physics system, without changing anything to the side.

I was actually talking about your Unity example here and your doubting their approach.

I think the confusion here stems from the fact that you see ECS as a catch-all pattern for gaming related systems design.

The pattern originated because people were trying to find an alternative to the dilemma posed by heavy inheritance trees and the resulting god classes.

You still have plenty of different patterns at your disposal for dealing with other aspects of your game.

I would describe your situation as follows:

Physics system

responsible for maintaining the physical representation of your game. Rigid bodys, collision shapes, velocity rotation.

Maintains and encapsulates physics state, advances physics state in discrete timesteps.

Provides an interface to manipulate/query the physics state.

This interface includes components where you can apply forces/torque on single entities and a global view where you can query information about all physics components.

A raycast is just a query in this case: Return to me a list of all colliders/rigid bodies that intersect with this ray.

You can still use the interface pattern to abstract away the specific physics implementation.


If the logic code needs references to the world etc. to cast rays, it will be harder to maintain, I can't switch out the physics system, and the logic code does things it shouldn't do.

mathed's approach divvies things up rather nicely. raycast is simply a query method of the physics engine's api.

that way process_addblock, the controlling or logic code, need not know about the world representation used by the physics engine. it just calls a physics routine and gets back a result, from which it computes space_is_empty, and then possibly calls addblock depending on the value of space_is_empty.

by making raycast part of the physics api, you can swap physics engines with no change to process_addblock, assuming you use a translation layer (interface pattern?) between your code and the swappable libraries.

when re-organizing api's like this results in such lucid code as the example above, you're pretty much guaranteed to be on the right track as to how things should be organized and what should be a part of which API. IE if an API change results in godlike simplicity and clarity, you're doing something right - VERY right.

Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php

This topic is closed to new replies.

Advertisement