DEV Community

Allen Antoine
Allen Antoine

Posted on

MSc Final Project Devlog #3: Improving Reflective Surfaces and Minimizing Redundant Line Traces

This post will examine improvements made to sound reflecting surfaces and methods used to minimize the generation of redundant line traces.

Improving Reflection Modelling

Conversations with classmates about methods for improving the reflective surfaces led to the revelation that the reflection logic had no means of differentiating where other actors in the level were positioned relative to the reflective surface. This meant that regardless of where these actors were the reflective surface would fire line traces at them when hit by a line trace. The result is some rather unrealistic behavior where a sound hitting a reflective surface could effectively travel through that surface to hit an actor on the opposite side of where the original signal would hit that reflective surface. The image below depicts this unwanted behavior as well as the correct behavior for this situation.

Figure 1: An image of a reflective surface displaying incorrect and correct behavior with regard to the location of actors in the level relative to the location of the original source of a sound
Figure 1: An image of a reflective surface displaying incorrect and correct behavior with regard to the location of actors in the level relative to the location of the original source of a sound

To fix this unwanted behavior a check needed to be added to determine whether or not each actor in the Puzzle Actors array was on the same side of a reflective surface as the point where that surface was hit by an incoming sound. To make this check, the Dot Product between the vector formed by the difference between each actor and the original sound's point of impact on the reflective surface and the normal vector for the original sound's point of impact on the surface was calculated.

Getting all of the required values for this calculation required changes to not just the reflective surface blueprint, but also the player's sound generation blueprint and the signal structure used to pass sound signal data between actors. The reason for these changes is that the only place where the point of impact and the normal of the hit can be gathered is in the hit result of the line traces generated by the player in its blueprint. In order to pass this information along to the reflective surfaces, that information needed to be stored in the signal structure along with other signal data. For this reason, two vectors called Impact Point and Normal were added to the signal struct.

The images below show the portion of the player blueprint where the two vectors are passed into the signal structure and the portion of the reflective surface blueprint that handled this calculation.

Figure 2: An image of the nodes in the player blueprint responsible for taking the impact point and normal of each line trace hit and storing those values within the signal structure as variables
Figure 2: An image of the nodes in the player blueprint responsible for taking the impact point and normal of each line trace hit and storing those values within the signal structure as variables

Figure 3: An image of the nodes in the reflective surface blueprint responsible for calculating the Dot Product between the vector formed by the difference between each actor and the original sound's point of impact on the reflective surface and the normal vector for the original sound's point of impact
Figure 3: An image of the nodes in the reflective surface blueprint responsible for calculating the Dot Product between the vector formed by the difference between each actor and the original sound's point of impact on the reflective surface and the normal vector for the original sound's point of impact

The Dot Product is calculated by adding the products of like elements from two vectors. Thus if vector V = (a, b, c) and and vector U = (d, e, f), then their Dot Product VU = ad + be + cf.

The results of this calculation can be interpreted as follows:

  • If the value is positive, the actor resides on the same side of the reflective surface as the original sound's point of impact.
  • If the value is zero, the actor is perpendicular to the the original sound's point of impact
  • If the value is negative, the actor resides on the opposite side of the reflective surface from the original sound's point of impact.

The end result of this addition to the reflective surface logic is that those actors that are on the same side of the reflective surface as an incoming sound will be targeted for reflected copies of that sound. This has the added benefit of reducing the overall number of reflected signals that each reflective surface will send out, thus making the game run more efficiently.

Maximizing Efficiency with Signal IDs

While working on improving reflection modelling as described above, another opportunity for improving efficiency revealed itself during testing. The situation that revealed it is as follows:

Suppose the player is standing in front of a reflective surface. On the other side of that surface is another actor that is part of the Puzzle Actors array. With the reflective surface standing between the player and the actor, any line trace targeting that actor will hit the reflective surface instead, triggering its reflection logic a second time.

This was undesired behavior, because each time the reflective surface was triggered, it would fire line traces at all of the puzzle actors on the correct side of the surface, and this could cause subsequent reflective surfaces to do the same.

Changing this behavior required the reflective surface actor to have a way of keeping track of which signals it had already responded to in order to ensure it did not respond to the same signal more than once. Allowing reflective surfaces to keep track of which signals it had processed in turn required that signals have a unique identifier and that the reflective surfaces have a way of storing references to signals they had already processed. Fortunately, Unreal Engine already had a mechanism for giving unique identifiers to actors. By adding a Globally Unique Identifier (GUID) structure to the existing signal structure, a unique identifier was applied to each signal generated by the player as well as each signal reflected by a reflective surface. An array of GUIDs was added to the reflective surface blueprint so that each reflective surface could store references to each signal it had processed and cross check the values in that array against each incoming signal GUID before processing it.

The images below show how GUIDs are being added to signals as well as how reflective surfaces are checking these GUIDs to ensure they only react to each signal once.

Figure 4: An image of the nodes in the player blueprint responsible for adding a GUID to each signal struct generated by the player
Figure 4: An image of the nodes in the player blueprint responsible for adding a GUID to each signal struct generated by the player

Figure 5: An image of the nodes in the reflective surface blueprint responsible for adding a GUID to each reflected signal generated by the reflective surface
Figure 5: An image of the nodes in the reflective surface blueprint responsible for adding a GUID to each reflected signal generated by the reflective surface

Figure 6: An image of the nodes in the reflective surface blueprint responsible for cross-checking an incoming signal's GUID against the list of previously processed signal GUIDs
Figure 6: An image of the nodes in the reflective surface blueprint responsible for cross-checking an incoming signal's GUID against the list of previously processed signal GUIDs

Between the improved modelling of reflections and the addition of GUIDs to prevent duplicate activations of reflective surfaces, the number of unnecessary line traces has been reduced and the game is more efficient as a result. This video explains and demonstrates all of the additions to the project discussed in this devlog:

Top comments (0)