1. Unity.Physics 추가
https://docs.unity3d.com/Packages/com.unity.physics@1.0/manual/index.html
Unity Physics overview | Unity Physics | 1.0.0-pre.15
Unity Physics overview The Unity Physics package, part of Unity's Data-Oriented Technology Stack (DOTS), provides a deterministic rigid body dynamics system and spatial query system. See the Unity Physics Samples for introductory material, including tutori
docs.unity3d.com
이번에는 Unity.Physics 패키지를 추가했습니다.
간단하게 추가하고 Rigidbody, Collider를 추가하는 것만으로도 쉽게 물리 동작을 해줄 수 있네요.
2. Job System 적용
https://docs.unity3d.com/Packages/com.unity.entities@1.0/manual/ecs-workflow-optimize-systems.html
Optimize the system for the spawner example | Entities | 1.0.0-pre.15
Optimize the system for the spawner example This task shows you how to modify a system so that it uses Burst-compatible jobs that run in parallel on multiple threads. Note Before you modify a system to run in parallel on multiple threads, consider whether
docs.unity3d.com
Unity.Physics 예제코드들을 보니 Job System을 기본으로 하고 있어서 이참에 기존 코드들도 Job System을 써볼까 합니다.
Entities예제 코드 중에 Job System으로 코드를 바꾸는 예제가 있어서 참고하였습니다.
public partial struct MovableSystem : ISystem, MainInputAction.IPlayerActions
{
public void OnUpdate(ref SystemState state)
{
if (moveDir == Vector2.zero) return;
new ProcessMovableJob
{
dir = new Vector3(moveDir.x, 0f, moveDir.y),
deltaTime = SystemAPI.Time.DeltaTime,
}.ScheduleParallel();
}
}
public partial struct ProcessMovableJob : IJobEntity
{
public Vector3 dir;
public float deltaTime;
private void Execute(ref LocalTransform transform, in MovableComponent movable)
{
transform.Rotation = Quaternion.Euler(0f, 90f - Mathf.Atan2(dir.z, dir.x) * Mathf.Rad2Deg, 0f);
transform = transform.Translate(dir * movable.moveSpeed * deltaTime);
}
}
public partial struct BulletSystem : ISystem
{
public void OnUpdate(ref SystemState state)
{
new ProcessBulletMoveJob()
{
deltaTime = SystemAPI.Time.DeltaTime,
}.ScheduleParallel();
}
}
public partial struct ProcessBulletMoveJob : IJobEntity
{
public float deltaTime;
private void Execute(ref LocalTransform transform, in BulletComponent bullet)
{
transform = transform.Translate(math.mul(transform.Rotation, Vector3.forward) * bullet.Speed * deltaTime);
}
}
간단하게 Transform을 변경하는 부분은 위 코드 처럼 간단하게 수정했습니다.
Execute함수의 인자를 기준으로 내부에서 쿼리해서 값을 보내주는 방식인 것 같네요.
public partial struct ShootableSystem : ISystem, MainInputAction.IPlayerActions
{
public void OnUpdate(ref SystemState state)
{
new ProcessShootbleReloadJob()
{
deltaTime = SystemAPI.Time.DeltaTime,
}.ScheduleParallel();
if (m_OnKeyFire == true)
{
m_OnKeyFire = false;
var ecbSingleton = SystemAPI.GetSingleton<BeginSimulationEntityCommandBufferSystem.Singleton>();
var ecb = ecbSingleton.CreateCommandBuffer(state.WorldUnmanaged);
new ProcessShootbleFireJob()
{
ecb = ecb.AsParallelWriter(),
elapsedTime = SystemAPI.Time.ElapsedTime,
}.ScheduleParallel();
}
}
}
public partial struct ProcessShootbleReloadJob : IJobEntity
{
public float deltaTime;
private void Execute(ref ShootableComponent shootable)
{
if (shootable.BulletCount >= shootable.BulletCountMax)
return;
if (shootable.BulletReloadTime < shootable.BulletReloadTimeMax)
{
shootable.BulletReloadTime += deltaTime;
}
else
{
shootable.BulletReloadTime -= shootable.BulletReloadTimeMax;
shootable.BulletCount++;
Debug.Log("On Reload " + shootable.BulletCount);
if (shootable.BulletCount >= shootable.BulletCountMax)
{
shootable.BulletReloadTime = 0f;
}
}
}
}
public partial struct ProcessShootbleFireJob : IJobEntity
{
public EntityCommandBuffer.ParallelWriter ecb;
public double elapsedTime;
private void Execute([ChunkIndexInQuery] int chunkIndex, in LocalTransform transform, ref ShootableComponent shootable)
{
if (shootable.BulletCount <= 0 || elapsedTime < shootable.BulletLastShotTime + shootable.BulletShotCooltime)
return;
shootable.BulletCount--;
shootable.BulletLastShotTime = (float)elapsedTime;
if (shootable.BulletPrefab == Entity.Null)
return;
var bulletEntity = ecb.Instantiate(chunkIndex, shootable.BulletPrefab);
var bulletTransform = new LocalTransform() { Position = transform.Position, Scale = 1f, Rotation = transform.Rotation };
bulletTransform.Position += math.mul(transform.Rotation, shootable.ShootOffset);
ecb.SetComponent(chunkIndex, bulletEntity, bulletTransform);
}
}
총알 관련 코드입니다.
ProcessShootbleFireJob는 예제를 많이 참고해서 만들었는데 이해되지 않는 부분에 대한 설명을 찾느라 조금 오래 걸렸네요.
저는 아래 블로그 보고 참고했습니다.
https://mrbinggrae.tistory.com/
게임 제작, 리뷰 등
유니티로 게임 제작 게임/제품 리뷰 등을 합니다
mrbinggrae.tistory.com
3. Trigger Event 예제 코드
https://docs.unity3d.com/Packages/com.unity.physics@1.0/manual/simulation-results.html
Simulation results | Unity Physics | 1.0.0-pre.15
Simulation results After completion of PhysicsSimulationGroup, the simulation step is complete. The results of the simulation are: New positions, rotations and velocities of dynamic bodies - stored inside a PhysicsWorld. Collision and trigger events - stor
docs.unity3d.com
이번에는 드디어 Unity.Physics 예제 코드를 적용해 보겠습니다.
제가 필요로 하는 Trigger Event 쪽만 진행해 봤습니다.
public void OnUpdate(ref SystemState state)
{
new ProcessBulletMoveJob()
{
deltaTime = SystemAPI.Time.DeltaTime,
}.ScheduleParallel();
NativeReference<int> numTriggerEvents = new NativeReference<int>(0, Allocator.TempJob);
new CountNumTriggerEvents
{
NumTriggerEvents = numTriggerEvents
}.Schedule(SystemAPI.GetSingleton<SimulationSingleton>(), state.Dependency);
}
예제 코드를 그대로 사용 시에 위처럼 에러가 나는데, 아래처럼 API가 바뀐 것으로 보입니다.
다행히 정상적으로 동작하는 것으로 보이네요.
4. Trigger Event로 공격 구현
public enum HitLayerType
{
Player = 1,
Enemy = 2,
}
public partial struct HitLayerComponent : IComponentData
{
public HitLayerType attackLayerMask;
public HitLayerType hitLayer;
}
Trigger Event 발생 시에 공격 가능한지 체크하기 위해 HitLayer를 추가했습니다.
public partial struct ProcessBulletTriggerEventJob : ITriggerEventsJob
{
public void Execute(TriggerEvent collisionEvent)
{
var entityManager = World.DefaultGameObjectInjectionWorld.EntityManager;
var layerA = entityManager.GetComponentData<HitLayerComponent>(collisionEvent.EntityA);
var layerB = entityManager.GetComponentData<HitLayerComponent>(collisionEvent.EntityB);
if (layerB.attackLayerMask.HasFlag(layerA.hitLayer) == true &&
entityManager.HasComponent<BulletComponent>(collisionEvent.EntityB) == true &&
entityManager.HasComponent<CharacterComponent>(collisionEvent.EntityA) == true)
{
var bullet = entityManager.GetComponentData<BulletComponent>(collisionEvent.EntityB);
var character = entityManager.GetComponentData<CharacterComponent>(collisionEvent.EntityA);
character.HP = math.max(character.HP - bullet.Damage, 0);
entityManager.SetComponentData(collisionEvent.EntityA, character);
entityManager.DestroyEntity(collisionEvent.EntityB);
}
}
}
InvalidOperationException: Managing jobs from within jobs is not allowed
위처럼 구현했는데 GetComponentData<BulletComponent> 라인에서 에러가 났습니다.
아마도 위에서 구현한 ProcessBulletMoveJob에서 물고 있어서 나오는 에러로 보입니다.
현제 제가 아는 지식선에서는 할 수 있는 방법이 없네요.
결국 Trigger를 제대로 사용할 수 없는 상황이라면 Unity.Physics를 사용할 이유가 없어서 패키지를 제거했습니다.
다음에는 Job System을 한번 건드려봤으니 Burst Compiler를 이해해 보고 써볼 예정입니다.
'Study Log' 카테고리의 다른 글
Unity ECS Study Log - 7 (0) | 2023.01.21 |
---|---|
Unity ECS Study Log - 6 (0) | 2023.01.15 |
Unity ECS Study Log - 4 (0) | 2023.01.09 |
Unity ECS Study Log - 3 (1) | 2023.01.07 |
Unity ECS Study Log - 2 (0) | 2023.01.01 |