Commit 09de87fa by alsunj

add documentation

parent cb7803e0
Showing with 761 additions and 122 deletions
......@@ -3,37 +3,56 @@ using Unity.Collections;
using Unity.Entities;
using Unity.NetCode;
[UpdateInGroup(typeof(PredictedSimulationSystemGroup), OrderLast = true)]
[UpdateAfter(typeof(CalculateFrameDamageSystem))]
public partial struct ApplyDamageSystem : ISystem
{
/// <summary>
/// Called when the system is created. Ensures the system only updates when
/// the required components (NetworkTime and GamePlayingTag) are present.
/// </summary>
/// <param name="state">The system state.</param>
public void OnCreate(ref SystemState state)
{
state.RequireForUpdate<NetworkTime>();
state.RequireForUpdate<GamePlayingTag>();
}
/// <summary>
/// Called every frame to update the system. Processes damage for entities
/// with a DamageThisTick buffer and applies it to their CurrentHitPoints.
/// If an entity's hit points drop to zero or below, it is marked for destruction.
/// </summary>
/// <param name="state">The system state.</param>
[BurstCompile]
public void OnUpdate(ref SystemState state)
{
// Get the current server tick from the NetworkTime singleton.
var currentTick = SystemAPI.GetSingleton<NetworkTime>().ServerTick;
// Create a temporary EntityCommandBuffer to queue entity modifications.
var ecb = new EntityCommandBuffer(Allocator.Temp);
// Query entities with CurrentHitPoints, DamageThisTick buffer, and Simulate tag.
foreach (var (currentHitPoints, damageThisTickBuffer, entity) in SystemAPI
.Query<RefRW<CurrentHitPoints>, DynamicBuffer<DamageThisTick>>().WithAll<Simulate>()
.WithEntityAccess())
{
// Skip if no damage data exists for the current tick.
if (!damageThisTickBuffer.GetDataAtTick(currentTick, out var damageThisTick)) continue;
if (damageThisTick.Tick != currentTick) continue;
// Apply the damage to the entity's current hit points.
currentHitPoints.ValueRW.Value -= damageThisTick.Value;
// If hit points drop to zero or below, mark the entity for destruction.
if (currentHitPoints.ValueRO.Value <= 0)
{
ecb.AddComponent<DestroyEntityTag>(entity);
}
}
// Apply all queued entity modifications.
ecb.Playback(state.EntityManager);
}
}
\ No newline at end of file
......@@ -2,9 +2,18 @@ using Unity.Burst;
using Unity.Entities;
using Unity.NetCode;
/// <summary>
/// System responsible for calculating the total damage for the current frame.
/// This system runs in the PredictedSimulationSystemGroup and executes last in the group.
/// </summary>
[UpdateInGroup(typeof(PredictedSimulationSystemGroup), OrderLast = true)]
public partial struct CalculateFrameDamageSystem : ISystem
{
/// <summary>
/// Called when the system is created. Ensures the system only updates when
/// the required components (NetworkTime, GamePlayingTag, and DamageBufferElement) are present.
/// </summary>
/// <param name="state">The system state.</param>
public void OnCreate(ref SystemState state)
{
state.RequireForUpdate<NetworkTime>();
......@@ -12,32 +21,44 @@ public partial struct CalculateFrameDamageSystem : ISystem
state.RequireForUpdate<DamageBufferElement>();
}
/// <summary>
/// Called every frame to update the system. Aggregates damage from the DamageBufferElement
/// and stores the total damage for the current tick in the DamageThisTick buffer.
/// Clears the damage buffer after processing.
/// </summary>
/// <param name="state">The system state.</param>
[BurstCompile]
public void OnUpdate(ref SystemState state)
{
// Retrieve the current server tick from the NetworkTime singleton.
var currentTick = SystemAPI.GetSingleton<NetworkTime>().ServerTick;
// Iterate over entities with DamageBufferElement and DamageThisTick buffers, and the Simulate tag.
foreach (var (damageBuffer, damageThisTickBuffer) in SystemAPI
.Query<DynamicBuffer<DamageBufferElement>, DynamicBuffer<DamageThisTick>>()
.WithAll<Simulate>())
{
// If the damage buffer is empty, add a zero-damage entry for the current tick.
if (damageBuffer.IsEmpty)
{
damageThisTickBuffer.AddCommandData(new DamageThisTick { Tick = currentTick, Value = 0 });
}
else
{
// Calculate the total damage for the current tick.
var totalDamage = 0;
if (damageThisTickBuffer.GetDataAtTick(currentTick, out var damageThisTick))
{
totalDamage = damageThisTick.Value;
}
// Add up all damage values from the damage buffer.
foreach (var damage in damageBuffer)
{
totalDamage += damage.Value;
}
// Store the total damage in the DamageThisTick buffer and clear the damage buffer.
damageThisTickBuffer.AddCommandData(new DamageThisTick { Tick = currentTick, Value = totalDamage });
damageBuffer.Clear();
}
......
......@@ -4,10 +4,20 @@ using Unity.Entities;
using Unity.Physics;
using Unity.Physics.Systems;
/// <summary>
/// System responsible for handling damage when trigger events occur between entities, applies the damage on DamageBufferElement buffer.
/// This system runs within the PhysicsSystemGroup and executes after the PhysicsSimulationGroup.
/// </summary>
[UpdateInGroup(typeof(PhysicsSystemGroup))]
[UpdateAfter(typeof(PhysicsSimulationGroup))]
public partial struct DamageOnTriggerSystem : ISystem
{
/// <summary>
/// Initializes the system by specifying the required components for it to update.
/// Ensures that the system only runs when the SimulationSingleton, EndSimulationEntityCommandBufferSystem.Singleton,
/// and GamePlayingTag components are present.
/// </summary>
/// <param name="state">The current state of the system.</param>
public void OnCreate(ref SystemState state)
{
state.RequireForUpdate<SimulationSingleton>();
......@@ -15,11 +25,19 @@ public partial struct DamageOnTriggerSystem : ISystem
state.RequireForUpdate<GamePlayingTag>();
}
/// <summary>
/// Executes the system logic every frame. Schedules the DamageOnTriggerJob to process trigger events
/// and apply damage to entities based on their interactions.
/// </summary>
/// <param name="state">The current state of the system.</param>
[BurstCompile]
public void OnUpdate(ref SystemState state)
{
// Retrieve the singleton for the EndSimulationEntityCommandBufferSystem.
var ecbSingleton = SystemAPI.GetSingleton<EndSimulationEntityCommandBufferSystem.Singleton>();
var damageOnCollisionJob = new DamageOnTriggerJob
// Create and configure the DamageOnTriggerJob.
var damageOnTriggerJob = new DamageOnTriggerJob
{
DamageOnTriggerLookup = SystemAPI.GetComponentLookup<DamageOnTrigger>(true),
AlreadyDamagedLookup = SystemAPI.GetBufferLookup<AlreadyDamagedEntity>(true),
......@@ -27,21 +45,42 @@ public partial struct DamageOnTriggerSystem : ISystem
ECB = ecbSingleton.CreateCommandBuffer(state.WorldUnmanaged)
};
// Retrieve the SimulationSingleton and schedule the job.
var simulationSingleton = SystemAPI.GetSingleton<SimulationSingleton>();
state.Dependency = damageOnCollisionJob.Schedule(simulationSingleton, state.Dependency);
state.Dependency = damageOnTriggerJob.Schedule(simulationSingleton, state.Dependency);
}
}
//team lookup is no more necessary as the enemy only collides with player now.
/// <summary>
/// Job that processes trigger events and applies damage to entities based on their interactions.
/// </summary>
public struct DamageOnTriggerJob : ITriggerEventsJob
{
/// <summary>
/// Read-only lookup for DamageOnTrigger components to determine damage-dealing entities.
/// </summary>
[ReadOnly] public ComponentLookup<DamageOnTrigger> DamageOnTriggerLookup;
/// <summary>
/// Lookup for AlreadyDamagedEntity buffers to track entities that have already been damaged.
/// </summary>
public BufferLookup<AlreadyDamagedEntity> AlreadyDamagedLookup;
/// <summary>
/// Lookup for DamageBufferElement buffers to store damage values for entities.
/// </summary>
public BufferLookup<DamageBufferElement> DamageBufferLookup;
/// <summary>
/// EntityCommandBuffer used to queue entity modifications during the job execution.
/// </summary>
public EntityCommandBuffer ECB;
/// <summary>
/// Executes the job for each trigger event. Determines the damage-dealing and damage-receiving entities,
/// applies damage, and marks entities for destruction if necessary.
/// </summary>
/// <param name="triggerEvent">The trigger event containing the interacting entities.</param>
public void Execute(TriggerEvent triggerEvent)
{
Entity entityA = triggerEvent.EntityA;
......@@ -49,6 +88,7 @@ public struct DamageOnTriggerJob : ITriggerEventsJob
Entity damageDealingEntity = Entity.Null;
Entity damageReceivingEntity = Entity.Null;
// Determine which entity is dealing damage and which is receiving damage.
if (DamageBufferLookup.HasBuffer(entityA) &&
DamageOnTriggerLookup.HasComponent(entityB))
{
......@@ -66,6 +106,7 @@ public struct DamageOnTriggerJob : ITriggerEventsJob
return;
}
// Check if the damage-dealing entity has already damaged the receiving entity.
if (AlreadyDamagedLookup.HasBuffer(damageDealingEntity))
{
var alreadyDamagedBuffer = AlreadyDamagedLookup[damageDealingEntity];
......@@ -76,9 +117,11 @@ public struct DamageOnTriggerJob : ITriggerEventsJob
}
else
{
// Add a buffer to track already damaged entities if it doesn't exist.
ECB.AddBuffer<AlreadyDamagedEntity>(damageDealingEntity);
}
// Apply damage and mark the damage-dealing entity for destruction if applicable.
if (DamageOnTriggerLookup.TryGetComponent(damageDealingEntity, out var damageOnTrigger))
{
ECB.AddComponent<DestroyEntityTag>(damageDealingEntity);
......
......@@ -3,54 +3,87 @@ using Unity.Transforms;
using UnityEngine;
using UnityEngine.UI;
/// <summary>
/// System responsible for managing health bars in the game. This includes spawning health bars for entities,
/// updating their positions and values, and cleaning up health bars when their associated entities are destroyed.
/// Does not have a requirement for GamePlayingTag, so player health can be seen when the players are spawned and the game has not started yet
/// </summary>
[UpdateAfter(typeof(TransformSystemGroup))]
[WorldSystemFilter(WorldSystemFilterFlags.ClientSimulation)]
public partial struct HealthBarSystem : ISystem
{
/// <summary>
/// Called when the system is created. Ensures the system only updates when the required components
/// (EndSimulationEntityCommandBufferSystem.Singleton and UIPrefabs) are present.
/// </summary>
/// <param name="state">The current state of the system.</param>
public void OnCreate(ref SystemState state)
{
state.RequireForUpdate<EndSimulationEntityCommandBufferSystem.Singleton>();
state.RequireForUpdate<UIPrefabs>();
state.RequireForUpdate<GamePlayingTag>();
}
/// <summary>
/// Called every frame to update the system. Handles the creation, updating, and cleanup of health bars.
/// </summary>
/// <param name="state">The current state of the system.</param>
public void OnUpdate(ref SystemState state)
{
// Retrieve the EntityCommandBuffer for queuing entity modifications.
var ecbSingleton = SystemAPI.GetSingleton<EndSimulationEntityCommandBufferSystem.Singleton>();
var ecb = ecbSingleton.CreateCommandBuffer(state.WorldUnmanaged);
// Spawn health bars for entities that do not already have one.
foreach (var (transform, healthBarOffset, maxHitPoints, entity) in SystemAPI
.Query<LocalTransform, HealthBarOffset, MaxHitPoints>().WithNone<HealthBarUIReference>()
.WithEntityAccess())
{
// Instantiate a new health bar at the entity's position plus the offset.
var healthBarPrefab = SystemAPI.ManagedAPI.GetSingleton<UIPrefabs>().PlayerHealthUIEntity;
var spawnPosition = transform.Position + healthBarOffset.Value;
var newHealthBar = Object.Instantiate(healthBarPrefab, spawnPosition, Quaternion.identity);
// Initialize the health bar with the entity's maximum hit points.
SetHealthBar(newHealthBar, maxHitPoints.Value, maxHitPoints.Value);
// Add a reference to the health bar in the entity's components.
ecb.AddComponent(entity, new HealthBarUIReference { Value = newHealthBar });
}
// Update position and values of health bar
// Update the position and values of existing health bars.
foreach (var (transform, healthBarOffset, currentHitPoints, maxHitPoints, healthBarUI) in SystemAPI
.Query<LocalTransform, HealthBarOffset, CurrentHitPoints, MaxHitPoints, HealthBarUIReference>())
{
// Update the health bar's position to match the entity's position plus the offset.
var healthBarPosition = transform.Position + healthBarOffset.Value;
healthBarUI.Value.transform.position = healthBarPosition;
// Update the health bar's slider values to reflect the entity's current and maximum hit points.
SetHealthBar(healthBarUI.Value, currentHitPoints.Value, maxHitPoints.Value);
}
// Cleanup health bar once associated entity is destroyed
// Cleanup health bars for entities that no longer exist.
foreach (var (healthBarUI, entity) in SystemAPI.Query<HealthBarUIReference>().WithNone<LocalTransform>()
.WithEntityAccess())
{
// Destroy the health bar GameObject and remove the reference component from the entity.
Object.Destroy(healthBarUI.Value);
ecb.RemoveComponent<HealthBarUIReference>(entity);
}
}
/// <summary>
/// Configures the health bar's slider to display the current and maximum hit points.
/// </summary>
/// <param name="healthBarCanvasObject">The GameObject representing the health bar.</param>
/// <param name="curHitPoints">The current hit points of the entity.</param>
/// <param name="maxHitPoints">The maximum hit points of the entity.</param>
private void SetHealthBar(GameObject healthBarCanvasObject, int curHitPoints, int maxHitPoints)
{
// Retrieve the Slider component from the health bar GameObject.
var healthBarSlider = healthBarCanvasObject.GetComponentInChildren<Slider>();
// Set the slider's minimum, maximum, and current values.
healthBarSlider.minValue = 0;
healthBarSlider.maxValue = maxHitPoints;
healthBarSlider.value = curHitPoints;
......
......@@ -4,15 +4,28 @@ using Unity.Physics;
using Unity.Transforms;
using Unity.NetCode;
/// <summary>
/// System responsible for handling movement abilities of entities.
/// Updates the velocity of entities based on their movement speed and direction.
/// </summary>
[UpdateInGroup(typeof(PredictedSimulationSystemGroup))]
public partial struct MoveAbilitySystem : ISystem
{
/// <summary>
/// Called when the system is created. Ensures the system only updates when the required components
/// (GamePlayingTag and AbilityMoveSpeed) are present.
/// </summary>
/// <param name="state">The current state of the system.</param>
public void OnCreate(ref SystemState state)
{
state.RequireForUpdate<GamePlayingTag>();
state.RequireForUpdate<GamePlayingTag>();
state.RequireForUpdate<AbilityMoveSpeed>();
}
/// <summary>
/// Called every frame to update the system. Schedules the MoveAbilityJob to process entity movement in parallel.
/// </summary>
/// <param name="state">The current state of the system.</param>
public void OnUpdate(ref SystemState state)
{
state.Dependency = new MoveAbilityJob()
......@@ -20,10 +33,21 @@ public partial struct MoveAbilitySystem : ISystem
}
}
/// <summary>
/// Job that processes entity movement by updating their velocity based on their forward direction
/// and movement speed. Excludes entities with the SlimeTag component.
/// </summary>
[BurstCompile]
[WithNone(typeof(SlimeTag))]
public partial struct MoveAbilityJob : IJobEntity
{
/// <summary>
/// Executes the job for each entity. Calculates the linear velocity of the entity
/// based on its forward direction and movement speed.
/// </summary>
/// <param name="velocity">The physics velocity of the entity.</param>
/// <param name="transform">The local transform of the entity.</param>
/// <param name="moveSpeed">The movement speed of the entity.</param>
[BurstCompile]
private void Execute(
ref PhysicsVelocity velocity,
......
using Unity.Burst;
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
using Unity.NetCode;
using Unity.Physics;
using Unity.Transforms;
/// <summary>
/// System responsible for moving slime entities towards their target positions.
/// This system runs in the PredictedSimulationSystemGroup and executes last in the group.
/// </summary>
[UpdateInGroup(typeof(PredictedSimulationSystemGroup), OrderLast = true)]
public partial struct MoveSlimeSystem : ISystem
{
/// <summary>
/// Called when the system is created. Ensures the system only updates when
/// entities with the required components (SlimeTag and GamePlayingTag) exist.
/// </summary>
/// <param name="state">The system state.</param>
public void OnCreate(ref SystemState state)
{
state.RequireForUpdate<SlimeTag>();
state.RequireForUpdate<GamePlayingTag>();
}
/// <summary>
/// Called every frame to update the system. Schedules a job to move slime entities
/// towards their target positions based on their move speed and target entity.
/// </summary>
/// <param name="state">The system state.</param>
[BurstCompile]
public void OnUpdate(ref SystemState state)
{
// Lookup for LocalTransform components, used to get the position of target entities.
var transformLookup = SystemAPI.GetComponentLookup<LocalTransform>(true);
// Schedule the SlimeMoveDirectJob to run in parallel.
state.Dependency = new SlimeMoveDirectJob
{
TransformLookup = transformLookup
}.ScheduleParallel(state.Dependency);
}
/// <summary>
/// Job that handles the movement of slime entities towards their target positions.
/// </summary>
[BurstCompile]
public partial struct SlimeMoveDirectJob : IJobEntity
{
/// <summary>
/// Read-only lookup for LocalTransform components to access target entity positions.
/// </summary>
[ReadOnly] public ComponentLookup<LocalTransform> TransformLookup;
/// <summary>
/// Executes the job for each entity. Updates the entity's velocity to move it
/// towards its target position, or stops it if no valid target exists.
/// </summary>
/// <param name="velocity">The entity's current velocity.</param>
/// <param name="transform">The entity's current transform.</param>
/// <param name="moveSpeed">The entity's movement speed.</param>
/// <param name="targetEntity">The target entity the slime is moving towards.</param>
[BurstCompile]
private void Execute(
ref PhysicsVelocity velocity,
in LocalTransform transform,
in AbilityMoveSpeed moveSpeed,
in NpcTargetEntity targetEntity)
{
// If the target entity is null or does not have a LocalTransform, stop the entity.
if (targetEntity.Value == Entity.Null || !TransformLookup.HasComponent(targetEntity.Value))
{
velocity.Linear = float3.zero;
return;
}
// Calculate the direction towards the target and update the velocity.
var targetPosition = TransformLookup[targetEntity.Value].Position;
var direction = math.normalize(targetPosition - transform.Position);
velocity.Linear = direction * moveSpeed.Value;
}
}
}
\ No newline at end of file
fileFormatVersion: 2
guid: 3783f791f48f7b047a3354bfe6fc88c9
\ No newline at end of file
guid: 86f8c4a7fca9c2244981a82be26b0c7d
\ No newline at end of file
......@@ -5,9 +5,18 @@ using Unity.Mathematics;
using Unity.NetCode;
using Unity.Transforms;
/// <summary>
/// System responsible for handling rogue NPC attacks. This system ensures that NPCs can attack their targets
/// based on cooldowns and attack properties, and updates their transformations accordingly.
/// </summary>
[UpdateInGroup(typeof(PredictedSimulationSystemGroup))]
public partial struct RogueAttackSystem : ISystem
{
/// <summary>
/// Called when the system is created. Ensures the system only updates when the required components
/// (NetworkTime, BeginSimulationEntityCommandBufferSystem.Singleton, and GamePlayingTag) are present.
/// </summary>
/// <param name="state">The current state of the system.</param>
public void OnCreate(ref SystemState state)
{
state.RequireForUpdate<NetworkTime>();
......@@ -15,11 +24,17 @@ public partial struct RogueAttackSystem : ISystem
state.RequireForUpdate<GamePlayingTag>();
}
/// <summary>
/// Called every frame to update the system. Schedules the NpcAttackJob to process NPC attacks in parallel.
/// </summary>
/// <param name="state">The current state of the system.</param>
public void OnUpdate(ref SystemState state)
{
// Retrieve the EntityCommandBuffer singleton and the current network time.
var ecbSingleton = SystemAPI.GetSingleton<BeginSimulationEntityCommandBufferSystem.Singleton>();
var networkTime = SystemAPI.GetSingleton<NetworkTime>();
// Schedule the NpcAttackJob to handle NPC attack logic.
state.Dependency = new NpcAttackJob
{
CurrentTick = networkTime.ServerTick,
......@@ -29,51 +44,85 @@ public partial struct RogueAttackSystem : ISystem
}
}
/// <summary>
/// Job that processes NPC attack logic. Handles attack cooldowns, target rotation, and attack instantiation.
/// Excludes entities with the SlimeTag component.
/// </summary>
[BurstCompile]
[WithAll(typeof(Simulate))]
[WithNone(typeof(SlimeTag))]
public partial struct NpcAttackJob : IJobEntity
{
/// <summary>
/// The current server tick used to determine attack cooldowns.
/// </summary>
[ReadOnly] public NetworkTick CurrentTick;
/// <summary>
/// Read-only lookup for LocalTransform components to access entity positions and rotations.
/// </summary>
[ReadOnly] public ComponentLookup<LocalTransform> TransformLookup;
/// <summary>
/// Parallel writer for queuing entity modifications during the job execution.
/// </summary>
public EntityCommandBuffer.ParallelWriter ECB;
/// <summary>
/// Executes the job for each entity. Handles attack cooldowns, rotates the NPC towards its target,
/// and spawns attack entities with the appropriate transformations.
/// </summary>
/// <param name="attackCooldown">The buffer storing attack cooldown data for the NPC.</param>
/// <param name="attackProperties">The properties defining the NPC's attack behavior.</param>
/// <param name="targetEntity">The target entity the NPC is attacking.</param>
/// <param name="enemyEntity">The NPC entity performing the attack.</param>
/// <param name="team">The team type of the NPC.</param>
/// <param name="sortKey">The chunk index used for parallel execution.</param>
[BurstCompile]
private void Execute(ref DynamicBuffer<NpcAttackCooldown> attackCooldown, in NpcAttackProperties attackProperties,
in NpcTargetEntity targetEntity, Entity enemyEntity, TeamTypes team, [ChunkIndexInQuery] int sortKey)
private void Execute(
ref DynamicBuffer<NpcAttackCooldown> attackCooldown,
in NpcAttackProperties attackProperties,
in NpcTargetEntity targetEntity,
Entity enemyEntity,
TeamTypes team,
[ChunkIndexInQuery] int sortKey)
{
// Ensure the target entity has a LocalTransform component.
if (!TransformLookup.HasComponent(targetEntity.Value)) return;
// Retrieve or initialize the cooldown expiration tick.
if (!attackCooldown.GetDataAtTick(CurrentTick, out var cooldownExpirationTick))
{
cooldownExpirationTick.Value = NetworkTick.Invalid;
}
// Check if the NPC can attack based on the cooldown.
var canAttack = !cooldownExpirationTick.Value.IsValid ||
CurrentTick.IsNewerThan(cooldownExpirationTick.Value);
if (!canAttack) return;
// Retrieve the enemy's and target's transformations.
var enemyTransform = TransformLookup[enemyEntity];
var spawnPosition = enemyTransform.Position;
var targetPosition = TransformLookup[targetEntity.Value].Position;
// Get the vector from the enemy to the player
var direction = math.normalize(targetPosition - spawnPosition);
// Calculate the direction and rotation towards the target.
var direction = math.normalize(targetPosition - enemyTransform.Position);
var targetRotation = quaternion.LookRotationSafe(direction, math.up());
// Rotate the enemy towards player
// Rotate the NPC towards the target.
enemyTransform.Rotation = targetRotation;
ECB.SetComponent(sortKey, enemyEntity, enemyTransform);
// Spawn the attack entity at the appropriate position and rotation.
var newAttack = ECB.Instantiate(sortKey, attackProperties.AttackPrefab);
var newAttackTransform = LocalTransform.FromPositionRotation(spawnPosition + attackProperties.FirePointOffset,
quaternion.LookRotationSafe(targetPosition - spawnPosition, math.up()));
var newAttackTransform = LocalTransform.FromPositionRotation(
enemyTransform.Position + attackProperties.FirePointOffset,
quaternion.LookRotationSafe(targetPosition - enemyTransform.Position, math.up()));
ECB.SetComponent(sortKey, newAttack, newAttackTransform);
ECB.SetComponent(sortKey, newAttack, team);
// Update the attack cooldown.
var newCooldownTick = CurrentTick;
newCooldownTick.Add(attackProperties.CooldownTickCount);
attackCooldown.AddCommandData(new NpcAttackCooldown { Tick = CurrentTick, Value = newCooldownTick });
......
using Unity.Burst;
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
using Unity.NetCode;
using Unity.Physics;
using Unity.Transforms;
[UpdateInGroup(typeof(PredictedSimulationSystemGroup), OrderLast = true)]
public partial struct MoveSlimeSystem : ISystem
{
public void OnCreate(ref SystemState state)
{
state.RequireForUpdate<BeginSimulationEntityCommandBufferSystem.Singleton>();
state.RequireForUpdate<SlimeTag>();
state.RequireForUpdate<GamePlayingTag>();
}
[BurstCompile]
public void OnUpdate(ref SystemState state)
{
var transformLookup = SystemAPI.GetComponentLookup<LocalTransform>(true);
state.Dependency = new SlimeMoveDirectJob
{
TransformLookup = transformLookup
}.ScheduleParallel(state.Dependency);
}
[BurstCompile]
public partial struct SlimeMoveDirectJob : IJobEntity
{
[ReadOnly] public ComponentLookup<LocalTransform> TransformLookup;
[BurstCompile]
private void Execute(
ref PhysicsVelocity velocity,
in LocalTransform transform,
in AbilityMoveSpeed moveSpeed,
in NpcTargetEntity targetEntity)
{
if (targetEntity.Value == Entity.Null || !TransformLookup.HasComponent(targetEntity.Value))
{
velocity.Linear = float3.zero;
return;
}
var targetPosition = TransformLookup[targetEntity.Value].Position;
var direction = math.normalize(targetPosition - transform.Position);
velocity.Linear = direction * moveSpeed.Value;
}
}
}
\ No newline at end of file
fileFormatVersion: 2
guid: 86f8c4a7fca9c2244981a82be26b0c7d
\ No newline at end of file
......@@ -5,13 +5,25 @@ using Unity.Physics;
using Unity.Physics.Systems;
using Unity.Transforms;
/// <summary>
/// System responsible for targeting logic for NPCs. This system determines the closest valid target
/// within a specified radius for each NPC and updates the target entity accordingly.
/// </summary>
[UpdateInGroup(typeof(PhysicsSystemGroup))]
[UpdateAfter(typeof(PhysicsSimulationGroup))]
[UpdateBefore(typeof(ExportPhysicsWorld))]
public partial struct TargetingSystem : ISystem
{
/// <summary>
/// Collision filter used to define which layers the NPCs can target.
/// </summary>
private CollisionFilter _npcAttackFilter;
/// <summary>
/// Called when the system is created. Ensures the system only updates when the required components
/// (PhysicsWorldSingleton and GamePlayingTag) are present. Initializes the collision filter for NPC targeting.
/// </summary>
/// <param name="state">The current state of the system.</param>
[BurstCompile]
public void OnCreate(ref SystemState state)
{
......@@ -19,12 +31,15 @@ public partial struct TargetingSystem : ISystem
state.RequireForUpdate<GamePlayingTag>();
_npcAttackFilter = new CollisionFilter
{
BelongsTo = 1 << 6, //Target Cast
CollidesWith = 1 << 1 //Player
BelongsTo = 1 << 6, // Target Cast
CollidesWith = 1 << 1 // Player
};
}
/// <summary>
/// Called every frame to update the system. Schedules the NpcTargetingJob to process NPC targeting logic in parallel.
/// </summary>
/// <param name="state">The current state of the system.</param>
[BurstCompile]
public void OnUpdate(ref SystemState state)
{
......@@ -36,26 +51,51 @@ public partial struct TargetingSystem : ISystem
}.ScheduleParallel(state.Dependency);
}
/// <summary>
/// Job that processes NPC targeting logic. Identifies the closest valid target within the NPC's targeting radius
/// and updates the target entity component.
/// </summary>
[BurstCompile]
[WithAll(typeof(Simulate))]
public partial struct NpcTargetingJob : IJobEntity
{
/// <summary>
/// The collision world used to perform overlap sphere queries for detecting potential targets.
/// </summary>
[ReadOnly] public CollisionWorld CollisionWorld;
/// <summary>
/// The collision filter defining the layers that NPCs can target.
/// </summary>
[ReadOnly] public CollisionFilter CollisionFilter;
/// <summary>
/// Read-only lookup for TeamTypes components to ensure NPCs do not target entities on the same team.
/// </summary>
[ReadOnly] public ComponentLookup<TeamTypes> TeamTypeLookup;
/// <summary>
/// Executes the job for each NPC entity. Finds the closest valid target within the targeting radius
/// and updates the NpcTargetEntity component with the target entity.
/// </summary>
/// <param name="enemyEntity">The NPC entity performing the targeting.</param>
/// <param name="targetEntity">The component storing the current target entity for the NPC.</param>
/// <param name="transform">The local transform of the NPC entity.</param>
/// <param name="targetRadius">The targeting radius of the NPC.</param>
[BurstCompile]
private void Execute(Entity enemyEntity, ref NpcTargetEntity targetEntity, in LocalTransform transform,
in NpcTargetRadius targetRadius)
{
// Temporary list to store potential target hits.
var hits = new NativeList<DistanceHit>(Allocator.TempJob);
// Perform an overlap sphere query to find potential targets within the targeting radius.
if (CollisionWorld.OverlapSphere(transform.Position, targetRadius.Value, ref hits, CollisionFilter))
{
var closestDistance = float.MaxValue;
var closestEntity = Entity.Null;
// Iterate through the hits to find the closest valid target.
foreach (var hit in hits)
{
if (!TeamTypeLookup.TryGetComponent(hit.Entity, out var teamTypes)) continue;
......@@ -67,13 +107,16 @@ public partial struct TargetingSystem : ISystem
}
}
// Update the target entity with the closest valid target.
targetEntity.Value = closestEntity;
}
else
{
// If no valid targets are found, set the target entity to null.
targetEntity.Value = Entity.Null;
}
// Dispose of the temporary hits list.
hits.Dispose();
}
}
......
......@@ -4,9 +4,19 @@ using Unity.NetCode;
using UnityEngine;
/// <summary>
/// System responsible for handling game entry requests from client players.
/// This system ensures that clients are marked as "in-game" and initializes their game-related components,
/// such as creating a camera and associating it with the player.
/// </summary>
[WorldSystemFilter(WorldSystemFilterFlags.ClientSimulation)]
public partial struct ClientRequestGameEntrySystem : ISystem
{
/// <summary>
/// Called when the system is created. Ensures the system only updates when there are entities
/// with a `NetworkId` component but without a `NetworkStreamInGame` component.
/// </summary>
/// <param name="state">The current state of the system.</param>
public void OnCreate(ref SystemState state)
{
EntityQueryBuilder entityQueryBuilder = new EntityQueryBuilder(Allocator.Temp)
......@@ -16,35 +26,56 @@ public partial struct ClientRequestGameEntrySystem : ISystem
entityQueryBuilder.Dispose();
}
/// <summary>
/// Called every frame to process game entry requests. Marks clients as "in-game",
/// creates a camera for each client, and sends an RPC request to notify the server.
/// </summary>
/// <param name="state">The current state of the system.</param>
public void OnUpdate(ref SystemState state)
{
EntityCommandBuffer entityCommandBuffer = new EntityCommandBuffer(Unity.Collections.Allocator.Temp);
// Iterate through all entities with a `NetworkId` component but without a `NetworkStreamInGame` component.
foreach ((
RefRO<NetworkId> networkId,
Entity entity)
in SystemAPI.Query
<RefRO<NetworkId>>().WithNone<NetworkStreamInGame>().WithEntityAccess())
{
// Add the `NetworkStreamInGame` component to mark the client as "in-game".
entityCommandBuffer.AddComponent<NetworkStreamInGame>(entity);
// Create a new entity for the game entry request.
var requestGameConnection = entityCommandBuffer.CreateEntity();
// Create a new camera GameObject for the client and associate it with the player.
GameObject playerCameraGO = new GameObject($"Camera{networkId.ValueRO.Value}");
playerCameraGO.AddComponent<Camera>();
FollowPlayer followScript = playerCameraGO.AddComponent<FollowPlayer>();
followScript.networkId = networkId.ValueRO.Value;
// Add components to the request entity to send an RPC to the server.
entityCommandBuffer.AddComponent<GoInGameRequestRpc>(requestGameConnection);
entityCommandBuffer.AddComponent<SendRpcCommandRequest>(requestGameConnection);
}
// Apply all queued entity modifications.
entityCommandBuffer.Playback(state.EntityManager);
}
}
/// <summary>
/// System responsible for handling game entry requests from thin clients.
/// This system ensures that thin clients are marked as "in-game" and sends an RPC request to notify the server.
/// </summary>
[WorldSystemFilter(WorldSystemFilterFlags.ThinClientSimulation)]
public partial struct ThinClientRequestGameEntrySystem : ISystem
{
/// <summary>
/// Called when the system is created. Ensures the system only updates when there are entities
/// with a `NetworkId` component but without a `NetworkStreamInGame` component.
/// </summary>
/// <param name="state">The current state of the system.</param>
public void OnCreate(ref SystemState state)
{
EntityQueryBuilder entityQueryBuilder = new EntityQueryBuilder(Allocator.Temp)
......@@ -54,24 +85,34 @@ public partial struct ThinClientRequestGameEntrySystem : ISystem
entityQueryBuilder.Dispose();
}
/// <summary>
/// Called every frame to process game entry requests. Marks thin clients as "in-game"
/// and sends an RPC request to notify the server.
/// </summary>
/// <param name="state">The current state of the system.</param>
public void OnUpdate(ref SystemState state)
{
EntityCommandBuffer entityCommandBuffer = new EntityCommandBuffer(Unity.Collections.Allocator.Temp);
// Iterate through all entities with a `NetworkId` component but without a `NetworkStreamInGame` component.
foreach ((
RefRO<NetworkId> networkId,
Entity entity)
in SystemAPI.Query
<RefRO<NetworkId>>().WithNone<NetworkStreamInGame>().WithEntityAccess())
{
// Add the `NetworkStreamInGame` component to mark the thin client as "in-game".
entityCommandBuffer.AddComponent<NetworkStreamInGame>(entity);
// Create a new entity for the game entry request.
var requestGameConnection = entityCommandBuffer.CreateEntity();
// Add components to the request entity to send an RPC to the server.
entityCommandBuffer.AddComponent<GoInGameRequestRpc>(requestGameConnection);
entityCommandBuffer.AddComponent<SendRpcCommandRequest>(requestGameConnection);
}
// Apply all queued entity modifications.
entityCommandBuffer.Playback(state.EntityManager);
}
}
\ No newline at end of file
......@@ -4,38 +4,65 @@ using Unity.Collections;
using Unity.Entities;
using Unity.NetCode;
/// <summary>
/// System responsible for handling client-side game start logic. This system processes RPCs related to the
/// number of players remaining to start the game and the game start tick, triggering appropriate actions
/// and updating the game state on the client.
/// </summary>
[WorldSystemFilter(WorldSystemFilterFlags.ClientSimulation)]
public partial class ClientStartGameSystem : SystemBase
{
/// <summary>
/// Action invoked when the number of players remaining to start the game is updated.
/// </summary>
public Action<int> OnUpdatePlayersRemainingToStart;
/// <summary>
/// Action invoked when the game start countdown begins.
/// </summary>
public Action OnStartGameCountdown;
/// <summary>
/// Called every frame to process game start-related RPCs. Handles the destruction of RPC entities,
/// updates the number of players remaining to start, and initializes the game start tick on the client.
/// </summary>
protected override void OnUpdate()
{
// Create a temporary EntityCommandBuffer to queue entity modifications.
var ecb = new EntityCommandBuffer(Allocator.Temp);
// Process RPCs for updating the number of players remaining to start the game.
foreach (var (playersRemainingToStart, entity) in SystemAPI.Query<PlayersRemainingToStart>()
.WithAll<ReceiveRpcCommandRequest>().WithEntityAccess())
{
// Destroy the RPC entity after processing.
ecb.DestroyEntity(entity);
// Invoke the action to update the number of players remaining to start.
OnUpdatePlayersRemainingToStart?.Invoke(playersRemainingToStart.Value);
}
// Process RPCs for the game start tick.
foreach (var (gameStartTick, entity) in SystemAPI.Query<GameStartTickRpc>()
.WithAll<ReceiveRpcCommandRequest>().WithEntityAccess())
{
// Destroy the RPC entity after processing.
ecb.DestroyEntity(entity);
// Invoke the action to start the game countdown.
OnStartGameCountdown?.Invoke();
// Create a new entity to store the game start tick on the client side.
var gameStartEntity = ecb.CreateEntity();
//creates the entity about when the game has started on client side
// Add the GameStartTick component to the new entity with the received tick value.
ecb.AddComponent(gameStartEntity, new GameStartTick
{
Value = gameStartTick.Value
});
}
// Apply all queued entity modifications.
ecb.Playback(EntityManager);
}
}
\ No newline at end of file
......@@ -5,46 +5,79 @@ using Unity.Entities;
using Unity.Mathematics;
using Unity.NetCode;
[UpdateInGroup(typeof(PredictedSimulationSystemGroup))]
public partial class CountdownToGameStartSystem : SystemBase
{
/// <summary>
/// Action invoked to update the countdown text with the number of seconds remaining until the game starts.
/// </summary>
public Action<int> OnUpdateCountdownText;
/// <summary>
/// Action invoked when the countdown ends and the game starts.
/// </summary>
public Action OnCountdownEnd;
/// <summary>
/// Called when the system is created. Ensures the system only updates when a `NetworkTime` component is present.
/// </summary>
protected override void OnCreate()
{
RequireForUpdate<NetworkTime>();
}
/// <summary>
/// Called every frame to process the countdown to the game start. Updates the countdown text or triggers
/// the game start logic when the countdown ends.
/// </summary>
protected override void OnUpdate()
{
// Retrieve the current network time.
var networkTime = SystemAPI.GetSingleton<NetworkTime>();
// Skip processing if this is not the first time fully predicting the current tick.
if (!networkTime.IsFirstTimeFullyPredictingTick) return;
// Get the current server tick.
var currentTick = networkTime.ServerTick;
// Create a temporary EntityCommandBuffer to queue entity modifications.
var ecb = new EntityCommandBuffer(Allocator.Temp);
// Iterate through all entities with a `GameStartTick` component and a `Simulate` tag.
foreach (var (gameStartTick, entity) in SystemAPI.Query<GameStartTick>().WithAll<Simulate>().WithEntityAccess())
{
// Check if the current tick is equal to or newer than the game start tick.
if (currentTick.Equals(gameStartTick.Value) || currentTick.IsNewerThan(gameStartTick.Value))
{
// Create a new entity to represent the game playing state.
var gamePlayingEntity = ecb.CreateEntity();
ecb.SetName(gamePlayingEntity, "GamePlayingEntity");
ecb.AddComponent<GamePlayingTag>(gamePlayingEntity);
// Destroy the `GameStartTick` entity as the countdown has ended.
ecb.DestroyEntity(entity);
// Invoke the action to signal the end of the countdown.
OnCountdownEnd?.Invoke();
}
else
{
// Calculate the number of ticks remaining until the game starts.
var ticksToStart = gameStartTick.Value.TickIndexForValidTick - currentTick.TickIndexForValidTick;
// Retrieve the simulation tick rate.
var simulationTickRate = NetCodeConfig.Global.ClientServerTickRate.SimulationTickRate;
// Calculate the number of seconds remaining until the game starts.
var secondsToStart = (int)math.ceil((float)ticksToStart / simulationTickRate);
// Invoke the action to update the countdown text.
OnUpdateCountdownText?.Invoke(secondsToStart);
}
}
// Apply all queued entity modifications.
ecb.Playback(EntityManager);
}
}
\ No newline at end of file
......@@ -8,6 +8,11 @@ using UnityEngine;
[WorldSystemFilter(WorldSystemFilterFlags.ServerSimulation)]
partial struct GoInGameServerSystem : ISystem
{
/// <summary>
/// Called when the system is created. Ensures the system only updates when the required components
/// and entities are present in the world.
/// </summary>
/// <param name="state">The current state of the system.</param>
[BurstCompile]
public void OnCreate(ref SystemState state)
{
......@@ -19,63 +24,81 @@ partial struct GoInGameServerSystem : ISystem
state.RequireForUpdate<GoInGameRequestRpc>();
}
/// <summary>
/// Called every frame to process incoming game entry requests from clients. Handles player instantiation,
/// updates the player counter, and sends RPCs to clients about the game start or remaining players.
/// </summary>
/// <param name="state">The current state of the system.</param>
public void OnUpdate(ref SystemState state)
{
// Create a temporary command buffer to queue entity modifications.
EntityCommandBuffer entityCommandBuffer = new EntityCommandBuffer(Unity.Collections.Allocator.Temp);
// Retrieve singleton components and entities required for processing.
EntititesReferences entititesReferences = SystemAPI.GetSingleton<EntititesReferences>();
Entity gameStartPropertiesEntity = SystemAPI.GetSingletonEntity<GameStartProperties>();
PlayerCounter playerCounter = SystemAPI.GetComponent<PlayerCounter>(gameStartPropertiesEntity);
GameStartProperties gameStartProperties =
SystemAPI.GetComponent<GameStartProperties>(gameStartPropertiesEntity);
// Iterate through all entities with a `ReceiveRpcCommandRequest` and `GoInGameRequestRpc` component.
foreach ((
RefRO<ReceiveRpcCommandRequest> receiveRpcCommandRequest,
Entity entity) in
SystemAPI.Query
<RefRO<ReceiveRpcCommandRequest>>().WithAll<GoInGameRequestRpc>().WithEntityAccess())
{
// Get the source connection entity from the RPC request.
Entity sourceConnection =
receiveRpcCommandRequest.ValueRO.SourceConnection; // Get the source connection entity
receiveRpcCommandRequest.ValueRO.SourceConnection;
// Mark the client as "in-game" by adding the `NetworkStreamInGame` component.
entityCommandBuffer.AddComponent<NetworkStreamInGame>(sourceConnection);
Debug.Log("Client Connected to Server");
entityCommandBuffer.DestroyEntity(entity);
// Destroy the RPC entity after processing.
entityCommandBuffer.DestroyEntity(entity);
// Instantiate player entity and place randomly on the x axis -+10
// Instantiate a player entity and set its position randomly along the x-axis.
Entity playerEntity = entityCommandBuffer.Instantiate(entititesReferences.PlayerPrefabEntity);
entityCommandBuffer.SetComponent(playerEntity, LocalTransform.FromPosition(new float3(
UnityEngine.Random.Range(-10, +10), 0, 0)));
NetworkId networkId = SystemAPI.GetComponent<NetworkId>(sourceConnection); // use sourceConnection
// Retrieve the `NetworkId` component from the source connection and assign it to the player entity.
NetworkId networkId = SystemAPI.GetComponent<NetworkId>(sourceConnection);
entityCommandBuffer.AddComponent(playerEntity, new GhostOwner
{
NetworkId = networkId.Value
});
entityCommandBuffer.AddComponent(playerEntity, new NetworkEntityReference { Value = sourceConnection });
entityCommandBuffer.AppendToBuffer(sourceConnection, new LinkedEntityGroup // use sourceConnection
// Link the player entity to the source connection using a `LinkedEntityGroup` buffer.
entityCommandBuffer.AppendToBuffer(sourceConnection, new LinkedEntityGroup
{
Value = playerEntity
});
// Update the player counter and calculate the number of players remaining to start the game.
playerCounter.Value++;
int playersRemainingToStart = gameStartProperties.PlayerAmount - playerCounter.Value;
// Create an RPC entity to notify clients about the game start or remaining players.
var gameStartRpc = entityCommandBuffer.CreateEntity();
if (playersRemainingToStart <= 0 && !SystemAPI.HasSingleton<GamePlayingTag>())
{
// Calculate the tick at which the game should start.
var simulationTickRate = NetCodeConfig.Global.ClientServerTickRate.SimulationTickRate;
var ticksUntilStart = (uint)(simulationTickRate * gameStartProperties.CountdownTime);
var gameStartTick = SystemAPI.GetSingleton<NetworkTime>().ServerTick;
gameStartTick.Add(ticksUntilStart);
// sends data to client about on what tick the game should start.
// Add a `GameStartTickRpc` component to the RPC entity with the calculated start tick.
entityCommandBuffer.AddComponent(gameStartRpc, new GameStartTickRpc
{
Value = gameStartTick
});
//creates the entity about when the game has started on server side
// Create a server-side entity to track the game start tick.
var gameStartEntity = entityCommandBuffer.CreateEntity();
entityCommandBuffer.AddComponent(gameStartEntity, new GameStartTick
{
......@@ -84,14 +107,19 @@ partial struct GoInGameServerSystem : ISystem
}
else
{
// Add a `PlayersRemainingToStart` component to the RPC entity with the remaining player count.
entityCommandBuffer.AddComponent(gameStartRpc,
new PlayersRemainingToStart { Value = playersRemainingToStart });
}
// Add a `SendRpcCommandRequest` component to the RPC entity to send it to clients.
entityCommandBuffer.AddComponent<SendRpcCommandRequest>(gameStartRpc);
}
// Apply all queued entity modifications.
entityCommandBuffer.Playback(state.EntityManager);
// Update the singleton `PlayerCounter` component with the new value.
SystemAPI.SetSingleton(playerCounter);
}
}
\ No newline at end of file
......@@ -5,12 +5,17 @@ using Unity.NetCode;
using Unity.Transforms;
using UnityEngine;
[UpdateInGroup(typeof(PredictedSimulationSystemGroup), OrderLast = true)]
public partial struct DestroyEntitySystem : ISystem
{
/// <summary>
/// Called when the system is created. Ensures the system only updates when the required components
/// and entities are present in the world.
/// </summary>
/// <param name="state">The current state of the system.</param>
public void OnCreate(ref SystemState state)
{
// Require specific components and entities for the system to update.
state.RequireForUpdate<RespawnEntityTag>();
state.RequireForUpdate<BeginSimulationEntityCommandBufferSystem.Singleton>();
state.RequireForUpdate<NetworkTime>();
......@@ -18,35 +23,49 @@ public partial struct DestroyEntitySystem : ISystem
state.RequireForUpdate<DestroyEntityTag>();
}
/// <summary>
/// Called every frame to process entities marked for destruction. Handles player-specific logic
/// such as respawn scheduling and destroys non-player entities directly.
/// </summary>
/// <param name="state">The current state of the system.</param>
public void OnUpdate(ref SystemState state)
{
// Retrieve the current network time.
var networkTime = SystemAPI.GetSingleton<NetworkTime>();
// Skip processing if this is not the first time fully predicting the current tick.
if (!networkTime.IsFirstTimeFullyPredictingTick) return;
// Get the current server tick.
var currentTick = networkTime.ServerTick;
// Retrieve the EntityCommandBuffer for queuing entity modifications.
var ecbSingleton = SystemAPI.GetSingleton<BeginSimulationEntityCommandBufferSystem.Singleton>();
var ecb = ecbSingleton.CreateCommandBuffer(state.WorldUnmanaged);
// Iterate through all entities with a LocalTransform component, DestroyEntityTag, and Simulate tag.
foreach (var (transform, entity) in SystemAPI.Query<RefRW<LocalTransform>>()
.WithAll<DestroyEntityTag, Simulate>().WithEntityAccess())
{
// Check if the current world is the server.
if (state.World.IsServer())
{
// Handle player entities.
if (SystemAPI.HasComponent<PlayerTag>(entity))
{
// Retrieve the network entity and respawn-related components.
var networkEntity = SystemAPI.GetComponent<NetworkEntityReference>(entity).Value;
var respawnEntity = SystemAPI.GetSingletonEntity<RespawnEntityTag>();
var respawnTickCount = SystemAPI.GetComponent<RespawnTickCount>(respawnEntity).Value;
// Calculate the tick at which the player should respawn.
var respawnTick = currentTick;
respawnTick.Add(respawnTickCount);
// Store NetworkId BEFORE destroying entity or networkEntity
// Store the NetworkId before destroying the entity.
int networkIdValue = SystemAPI.GetComponent<NetworkId>(networkEntity).Value;
// Append the player to the respawn buffer with the calculated respawn tick.
ecb.AppendToBuffer(respawnEntity, new RespawnBufferElement
{
NetworkEntity = networkEntity,
......@@ -54,11 +73,13 @@ public partial struct DestroyEntitySystem : ISystem
NetworkId = networkIdValue
});
// Destroy the player entity.
ecb.DestroyEntity(entity);
}
else
{
ecb.DestroyEntity(entity); // Destroy non-player entities
// Destroy non-player entities directly.
ecb.DestroyEntity(entity);
}
}
}
......
......@@ -5,27 +5,44 @@ using Unity.NetCode;
[UpdateInGroup(typeof(PredictedSimulationSystemGroup))]
public partial struct DestroyOnTimerSystem : ISystem
{
/// <summary>
/// Called when the system is created. Ensures the system only updates when the required components
/// and entities are present in the world.
/// </summary>
/// <param name="state">The current state of the system.</param>
public void OnCreate(ref SystemState state)
{
// Require specific components and entities for the system to update.
state.RequireForUpdate<NetworkTime>();
state.RequireForUpdate<EndSimulationEntityCommandBufferSystem.Singleton>();
state.RequireForUpdate<DestroyAtTick>();
state.RequireForUpdate<GamePlayingTag>();
}
/// <summary>
/// Called every frame to process entities with a `DestroyAtTick` component. Adds a `DestroyEntityTag`
/// to entities whose destruction tick has been reached or passed.
/// </summary>
/// <param name="state">The current state of the system.</param>
[BurstCompile]
public void OnUpdate(ref SystemState state)
{
// Retrieve the EntityCommandBuffer for queuing entity modifications.
var ecbSingleton = SystemAPI.GetSingleton<EndSimulationEntityCommandBufferSystem.Singleton>();
var ecb = ecbSingleton.CreateCommandBuffer(state.WorldUnmanaged);
// Get the current server tick.
var currentTick = SystemAPI.GetSingleton<NetworkTime>().ServerTick;
// Iterate through all entities with a `DestroyAtTick` component and a `Simulate` tag,
// but without a `DestroyEntityTag`.
foreach (var (destroyAtTick, entity) in SystemAPI.Query<RefRW<DestroyAtTick>>().WithAll<Simulate>()
.WithNone<DestroyEntityTag>().WithEntityAccess())
{
// Check if the current tick is equal to or newer than the destruction tick.
if (currentTick.Equals(destroyAtTick.ValueRW.Value) || currentTick.IsNewerThan(destroyAtTick.ValueRW.Value))
{
// Add the `DestroyEntityTag` to mark the entity for destruction.
ecb.AddComponent<DestroyEntityTag>(entity);
}
}
......
......@@ -2,30 +2,56 @@ using Unity.Collections;
using Unity.Entities;
using Unity.NetCode;
/// <summary>
/// System responsible for initializing the destruction timer for entities.
/// This system calculates the tick at which entities should be destroyed based on their lifetime
/// and adds a `DestroyAtTick` component to mark the destruction time.
/// </summary>
public partial struct InitializeDestroyOnTimerSystem : ISystem
{
/// <summary>
/// Called when the system is created. Ensures the system only updates when the required components
/// and entities are present in the world.
/// </summary>
/// <param name="state">The current state of the system.</param>
public void OnCreate(ref SystemState state)
{
// Require specific components and entities for the system to update.
state.RequireForUpdate<NetworkTime>();
state.RequireForUpdate<DestroyOnTimer>();
state.RequireForUpdate<GamePlayingTag>();
}
/// <summary>
/// Called every frame to process entities with a `DestroyOnTimer` component. Calculates the destruction tick
/// based on the entity's lifetime and adds a `DestroyAtTick` component to schedule its destruction.
/// </summary>
/// <param name="state">The current state of the system.</param>
public void OnUpdate(ref SystemState state)
{
// Create a temporary EntityCommandBuffer to queue entity modifications.
var ecb = new EntityCommandBuffer(Allocator.Temp);
// Retrieve the simulation tick rate and the current server tick.
var simulationTickRate = NetCodeConfig.Global.ClientServerTickRate.SimulationTickRate;
var currentTick = SystemAPI.GetSingleton<NetworkTime>().ServerTick;
// Iterate through all entities with a `DestroyOnTimer` component but without a `DestroyAtTick` component.
foreach (var (destroyOnTimer, entity) in SystemAPI.Query<RefRW<DestroyOnTimer>>().WithNone<DestroyAtTick>()
.WithEntityAccess())
{
// Calculate the lifetime in ticks based on the entity's lifetime in seconds.
var lifetimeInTicks = (uint)(destroyOnTimer.ValueRW.Value * simulationTickRate);
// Calculate the target tick at which the entity should be destroyed.
var targetTick = currentTick;
targetTick.Add(lifetimeInTicks);
// Add the `DestroyAtTick` component to the entity with the calculated destruction tick.
ecb.AddComponent(entity, new DestroyAtTick { Value = targetTick });
}
// Apply all queued entity modifications.
ecb.Playback(state.EntityManager);
}
}
\ No newline at end of file
......@@ -10,9 +10,20 @@ using UnityEngine;
[UpdateInGroup(typeof(PredictedSimulationSystemGroup))]
public partial class RespawnPlayerSystem : SystemBase
{
/// <summary>
/// Action invoked to update the respawn countdown with the number of seconds remaining until the player respawns.
/// </summary>
public Action<int> OnUpdateRespawnCountdown;
/// <summary>
/// Action invoked when the player respawns.
/// </summary>
public Action OnRespawn;
/// <summary>
/// Called when the system is created. Ensures the system only updates when the required components
/// and entities are present in the world.
/// </summary>
protected override void OnCreate()
{
RequireForUpdate<NetworkTime>();
......@@ -20,6 +31,9 @@ public partial class RespawnPlayerSystem : SystemBase
RequireForUpdate<GamePlayingTag>();
}
/// <summary>
/// Called when the system starts running. Instantiates the respawn entity if it does not already exist.
/// </summary>
protected override void OnStartRunning()
{
if (SystemAPI.HasSingleton<RespawnEntityTag>()) return;
......@@ -27,6 +41,10 @@ public partial class RespawnPlayerSystem : SystemBase
EntityManager.Instantiate(respawnPrefab);
}
/// <summary>
/// Called every frame to process player respawn logic. Handles server-side player instantiation
/// and client-side countdown updates.
/// </summary>
protected override void OnUpdate()
{
var networkTime = SystemAPI.GetSingleton<NetworkTime>();
......@@ -37,6 +55,7 @@ public partial class RespawnPlayerSystem : SystemBase
var ecb = new EntityCommandBuffer(Allocator.Temp);
// Iterate through all entities with a RespawnBufferElement buffer.
foreach (var respawnBuffer in SystemAPI.Query<DynamicBuffer<RespawnBufferElement>>()
.WithAll<RespawnTickCount, Simulate>())
{
......@@ -46,10 +65,12 @@ public partial class RespawnPlayerSystem : SystemBase
{
var curRespawn = respawnBuffer[i];
// Check if the current tick matches or exceeds the respawn tick.
if (currentTick.Equals(curRespawn.RespawnTick) || currentTick.IsNewerThan(curRespawn.RespawnTick))
{
if (isServer)
{
// Server-side logic: Instantiate a new player entity and set its components.
int networkId = SystemAPI.GetComponent<NetworkId>(curRespawn.NetworkEntity).Value;
Entity playerPrefab = SystemAPI.GetSingleton<EntititesReferences>().PlayerPrefabEntity;
......@@ -65,6 +86,7 @@ public partial class RespawnPlayerSystem : SystemBase
}
else
{
// Client-side logic: Invoke the respawn action and create a camera for the new player.
OnRespawn?.Invoke();
if (SystemAPI.TryGetSingleton<NetworkId>(out var clientNetworkId) &&
curRespawn.NetworkId == clientNetworkId.Value)
......@@ -75,6 +97,7 @@ public partial class RespawnPlayerSystem : SystemBase
}
else if (!isServer)
{
// Client-side logic: Update the respawn countdown.
if (SystemAPI.TryGetSingleton<NetworkId>(out var networkId))
{
if (networkId.Value == curRespawn.NetworkId)
......@@ -89,15 +112,21 @@ public partial class RespawnPlayerSystem : SystemBase
}
}
// Remove processed respawn entries from the buffer.
foreach (var respawnIndex in respawnsToCleanup)
{
respawnBuffer.RemoveAt(respawnIndex);
}
}
// Apply all queued entity modifications.
ecb.Playback(EntityManager);
}
/// <summary>
/// Creates a camera for the newly respawned player.
/// </summary>
/// <param name="networkId">The network ID of the player.</param>
private void CreateCameraForNewPlayer(int networkId)
{
GameObject playerCameraGO = new GameObject($"Camera{networkId}");
......
......@@ -2,55 +2,100 @@ using Unity.Entities;
using Unity.Entities.UniversalDelegates;
using UnityEngine;
/// <summary>
/// Represents an aspect for managing enemy spawning logic, including spawn timers,
/// spawn counters, and cooldowns for different enemy types.
/// </summary>
public readonly partial struct EnemySpawnerAspect : IAspect
{
// Reference to the enemy spawn timer component.
private readonly RefRW<EnemySpawnTimer> _enemySpawnTimer;
// Reference to the game start properties component.
private readonly RefRO<GameStartProperties> _gameStartProperties;
// Reference to the spawnable enemies counter component.
private readonly RefRW<SpawnableEnemiesCounter> _spawnableEnemiesCounter;
/// <summary>
/// Gets or sets the time remaining for the next slime enemy spawn.
/// </summary>
public float TimeForNextSlimeSpawn
{
get => _enemySpawnTimer.ValueRO.SlimeSpawnTimer;
set => _enemySpawnTimer.ValueRW.SlimeSpawnTimer = value;
}
/// <summary>
/// Gets or sets the time remaining for the next rogue enemy spawn.
/// </summary>
public float TimeForNextRogueSpawn
{
get => _enemySpawnTimer.ValueRO.RogueSpawnTimer;
set => _enemySpawnTimer.ValueRW.RogueSpawnTimer = value;
}
/// <summary>
/// Gets or sets the random spawn offset used to vary spawn timings.
/// </summary>
public float RandomSpawnOffset
{
get => _enemySpawnTimer.ValueRO.RandomSpawnOffset;
set => _enemySpawnTimer.ValueRW.RandomSpawnOffset = value;
}
/// <summary>
/// Gets the total number of rogue enemies to spawn, as defined in the game start properties.
/// </summary>
public int RogueSpawnAmount => _gameStartProperties.ValueRO.RogueEnemyAmount;
/// <summary>
/// Gets the total number of slime enemies to spawn, as defined in the game start properties.
/// </summary>
public int SlimeSpawnAmount => _gameStartProperties.ValueRO.SlimeEnemyAmount;
/// <summary>
/// Gets or sets the current count of spawned rogue enemies.
/// </summary>
public int RogueEnemyCounter
{
get => _spawnableEnemiesCounter.ValueRO.RogueEnemyCounter;
set => _spawnableEnemiesCounter.ValueRW.RogueEnemyCounter = value;
}
/// <summary>
/// Gets or sets the current count of spawned slime enemies.
/// </summary>
public int SlimeEnemyCounter
{
get => _spawnableEnemiesCounter.ValueRO.SlimeEnemyCounter;
set => _spawnableEnemiesCounter.ValueRW.SlimeEnemyCounter = value;
}
/// <summary>
/// Gets the cooldown duration for spawning slime enemies.
/// </summary>
public float SlimeSpawnCoolDown => _enemySpawnTimer.ValueRO.SlimeSpawnCooldown;
/// <summary>
/// Gets the cooldown duration for spawning rogue enemies.
/// </summary>
public float RogueSpawnCoolDown => _enemySpawnTimer.ValueRO.RogueSpawnCooldown;
/// <summary>
/// Determines whether a slime enemy can spawn based on the timer and spawn limits.
/// </summary>
public bool CanEnemySlimeSpawn => TimeForNextSlimeSpawn <= 0 && SlimeSpawnAmount > SlimeEnemyCounter;
public bool CanEnemyRogueSpawn => TimeForNextRogueSpawn <= 0 && RogueSpawnAmount > RogueEnemyCounter;
/// <summary>
/// Determines whether a rogue enemy can spawn based on the timer and spawn limits.
/// </summary>
public bool CanEnemyRogueSpawn => TimeForNextRogueSpawn <= 0 && RogueSpawnAmount > RogueEnemyCounter;
/// <summary>
/// Decrements the spawn timers and adjusts the random spawn offset over time.
/// </summary>
/// <param name="deltaTime">The time elapsed since the last update.</param>
public void DecrementTimers(float deltaTime)
{
TimeForNextSlimeSpawn -= deltaTime;
......@@ -61,21 +106,33 @@ public readonly partial struct EnemySpawnerAspect : IAspect
RandomSpawnOffset = 5f;
}
/// <summary>
/// Increments the counter for spawned slime enemies.
/// </summary>
public void IncreaseSlimeCounter()
{
SlimeEnemyCounter++;
}
/// <summary>
/// Increments the counter for spawned rogue enemies.
/// </summary>
public void IncreaseRogueCounter()
{
RogueEnemyCounter++;
}
/// <summary>
/// Resets the timer for spawning rogue enemies to the cooldown duration.
/// </summary>
public void ResetRoqueTimer()
{
TimeForNextRogueSpawn = RogueSpawnCoolDown;
}
/// <summary>
/// Resets the timer for spawning slime enemies to the cooldown duration.
/// </summary>
public void ResetSlimeTimer()
{
TimeForNextSlimeSpawn = SlimeSpawnCoolDown;
......
......@@ -5,11 +5,22 @@ using Unity.Physics;
using Unity.Transforms;
using UnityEngine;
/// <summary>
/// System responsible for spawning enemies during the game.
/// Handles the logic for decrementing spawn timers, checking spawn conditions,
/// and instantiating enemies at designated spawn points.
/// </summary>
[WorldSystemFilter(WorldSystemFilterFlags.ServerSimulation)]
partial struct EnemySpawnerSystem : ISystem
{
/// <summary>
/// Called when the system is created. Ensures the system only updates when the required components
/// and entities are present in the world.
/// </summary>
/// <param name="state">The current state of the system.</param>
public void OnCreate(ref SystemState state)
{
// Require specific components and entities for the system to update.
state.RequireForUpdate<GamePlayingTag>();
state.RequireForUpdate<EnemySpawnPoints>();
state.RequireForUpdate<EnemySpawnTimer>();
......@@ -17,17 +28,27 @@ partial struct EnemySpawnerSystem : ISystem
state.RequireForUpdate<BeginSimulationEntityCommandBufferSystem.Singleton>();
}
/// <summary>
/// Called every frame to process enemy spawning logic.
/// Decrements spawn timers, checks if enemies can spawn, and spawns them at designated positions.
/// </summary>
/// <param name="state">The current state of the system.</param>
[BurstCompile]
public void OnUpdate(ref SystemState state)
{
// Retrieve the time elapsed since the last frame.
var deltaTime = SystemAPI.Time.DeltaTime;
// Iterate through all entities with the EnemySpawnerAspect.
foreach (EnemySpawnerAspect aspect in SystemAPI.Query<EnemySpawnerAspect>())
{
// Decrement spawn timers for the current aspect.
aspect.DecrementTimers(deltaTime);
// Check if a slime enemy can spawn.
if (aspect.CanEnemySlimeSpawn)
{
// Retrieve spawn points and the command buffer for entity modifications.
Entity enemyPropertiesEntity = SystemAPI.GetSingletonEntity<EnemySpawnPoints>();
DynamicBuffer<EnemySpawnPoints> spawnPoints =
SystemAPI.GetBuffer<EnemySpawnPoints>(enemyPropertiesEntity);
......@@ -35,17 +56,22 @@ partial struct EnemySpawnerSystem : ISystem
var ecb = ecbSingleton.CreateCommandBuffer(state.WorldUnmanaged);
Entity enemySlimeEntity = SystemAPI.GetSingleton<EntititesReferences>().SlimeEnemyEntity;
// Calculate the spawn position for the slime enemy.
int slimeSpawnIndex = aspect.SlimeEnemyCounter % spawnPoints.Length;
float randomValue = aspect.RandomSpawnOffset;
float3 spawnPosition =
spawnPoints[slimeSpawnIndex].SpawnPoint + new float3(randomValue, 0, -randomValue);
// Spawn the slime enemy and update the aspect's counters and timers.
SpawnEnemy(ecb, enemySlimeEntity, spawnPosition);
aspect.IncreaseSlimeCounter();
aspect.ResetSlimeTimer();
}
// Check if a rogue enemy can spawn.
if (aspect.CanEnemyRogueSpawn)
{
// Retrieve spawn points and the command buffer for entity modifications.
Entity enemyPropertiesEntity = SystemAPI.GetSingletonEntity<EnemySpawnPoints>();
DynamicBuffer<EnemySpawnPoints> spawnPoints =
SystemAPI.GetBuffer<EnemySpawnPoints>(enemyPropertiesEntity);
......@@ -53,10 +79,13 @@ partial struct EnemySpawnerSystem : ISystem
var ecb = ecbSingleton.CreateCommandBuffer(state.WorldUnmanaged);
Entity enemyRogueEntity = SystemAPI.GetSingleton<EntititesReferences>().RougeEnemyEntity;
// Calculate the spawn position for the rogue enemy.
int rogueSpawnIndex = aspect.RogueEnemyCounter % spawnPoints.Length;
float randomValue = aspect.RandomSpawnOffset;
float3 spawnPosition =
spawnPoints[rogueSpawnIndex].SpawnPoint + new float3(randomValue, 0, -randomValue);
// Spawn the rogue enemy and update the aspect's counters and timers.
SpawnEnemy(ecb, enemyRogueEntity, spawnPosition);
aspect.IncreaseRogueCounter();
aspect.ResetRoqueTimer();
......@@ -64,10 +93,16 @@ partial struct EnemySpawnerSystem : ISystem
}
}
/// <summary>
/// Spawns an enemy entity at the specified position.
/// </summary>
/// <param name="ecb">The EntityCommandBuffer used to queue entity modifications.</param>
/// <param name="spawnableEntity">The entity prefab to spawn.</param>
/// <param name="position">The position where the entity should be spawned.</param>
void SpawnEnemy(EntityCommandBuffer ecb, Entity spawnableEntity, float3 position)
{
// Instantiate the enemy entity and set its position.
Entity newEnemy = ecb.Instantiate(spawnableEntity);
ecb.SetComponent(newEnemy,
LocalTransform.FromPosition(position));
ecb.SetComponent(newEnemy, LocalTransform.FromPosition(position));
}
}
\ No newline at end of file
......@@ -5,32 +5,46 @@ using Unity.Mathematics;
using Unity.NetCode;
using UnityEngine;
[UpdateInGroup(typeof(GhostInputSystemGroup))]
public partial class NetcodePlayerInputSystem : SystemBase
{
private InputSystem_Actions _inputActions;
/// <summary>
/// Called when the system is created. Initializes the input actions and ensures the system
/// only updates when the required components and entities are present in the world.
/// </summary>
protected override void OnCreate()
{
// Initialize and enable the input actions.
_inputActions = new InputSystem_Actions();
_inputActions.Enable();
// Require specific components and entities for the system to update.
RequireForUpdate<NetworkStreamInGame>();
RequireForUpdate<GamePlayingTag>();
RequireForUpdate<NetcodePlayerInput>();
}
/// <summary>
/// Called every frame to process player input. Updates the `NetcodePlayerInput` component
/// with the player's movement vector and sprinting state.
/// </summary>
protected override void OnUpdate()
{
// Iterate through all entities with a `NetcodePlayerInput` component and a `GhostOwnerIsLocal` tag.
foreach (RefRW<NetcodePlayerInput> netcodePlayerInput in SystemAPI.Query<RefRW<NetcodePlayerInput>>()
.WithAll<GhostOwnerIsLocal>())
{
// Update the input vector and sprinting state from the input actions.
netcodePlayerInput.ValueRW.inputVector = _inputActions.Player.Move.ReadValue<Vector2>();
netcodePlayerInput.ValueRW.isSprinting = _inputActions.Player.Sprint.IsPressed();
}
}
/// <summary>
/// Called when the system is destroyed. Disables the input actions to clean up resources.
/// </summary>
protected override void OnDestroy()
{
base.OnDestroy();
......
......@@ -6,12 +6,23 @@ using Unity.Physics;
using Unity.Transforms;
using UnityEngine;
/// <summary>
/// System responsible for handling player movement in a netcode environment.
/// This system processes player input, updates physics velocity, and adjusts player rotation
/// based on movement direction and sprinting state.
/// </summary>
[UpdateInGroup(typeof(PredictedSimulationSystemGroup))]
partial struct NetcodePlayerMovementSystem : ISystem
{
/// <summary>
/// Called when the system is created. Ensures the system only updates when the required components
/// and entities are present in the world.
/// </summary>
/// <param name="state">The current state of the system.</param>
[BurstCompile]
public void OnCreate(ref SystemState state)
{
// Require specific components and entities for the system to update.
state.RequireForUpdate<NetcodePlayerInput>();
state.RequireForUpdate<PhysicsVelocity>();
state.RequireForUpdate<LocalTransform>();
......@@ -19,11 +30,18 @@ partial struct NetcodePlayerMovementSystem : ISystem
state.RequireForUpdate<GamePlayingTag>();
}
/// <summary>
/// Called every frame to process player movement logic.
/// Updates the player's velocity and rotation based on input and sprinting state.
/// </summary>
/// <param name="state">The current state of the system.</param>
[BurstCompile]
public void OnUpdate(ref SystemState state)
{
// Retrieve the time elapsed since the last frame.
float deltaTime = SystemAPI.Time.DeltaTime;
// Iterate through all entities with the required components.
foreach ((RefRO<NetcodePlayerInput> netcodePlayerInput, RefRW<PhysicsVelocity> physicsVelocity,
RefRW<LocalTransform> localTransform, RefRW<PlayerSprintData> sprintData)
in SystemAPI
......@@ -31,13 +49,17 @@ partial struct NetcodePlayerMovementSystem : ISystem
RefRW<PlayerSprintData>>()
.WithAll<Simulate>())
{
// Calculate the movement vector based on player input.
float3 moveVector = new float3(netcodePlayerInput.ValueRO.inputVector.x, 0,
netcodePlayerInput.ValueRO.inputVector.y);
// Set the default movement speed to walking speed.
float moveSpeed = sprintData.ValueRO.walkSpeed;
// Check if the player is sprinting.
if (netcodePlayerInput.ValueRO.isSprinting)
{
// Handle sprinting logic and cooldowns.
if (!sprintData.ValueRO.isSprintCooldown)
{
sprintData.ValueRW.sprintRemaining -= deltaTime;
......@@ -53,6 +75,7 @@ partial struct NetcodePlayerMovementSystem : ISystem
}
}
// Handle sprint cooldown logic.
if (sprintData.ValueRO.isSprintCooldown)
{
sprintData.ValueRW.sprintCooldown -= deltaTime;
......@@ -62,10 +85,11 @@ partial struct NetcodePlayerMovementSystem : ISystem
}
}
// Smoothly update the player's velocity based on the movement vector and speed.
physicsVelocity.ValueRW.Linear =
math.lerp(physicsVelocity.ValueRO.Linear, moveVector * moveSpeed, deltaTime * 10);
// Update the player's rotation to face the movement direction if moving.
if (!math.all(moveVector == float3.zero))
{
quaternion targetRotation = quaternion.LookRotationSafe(moveVector, math.up());
......
......@@ -222,7 +222,7 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 7e19d579a4586dd40805276eb2d1a684, type: 3}
m_Name:
m_EditorClassIdentifier:
DestroyOnTimer: 2
DestroyOnTimer: 3
--- !u!114 &1149029900162062154
MonoBehaviour:
m_ObjectHideFlags: 0
......@@ -298,7 +298,7 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 2de495c310a322143be08aa7819a1a33, type: 3}
m_Name:
m_EditorClassIdentifier:
DamageOnTrigger: 0
DamageOnTrigger: 20
--- !u!1001 &7309850180045348080
PrefabInstance:
m_ObjectHideFlags: 0
......
......@@ -103,7 +103,7 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 2de495c310a322143be08aa7819a1a33, type: 3}
m_Name:
m_EditorClassIdentifier:
DamageOnTrigger: 50
DamageOnTrigger: 10
--- !u!114 &-8527907258641966621
MonoBehaviour:
m_ObjectHideFlags: 0
......@@ -129,7 +129,7 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: d8b4a5cfe7236db4dba833c5751c2665, type: 3}
m_Name:
m_EditorClassIdentifier:
NpcTargetRadius: 80
NpcTargetRadius: 10
--- !u!114 &-274669407858188047
MonoBehaviour:
m_ObjectHideFlags: 0
......
......@@ -100,7 +100,7 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 8194ce63a01ea9044a8c5a6ebd0e2829, type: 3}
m_Name:
m_EditorClassIdentifier:
NpcTargetRadius: 80
NpcTargetRadius: 13
AttackCooldownTime: 2
FirePointOffset: {x: 0, y: 1.5, z: 0}
AttackPrefab: {fileID: 357967689053387235, guid: 19127a4c1ac11844db4373b9e147918b, type: 3}
......
......@@ -149,8 +149,8 @@ MonoBehaviour:
m_EditorClassIdentifier:
sprintRemaining: 5
sprintDuration: 5
sprintSpeed: 12
walkSpeed: 8
sprintSpeed: 7
walkSpeed: 5
sprintCooldownReset: 1
--- !u!114 &3955402020692204287
MonoBehaviour:
......@@ -320,7 +320,7 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 8e73c985659b81d408f4e99cb152349d, type: 3}
m_Name:
m_EditorClassIdentifier:
MaxHitPoints: 1000
MaxHitPoints: 200
HealthBarOffset: {x: 0, y: 2.5, z: 0}
--- !u!1001 &6975352639711469968
PrefabInstance:
......
......@@ -63,7 +63,7 @@ MonoBehaviour:
OptimizationMode: 0
Importance: 10
MaxSendRate: 0
prefabId:
prefabId: b3cdfc2eac1a20f4fa01943b331adf7e
HasOwner: 0
SupportAutoCommandTarget: 1
TrackInterpolationDelay: 0
......@@ -84,4 +84,4 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier:
RespawnTime: 10
NetCodeConfig: {fileID: 11400000, guid: cd69de227738309429193c9089949c16, type: 2}
NetCodeConfig: {fileID: 11400000, guid: edda5bd0f33a95e4a98f780e0e3ae9e8, type: 2}
......@@ -489,9 +489,9 @@ MonoBehaviour:
RangerAmountContainer: {fileID: 7438304585132253662}
SlimeAmountContainer: {fileID: 7438304585132253661}
_connectButton: {fileID: 661734915}
_gameStartCountDownTime: 2
_slimeSpawnCooldownTime: 0.01
_rogueSpawnCooldownTime: 0.1
_gameStartCountDownTime: 10
_slimeSpawnCooldownTime: 1
_rogueSpawnCooldownTime: 2
--- !u!114 &1734898360 stripped
MonoBehaviour:
m_CorrespondingSourceObject: {fileID: 8650075725555599494, guid: 72327351aa28fbe4fb4c16c3d17e4de5, type: 3}
......
......@@ -336,6 +336,26 @@ PrefabInstance:
propertyPath: m_LocalEulerAnglesHint.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4973084550295746815, guid: dc695439125c44a4190bdc008aa5a0fc, type: 3}
propertyPath: m_IsActive
value: 0
objectReference: {fileID: 0}
- target: {fileID: 5689824232254750683, guid: dc695439125c44a4190bdc008aa5a0fc, type: 3}
propertyPath: m_text
value: 10
objectReference: {fileID: 0}
- target: {fileID: 5823419856895464815, guid: dc695439125c44a4190bdc008aa5a0fc, type: 3}
propertyPath: m_text
value: 10
objectReference: {fileID: 0}
- target: {fileID: 5823419856895464815, guid: dc695439125c44a4190bdc008aa5a0fc, type: 3}
propertyPath: 'm_ActiveFontFeatures.Array.data[0]'
value: 1801810542
objectReference: {fileID: 0}
- target: {fileID: 8914969948828194284, guid: dc695439125c44a4190bdc008aa5a0fc, type: 3}
propertyPath: m_IsActive
value: 0
objectReference: {fileID: 0}
m_RemovedComponents: []
m_RemovedGameObjects: []
m_AddedGameObjects: []
......
......@@ -9,11 +9,11 @@ EditorBuildSettings:
path: Assets/_Game/Scenes/ConnectionScene.unity
guid: 5652506fc72e4fa42a51d693df592812
- enabled: 1
path: Assets/_Game/Scenes/TestGameScene.unity
guid: a3178e3c90053df499a40c2abd214157
- enabled: 1
path: Assets/_Game/Scenes/GameScene.unity
guid: 99c9720ab356a0642a771bea13969a05
- enabled: 1
path: Assets/_Game/Scenes/TestGameScene.unity
guid: a3178e3c90053df499a40c2abd214157
m_configObjects:
com.unity.input.settings.actions: {fileID: -944628639613478452, guid: 14b037eabac02c946b6a54f5e7289a64, type: 3}
m_UseUCBPForAssetBundles: 0
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment