ECS

์™œ

React-Three-Fiber ๋ฏธ๋‹ˆํ”Œ์  ํ•˜๋ฉฐ ๊ฒŒ์ž„๋กœ์ง & ๋ Œ๋” ํŒจํ„ด์— ๋Œ€ํ•ด ์ฐพ์•„๋ณด๋‹ค๊ฐ€ ECS๋ฅผ ๋ฐœ๊ฒฌํ–ˆ๋‹ค. ์ง๊ด€์ ์ด๊ณ  ์‹ค์šฉ์ ์ธ ํŒจํ„ด์ด๋ผ๊ณ  ์ƒ๊ฐ๋ผ์„œ ๊ธฐ๋ณธ์ ์ธ ๋‚ด์šฉ๋“ค์„ ์ •๋ฆฌํ•ด ๋ณธ๋‹ค.

๊ทธ๋™์•ˆ ๋‚ด๊ฐ€ ํ–ˆ๋˜ ๋ฐฉ๋ฒ•๋“ค

C++ / DirectX

3D / 2D ๊ฒŒ์ž„์˜ ํด๋ผ์ด์–ธํŠธ ๋ฐ ์„œ๋ฒ„๋ฅผ ๊ฐœ๋ฐœํ–ˆ๋˜ ์‹œ๊ธฐ์˜€๋‹ค. ์—”์ง„ ์ด๋ผ๊ณ  ๋ถ€๋ฅผ๋งŒํ•œ ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ ์—†์ด ๋ Œ๋” ๋ถ€๋ถ„๋งŒ ๋ž˜ํ•‘ํ•ด๋†“์€ ์œ ํ‹ธ์„ฑ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋งŒ ๊ฐ€์ง€๊ณ  ๊ฐœ๋ฐœ์„ ํ–ˆ๋‹ค.

์ƒ์†์„ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ฐœ์ฒด ๋‚ด์—์„œ Update, Render ๋ชจ๋‘ ์ง„ํ–‰ํ•˜๋Š” ๊ตฌ์กฐ๋กœ ๊ฐœ๋ฐœํ–ˆ๋‹ค.

Unity

Unity๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ GameObject๋ฅผ ์ƒ์„ฑ์„ ํ•˜๊ณ  ๊ธฐ๋Œ€ํ•˜๋Š” ๋™์ž‘๋“ค์„ ์Šคํฌ๋ฆฝํŠธ๋กœ ๋งŒ๋“ค์–ด ์˜ค๋ธŒ์ ํŠธ์— ํ• ๋‹นํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ ๋ฐฉ์‹์„ ๋”ฐ๋ฅธ๋‹ค. ์ž…์ถœ๋ ฅ, ์›€์ง์ž„, ๋ Œ๋”๋“ฑ์„ ์ปดํฌ๋„ŒํŠธํ™” ์‹œํ‚จ๋‹ค. ํ•˜๋‚˜์˜ ๊ฑฐ๋Œ€ํ•œ ํด๋ž˜์Šค๊ฐ€ ์กด์žฌํ•˜๋Š”๊ฒŒ ์•„๋‹Œ, ๋ถ„๋ฆฌ๋œ ์—ญํ• ์„ ๊ฐ€์ง„ ์ปดํฌ๋„ŒํŠธ๋“ค์˜ ์กฐํ•ฉ์œผ๋กœ ๋ณต์žกํ•œํ–‰๋™(behaviour)์„ ํ•˜๋Š” ๊ฐœ์ฒด๋ฅผ ๋งŒ๋“œ๋Š”๊ฒƒ. ์ด ๊ตฌ์กฐ ๋‚ด์—์„œ ๊ฐœ๋ฐœ์ž์˜ ์—ญ๋Ÿ‰๊ณผ ์ทจํ–ฅ์— ๋”ฐ๋ผ ๋‹ค์–‘ํ•œ ํŒจํ„ด์„ ์ ์šฉํ•˜๊ฒŒ ๋˜๋Š”๊ฒƒ์ด๋‹ค.

classDiagram
  class GameObject
  class Component
  class RigidBody
  class MonoBehaviour

  RigidBody --|> Component
  MonoBehaviour --|> Component
  GameObject --> Component: list

๊ฐ ์ปดํฌ๋„ŒํŠธ์—์„œ ๊ฐ์ž ํ•˜๊ณ ์‹ถ์€ ๋ชฉ์ ์„ ์ด๋ฃจ๋ฉด๋œ๋‹ค. ํ–‰๋™ ๋ฐ ๋ Œ๋”๋„ ์ปดํฌ๋„ŒํŠธ๋กœ ๊ตฌํ˜„๋˜๋ฉฐ ์ด ๊ณผ์ •์ค‘ ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ ํ˜น์€ GameObject๋ฅผ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ๋‹ค.


