Skip to content

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.
  • 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.