Buffer Inspector and Organization – Update 11
Posted: February 2, 2025
Last Updated: February 2, 2025
Buffer Inspector and Organization – BB – Update 11
Timeframe: January 14, 2025 – January 31, 2025
Hello there. This is an update titled “Buffer Inspector and Organization” because it is partially about getting the DynamicBuffer Inspector working for the UI, partially about some project organization, and partially about a bunch of other stuff I added/fixed in the past few weeks. Lots of stuff here, so buckle up.
EVERYTHING:
Here’s all the stuff I did…
Builder Inspector
The biggest goal for the builder inspector was to get it working for List fields of the BindingObject (BO). Previously, I implemented a builder inspector that can take in an input config file and create bindings that link fields from my BindingObject (BO) to values on the builder subject entity. This worked for single fields in the BO and worked in a special way for fields representing top-level trigger bindings, but I had not gotten it working for general-purpose List fields representing DynamicBuffers on the subject. This is the main thing I implemented for the builder inspector.
Here are the big updates to the builder inspector:
- BuilderInspectorElementCreator: Now supports a new ‘LIST’ type that corresponds to a list of IBufferElementData in the BO. Creators with this type act like a BuilderInspectorGroup, storing a subset of BuilderInspectorElementCreators to be dynamically loaded/unloaded as elements are added or removed.
- BindingObjectAnalyzer: Construct a BindingObjectAnalyzer using a BindingObject from an InGameBuilder. This uses reflection to analyze the fields and attributes of the BO to help figure out which fields are typical data fields, trigger lists, or lists representing DynamicBuffers.
- BuilderInspectorDynamicBufferField: Like a typical BuilderInspectorField except for displaying a single element of a List from the BO.
- BuilderInspectorDynamicBufferDisplay: Similar to the old DynamicBufferDisplay, except it uses the BuilderInspector systems to dynamically create sub-BuilderInspectors for each element in the BO List field (representing the DynamicBuffer on the subject).
- InGameBuilder now updates child BuilderInspectorDynamicBufferDisplays during the call to UpdateBufferDisplays.
- BuilderInspectorHelpers: Now has a GetBufferFromData method which analyzes the given field name and the builder’s BO and can retrieve another BuilderInspectorElementCreator representing the BuilderInspectorDynamicBufferDisplay it is under.
- Field indices are now properly passed down to field widgets.

The new buffer inspector. It’s a little lackluster at the moment, but it builds based on the config file like the other inspector fields, so it should be nicer in the long run.
Organizational Changes
- New generalized setup for logic in the Orcsune.Core package. It is set up as 2 ComponentSystemGroups around the TransformSystemGroup because I have lots of logic that should run before and after the ParentSystem and LocalToWorldSystem.
- PreTransformGroup: Contains systems and groups that should run before the TransformSystemGroup.
- PreTransformSetupGroup: Generally for systems that call preemptive setup logic.
- PreTransformUpdateGroup: For main system logic.
- PreTransformEndGroup: For teardown/disposal/finalizing systems.
- // TransformSystemGroup Goes Here … //
- PostTransformGroup: Contains systems and groups that should run after the TransformSystemGroup.
- PostTransformSetupGroup: Like PreTransform counterpart.
- PostTransformUpdateGroup: Like PreTransform counterpart.
- PostTransformEndGroup: Like PreTransform counterpart.
- KillGroup: Runs within the LateSimulationSystemGroup and is mostly responsible for destroying entities marked with custom ToKill components.
- PreTransformGroup: Contains systems and groups that should run before the TransformSystemGroup.
Movement Overhaul
This whole time, I was using custom BulletMovementSystem, BulletAccelerateSystem, and BulletJerkSystem to apply custom movement to my bullets. Fine as it was, it still sat wrong with me that I had applied it specifically to bullets, so I started changing stuff over to more general-purpose incremental movement systems.
The following are component changes that I want to talk about a bit because I think it’s cool.
Component Changes:
- BulletMovementComponent → IncrementalSpeedComponent
- OLD BulletMovementComponent
- Velocity (float2)- Combined direction and magnitude (a.k.a. velocity) vector.
- MaxSpeed (float)- Scalar representing the max length of Velocity.
- AngularVelocity (float)- Turn speed of the bullet. The degrees per second that the Velocity vector would rotate by.
- CachedVelocity (float2)- Janky storage for the Velocity direction in case the speed went to 0. When the speed became very small, the velocity was cached so that a direction could be retrieved in case speed became greater than 0 again.
- NEW IncrementalSpeedComponent
- _Direction (float2)- Contains a normalized direction vector for movement.
- _Speed (float)- Contains a non-negative scalar value representing magnitude of movement per second.
- MaxSpeed (float)- Scalar also representing maximum _Speed value.
- TurnSpeed (float)- Scalar representing how many degrees to turn the _Direction vector every second.
- OLD BulletMovementComponent
- BulletAccelerateComponent → IncrementalAccelerateComponent
- OLD BulletAccelerateComponent
- useVec (bool)- Boolean describing whether to apply acceleration along a vector.
- dVelocity2 (float2)- The acceleration vector used when useVec is true.
- dVelocity (float)- The rate of acceleration along the same direction as the velocity vector used when useVec is false.
- dAngularVelocity (float)- The rate of change of AngularVelocity.
- NEW IncrementalAccelerateSystem
- AbsoluteAcceleration (float2)- The absolute acceleration vector to apply to the velocity. This is relative to the positive x-axis.
- RelativeAcceleration (float2)- The acceleration vector to apply relative to _Direction. This treats _Direction as if it were the +x-axis, so a RelativeAcceleration of [0,1] would apply an acceleration with magnitude 1 perpendicular to the velocity.
- TurnAcceleration (float)- Rate of change of TurnSpeed.
- OLD BulletAccelerateComponent
- BulletJerkComponent → IncrementalJerkComponent
- OLD BulletJerkComponent
- useVec (bool)- Boolean describing whether to apply jerk along a vector.
- ddVelocity2 (float2)- The jerk vector used when useVec is true. This may apply to dVelocity or dVelocity2 depending on if the acceleration component has useVec enabled.
- ddVelocity (float)- The rate of jerk along the same direction as the acceleration vector used when useVec is false.
- ddAngularVelocity (float)- The rate of change of dAngularVelocity.
- NEW IncrementalJerkComponent
- AbsoluteJerk (float2)- The absolute jerk vector to apply to the acceleration. This is relative to the positive x-axis. This is applied to both AbsoluteAcceleration and RelativeAcceleration.
- RelativeJerk (float2)- The jerk vector to apply relative to RelativeAcceleration. This treats RelativeAcceleration as if it were the +x-axis, so a RelativeJerk of [0,1] would apply a jerk with magnitude 1 perpendicular to the RelativeAcceleration.
- TurnJerk (float)- Rate of change of TurnAcceleration.
- OLD BulletJerkComponent
- BulletPauseComponent → IncrementalMovementPauseComponent
- Same thing, different name.
Just as a quick rundown, this seemed like a good approach for a couple reasons. First, I don’t have to do weird stuff like cache the old velocity vector in the speed component because the new component tracks speed and direction separately, so if a bullet is suddenly stopped (speed=0), then if we set the speed back above 0, it will continue moving in the old direction of movement with no problems. Second, this is nice because the acceleration can apply in two ways at the same time: with a relative and absolute vector. This is nice in cases where, for example, you want to apply faux gravity (with an AbsoluteAcceleration vector of [0,-1]) while at the same time, have the object speed up along its current direction of movement (with a RelativeAcceleration of [1,0]).
Another thing to clarify is that in the BulletJerkSystem, the final calculated jerk vector combining RelativeJerk and AbsoluteJerk is applied to both the RelativeAcceleration and AbsoluteAcceleration vectors. This might not be the best way to do things, but it’s what I’ve got for now.
System Changes:
- BulletMovementSystem → IncrementalSpeedSystem
- BulletAccelerateSystem → IncrementalAccelerateSystem
- BulletJerkSystem → IncrementalJerkSystem
- BulletPauseSystem → IncrementalMovementPauseSystem

Old bullet movement applied only to bullets and had some caveats. Top=bullet speed of 1, Middle=bullet acceleration of 1, Bottom=bullet jerk of 1.

New bullet movement uses a general movement system with fewer finagley aspects. These should look the same, though.
Collision Filters
While playing around a bit, I found that having a bunch of enemies and their bullets overlapping caused big slowdowns. Since bullets were allowed to collide with enemies or allies (which is checked after collision calculations), this caused a bunch of collision calculations that are never acted upon. I needed a way to change the collision filters of bullets as the firing point fired them, which is what this section is about.
This required a few new components and systems, but at the end of the day, it boils down to cloning a reference to a physics collider and setting new CollisionFilters in the PhysicsCollider on the object you want to change. I found this forum post which was very similar to what I wanted to do, and adapted it from there.
- ColliderChangerComponent: Contains 3 fields mimicking those in CollisionFilter to be applied to the entity.
- ColliderChangerSystem: Just schedules the job to change CollisionFilters with ColliderChangerComponent.
- CollisionLayerHelpers: Static helper class to aid with collision layer stuff. Ended up being not super helpful.
- TeamColliders: Static class (bullet hell-specific) that contains information and default ColliderChangerComponents for things like enemies, allies, enemy bullets, and ally bullets.
Debugging
This is more to do with the plots showing runtime for my job profiling tracker. It was getting a little annoying to have to manually add each line to the plot. Also, I didn’t want to have to manually make each plot for each DebugInfoManager. Also also, I wanted to be able to provide a toggle for each line. The solution I came up with was to add some DebugPlots that would do most of that for me.
- DebugPlot: A base class abstract MonoBehaviour that connects up to a LinePlotter and any number of DebugPlotToggles and DebugPlotLines automatically. Inherit from DebugPlot and override the SetupLines method to choose LinePlotSettings for each thing you want to plot
- DebugPlotLine: A data class containing LineSettings and a boolean representing whether the line should be displayed.
- DebugPlotToggle: Connects in input toggle to a DebugPlot.

Debug plot, notice when the firing point begins firing 1000 bullets every 0.1 seconds and the Firing Point runtime increases to over 3ms. We can also toggle different lines on and off.
Boundary
At this point, I had been using a pretty hard-coded method for the bullet boundary. Entities that act as boundaries have the BoundaryComponent and all things that are bounded have the BoundableComponent. The old system basically passes each BoundaryComponent into a job that iterates the BoundableComponent entities, and if the entity is outside a Boundary, it performs an action specified by a value in the BoundableComponent (for example, adds a ToKill component to destroy the out-of-bounds entity, or changes the LocalTransform component to reposition the entity inside the boundary).
This is not good because the boundaries become indistinct in the sense that if a Boundable entity is outside ANY boundary, it performs the action defined in the Boundable. Clearly this is a bad idea if we want multiple different boundaries that do different things for different entities. Because of this, I redesigned the boundary systems to be a little more configurable.
- BoundaryComponent: Component representing a boundary containing size and positional information.
- BoundableElement: Element of a DynamicBuffer that contains just a BoundableType (KILL vs. RESTRICT_MOVEMENT for now) and an Entity with a BoundaryComponent. This allows a single entity to interact differently with multiple boundaries.
- BoundaryTrackerComponent: A singleton with a NativeHashMap that maps a name (FixedString512Bytes) to a boundary entity. This is useful for easily accessing specific boundary entities by name in other systems. For example, you might query the tracker for a boundary named “Bullet Boundary” to apply in a BoundableElement of each bullet that you spawn.

The player has a BoundableElement that links to a rectangular Boundary with side lengths of 5. The BoundableElement also specifies a BoundaryType of RESTRICT_MOVEMENT, keeping the player within the boundary.

All the bullets are given a new BoundableElement that links to a boundary labeled “Bullet Boundary” and have a BoundaryType of KILL, ensuring that the bullets are marked for destruction when they exit a circle of radius 8.
Video Demos
Nothing here. What more do you want?