Extending Impact
Impact is designed to be modular and easily extensible in case the built-in components do not meet your needs. This section will detail how you can create your own components and assets that extend the Impact system.
Note: Impact has Interfaces available for most of its classes that the built-in base components and assets implement. You are free to use these interfaces to implement your custom classes, but be aware you will not be able to use the inspector to assign these classes to your objects due to Unity’s limitations with serializing interfaces.
Custom Impact Triggers
Impact Triggers are the simplest part of the Impact system to extend. You do not necessarily need to inherit from any class, but the ImpactTriggerBase class has properties and functions that you may find useful.
Interaction Data
A key part of a trigger's job is generating Interaction Data. Interaction Data is a data structure that contains various information about an interaction like velocity, normal, the objects that were involved, etc. In your custom trigger, you will need to create a class that implements the IInteractionData interface, and send this data to the Impact Manager via one of these methods:
- ProcessInteraction – Process an interaction for a single interaction event such as a collision.
- ProcessContinuousInteraction – Process an interaction that may persist over many frames and needs to be updated, such as sliding and rolling.
The InteractionData class provides an out-of-the-box implementation of the IInteractionData interface and has the following properties that can be set:
- int InteractionType – An integer that can be used to identify the type of interaction. For example, Collision, Slide, Roll, and Simple have integer values that are defined as constants on the InteractionData class. It is up to the Impact Interaction implementation to determine what to do with the value of this property.
- Vector3 Velocity – The velocity (direction and magnitude) of the interaction, such as the collision velocity.
- Vector3 Point – The world space position where the interaction occurred.
- Vector3 Normal – The normal of the surface where the interaction occurred.
- ImpactTagMask TagMask – he tag mask obtained from the object being interacted with, typically obtained from the object's material at the interaction point. This can be null if it is unknown.
- float CompositionValue – If using material composition, a 0 to 1 value representing the influence of the material at the contact point.
- int? PriorityOverride – If specified, this will override the Priority value of the Impact Object(s) this data is sent to.
- GameObject ThisObject – The object that originated the interaction. Typically this game object will have an IImpactObject component attached to it.
- GameObject OtherObject – The object we are interacting with. May or may not have an IImpactObject component attached to it.
Typically you do not need to create a custom implementation of IInteractionData, but you are absolutely able to do so if you need to implement certain things differently.
Custom Impact Interactions
Custom Impact Interactions allow you to create custom behaviors that occur for interactions outside of the standard Audio, Particle, and Decal interactions. There are 3 parts to creating your own custom Impact Interactions.
Impact Interactions
Impact Interactions take Interaction Data as input and return an Interaction Result as output. Whatever properties and logic is required to do this is 100% up to you.
To create a custom Impact Interaction, you should inherit from the ImpactMaterialInteractionBase abstract class (which is a ScriptableObject) or the IImpactMaterialInteraction interface. You only need to implement 2 functions:
- IInteractionResult GetInteractionResult(IInteractionData data) – Takes the given IInteractionData and returns an IInteractionResult.
- void Preload() – Pre-load any data needed by the interaction, such as object pools.
Interaction Results
Interaction Results contain all of the data built by an Impact Interaction and also hold the logic that needs to be performed for an interaction (for example: instantiating objects, playing sounds, etc). Depending on your needs there are 2 interfaces you can implement:
IInteractionResult
The IInteractionResult interface is the simplest type of interaction result. It is useful for single-event interactions such as collisions. It has 2 members that must be implemented:
- bool IsValid – Does this result contain any useful data? For example, audio interaction results are considered not valid if their volume is close to 0 or no audio clip is specified. If this is false, the result will be ignored completely.
- void Process(IImpactObject parent) – Here you write the code that executes when this interaction occurs. Parent is the Impact Object that created the interaction.
IContinuousInteractionResult
The IContinuousInteractionResult interface allows you to create interaction results that persist over multiple frames, such as sliding and rolling.
In addition to the members of IInteractionResult, there are a few extra members that must be implemented:
- long Key – A 64-bit integer identifying this interaction result so that it can be identified and updated in subsequent frames.
- bool IsAlive – Is the interaction still alive and should continue being updated? If this is false it will be removed from the Impact Manager's list of interaction results and will no longer receive FixedUpdate and KeepAlive.
- void FixedUpdate() – Update anything that needs to be updated.
- void KeepAlive(IInteractionResult newResult) – Update this result with data from a new Interaction Result.
Custom Impact Objects
Typically you would want to create a custom Impact Object if you needed specific algorithms for getting the Impact Material(s) of an object. For example, if you wanted to get a material based on the vertex colors of a mesh.
To write a custom Impact Object class, you will need to inherit from ImpactObjectBase (or one if its subclasses). Impact Objects have several methods for retrieving Impact Materials:
- IImpactMaterial GetPrimaryMaterial() – Get the main Impact Material when no contact point data is available.
- IImpactMaterial GetPrimaryMaterial(Vector3 point) – Get the main Impact Material at a given point on the object.
- int GetMaterialCompositionNonAlloc(Vector3 point, ImpactMaterialComposition[] results) – This method is useful if a given point on your object can have a combination of Impact Materials, such as on a terrain. You can populate the given array with the materials present at the given point and their influence, and return the number of materials returned.
ImpactObjectBase also has other methods you can override:
- VelocityData GetVelocityDataAtPoint(Vector3 contactPoint) – Get the velocity data for a (world-space position) point relative to this object. The VelocityData struct has 2 properties that should be populated:
- LinearVelocity – The velocity of the object as a whole.
- TangentialVelocity – The velocity of the point, not taking into account the velocity of the object. This typically would be a function of the point's position relative to the center of mass of the object and the object's angular velocity
- Vector3 GetContactPointRelativePosition(Vector3 point) – Get the point's position relative to this object's transform.
You may want to implement the IImpactObject interface instead. In that case, you must implement the above methods as well as the following members:
- int Priority – An integer denoting how important an object is.
- GameObject GameObject – The game object that the impact object is attached to.
Custom Impact Materials
You may find that you need to implement your own custom Impact Materials. To do this, create a class that inherits from ImpactMaterialBase and implement the following methods:
- int GetInteractionResultsNonAlloc(IInteractionData data, IInteractionResult[] results) – Takes Interaction Data as input and fills the given array with Interaction Results. Typically this will pass the interaction data to one or more Impact Material Interactions to get the Interaction Results.
- void Preload() – Pre-load any data needed by the material, such as object pools.
You can also choose to implement the IImpactMaterial interface, in which case you will also have to implement the following:
- ImpactTagMask MaterialTagsMask – The tags associated with this material.
Custom Impact Tag Libraries
Finally, you can implement your own custom Impact Tag Library by inheriting from ImpactTagLibraryBase or by implementing the IImpactTagLibrary interface.
The only thing you need to implement for a custom Impact Tag Library is an indexer that can get and set a string at a given index.
Note: Remember that Impact Tag Libraries can have a maximum of 32 tags, since tag masks are represented by 32-bit integer bitmasks.