Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
Multiplayer-arena-game
/
Multiplayer-arena-game-Entities
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
09de87fa
authored
May 15, 2025
by
alsunj
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
add documentation
parent
cb7803e0
Hide whitespace changes
Inline
Side-by-side
Showing
31 changed files
with
761 additions
and
122 deletions
Assets/_Game/Code/Systems/Combat/ApplyDamageSystem.cs
Assets/_Game/Code/Systems/Combat/CalculateFrameDamageSystem.cs
Assets/_Game/Code/Systems/Combat/DamageOnTriggerSystem.cs
Assets/_Game/Code/Systems/Combat/HealthBarSystem.cs
Assets/_Game/Code/Systems/Combat/MoveAbilitySystem.cs
Assets/_Game/Code/Systems/Combat/MoveSlimeSystem.cs
Assets/_Game/Code/Systems/Combat/MoveSlimeSystem.cs.meta
Assets/_Game/Code/Systems/Combat/RogueAttackSystem.cs
Assets/_Game/Code/Systems/Combat/SetTargetSlimeSystem.cs
Assets/_Game/Code/Systems/Combat/SetTargetSlimeSystem.cs.meta
Assets/_Game/Code/Systems/Combat/TargetingSystem.cs
Assets/_Game/Code/Systems/Connection/ClientRequestGameEntrySystem.cs
Assets/_Game/Code/Systems/Connection/ClientStartGameSystem.cs
Assets/_Game/Code/Systems/Connection/CountDownToGameStartSystem.cs
Assets/_Game/Code/Systems/Connection/GoInGameServerSystem.cs
Assets/_Game/Code/Systems/Destroy/DestroyEntitySystem.cs
Assets/_Game/Code/Systems/Destroy/DestroyOnTimerSystem.cs
Assets/_Game/Code/Systems/Destroy/InitializeDestroyOnTimerSystem.cs
Assets/_Game/Code/Systems/Destroy/RespawnPlayerSystem.cs
Assets/_Game/Code/Systems/Enemy/EnemySpawnerAspect.cs
Assets/_Game/Code/Systems/Enemy/EnemySpawnerSystem.cs
Assets/_Game/Code/Systems/Input/NetcodePlayerInputSystem.cs
Assets/_Game/Code/Systems/Input/NetcodePlayerMovementSystem.cs
Assets/_Game/Prefabs/Enemies/Arrow.prefab
Assets/_Game/Prefabs/Enemies/Cube.prefab
Assets/_Game/Prefabs/Enemies/RogueEnemy.prefab
Assets/_Game/Prefabs/Player/Player.prefab
Assets/_Game/Prefabs/Player/RespawnEntity.prefab
Assets/_Game/Scenes/ConnectionScene.unity
Assets/_Game/Scenes/GameScene.unity
ProjectSettings/EditorBuildSettings.asset
Assets/_Game/Code/Systems/Combat/ApplyDamageSystem.cs
View file @
09de87fa
...
@@ -3,37 +3,56 @@ using Unity.Collections;
...
@@ -3,37 +3,56 @@ using Unity.Collections;
using
Unity.Entities
;
using
Unity.Entities
;
using
Unity.NetCode
;
using
Unity.NetCode
;
[UpdateInGroup(typeof(PredictedSimulationSystemGroup), OrderLast = true)]
[UpdateInGroup(typeof(PredictedSimulationSystemGroup), OrderLast = true)]
[UpdateAfter(typeof(CalculateFrameDamageSystem))]
[UpdateAfter(typeof(CalculateFrameDamageSystem))]
public
partial
struct
ApplyDamageSystem
:
ISystem
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
)
public
void
OnCreate
(
ref
SystemState
state
)
{
{
state
.
RequireForUpdate
<
NetworkTime
>();
state
.
RequireForUpdate
<
NetworkTime
>();
state
.
RequireForUpdate
<
GamePlayingTag
>();
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
]
[
BurstCompile
]
public
void
OnUpdate
(
ref
SystemState
state
)
public
void
OnUpdate
(
ref
SystemState
state
)
{
{
// Get the current server tick from the NetworkTime singleton.
var
currentTick
=
SystemAPI
.
GetSingleton
<
NetworkTime
>().
ServerTick
;
var
currentTick
=
SystemAPI
.
GetSingleton
<
NetworkTime
>().
ServerTick
;
// Create a temporary EntityCommandBuffer to queue entity modifications.
var
ecb
=
new
EntityCommandBuffer
(
Allocator
.
Temp
);
var
ecb
=
new
EntityCommandBuffer
(
Allocator
.
Temp
);
// Query entities with CurrentHitPoints, DamageThisTick buffer, and Simulate tag.
foreach
(
var
(
currentHitPoints
,
damageThisTickBuffer
,
entity
)
in
SystemAPI
foreach
(
var
(
currentHitPoints
,
damageThisTickBuffer
,
entity
)
in
SystemAPI
.
Query
<
RefRW
<
CurrentHitPoints
>,
DynamicBuffer
<
DamageThisTick
>>().
WithAll
<
Simulate
>()
.
Query
<
RefRW
<
CurrentHitPoints
>,
DynamicBuffer
<
DamageThisTick
>>().
WithAll
<
Simulate
>()
.
WithEntityAccess
())
.
WithEntityAccess
())
{
{
// Skip if no damage data exists for the current tick.
if
(!
damageThisTickBuffer
.
GetDataAtTick
(
currentTick
,
out
var
damageThisTick
))
continue
;
if
(!
damageThisTickBuffer
.
GetDataAtTick
(
currentTick
,
out
var
damageThisTick
))
continue
;
if
(
damageThisTick
.
Tick
!=
currentTick
)
continue
;
if
(
damageThisTick
.
Tick
!=
currentTick
)
continue
;
// Apply the damage to the entity's current hit points.
currentHitPoints
.
ValueRW
.
Value
-=
damageThisTick
.
Value
;
currentHitPoints
.
ValueRW
.
Value
-=
damageThisTick
.
Value
;
// If hit points drop to zero or below, mark the entity for destruction.
if
(
currentHitPoints
.
ValueRO
.
Value
<=
0
)
if
(
currentHitPoints
.
ValueRO
.
Value
<=
0
)
{
{
ecb
.
AddComponent
<
DestroyEntityTag
>(
entity
);
ecb
.
AddComponent
<
DestroyEntityTag
>(
entity
);
}
}
}
}
// Apply all queued entity modifications.
ecb
.
Playback
(
state
.
EntityManager
);
ecb
.
Playback
(
state
.
EntityManager
);
}
}
}
}
\ No newline at end of file
Assets/_Game/Code/Systems/Combat/CalculateFrameDamageSystem.cs
View file @
09de87fa
...
@@ -2,9 +2,18 @@ using Unity.Burst;
...
@@ -2,9 +2,18 @@ using Unity.Burst;
using
Unity.Entities
;
using
Unity.Entities
;
using
Unity.NetCode
;
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)]
[UpdateInGroup(typeof(PredictedSimulationSystemGroup), OrderLast = true)]
public
partial
struct
CalculateFrameDamageSystem
:
ISystem
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
)
public
void
OnCreate
(
ref
SystemState
state
)
{
{
state
.
RequireForUpdate
<
NetworkTime
>();
state
.
RequireForUpdate
<
NetworkTime
>();
...
@@ -12,32 +21,44 @@ public partial struct CalculateFrameDamageSystem : ISystem
...
@@ -12,32 +21,44 @@ public partial struct CalculateFrameDamageSystem : ISystem
state
.
RequireForUpdate
<
DamageBufferElement
>();
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
]
[
BurstCompile
]
public
void
OnUpdate
(
ref
SystemState
state
)
public
void
OnUpdate
(
ref
SystemState
state
)
{
{
// Retrieve the current server tick from the NetworkTime singleton.
var
currentTick
=
SystemAPI
.
GetSingleton
<
NetworkTime
>().
ServerTick
;
var
currentTick
=
SystemAPI
.
GetSingleton
<
NetworkTime
>().
ServerTick
;
// Iterate over entities with DamageBufferElement and DamageThisTick buffers, and the Simulate tag.
foreach
(
var
(
damageBuffer
,
damageThisTickBuffer
)
in
SystemAPI
foreach
(
var
(
damageBuffer
,
damageThisTickBuffer
)
in
SystemAPI
.
Query
<
DynamicBuffer
<
DamageBufferElement
>,
DynamicBuffer
<
DamageThisTick
>>()
.
Query
<
DynamicBuffer
<
DamageBufferElement
>,
DynamicBuffer
<
DamageThisTick
>>()
.
WithAll
<
Simulate
>())
.
WithAll
<
Simulate
>())
{
{
// If the damage buffer is empty, add a zero-damage entry for the current tick.
if
(
damageBuffer
.
IsEmpty
)
if
(
damageBuffer
.
IsEmpty
)
{
{
damageThisTickBuffer
.
AddCommandData
(
new
DamageThisTick
{
Tick
=
currentTick
,
Value
=
0
});
damageThisTickBuffer
.
AddCommandData
(
new
DamageThisTick
{
Tick
=
currentTick
,
Value
=
0
});
}
}
else
else
{
{
// Calculate the total damage for the current tick.
var
totalDamage
=
0
;
var
totalDamage
=
0
;
if
(
damageThisTickBuffer
.
GetDataAtTick
(
currentTick
,
out
var
damageThisTick
))
if
(
damageThisTickBuffer
.
GetDataAtTick
(
currentTick
,
out
var
damageThisTick
))
{
{
totalDamage
=
damageThisTick
.
Value
;
totalDamage
=
damageThisTick
.
Value
;
}
}
// Add up all damage values from the damage buffer.
foreach
(
var
damage
in
damageBuffer
)
foreach
(
var
damage
in
damageBuffer
)
{
{
totalDamage
+=
damage
.
Value
;
totalDamage
+=
damage
.
Value
;
}
}
// Store the total damage in the DamageThisTick buffer and clear the damage buffer.
damageThisTickBuffer
.
AddCommandData
(
new
DamageThisTick
{
Tick
=
currentTick
,
Value
=
totalDamage
});
damageThisTickBuffer
.
AddCommandData
(
new
DamageThisTick
{
Tick
=
currentTick
,
Value
=
totalDamage
});
damageBuffer
.
Clear
();
damageBuffer
.
Clear
();
}
}
...
...
Assets/_Game/Code/Systems/Combat/DamageOnTriggerSystem.cs
View file @
09de87fa
...
@@ -4,10 +4,20 @@ using Unity.Entities;
...
@@ -4,10 +4,20 @@ using Unity.Entities;
using
Unity.Physics
;
using
Unity.Physics
;
using
Unity.Physics.Systems
;
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))]
[UpdateInGroup(typeof(PhysicsSystemGroup))]
[UpdateAfter(typeof(PhysicsSimulationGroup))]
[UpdateAfter(typeof(PhysicsSimulationGroup))]
public
partial
struct
DamageOnTriggerSystem
:
ISystem
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
)
public
void
OnCreate
(
ref
SystemState
state
)
{
{
state
.
RequireForUpdate
<
SimulationSingleton
>();
state
.
RequireForUpdate
<
SimulationSingleton
>();
...
@@ -15,11 +25,19 @@ public partial struct DamageOnTriggerSystem : ISystem
...
@@ -15,11 +25,19 @@ public partial struct DamageOnTriggerSystem : ISystem
state
.
RequireForUpdate
<
GamePlayingTag
>();
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
]
[
BurstCompile
]
public
void
OnUpdate
(
ref
SystemState
state
)
public
void
OnUpdate
(
ref
SystemState
state
)
{
{
// Retrieve the singleton for the EndSimulationEntityCommandBufferSystem.
var
ecbSingleton
=
SystemAPI
.
GetSingleton
<
EndSimulationEntityCommandBufferSystem
.
Singleton
>();
var
ecbSingleton
=
SystemAPI
.
GetSingleton
<
EndSimulationEntityCommandBufferSystem
.
Singleton
>();
var
damageOnCollisionJob
=
new
DamageOnTriggerJob
// Create and configure the DamageOnTriggerJob.
var
damageOnTriggerJob
=
new
DamageOnTriggerJob
{
{
DamageOnTriggerLookup
=
SystemAPI
.
GetComponentLookup
<
DamageOnTrigger
>(
true
),
DamageOnTriggerLookup
=
SystemAPI
.
GetComponentLookup
<
DamageOnTrigger
>(
true
),
AlreadyDamagedLookup
=
SystemAPI
.
GetBufferLookup
<
AlreadyDamagedEntity
>(
true
),
AlreadyDamagedLookup
=
SystemAPI
.
GetBufferLookup
<
AlreadyDamagedEntity
>(
true
),
...
@@ -27,21 +45,42 @@ public partial struct DamageOnTriggerSystem : ISystem
...
@@ -27,21 +45,42 @@ public partial struct DamageOnTriggerSystem : ISystem
ECB
=
ecbSingleton
.
CreateCommandBuffer
(
state
.
WorldUnmanaged
)
ECB
=
ecbSingleton
.
CreateCommandBuffer
(
state
.
WorldUnmanaged
)
};
};
// Retrieve the SimulationSingleton and schedule the job.
var
simulationSingleton
=
SystemAPI
.
GetSingleton
<
SimulationSingleton
>();
var
simulationSingleton
=
SystemAPI
.
GetSingleton
<
SimulationSingleton
>();
state
.
Dependency
=
damageOn
Collision
Job
.
Schedule
(
simulationSingleton
,
state
.
Dependency
);
state
.
Dependency
=
damageOn
Trigger
Job
.
Schedule
(
simulationSingleton
,
state
.
Dependency
);
}
}
}
}
/// <summary>
//team lookup is no more necessary as the enemy only collides with player now.
/// Job that processes trigger events and applies damage to entities based on their interactions.
/// </summary>
public
struct
DamageOnTriggerJob
:
ITriggerEventsJob
public
struct
DamageOnTriggerJob
:
ITriggerEventsJob
{
{
/// <summary>
/// Read-only lookup for DamageOnTrigger components to determine damage-dealing entities.
/// </summary>
[
ReadOnly
]
public
ComponentLookup
<
DamageOnTrigger
>
DamageOnTriggerLookup
;
[
ReadOnly
]
public
ComponentLookup
<
DamageOnTrigger
>
DamageOnTriggerLookup
;
/// <summary>
/// Lookup for AlreadyDamagedEntity buffers to track entities that have already been damaged.
/// </summary>
public
BufferLookup
<
AlreadyDamagedEntity
>
AlreadyDamagedLookup
;
public
BufferLookup
<
AlreadyDamagedEntity
>
AlreadyDamagedLookup
;
/// <summary>
/// Lookup for DamageBufferElement buffers to store damage values for entities.
/// </summary>
public
BufferLookup
<
DamageBufferElement
>
DamageBufferLookup
;
public
BufferLookup
<
DamageBufferElement
>
DamageBufferLookup
;
/// <summary>
/// EntityCommandBuffer used to queue entity modifications during the job execution.
/// </summary>
public
EntityCommandBuffer
ECB
;
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
)
public
void
Execute
(
TriggerEvent
triggerEvent
)
{
{
Entity
entityA
=
triggerEvent
.
EntityA
;
Entity
entityA
=
triggerEvent
.
EntityA
;
...
@@ -49,6 +88,7 @@ public struct DamageOnTriggerJob : ITriggerEventsJob
...
@@ -49,6 +88,7 @@ public struct DamageOnTriggerJob : ITriggerEventsJob
Entity
damageDealingEntity
=
Entity
.
Null
;
Entity
damageDealingEntity
=
Entity
.
Null
;
Entity
damageReceivingEntity
=
Entity
.
Null
;
Entity
damageReceivingEntity
=
Entity
.
Null
;
// Determine which entity is dealing damage and which is receiving damage.
if
(
DamageBufferLookup
.
HasBuffer
(
entityA
)
&&
if
(
DamageBufferLookup
.
HasBuffer
(
entityA
)
&&
DamageOnTriggerLookup
.
HasComponent
(
entityB
))
DamageOnTriggerLookup
.
HasComponent
(
entityB
))
{
{
...
@@ -66,6 +106,7 @@ public struct DamageOnTriggerJob : ITriggerEventsJob
...
@@ -66,6 +106,7 @@ public struct DamageOnTriggerJob : ITriggerEventsJob
return
;
return
;
}
}
// Check if the damage-dealing entity has already damaged the receiving entity.
if
(
AlreadyDamagedLookup
.
HasBuffer
(
damageDealingEntity
))
if
(
AlreadyDamagedLookup
.
HasBuffer
(
damageDealingEntity
))
{
{
var
alreadyDamagedBuffer
=
AlreadyDamagedLookup
[
damageDealingEntity
];
var
alreadyDamagedBuffer
=
AlreadyDamagedLookup
[
damageDealingEntity
];
...
@@ -76,9 +117,11 @@ public struct DamageOnTriggerJob : ITriggerEventsJob
...
@@ -76,9 +117,11 @@ public struct DamageOnTriggerJob : ITriggerEventsJob
}
}
else
else
{
{
// Add a buffer to track already damaged entities if it doesn't exist.
ECB
.
AddBuffer
<
AlreadyDamagedEntity
>(
damageDealingEntity
);
ECB
.
AddBuffer
<
AlreadyDamagedEntity
>(
damageDealingEntity
);
}
}
// Apply damage and mark the damage-dealing entity for destruction if applicable.
if
(
DamageOnTriggerLookup
.
TryGetComponent
(
damageDealingEntity
,
out
var
damageOnTrigger
))
if
(
DamageOnTriggerLookup
.
TryGetComponent
(
damageDealingEntity
,
out
var
damageOnTrigger
))
{
{
ECB
.
AddComponent
<
DestroyEntityTag
>(
damageDealingEntity
);
ECB
.
AddComponent
<
DestroyEntityTag
>(
damageDealingEntity
);
...
...
Assets/_Game/Code/Systems/Combat/HealthBarSystem.cs
View file @
09de87fa
...
@@ -3,54 +3,87 @@ using Unity.Transforms;
...
@@ -3,54 +3,87 @@ using Unity.Transforms;
using
UnityEngine
;
using
UnityEngine
;
using
UnityEngine.UI
;
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))]
[UpdateAfter(typeof(TransformSystemGroup))]
[WorldSystemFilter(WorldSystemFilterFlags.ClientSimulation)]
[WorldSystemFilter(WorldSystemFilterFlags.ClientSimulation)]
public
partial
struct
HealthBarSystem
:
ISystem
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
)
public
void
OnCreate
(
ref
SystemState
state
)
{
{
state
.
RequireForUpdate
<
EndSimulationEntityCommandBufferSystem
.
Singleton
>();
state
.
RequireForUpdate
<
EndSimulationEntityCommandBufferSystem
.
Singleton
>();
state
.
RequireForUpdate
<
UIPrefabs
>();
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
)
public
void
OnUpdate
(
ref
SystemState
state
)
{
{
// Retrieve the EntityCommandBuffer for queuing entity modifications.
var
ecbSingleton
=
SystemAPI
.
GetSingleton
<
EndSimulationEntityCommandBufferSystem
.
Singleton
>();
var
ecbSingleton
=
SystemAPI
.
GetSingleton
<
EndSimulationEntityCommandBufferSystem
.
Singleton
>();
var
ecb
=
ecbSingleton
.
CreateCommandBuffer
(
state
.
WorldUnmanaged
);
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
foreach
(
var
(
transform
,
healthBarOffset
,
maxHitPoints
,
entity
)
in
SystemAPI
.
Query
<
LocalTransform
,
HealthBarOffset
,
MaxHitPoints
>().
WithNone
<
HealthBarUIReference
>()
.
Query
<
LocalTransform
,
HealthBarOffset
,
MaxHitPoints
>().
WithNone
<
HealthBarUIReference
>()
.
WithEntityAccess
())
.
WithEntityAccess
())
{
{
// Instantiate a new health bar at the entity's position plus the offset.
var
healthBarPrefab
=
SystemAPI
.
ManagedAPI
.
GetSingleton
<
UIPrefabs
>().
PlayerHealthUIEntity
;
var
healthBarPrefab
=
SystemAPI
.
ManagedAPI
.
GetSingleton
<
UIPrefabs
>().
PlayerHealthUIEntity
;
var
spawnPosition
=
transform
.
Position
+
healthBarOffset
.
Value
;
var
spawnPosition
=
transform
.
Position
+
healthBarOffset
.
Value
;
var
newHealthBar
=
Object
.
Instantiate
(
healthBarPrefab
,
spawnPosition
,
Quaternion
.
identity
);
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
);
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
});
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
foreach
(
var
(
transform
,
healthBarOffset
,
currentHitPoints
,
maxHitPoints
,
healthBarUI
)
in
SystemAPI
.
Query
<
LocalTransform
,
HealthBarOffset
,
CurrentHitPoints
,
MaxHitPoints
,
HealthBarUIReference
>())
.
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
;
var
healthBarPosition
=
transform
.
Position
+
healthBarOffset
.
Value
;
healthBarUI
.
Value
.
transform
.
position
=
healthBarPosition
;
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
);
SetHealthBar
(
healthBarUI
.
Value
,
currentHitPoints
.
Value
,
maxHitPoints
.
Value
);
}
}
// Cleanup health bar
once associated entity is destroyed
// Cleanup health bar
s for entities that no longer exist.
foreach
(
var
(
healthBarUI
,
entity
)
in
SystemAPI
.
Query
<
HealthBarUIReference
>().
WithNone
<
LocalTransform
>()
foreach
(
var
(
healthBarUI
,
entity
)
in
SystemAPI
.
Query
<
HealthBarUIReference
>().
WithNone
<
LocalTransform
>()
.
WithEntityAccess
())
.
WithEntityAccess
())
{
{
// Destroy the health bar GameObject and remove the reference component from the entity.
Object
.
Destroy
(
healthBarUI
.
Value
);
Object
.
Destroy
(
healthBarUI
.
Value
);
ecb
.
RemoveComponent
<
HealthBarUIReference
>(
entity
);
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
)
private
void
SetHealthBar
(
GameObject
healthBarCanvasObject
,
int
curHitPoints
,
int
maxHitPoints
)
{
{
// Retrieve the Slider component from the health bar GameObject.
var
healthBarSlider
=
healthBarCanvasObject
.
GetComponentInChildren
<
Slider
>();
var
healthBarSlider
=
healthBarCanvasObject
.
GetComponentInChildren
<
Slider
>();
// Set the slider's minimum, maximum, and current values.
healthBarSlider
.
minValue
=
0
;
healthBarSlider
.
minValue
=
0
;
healthBarSlider
.
maxValue
=
maxHitPoints
;
healthBarSlider
.
maxValue
=
maxHitPoints
;
healthBarSlider
.
value
=
curHitPoints
;
healthBarSlider
.
value
=
curHitPoints
;
...
...
Assets/_Game/Code/Systems/Combat/MoveAbilitySystem.cs
View file @
09de87fa
...
@@ -4,15 +4,28 @@ using Unity.Physics;
...
@@ -4,15 +4,28 @@ using Unity.Physics;
using
Unity.Transforms
;
using
Unity.Transforms
;
using
Unity.NetCode
;
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))]
[UpdateInGroup(typeof(PredictedSimulationSystemGroup))]
public
partial
struct
MoveAbilitySystem
:
ISystem
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
)
public
void
OnCreate
(
ref
SystemState
state
)
{
{
state
.
RequireForUpdate
<
GamePlayingTag
>();
state
.
RequireForUpdate
<
GamePlayingTag
>();
state
.
RequireForUpdate
<
AbilityMoveSpeed
>();
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
)
public
void
OnUpdate
(
ref
SystemState
state
)
{
{
state
.
Dependency
=
new
MoveAbilityJob
()
state
.
Dependency
=
new
MoveAbilityJob
()
...
@@ -20,10 +33,21 @@ public partial struct MoveAbilitySystem : ISystem
...
@@ -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]
[BurstCompile]
[WithNone(typeof(SlimeTag))]
[WithNone(typeof(SlimeTag))]
public
partial
struct
MoveAbilityJob
:
IJobEntity
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
]
[
BurstCompile
]
private
void
Execute
(
private
void
Execute
(
ref
PhysicsVelocity
velocity
,
ref
PhysicsVelocity
velocity
,
...
...
Assets/_Game/Code/Systems/Combat/MoveSlimeSystem.cs
View file @
09de87fa
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
Assets/_Game/Code/Systems/Combat/MoveSlimeSystem.cs.meta
View file @
09de87fa
fileFormatVersion: 2
fileFormatVersion: 2
guid: 3783f791f48f7b047a3354bfe6fc88c9
guid: 86f8c4a7fca9c2244981a82be26b0c7d
\ No newline at end of file
\ No newline at end of file
Assets/_Game/Code/Systems/Combat/RogueAttackSystem.cs
View file @
09de87fa
...
@@ -5,9 +5,18 @@ using Unity.Mathematics;
...
@@ -5,9 +5,18 @@ using Unity.Mathematics;
using
Unity.NetCode
;
using
Unity.NetCode
;
using
Unity.Transforms
;
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))]
[UpdateInGroup(typeof(PredictedSimulationSystemGroup))]
public
partial
struct
RogueAttackSystem
:
ISystem
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
)
public
void
OnCreate
(
ref
SystemState
state
)
{
{
state
.
RequireForUpdate
<
NetworkTime
>();
state
.
RequireForUpdate
<
NetworkTime
>();
...
@@ -15,11 +24,17 @@ public partial struct RogueAttackSystem : ISystem
...
@@ -15,11 +24,17 @@ public partial struct RogueAttackSystem : ISystem
state
.
RequireForUpdate
<
GamePlayingTag
>();
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
)
public
void
OnUpdate
(
ref
SystemState
state
)
{
{
// Retrieve the EntityCommandBuffer singleton and the current network time.
var
ecbSingleton
=
SystemAPI
.
GetSingleton
<
BeginSimulationEntityCommandBufferSystem
.
Singleton
>();
var
ecbSingleton
=
SystemAPI
.
GetSingleton
<
BeginSimulationEntityCommandBufferSystem
.
Singleton
>();
var
networkTime
=
SystemAPI
.
GetSingleton
<
NetworkTime
>();
var
networkTime
=
SystemAPI
.
GetSingleton
<
NetworkTime
>();
// Schedule the NpcAttackJob to handle NPC attack logic.
state
.
Dependency
=
new
NpcAttackJob
state
.
Dependency
=
new
NpcAttackJob
{
{
CurrentTick
=
networkTime
.
ServerTick
,
CurrentTick
=
networkTime
.
ServerTick
,
...
@@ -29,51 +44,85 @@ public partial struct RogueAttackSystem : ISystem
...
@@ -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]
[BurstCompile]
[WithAll(typeof(Simulate))]
[WithAll(typeof(Simulate))]
[WithNone(typeof(SlimeTag))]
[WithNone(typeof(SlimeTag))]
public
partial
struct
NpcAttackJob
:
IJobEntity
public
partial
struct
NpcAttackJob
:
IJobEntity
{
{
/// <summary>
/// The current server tick used to determine attack cooldowns.
/// </summary>
[
ReadOnly
]
public
NetworkTick
CurrentTick
;
[
ReadOnly
]
public
NetworkTick
CurrentTick
;
/// <summary>
/// Read-only lookup for LocalTransform components to access entity positions and rotations.
/// </summary>
[
ReadOnly
]
public
ComponentLookup
<
LocalTransform
>
TransformLookup
;
[
ReadOnly
]
public
ComponentLookup
<
LocalTransform
>
TransformLookup
;
/// <summary>
/// Parallel writer for queuing entity modifications during the job execution.
/// </summary>
public
EntityCommandBuffer
.
ParallelWriter
ECB
;
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
]
[
BurstCompile
]
private
void
Execute
(
ref
DynamicBuffer
<
NpcAttackCooldown
>
attackCooldown
,
in
NpcAttackProperties
attackProperties
,
private
void
Execute
(
in
NpcTargetEntity
targetEntity
,
Entity
enemyEntity
,
TeamTypes
team
,
[
ChunkIndexInQuery
]
int
sortKey
)
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
;
if
(!
TransformLookup
.
HasComponent
(
targetEntity
.
Value
))
return
;
// Retrieve or initialize the cooldown expiration tick.
if
(!
attackCooldown
.
GetDataAtTick
(
CurrentTick
,
out
var
cooldownExpirationTick
))
if
(!
attackCooldown
.
GetDataAtTick
(
CurrentTick
,
out
var
cooldownExpirationTick
))
{
{
cooldownExpirationTick
.
Value
=
NetworkTick
.
Invalid
;
cooldownExpirationTick
.
Value
=
NetworkTick
.
Invalid
;
}
}
// Check if the NPC can attack based on the cooldown.
var
canAttack
=
!
cooldownExpirationTick
.
Value
.
IsValid
||
var
canAttack
=
!
cooldownExpirationTick
.
Value
.
IsValid
||
CurrentTick
.
IsNewerThan
(
cooldownExpirationTick
.
Value
);
CurrentTick
.
IsNewerThan
(
cooldownExpirationTick
.
Value
);
if
(!
canAttack
)
return
;
if
(!
canAttack
)
return
;
// Retrieve the enemy's and target's transformations.
var
enemyTransform
=
TransformLookup
[
enemyEntity
];
var
enemyTransform
=
TransformLookup
[
enemyEntity
];
var
spawnPosition
=
enemyTransform
.
Position
;
var
targetPosition
=
TransformLookup
[
targetEntity
.
Value
].
Position
;
var
targetPosition
=
TransformLookup
[
targetEntity
.
Value
].
Position
;
//
Get the vector from the enemy to the player
//
Calculate the direction and rotation towards the target.
var
direction
=
math
.
normalize
(
targetPosition
-
spawn
Position
);
var
direction
=
math
.
normalize
(
targetPosition
-
enemyTransform
.
Position
);
var
targetRotation
=
quaternion
.
LookRotationSafe
(
direction
,
math
.
up
());
var
targetRotation
=
quaternion
.
LookRotationSafe
(
direction
,
math
.
up
());
// Rotate the
enemy towards player
// Rotate the
NPC towards the target.
enemyTransform
.
Rotation
=
targetRotation
;
enemyTransform
.
Rotation
=
targetRotation
;
ECB
.
SetComponent
(
sortKey
,
enemyEntity
,
enemyTransform
);
ECB
.
SetComponent
(
sortKey
,
enemyEntity
,
enemyTransform
);
// Spawn the attack entity at the appropriate position and rotation.
var
newAttack
=
ECB
.
Instantiate
(
sortKey
,
attackProperties
.
AttackPrefab
);
var
newAttack
=
ECB
.
Instantiate
(
sortKey
,
attackProperties
.
AttackPrefab
);
var
newAttackTransform
=
LocalTransform
.
FromPositionRotation
(
spawnPosition
+
attackProperties
.
FirePointOffset
,
var
newAttackTransform
=
LocalTransform
.
FromPositionRotation
(
quaternion
.
LookRotationSafe
(
targetPosition
-
spawnPosition
,
math
.
up
()));
enemyTransform
.
Position
+
attackProperties
.
FirePointOffset
,
quaternion
.
LookRotationSafe
(
targetPosition
-
enemyTransform
.
Position
,
math
.
up
()));
ECB
.
SetComponent
(
sortKey
,
newAttack
,
newAttackTransform
);
ECB
.
SetComponent
(
sortKey
,
newAttack
,
newAttackTransform
);
ECB
.
SetComponent
(
sortKey
,
newAttack
,
team
);
ECB
.
SetComponent
(
sortKey
,
newAttack
,
team
);
// Update the attack cooldown.
var
newCooldownTick
=
CurrentTick
;
var
newCooldownTick
=
CurrentTick
;
newCooldownTick
.
Add
(
attackProperties
.
CooldownTickCount
);
newCooldownTick
.
Add
(
attackProperties
.
CooldownTickCount
);
attackCooldown
.
AddCommandData
(
new
NpcAttackCooldown
{
Tick
=
CurrentTick
,
Value
=
newCooldownTick
});
attackCooldown
.
AddCommandData
(
new
NpcAttackCooldown
{
Tick
=
CurrentTick
,
Value
=
newCooldownTick
});
...
...
Assets/_Game/Code/Systems/Combat/SetTargetSlimeSystem.cs
deleted
100644 → 0
View file @
cb7803e0
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
Assets/_Game/Code/Systems/Combat/SetTargetSlimeSystem.cs.meta
deleted
100644 → 0
View file @
cb7803e0
fileFormatVersion: 2
guid: 86f8c4a7fca9c2244981a82be26b0c7d
\ No newline at end of file
Assets/_Game/Code/Systems/Combat/TargetingSystem.cs
View file @
09de87fa
...
@@ -5,13 +5,25 @@ using Unity.Physics;
...
@@ -5,13 +5,25 @@ using Unity.Physics;
using
Unity.Physics.Systems
;
using
Unity.Physics.Systems
;
using
Unity.Transforms
;
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))]
[UpdateInGroup(typeof(PhysicsSystemGroup))]
[UpdateAfter(typeof(PhysicsSimulationGroup))]
[UpdateAfter(typeof(PhysicsSimulationGroup))]
[UpdateBefore(typeof(ExportPhysicsWorld))]
[UpdateBefore(typeof(ExportPhysicsWorld))]
public
partial
struct
TargetingSystem
:
ISystem
public
partial
struct
TargetingSystem
:
ISystem
{
{
/// <summary>
/// Collision filter used to define which layers the NPCs can target.
/// </summary>
private
CollisionFilter
_npcAttackFilter
;
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
]
[
BurstCompile
]
public
void
OnCreate
(
ref
SystemState
state
)
public
void
OnCreate
(
ref
SystemState
state
)
{
{
...
@@ -19,12 +31,15 @@ public partial struct TargetingSystem : ISystem
...
@@ -19,12 +31,15 @@ public partial struct TargetingSystem : ISystem
state
.
RequireForUpdate
<
GamePlayingTag
>();
state
.
RequireForUpdate
<
GamePlayingTag
>();
_npcAttackFilter
=
new
CollisionFilter
_npcAttackFilter
=
new
CollisionFilter
{
{
BelongsTo
=
1
<<
6
,
//Target Cast
BelongsTo
=
1
<<
6
,
//
Target Cast
CollidesWith
=
1
<<
1
//Player
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
]
[
BurstCompile
]
public
void
OnUpdate
(
ref
SystemState
state
)
public
void
OnUpdate
(
ref
SystemState
state
)
{
{
...
@@ -36,26 +51,51 @@ public partial struct TargetingSystem : ISystem
...
@@ -36,26 +51,51 @@ public partial struct TargetingSystem : ISystem
}.
ScheduleParallel
(
state
.
Dependency
);
}.
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
]
[
BurstCompile
]
[
WithAll
(
typeof
(
Simulate
))]
[
WithAll
(
typeof
(
Simulate
))]
public
partial
struct
NpcTargetingJob
:
IJobEntity
public
partial
struct
NpcTargetingJob
:
IJobEntity
{
{
/// <summary>
/// The collision world used to perform overlap sphere queries for detecting potential targets.
/// </summary>
[
ReadOnly
]
public
CollisionWorld
CollisionWorld
;
[
ReadOnly
]
public
CollisionWorld
CollisionWorld
;
/// <summary>
/// The collision filter defining the layers that NPCs can target.
/// </summary>
[
ReadOnly
]
public
CollisionFilter
CollisionFilter
;
[
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
;
[
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
]
[
BurstCompile
]
private
void
Execute
(
Entity
enemyEntity
,
ref
NpcTargetEntity
targetEntity
,
in
LocalTransform
transform
,
private
void
Execute
(
Entity
enemyEntity
,
ref
NpcTargetEntity
targetEntity
,
in
LocalTransform
transform
,
in
NpcTargetRadius
targetRadius
)
in
NpcTargetRadius
targetRadius
)
{
{
// Temporary list to store potential target hits.
var
hits
=
new
NativeList
<
DistanceHit
>(
Allocator
.
TempJob
);
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
))
if
(
CollisionWorld
.
OverlapSphere
(
transform
.
Position
,
targetRadius
.
Value
,
ref
hits
,
CollisionFilter
))
{
{
var
closestDistance
=
float
.
MaxValue
;
var
closestDistance
=
float
.
MaxValue
;
var
closestEntity
=
Entity
.
Null
;
var
closestEntity
=
Entity
.
Null
;
// Iterate through the hits to find the closest valid target.
foreach
(
var
hit
in
hits
)
foreach
(
var
hit
in
hits
)
{
{
if
(!
TeamTypeLookup
.
TryGetComponent
(
hit
.
Entity
,
out
var
teamTypes
))
continue
;
if
(!
TeamTypeLookup
.
TryGetComponent
(
hit
.
Entity
,
out
var
teamTypes
))
continue
;
...
@@ -67,13 +107,16 @@ public partial struct TargetingSystem : ISystem
...
@@ -67,13 +107,16 @@ public partial struct TargetingSystem : ISystem
}
}
}
}
// Update the target entity with the closest valid target.
targetEntity
.
Value
=
closestEntity
;
targetEntity
.
Value
=
closestEntity
;
}
}
else
else
{
{
// If no valid targets are found, set the target entity to null.
targetEntity
.
Value
=
Entity
.
Null
;
targetEntity
.
Value
=
Entity
.
Null
;
}
}
// Dispose of the temporary hits list.
hits
.
Dispose
();
hits
.
Dispose
();
}
}
}
}
...
...
Assets/_Game/Code/Systems/Connection/ClientRequestGameEntrySystem.cs
View file @
09de87fa
...
@@ -4,9 +4,19 @@ using Unity.NetCode;
...
@@ -4,9 +4,19 @@ using Unity.NetCode;
using
UnityEngine
;
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)]
[WorldSystemFilter(WorldSystemFilterFlags.ClientSimulation)]
public
partial
struct
ClientRequestGameEntrySystem
:
ISystem
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
)
public
void
OnCreate
(
ref
SystemState
state
)
{
{
EntityQueryBuilder
entityQueryBuilder
=
new
EntityQueryBuilder
(
Allocator
.
Temp
)
EntityQueryBuilder
entityQueryBuilder
=
new
EntityQueryBuilder
(
Allocator
.
Temp
)
...
@@ -16,35 +26,56 @@ public partial struct ClientRequestGameEntrySystem : ISystem
...
@@ -16,35 +26,56 @@ public partial struct ClientRequestGameEntrySystem : ISystem
entityQueryBuilder
.
Dispose
();
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
)
public
void
OnUpdate
(
ref
SystemState
state
)
{
{
EntityCommandBuffer
entityCommandBuffer
=
new
EntityCommandBuffer
(
Unity
.
Collections
.
Allocator
.
Temp
);
EntityCommandBuffer
entityCommandBuffer
=
new
EntityCommandBuffer
(
Unity
.
Collections
.
Allocator
.
Temp
);
// Iterate through all entities with a `NetworkId` component but without a `NetworkStreamInGame` component.
foreach
((
foreach
((
RefRO
<
NetworkId
>
networkId
,
RefRO
<
NetworkId
>
networkId
,
Entity
entity
)
Entity
entity
)
in
SystemAPI
.
Query
in
SystemAPI
.
Query
<
RefRO
<
NetworkId
>>().
WithNone
<
NetworkStreamInGame
>().
WithEntityAccess
())
<
RefRO
<
NetworkId
>>().
WithNone
<
NetworkStreamInGame
>().
WithEntityAccess
())
{
{
// Add the `NetworkStreamInGame` component to mark the client as "in-game".
entityCommandBuffer
.
AddComponent
<
NetworkStreamInGame
>(
entity
);
entityCommandBuffer
.
AddComponent
<
NetworkStreamInGame
>(
entity
);
// Create a new entity for the game entry request.
var
requestGameConnection
=
entityCommandBuffer
.
CreateEntity
();
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
}
"
);
GameObject
playerCameraGO
=
new
GameObject
(
$"Camera
{
networkId
.
ValueRO
.
Value
}
"
);
playerCameraGO
.
AddComponent
<
Camera
>();
playerCameraGO
.
AddComponent
<
Camera
>();
FollowPlayer
followScript
=
playerCameraGO
.
AddComponent
<
FollowPlayer
>();
FollowPlayer
followScript
=
playerCameraGO
.
AddComponent
<
FollowPlayer
>();
followScript
.
networkId
=
networkId
.
ValueRO
.
Value
;
followScript
.
networkId
=
networkId
.
ValueRO
.
Value
;
// Add components to the request entity to send an RPC to the server.
entityCommandBuffer
.
AddComponent
<
GoInGameRequestRpc
>(
requestGameConnection
);
entityCommandBuffer
.
AddComponent
<
GoInGameRequestRpc
>(
requestGameConnection
);
entityCommandBuffer
.
AddComponent
<
SendRpcCommandRequest
>(
requestGameConnection
);
entityCommandBuffer
.
AddComponent
<
SendRpcCommandRequest
>(
requestGameConnection
);
}
}
// Apply all queued entity modifications.
entityCommandBuffer
.
Playback
(
state
.
EntityManager
);
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)]
[WorldSystemFilter(WorldSystemFilterFlags.ThinClientSimulation)]
public
partial
struct
ThinClientRequestGameEntrySystem
:
ISystem
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
)
public
void
OnCreate
(
ref
SystemState
state
)
{
{
EntityQueryBuilder
entityQueryBuilder
=
new
EntityQueryBuilder
(
Allocator
.
Temp
)
EntityQueryBuilder
entityQueryBuilder
=
new
EntityQueryBuilder
(
Allocator
.
Temp
)
...
@@ -54,24 +85,34 @@ public partial struct ThinClientRequestGameEntrySystem : ISystem
...
@@ -54,24 +85,34 @@ public partial struct ThinClientRequestGameEntrySystem : ISystem
entityQueryBuilder
.
Dispose
();
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
)
public
void
OnUpdate
(
ref
SystemState
state
)
{
{
EntityCommandBuffer
entityCommandBuffer
=
new
EntityCommandBuffer
(
Unity
.
Collections
.
Allocator
.
Temp
);
EntityCommandBuffer
entityCommandBuffer
=
new
EntityCommandBuffer
(
Unity
.
Collections
.
Allocator
.
Temp
);
// Iterate through all entities with a `NetworkId` component but without a `NetworkStreamInGame` component.
foreach
((
foreach
((
RefRO
<
NetworkId
>
networkId
,
RefRO
<
NetworkId
>
networkId
,
Entity
entity
)
Entity
entity
)
in
SystemAPI
.
Query
in
SystemAPI
.
Query
<
RefRO
<
NetworkId
>>().
WithNone
<
NetworkStreamInGame
>().
WithEntityAccess
())
<
RefRO
<
NetworkId
>>().
WithNone
<
NetworkStreamInGame
>().
WithEntityAccess
())
{
{
// Add the `NetworkStreamInGame` component to mark the thin client as "in-game".
entityCommandBuffer
.
AddComponent
<
NetworkStreamInGame
>(
entity
);
entityCommandBuffer
.
AddComponent
<
NetworkStreamInGame
>(
entity
);
// Create a new entity for the game entry request.
var
requestGameConnection
=
entityCommandBuffer
.
CreateEntity
();
var
requestGameConnection
=
entityCommandBuffer
.
CreateEntity
();
// Add components to the request entity to send an RPC to the server.
entityCommandBuffer
.
AddComponent
<
GoInGameRequestRpc
>(
requestGameConnection
);
entityCommandBuffer
.
AddComponent
<
GoInGameRequestRpc
>(
requestGameConnection
);
entityCommandBuffer
.
AddComponent
<
SendRpcCommandRequest
>(
requestGameConnection
);
entityCommandBuffer
.
AddComponent
<
SendRpcCommandRequest
>(
requestGameConnection
);
}
}
// Apply all queued entity modifications.
entityCommandBuffer
.
Playback
(
state
.
EntityManager
);
entityCommandBuffer
.
Playback
(
state
.
EntityManager
);
}
}
}
}
\ No newline at end of file
Assets/_Game/Code/Systems/Connection/ClientStartGameSystem.cs
View file @
09de87fa
...
@@ -4,38 +4,65 @@ using Unity.Collections;
...
@@ -4,38 +4,65 @@ using Unity.Collections;
using
Unity.Entities
;
using
Unity.Entities
;
using
Unity.NetCode
;
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)]
[WorldSystemFilter(WorldSystemFilterFlags.ClientSimulation)]
public
partial
class
ClientStartGameSystem
:
SystemBase
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
;
public
Action
<
int
>
OnUpdatePlayersRemainingToStart
;
/// <summary>
/// Action invoked when the game start countdown begins.
/// </summary>
public
Action
OnStartGameCountdown
;
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
()
protected
override
void
OnUpdate
()
{
{
// Create a temporary EntityCommandBuffer to queue entity modifications.
var
ecb
=
new
EntityCommandBuffer
(
Allocator
.
Temp
);
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
>()
foreach
(
var
(
playersRemainingToStart
,
entity
)
in
SystemAPI
.
Query
<
PlayersRemainingToStart
>()
.
WithAll
<
ReceiveRpcCommandRequest
>().
WithEntityAccess
())
.
WithAll
<
ReceiveRpcCommandRequest
>().
WithEntityAccess
())
{
{
// Destroy the RPC entity after processing.
ecb
.
DestroyEntity
(
entity
);
ecb
.
DestroyEntity
(
entity
);
// Invoke the action to update the number of players remaining to start.
OnUpdatePlayersRemainingToStart
?.
Invoke
(
playersRemainingToStart
.
Value
);
OnUpdatePlayersRemainingToStart
?.
Invoke
(
playersRemainingToStart
.
Value
);
}
}
// Process RPCs for the game start tick.
foreach
(
var
(
gameStartTick
,
entity
)
in
SystemAPI
.
Query
<
GameStartTickRpc
>()
foreach
(
var
(
gameStartTick
,
entity
)
in
SystemAPI
.
Query
<
GameStartTickRpc
>()
.
WithAll
<
ReceiveRpcCommandRequest
>().
WithEntityAccess
())
.
WithAll
<
ReceiveRpcCommandRequest
>().
WithEntityAccess
())
{
{
// Destroy the RPC entity after processing.
ecb
.
DestroyEntity
(
entity
);
ecb
.
DestroyEntity
(
entity
);
// Invoke the action to start the game countdown.
OnStartGameCountdown
?.
Invoke
();
OnStartGameCountdown
?.
Invoke
();
// Create a new entity to store the game start tick on the client side.
var
gameStartEntity
=
ecb
.
CreateEntity
();
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
ecb
.
AddComponent
(
gameStartEntity
,
new
GameStartTick
{
{
Value
=
gameStartTick
.
Value
Value
=
gameStartTick
.
Value
});
});
}
}
// Apply all queued entity modifications.
ecb
.
Playback
(
EntityManager
);
ecb
.
Playback
(
EntityManager
);
}
}
}
}
\ No newline at end of file
Assets/_Game/Code/Systems/Connection/CountDownToGameStartSystem.cs
View file @
09de87fa
...
@@ -5,46 +5,79 @@ using Unity.Entities;
...
@@ -5,46 +5,79 @@ using Unity.Entities;
using
Unity.Mathematics
;
using
Unity.Mathematics
;
using
Unity.NetCode
;
using
Unity.NetCode
;
[UpdateInGroup(typeof(PredictedSimulationSystemGroup))]
[UpdateInGroup(typeof(PredictedSimulationSystemGroup))]
public
partial
class
CountdownToGameStartSystem
:
SystemBase
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
;
public
Action
<
int
>
OnUpdateCountdownText
;
/// <summary>
/// Action invoked when the countdown ends and the game starts.
/// </summary>
public
Action
OnCountdownEnd
;
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
()
protected
override
void
OnCreate
()
{
{
RequireForUpdate
<
NetworkTime
>();
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
()
protected
override
void
OnUpdate
()
{
{
// Retrieve the current network time.
var
networkTime
=
SystemAPI
.
GetSingleton
<
NetworkTime
>();
var
networkTime
=
SystemAPI
.
GetSingleton
<
NetworkTime
>();
// Skip processing if this is not the first time fully predicting the current tick.
if
(!
networkTime
.
IsFirstTimeFullyPredictingTick
)
return
;
if
(!
networkTime
.
IsFirstTimeFullyPredictingTick
)
return
;
// Get the current server tick.
var
currentTick
=
networkTime
.
ServerTick
;
var
currentTick
=
networkTime
.
ServerTick
;
// Create a temporary EntityCommandBuffer to queue entity modifications.
var
ecb
=
new
EntityCommandBuffer
(
Allocator
.
Temp
);
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
())
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
))
if
(
currentTick
.
Equals
(
gameStartTick
.
Value
)
||
currentTick
.
IsNewerThan
(
gameStartTick
.
Value
))
{
{
// Create a new entity to represent the game playing state.
var
gamePlayingEntity
=
ecb
.
CreateEntity
();
var
gamePlayingEntity
=
ecb
.
CreateEntity
();
ecb
.
SetName
(
gamePlayingEntity
,
"GamePlayingEntity"
);
ecb
.
SetName
(
gamePlayingEntity
,
"GamePlayingEntity"
);
ecb
.
AddComponent
<
GamePlayingTag
>(
gamePlayingEntity
);
ecb
.
AddComponent
<
GamePlayingTag
>(
gamePlayingEntity
);
// Destroy the `GameStartTick` entity as the countdown has ended.
ecb
.
DestroyEntity
(
entity
);
ecb
.
DestroyEntity
(
entity
);
// Invoke the action to signal the end of the countdown.
OnCountdownEnd
?.
Invoke
();
OnCountdownEnd
?.
Invoke
();
}
}
else
else
{
{
// Calculate the number of ticks remaining until the game starts.
var
ticksToStart
=
gameStartTick
.
Value
.
TickIndexForValidTick
-
currentTick
.
TickIndexForValidTick
;
var
ticksToStart
=
gameStartTick
.
Value
.
TickIndexForValidTick
-
currentTick
.
TickIndexForValidTick
;
// Retrieve the simulation tick rate.
var
simulationTickRate
=
NetCodeConfig
.
Global
.
ClientServerTickRate
.
SimulationTickRate
;
var
simulationTickRate
=
NetCodeConfig
.
Global
.
ClientServerTickRate
.
SimulationTickRate
;
// Calculate the number of seconds remaining until the game starts.
var
secondsToStart
=
(
int
)
math
.
ceil
((
float
)
ticksToStart
/
simulationTickRate
);
var
secondsToStart
=
(
int
)
math
.
ceil
((
float
)
ticksToStart
/
simulationTickRate
);
// Invoke the action to update the countdown text.
OnUpdateCountdownText
?.
Invoke
(
secondsToStart
);
OnUpdateCountdownText
?.
Invoke
(
secondsToStart
);
}
}
}
}
// Apply all queued entity modifications.
ecb
.
Playback
(
EntityManager
);
ecb
.
Playback
(
EntityManager
);
}
}
}
}
\ No newline at end of file
Assets/_Game/Code/Systems/Connection/GoInGameServerSystem.cs
View file @
09de87fa
...
@@ -8,6 +8,11 @@ using UnityEngine;
...
@@ -8,6 +8,11 @@ using UnityEngine;
[WorldSystemFilter(WorldSystemFilterFlags.ServerSimulation)]
[WorldSystemFilter(WorldSystemFilterFlags.ServerSimulation)]
partial
struct
GoInGameServerSystem
:
ISystem
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
]
[
BurstCompile
]
public
void
OnCreate
(
ref
SystemState
state
)
public
void
OnCreate
(
ref
SystemState
state
)
{
{
...
@@ -19,63 +24,81 @@ partial struct GoInGameServerSystem : ISystem
...
@@ -19,63 +24,81 @@ partial struct GoInGameServerSystem : ISystem
state
.
RequireForUpdate
<
GoInGameRequestRpc
>();
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
)
public
void
OnUpdate
(
ref
SystemState
state
)
{
{
// Create a temporary command buffer to queue entity modifications.
EntityCommandBuffer
entityCommandBuffer
=
new
EntityCommandBuffer
(
Unity
.
Collections
.
Allocator
.
Temp
);
EntityCommandBuffer
entityCommandBuffer
=
new
EntityCommandBuffer
(
Unity
.
Collections
.
Allocator
.
Temp
);
// Retrieve singleton components and entities required for processing.
EntititesReferences
entititesReferences
=
SystemAPI
.
GetSingleton
<
EntititesReferences
>();
EntititesReferences
entititesReferences
=
SystemAPI
.
GetSingleton
<
EntititesReferences
>();
Entity
gameStartPropertiesEntity
=
SystemAPI
.
GetSingletonEntity
<
GameStartProperties
>();
Entity
gameStartPropertiesEntity
=
SystemAPI
.
GetSingletonEntity
<
GameStartProperties
>();
PlayerCounter
playerCounter
=
SystemAPI
.
GetComponent
<
PlayerCounter
>(
gameStartPropertiesEntity
);
PlayerCounter
playerCounter
=
SystemAPI
.
GetComponent
<
PlayerCounter
>(
gameStartPropertiesEntity
);
GameStartProperties
gameStartProperties
=
GameStartProperties
gameStartProperties
=
SystemAPI
.
GetComponent
<
GameStartProperties
>(
gameStartPropertiesEntity
);
SystemAPI
.
GetComponent
<
GameStartProperties
>(
gameStartPropertiesEntity
);
// Iterate through all entities with a `ReceiveRpcCommandRequest` and `GoInGameRequestRpc` component.
foreach
((
foreach
((
RefRO
<
ReceiveRpcCommandRequest
>
receiveRpcCommandRequest
,
RefRO
<
ReceiveRpcCommandRequest
>
receiveRpcCommandRequest
,
Entity
entity
)
in
Entity
entity
)
in
SystemAPI
.
Query
SystemAPI
.
Query
<
RefRO
<
ReceiveRpcCommandRequest
>>().
WithAll
<
GoInGameRequestRpc
>().
WithEntityAccess
())
<
RefRO
<
ReceiveRpcCommandRequest
>>().
WithAll
<
GoInGameRequestRpc
>().
WithEntityAccess
())
{
{
// Get the source connection entity from the RPC request.
Entity
sourceConnection
=
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
);
entityCommandBuffer
.
AddComponent
<
NetworkStreamInGame
>(
sourceConnection
);
Debug
.
Log
(
"Client Connected to Server"
);
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
);
Entity
playerEntity
=
entityCommandBuffer
.
Instantiate
(
entititesReferences
.
PlayerPrefabEntity
);
entityCommandBuffer
.
SetComponent
(
playerEntity
,
LocalTransform
.
FromPosition
(
new
float3
(
entityCommandBuffer
.
SetComponent
(
playerEntity
,
LocalTransform
.
FromPosition
(
new
float3
(
UnityEngine
.
Random
.
Range
(-
10
,
+
10
),
0
,
0
)));
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
entityCommandBuffer
.
AddComponent
(
playerEntity
,
new
GhostOwner
{
{
NetworkId
=
networkId
.
Value
NetworkId
=
networkId
.
Value
});
});
entityCommandBuffer
.
AddComponent
(
playerEntity
,
new
NetworkEntityReference
{
Value
=
sourceConnection
});
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
Value
=
playerEntity
});
});
// Update the player counter and calculate the number of players remaining to start the game.
playerCounter
.
Value
++;
playerCounter
.
Value
++;
int
playersRemainingToStart
=
gameStartProperties
.
PlayerAmount
-
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
();
var
gameStartRpc
=
entityCommandBuffer
.
CreateEntity
();
if
(
playersRemainingToStart
<=
0
&&
!
SystemAPI
.
HasSingleton
<
GamePlayingTag
>())
if
(
playersRemainingToStart
<=
0
&&
!
SystemAPI
.
HasSingleton
<
GamePlayingTag
>())
{
{
// Calculate the tick at which the game should start.
var
simulationTickRate
=
NetCodeConfig
.
Global
.
ClientServerTickRate
.
SimulationTickRate
;
var
simulationTickRate
=
NetCodeConfig
.
Global
.
ClientServerTickRate
.
SimulationTickRate
;
var
ticksUntilStart
=
(
uint
)(
simulationTickRate
*
gameStartProperties
.
CountdownTime
);
var
ticksUntilStart
=
(
uint
)(
simulationTickRate
*
gameStartProperties
.
CountdownTime
);
var
gameStartTick
=
SystemAPI
.
GetSingleton
<
NetworkTime
>().
ServerTick
;
var
gameStartTick
=
SystemAPI
.
GetSingleton
<
NetworkTime
>().
ServerTick
;
gameStartTick
.
Add
(
ticksUntilStart
);
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
entityCommandBuffer
.
AddComponent
(
gameStartRpc
,
new
GameStartTickRpc
{
{
Value
=
gameStartTick
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
();
var
gameStartEntity
=
entityCommandBuffer
.
CreateEntity
();
entityCommandBuffer
.
AddComponent
(
gameStartEntity
,
new
GameStartTick
entityCommandBuffer
.
AddComponent
(
gameStartEntity
,
new
GameStartTick
{
{
...
@@ -84,14 +107,19 @@ partial struct GoInGameServerSystem : ISystem
...
@@ -84,14 +107,19 @@ partial struct GoInGameServerSystem : ISystem
}
}
else
else
{
{
// Add a `PlayersRemainingToStart` component to the RPC entity with the remaining player count.
entityCommandBuffer
.
AddComponent
(
gameStartRpc
,
entityCommandBuffer
.
AddComponent
(
gameStartRpc
,
new
PlayersRemainingToStart
{
Value
=
playersRemainingToStart
});
new
PlayersRemainingToStart
{
Value
=
playersRemainingToStart
});
}
}
// Add a `SendRpcCommandRequest` component to the RPC entity to send it to clients.
entityCommandBuffer
.
AddComponent
<
SendRpcCommandRequest
>(
gameStartRpc
);
entityCommandBuffer
.
AddComponent
<
SendRpcCommandRequest
>(
gameStartRpc
);
}
}
// Apply all queued entity modifications.
entityCommandBuffer
.
Playback
(
state
.
EntityManager
);
entityCommandBuffer
.
Playback
(
state
.
EntityManager
);
// Update the singleton `PlayerCounter` component with the new value.
SystemAPI
.
SetSingleton
(
playerCounter
);
SystemAPI
.
SetSingleton
(
playerCounter
);
}
}
}
}
\ No newline at end of file
Assets/_Game/Code/Systems/Destroy/DestroyEntitySystem.cs
View file @
09de87fa
...
@@ -5,12 +5,17 @@ using Unity.NetCode;
...
@@ -5,12 +5,17 @@ using Unity.NetCode;
using
Unity.Transforms
;
using
Unity.Transforms
;
using
UnityEngine
;
using
UnityEngine
;
[UpdateInGroup(typeof(PredictedSimulationSystemGroup), OrderLast = true)]
[UpdateInGroup(typeof(PredictedSimulationSystemGroup), OrderLast = true)]
public
partial
struct
DestroyEntitySystem
:
ISystem
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
)
public
void
OnCreate
(
ref
SystemState
state
)
{
{
// Require specific components and entities for the system to update.
state
.
RequireForUpdate
<
RespawnEntityTag
>();
state
.
RequireForUpdate
<
RespawnEntityTag
>();
state
.
RequireForUpdate
<
BeginSimulationEntityCommandBufferSystem
.
Singleton
>();
state
.
RequireForUpdate
<
BeginSimulationEntityCommandBufferSystem
.
Singleton
>();
state
.
RequireForUpdate
<
NetworkTime
>();
state
.
RequireForUpdate
<
NetworkTime
>();
...
@@ -18,35 +23,49 @@ public partial struct DestroyEntitySystem : ISystem
...
@@ -18,35 +23,49 @@ public partial struct DestroyEntitySystem : ISystem
state
.
RequireForUpdate
<
DestroyEntityTag
>();
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
)
public
void
OnUpdate
(
ref
SystemState
state
)
{
{
// Retrieve the current network time.
var
networkTime
=
SystemAPI
.
GetSingleton
<
NetworkTime
>();
var
networkTime
=
SystemAPI
.
GetSingleton
<
NetworkTime
>();
// Skip processing if this is not the first time fully predicting the current tick.
if
(!
networkTime
.
IsFirstTimeFullyPredictingTick
)
return
;
if
(!
networkTime
.
IsFirstTimeFullyPredictingTick
)
return
;
// Get the current server tick.
var
currentTick
=
networkTime
.
ServerTick
;
var
currentTick
=
networkTime
.
ServerTick
;
// Retrieve the EntityCommandBuffer for queuing entity modifications.
var
ecbSingleton
=
SystemAPI
.
GetSingleton
<
BeginSimulationEntityCommandBufferSystem
.
Singleton
>();
var
ecbSingleton
=
SystemAPI
.
GetSingleton
<
BeginSimulationEntityCommandBufferSystem
.
Singleton
>();
var
ecb
=
ecbSingleton
.
CreateCommandBuffer
(
state
.
WorldUnmanaged
);
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
>>()
foreach
(
var
(
transform
,
entity
)
in
SystemAPI
.
Query
<
RefRW
<
LocalTransform
>>()
.
WithAll
<
DestroyEntityTag
,
Simulate
>().
WithEntityAccess
())
.
WithAll
<
DestroyEntityTag
,
Simulate
>().
WithEntityAccess
())
{
{
// Check if the current world is the server.
if
(
state
.
World
.
IsServer
())
if
(
state
.
World
.
IsServer
())
{
{
// Handle player entities.
if
(
SystemAPI
.
HasComponent
<
PlayerTag
>(
entity
))
if
(
SystemAPI
.
HasComponent
<
PlayerTag
>(
entity
))
{
{
// Retrieve the network entity and respawn-related components.
var
networkEntity
=
SystemAPI
.
GetComponent
<
NetworkEntityReference
>(
entity
).
Value
;
var
networkEntity
=
SystemAPI
.
GetComponent
<
NetworkEntityReference
>(
entity
).
Value
;
var
respawnEntity
=
SystemAPI
.
GetSingletonEntity
<
RespawnEntityTag
>();
var
respawnEntity
=
SystemAPI
.
GetSingletonEntity
<
RespawnEntityTag
>();
var
respawnTickCount
=
SystemAPI
.
GetComponent
<
RespawnTickCount
>(
respawnEntity
).
Value
;
var
respawnTickCount
=
SystemAPI
.
GetComponent
<
RespawnTickCount
>(
respawnEntity
).
Value
;
// Calculate the tick at which the player should respawn.
var
respawnTick
=
currentTick
;
var
respawnTick
=
currentTick
;
respawnTick
.
Add
(
respawnTickCount
);
respawnTick
.
Add
(
respawnTickCount
);
// Store
NetworkId BEFORE destroying entity or networkEntity
// Store
the NetworkId before destroying the entity.
int
networkIdValue
=
SystemAPI
.
GetComponent
<
NetworkId
>(
networkEntity
).
Value
;
int
networkIdValue
=
SystemAPI
.
GetComponent
<
NetworkId
>(
networkEntity
).
Value
;
// Append the player to the respawn buffer with the calculated respawn tick.
ecb
.
AppendToBuffer
(
respawnEntity
,
new
RespawnBufferElement
ecb
.
AppendToBuffer
(
respawnEntity
,
new
RespawnBufferElement
{
{
NetworkEntity
=
networkEntity
,
NetworkEntity
=
networkEntity
,
...
@@ -54,11 +73,13 @@ public partial struct DestroyEntitySystem : ISystem
...
@@ -54,11 +73,13 @@ public partial struct DestroyEntitySystem : ISystem
NetworkId
=
networkIdValue
NetworkId
=
networkIdValue
});
});
// Destroy the player entity.
ecb
.
DestroyEntity
(
entity
);
ecb
.
DestroyEntity
(
entity
);
}
}
else
else
{
{
ecb
.
DestroyEntity
(
entity
);
// Destroy non-player entities
// Destroy non-player entities directly.
ecb
.
DestroyEntity
(
entity
);
}
}
}
}
}
}
...
...
Assets/_Game/Code/Systems/Destroy/DestroyOnTimerSystem.cs
View file @
09de87fa
...
@@ -5,27 +5,44 @@ using Unity.NetCode;
...
@@ -5,27 +5,44 @@ using Unity.NetCode;
[UpdateInGroup(typeof(PredictedSimulationSystemGroup))]
[UpdateInGroup(typeof(PredictedSimulationSystemGroup))]
public
partial
struct
DestroyOnTimerSystem
:
ISystem
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
)
public
void
OnCreate
(
ref
SystemState
state
)
{
{
// Require specific components and entities for the system to update.
state
.
RequireForUpdate
<
NetworkTime
>();
state
.
RequireForUpdate
<
NetworkTime
>();
state
.
RequireForUpdate
<
EndSimulationEntityCommandBufferSystem
.
Singleton
>();
state
.
RequireForUpdate
<
EndSimulationEntityCommandBufferSystem
.
Singleton
>();
state
.
RequireForUpdate
<
DestroyAtTick
>();
state
.
RequireForUpdate
<
DestroyAtTick
>();
state
.
RequireForUpdate
<
GamePlayingTag
>();
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
]
[
BurstCompile
]
public
void
OnUpdate
(
ref
SystemState
state
)
public
void
OnUpdate
(
ref
SystemState
state
)
{
{
// Retrieve the EntityCommandBuffer for queuing entity modifications.
var
ecbSingleton
=
SystemAPI
.
GetSingleton
<
EndSimulationEntityCommandBufferSystem
.
Singleton
>();
var
ecbSingleton
=
SystemAPI
.
GetSingleton
<
EndSimulationEntityCommandBufferSystem
.
Singleton
>();
var
ecb
=
ecbSingleton
.
CreateCommandBuffer
(
state
.
WorldUnmanaged
);
var
ecb
=
ecbSingleton
.
CreateCommandBuffer
(
state
.
WorldUnmanaged
);
// Get the current server tick.
var
currentTick
=
SystemAPI
.
GetSingleton
<
NetworkTime
>().
ServerTick
;
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
>()
foreach
(
var
(
destroyAtTick
,
entity
)
in
SystemAPI
.
Query
<
RefRW
<
DestroyAtTick
>>().
WithAll
<
Simulate
>()
.
WithNone
<
DestroyEntityTag
>().
WithEntityAccess
())
.
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
))
if
(
currentTick
.
Equals
(
destroyAtTick
.
ValueRW
.
Value
)
||
currentTick
.
IsNewerThan
(
destroyAtTick
.
ValueRW
.
Value
))
{
{
// Add the `DestroyEntityTag` to mark the entity for destruction.
ecb
.
AddComponent
<
DestroyEntityTag
>(
entity
);
ecb
.
AddComponent
<
DestroyEntityTag
>(
entity
);
}
}
}
}
...
...
Assets/_Game/Code/Systems/Destroy/InitializeDestroyOnTimerSystem.cs
View file @
09de87fa
...
@@ -2,30 +2,56 @@ using Unity.Collections;
...
@@ -2,30 +2,56 @@ using Unity.Collections;
using
Unity.Entities
;
using
Unity.Entities
;
using
Unity.NetCode
;
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
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
)
public
void
OnCreate
(
ref
SystemState
state
)
{
{
// Require specific components and entities for the system to update.
state
.
RequireForUpdate
<
NetworkTime
>();
state
.
RequireForUpdate
<
NetworkTime
>();
state
.
RequireForUpdate
<
DestroyOnTimer
>();
state
.
RequireForUpdate
<
DestroyOnTimer
>();
state
.
RequireForUpdate
<
GamePlayingTag
>();
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
)
public
void
OnUpdate
(
ref
SystemState
state
)
{
{
// Create a temporary EntityCommandBuffer to queue entity modifications.
var
ecb
=
new
EntityCommandBuffer
(
Allocator
.
Temp
);
var
ecb
=
new
EntityCommandBuffer
(
Allocator
.
Temp
);
// Retrieve the simulation tick rate and the current server tick.
var
simulationTickRate
=
NetCodeConfig
.
Global
.
ClientServerTickRate
.
SimulationTickRate
;
var
simulationTickRate
=
NetCodeConfig
.
Global
.
ClientServerTickRate
.
SimulationTickRate
;
var
currentTick
=
SystemAPI
.
GetSingleton
<
NetworkTime
>().
ServerTick
;
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
>()
foreach
(
var
(
destroyOnTimer
,
entity
)
in
SystemAPI
.
Query
<
RefRW
<
DestroyOnTimer
>>().
WithNone
<
DestroyAtTick
>()
.
WithEntityAccess
())
.
WithEntityAccess
())
{
{
// Calculate the lifetime in ticks based on the entity's lifetime in seconds.
var
lifetimeInTicks
=
(
uint
)(
destroyOnTimer
.
ValueRW
.
Value
*
simulationTickRate
);
var
lifetimeInTicks
=
(
uint
)(
destroyOnTimer
.
ValueRW
.
Value
*
simulationTickRate
);
// Calculate the target tick at which the entity should be destroyed.
var
targetTick
=
currentTick
;
var
targetTick
=
currentTick
;
targetTick
.
Add
(
lifetimeInTicks
);
targetTick
.
Add
(
lifetimeInTicks
);
// Add the `DestroyAtTick` component to the entity with the calculated destruction tick.
ecb
.
AddComponent
(
entity
,
new
DestroyAtTick
{
Value
=
targetTick
});
ecb
.
AddComponent
(
entity
,
new
DestroyAtTick
{
Value
=
targetTick
});
}
}
// Apply all queued entity modifications.
ecb
.
Playback
(
state
.
EntityManager
);
ecb
.
Playback
(
state
.
EntityManager
);
}
}
}
}
\ No newline at end of file
Assets/_Game/Code/Systems/Destroy/RespawnPlayerSystem.cs
View file @
09de87fa
...
@@ -10,9 +10,20 @@ using UnityEngine;
...
@@ -10,9 +10,20 @@ using UnityEngine;
[UpdateInGroup(typeof(PredictedSimulationSystemGroup))]
[UpdateInGroup(typeof(PredictedSimulationSystemGroup))]
public
partial
class
RespawnPlayerSystem
:
SystemBase
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
;
public
Action
<
int
>
OnUpdateRespawnCountdown
;
/// <summary>
/// Action invoked when the player respawns.
/// </summary>
public
Action
OnRespawn
;
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
()
protected
override
void
OnCreate
()
{
{
RequireForUpdate
<
NetworkTime
>();
RequireForUpdate
<
NetworkTime
>();
...
@@ -20,6 +31,9 @@ public partial class RespawnPlayerSystem : SystemBase
...
@@ -20,6 +31,9 @@ public partial class RespawnPlayerSystem : SystemBase
RequireForUpdate
<
GamePlayingTag
>();
RequireForUpdate
<
GamePlayingTag
>();
}
}
/// <summary>
/// Called when the system starts running. Instantiates the respawn entity if it does not already exist.
/// </summary>
protected
override
void
OnStartRunning
()
protected
override
void
OnStartRunning
()
{
{
if
(
SystemAPI
.
HasSingleton
<
RespawnEntityTag
>())
return
;
if
(
SystemAPI
.
HasSingleton
<
RespawnEntityTag
>())
return
;
...
@@ -27,6 +41,10 @@ public partial class RespawnPlayerSystem : SystemBase
...
@@ -27,6 +41,10 @@ public partial class RespawnPlayerSystem : SystemBase
EntityManager
.
Instantiate
(
respawnPrefab
);
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
()
protected
override
void
OnUpdate
()
{
{
var
networkTime
=
SystemAPI
.
GetSingleton
<
NetworkTime
>();
var
networkTime
=
SystemAPI
.
GetSingleton
<
NetworkTime
>();
...
@@ -37,6 +55,7 @@ public partial class RespawnPlayerSystem : SystemBase
...
@@ -37,6 +55,7 @@ public partial class RespawnPlayerSystem : SystemBase
var
ecb
=
new
EntityCommandBuffer
(
Allocator
.
Temp
);
var
ecb
=
new
EntityCommandBuffer
(
Allocator
.
Temp
);
// Iterate through all entities with a RespawnBufferElement buffer.
foreach
(
var
respawnBuffer
in
SystemAPI
.
Query
<
DynamicBuffer
<
RespawnBufferElement
>>()
foreach
(
var
respawnBuffer
in
SystemAPI
.
Query
<
DynamicBuffer
<
RespawnBufferElement
>>()
.
WithAll
<
RespawnTickCount
,
Simulate
>())
.
WithAll
<
RespawnTickCount
,
Simulate
>())
{
{
...
@@ -46,10 +65,12 @@ public partial class RespawnPlayerSystem : SystemBase
...
@@ -46,10 +65,12 @@ public partial class RespawnPlayerSystem : SystemBase
{
{
var
curRespawn
=
respawnBuffer
[
i
];
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
(
currentTick
.
Equals
(
curRespawn
.
RespawnTick
)
||
currentTick
.
IsNewerThan
(
curRespawn
.
RespawnTick
))
{
{
if
(
isServer
)
if
(
isServer
)
{
{
// Server-side logic: Instantiate a new player entity and set its components.
int
networkId
=
SystemAPI
.
GetComponent
<
NetworkId
>(
curRespawn
.
NetworkEntity
).
Value
;
int
networkId
=
SystemAPI
.
GetComponent
<
NetworkId
>(
curRespawn
.
NetworkEntity
).
Value
;
Entity
playerPrefab
=
SystemAPI
.
GetSingleton
<
EntititesReferences
>().
PlayerPrefabEntity
;
Entity
playerPrefab
=
SystemAPI
.
GetSingleton
<
EntititesReferences
>().
PlayerPrefabEntity
;
...
@@ -65,6 +86,7 @@ public partial class RespawnPlayerSystem : SystemBase
...
@@ -65,6 +86,7 @@ public partial class RespawnPlayerSystem : SystemBase
}
}
else
else
{
{
// Client-side logic: Invoke the respawn action and create a camera for the new player.
OnRespawn
?.
Invoke
();
OnRespawn
?.
Invoke
();
if
(
SystemAPI
.
TryGetSingleton
<
NetworkId
>(
out
var
clientNetworkId
)
&&
if
(
SystemAPI
.
TryGetSingleton
<
NetworkId
>(
out
var
clientNetworkId
)
&&
curRespawn
.
NetworkId
==
clientNetworkId
.
Value
)
curRespawn
.
NetworkId
==
clientNetworkId
.
Value
)
...
@@ -75,6 +97,7 @@ public partial class RespawnPlayerSystem : SystemBase
...
@@ -75,6 +97,7 @@ public partial class RespawnPlayerSystem : SystemBase
}
}
else
if
(!
isServer
)
else
if
(!
isServer
)
{
{
// Client-side logic: Update the respawn countdown.
if
(
SystemAPI
.
TryGetSingleton
<
NetworkId
>(
out
var
networkId
))
if
(
SystemAPI
.
TryGetSingleton
<
NetworkId
>(
out
var
networkId
))
{
{
if
(
networkId
.
Value
==
curRespawn
.
NetworkId
)
if
(
networkId
.
Value
==
curRespawn
.
NetworkId
)
...
@@ -89,15 +112,21 @@ public partial class RespawnPlayerSystem : SystemBase
...
@@ -89,15 +112,21 @@ public partial class RespawnPlayerSystem : SystemBase
}
}
}
}
// Remove processed respawn entries from the buffer.
foreach
(
var
respawnIndex
in
respawnsToCleanup
)
foreach
(
var
respawnIndex
in
respawnsToCleanup
)
{
{
respawnBuffer
.
RemoveAt
(
respawnIndex
);
respawnBuffer
.
RemoveAt
(
respawnIndex
);
}
}
}
}
// Apply all queued entity modifications.
ecb
.
Playback
(
EntityManager
);
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
)
private
void
CreateCameraForNewPlayer
(
int
networkId
)
{
{
GameObject
playerCameraGO
=
new
GameObject
(
$"Camera
{
networkId
}
"
);
GameObject
playerCameraGO
=
new
GameObject
(
$"Camera
{
networkId
}
"
);
...
...
Assets/_Game/Code/Systems/Enemy/EnemySpawnerAspect.cs
View file @
09de87fa
...
@@ -2,55 +2,100 @@ using Unity.Entities;
...
@@ -2,55 +2,100 @@ using Unity.Entities;
using
Unity.Entities.UniversalDelegates
;
using
Unity.Entities.UniversalDelegates
;
using
UnityEngine
;
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
public
readonly
partial
struct
EnemySpawnerAspect
:
IAspect
{
{
// Reference to the enemy spawn timer component.
private
readonly
RefRW
<
EnemySpawnTimer
>
_enemySpawnTimer
;
private
readonly
RefRW
<
EnemySpawnTimer
>
_enemySpawnTimer
;
// Reference to the game start properties component.
private
readonly
RefRO
<
GameStartProperties
>
_gameStartProperties
;
private
readonly
RefRO
<
GameStartProperties
>
_gameStartProperties
;
// Reference to the spawnable enemies counter component.
private
readonly
RefRW
<
SpawnableEnemiesCounter
>
_spawnableEnemiesCounter
;
private
readonly
RefRW
<
SpawnableEnemiesCounter
>
_spawnableEnemiesCounter
;
/// <summary>
/// Gets or sets the time remaining for the next slime enemy spawn.
/// </summary>
public
float
TimeForNextSlimeSpawn
public
float
TimeForNextSlimeSpawn
{
{
get
=>
_enemySpawnTimer
.
ValueRO
.
SlimeSpawnTimer
;
get
=>
_enemySpawnTimer
.
ValueRO
.
SlimeSpawnTimer
;
set
=>
_enemySpawnTimer
.
ValueRW
.
SlimeSpawnTimer
=
value
;
set
=>
_enemySpawnTimer
.
ValueRW
.
SlimeSpawnTimer
=
value
;
}
}
/// <summary>
/// Gets or sets the time remaining for the next rogue enemy spawn.
/// </summary>
public
float
TimeForNextRogueSpawn
public
float
TimeForNextRogueSpawn
{
{
get
=>
_enemySpawnTimer
.
ValueRO
.
RogueSpawnTimer
;
get
=>
_enemySpawnTimer
.
ValueRO
.
RogueSpawnTimer
;
set
=>
_enemySpawnTimer
.
ValueRW
.
RogueSpawnTimer
=
value
;
set
=>
_enemySpawnTimer
.
ValueRW
.
RogueSpawnTimer
=
value
;
}
}
/// <summary>
/// Gets or sets the random spawn offset used to vary spawn timings.
/// </summary>
public
float
RandomSpawnOffset
public
float
RandomSpawnOffset
{
{
get
=>
_enemySpawnTimer
.
ValueRO
.
RandomSpawnOffset
;
get
=>
_enemySpawnTimer
.
ValueRO
.
RandomSpawnOffset
;
set
=>
_enemySpawnTimer
.
ValueRW
.
RandomSpawnOffset
=
value
;
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
;
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
;
public
int
SlimeSpawnAmount
=>
_gameStartProperties
.
ValueRO
.
SlimeEnemyAmount
;
/// <summary>
/// Gets or sets the current count of spawned rogue enemies.
/// </summary>
public
int
RogueEnemyCounter
public
int
RogueEnemyCounter
{
{
get
=>
_spawnableEnemiesCounter
.
ValueRO
.
RogueEnemyCounter
;
get
=>
_spawnableEnemiesCounter
.
ValueRO
.
RogueEnemyCounter
;
set
=>
_spawnableEnemiesCounter
.
ValueRW
.
RogueEnemyCounter
=
value
;
set
=>
_spawnableEnemiesCounter
.
ValueRW
.
RogueEnemyCounter
=
value
;
}
}
/// <summary>
/// Gets or sets the current count of spawned slime enemies.
/// </summary>
public
int
SlimeEnemyCounter
public
int
SlimeEnemyCounter
{
{
get
=>
_spawnableEnemiesCounter
.
ValueRO
.
SlimeEnemyCounter
;
get
=>
_spawnableEnemiesCounter
.
ValueRO
.
SlimeEnemyCounter
;
set
=>
_spawnableEnemiesCounter
.
ValueRW
.
SlimeEnemyCounter
=
value
;
set
=>
_spawnableEnemiesCounter
.
ValueRW
.
SlimeEnemyCounter
=
value
;
}
}
/// <summary>
/// Gets the cooldown duration for spawning slime enemies.
/// </summary>
public
float
SlimeSpawnCoolDown
=>
_enemySpawnTimer
.
ValueRO
.
SlimeSpawnCooldown
;
public
float
SlimeSpawnCoolDown
=>
_enemySpawnTimer
.
ValueRO
.
SlimeSpawnCooldown
;
/// <summary>
/// Gets the cooldown duration for spawning rogue enemies.
/// </summary>
public
float
RogueSpawnCoolDown
=>
_enemySpawnTimer
.
ValueRO
.
RogueSpawnCooldown
;
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
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
)
public
void
DecrementTimers
(
float
deltaTime
)
{
{
TimeForNextSlimeSpawn
-=
deltaTime
;
TimeForNextSlimeSpawn
-=
deltaTime
;
...
@@ -61,21 +106,33 @@ public readonly partial struct EnemySpawnerAspect : IAspect
...
@@ -61,21 +106,33 @@ public readonly partial struct EnemySpawnerAspect : IAspect
RandomSpawnOffset
=
5f
;
RandomSpawnOffset
=
5f
;
}
}
/// <summary>
/// Increments the counter for spawned slime enemies.
/// </summary>
public
void
IncreaseSlimeCounter
()
public
void
IncreaseSlimeCounter
()
{
{
SlimeEnemyCounter
++;
SlimeEnemyCounter
++;
}
}
/// <summary>
/// Increments the counter for spawned rogue enemies.
/// </summary>
public
void
IncreaseRogueCounter
()
public
void
IncreaseRogueCounter
()
{
{
RogueEnemyCounter
++;
RogueEnemyCounter
++;
}
}
/// <summary>
/// Resets the timer for spawning rogue enemies to the cooldown duration.
/// </summary>
public
void
ResetRoqueTimer
()
public
void
ResetRoqueTimer
()
{
{
TimeForNextRogueSpawn
=
RogueSpawnCoolDown
;
TimeForNextRogueSpawn
=
RogueSpawnCoolDown
;
}
}
/// <summary>
/// Resets the timer for spawning slime enemies to the cooldown duration.
/// </summary>
public
void
ResetSlimeTimer
()
public
void
ResetSlimeTimer
()
{
{
TimeForNextSlimeSpawn
=
SlimeSpawnCoolDown
;
TimeForNextSlimeSpawn
=
SlimeSpawnCoolDown
;
...
...
Assets/_Game/Code/Systems/Enemy/EnemySpawnerSystem.cs
View file @
09de87fa
...
@@ -5,11 +5,22 @@ using Unity.Physics;
...
@@ -5,11 +5,22 @@ using Unity.Physics;
using
Unity.Transforms
;
using
Unity.Transforms
;
using
UnityEngine
;
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)]
[WorldSystemFilter(WorldSystemFilterFlags.ServerSimulation)]
partial
struct
EnemySpawnerSystem
:
ISystem
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
)
public
void
OnCreate
(
ref
SystemState
state
)
{
{
// Require specific components and entities for the system to update.
state
.
RequireForUpdate
<
GamePlayingTag
>();
state
.
RequireForUpdate
<
GamePlayingTag
>();
state
.
RequireForUpdate
<
EnemySpawnPoints
>();
state
.
RequireForUpdate
<
EnemySpawnPoints
>();
state
.
RequireForUpdate
<
EnemySpawnTimer
>();
state
.
RequireForUpdate
<
EnemySpawnTimer
>();
...
@@ -17,17 +28,27 @@ partial struct EnemySpawnerSystem : ISystem
...
@@ -17,17 +28,27 @@ partial struct EnemySpawnerSystem : ISystem
state
.
RequireForUpdate
<
BeginSimulationEntityCommandBufferSystem
.
Singleton
>();
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
]
[
BurstCompile
]
public
void
OnUpdate
(
ref
SystemState
state
)
public
void
OnUpdate
(
ref
SystemState
state
)
{
{
// Retrieve the time elapsed since the last frame.
var
deltaTime
=
SystemAPI
.
Time
.
DeltaTime
;
var
deltaTime
=
SystemAPI
.
Time
.
DeltaTime
;
// Iterate through all entities with the EnemySpawnerAspect.
foreach
(
EnemySpawnerAspect
aspect
in
SystemAPI
.
Query
<
EnemySpawnerAspect
>())
foreach
(
EnemySpawnerAspect
aspect
in
SystemAPI
.
Query
<
EnemySpawnerAspect
>())
{
{
// Decrement spawn timers for the current aspect.
aspect
.
DecrementTimers
(
deltaTime
);
aspect
.
DecrementTimers
(
deltaTime
);
// Check if a slime enemy can spawn.
if
(
aspect
.
CanEnemySlimeSpawn
)
if
(
aspect
.
CanEnemySlimeSpawn
)
{
{
// Retrieve spawn points and the command buffer for entity modifications.
Entity
enemyPropertiesEntity
=
SystemAPI
.
GetSingletonEntity
<
EnemySpawnPoints
>();
Entity
enemyPropertiesEntity
=
SystemAPI
.
GetSingletonEntity
<
EnemySpawnPoints
>();
DynamicBuffer
<
EnemySpawnPoints
>
spawnPoints
=
DynamicBuffer
<
EnemySpawnPoints
>
spawnPoints
=
SystemAPI
.
GetBuffer
<
EnemySpawnPoints
>(
enemyPropertiesEntity
);
SystemAPI
.
GetBuffer
<
EnemySpawnPoints
>(
enemyPropertiesEntity
);
...
@@ -35,17 +56,22 @@ partial struct EnemySpawnerSystem : ISystem
...
@@ -35,17 +56,22 @@ partial struct EnemySpawnerSystem : ISystem
var
ecb
=
ecbSingleton
.
CreateCommandBuffer
(
state
.
WorldUnmanaged
);
var
ecb
=
ecbSingleton
.
CreateCommandBuffer
(
state
.
WorldUnmanaged
);
Entity
enemySlimeEntity
=
SystemAPI
.
GetSingleton
<
EntititesReferences
>().
SlimeEnemyEntity
;
Entity
enemySlimeEntity
=
SystemAPI
.
GetSingleton
<
EntititesReferences
>().
SlimeEnemyEntity
;
// Calculate the spawn position for the slime enemy.
int
slimeSpawnIndex
=
aspect
.
SlimeEnemyCounter
%
spawnPoints
.
Length
;
int
slimeSpawnIndex
=
aspect
.
SlimeEnemyCounter
%
spawnPoints
.
Length
;
float
randomValue
=
aspect
.
RandomSpawnOffset
;
float
randomValue
=
aspect
.
RandomSpawnOffset
;
float3
spawnPosition
=
float3
spawnPosition
=
spawnPoints
[
slimeSpawnIndex
].
SpawnPoint
+
new
float3
(
randomValue
,
0
,
-
randomValue
);
spawnPoints
[
slimeSpawnIndex
].
SpawnPoint
+
new
float3
(
randomValue
,
0
,
-
randomValue
);
// Spawn the slime enemy and update the aspect's counters and timers.
SpawnEnemy
(
ecb
,
enemySlimeEntity
,
spawnPosition
);
SpawnEnemy
(
ecb
,
enemySlimeEntity
,
spawnPosition
);
aspect
.
IncreaseSlimeCounter
();
aspect
.
IncreaseSlimeCounter
();
aspect
.
ResetSlimeTimer
();
aspect
.
ResetSlimeTimer
();
}
}
// Check if a rogue enemy can spawn.
if
(
aspect
.
CanEnemyRogueSpawn
)
if
(
aspect
.
CanEnemyRogueSpawn
)
{
{
// Retrieve spawn points and the command buffer for entity modifications.
Entity
enemyPropertiesEntity
=
SystemAPI
.
GetSingletonEntity
<
EnemySpawnPoints
>();
Entity
enemyPropertiesEntity
=
SystemAPI
.
GetSingletonEntity
<
EnemySpawnPoints
>();
DynamicBuffer
<
EnemySpawnPoints
>
spawnPoints
=
DynamicBuffer
<
EnemySpawnPoints
>
spawnPoints
=
SystemAPI
.
GetBuffer
<
EnemySpawnPoints
>(
enemyPropertiesEntity
);
SystemAPI
.
GetBuffer
<
EnemySpawnPoints
>(
enemyPropertiesEntity
);
...
@@ -53,10 +79,13 @@ partial struct EnemySpawnerSystem : ISystem
...
@@ -53,10 +79,13 @@ partial struct EnemySpawnerSystem : ISystem
var
ecb
=
ecbSingleton
.
CreateCommandBuffer
(
state
.
WorldUnmanaged
);
var
ecb
=
ecbSingleton
.
CreateCommandBuffer
(
state
.
WorldUnmanaged
);
Entity
enemyRogueEntity
=
SystemAPI
.
GetSingleton
<
EntititesReferences
>().
RougeEnemyEntity
;
Entity
enemyRogueEntity
=
SystemAPI
.
GetSingleton
<
EntititesReferences
>().
RougeEnemyEntity
;
// Calculate the spawn position for the rogue enemy.
int
rogueSpawnIndex
=
aspect
.
RogueEnemyCounter
%
spawnPoints
.
Length
;
int
rogueSpawnIndex
=
aspect
.
RogueEnemyCounter
%
spawnPoints
.
Length
;
float
randomValue
=
aspect
.
RandomSpawnOffset
;
float
randomValue
=
aspect
.
RandomSpawnOffset
;
float3
spawnPosition
=
float3
spawnPosition
=
spawnPoints
[
rogueSpawnIndex
].
SpawnPoint
+
new
float3
(
randomValue
,
0
,
-
randomValue
);
spawnPoints
[
rogueSpawnIndex
].
SpawnPoint
+
new
float3
(
randomValue
,
0
,
-
randomValue
);
// Spawn the rogue enemy and update the aspect's counters and timers.
SpawnEnemy
(
ecb
,
enemyRogueEntity
,
spawnPosition
);
SpawnEnemy
(
ecb
,
enemyRogueEntity
,
spawnPosition
);
aspect
.
IncreaseRogueCounter
();
aspect
.
IncreaseRogueCounter
();
aspect
.
ResetRoqueTimer
();
aspect
.
ResetRoqueTimer
();
...
@@ -64,10 +93,16 @@ partial struct EnemySpawnerSystem : ISystem
...
@@ -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
)
void
SpawnEnemy
(
EntityCommandBuffer
ecb
,
Entity
spawnableEntity
,
float3
position
)
{
{
// Instantiate the enemy entity and set its position.
Entity
newEnemy
=
ecb
.
Instantiate
(
spawnableEntity
);
Entity
newEnemy
=
ecb
.
Instantiate
(
spawnableEntity
);
ecb
.
SetComponent
(
newEnemy
,
ecb
.
SetComponent
(
newEnemy
,
LocalTransform
.
FromPosition
(
position
));
LocalTransform
.
FromPosition
(
position
));
}
}
}
}
\ No newline at end of file
Assets/_Game/Code/Systems/Input/NetcodePlayerInputSystem.cs
View file @
09de87fa
...
@@ -5,32 +5,46 @@ using Unity.Mathematics;
...
@@ -5,32 +5,46 @@ using Unity.Mathematics;
using
Unity.NetCode
;
using
Unity.NetCode
;
using
UnityEngine
;
using
UnityEngine
;
[UpdateInGroup(typeof(GhostInputSystemGroup))]
[UpdateInGroup(typeof(GhostInputSystemGroup))]
public
partial
class
NetcodePlayerInputSystem
:
SystemBase
public
partial
class
NetcodePlayerInputSystem
:
SystemBase
{
{
private
InputSystem_Actions
_inputActions
;
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
()
protected
override
void
OnCreate
()
{
{
// Initialize and enable the input actions.
_inputActions
=
new
InputSystem_Actions
();
_inputActions
=
new
InputSystem_Actions
();
_inputActions
.
Enable
();
_inputActions
.
Enable
();
// Require specific components and entities for the system to update.
RequireForUpdate
<
NetworkStreamInGame
>();
RequireForUpdate
<
NetworkStreamInGame
>();
RequireForUpdate
<
GamePlayingTag
>();
RequireForUpdate
<
GamePlayingTag
>();
RequireForUpdate
<
NetcodePlayerInput
>();
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
()
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
>>()
foreach
(
RefRW
<
NetcodePlayerInput
>
netcodePlayerInput
in
SystemAPI
.
Query
<
RefRW
<
NetcodePlayerInput
>>()
.
WithAll
<
GhostOwnerIsLocal
>())
.
WithAll
<
GhostOwnerIsLocal
>())
{
{
// Update the input vector and sprinting state from the input actions.
netcodePlayerInput
.
ValueRW
.
inputVector
=
_inputActions
.
Player
.
Move
.
ReadValue
<
Vector2
>();
netcodePlayerInput
.
ValueRW
.
inputVector
=
_inputActions
.
Player
.
Move
.
ReadValue
<
Vector2
>();
netcodePlayerInput
.
ValueRW
.
isSprinting
=
_inputActions
.
Player
.
Sprint
.
IsPressed
();
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
()
protected
override
void
OnDestroy
()
{
{
base
.
OnDestroy
();
base
.
OnDestroy
();
...
...
Assets/_Game/Code/Systems/Input/NetcodePlayerMovementSystem.cs
View file @
09de87fa
...
@@ -6,12 +6,23 @@ using Unity.Physics;
...
@@ -6,12 +6,23 @@ using Unity.Physics;
using
Unity.Transforms
;
using
Unity.Transforms
;
using
UnityEngine
;
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))]
[UpdateInGroup(typeof(PredictedSimulationSystemGroup))]
partial
struct
NetcodePlayerMovementSystem
:
ISystem
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
]
[
BurstCompile
]
public
void
OnCreate
(
ref
SystemState
state
)
public
void
OnCreate
(
ref
SystemState
state
)
{
{
// Require specific components and entities for the system to update.
state
.
RequireForUpdate
<
NetcodePlayerInput
>();
state
.
RequireForUpdate
<
NetcodePlayerInput
>();
state
.
RequireForUpdate
<
PhysicsVelocity
>();
state
.
RequireForUpdate
<
PhysicsVelocity
>();
state
.
RequireForUpdate
<
LocalTransform
>();
state
.
RequireForUpdate
<
LocalTransform
>();
...
@@ -19,11 +30,18 @@ partial struct NetcodePlayerMovementSystem : ISystem
...
@@ -19,11 +30,18 @@ partial struct NetcodePlayerMovementSystem : ISystem
state
.
RequireForUpdate
<
GamePlayingTag
>();
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
]
[
BurstCompile
]
public
void
OnUpdate
(
ref
SystemState
state
)
public
void
OnUpdate
(
ref
SystemState
state
)
{
{
// Retrieve the time elapsed since the last frame.
float
deltaTime
=
SystemAPI
.
Time
.
DeltaTime
;
float
deltaTime
=
SystemAPI
.
Time
.
DeltaTime
;
// Iterate through all entities with the required components.
foreach
((
RefRO
<
NetcodePlayerInput
>
netcodePlayerInput
,
RefRW
<
PhysicsVelocity
>
physicsVelocity
,
foreach
((
RefRO
<
NetcodePlayerInput
>
netcodePlayerInput
,
RefRW
<
PhysicsVelocity
>
physicsVelocity
,
RefRW
<
LocalTransform
>
localTransform
,
RefRW
<
PlayerSprintData
>
sprintData
)
RefRW
<
LocalTransform
>
localTransform
,
RefRW
<
PlayerSprintData
>
sprintData
)
in
SystemAPI
in
SystemAPI
...
@@ -31,13 +49,17 @@ partial struct NetcodePlayerMovementSystem : ISystem
...
@@ -31,13 +49,17 @@ partial struct NetcodePlayerMovementSystem : ISystem
RefRW
<
PlayerSprintData
>>()
RefRW
<
PlayerSprintData
>>()
.
WithAll
<
Simulate
>())
.
WithAll
<
Simulate
>())
{
{
// Calculate the movement vector based on player input.
float3
moveVector
=
new
float3
(
netcodePlayerInput
.
ValueRO
.
inputVector
.
x
,
0
,
float3
moveVector
=
new
float3
(
netcodePlayerInput
.
ValueRO
.
inputVector
.
x
,
0
,
netcodePlayerInput
.
ValueRO
.
inputVector
.
y
);
netcodePlayerInput
.
ValueRO
.
inputVector
.
y
);
// Set the default movement speed to walking speed.
float
moveSpeed
=
sprintData
.
ValueRO
.
walkSpeed
;
float
moveSpeed
=
sprintData
.
ValueRO
.
walkSpeed
;
// Check if the player is sprinting.
if
(
netcodePlayerInput
.
ValueRO
.
isSprinting
)
if
(
netcodePlayerInput
.
ValueRO
.
isSprinting
)
{
{
// Handle sprinting logic and cooldowns.
if
(!
sprintData
.
ValueRO
.
isSprintCooldown
)
if
(!
sprintData
.
ValueRO
.
isSprintCooldown
)
{
{
sprintData
.
ValueRW
.
sprintRemaining
-=
deltaTime
;
sprintData
.
ValueRW
.
sprintRemaining
-=
deltaTime
;
...
@@ -53,6 +75,7 @@ partial struct NetcodePlayerMovementSystem : ISystem
...
@@ -53,6 +75,7 @@ partial struct NetcodePlayerMovementSystem : ISystem
}
}
}
}
// Handle sprint cooldown logic.
if
(
sprintData
.
ValueRO
.
isSprintCooldown
)
if
(
sprintData
.
ValueRO
.
isSprintCooldown
)
{
{
sprintData
.
ValueRW
.
sprintCooldown
-=
deltaTime
;
sprintData
.
ValueRW
.
sprintCooldown
-=
deltaTime
;
...
@@ -62,10 +85,11 @@ partial struct NetcodePlayerMovementSystem : ISystem
...
@@ -62,10 +85,11 @@ partial struct NetcodePlayerMovementSystem : ISystem
}
}
}
}
// Smoothly update the player's velocity based on the movement vector and speed.
physicsVelocity
.
ValueRW
.
Linear
=
physicsVelocity
.
ValueRW
.
Linear
=
math
.
lerp
(
physicsVelocity
.
ValueRO
.
Linear
,
moveVector
*
moveSpeed
,
deltaTime
*
10
);
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
))
if
(!
math
.
all
(
moveVector
==
float3
.
zero
))
{
{
quaternion
targetRotation
=
quaternion
.
LookRotationSafe
(
moveVector
,
math
.
up
());
quaternion
targetRotation
=
quaternion
.
LookRotationSafe
(
moveVector
,
math
.
up
());
...
...
Assets/_Game/Prefabs/Enemies/Arrow.prefab
View file @
09de87fa
...
@@ -222,7 +222,7 @@ MonoBehaviour:
...
@@ -222,7 +222,7 @@ MonoBehaviour:
m_Script
:
{
fileID
:
11500000
,
guid
:
7e19d579a4586dd40805276eb2d1a684
,
type
:
3
}
m_Script
:
{
fileID
:
11500000
,
guid
:
7e19d579a4586dd40805276eb2d1a684
,
type
:
3
}
m_Name
:
m_Name
:
m_EditorClassIdentifier
:
m_EditorClassIdentifier
:
DestroyOnTimer
:
2
DestroyOnTimer
:
3
---
!u!114
&1149029900162062154
---
!u!114
&1149029900162062154
MonoBehaviour
:
MonoBehaviour
:
m_ObjectHideFlags
:
0
m_ObjectHideFlags
:
0
...
@@ -298,7 +298,7 @@ MonoBehaviour:
...
@@ -298,7 +298,7 @@ MonoBehaviour:
m_Script
:
{
fileID
:
11500000
,
guid
:
2de495c310a322143be08aa7819a1a33
,
type
:
3
}
m_Script
:
{
fileID
:
11500000
,
guid
:
2de495c310a322143be08aa7819a1a33
,
type
:
3
}
m_Name
:
m_Name
:
m_EditorClassIdentifier
:
m_EditorClassIdentifier
:
DamageOnTrigger
:
0
DamageOnTrigger
:
2
0
---
!u!1001
&7309850180045348080
---
!u!1001
&7309850180045348080
PrefabInstance
:
PrefabInstance
:
m_ObjectHideFlags
:
0
m_ObjectHideFlags
:
0
...
...
Assets/_Game/Prefabs/Enemies/Cube.prefab
View file @
09de87fa
...
@@ -103,7 +103,7 @@ MonoBehaviour:
...
@@ -103,7 +103,7 @@ MonoBehaviour:
m_Script
:
{
fileID
:
11500000
,
guid
:
2de495c310a322143be08aa7819a1a33
,
type
:
3
}
m_Script
:
{
fileID
:
11500000
,
guid
:
2de495c310a322143be08aa7819a1a33
,
type
:
3
}
m_Name
:
m_Name
:
m_EditorClassIdentifier
:
m_EditorClassIdentifier
:
DamageOnTrigger
:
5
0
DamageOnTrigger
:
1
0
---
!u!114
&-8527907258641966621
---
!u!114
&-8527907258641966621
MonoBehaviour
:
MonoBehaviour
:
m_ObjectHideFlags
:
0
m_ObjectHideFlags
:
0
...
@@ -129,7 +129,7 @@ MonoBehaviour:
...
@@ -129,7 +129,7 @@ MonoBehaviour:
m_Script
:
{
fileID
:
11500000
,
guid
:
d8b4a5cfe7236db4dba833c5751c2665
,
type
:
3
}
m_Script
:
{
fileID
:
11500000
,
guid
:
d8b4a5cfe7236db4dba833c5751c2665
,
type
:
3
}
m_Name
:
m_Name
:
m_EditorClassIdentifier
:
m_EditorClassIdentifier
:
NpcTargetRadius
:
8
0
NpcTargetRadius
:
1
0
---
!u!114
&-274669407858188047
---
!u!114
&-274669407858188047
MonoBehaviour
:
MonoBehaviour
:
m_ObjectHideFlags
:
0
m_ObjectHideFlags
:
0
...
...
Assets/_Game/Prefabs/Enemies/RogueEnemy.prefab
View file @
09de87fa
...
@@ -100,7 +100,7 @@ MonoBehaviour:
...
@@ -100,7 +100,7 @@ MonoBehaviour:
m_Script
:
{
fileID
:
11500000
,
guid
:
8194ce63a01ea9044a8c5a6ebd0e2829
,
type
:
3
}
m_Script
:
{
fileID
:
11500000
,
guid
:
8194ce63a01ea9044a8c5a6ebd0e2829
,
type
:
3
}
m_Name
:
m_Name
:
m_EditorClassIdentifier
:
m_EditorClassIdentifier
:
NpcTargetRadius
:
80
NpcTargetRadius
:
13
AttackCooldownTime
:
2
AttackCooldownTime
:
2
FirePointOffset
:
{
x
:
0
,
y
:
1.5
,
z
:
0
}
FirePointOffset
:
{
x
:
0
,
y
:
1.5
,
z
:
0
}
AttackPrefab
:
{
fileID
:
357967689053387235
,
guid
:
19127a4c1ac11844db4373b9e147918b
,
type
:
3
}
AttackPrefab
:
{
fileID
:
357967689053387235
,
guid
:
19127a4c1ac11844db4373b9e147918b
,
type
:
3
}
...
...
Assets/_Game/Prefabs/Player/Player.prefab
View file @
09de87fa
...
@@ -149,8 +149,8 @@ MonoBehaviour:
...
@@ -149,8 +149,8 @@ MonoBehaviour:
m_EditorClassIdentifier
:
m_EditorClassIdentifier
:
sprintRemaining
:
5
sprintRemaining
:
5
sprintDuration
:
5
sprintDuration
:
5
sprintSpeed
:
12
sprintSpeed
:
7
walkSpeed
:
8
walkSpeed
:
5
sprintCooldownReset
:
1
sprintCooldownReset
:
1
---
!u!114
&3955402020692204287
---
!u!114
&3955402020692204287
MonoBehaviour
:
MonoBehaviour
:
...
@@ -320,7 +320,7 @@ MonoBehaviour:
...
@@ -320,7 +320,7 @@ MonoBehaviour:
m_Script
:
{
fileID
:
11500000
,
guid
:
8e73c985659b81d408f4e99cb152349d
,
type
:
3
}
m_Script
:
{
fileID
:
11500000
,
guid
:
8e73c985659b81d408f4e99cb152349d
,
type
:
3
}
m_Name
:
m_Name
:
m_EditorClassIdentifier
:
m_EditorClassIdentifier
:
MaxHitPoints
:
10
00
MaxHitPoints
:
2
00
HealthBarOffset
:
{
x
:
0
,
y
:
2.5
,
z
:
0
}
HealthBarOffset
:
{
x
:
0
,
y
:
2.5
,
z
:
0
}
---
!u!1001
&6975352639711469968
---
!u!1001
&6975352639711469968
PrefabInstance
:
PrefabInstance
:
...
...
Assets/_Game/Prefabs/Player/RespawnEntity.prefab
View file @
09de87fa
...
@@ -63,7 +63,7 @@ MonoBehaviour:
...
@@ -63,7 +63,7 @@ MonoBehaviour:
OptimizationMode
:
0
OptimizationMode
:
0
Importance
:
10
Importance
:
10
MaxSendRate
:
0
MaxSendRate
:
0
prefabId
:
prefabId
:
b3cdfc2eac1a20f4fa01943b331adf7e
HasOwner
:
0
HasOwner
:
0
SupportAutoCommandTarget
:
1
SupportAutoCommandTarget
:
1
TrackInterpolationDelay
:
0
TrackInterpolationDelay
:
0
...
@@ -84,4 +84,4 @@ MonoBehaviour:
...
@@ -84,4 +84,4 @@ MonoBehaviour:
m_Name
:
m_Name
:
m_EditorClassIdentifier
:
m_EditorClassIdentifier
:
RespawnTime
:
10
RespawnTime
:
10
NetCodeConfig
:
{
fileID
:
11400000
,
guid
:
cd69de227738309429193c9089949c16
,
type
:
2
}
NetCodeConfig
:
{
fileID
:
11400000
,
guid
:
edda5bd0f33a95e4a98f780e0e3ae9e8
,
type
:
2
}
Assets/_Game/Scenes/ConnectionScene.unity
View file @
09de87fa
...
@@ -489,9 +489,9 @@ MonoBehaviour:
...
@@ -489,9 +489,9 @@ MonoBehaviour:
RangerAmountContainer
:
{
fileID
:
7438304585132253662
}
RangerAmountContainer
:
{
fileID
:
7438304585132253662
}
SlimeAmountContainer
:
{
fileID
:
7438304585132253661
}
SlimeAmountContainer
:
{
fileID
:
7438304585132253661
}
_connectButton
:
{
fileID
:
661734915
}
_connectButton
:
{
fileID
:
661734915
}
_gameStartCountDownTime
:
2
_gameStartCountDownTime
:
10
_slimeSpawnCooldownTime
:
0.0
1
_slimeSpawnCooldownTime
:
1
_rogueSpawnCooldownTime
:
0.1
_rogueSpawnCooldownTime
:
2
---
!u!114
&1734898360
stripped
---
!u!114
&1734898360
stripped
MonoBehaviour
:
MonoBehaviour
:
m_CorrespondingSourceObject
:
{
fileID
:
8650075725555599494
,
guid
:
72327351aa28fbe4fb4c16c3d17e4de5
,
type
:
3
}
m_CorrespondingSourceObject
:
{
fileID
:
8650075725555599494
,
guid
:
72327351aa28fbe4fb4c16c3d17e4de5
,
type
:
3
}
...
...
Assets/_Game/Scenes/GameScene.unity
View file @
09de87fa
...
@@ -336,6 +336,26 @@ PrefabInstance:
...
@@ -336,6 +336,26 @@ PrefabInstance:
propertyPath
:
m_LocalEulerAnglesHint.z
propertyPath
:
m_LocalEulerAnglesHint.z
value
:
0
value
:
0
objectReference
:
{
fileID
:
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_RemovedComponents
:
[]
m_RemovedGameObjects
:
[]
m_RemovedGameObjects
:
[]
m_AddedGameObjects
:
[]
m_AddedGameObjects
:
[]
...
...
ProjectSettings/EditorBuildSettings.asset
View file @
09de87fa
...
@@ -9,11 +9,11 @@ EditorBuildSettings:
...
@@ -9,11 +9,11 @@ EditorBuildSettings:
path
:
Assets/_Game/Scenes/ConnectionScene.unity
path
:
Assets/_Game/Scenes/ConnectionScene.unity
guid
:
5652506fc72e4fa42a51d693df592812
guid
:
5652506fc72e4fa42a51d693df592812
-
enabled
:
1
-
enabled
:
1
path
:
Assets/_Game/Scenes/TestGameScene.unity
guid
:
a3178e3c90053df499a40c2abd214157
-
enabled
:
1
path
:
Assets/_Game/Scenes/GameScene.unity
path
:
Assets/_Game/Scenes/GameScene.unity
guid
:
99c9720ab356a0642a771bea13969a05
guid
:
99c9720ab356a0642a771bea13969a05
-
enabled
:
1
path
:
Assets/_Game/Scenes/TestGameScene.unity
guid
:
a3178e3c90053df499a40c2abd214157
m_configObjects
:
m_configObjects
:
com.unity.input.settings.actions
:
{
fileID
:
-944628639613478452
,
guid
:
14b037eabac02c946b6a54f5e7289a64
,
type
:
3
}
com.unity.input.settings.actions
:
{
fileID
:
-944628639613478452
,
guid
:
14b037eabac02c946b6a54f5e7289a64
,
type
:
3
}
m_UseUCBPForAssetBundles
:
0
m_UseUCBPForAssetBundles
:
0
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment