godotengine / godot-proposals

Godot Improvement Proposals (GIPs)
MIT License
1.14k stars 93 forks source link

Add collision signals for KinematicBody #1063

Open DoctorWhoof opened 4 years ago

DoctorWhoof commented 4 years ago

Describe the project you are working on: A 3D top down Action-RPG. There are lots of enemies on screen around the player, all of them driven by a KinematicBody. The collision information from those bodies is used to inflict damage from contact (collision with player and with other characters) and determine the type of terrain the characters are in (collision with terrain). Graphics are minimalist, so GPU is not a bottleneck.

Describe the problem or limitation you are having in your project: The amount of processing done in GDScript per frame, per character, is causing CPU use to be high on the target hardware (5 year old laptop or low end PC) when there are large groups of characters. While some of that performance bottleneck is coming from the collision detection itself, the amount of GDScript code processed per frame could be reduced if the engine provided a few extra KinematicBody signals.

Describe the feature / enhancement and how it helps to overcome the problem or limitation: I propose minimizing the amount of processing that needs to be done per character per frame by implementing collision signals in KinematicBody at the engine level. Currently I need to keep arrays tracking the current collisions and the collisions the occurred on the last frame, and then compare the two on every frame to figure out if a collision is "new", to only then emit a "collision_started" signal. If that processing happened on the engine side it would be much more efficient, and my GDSCript code would only "kick in" when a new collision actually occurred.

Describe how your proposal will work, with code, pseudocode, mockups, and/or diagrams: Just like Area has "area_entered" style signals, a KinematicBody should have these signals: collision_started(body) collision_stayed(body) collision_exited(body) A KinematicBody could connect those signals to itself on _ready, and execute just a minimal amount of code that actually handles the gameplay logic. Not only it would reduce per-frame script processing, it would make the KinematicBody signals more consistent with the Area signals, and more consistent with Godot's signal-oriented design.

If this enhancement will not be used often, can it be worked around with a few lines of script?: I currently have those signals generated via GDScript. It works, but it's CPU hungry with a lot of characters.

Is there a reason why this should be core and not an add-on in the asset library?: Collision signals seem to fit really well into Godot's Signal oriented philosophy. It would be consistent with how Area signals work. They are also very common in other engines (i.e. Unity has virtual methods like "OnCollisionEntered", etc) and having a direct equivalent in Godot can lower the entry barrier.

KoBeWi commented 4 years ago

What do you use to move the kinematic bodies? Both move_and_collide and move_and_slide provide some collision information.

Also this looks like perfect example of a project where GDNative would be useful.

DoctorWhoof commented 4 years ago

Move_and_slide is what I'm using right now. However, it doesn't distinguish between the start of a collision, the continuation of it and its end, which needs to be done via code.

Yes, GDNative would be perfect for this! But looking at the lots of steps required to add a simple class via GDNative, those of us who rely on GDScript feel very discouraged. Add to that how unfamiliar I am with C++ (the closest I can get is writing GLSL, which is way simpler) and it quickly goes into forbidden territory...

Adding this to the engine would make it instantly available to all users, who would be able to approach collisions in a more friendly, less code heavy, more efficient, more signal oriented way like the rest of the engine.

Thanks!

lawnjelly commented 2 years ago

I am agreeing in principle, but the only thing I can see as a snag is that with actual physics objects we might tend to get vibrations where objects are in collision one frame, then not the next, then in collision etc. So this could end up firing a lot of signals. Maybe not in your exact situation, but there may be situations where this happens.

An alternative is to make an Area as well as the KinematicBody. Maybe make the area a child of the kinematic body, and make it slightly larger so it will not suffer the vibration issue. Then use the Area signals instead.

This is slight overkill in the BVH as it is doing broadphase for both, but it should be able to handle this fine I expect unless you are doing e.g. 100,000 characters (and then you will have other problems). At a guess this will be far cheaper than trying to do this processing in gdscript.

Note that broadphase used for areas is much cheaper than "full physics" (which is broadphase + a load of bouncing etc), areas are a lot cheaper than Kinematic / Rigid bodies etc.