Collisions using synchronous and asynchronous line trace and sweep trace [C++, UE5, Tutorial]

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hello everyone i have a wall here that briefly flashes red whenever something overlaps with it and a character that shoots a projectile at 500 units per second you can see that sometimes the wall flashes red and sometimes it doesn't this is because the bullet travels in a discrete steps instead of continuous motion which results in inconsistent collision detections this means that when bullet moves from a to b unreal doesn't check collisions in between a and b it only checks for collisions at b so when a small actor like a bullet moves at high velocity where it is moving more than its own size in each frame because of how collision detection works bullet misses collisions with the wall this is known as paper bullet problem and common advices given to solve this include enabling ccd for projectiles enabling physics sub stepping and increasing the frequency at which physics is solved telling unreal to check for sweep collisions when moving actors increasing the size of the colliders on bullet or thickness of the walls but these aren't perfect solutions since enabling ccd and increasing physics substrate frequency still has the same problems and comes with considerable cost on performance and changing collider size will result in better collision detection but creates lot of other bugs without solving the real issue other than performance head enabling ccd and increasing physics substrate frequency will check for collisions in between the gaps but it still is checking at discrete steps which means it can still miss collisions enabling sweep only works if the root actor is collider and only works when using set actor location node so if movement is defined by animations like slash fs1 that doesn't work i solve this using line trace and sweep check that unreal has and made it into a plugin so that i can use the plugin in all my prototypes i want to give this plugin so that you can use it in your project and you can replace your current collisions with ease i also extended the functionality to use asynchronous tracing to elevate the load from game thread which makes my game use other available cores i will leave the link to github repo where you can download the plugin and use it in the project open plugins menu available under edit and click new plugin button this brings up a wizard with predefined templates for different types of plugins choose blank plugin here you can name your plugin anything i named my plugin trace and sweep collision and press create plugin once unreal is done creating your plugin you can find the plugin in plugin browser under other category click on edit and you can change basic details of plugin like icon and description i changed my plugin category to gameplay this will create a new plugin in your project folder when you open plugins folder inside your project folder you can see that andrea created basic structures needed to create a new plugin i created a new c plus plus class that extends from scene component by default unreal selects your game module it is important to change the module to your plugin so that unreal creates this new class inside the plugin i named my class trace and sweep collision component i want to be able to control the frequency at which it checks for collisions so i created a float variable which defines the number of traces per second default value is 30 traces per second and if the value is 0 it will trace every frame i added a setter for traces per second do collision test function will contain all the logic to do collisions i set the default mobility of component to mobile in constructor and set the traces per second in begin play i also call do collision test and tick when enabled set traces per second will set the component interval so that the tick is called based on required traces per second before diving into code i want to take a moment to talk about the types of trace and sweep available in unreal line trace is when a line goes from start till the end and checks for collisions in that line line doesn't have thickness or volume sweep is when a 3d shape is used to test for collisions there are three types of shapes that can be used to test for collisions box sphere and capsule there are two ways in which these checks can be made single and multi when using single trace once first blocking hit happens it stops and returns the result it's single trace because it returns single result the first blocking head whereas in multi even after reaching a blocking hit the trace will keep on going till it reaches the end and will return all the blocking hits in its path example usage can be a normal bullet would use single trace while a sniper bullet that can penetrate multiple targets can be a multitrace hey guys the future may realize that multitrace doesn't always return blocking heads when using trace channel or collision preset it stops at first blocking head but along with blocking head it will return all the non-blocking hits in between start and the first blocking head whereas using object type it will return all the blocking hits this creates some bugs which i will discuss later in the video along with how i fix those bugs single and multitrace are available for both line trace and sweep we have to tell unreal how to detect what is blocking the trace and what isn't this can be done in three ways by trace channel or by object type or by profile these can be found in collision category of project settings where you can add new trace channels or object types or define new profiles that suits your needs everything i have shown till now are synchronous calls that means once these functions are called they will stop everything and execute the trace on game thread and return the results modern hardware can handle lot of these traces in a single frame without a problem but tracing and sweeping is costly on cpu and since they run on game thread game thread can quickly become a bottleneck as new stuff is added into the game to counter this there are asynchronous functions for all the traces and sweeps that will run on a separate thread and will return results in next frame it is important to remember that the results will always be at least one frame late we can send a delegate as a call back and wait till we get the results asynchronous trace and sweep are currently c plus plus only functions and haven't been exposed to blueprint as of making this video back in the code i made enums that represent all the different options that ryzen sweep has i also created two structs one for line trace and one for sweep i want to be able to use scene component to track the position but i also want the ability to track sockets or bones when there is skeletal mesh so i created two variables one to track scene component and other to track the socket as for shape data there is enum representing the type of shape and variables to define the selected shape and offsets to shapes so that they can be moved and rotated since unreal 4.23 version full c plus plus statements can be given to edit conditions so checks like comparing enums can be added now this makes it easy to show and hide properties based on context or other properties inside the component header i created u property for all these types as edit default only properties so that they can be editable remember to restart the editor after changes to see those changes reflected since plugins are loaded into editor during the loading time of the editor changes will not be applied during the heart reload i realize that if i add component to blueprint and restart the editor it gives me error saying unable to find the component this is because plugin module is being loaded after the game module so i changed the loading time of our plugin module to pre-default so that our plugin module will be loaded before the game module after restarting the editor i can now add the component to any blueprint you can see when i select different options the properties shown in the details panel update to only show necessary properties [Music] at this point i realized that begin overlap and end overlap delicates are inside primitive component and not in the scene component and they also take primitive component as a parameter so i change the parent class of the component from scene component to primitive component primitive component has a very handy virtual function called create scene proxy this function is used to draw outlines for shapes and meshes so we can use this function to draw debug shapes for our component so that when adding new shapes it will be easier to visualize them i looked through box component to see how it is done and did the same thing but instead of drawing one shape i loop through all the shapes and draw them with appropriate transforms that are calculated from a primitive component transform in case of line trace i draw arrows that point to the location of the scene component or selected bone if skeletal mesh now when i add shapes i can see the shapes drawn live in the viewport this will let me edit the shapes and set their locations and rotations easily clicking on any shape will select our component then i can change the position rotation and scale for each component in details panel and see it affect the shape in real time in the editor this makes it very easy to add multiple shapes and set them into correct locations and rotations with ease when there are multiple shapes i was unable to add the ability to select individual shapes and change them separately using transform tool inside the viewport but i know it is possible since it is done in spline component where you can select individual control points for flying for now this satisfy my needs so i will look through spline component and see how it's done later and if i was able to get it working i will update the code in github so that anyone who clone this repo will get the update when it's done another bug that i encountered with my code is that these shapes aren't visible when switching to wired mode now let's take a break from the code and think about implementation imagine that i have a swot that is being swung and it has to collide with the black object the red circles on the sword are either shells in components of the sword or bones that we are tracking for collision detection in this frame the second point that we are tracking hits the actor so we can broadcast the on begin overlap event in the next frame the third point is hitting but we shouldn't be broadcasting the on begin overlap because it's the same actor and the same component that overlapped and we already broadcasted the event in the previous frame solution to this would be to save the heat result from the second point hitting the object and up for every subsequent hit check if the result already exists then broadcast the event only if the result is not found in the saved hit results and the animation continues and sword leaves the bond of the actor but if player does the same thing again and the sword is overlapping again since we still have the hit result saved from before it will not trigger the on begin overlap so we need to flush the saved hit results but the question is when should they be flushed obvious answer would be when the last point leave the actor we should remove it from the saved list if we look at how line trace works when we trace from outside of the object to inside it can detect a head but doing a line trace from inside of an object outside it does not detect the head this is because traces does not work on back faces that means there is no way of knowing when we are leaving an object we can use this fact to our advantage here by reversing the direction of line trace it is no longer retraced from inside to outside but from outside to inside this means that we have to do two traces for each collision check one in forward direction from start to end to detect the sword entering an object and one trace in reverse direction from end to start to rotate sword leaving the object if you are using only one shape or one point to detect the collision then this wouldn't be a problem since there will be only one race or one sweep happening so if you only need begin overlap and don't need an overlap you can skip the second trace in reverse direction and save some performance so i will add a boolean variable called generate end overlap which when disabled doesn't do the reverse trace remember that disabling and overlap only works if you are using only one shape or one point to detect the collision a feed result doesn't have a comparison operator overloaded this means it is impossible to compare two hit results as discussed before we need to be able to save the hit results in an array and check if certain heat is already processed but since hit result doesn't have a comparison operator it is not possible to call functions like add unique or find or contains on the array so i created a wrapper struct called f hit result wrapper that has hit result and a comparison operator overloaded i also added an in-call count that i will increment every time a similar hit is made when doing the trace in forward direction and decrement it when similar hit result is made while doing the trace and reverse direction i decided that for two hit results to be considered similar the other actor other component and item index should be equal i created an area of heat result wrapper struct called overlapped results that saves hit results when for what race hit something and also added a bunch of helper functions for each type of trace that is possible in do collision function i check for the conditions and call the appropriate type of trace or sweep i am implementing the synchronous single line trace first since it is the simplest one in do synchronous line single collision test function i first create a collision query param object where i can set all the advanced conditions for the trace and also add owner as actor to ignore the start location will be the previous location where the point was during the previous test and the end will be the current location if we are tracking a scene object then i get the scene object's location or else if we are tracking a bone for the skeletal mesh then i check if the bone exists and if it does get the bones location i consider this as a valid data for tracing if scene component or bone exist then i call do line single with the data do line single will call the appropriate line trace function based on the channel type then i set the previous location to current location so that the next time we do trace it will be from the current location and there won't be any gaps in between our collision test we only have to do backward line trace if generate and overlap is enabled i am doing a multi trace for backward tracing instead of single trace and there is a reason for that let's go back to our swot example imagine that there is a small red object next to the black object that we are checking collisions for during this frame when we are doing a reverse trace of type single it will stop when it hits the red object this means we won't be getting a hit result for the black object and the counter will never decrement for the black object remember that we are getting 4 hits for the black object during a forward trace but we only get two hits for the black object during the backward trace this means that the count will never be zero and it will never fire end overlap and the black object will be saved in overlapping result forever if you look at this example where i play attack animation and test for collisions at 30 times per second there are places where the gap between two checks is huge and this situation can easily happen between those gaps to avoid this i do multitrace instead of single trace for reverse stress check so that i will always get all the heat results and can process the overlap results correctly if it's a blocking hit then i save heat results from forward trace and backward trace in separate arrays process forward trace result will loop through all the forward hit results and if count of a result is 0 that means it's a fresh hit and it will broadcast begin overlap event to our component and if generate overlap event is enabled on the other component then broadcast even to the other component too then increment the count and save this as overlapping result process backward rest result will loop through all the backward trace results and check if it already exists in the overlapping result and if it does decrement the count if the count is 0 that means all the traces that enter the object has left the object so broadcast end overlap event on our component and the other component now that we have all the helper functions set up it is just matter of calling the helper functions for other types of traces two line multi is same as do line single except during the forward race i will call multi trace instead of single trace and also send an array of f hit results instead of single hit result as a parameter for sweep i will loop through all the shape data and same as line i will call single sweep for single sweep collision test and call multi sweep for multi sweep collision test the helper function for sweep changes a little bit from the line trace helper functions i will have to create shape data based on the type of the shape since to call sweep trace i need collision shapes to send as a parameter as for asynchronous collisions since we won't get data immediately we have to create a callback delegate of type f trace delegate and use this delegate in our traces synchronous trace will have a trace delegate that represents the trace that we just requested and we have to save the handle so that when our callback delegate is called we can use the handle to know which request the callback belongs to imagine if there are multiple points or shapes to trace we will fire multiple requests for tracing we should be able to determine which request the callback is sending data about so i added variables to line and shape data structs that will hold the trace handle for asynchronous trace requests inside the trace callback we don't know which asynchronous request this callback belongs to but since callbacks gives us trace handle i try to find the trace data that has the same trace handle in the forward trace data if found then i add all the blocking hits into the forward hit result i do the same for the reverse trace 2. then we will check if all the asynchronous trace requests that we started are completed if all the trace requests fired are completed then we'll call process hit result then we set the previous trace complete to true so that the next race can start the coding part is done so all i have to do now is to replace the sphere collider in the bullet blueprint with the component that i made so inside the bullet blueprint i remove the sphere component that i used for collisions and add trace and sweep collisions then set the execution type to synchronous and collision preset to the same preset that i used for in the sphere collision then add a syn component under the trace and sweep collision component so that it will use that scene component to track the location then compile and save that is all changes i need to do inside my bullet blueprint you can see that the collisions work perfectly all the time every time the bullet passes through the wall it registers the collision without a mess i also wanted to test with multiple collisions so i duplicated the same wall four times and changed the trace type to multi-on bullet this is where i realized my misconception about multi-tracing when using trace channel or collision preset multitrace will stop at the first blocking hit and won't give results after the first blocking hit but when using object channel multitrace will return all the blocking hits in its path the video shows how when using trace channel or collision preset bullet is missing some walls compare that to using object channel where bullet is hitting all four walls all the time when i enable debug drawing i can see that the trace is going through multiple walls and when trace is using trace channel or collision preset it stops at the first blocking head whereas when i use object channel it detects hits on multiple walls even in a single trace since i am using multi trace for reverse stressing to detect leaving the object this will create bugs where on and overlap will not be fired correctly when using trace channel or collision preset i added a quick fix for this by doing reverse trace on all object channels when using trace channel or collision preset so that end overlap will be fired correctly i also wanted to aggregate all the component ticks into a single tick so i created an actor called trace and sweep collision manager and added functions to register and unregister from the manager then inside the tick of the manager i look through all the registered components and call external tick function of the component regarding tracing sweep collision component i disable the tick for the component and in begin play i register the component to the manager and in end play i unregister the component from the manager for this to work the manager has to be placed in the level [Music] i created a test scene that has 220 walls and an actor that fires 100 bullets every 0.2 seconds so that at any given second there are 500 bullets attempting to check collision with 220 walls here is a comparison when using sphere collider versus using this plugin with different settings since i am spawning and destroying 500 actors per second the frame rate is all over the place but this video already shows the performance gains from using traces instead of colliders object pulling all the bullets will help with getting consistent frame rates and give better performance one drawback while using this plugin would be that a trace cannot collide with another trace so if for example i want two bullets to collide with each other then i would need to have a proxy collider on the bullet that the trace can hit against the manager is very rudimentary lot of functionalities can be added into the manager like only taking particular number of components per frame thereby spreading the load into multiple frames instead of a single frame i will update the code as i use this plugin and feel the need for any new functionalities that is all i have for today don't forget to check the code in github link is in the description below thanks for watching [Music] you
Info
Channel: Plato Manchi
Views: 4,277
Rating: undefined out of 5
Keywords:
Id: V5T8w7LT_WE
Channel Id: undefined
Length: 22min 57sec (1377 seconds)
Published: Wed Apr 27 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.