Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save dezashibi/b8ceb0c6b38a1f0372e536f5ce282047 to your computer and use it in GitHub Desktop.
Save dezashibi/b8ceb0c6b38a1f0372e536f5ce282047 to your computer and use it in GitHub Desktop.
Scripts from tutorial: Tutorial: SURVIVORS-LIKE w/ Unity DOTS & ECS - https://youtu.be/cc5l66FwpQ4
using UnityEngine;
namespace TMG.Survivors
{
public class CameraTargetSingleton : MonoBehaviour
{
public static CameraTargetSingleton Instance;
public void Awake()
{
if (Instance != null)
{
Debug.LogWarning("Warning multiple instances of CameraTargetSingleton detected. Destroying new instance.", Instance);
Destroy(gameObject);
return;
}
Instance = this;
}
}
}
using UnityEngine;
using Unity.Entities;
using Unity.Mathematics;
using Unity.Physics;
using Unity.Burst;
using Unity.Rendering;
namespace TMG.Survivors
{
public struct InitializeCharacterFlag : IComponentData, IEnableableComponent {}
public struct CharacterMoveDirection : IComponentData
{
public float2 Value;
}
public struct CharacterMoveSpeed : IComponentData
{
public float Value;
}
[MaterialProperty("_FacingDirection")]
public struct FacingDirectionOverride : IComponentData
{
public float Value;
}
public struct CharacterMaxHitPoints : IComponentData
{
public int Value;
}
public struct CharacterCurrentHitPoints : IComponentData
{
public int Value;
}
public struct DamageThisFrame : IBufferElementData
{
public int Value;
}
public class CharacterAuthoring : MonoBehaviour
{
public float MoveSpeed;
public int HitPoints;
private class Baker : Baker<CharacterAuthoring>
{
public override void Bake(CharacterAuthoring authoring)
{
var entity = GetEntity(TransformUsageFlags.Dynamic);
AddComponent<InitializeCharacterFlag>(entity);
AddComponent<CharacterMoveDirection>(entity);
AddComponent(entity, new CharacterMoveSpeed
{
Value = authoring.MoveSpeed
});
AddComponent(entity, new FacingDirectionOverride
{
Value = 1
});
AddComponent(entity, new CharacterMaxHitPoints
{
Value = authoring.HitPoints
});
AddComponent(entity, new CharacterCurrentHitPoints
{
Value = authoring.HitPoints
});
AddBuffer<DamageThisFrame>(entity);
AddComponent<DestroyEntityFlag>(entity);
SetComponentEnabled<DestroyEntityFlag>(entity, false);
}
}
}
[UpdateInGroup(typeof(InitializationSystemGroup))]
public partial struct CharacterInitializationSystem : ISystem
{
[BurstCompile]
public void OnUpdate(ref SystemState state)
{
foreach (var (mass, shouldInitialize) in SystemAPI.Query<RefRW<PhysicsMass>, EnabledRefRW<InitializeCharacterFlag>>())
{
mass.ValueRW.InverseInertia = float3.zero;
shouldInitialize.ValueRW = false;
}
}
}
public partial struct CharacterMoveSystem : ISystem
{
[BurstCompile]
public void OnUpdate(ref SystemState state)
{
foreach (var (velocity, facingDirection, direction, speed, entity) in SystemAPI.Query<RefRW<PhysicsVelocity>, RefRW<FacingDirectionOverride>, CharacterMoveDirection, CharacterMoveSpeed>().WithEntityAccess())
{
var moveStep2d = direction.Value * speed.Value;
velocity.ValueRW.Linear = new float3(moveStep2d, 0f);
if (math.abs(moveStep2d.x) > 0.15f)
{
facingDirection.ValueRW.Value = math.sign(moveStep2d.x);
}
if (SystemAPI.HasComponent<PlayerTag>(entity))
{
var animationOverride = SystemAPI.GetComponentRW<AnimationIndexOverride>(entity);
var animationType = math.lengthsq(moveStep2d) > float.Epsilon ? PlayerAnimationIndex.Movement : PlayerAnimationIndex.Idle;
animationOverride.ValueRW.Value = (float)animationType;
}
}
}
}
public partial struct GlobalTimeUpdateSystem : ISystem
{
private static int _globalTimeShaderPropertyID;
public void OnCreate(ref SystemState state)
{
_globalTimeShaderPropertyID = Shader.PropertyToID("_GlobalTime");
}
public void OnUpdate(ref SystemState state)
{
Shader.SetGlobalFloat(_globalTimeShaderPropertyID, (float)SystemAPI.Time.ElapsedTime);
}
}
public partial struct ProcessDamageThisFrameSystem : ISystem
{
[BurstCompile]
public void OnUpdate(ref SystemState state)
{
foreach (var (hitPoints, damageThisFrame, entity) in SystemAPI.Query<RefRW<CharacterCurrentHitPoints>, DynamicBuffer<DamageThisFrame>>().WithPresent<DestroyEntityFlag>().WithEntityAccess())
{
if (damageThisFrame.IsEmpty) continue;
foreach (var damage in damageThisFrame)
{
hitPoints.ValueRW.Value -= damage.Value;
}
damageThisFrame.Clear();
if (hitPoints.ValueRO.Value <= 0)
{
SystemAPI.SetComponentEnabled<DestroyEntityFlag>(entity, true);
}
}
}
}
}
using Unity.Entities;
using Unity.Transforms;
namespace TMG.Survivors
{
public struct DestroyEntityFlag : IComponentData, IEnableableComponent {}
[UpdateInGroup(typeof(SimulationSystemGroup), OrderLast = true)]
[UpdateBefore(typeof(EndSimulationEntityCommandBufferSystem))]
public partial struct DestroyEntitySystem : ISystem
{
public void OnCreate(ref SystemState state)
{
state.RequireForUpdate<BeginInitializationEntityCommandBufferSystem.Singleton>();
state.RequireForUpdate<EndSimulationEntityCommandBufferSystem.Singleton>();
}
public void OnUpdate(ref SystemState state)
{
var endEcbSystem = SystemAPI.GetSingleton<EndSimulationEntityCommandBufferSystem.Singleton>();
var endEcb = endEcbSystem.CreateCommandBuffer(state.WorldUnmanaged);
var beginEcbSystem = SystemAPI.GetSingleton<BeginInitializationEntityCommandBufferSystem.Singleton>();
var beginEcb = beginEcbSystem.CreateCommandBuffer(state.WorldUnmanaged);
foreach (var (_, entity) in SystemAPI.Query<DestroyEntityFlag>().WithEntityAccess())
{
if (SystemAPI.HasComponent<PlayerTag>(entity))
{
GameUIController.Instance.ShowGameOverUI();
}
if (SystemAPI.HasComponent<GemPrefab>(entity))
{
var gemPrefab = SystemAPI.GetComponent<GemPrefab>(entity).Value;
var newGem = beginEcb.Instantiate(gemPrefab);
var spawnPosition = SystemAPI.GetComponent<LocalToWorld>(entity).Position;
beginEcb.SetComponent(newGem, LocalTransform.FromPosition(spawnPosition));
}
endEcb.DestroyEntity(entity);
}
}
}
}
using Unity.Burst;
using Unity.Collections;
using UnityEngine;
using Unity.Entities;
using Unity.Mathematics;
using Unity.Physics;
using Unity.Physics.Systems;
using Unity.Transforms;
namespace TMG.Survivors
{
public struct EnemyTag : IComponentData {}
public struct EnemyAttackData : IComponentData
{
public int HitPoints;
public float CooldownTime;
}
public struct EnemyCooldownExpirationTimestamp : IComponentData, IEnableableComponent
{
public double Value;
}
public struct GemPrefab : IComponentData
{
public Entity Value;
}
[RequireComponent(typeof(CharacterAuthoring))]
public class EnemyAuthoring : MonoBehaviour
{
public int AttackDamage;
public float CooldownTime;
public GameObject GemPrefab;
private class Baker : Baker<EnemyAuthoring>
{
public override void Bake(EnemyAuthoring authoring)
{
var entity = GetEntity(TransformUsageFlags.Dynamic);
AddComponent<EnemyTag>(entity);
AddComponent(entity, new EnemyAttackData
{
HitPoints = authoring.AttackDamage,
CooldownTime = authoring.CooldownTime
});
AddComponent<EnemyCooldownExpirationTimestamp>(entity);
SetComponentEnabled<EnemyCooldownExpirationTimestamp>(entity, false);
AddComponent(entity, new GemPrefab
{
Value = GetEntity(authoring.GemPrefab, TransformUsageFlags.Dynamic)
});
}
}
}
public partial struct EnemyMoveToPlayerSystem : ISystem
{
public void OnCreate(ref SystemState state)
{
state.RequireForUpdate<PlayerTag>();
}
public void OnUpdate(ref SystemState state)
{
var playerEntity = SystemAPI.GetSingletonEntity<PlayerTag>();
var playerPosition = SystemAPI.GetComponent<LocalTransform>(playerEntity).Position.xy;
var moveToPlayerJob = new EnemyMoveToPlayerJob
{
PlayerPosition = playerPosition
};
state.Dependency = moveToPlayerJob.ScheduleParallel(state.Dependency);
}
}
[BurstCompile]
[WithAll(typeof(EnemyTag))]
public partial struct EnemyMoveToPlayerJob : IJobEntity
{
public float2 PlayerPosition;
private void Execute(ref CharacterMoveDirection direction, in LocalTransform transform)
{
var vectorToPlayer = PlayerPosition - transform.Position.xy;
direction.Value = math.normalize(vectorToPlayer);
}
}
[UpdateInGroup(typeof(PhysicsSystemGroup))]
[UpdateAfter(typeof(PhysicsSimulationGroup))]
[UpdateBefore(typeof(AfterPhysicsSystemGroup))]
public partial struct EnemyAttackSystem : ISystem
{
public void OnCreate(ref SystemState state)
{
state.RequireForUpdate<SimulationSingleton>();
}
[BurstCompile]
public void OnUpdate(ref SystemState state)
{
var elapsedTime = SystemAPI.Time.ElapsedTime;
foreach (var (expirationTimestamp, cooldownEnabled) in SystemAPI.Query<EnemyCooldownExpirationTimestamp, EnabledRefRW<EnemyCooldownExpirationTimestamp>>())
{
if (expirationTimestamp.Value > elapsedTime) continue;
cooldownEnabled.ValueRW = false;
}
var attackJob = new EnemyAttackJob
{
PlayerLookup = SystemAPI.GetComponentLookup<PlayerTag>(true),
AttackDataLookup = SystemAPI.GetComponentLookup<EnemyAttackData>(true),
CooldownLookup = SystemAPI.GetComponentLookup<EnemyCooldownExpirationTimestamp>(),
DamageBufferLookup = SystemAPI.GetBufferLookup<DamageThisFrame>(),
ElapsedTime = elapsedTime
};
var simulationSingleton = SystemAPI.GetSingleton<SimulationSingleton>();
state.Dependency = attackJob.Schedule(simulationSingleton, state.Dependency);
}
}
[BurstCompile]
public struct EnemyAttackJob : ICollisionEventsJob
{
[ReadOnly] public ComponentLookup<PlayerTag> PlayerLookup;
[ReadOnly] public ComponentLookup<EnemyAttackData> AttackDataLookup;
public ComponentLookup<EnemyCooldownExpirationTimestamp> CooldownLookup;
public BufferLookup<DamageThisFrame> DamageBufferLookup;
public double ElapsedTime;
public void Execute(CollisionEvent collisionEvent)
{
Entity playerEntity;
Entity enemyEntity;
if (PlayerLookup.HasComponent(collisionEvent.EntityA) && AttackDataLookup.HasComponent(collisionEvent.EntityB))
{
playerEntity = collisionEvent.EntityA;
enemyEntity = collisionEvent.EntityB;
}
else if (PlayerLookup.HasComponent(collisionEvent.EntityB) && AttackDataLookup.HasComponent(collisionEvent.EntityA))
{
playerEntity = collisionEvent.EntityB;
enemyEntity = collisionEvent.EntityA;
}
else
{
return;
}
if (CooldownLookup.IsComponentEnabled(enemyEntity)) return;
var attackData = AttackDataLookup[enemyEntity];
CooldownLookup[enemyEntity] = new EnemyCooldownExpirationTimestamp { Value = ElapsedTime + attackData.CooldownTime };
CooldownLookup.SetComponentEnabled(enemyEntity, true);
var playerDamageBuffer = DamageBufferLookup[playerEntity];
playerDamageBuffer.Add(new DamageThisFrame
{
Value = attackData.HitPoints
});
}
}
}
using Unity.Burst;
using UnityEngine;
using Unity.Entities;
using Unity.Mathematics;
using Unity.Transforms;
using Random = Unity.Mathematics.Random;
namespace TMG.Survivors
{
public struct EnemySpawnData : IComponentData
{
public Entity EnemyPrefab;
public Entity ReaperPrefab;
public float SpawnInterval;
public float SpawnDistance;
}
public struct EnemySpawnState : IComponentData
{
public float SpawnTimer;
public float ReaperSpawnTimer;
public Random Random;
}
public class EnemySpawnerAuthoring : MonoBehaviour
{
public GameObject EnemyPrefab;
public GameObject ReaperPrefab;
public float ReaperSpawnTime;
public float SpawnInterval;
public float SpawnDistance;
public uint RandomSeed;
private class Baker : Baker<EnemySpawnerAuthoring>
{
public override void Bake(EnemySpawnerAuthoring authoring)
{
var entity = GetEntity(TransformUsageFlags.None);
AddComponent(entity, new EnemySpawnData
{
EnemyPrefab = GetEntity(authoring.EnemyPrefab, TransformUsageFlags.Dynamic),
ReaperPrefab = GetEntity(authoring.ReaperPrefab, TransformUsageFlags.Dynamic),
SpawnInterval = authoring.SpawnInterval,
SpawnDistance = authoring.SpawnDistance
});
AddComponent(entity, new EnemySpawnState
{
SpawnTimer = 0f,
ReaperSpawnTimer = authoring.ReaperSpawnTime,
Random = Random.CreateFromIndex(authoring.RandomSeed)
});
}
}
}
public partial struct EnemySpawnSystem : ISystem
{
public void OnCreate(ref SystemState state)
{
state.RequireForUpdate<PlayerTag>();
state.RequireForUpdate<BeginInitializationEntityCommandBufferSystem.Singleton>();
}
[BurstCompile]
public void OnUpdate(ref SystemState state)
{
var deltaTime = SystemAPI.Time.DeltaTime;
var ecbSystem = SystemAPI.GetSingleton<BeginInitializationEntityCommandBufferSystem.Singleton>();
var ecb = ecbSystem.CreateCommandBuffer(state.WorldUnmanaged);
var playerEntity = SystemAPI.GetSingletonEntity<PlayerTag>();
var playerPosition = SystemAPI.GetComponent<LocalTransform>(playerEntity).Position;
foreach (var (spawnState, spawnData, entity) in SystemAPI.Query<RefRW<EnemySpawnState>, EnemySpawnData>().WithEntityAccess())
{
spawnState.ValueRW.ReaperSpawnTimer -= deltaTime;
if (spawnState.ValueRO.ReaperSpawnTimer <= 0f)
{
var reaper = ecb.Instantiate(spawnData.ReaperPrefab);
var reaperSpawnPoint = playerPosition + new float3(15f, 10f, 0f);
ecb.SetComponent(reaper, LocalTransform.FromPositionRotationScale(reaperSpawnPoint, quaternion.identity, 4f));
var enemyQuery = SystemAPI.QueryBuilder().WithAll<EnemyTag>().Build();
var enemies = enemyQuery.ToEntityArray(state.WorldUpdateAllocator);
ecb.DestroyEntity(enemies);
ecb.DestroyEntity(entity);
continue;
}
spawnState.ValueRW.SpawnTimer -= deltaTime;
if (spawnState.ValueRO.SpawnTimer > 0f) continue;
spawnState.ValueRW.SpawnTimer = spawnData.SpawnInterval;
var newEnemy = ecb.Instantiate(spawnData.EnemyPrefab);
var spawnAngle = spawnState.ValueRW.Random.NextFloat(0f, math.TAU);
var spawnPoint = new float3
{
x = math.sin(spawnAngle),
y = math.cos(spawnAngle),
z = 0f
};
spawnPoint *= spawnData.SpawnDistance;
spawnPoint += playerPosition;
ecb.SetComponent(newEnemy, LocalTransform.FromPosition(spawnPoint));
}
}
}
}
using Unity.Burst;
using UnityEngine;
using Unity.Entities;
using Unity.Physics;
using Unity.Collections;
namespace TMG.Survivors
{
public struct GemTag : IComponentData {}
public class GemAuthoring : MonoBehaviour
{
private class Baker : Baker<GemAuthoring>
{
public override void Bake(GemAuthoring authoring)
{
var entity = GetEntity(TransformUsageFlags.Dynamic);
AddComponent<GemTag>(entity);
AddComponent<DestroyEntityFlag>(entity);
SetComponentEnabled<DestroyEntityFlag>(entity, false);
}
}
}
public partial struct CollectGemSystem : ISystem
{
public void OnCreate(ref SystemState state)
{
state.RequireForUpdate<SimulationSingleton>();
}
[BurstCompile]
public void OnUpdate(ref SystemState state)
{
var newCollectJob = new CollectGemJob
{
GemLookup = SystemAPI.GetComponentLookup<GemTag>(true),
GemsCollectedLookup = SystemAPI.GetComponentLookup<GemsCollectedCount>(),
DestroyEntityLookup = SystemAPI.GetComponentLookup<DestroyEntityFlag>(),
UpdateGemUILookup = SystemAPI.GetComponentLookup<UpdateGemUIFlag>()
};
var simulationSingleton = SystemAPI.GetSingleton<SimulationSingleton>();
state.Dependency = newCollectJob.Schedule(simulationSingleton, state.Dependency);
}
}
[BurstCompile]
public struct CollectGemJob : ITriggerEventsJob
{
[ReadOnly] public ComponentLookup<GemTag> GemLookup;
public ComponentLookup<GemsCollectedCount> GemsCollectedLookup;
public ComponentLookup<DestroyEntityFlag> DestroyEntityLookup;
public ComponentLookup<UpdateGemUIFlag> UpdateGemUILookup;
public void Execute(TriggerEvent triggerEvent)
{
Entity gemEntity;
Entity playerEntity;
if (GemLookup.HasComponent(triggerEvent.EntityA) && GemsCollectedLookup.HasComponent(triggerEvent.EntityB))
{
gemEntity = triggerEvent.EntityA;
playerEntity = triggerEvent.EntityB;
}
else if (GemLookup.HasComponent(triggerEvent.EntityB) && GemsCollectedLookup.HasComponent(triggerEvent.EntityA))
{
gemEntity = triggerEvent.EntityB;
playerEntity = triggerEvent.EntityA;
}
else
{
return;
}
var gemsCollected = GemsCollectedLookup[playerEntity];
gemsCollected.Value += 1;
GemsCollectedLookup[playerEntity] = gemsCollected;
UpdateGemUILookup.SetComponentEnabled(playerEntity, true);
DestroyEntityLookup.SetComponentEnabled(gemEntity, true);
}
}
}
using Unity.Collections;
using UnityEngine;
using Unity.Entities;
using Unity.Physics;
using Unity.Physics.Systems;
using Unity.Transforms;
namespace TMG.Survivors
{
public struct PlasmaBlastData : IComponentData
{
public float MoveSpeed;
public int AttackDamage;
}
public struct PlasmaBlastExpirationTimer : IComponentData
{
public float Value;
}
public class PlasmaBlastAuthoring : MonoBehaviour
{
public float MoveSpeed;
public int AttackDamage;
public float DestroyAfterTime;
private class Baker : Baker<PlasmaBlastAuthoring>
{
public override void Bake(PlasmaBlastAuthoring authoring)
{
var entity = GetEntity(TransformUsageFlags.Dynamic);
AddComponent(entity, new PlasmaBlastData
{
MoveSpeed = authoring.MoveSpeed,
AttackDamage = authoring.AttackDamage
});
AddComponent(entity, new PlasmaBlastExpirationTimer
{
Value = authoring.DestroyAfterTime
});
AddComponent<DestroyEntityFlag>(entity);
SetComponentEnabled<DestroyEntityFlag>(entity, false);
}
}
}
public partial struct MovePlasmaBlastSystem : ISystem
{
public void OnUpdate(ref SystemState state)
{
var deltaTime = SystemAPI.Time.DeltaTime;
foreach (var (transform, data) in SystemAPI.Query<RefRW<LocalTransform>, PlasmaBlastData>())
{
transform.ValueRW.Position += transform.ValueRO.Right() * data.MoveSpeed * deltaTime;
}
// Destroy Plasma Blast After Time
foreach (var (timer, entity) in SystemAPI.Query<RefRW<PlasmaBlastExpirationTimer>>().WithPresent<DestroyEntityFlag>().WithEntityAccess())
{
timer.ValueRW.Value -= deltaTime;
if (timer.ValueRO.Value > 0) continue;
SystemAPI.SetComponentEnabled<DestroyEntityFlag>(entity, true);
}
}
}
[UpdateInGroup(typeof(PhysicsSystemGroup))]
[UpdateAfter(typeof(PhysicsSimulationGroup))]
[UpdateBefore(typeof(AfterPhysicsSystemGroup))]
public partial struct PlasmaBlastAttackSystem : ISystem
{
public void OnCreate(ref SystemState state)
{
state.RequireForUpdate<SimulationSingleton>();
}
public void OnUpdate(ref SystemState state)
{
var attackJob = new PlasmaBlastAttackJob
{
PlasmaBlastLookup = SystemAPI.GetComponentLookup<PlasmaBlastData>(true),
EnemyLookup = SystemAPI.GetComponentLookup<EnemyTag>(true),
DamageBufferLookup = SystemAPI.GetBufferLookup<DamageThisFrame>(),
DestroyEntityLookup = SystemAPI.GetComponentLookup<DestroyEntityFlag>()
};
var simulationSingleton = SystemAPI.GetSingleton<SimulationSingleton>();
state.Dependency = attackJob.Schedule(simulationSingleton, state.Dependency);
}
}
public struct PlasmaBlastAttackJob : ITriggerEventsJob
{
[ReadOnly] public ComponentLookup<PlasmaBlastData> PlasmaBlastLookup;
[ReadOnly] public ComponentLookup<EnemyTag> EnemyLookup;
public BufferLookup<DamageThisFrame> DamageBufferLookup;
public ComponentLookup<DestroyEntityFlag> DestroyEntityLookup;
public void Execute(TriggerEvent triggerEvent)
{
Entity plasmaBlastEntity;
Entity enemyEntity;
if (PlasmaBlastLookup.HasComponent(triggerEvent.EntityA) && EnemyLookup.HasComponent(triggerEvent.EntityB))
{
plasmaBlastEntity = triggerEvent.EntityA;
enemyEntity = triggerEvent.EntityB;
}
else if (PlasmaBlastLookup.HasComponent(triggerEvent.EntityB) && EnemyLookup.HasComponent(triggerEvent.EntityA))
{
plasmaBlastEntity = triggerEvent.EntityB;
enemyEntity = triggerEvent.EntityA;
}
else
{
return;
}
var attackDamage = PlasmaBlastLookup[plasmaBlastEntity].AttackDamage;
var enemyDamageBuffer = DamageBufferLookup[enemyEntity];
enemyDamageBuffer.Add(new DamageThisFrame { Value = attackDamage });
DestroyEntityLookup.SetComponentEnabled(plasmaBlastEntity, true);
}
}
}
using Unity.Collections;
using UnityEngine;
using Unity.Entities;
using Unity.Mathematics;
using Unity.Physics;
using Unity.Rendering;
using Unity.Transforms;
using UnityEngine.UI;
namespace TMG.Survivors
{
public struct PlayerTag : IComponentData {}
public struct CameraTarget : IComponentData
{
public UnityObjectRef<Transform> CameraTransform;
}
public struct InitializeCameraTargetTag : IComponentData {}
[MaterialProperty("_AnimationIndex")]
public struct AnimationIndexOverride : IComponentData
{
public float Value;
}
public enum PlayerAnimationIndex : byte
{
Movement = 0,
Idle = 1,
None = byte.MaxValue
}
public struct PlayerAttackData : IComponentData
{
public Entity AttackPrefab;
public float CooldownTime;
public float3 DetectionSize;
public CollisionFilter CollisionFilter;
}
public struct PlayerCooldownExpirationTimestamp : IComponentData
{
public double Value;
}
public struct GemsCollectedCount : IComponentData
{
public int Value;
}
public struct UpdateGemUIFlag : IComponentData, IEnableableComponent {}
public struct PlayerWorldUI : ICleanupComponentData
{
public UnityObjectRef<Transform> CanvasTransform;
public UnityObjectRef<Slider> HealthBarSlider;
}
public struct PlayerWorldUIPrefab : IComponentData
{
public UnityObjectRef<GameObject> Value;
}
public class PlayerAuthoring : MonoBehaviour
{
public GameObject AttackPrefab;
public float CooldownTime;
public float DetectionSize;
public GameObject WorldUIPrefab;
private class Baker : Baker<PlayerAuthoring>
{
public override void Bake(PlayerAuthoring authoring)
{
var entity = GetEntity(TransformUsageFlags.Dynamic);
AddComponent<PlayerTag>(entity);
AddComponent<InitializeCameraTargetTag>(entity);
AddComponent<CameraTarget>(entity);
AddComponent<AnimationIndexOverride>(entity);
var enemyLayer = LayerMask.NameToLayer("Enemy");
var enemyLayerMask = (uint)math.pow(2, enemyLayer);
var attackCollisionFilter = new CollisionFilter
{
BelongsTo = uint.MaxValue,
CollidesWith = enemyLayerMask
};
AddComponent(entity, new PlayerAttackData
{
AttackPrefab = GetEntity(authoring.AttackPrefab, TransformUsageFlags.Dynamic),
CooldownTime = authoring.CooldownTime,
DetectionSize = new float3(authoring.DetectionSize),
CollisionFilter = attackCollisionFilter
});
AddComponent<PlayerCooldownExpirationTimestamp>(entity);
AddComponent<GemsCollectedCount>(entity);
AddComponent<UpdateGemUIFlag>(entity);
AddComponent(entity, new PlayerWorldUIPrefab
{
Value = authoring.WorldUIPrefab
});
}
}
}
[UpdateInGroup(typeof(InitializationSystemGroup))]
public partial struct CameraInitializationSystem : ISystem
{
public void OnCreate(ref SystemState state)
{
state.RequireForUpdate<InitializeCameraTargetTag>();
}
public void OnUpdate(ref SystemState state)
{
if (CameraTargetSingleton.Instance == null) return;
var cameraTargetTransform = CameraTargetSingleton.Instance.transform;
var ecb = new EntityCommandBuffer(state.WorldUpdateAllocator);
foreach (var (cameraTarget, entity) in SystemAPI.Query<RefRW<CameraTarget>>().WithAll<InitializeCameraTargetTag, PlayerTag>().WithEntityAccess())
{
cameraTarget.ValueRW.CameraTransform = cameraTargetTransform;
ecb.RemoveComponent<InitializeCameraTargetTag>(entity);
}
ecb.Playback(state.EntityManager);
}
}
[UpdateAfter(typeof(TransformSystemGroup))]
public partial struct MoveCameraSystem : ISystem
{
public void OnUpdate(ref SystemState state)
{
foreach (var (transform, cameraTarget) in SystemAPI.Query<LocalToWorld, CameraTarget>().WithAll<PlayerTag>().WithNone<InitializeCameraTargetTag>())
{
cameraTarget.CameraTransform.Value.position = transform.Position;
}
}
}
public partial class PlayerInputSystem : SystemBase
{
private SurvivorsInput _input;
protected override void OnCreate()
{
_input = new SurvivorsInput();
_input.Enable();
}
protected override void OnUpdate()
{
var currentInput = (float2)_input.Player.Move.ReadValue<Vector2>();
foreach (var direction in SystemAPI.Query<RefRW<CharacterMoveDirection>>().WithAll<PlayerTag>())
{
direction.ValueRW.Value = currentInput;
}
}
}
public partial struct PlayerAttackSystem : ISystem
{
public void OnCreate(ref SystemState state)
{
state.RequireForUpdate<PhysicsWorldSingleton>();
state.RequireForUpdate<BeginInitializationEntityCommandBufferSystem.Singleton>();
}
public void OnUpdate(ref SystemState state)
{
var elapsedTime = SystemAPI.Time.ElapsedTime;
var ecbSystem = SystemAPI.GetSingleton<BeginInitializationEntityCommandBufferSystem.Singleton>();
var ecb = ecbSystem.CreateCommandBuffer(state.WorldUnmanaged);
var physicsWorldSingleton = SystemAPI.GetSingleton<PhysicsWorldSingleton>();
foreach (var (expirationTimestamp, attackData, transform) in SystemAPI.Query<RefRW<PlayerCooldownExpirationTimestamp>, PlayerAttackData, LocalTransform>())
{
if (expirationTimestamp.ValueRO.Value > elapsedTime) continue;
var spawnPosition = transform.Position;
var minDetectPosition = spawnPosition - attackData.DetectionSize;
var maxDetectPosition = spawnPosition + attackData.DetectionSize;
var aabbInput = new OverlapAabbInput
{
Aabb = new Aabb
{
Min = minDetectPosition,
Max = maxDetectPosition
},
Filter = attackData.CollisionFilter
};
var overlapHits = new NativeList<int>(state.WorldUpdateAllocator);
if (!physicsWorldSingleton.OverlapAabb(aabbInput, ref overlapHits))
{
continue;
}
var maxDistanceSq = float.MaxValue;
var closestEnemyPosition = float3.zero;
foreach (var overlapHit in overlapHits)
{
var curEnemyPosition = physicsWorldSingleton.Bodies[overlapHit].WorldFromBody.pos;
var distanceToPlayerSq = math.distancesq(spawnPosition.xy, curEnemyPosition.xy);
if (distanceToPlayerSq < maxDistanceSq)
{
maxDistanceSq = distanceToPlayerSq;
closestEnemyPosition = curEnemyPosition;
}
}
var vectorToClosestEnemy = closestEnemyPosition - spawnPosition;
var angleToClosestEnemy = math.atan2(vectorToClosestEnemy.y, vectorToClosestEnemy.x);
var spawnOrientation = quaternion.Euler(0f, 0f, angleToClosestEnemy);
var newAttack = ecb.Instantiate(attackData.AttackPrefab);
ecb.SetComponent(newAttack, LocalTransform.FromPositionRotation(spawnPosition, spawnOrientation));
expirationTimestamp.ValueRW.Value = elapsedTime + attackData.CooldownTime;
}
}
}
public partial struct UpdateGemUISystem : ISystem
{
public void OnUpdate(ref SystemState state)
{
foreach (var (gemCount, shouldUpdateUI) in SystemAPI.Query<GemsCollectedCount, EnabledRefRW<UpdateGemUIFlag>>())
{
GameUIController.Instance.UpdateGemsCollectedText(gemCount.Value);
shouldUpdateUI.ValueRW = false;
}
}
}
public partial struct PlayerWorldUISystem : ISystem
{
public void OnUpdate(ref SystemState state)
{
var ecb = new EntityCommandBuffer(state.WorldUpdateAllocator);
foreach (var (uiPrefab, entity) in SystemAPI.Query<PlayerWorldUIPrefab>().WithNone<PlayerWorldUI>().WithEntityAccess())
{
var newWorldUI = Object.Instantiate(uiPrefab.Value.Value);
ecb.AddComponent(entity, new PlayerWorldUI
{
CanvasTransform = newWorldUI.transform,
HealthBarSlider = newWorldUI.GetComponentInChildren<Slider>()
});
}
foreach (var (transform, worldUI, currentHitPoints, maxHitPoints) in SystemAPI.Query<LocalToWorld, PlayerWorldUI, CharacterCurrentHitPoints, CharacterMaxHitPoints>())
{
worldUI.CanvasTransform.Value.position = transform.Position;
var healthValue = (float)currentHitPoints.Value / maxHitPoints.Value;
worldUI.HealthBarSlider.Value.value = healthValue;
}
foreach (var (worldUI, entity) in SystemAPI.Query<PlayerWorldUI>().WithNone<LocalToWorld>().WithEntityAccess())
{
if (worldUI.CanvasTransform.Value != null)
{
Object.Destroy(worldUI.CanvasTransform.Value.gameObject);
}
ecb.RemoveComponent<PlayerWorldUI>(entity);
}
ecb.Playback(state.EntityManager);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment