Game.TreeGrowthSystem
Assembly:
Assembly-CSharp (game code)
Namespace:
Game.Simulation
Type:
class
Base:
Game.Common.GameSystemBase
Summary:
TreeGrowthSystem is a scheduled simulation system that advances the lifecycle and recovery of tree entities. It runs a Burst-compiled IJobChunk (TreeGrowthJob) that iterates tree components and updates growth state, handles damaged and destroyed trees, and issues EntityCommandBuffer commands (via EndFrameBarrier) to add/remove components when a tree’s state changes. Growth progression is stochastic and distributed across update frames to spread CPU cost.
Fields
-
public const int UPDATES_PER_DAY
Defines how many discrete tree-update ticks are considered to occur per simulated day (value: 32). Used when choosing which shared UpdateFrame to process each run. -
public const int TICK_SPEED_CHILD
Tick speed constant used by the child->teen growth calculation (value: 1280). See TickChild for how the Random.NextInt is used. -
public const int TICK_SPEED_TEEN
Tick speed constant for teen->adult progression (value: 938). -
public const int TICK_SPEED_ADULT
Tick speed constant for adult->elderly progression (value: 548). -
public const int TICK_SPEED_ELDERLY
Tick speed constant for elderly->dead progression (value: 548). -
public const int TICK_SPEED_DEAD
Tick speed constant for dead->regrowth (value: 2304). -
private SimulationSystem m_SimulationSystem
Reference to the central SimulationSystem used to obtain the current frame index for UpdateFrame computation. -
private EndFrameBarrier m_EndFrameBarrier
Barrier system used to create an EntityCommandBuffer (parallel writer) to safely add/remove components from within the scheduled job. The buffer is played back at the end of the frame. -
private EntityQuery m_TreeQuery
EntityQuery used to select Tree entities eligible for growth updates. It reads/writes Tree and filters by UpdateFrame shared component and excludes Deleted, Overridden, and Temp. -
private TypeHandle __TypeHandle
Holds cached EntityTypeHandle and ComponentTypeHandle instances used to build the job's type handles each frame. -
private struct TypeHandle
Nested helper struct that stores EntityTypeHandle and ComponentTypeHandles for Tree, Destroyed and Damaged components and assigns them via __AssignHandles.
Properties
- This class exposes no public properties.
Constructors
public TreeGrowthSystem()
Default constructor. The system is set up via OnCreate. Marked with [Preserve] on OnCreate/ctor to avoid stripping in builds.
Methods
-
public override int GetUpdateInterval(SystemUpdatePhase phase)
Returns the system update interval (512). This controls how frequently the system is considered for scheduling relative to the simulation phase. -
protected override void OnCreate()
Initializes references and the entity query: - Retrieves SimulationSystem and EndFrameBarrier from the World.
- Builds m_TreeQuery to match entities with Tree and UpdateFrame shared component, excluding Deleted, Overridden, and Temp components.
-
Calls RequireForUpdate(m_TreeQuery) so the system only runs if relevant entities exist.
-
protected override void OnUpdate()
Main scheduling logic. Steps performed: - Computes which UpdateFrame shared component index to process using SimulationUtils.GetUpdateFrame(m_SimulationSystem.frameIndex, UPDATES_PER_DAY, 16). This distributes trees across subframes to smooth work.
- Sets the shared component filter on m_TreeQuery to only process that UpdateFrame.
- Constructs and schedules a Burst-compiled TreeGrowthJob via JobChunkExtensions.ScheduleParallel. The job receives:
- EntityTypeHandle and ComponentTypeHandles (Tree, Destroyed, Damaged) via InternalCompilerInterface and the cached __TypeHandle.
- A RandomSeed (RandomSeed.Next()) for per-chunk randomness.
- An EntityCommandBuffer.ParallelWriter from m_EndFrameBarrier.
- Adds the returned JobHandle to the EndFrameBarrier (AddJobHandleForProducer) and assigns it to base.Dependency.
Notes: - The job is scheduled parallel across chunks and writes structural changes via the provided parallel ECB. - The job uses the unfilteredChunkIndex as the parallel index when writing to the ECB.
-
private void __AssignQueries(ref SystemState state)
Compiler-generated stub used during OnCreateForCompiler; in this class it only creates and disposes an EntityQueryBuilder to satisfy codegen patterns. -
protected override void OnCreateForCompiler()
Compiler helper that calls __AssignQueries and __TypeHandle.__AssignHandles to initialize type handles when building for the Burst/compiled path. -
private struct TypeHandle.__AssignHandles(ref SystemState state)
Assigns: - EntityTypeHandle via state.GetEntityTypeHandle()
- ComponentTypeHandle
, ComponentTypeHandle , ComponentTypeHandle via state.GetComponentTypeHandle ()
Nested type: TreeGrowthJob (private, BurstCompile) — implements IJobChunk
TreeGrowthJob.Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask, in v128 chunkEnabledMask)
Core per-chunk work. Behavior:- Retrieves NativeArray
and component arrays for Tree, Destroyed and Damaged as available. - Obtains a Random instance from the job's RandomSeed using unfilteredChunkIndex — this ensures each chunk has deterministic/independent randomness.
- Branches based on which components exist:
- If Destroyed component exists for the chunk, iterate trees and call TickTree(ref Tree, ref Destroyed, ref Random). If TickTree returns true (cleared), it:
- Adds BatchesUpdated component via ECB (signals to other systems that batch visuals need updating).
- Removes Destroyed and Damaged components via ECB.
- Else if Damaged component exists, iterate and call TickTree(ref Tree, ref Damaged, ref Random, out stateChanged). Logic:
- Damaged recovery reduces Damaged.m_Damage by random.NextFloat3(0.03137255f) each tick (clamped to zero).
- If damage reaches zero, the Damaged component is removed and BatchesUpdated added. If the tree’s state changed (e.g., elderly progressed), a BatchesUpdated is also added.
- Else (no Destroyed/Damaged) iterate and call TickTree(ref Tree, ref Random). If TickTree returns true (state changed), add BatchesUpdated.
-
Writes modified Tree/Destroyed/Damaged values back into the chunk arrays.
-
Tick methods inside TreeGrowthJob:
-
private bool TickTree(ref Tree tree, ref Random random)
Switches on the tree state and delegates to TickChild/TickTeen/TickAdult/TickElderly/TickDead accordingly. Returns true when the tree state changed (so caller can issue structural/visual updates). -
private bool TickTree(ref Tree tree, ref Damaged damaged, ref Random random, out bool stateChanged)
Logic for trees with Damaged component:- Elderly trees: call TickElderly; then reduce damaged.m_Damage by a small random float3; clamp to zero. Returns true when damage is fully recovered (so Damaged is removed).
- Dead/Stump: call TickDead and return whether the TickDead caused a state change (and hence may allow removal of Damaged).
- Others: do not advance age state (stateChanged=false), but decrease damage and return whether damage is zero.
-
private bool TickTree(ref Tree tree, ref Destroyed destroyed, ref Random random)
Handles destroyed trees clearing over time:- Increases destroyed.m_Cleared by random.NextFloat(0.03137255f) per tick. When cleared >= 1:
- Clears the growth-related state flags (Teen/Adult/Elderly/Dead/Stump).
- Sets tree.m_Growth = 0 and destroyed.m_Cleared = 1.
- Returns true to indicate the tree became "cleared" (ready to regrow).
- Returns false while not cleared.
- Increases destroyed.m_Cleared by random.NextFloat(0.03137255f) per tick. When cleared >= 1:
-
private bool TickChild(ref Tree tree, ref Random random)
Child -> Teen progression:- Adds a stochastic increment: tree.m_Growth += (random.NextInt(1280) >> 8).
- If accumulated growth >= 256, sets Teen flag and resets m_Growth. Returns true on state change.
-
private bool TickTeen(ref Tree tree, ref Random random)
Teen -> Adult progression using TICK_SPEED_TEEN (938); similar pattern. -
private bool TickAdult(ref Tree tree, ref Random random)
Adult -> Elderly progression using TICK_SPEED_ADULT (548). -
private bool TickElderly(ref Tree tree, ref Random random)
Elderly -> Dead progression using TICK_SPEED_ELDERLY (548). -
private bool TickDead(ref Tree tree, ref Random random)
Dead -> regrowth (clear Dead/Stump flags) using TICK_SPEED_DEAD (2304).
Implementation notes: - All growth accumulators use a byte m_Growth and compare against 256 to determine when to advance. - Random.NextInt(N) >> 8 is used to produce a small increment; the choice of constants leads to expected timing consistent with the desired lifecycle lengths. - State flags are stored in tree.m_State and bit-masked to test/modify life stages.
void IJobChunk.Execute(...)
IJobChunk explicit interface implementation that forwards to the strongly typed Execute method.
Usage Example
[Preserve]
protected override void OnCreate()
{
base.OnCreate();
m_SimulationSystem = base.World.GetOrCreateSystemManaged<SimulationSystem>();
m_EndFrameBarrier = base.World.GetOrCreateSystemManaged<EndFrameBarrier>();
m_TreeQuery = GetEntityQuery(
ComponentType.ReadWrite<Tree>(),
ComponentType.ReadOnly<UpdateFrame>(),
ComponentType.Exclude<Deleted>(),
ComponentType.Exclude<Overridden>(),
ComponentType.Exclude<Temp>());
RequireForUpdate(m_TreeQuery);
}
[Preserve]
protected override void OnUpdate()
{
uint updateFrame = SimulationUtils.GetUpdateFrame(m_SimulationSystem.frameIndex, 32, 16);
m_TreeQuery.ResetFilter();
m_TreeQuery.SetSharedComponentFilter(new UpdateFrame(updateFrame));
JobHandle jobHandle = JobChunkExtensions.ScheduleParallel(new TreeGrowthJob
{
m_EntityType = InternalCompilerInterface.GetEntityTypeHandle(ref __TypeHandle.__Unity_Entities_Entity_TypeHandle, ref base.CheckedStateRef),
m_TreeType = InternalCompilerInterface.GetComponentTypeHandle(ref __TypeHandle.__Game_Objects_Tree_RW_ComponentTypeHandle, ref base.CheckedStateRef),
m_DestroyedType = InternalCompilerInterface.GetComponentTypeHandle(ref __TypeHandle.__Game_Common_Destroyed_RW_ComponentTypeHandle, ref base.CheckedStateRef),
m_DamagedType = InternalCompilerInterface.GetComponentTypeHandle(ref __TypeHandle.__Game_Objects_Damaged_RW_ComponentTypeHandle, ref base.CheckedStateRef),
m_RandomSeed = RandomSeed.Next(),
m_CommandBuffer = m_EndFrameBarrier.CreateCommandBuffer().AsParallelWriter()
}, m_TreeQuery, base.Dependency);
m_EndFrameBarrier.AddJobHandleForProducer(jobHandle);
base.Dependency = jobHandle;
}
Additional tips for modders: - The job is Burst-compiled and uses parallel ECB writes, so structural changes (AddComponent/RemoveComponent) are deferred and safe. - To influence tree update frequency for mod testing, adjust UPDATES_PER_DAY or the update interval, but be careful: these constants was chosen to balance simulation realism and performance. - If you need to observe or extend growth behavior, consider adding a separate system that runs after this system (observe BatchesUpdated or use EndFrameBarrier playback ordering) to make rendering or policy changes. - When adding new per-tree state, prefer non-structural (component data) fields rather than adding/removing components every tick to avoid structural change overhead.