using UnityEngine;
using System.Collections;

public class ExampleClass : MonoBehaviour {
    public float thrust;
    public Rigidbody rb;
    void Start() {
        rb = GetComponent<Rigidbody>();
    }
    void FixedUpdate() {
        rb.AddForce(transform.forward * thrust);
    }
}

R3F(React Three Fiber)

React๋Š” ์„ ์–ธํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ, Three๋Š” ๊ทธ๋ž˜ํ”ฝ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๋‹ค. ๋‘๊ฐ€์ง€๋ฅผ ๊ฐ€์ง€๊ณ  ๊ฒŒ์ž„์„ ๋งŒ๋“ค๋•Œ ์–ด๋–ค์‹์œผ๋กœ ์‚ฌ์šฉํ•˜๊ฒŒ ๋ ๊นŒ.

<Canvas>
  <Scene>
    <Camera />
    <GameObjects>
      <Player name={"james"} weapons={weapons} />
      {otherPlayers}
      {enemies}
      <Fairy target={"james"} />
      <SomeEffect target={"james"} />
    </GameObjects>
  </Scene>
</Canvas>

Unity ์ฒ˜๋Ÿผ ์บ”๋ฐ”์Šค์—๋‹ค๊ฐ€ ์˜ค๋ธŒ์ ํŠธ๋“ค์„ ๋‚˜์—ดํ•ด ๋†“๊ฒŒ๋œ๋‹ค. ์ปดํฌ๋„ŒํŠธ๋“ค์€ ์ƒํ˜ธ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ๊ณ  ๋ถ€๋ชจ ์ž์‹์œผ๋กœ ๋†“์ผ ์ˆ˜ ์žˆ๋‹ค. Fairy๋Š” ์บ๋ฆญํ„ฐ๋ฅผ ๋”ฐ๋ผ๋‹ค๋‹ˆ๋Š” ์š”์ •์ด๋ฏ€๋กœ ์•„๋ž˜ ๋ฐฉ๋ฒ•๋“ค์ฒ˜๋Ÿผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ๋ณธ๋‹ค. SomeEffect ๋„ ๋น„์Šทํ•˜๋‹ค.

const Fairy = ({ target }) => {
  const { findObjectByName } = useObjects();
  const ref = useRef();

  useFrame(() => {
    const targetObject = findObjectByName(target);
    ref.current.position = targetObject.position;

    // do some actions
  });

  return <mesh ref={ref}>...</mesh>;
};

one way or another

๊ทธ๋ž˜์„œ, ์–ด๋–ค ๋ฐฉ๋ฒ•์œผ๋กœ ๊ฐœ๋ฐœ์„ ํ•˜๋‹ค๋ณด๋‹ˆ, ๊ฐ ๊ฐ์ฒด๋งˆ๋‹ค ํ‘œํ˜„ํ•˜๊ณ ์ž ํ•˜๋Š” ๋ฐ์ดํ„ฐ(์ƒํƒœ, ๋ชฉ์ ), ๊ทธ๋ฆฌ๊ณ  ํ–‰๋™์„ ์ง์ ‘ ์ˆ˜ํ–‰ํ•˜๊ฒŒ๋” ๋””์ž์ธํ–ˆ๋‹ค. 1) ํด๋ž˜์Šค์˜ ๊ด€๊ณ„๋ฅผ ์–ด๋–ป๊ฒŒ ์ •๋ฆฝํ• ๊ฒƒ์ธ์ง€ 2) ์–ด๋–ค ๋ฉ”์†Œ๋“œ๋ฅผ ๋…ธ์ถœ์‹œํ‚ฌ๊ฑด์ง€ - ์ฃผ๋กœ ์ด ๋‘๊ฐ€์ง€์— ๋Œ€ํ•ด์„œ ๊ณ ๋ฏผํ•˜๋ฉฐ ๋กœ์ง์„ ์™„์„ฑํ•ด๊ฐ”๋‹ค. ์ปดํฌ๋„ŒํŠธ์˜ ํ–‰์œ„๊ฐ€ ๋ณต์žกํ•ด์งˆ ์ˆ˜๋ก ์ฝ”๋“œ ๋ฉ์–ด๋ฆฌ๋Š” ๋น„๋Œ€ํ•ด์ง€๊ณ  ๊ฐ ๊ฐœ์ฒด๊ฐ„์˜ ์ปคํ”Œ๋ง์€ ์‹ฌํ™”๋œ๋‹ค. ์ค‘๋ณต์„ ์ œ๊ฑฐํ•˜๊ธฐ ์œ„ํ•ด ์ƒ์† ๋“ฑ์„ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜์ง€๋งŒ, ๊ฒฐ๊ตญ ์š”๊ตฌ์‚ฌํ•ญ์˜ ๋ณ€๊ฒฝ์ด๋‚˜ ๊ธฐ๋Šฅ์˜ ์ถ”๊ฐ€์— ๋ณ€๊ฒฝ๋˜๋Š” ์ฝ”๋“œ์˜ ์–‘์€ ๊ธฐํ•˜๊ธ‰์ˆ˜์ ์œผ๋กœ ๋Š˜์–ด๋‚œ๋‹ค. ๋˜ํ•œ ๋ณต์žกํ•œ ๋กœ์ง์„ ๊ฐ€์ง„ ๋ฉ”์†Œ๋“œ๋ฅผ ์—ฌ๋Ÿฌ ๊ฐ์ฒด์—์„œ ์ˆ˜ํ–‰ํ•˜๋‹ค๋ณด๋ฉด ์„ฑ๋Šฅ์€ ๋–จ์–ด์ง„๋‹ค.

์ƒ๊ฐํ•ด๋ณด๋‹ˆ ๊ฐœ๋ฐœ ๋ชฉ์ , ์ƒํ™ฉ, ๊ฐœ๋ฐœ์ž์˜ ์—ญ๋Ÿ‰๋“ฑ์— ๋‹ฌ๋ฆฐ ๋ฌธ์ œ์ผ ์ˆ˜๋„ ์žˆ๋‹ค. OOP์˜ ๋‹จ์ ์„ ์ฐพ์•„ ECS๋กœ ๋ฆฌํŒฉํ† ๋ง ํ•œ ๋ช‡๋ช‡ ์˜ˆ์ œ๋“ค์„ ์ฐพ์•„๋ณด์•˜๋Š”๋ฐ, ์ฒ˜์Œ์—” ์•„! ํ–ˆ๋‹ค๊ฐ€๋„ ์ด๋‚ด ์ด๊ฑด OOP์˜ ๋ฌธ์ œ๋ผ๊ธฐ๋ณด๋‹ค๋Š” ์„ค๊ณ„์˜ ์ฐจ์ด์ธ ๊ฒฝ์šฐ๋„ ์žˆ๊ณ .

ECS(Entity Component System)

ECS๋Š” ์ปดํฌ์ง€์…˜์„ ์ ๊ทน ํ™œ์šฉํ•˜๋Š” ํŒจ๋Ÿฌ๋‹ค์ž„์ด๋‹ค.

  • Entity: ์ฃผ์ฒด
  • Component: ์ƒํƒœ
  • System: ํ–‰์œ„

์ฃผ์ฒด์— ์ƒํƒœ๋ฅผ ๋ถ€์—ฌํ•˜๋ฉด, ์‹œ์Šคํ…œ(๋Œ€์ž์—ฐ)์ด ํ–‰์œ„๋ฅผ ์ง„ํ–‰ํ•œ๋‹ค.

The beautiful MDN logo.
ECS pattern(์ถœ์ฒ˜: ์œ ๋‹ˆํ‹ฐ ๋งค๋‰ด์–ผ)

Unity ์—์„œ์˜ classic vs ECS

  // classic component
  public class SomeActionComponent : MonoBehaviour {
    private float x;
    private float y;

    void Start() {
      x = 0;
      y = 0;
    }

    void Update() {
      x++;
      y++;
    }
  }
  // ecs ์˜ˆ์‹œ
  struct PositionComponent {
    float x;
    float y;
  }

  class MovePositionSystem : ComponentSystem {
    protected override void OnUpdate() {
      Entities.ForEach((ref PositionComponent position, in Data data) => {

        // update logics
        position.Value.x = data.x
        position.Value.y = data.y
      })
    }
  }

ECS ์˜ ์žฅ์ 

ECS๋Š” ์ˆ˜๋งŽ์€ ๊ฐœ์ฒด๋“ค์ด ์กด์žฌํ• ๋•Œ ์•„์ฃผ ๋น ๋ฅด๋‹ค. (์„ฑ๋Šฅ ๋น„๊ต ์˜์ƒ) ํ†ต์ƒ์ ์ธ OOP๊ธฐ๋ฒ•์œผ๋กœ ์ž‘์„ฑ๋œ ์ฝ”๋“œ๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค. (์ถœ์ฒ˜)

for each entity
   if entity can move
        move(entity)
   switch entity attacking type
       when shooter
         shoot(entity)
       when melee
         meleeAttack(entity)
       when nothing
         doNothing(entity)
   if entity has health
        checkIfDead(entity)

๊ฐœ์ฒด์˜ ๊ด€์ ์—์„œ ์กฐ๊ฑด์— ๋”ฐ๋ผ ํ–‰๋™์„ ์ˆ˜ํ–‰ํ•œ๋‹ค. ๋ฐ˜๋ฉด ecs๋ฅผ ๋ณด๋ฉด

for each entity which can move
    move(entity)
for each entity which can shoot
    shoot(entity)
for each entity which can meleeAttack
    meleeAttack(entity)
for each entity which has health
    checkIfDead(entity)

ํ•ด์•ผํ•  ์ผ๋“ค์„ ๋ชจ์•„์„œ ์‹œ์Šคํ…œ๋ณ„๋กœ ์ฒ˜๋ฆฌํ•  ๋ฟ์ด๋‹ค. ์–ด๋–ค ์—”ํ‹ฐํ‹ฐ์ธ๊ฐ€๋Š” ํฌ๊ฒŒ ์ค‘์š”ํ•˜์ง€ ์•Š๋‹ค. ์ •๋ฆฌํ•ด๋ณด๋ฉด

  1. ํ•„์š”ํ•œ ์ฝ”๋“œ๋งŒ ์ˆ˜ํ–‰
  2. Data Oriented Design ์˜ ์žฅ์  ์ ๊ทน ํ™œ์šฉ ๊ฐ€๋Šฅ, ์บ์‹œ ํžˆํŠธ์œจ ํ–ฅ์ƒ ๋“ฑ(DOD์˜ ์„ฑ๋Šฅ ์ฐธ๊ณ )
  3. ์ฝ”๋“œ ๊ฐ€๋…์„ฑ

์ฝ”๋“œ ๊ฐ€๋…์„ฑ์— ๋Œ€ํ•ด์„œ๋Š” ์—ฌ๋Ÿฌ ๊ด€์ ์ด ์žˆ์„ ์ˆ˜ ์žˆ๋‹ค๊ณ  ๋ณธ๋‹ค. ๊ทธ๋Ÿผ์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ  ๋ฐ์ดํ„ฐ๋กœ๋ถ€ํ„ฐ ์™„์ „ํžˆ ๋ถ„๋ฆฌ๋œ ํ–‰์œ„๋Š” ์ˆœ์ˆ˜ํ•จ์ˆ˜ ํ˜•ํƒœ๋กœ ์ž‘์„ฑ๋  ๊ฒƒ์ด๋ฉฐ ๊ฐœ๋ฐœ์ž๋Š” ํ˜„์žฌ ๊ตฌํ˜„ํ•˜๊ณ ์ž๋Š” ํ–‰์œ„์— ๋Œ€ํ•ด์„œ๋งŒ ์˜จ์ „ํžˆ ์ง‘์ค‘ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค๊ณ  ๋ณธ๋‹ค. ๋‹ค๋งŒ ์ด๋ฅผ ์œ„ํ•ด์„œ๋Š” SRP๋ฅผ ์ค€์ˆ˜ํ•ด์•ผํ•œ๋‹ค.(ํ•˜๋‚˜์˜ ์‹œ์Šคํ…œ์€ ํ•˜๋‚˜์˜ ์ด์œ ์— ๋Œ€ํ•ด์„œ๋งŒ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ณ€๊ฒฝํ•œ๋‹ค)

์ž๋ฌธ์ž๋‹ต

Q) ์–ด๋–ค ํ•จ์ˆ˜๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ ๋  ์ˆ˜ ์žˆ์„๊นŒ?
A) ๊ธฐ๋ณธ์ ์œผ๋กœ ์ปดํฌ๋„ŒํŠธ์—๋Š” lambda์‹์„ ํฌํ•จํ•œ ์–ด๋–ค ์—ฐ์‚ฐ๋„ ๋„ฃ์ง€ ์•Š๋Š”๊ฒŒ ์ข‹๋‹ค

Q) ECS ๋Š” Data Oriented Deisgn ์ธ์ง€?
A) ECS ๊ธฐ๋ฒ•์„ ์“ด๋‹ค๊ณ  ํ•ด์„œ DOD๊ฐ€ ๋˜๋Š”๊ฒƒ์ด ์•„๋‹Œ, DOD๋ฅผ ํ™œ์šฉํ•˜๊ธฐ ์ข‹์€ ํ™˜๊ฒฝ์ด ๋งŒ๋“ค์–ด์ง„๋‹ค.

ECS for Web

  • ECS ํŒจํ„ด์„ ์›น ๊ฐœ๋ฐœ์— ์ ์šฉํ•œ๋‹ค๋ฉด? - ์ž‘์„ฑ์ค‘

๊ด€๋ จ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ

์ฐธ๊ณ ๋งํฌ

https://www.sebaslab.com/the-quest-for-maintainable-code-and-the-path-to-ecs/

https://www.sebaslab.com/code-design-articles/

https://www.youtube.com/watch?v=ILfUuBLfzGI

Leave a comment