<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Component Archives - 어제와 내일의 나 그 사이의 이야기</title>
	<atom:link href="https://lycos7560.com/tag/component/feed/" rel="self" type="application/rss+xml" />
	<link></link>
	<description>생각의 흐름을 타고 다니며 만드는 블로그</description>
	<lastBuildDate>Wed, 19 Feb 2025 04:52:44 +0000</lastBuildDate>
	<language>ko-KR</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>

<image>
	<url>https://lycos7560.com/wp-content/uploads/2022/11/cropped-cropped-cropped-log-1-150x150-1-80x80.png</url>
	<title>Component Archives - 어제와 내일의 나 그 사이의 이야기</title>
	<link></link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>ECS의 World와 SubScene의 관계</title>
		<link>https://lycos7560.com/unity/ecs%ec%9d%98-world%ec%99%80-subscene%ec%9d%98-%ea%b4%80%ea%b3%84/39668/</link>
					<comments>https://lycos7560.com/unity/ecs%ec%9d%98-world%ec%99%80-subscene%ec%9d%98-%ea%b4%80%ea%b3%84/39668/#respond</comments>
		
		<dc:creator><![CDATA[lycos7560]]></dc:creator>
		<pubDate>Wed, 19 Feb 2025 04:52:42 +0000</pubDate>
				<category><![CDATA[Unity]]></category>
		<category><![CDATA[Archetype]]></category>
		<category><![CDATA[Archetypes]]></category>
		<category><![CDATA[architecture]]></category>
		<category><![CDATA[Component]]></category>
		<category><![CDATA[ComponentBased]]></category>
		<category><![CDATA[ComponentData]]></category>
		<category><![CDATA[Container]]></category>
		<category><![CDATA[DataContainer]]></category>
		<category><![CDATA[DataManagement]]></category>
		<category><![CDATA[DataOriented]]></category>
		<category><![CDATA[DataStructure]]></category>
		<category><![CDATA[DOTS]]></category>
		<category><![CDATA[ECS]]></category>
		<category><![CDATA[Entity]]></category>
		<category><![CDATA[EntityManager]]></category>
		<category><![CDATA[EntityQuery]]></category>
		<category><![CDATA[EntityState]]></category>
		<category><![CDATA[EntitySystem]]></category>
		<category><![CDATA[GameEngine]]></category>
		<category><![CDATA[GameObject]]></category>
		<category><![CDATA[Query]]></category>
		<category><![CDATA[QuerySystem]]></category>
		<category><![CDATA[Scene]]></category>
		<category><![CDATA[SceneManagement]]></category>
		<category><![CDATA[SceneSystem]]></category>
		<category><![CDATA[StateManagement]]></category>
		<category><![CDATA[study]]></category>
		<category><![CDATA[SubScene]]></category>
		<category><![CDATA[System]]></category>
		<category><![CDATA[SystemBase]]></category>
		<category><![CDATA[World]]></category>
		<category><![CDATA[WorldState]]></category>
		<category><![CDATA[WorldSystem]]></category>
		<category><![CDATA[공부]]></category>
		<guid isPermaLink="false">https://lycos7560.com/?p=39668</guid>

					<description><![CDATA[<p>⭐ 개념 정리 ✅ World ECS에서 World는 Entity와 System 등을 관리하는 Container 역할 World는 특정 씬(Scene)에 종속되지 않고, 모든 ECS 데이터를 포함하여 관리한다. World가 관리하는 핵심 구성 요소 4가지 ✅ SubScene SubScene은 씬(Scene) 내부에서 특정 ECS 데이터(Entity)를 포함하는 컨테이너 역할을 한다. SubScene이 로드되면, 그 안의 GameObject들이 자동으로 Entity로 변환(Entity Conversion)되어 World에 등록 ➡️ SubScene의 로드 [&#8230;]</p>
<p>The post <a href="https://lycos7560.com/unity/ecs%ec%9d%98-world%ec%99%80-subscene%ec%9d%98-%ea%b4%80%ea%b3%84/39668/">ECS의 World와 SubScene의 관계</a> appeared first on <a href="https://lycos7560.com">어제와 내일의 나 그 사이의 이야기</a>.</p>
]]></description>
										<content:encoded><![CDATA[				<div class="wp-block-uagb-table-of-contents uagb-toc__align-left uagb-toc__columns-1  uagb-block-4370eae9      "
					data-scroll= "1"
					data-offset= "30"
					style=""
				>
				<div class="uagb-toc__wrap">
						<div class="uagb-toc__title">
							ECS의 World와 SubScene의 관계						</div>
																						<div class="uagb-toc__list-wrap ">
						<ol class="uagb-toc__list"><li class="uagb-toc__list"><a href="#개념-정리" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 개념 정리</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#world" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> World</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#subscene" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> SubScene</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#subscene의-로드-과정" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/27a1.png" alt="➡" class="wp-smiley" style="height: 1em; max-height: 1em;" /> SubScene의 로드 과정</a></li></ul><li class="uagb-toc__list"><a href="#world와-subscene의-관계" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> World와 SubScene의 관계</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#world는-subscene을-포함하는-상위-컨테이너인가" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2753.png" alt="❓" class="wp-smiley" style="height: 1em; max-height: 1em;" /> World는 SubScene을 포함하는 상위 컨테이너인가?</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#서로-다른-world에서-같은-subscene을-공유할-수-있을까" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2753.png" alt="❓" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 서로 다른 World에서 같은 SubScene을 공유할 수 있을까?</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#서로-다른-world에서-같은-subscene을-공유하는-방법" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/27a1.png" alt="➡" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 서로 다른 World에서 같은 SubScene을 공유하는 방법</a></li></ul><li class="uagb-toc__list"><a href="#subscene-내의-entity들이-서로-다른-world에-포함될-수-있을까" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2753.png" alt="❓" class="wp-smiley" style="height: 1em; max-height: 1em;" />SubScene 내의 Entity들이 서로 다른 World에 포함될 수 있을까?</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#일반적인-subscene의-world-포함-방식" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/27a1.png" alt="➡" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 일반적인 SubScene의 World 포함 방식</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#특정-entity만-다른-world에-포함시키는-방법" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/27a1.png" alt="➡" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 특정 Entity만 다른 World에 포함시키는 방법</a></ul></ul></ol>					</div>
									</div>
				</div>
			


<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 개념 정리</h2>



<h3 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> World</h3>



<p><code><strong>ECS</strong></code>에서<code> <strong>World</strong></code>는 <code><strong>Entity</strong></code>와 <code><strong>System</strong></code> 등을 관리하는 Container 역할</p>



<p><code><strong>World</strong></code>는 <strong>특정 씬(Scene)에 종속되지 않고</strong>, 모든 ECS 데이터를 포함하여 관리한다.</p>



<p><code><strong>World</strong></code>가 관리하는 핵심 구성 요소 4가지</p>



<ul class="wp-block-list">
<li><strong><code>EntityManager</code></strong> → <code><strong>Entity</strong></code>와 <strong><code>Component</code></strong>관리</li>



<li><strong><code>Systems</code></strong> → ECS 시스템 실행 및 업데이트</li>



<li><strong><code>Archetypes</code></strong> → <code><strong>Entity</strong></code> 구조 저장 및 관리</li>



<li><strong><code>Queries</code></strong> → 특정 조건을 만족하는 <code><strong>Entity</strong></code> 검색</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity is-style-wide" style="margin-top:var(--wp--preset--spacing--80);margin-bottom:var(--wp--preset--spacing--80)"/>



<h3 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> SubScene</h3>



<p><code><strong>SubScene</strong></code>은 씬(Scene) 내부에서 특정 ECS 데이터(<code><strong>Entity</strong></code>)를 포함하는 컨테이너 역할을 한다.</p>



<p><code><strong>SubScene</strong></code>이 로드되면, 그 안의 GameObject들이 자동으로 <code><strong>Entity</strong></code>로 변환(Entity Conversion)되어 <code><code><strong>World</strong></code></code>에 등록</p>



<h4 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/27a1.png" alt="➡" class="wp-smiley" style="height: 1em; max-height: 1em;" /> SubScene의 로드 과정</h4>



<ol class="wp-block-list">
<li> <strong><code><strong>SubScene</strong></code></strong> <strong>로드</strong> → <code><strong>SceneSystem</strong></code>이 <code><strong>SubScene</strong></code>을 <code><code><strong>World</strong></code></code>에 추가</li>



<li>GameObject <strong>→ <code><strong>Entity</strong></code> </strong>변환</li>



<li>EntityManager가 모든 <strong><code>Entity</code> </strong>관리</li>



<li>ECS 시스템이 해당 <strong><code>Entity</code> </strong>를 처리</li>
</ol>



<hr class="wp-block-separator has-alpha-channel-opacity is-style-wide" style="margin-top:var(--wp--preset--spacing--80);margin-bottom:var(--wp--preset--spacing--80)"/>



<h3 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> World와 SubScene의 관계</h3>



<p><code><code><strong>World</strong></code></code>는 <strong><code><strong>MainScene</strong></code></strong>과 <strong><code><strong>SubScene</strong></code></strong>을 구분하지 않고, 모든 ECS 데이터를 포함하여 관리</p>



<p><strong><code><strong>SubScene</strong></code></strong>이 로드되면 해당 오브젝트들이 ECS <strong><code>Entity</code></strong>로 변환되어 <code><code><code><code><code><strong>World</strong></code></code></code></code></code>에 등록됨</p>



<p><strong><code><code>World</code></code> </strong>자체는 <strong>씬(Scene)</strong>을 직접적으로 관리하지 않지만, <strong><code><code>World</code></code></strong>가 관리하는 <code><strong>SceneSystem</strong></code>을 사용해 <strong><code><strong>SubScene</strong></code></strong>을 동적으로 로드/언로드할 수는 있음</p>



<p><strong>즉, <code><code>World</code></code> 는 씬(Scene) 개념과 독립적이지만, <code>SubScene</code>과는 밀접한 관계를 가짐</strong></p>



<hr class="wp-block-separator has-alpha-channel-opacity is-style-wide" style="margin-top:var(--wp--preset--spacing--80);margin-bottom:var(--wp--preset--spacing--80)"/>



<h3 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2753.png" alt="❓" class="wp-smiley" style="height: 1em; max-height: 1em;" /> World는 SubScene을 포함하는 상위 컨테이너인가?</h3>



<p>Yes, 하지만 직접적인 포함 관계는 아님</p>



<p><strong><code><code>World</code></code></strong>는 <strong>ECS 데이터를 관리하는 컨테이너</strong>이므로, <strong><code><strong>SubScene</strong></code></strong>에서 생성된 ECS 데이터(<code><strong>Entity</strong></code>, <code><strong>Component</strong></code>, <code><strong>System </strong></code>등)는 <strong>결국 <strong><code><code>World</code></code></strong>에 포함됨</strong>.</p>



<p>하지만, <strong>Unity의 <strong><code><strong>SubScene</strong></code></strong>자체가 World의 일부는 아니다</strong>. (<strong><code><strong>SubScene</strong></code></strong>은 <strong><strong>씬(Scene)</strong>의 일부</strong>, World는 <strong>ECS의 컨테이너</strong>)</p>



<hr class="wp-block-separator has-alpha-channel-opacity is-style-wide" style="margin-top:var(--wp--preset--spacing--80);margin-bottom:var(--wp--preset--spacing--80)"/>



<h3 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2753.png" alt="❓" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 서로 다른 World에서 같은 SubScene을 공유할 수 있을까?</h3>



<p>일반적으로 Unity ECS에서는 <strong><strong><strong><code>SubScene</code></strong></strong>은 하나의 <strong><code><strong>World</strong></code></strong>에 속하는 것이 원칙</strong></p>



<p>특정한 방식으로 <strong>다른 <code><strong>World</strong></code>에서 동일한 <strong><code>SubScene</code></strong>을 공유하는 것</strong>도 가능할 수 있음</p>



<h4 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/27a1.png" alt="➡" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 서로 다른 World에서 같은 SubScene을 공유하는 방법</h4>



<h5 class="wp-block-heading">1. 각 World에서 같은 SubScene을 개별적으로 로드</h5>



<ul class="wp-block-list">
<li>각 <code><strong>World</strong></code>가 동일한 <strong><code>SubScene</code></strong>을 독립적으로 로드하여 각각의 ECS 데이터를 가질 수 있음</li>



<li>서로 다른 <code><strong>World</strong></code>에 있지만, 동일한 형태의 ECS 데이터가 생성</li>
</ul>



<h5 class="wp-block-heading">2. Persistent Scene을 활용해 한 World에서 로드한 데이터를 다른 World가 참조</h5>



<ul class="wp-block-list">
<li>한 <code><strong>World</strong></code>에서 <strong><code>SubScene</code></strong>을 로드하고, 다른 <code><strong>World</strong></code>는 해당 데이터를 복사하거나 참조하도록 설계</li>



<li>예를 들어, 멀티플레이어 게임에서 서버 <code><strong>World</strong></code>와 클라이언트 <code><strong>World</strong></code>가 동일한 맵 데이터(<strong><code>SubScene</code></strong>)를 참조하는 방식</li>



<li>하지만, Unity ECS가 기본적으로 제공하는 기능은 아니므로 별도의 데이터 동기화 로직이 필요함.</li>
</ul>



<h5 class="wp-block-heading">3. Bake 시점에 SubScene의 ECS 데이터를 여러 World로 복사</h5>



<ul class="wp-block-list">
<li><strong><code>SubScene</code></strong>이 변환될 때(<code><strong>Bake</strong></code>), ECS 데이터를 특정 World가 아니라 <strong>여러 <code><strong>World</strong></code>에 분산해서 배치하는 방식</strong>도 가능</li>



<li><code><strong>EntityManager.CopyEntitiesFrom()</strong></code> 등을 사용하여 <strong><strong><code>SubScene</code></strong>의 데이터를 한 <strong><code><code>World</code></code></strong>에서 다른 <strong><code><code>World</code></code></strong>로 복사 가능</strong>.</li>



<li>Unity ECS가 기본적으로 지원하는 기능이 아니므로, 수동으로 구현해야 할 수도 있음.</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity is-style-wide" style="margin-top:var(--wp--preset--spacing--80);margin-bottom:var(--wp--preset--spacing--80)"/>



<h3 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2753.png" alt="❓" class="wp-smiley" style="height: 1em; max-height: 1em;" />SubScene 내의 Entity들이 서로 다른 World에 포함될 수 있을까?</h3>



<p>Yes, 하지만 Unity ECS에서 기본적으로 지원하는 방식은 아님</p>



<p>특정한 방법을 사용하면 일부 <code><strong>Entity</strong></code>를 다른 <code><strong>World</strong></code>로 이동시키는 것은 가능함</p>



<h4 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/27a1.png" alt="➡" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 일반적인 SubScene의 World 포함 방식</h4>



<p>기본적으로 <strong><code>SubScene</code></strong>이 로드되면, 그 안의 모든 GameObject가 동일한 <code><strong>World</strong></code>로 변환(<strong><code>Bake</code></strong>)됨</p>



<p>즉, <strong>SubScene </strong>내의 모든 <code><strong>Entity</strong></code>들은 한 개의 <code><strong>World</strong></code>에 속하는 것이 원칙</p>



<p><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4cc.png" alt="📌" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 예제: 일반적인 <strong><code>SubScene</code></strong> 로드 과정</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">SceneSystem.LoadSceneAsync(world, subSceneEntity, loadParameters);</pre>



<p>특정 <strong><code><strong>World</strong></code></strong>에서 <strong><code>SubScene</code></strong>을 로드함 → <strong><code>SubScene</code></strong> 내부의 <code><strong>Entity</strong></code>들이 해당 <code><strong>World</strong></code>에 포함됨</p>



<p>결과적으로 <strong><code>SubScene</code></strong>의 모든 <code><strong>Entity</strong></code>들은 동일한 <code><strong>World</strong></code>에 속하게 됨</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/27a1.png" alt="➡" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 특정 Entity만 다른 World에 포함시키는 방법</h4>



<p>하지만, 특정한 방식으로 <strong><code>SubScene</code></strong> 내부의 <code><strong>Entity</strong></code>를 다른 World로 이동시키는 것도 가능</p>



<h5 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f539.png" alt="🔹" class="wp-smiley" style="height: 1em; max-height: 1em;" />방법 1: 특정 Entity를 다른 World로 복사하기</h5>



<p><strong><code>SubScene</code></strong>이 로드된 후, 특정 <code><strong>Entity</strong></code>를 <strong>다른 <code><strong>World</strong></code>로 복사</strong>하는 방식</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">// 현재 World에서 Entity 가져오기
EntityManager entityManager = sourceWorld.EntityManager;
EntityQuery query = entityManager.CreateEntityQuery(ComponentType.ReadOnly&lt;MyComponent>());

// 대상 World로 Entity 복사
EntityManager destinationManager = targetWorld.EntityManager;
NativeArray&lt;Entity> entities = query.ToEntityArray(Allocator.Temp);
foreach (var entity in entities)
{
    Entity newEntity = destinationManager.Instantiate(entity);
}
entities.Dispose();
</pre>



<ul class="wp-block-list">
<li>원본 <code><strong>Entity</strong></code>를 삭제하지 않으면 동일한 <code><strong>Entity</strong></code>가 두 개의 <strong><code><strong>World</strong></code></strong>에 존재할 수도 있음! → 원본을 삭제할지 여부를 잘 결정해야 함</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h5 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f539.png" alt="🔹" class="wp-smiley" style="height: 1em; max-height: 1em;" />방법 2: SubScene을 특정 World에서 로드한 후 일부 Entity만 다른 World에서 활용</h5>



<ul class="wp-block-list">
<li><strong><code>SubScene</code></strong>을 <strong><code>World A</code>에서 로드</strong></li>



<li>특정 <code><strong>Entity</strong></code>를 <code><strong>World</strong></code><strong><code> B</code></strong>에서 참조할 수 있도록 설계</li>



<li><strong><code>EntityCommandBuffer</code> </strong>또는 <code><strong>CopyEntitiesFrom</strong></code>을 활용하여 데이터 이동 가능</li>
</ul>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">targetWorld.EntityManager.CopyEntitiesFrom(sourceWorld.EntityManager, query);
</pre>



<ul class="wp-block-list">
<li>하지만, 시스템(System)과 동작 방식이 달라질 수 있으므로 주의해야 함</li>
</ul>



<p></p>
<p>The post <a href="https://lycos7560.com/unity/ecs%ec%9d%98-world%ec%99%80-subscene%ec%9d%98-%ea%b4%80%ea%b3%84/39668/">ECS의 World와 SubScene의 관계</a> appeared first on <a href="https://lycos7560.com">어제와 내일의 나 그 사이의 이야기</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://lycos7560.com/unity/ecs%ec%9d%98-world%ec%99%80-subscene%ec%9d%98-%ea%b4%80%ea%b3%84/39668/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>ECS &#8211; EntityQuery 정리</title>
		<link>https://lycos7560.com/unity/ecs-entityquery-%ec%a0%95%eb%a6%ac/39563/</link>
					<comments>https://lycos7560.com/unity/ecs-entityquery-%ec%a0%95%eb%a6%ac/39563/#respond</comments>
		
		<dc:creator><![CDATA[lycos7560]]></dc:creator>
		<pubDate>Sun, 09 Feb 2025 10:22:42 +0000</pubDate>
				<category><![CDATA[Unity]]></category>
		<category><![CDATA[AsNativeArray]]></category>
		<category><![CDATA[AsNativeArray()]]></category>
		<category><![CDATA[BufferTypeHandle]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[Chunk components]]></category>
		<category><![CDATA[Cleanup components]]></category>
		<category><![CDATA[Component]]></category>
		<category><![CDATA[concept]]></category>
		<category><![CDATA[Data]]></category>
		<category><![CDATA[DOTS]]></category>
		<category><![CDATA[ECS]]></category>
		<category><![CDATA[Entity Component System]]></category>
		<category><![CDATA[EntityQuery]]></category>
		<category><![CDATA[IJobChunk]]></category>
		<category><![CDATA[job]]></category>
		<category><![CDATA[Job 시스템]]></category>
		<category><![CDATA[Managed Components]]></category>
		<category><![CDATA[OnUpdate]]></category>
		<category><![CDATA[OnUpdate()]]></category>
		<category><![CDATA[Physics]]></category>
		<category><![CDATA[Physics Scene Basic]]></category>
		<category><![CDATA[Sample]]></category>
		<category><![CDATA[ScheduleParallel]]></category>
		<category><![CDATA[Shared Components]]></category>
		<category><![CDATA[study]]></category>
		<category><![CDATA[SystemAPI]]></category>
		<category><![CDATA[Tag components]]></category>
		<category><![CDATA[ToEntityArray()]]></category>
		<category><![CDATA[Unity Physics 101]]></category>
		<category><![CDATA[Unmanaged]]></category>
		<guid isPermaLink="false">https://lycos7560.com/?p=39563</guid>

					<description><![CDATA[<p>EntityQuery 정리 ECS(Entity Component System)에서 특정 조건을 만족하는 Entity들을 효율적으로 검색하는 도구 Entity가 가진 컴포넌트 조합을 기준으로 검색하며, 이를 통해 메모리 캐시 효율을 극대화 1. EntityQuery의 기본 개념 &#8211; 특정 컴포넌트를 가진 Entity 찾기 2. EntityQuery의 주요 조건 조건 설명 WithAll&#60;T&#62;() 해당 컴포넌트를 모두 가진 Entity만 검색 WithAny&#60;T&#62;() 지정한 컴포넌트 중 하나라도 가진 Entity 검색 [&#8230;]</p>
<p>The post <a href="https://lycos7560.com/unity/ecs-entityquery-%ec%a0%95%eb%a6%ac/39563/">ECS &#8211; EntityQuery 정리</a> appeared first on <a href="https://lycos7560.com">어제와 내일의 나 그 사이의 이야기</a>.</p>
]]></description>
										<content:encoded><![CDATA[				<div class="wp-block-uagb-table-of-contents uagb-toc__align-left uagb-toc__columns-1  uagb-block-0a6626f3      "
					data-scroll= "1"
					data-offset= "30"
					style=""
				>
				<div class="uagb-toc__wrap">
						<div class="uagb-toc__title">
							EntityQuery						</div>
																						<div class="uagb-toc__list-wrap ">
						<ol class="uagb-toc__list"><li class="uagb-toc__list"><a href="#entityquery-정리" class="uagb-toc-link__trigger">EntityQuery 정리</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#1-entityquery의-기본-개념" class="uagb-toc-link__trigger">1. EntityQuery의 기본 개념</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#특정-컴포넌트를-가진-entity-찾기" class="uagb-toc-link__trigger">&#8211; 특정 컴포넌트를 가진 Entity 찾기</a></li></ul><li class="uagb-toc__list"><a href="#2-entityquery의-주요-조건" class="uagb-toc-link__trigger">2. EntityQuery의 주요 조건</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#특정-조건의-엔티티-찾기" class="uagb-toc-link__trigger">&#8211; 특정 조건의 엔티티 찾기</a></li></ul><li class="uagb-toc__list"><a href="#3-entityquery를-사용한-데이터-접근-방법" class="uagb-toc-link__trigger">3. EntityQuery를 사용한 데이터 접근 방법</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#1-systemapiquery-사용" class="uagb-toc-link__trigger">(1) SystemAPI.Query() 사용</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#2-toentityarray로-entity-리스트-가져오기" class="uagb-toc-link__trigger">(2) ToEntityArray()로 Entity 리스트 가져오기</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#3-tocomponentdataarray로-특정-컴포넌트-리스트-가져오기" class="uagb-toc-link__trigger">(3) ToComponentDataArray()로 특정 컴포넌트 리스트 가져오기</a></li></ul><li class="uagb-toc__list"><a href="#4-ijobchunk과-함께-사용하기" class="uagb-toc-link__trigger">4. IJobChunk과 함께 사용하기</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#q-componenttypehandle을-사용하여-nativearray를-생성하는-것과-미리-nativearray를-넘겨주는-것의-차이점" class="uagb-toc-link__trigger">(Q) ComponentTypeHandle을 사용하여 NativeArray를 생성하는 것과 미리 NativeArray를 넘겨주는 것의 차이점</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#1-componenttypehandlet을-사용하는-이유" class="uagb-toc-link__trigger">(1) ComponentTypeHandle을 사용하는 이유</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#2-componenttypehandlet과-nativearrayt-차이점-비교" class="uagb-toc-link__trigger">(2) ComponentTypeHandle과 NativeArray 차이점 비교</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#3-componenttypehandlet-방식" class="uagb-toc-link__trigger">(3) ComponentTypeHandle 방식</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#4-nativearray-job-방식" class="uagb-toc-link__trigger">(4) NativeArray  Job 방식</a></li></ul></li></ul><li class="uagb-toc__list"><a href="#5-entityquery의-추가-기능" class="uagb-toc-link__trigger">5. EntityQuery의 추가 기능</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#1-calculateentitycount로-개수-확인" class="uagb-toc-link__trigger">(1) CalculateEntityCount()로 개수 확인</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#2-setchangedversionfilter로-변경된-컴포넌트만-검색" class="uagb-toc-link__trigger">(2) SetChangedVersionFilter()로 변경된 컴포넌트만 검색</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#3-enabledisable로-쿼리-활성화비활성화" class="uagb-toc-link__trigger">(3) Enable/Disable로 쿼리 활성화/비활성화</a></ul></ul></ol>					</div>
									</div>
				</div>
			


<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading">EntityQuery 정리</h2>



<p>ECS(Entity Component System)에서 <strong>특정 조건을 만족하는 Entity들을 효율적으로 검색</strong>하는 도구</p>



<p>Entity가 가진 <strong>컴포넌트 조합을 기준</strong>으로 검색하며, 이를 통해 <strong>메모리 캐시 효율을 극대화</strong></p>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">1. <strong>EntityQuery의 기본 개념</strong></h3>



<ul class="wp-block-list">
<li><strong>Entity 필터링</strong>: 특정한 <strong>컴포넌트를 포함하거나 제외하는 기준</strong>을 정할 수 있다.</li>



<li><strong>Chunk 단위 검색</strong>: <code>EntityQuery</code>는 Entity 개별 조회가 아닌 Chunk 단위(Archetype Chunk)로 데이터를 가져옴</li>



<li><strong>즉시 검색 or <code>Job</code> 시스템과 병렬 처리</strong> 가능</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading">&#8211; 특정 컴포넌트를 가진 Entity 찾기</h4>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">public partial struct FindEntitiesSystem : ISystem
{
    public void OnUpdate(ref SystemState state)
    {
        // PositionComponent와 VelocityComponent를 가진 Entity 찾기
        EntityQuery query = SystemAPI.QueryBuilder()
            .WithAll&lt;PositionComponent, VelocityComponent>()
            .Build();

        foreach (var (position, velocity) in SystemAPI.Query&lt;RefRW&lt;PositionComponent>, RefRO&lt;VelocityComponent>>())
        {
            position.ValueRW.Value += velocity.ValueRO.Value * SystemAPI.Time.DeltaTime;
        }
    }
}
</pre>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<ul class="wp-block-list">
<li>WithAll() → 모든 엔티티 중 PositionComponent와 VelocityComponent를 가진 것만 검색</li>



<li>RefRW → 읽기/쓰기 가능</li>



<li>RefRO → 읽기 전용</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity is-style-wide" style="margin-top:var(--wp--preset--spacing--80);margin-bottom:var(--wp--preset--spacing--80)"/>



<h3 class="wp-block-heading">2. EntityQuery의 주요 조건</h3>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>조건</th><th>설명</th></tr></thead><tbody><tr><td><code>WithAll&lt;T&gt;()</code></td><td>해당 <strong>컴포넌트를 모두 가진 Entity만 검색</strong></td></tr><tr><td><code>WithAny&lt;T&gt;()</code></td><td>지정한 <strong>컴포넌트 중 하나라도 가진 <strong>Entity</strong> 검색</strong></td></tr><tr><td><code>WithNone&lt;T&gt;()</code></td><td>특정한 <strong>컴포넌트를 가지지 않은 <strong>Entity</strong> 검색</strong></td></tr><tr><td><code>WithDisabled&lt;T&gt;()</code></td><td><strong>비활성화된 컴포넌트</strong>가 있는 Entity 검색</td></tr><tr><td><code>WithAbsent&lt;T&gt;()</code></td><td>특정한 <strong>컴포넌트가 없는 <strong>Entity</strong> 검색</strong></td></tr></tbody></table></figure>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading">&#8211; <strong>특정 조건의 엔티티 찾기</strong></h4>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">EntityQuery query = SystemAPI.QueryBuilder()
    .WithAll&lt;PositionComponent>()   // PositionComponent가 있어야 함
    .WithAny&lt;VelocityComponent, AccelerationComponent>()  // 둘 중 하나만 있으면 됨
    .WithNone&lt;DestroyedTag>()        // DestroyedTag가 있으면 제외
    .Build();
</pre>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<ul class="wp-block-list">
<li>WithAll() → 반드시 포함</li>



<li>WithAny() → 하나라도 포함</li>



<li>WithNone() → 포함하면 안 됨</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity is-style-wide" style="margin-top:var(--wp--preset--spacing--80);margin-bottom:var(--wp--preset--spacing--80)"/>



<h3 class="wp-block-heading">3. EntityQuery를 사용한 데이터 접근 방법</h3>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading">(1) <strong>SystemAPI.Query() 사용</strong></h4>



<ul class="wp-block-list">
<li><code>EntityQuery</code>를 생성하고 <code>foreach</code> 루프에서 엔티티 데이터를 순회 가능</li>
</ul>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">foreach (var (position, velocity) in SystemAPI.Query&lt;RefRW&lt;PositionComponent>, RefRO&lt;VelocityComponent>>())
{
    position.ValueRW.Value += velocity.ValueRO.Value * SystemAPI.Time.DeltaTime;
}
</pre>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading">(2) <strong>ToEntityArray()로 Entity 리스트 가져오기</strong></h4>



<ul class="wp-block-list">
<li><code>ToEntityArray()</code>를 사용하면 <strong>해당 <code>EntityQuery</code>에 해당하는 Entity 리스트를 가져옴</strong></li>
</ul>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">var entities = query.ToEntityArray(Allocator.Temp);
foreach (var entity in entities)
{
    Debug.Log($"찾은 Entity: {entity}");
}
entities.Dispose();
</pre>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<ul class="wp-block-list">
<li><code>ToEntityArray(Allocator.Temp)</code> → <strong>NativeArray로 Entity를 가져옴</strong></li>



<li><code>Dispose()</code> 필수! <strong>메모리 해제 안 하면 메모리 누수 발생</strong></li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading">(3) <strong>ToComponentDataArray()로 특정 컴포넌트 리스트 가져오기</strong></h4>



<ul class="wp-block-list">
<li>특정 컴포넌트의 값만 가져올 수도 있음.</li>
</ul>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">var positions = query.ToComponentDataArray&lt;PositionComponent>(Allocator.Temp);
foreach (var position in positions)
{
    Debug.Log($"Position: {position.Value}");
}
positions.Dispose();
</pre>



<ul class="wp-block-list">
<li><strong>데이터만 필요한 경우 성능 최적화 가능</strong></li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity is-style-wide" style="margin-top:var(--wp--preset--spacing--80);margin-bottom:var(--wp--preset--spacing--80)"/>



<h3 class="wp-block-heading">4. <strong>IJobChunk과 함께 사용하기</strong></h3>



<ul class="wp-block-list">
<li><strong><code>IJobChunk</code>를 사용하면 <code>EntityQuery</code>를 멀티스레드로 병렬 처리 가능</strong></li>



<li> <code>ArchetypeChunk</code>을 사용하여 <strong>Chunk 단위 데이터 접근</strong> 가능</li>
</ul>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">[BurstCompile]
public struct MoveJob : IJobChunk
{
    public ComponentTypeHandle&lt;PositionComponent> PositionHandle;
    [ReadOnly] public ComponentTypeHandle&lt;VelocityComponent> VelocityHandle;
    public float DeltaTime;

    public void Execute(ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex)
    {
        var positions = chunk.GetNativeArray(PositionHandle);
        var velocities = chunk.GetNativeArray(VelocityHandle);

        for (int i = 0; i &lt; chunk.Count; i++)
        {
            positions[i] = new PositionComponent
            {
                Value = positions[i].Value + velocities[i].Value * DeltaTime
            };
        }
    }
}

public partial struct MoveSystem : ISystem
{
    public void OnUpdate(ref SystemState state)
    {
        var query = SystemAPI.QueryBuilder()
            .WithAll&lt;PositionComponent, VelocityComponent>()
            .Build();

        var positionHandle = SystemAPI.GetComponentTypeHandle&lt;PositionComponent>();
        var velocityHandle = SystemAPI.GetComponentTypeHandle&lt;VelocityComponent>(isReadOnly: true);

        var job = new MoveJob
        {
            PositionHandle = positionHandle,
            VelocityHandle = velocityHandle,
            DeltaTime = SystemAPI.Time.DeltaTime
        };
        // query를 기반으로 Schedule()을 호출
        state.Dependency = job.Schedule(query, state.Dependency);
    }
}
</pre>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<ul class="wp-block-list">
<li><code>GetComponentTypeHandle&lt;T>()</code> 사용 → <strong>컴포넌트 데이터를 가져올 때 캐시 최적화</strong><br><code>GetComponentTypeHandle&lt;T>()</code>은 <strong>ECS(Worlds)의 청크 단위 데이터 접근을 최적화</strong>하기 위한 API</li>



<li><code>IJobChunk</code>으로 <strong>멀티스레드 병렬 처리 가능</strong></li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p><strong><code>job.Schedule(query, state.Dependency)</code>의 실행 과정</strong></p>



<p><strong><code>query</code>를 사용하여 <code>IJobChunk</code>가 처리할 청크들을 찾음</strong></p>



<ul class="wp-block-list">
<li><code>query</code>는 <code>PositionComponent</code>와 <code>VelocityComponent</code>를 포함하는 엔티티들의 <strong>청크 목록</strong>을 가져옴.</li>



<li><code>IJobChunk</code>는 이 청크 목록을 기반으로 실행됨.</li>
</ul>



<p> <strong><code>job.Schedule()</code>을 호출하여 <code>MoveJob</code>을 실행</strong></p>



<ul class="wp-block-list">
<li><code>query</code>에서 반환된 청크를 <code>MoveJob</code>에서 병렬적으로 처리.</li>



<li>내부적으로 <strong>각 청크별로 <code>MoveJob.Execute()</code>가 실행됨</strong>.</li>
</ul>



<p><strong><code>state.Dependency</code>를 업데이트하여 잡 실행을 관리</strong></p>



<ul class="wp-block-list">
<li><code>state.Dependency</code>는 현재 시스템의 <strong>의존성 체인</strong>을 관리.</li>



<li>이전 프레임에서 실행된 다른 잡들과의 <strong>의존 관계를 고려하여 동기화</strong>함.</li>



<li>병렬 처리를 안전하게 실행하기 위해 필요함.</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>매개변수</th><th>타입</th><th>설명</th></tr></thead><tbody><tr><td><code>chunk</code></td><td><code>ArchetypeChunk</code></td><td>현재 처리 중인 <strong>Chunk</strong> 객체. <br>해당 <strong>Chunk</strong> 내의 Entity 및 컴포넌트에 접근할 수 있음.</td></tr><tr><td><code>chunkIndex</code></td><td><code>int</code></td><td>현재 <strong><strong>Chunk</strong> 의 인덱스</strong> (0부터 시작). <br>병렬 작업을 할 때 <strong>Chunk</strong> 가 몇 번째인지 확인 가능.</td></tr><tr><td><code>firstEntityIndex</code></td><td><code>int</code></td><td>현재 <strong>Chunk</strong> 에서 <strong>첫 번째 Entity 의 전역 인덱스</strong>.<br>(전체 Entity  중 몇 번째인지 나타냄)</td></tr></tbody></table></figure>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading">(Q) ComponentTypeHandle을 사용하여 NativeArray를 생성하는 것과 미리 NativeArray를 넘겨주는 것의 차이점</h4>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h5 class="wp-block-heading">(1) <code>ComponentTypeHandle&lt;T></code>을 사용하는 이유</h5>



<p><code>GetComponentTypeHandle&lt;T>()</code>을 사용하여 <code>IJobChunk</code> 내부에서 <code>NativeArray</code>를 가져오는 이유는 <strong>Chunk 단위 접근 최적화</strong> 때문이다.</p>



<ul class="wp-block-list">
<li><code>ComponentTypeHandle&lt;T></code>을 사용하면 <strong>각 Chunk에 맞는 <code>NativeArray</code>를 개별적으로 가져올 수 있음</strong></li>



<li>만약 <code>NativeArray</code>를 미리 만들어서 넘겨준다면, <strong>Chunk별로 나눠진 데이터가 하나의 배열에 병합되어야 하므로 메모리 효율이 떨어지고 캐시 활용도가 낮아짐</strong></li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h5 class="wp-block-heading">(2) <code>ComponentTypeHandle&lt;T></code>과 <code>NativeArray&lt;T></code> 차이점 비교</h5>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>방식</th><th>설명</th><th>장점</th><th>단점</th></tr></thead><tbody><tr><td><strong><code>ComponentTypeHandle&lt;T></code> 사용 </strong><br><strong>(<code>IJobChunk</code> 내부에서 <code>GetNativeArray()</code>)</strong></td><td><strong>각 청크에서 해당 컴포넌트의 데이터 배열을 가져옴</strong></td><td><strong>&#8211; 청크별로 최적화된 데이터 접근 가능</strong><br><strong>&#8211; 캐시 친화적이며 성능 최적화 가능</strong><br><strong>&#8211; Burst 및 멀티스레딩에 적합</strong></td><td><strong>&#8211; <code>IJobChunk</code>을 사용해야 함</strong></td></tr><tr><td><strong><code>NativeArray&lt;T&gt;</code>를 미리 만들어 Job에 넘김</strong></td><td><strong>전체 데이터를 하나의 <code>NativeArray</code>로 만들고 Job에서 사용</strong></td><td><strong>&#8211; 간단한 구조, 직관적</strong><br><strong>&#8211; 작은 데이터셋에서는 부담이 적음</strong></td><td><strong>&#8211; 전체 데이터를 하나의 배열로 만들기 때문에 캐시 미스 발생 가능성 증가</strong><br><strong>&#8211; Chunk별 최적화가 불가능하여 성능 저하 가능성 있음</strong></td></tr></tbody></table></figure>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h5 class="wp-block-heading">(3) <code>ComponentTypeHandle&lt;T></code> 방식</h5>



<p>1> <code>SystemAPI.GetComponentTypeHandle&lt;T>()</code> 실행</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">var positionHandle = SystemAPI.GetComponentTypeHandle&lt;PositionComponent>();
var velocityHandle = SystemAPI.GetComponentTypeHandle&lt;VelocityComponent>(isReadOnly: true);
</pre>



<ul class="wp-block-list">
<li><code>ComponentTypeHandle&lt;T></code>을 가져옴</li>



<li>Chunk별로 데이터가 분산되어 있기 때문에 <code>NativeArray&lt;T></code>로 직접 변환하지 않음</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p>2> <code>MoveJob.Schedule()</code> 실행</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">var job = new MoveJob
{
    PositionHandle = positionHandle,
    VelocityHandle = velocityHandle,
    DeltaTime = SystemAPI.Time.DeltaTime
};

state.Dependency = job.Schedule(query, state.Dependency);
</pre>



<ul class="wp-block-list">
<li><code>MoveJob</code>을 스케줄할 때 <code>ComponentTypeHandle&lt;T></code>을 넘김</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p>3> <code>MoveJob.Execute()</code> 내부에서 <code>GetNativeArray()</code> 실행</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">var positions = chunk.GetNativeArray(PositionHandle);
var velocities = chunk.GetNativeArray(VelocityHandle);
</pre>



<ul class="wp-block-list">
<li>각 Chunk에서 <code>NativeArray&lt;T></code>를 가져와서 사용함.</li>



<li>이렇게 하면 각 Chunk가 독립적으로 자신의 데이터에 접근할 수 있으며, 캐시 최적화 및 멀티스레딩 효율 증가</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<h5 class="wp-block-heading">(4) <code>NativeArray</code>  Job 방식</h5>



<p>만약 <code>NativeArray&lt;T></code>를 미리 만들어서 Job에 넘기면, <strong>멀티스레딩 최적화가 어렵고 성능 저하 가능성 증가</strong>.</p>



<p><strong>전체 데이터를 하나의 <code>NativeArray</code>로 병합</strong>하는 과정이 필요, <strong>Chunk별 데이터 레이아웃이 깨질 수 있음</strong> → CPU 캐시 효율 저하.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">NativeArray&lt;PositionComponent> positions = new NativeArray&lt;PositionComponent>(entityCount, Allocator.TempJob);
NativeArray&lt;VelocityComponent> velocities = new NativeArray&lt;VelocityComponent>(entityCount, Allocator.TempJob);

// Job에 NativeArray를 직접 넘김
var job = new MoveJob
{
    Positions = positions,
    Velocities = velocities,
    DeltaTime = SystemAPI.Time.DeltaTime
};

state.Dependency = job.Schedule(state.Dependency);
</pre>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p>이 경우, <strong><code>positions</code>와 <code>velocities</code>를 Chunk별로 나누지 않고 전체 데이터를 하나의 배열로 만듦.</strong></p>



<p>Chunk별 데이터가 섞일 가능성이 높아져 캐시 미스(Cache Miss)가 증가할 수 있음</p>



<hr class="wp-block-separator has-alpha-channel-opacity is-style-wide" style="margin-top:var(--wp--preset--spacing--80);margin-bottom:var(--wp--preset--spacing--80)"/>



<h3 class="wp-block-heading">5. <strong>EntityQuery의 추가 기능</strong></h3>



<h4 class="wp-block-heading">(1) CalculateEntityCount()로 개수 확인</h4>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">int entityCount = query.CalculateEntityCount();
Debug.Log($"현재 쿼리 결과 개수: {entityCount}");
</pre>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading">(2) SetChangedVersionFilter()로 변경된 컴포넌트만 검색</h4>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">query.SetChangedVersionFilter&lt;PositionComponent>();
</pre>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p>최근 변경된 <code>PositionComponent</code>를 가진 엔티티만 검색</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading">(3) Enable/Disable로 쿼리 활성화/비활성화</h4>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">query.SetEnabled(false);  // 쿼리 비활성화
query.SetEnabled(true);   // 다시 활성화
</pre>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p>쿼리를 특정 조건에서만 사용하고 싶을 때 활용</p>
<p>The post <a href="https://lycos7560.com/unity/ecs-entityquery-%ec%a0%95%eb%a6%ac/39563/">ECS &#8211; EntityQuery 정리</a> appeared first on <a href="https://lycos7560.com">어제와 내일의 나 그 사이의 이야기</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://lycos7560.com/unity/ecs-entityquery-%ec%a0%95%eb%a6%ac/39563/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>ECS &#8211; MouseHoverAuthoring</title>
		<link>https://lycos7560.com/unity/ecs-mousehoverauthoring/39539/</link>
					<comments>https://lycos7560.com/unity/ecs-mousehoverauthoring/39539/#respond</comments>
		
		<dc:creator><![CDATA[lycos7560]]></dc:creator>
		<pubDate>Wed, 05 Feb 2025 10:02:15 +0000</pubDate>
				<category><![CDATA[Unity]]></category>
		<category><![CDATA[AsNativeArray]]></category>
		<category><![CDATA[AsNativeArray()]]></category>
		<category><![CDATA[BufferTypeHandle]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[Chunk components]]></category>
		<category><![CDATA[Cleanup components]]></category>
		<category><![CDATA[Component]]></category>
		<category><![CDATA[concept]]></category>
		<category><![CDATA[Data]]></category>
		<category><![CDATA[DOTS]]></category>
		<category><![CDATA[ECS]]></category>
		<category><![CDATA[Entity Component System]]></category>
		<category><![CDATA[IJobChunk]]></category>
		<category><![CDATA[job]]></category>
		<category><![CDATA[Job 시스템]]></category>
		<category><![CDATA[Managed Components]]></category>
		<category><![CDATA[OnUpdate]]></category>
		<category><![CDATA[OnUpdate()]]></category>
		<category><![CDATA[Physics]]></category>
		<category><![CDATA[Physics Scene Basic]]></category>
		<category><![CDATA[Sample]]></category>
		<category><![CDATA[ScheduleParallel]]></category>
		<category><![CDATA[Shared Components]]></category>
		<category><![CDATA[study]]></category>
		<category><![CDATA[Tag components]]></category>
		<category><![CDATA[Unity Physics 101]]></category>
		<category><![CDATA[Unmanaged]]></category>
		<guid isPermaLink="false">https://lycos7560.com/?p=39539</guid>

					<description><![CDATA[<p>1. MouseHover (IComponentData) 2. MouseHoverBaker (Baker) 3. MouseHoverSystem (SystemBase) 1) InitializationSystemGroup 초기화 단계에서 실행됨 2) WorldRaycastJob (IJob) 마우스 위치에서 Raycast를 실행하는 IJob 3) OnUpdate() 1> CollisionWorld 가져오기 2> 마우스 위치에서 레이(Ray) 생성 3> RaycastInput 설정 RaycastInput은 Unity Physics에서 사용되는 구조체로, 물리 엔진이 사용할 레이캐스트 정보를 포함 4> MouseHover 컴포넌트 가져오기 5> 레이캐스트 실행 NativeReference&#60;RaycastHit>을 사용하여 [&#8230;]</p>
<p>The post <a href="https://lycos7560.com/unity/ecs-mousehoverauthoring/39539/">ECS &#8211; MouseHoverAuthoring</a> appeared first on <a href="https://lycos7560.com">어제와 내일의 나 그 사이의 이야기</a>.</p>
]]></description>
										<content:encoded><![CDATA[				<div class="wp-block-uagb-table-of-contents uagb-toc__align-left uagb-toc__columns-1  uagb-block-767988eb      "
					data-scroll= "1"
					data-offset= "30"
					style=""
				>
				<div class="uagb-toc__wrap">
						<div class="uagb-toc__title">
							ECS &#8211; MouseHoverAuthoring						</div>
																						<div class="uagb-toc__list-wrap ">
						<ol class="uagb-toc__list"><li class="uagb-toc__list"><a href="#1-mousehover-icomponentdata" class="uagb-toc-link__trigger">1. MouseHover (IComponentData)</a><li class="uagb-toc__list"><a href="#2-mousehoverbaker-baker" class="uagb-toc-link__trigger">2. MouseHoverBaker (Baker)</a><li class="uagb-toc__list"><a href="#3-mousehoversystem-systembase" class="uagb-toc-link__trigger">3. MouseHoverSystem (SystemBase)</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#1-initializationsystemgroup" class="uagb-toc-link__trigger">1) InitializationSystemGroup</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#2-worldraycastjob-ijob" class="uagb-toc-link__trigger">2) WorldRaycastJob (IJob)</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#3-onupdate" class="uagb-toc-link__trigger">3) OnUpdate()</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#1-collisionworld-가져오기" class="uagb-toc-link__trigger">1&gt; CollisionWorld 가져오기</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#2-마우스-위치에서-레이ray-생성" class="uagb-toc-link__trigger">2&gt; 마우스 위치에서 레이(Ray) 생성</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#3-raycastinput-설정" class="uagb-toc-link__trigger">3&gt; RaycastInput 설정</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#4-mousehover-컴포넌트-가져오기" class="uagb-toc-link__trigger">4&gt; MouseHover 컴포넌트 가져오기</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#5-레이캐스트-실행" class="uagb-toc-link__trigger">5&gt; 레이캐스트 실행</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#5-물리-entity에서-그래픽-entity-찾기" class="uagb-toc-link__trigger">5&gt; 물리 Entity에서 그래픽 Entity 찾기</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#6-동일한-엔터티를-계속-가리키면-무시" class="uagb-toc-link__trigger">6&gt; 동일한 엔터티를 계속 가리키면 무시</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#7-이전-및-현재-마우스-오버-entity-업데이트" class="uagb-toc-link__trigger">7&gt; 이전 및 현재 마우스 오버 Entity 업데이트</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#8-이전-entity의-원래-상태-복구" class="uagb-toc-link__trigger">8&gt; 이전 Entity의 원래 상태 복구</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#9-새로운-entity의-마우스-hover-효과-적용" class="uagb-toc-link__trigger">9&gt; 새로운 Entity의 마우스 Hover 효과 적용</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#10-마우스-hover-머티리얼-적용" class="uagb-toc-link__trigger">10&gt; 마우스 Hover 머티리얼 적용</a></ul></ul></ol>					</div>
									</div>
				</div>
			


<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>



<figure class="wp-block-video"><video height="1228" style="aspect-ratio: 1952 / 1228;" width="1952" controls src="https://lycos7560.com/wp-content/uploads/2025/02/녹화_2025_02_05_19_00_37_49.mp4"></video><figcaption class="wp-element-caption">Physics Scene Basic &#8211; Query</figcaption></figure>



<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">using System;
using Unity.Burst;
using Unity.Collections;
using Unity.Entities;
using Unity.Jobs;
using Unity.Mathematics;
using Unity.Physics.GraphicsIntegration;
using Unity.Rendering;
using Unity.Transforms;
using UnityEngine;
using static Unity.Physics.Extensions.PhysicsSamplesExtensions;

namespace Unity.Physics.Extensions
{
    public class MouseHover : IComponentData
    {
        public bool IgnoreTriggers;
        public bool IgnoreStatic;
        public Entity PreviousEntity;
        public Entity CurrentEntity;
        public Entity HoverEntity;
        public MaterialMeshInfo OriginalMeshInfo;
        public RenderMeshArray OriginalRenderMeshes;
    }

    [DisallowMultipleComponent]
    public class MouseHoverAuthoring : MonoBehaviour
    {
        public GameObject HoverPrefab;
        public bool IgnoreTriggers = true;
        public bool IgnoreStatic = true;

        // Note: override OnEnable to be able to disable the component in the editor
        protected void OnEnable() {}
    }

    class MouseHoverBaker : Baker&lt;MouseHoverAuthoring>
    {
        public override void Bake(MouseHoverAuthoring authoring)
        {
            var entity = GetEntity(TransformUsageFlags.None);
            AddComponentObject(entity, new MouseHover()
            {
                PreviousEntity = Entity.Null,
                CurrentEntity = Entity.Null,
                IgnoreTriggers = authoring.IgnoreTriggers,
                IgnoreStatic = authoring.IgnoreStatic,
                HoverEntity = GetEntity(authoring.HoverPrefab, TransformUsageFlags.Dynamic),
            });
        }
    }

    // Applies any mouse spring as a change in velocity on the entity's motion component
    // Limitations: works only if the physics objects in the scene come from the same subscene as MouseHoverAuthoring
    // Will be fixable if there is a Unity.Rendering API that lets you get the UnityEngine.Mesh that an entity is using for renderin.

    [UpdateInGroup(typeof(InitializationSystemGroup))]
    public partial class MouseHoverSystem : SystemBase
    {
        [BurstCompile]
        public struct WorldRaycastJob : IJob
        {
            public RaycastInput RayInput;
            [ReadOnly] public CollisionWorld CollisionWorld;
            [ReadOnly] public bool IgnoreTriggers;
            [ReadOnly] public bool IgnoreStatic;

            public NativeReference&lt;RaycastHit> RaycastHitRef;

            public void Execute()
            {
                var mousePickCollector = new MousePickCollector(CollisionWorld.NumDynamicBodies)
                {
                    IgnoreTriggers = IgnoreTriggers,
                    IgnoreStatic = IgnoreStatic
                };

                if (CollisionWorld.CastRay(RayInput, ref mousePickCollector))
                {
                    RaycastHitRef.Value = mousePickCollector.Hit;
                }
            }
        }

        protected override void OnCreate()
        {
            base.OnCreate();
            RequireForUpdate&lt;MouseHover>();
        }

        // Find the Entity holding the Graphical representation of a Physics Shape.
        // It may be that the Physics and Graphics representation are on the same Entity.
        public Entity FindGraphicsEntityFromPhysics(Entity bodyEntity) => FindGraphicsEntityFromPhysics(bodyEntity, ColliderKey.Empty);
        public Entity FindGraphicsEntityFromPhysics(Entity bodyEntity, ColliderKey leafColliderKey)
        {
            if (bodyEntity.Equals(Entity.Null))
            {
                // No Physics so no Graphics
                return Entity.Null;
            }

            // Set the Graphics Entity to the supplied Physics Entity
            var renderEntity = bodyEntity;

            // Check if we have hit a leaf node
            if (!leafColliderKey.Equals(ColliderKey.Empty))
            {
                // Get the Physics Collider
                var rootCollider = EntityManager.GetComponentData&lt;PhysicsCollider>(bodyEntity).Value;

                // If we hit a CompoundCollider we need to find the original Entity associated
                // the actual leaf Collider that was hit.
                if (rootCollider.Value.Type == ColliderType.Compound)
                {
                    #region Find a Leaf Entity and ColliderKey
                    var leafEntity = Entity.Null;
                    unsafe
                    {
                        var rootColliderPtr = rootCollider.AsPtr();

                        // Get the leaf Collider and check if we hit was a PolygonCollider (i.e. a Triangle or a Quad)
                        rootColliderPtr->GetLeaf(leafColliderKey, out var childCollider);
                        leafEntity = childCollider.Entity;

                        // PolygonColliders are likely to not have an original Entity associated with them
                        // So if we have a Polygon and it has no Entity then we really need to check for
                        // the higher level Mesh or Terrain Collider instead.
                        var childColliderType = childCollider.Collider->Type;
                        var childColliderIsPolygon = childColliderType == ColliderType.Triangle || childColliderType == ColliderType.Quad;
                        if (childColliderIsPolygon &amp;&amp; childCollider.Entity.Equals(Entity.Null))
                        {
                            // Get the ColliderKey of the Polygon's parent
                            if (TryGetParentColliderKey(rootColliderPtr, leafColliderKey, out leafColliderKey))
                            {
                                // Get the Mesh or Terrain Collider of the Polygon
                                TryGetChildInHierarchy(rootColliderPtr, leafColliderKey, out childCollider);
                                leafEntity = childCollider.Entity;
                            }
                        }
                    }
                    #endregion

                    // The Entities recorded in the leaves of a CompoundCollider may have been correct
                    // at the time of conversion. However, if the Collider blob is shared, or came up
                    // through a sub scene, we cannot assume that the baked Entities in the
                    // CompoundCollider are still valid.

                    // On conversion Entities using a CompoundCollider have an extra dynamic buffer added
                    // which holds a list of Entity/ColliderKey pairs. This buffer should be patched up
                    // automatically and be valid with each instance, at least until you start messing
                    // with the Entity hierarchy yourself e.g. by deleting Entities.

                    #region Check the Leaf Entity is valid
                    // If the leafEntity was never assigned in the first place
                    // there is no point in looking up any Buffers.
                    if (!leafEntity.Equals(Entity.Null))
                    {
                        // Check for an Key/Entity pair buffer first.
                        // This should exist if the Physics conversion pipeline was invoked.
                        var colliderKeyEntityPairBuffers = GetBufferLookup&lt;PhysicsColliderKeyEntityPair>(true);
                        if (colliderKeyEntityPairBuffers.HasBuffer(bodyEntity))
                        {
                            var colliderKeyEntityBuffer = colliderKeyEntityPairBuffers[bodyEntity];
                            for (int i = 0; i &lt; colliderKeyEntityBuffer.Length; i++)
                            {
                                var bufferColliderKey = colliderKeyEntityBuffer[i].Key;
                                if (leafColliderKey.Equals(bufferColliderKey))
                                {
                                    renderEntity = colliderKeyEntityBuffer[i].Entity;
                                    break;
                                }
                            }
                        }
                        else
                        {
                            // We haven't found a Key/Entity pair buffer so the compound collider
                            // may have been created in code.

                            // We'll assume the Entity in the CompoundCollider is valid
                            renderEntity = leafEntity;

                            // If this CompoundCollider was instanced from a prefab then the entities
                            // in the compound children would actually reference the original prefab hierarchy.
                            var rootEntityFromLeaf = leafEntity;
                            while (SystemAPI.HasComponent&lt;Parent>(rootEntityFromLeaf))
                            {
                                rootEntityFromLeaf = SystemAPI.GetComponent&lt;Parent>(rootEntityFromLeaf).Value;
                            }

                            // If the root Entity found from the leaf does not match the body Entity
                            // then we have hit an instance using the same CompoundCollider.
                            // This means we can try and remap the leaf Entity to the new hierarchy.
                            if (!rootEntityFromLeaf.Equals(bodyEntity))
                            {
                                // This assumes there is a LinkedEntityGroup Buffer on original and instance Entity.
                                // No doubt there is a more optimal way of doing this remap with more specific
                                // knowledge of the final application.
                                var linkedEntityGroupBuffers = GetBufferLookup&lt;LinkedEntityGroup>(true);

                                // Only remap if the buffers exist, have been created and are of equal length.
                                bool hasBufferRootEntity = linkedEntityGroupBuffers.HasBuffer(rootEntityFromLeaf);
                                bool hasBufferBodyEntity = linkedEntityGroupBuffers.HasBuffer(bodyEntity);
                                if (hasBufferRootEntity &amp;&amp; hasBufferBodyEntity)
                                {
                                    var prefabEntityGroupBuffer = linkedEntityGroupBuffers[rootEntityFromLeaf];
                                    var instanceEntityGroupBuffer = linkedEntityGroupBuffers[bodyEntity];

                                    if (prefabEntityGroupBuffer.IsCreated &amp;&amp; instanceEntityGroupBuffer.IsCreated
                                        &amp;&amp; (prefabEntityGroupBuffer.Length == instanceEntityGroupBuffer.Length))
                                    {
                                        var prefabEntityGroup = prefabEntityGroupBuffer.AsNativeArray();
                                        var instanceEntityGroup = instanceEntityGroupBuffer.AsNativeArray();

                                        for (int i = 0; i &lt; prefabEntityGroup.Length; i++)
                                        {
                                            // If we've found the renderEntity index in the prefab hierarchy,
                                            // set the renderEntity to the equivalent Entity in the instance
                                            if (prefabEntityGroup[i].Value.Equals(renderEntity))
                                            {
                                                renderEntity = instanceEntityGroup[i].Value;
                                                break;
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                    #endregion
                }
            }

            // Finally check to see if we have a graphics redirection on the shape Entity.
            if (SystemAPI.HasComponent&lt;PhysicsRenderEntity>(renderEntity))
            {
                renderEntity = SystemAPI.GetComponent&lt;PhysicsRenderEntity>(renderEntity).Entity;
            }

            // If no render info is found on the located render entity, we try to find any child which has render info
            if (renderEntity != Entity.Null &amp;&amp; !EntityManager.HasComponent&lt;MaterialMeshInfo>(renderEntity))
            {
                // No render information on this entity. Try to find the actual render entity in the hierarchy.
                if (EntityManager.HasBuffer&lt;Child>(renderEntity))
                {
                    var children = EntityManager.GetBuffer&lt;Child>(renderEntity);
                    foreach (var childElement in children)
                    {
                        // find the first child with render info
                        if (EntityManager.HasComponent&lt;MaterialMeshInfo>(childElement.Value))
                        {
                            renderEntity = childElement.Value;
                        }
                    }
                }
            }

            return renderEntity;
        }

        protected override void OnUpdate()
        {
            var collisionWorld = SystemAPI.GetSingleton&lt;PhysicsWorldSingleton>().CollisionWorld;
            Vector2 mousePosition = Input.mousePosition;
            UnityEngine.Ray unityRay = Camera.main.ScreenPointToRay(mousePosition);
            var rayInput = new RaycastInput
            {
                Start = unityRay.origin,
                End = unityRay.origin + unityRay.direction * MousePickSystem.k_MaxDistance,
                Filter = CollisionFilter.Default,
            };

            var mouseHover = SystemAPI.ManagedAPI.GetSingleton&lt;MouseHover>();

            RaycastHit hit;
            using (var raycastHitRef = new NativeReference&lt;RaycastHit>(Allocator.TempJob))
            {
                var rcj = new WorldRaycastJob()
                {
                    CollisionWorld = collisionWorld,
                    RayInput = rayInput,
                    IgnoreTriggers = mouseHover.IgnoreTriggers,
                    IgnoreStatic = mouseHover.IgnoreStatic,
                    RaycastHitRef = raycastHitRef
                };
                rcj.Run();
                hit = raycastHitRef.Value;
            }

            var graphicsEntity = FindGraphicsEntityFromPhysics(hit.Entity, hit.ColliderKey);

            // If still hovering over the same entity then do nothing.
            if (mouseHover.CurrentEntity.Equals(graphicsEntity)) return;

            mouseHover.PreviousEntity = mouseHover.CurrentEntity;
            mouseHover.CurrentEntity = graphicsEntity;

            bool hasPreviousEntity = !mouseHover.PreviousEntity.Equals(Entity.Null);
            bool hasCurrentEntity = !mouseHover.CurrentEntity.Equals(Entity.Null);

            if (hasPreviousEntity &amp;&amp; EntityManager.HasComponent&lt;MaterialMeshInfo>(mouseHover.PreviousEntity))
            {
                // restore render info to original in the last entity we were hovering over
                EntityManager.SetComponentData(mouseHover.PreviousEntity, mouseHover.OriginalMeshInfo);
                EntityManager.SetSharedComponentManaged(mouseHover.PreviousEntity, mouseHover.OriginalRenderMeshes);
            }

            if (hasCurrentEntity &amp;&amp; EntityManager.HasComponent&lt;MaterialMeshInfo>(mouseHover.CurrentEntity) &amp;&amp; EntityManager.HasComponent&lt;RenderMeshArray>(mouseHover.CurrentEntity))
            {
                mouseHover.PreviousEntity = mouseHover.CurrentEntity;
                mouseHover.CurrentEntity = graphicsEntity;
                mouseHover.OriginalMeshInfo = EntityManager.GetComponentData&lt;MaterialMeshInfo>(mouseHover.CurrentEntity);
                mouseHover.OriginalRenderMeshes = EntityManager.GetSharedComponentManaged&lt;RenderMeshArray>(mouseHover.CurrentEntity);

                // get render info from the hover entity
                var hoverMeshInfo = EntityManager.GetComponentData&lt;MaterialMeshInfo>(mouseHover.HoverEntity);
                var hoverRenderMeshes = EntityManager.GetSharedComponentManaged&lt;RenderMeshArray>(mouseHover.HoverEntity);

                // create new render info for the current entity that we hover over:

                // use the materials from the hover entity, but the meshes from the current entity
                var newRenderMeshes = new RenderMeshArray(hoverRenderMeshes.MaterialReferences, mouseHover.OriginalRenderMeshes.MeshReferences);

                // use the material id from the hover entity, but the mesh id from the current entity
                var newMeshInfo = MaterialMeshInfo.FromRenderMeshArrayIndices(hoverMeshInfo.Material, mouseHover.OriginalMeshInfo.Mesh);

                // apply the new render info to the current entity
                EntityManager.SetComponentData(mouseHover.CurrentEntity, newMeshInfo);
                EntityManager.SetSharedComponentManaged(mouseHover.CurrentEntity, newRenderMeshes);
            }
        }
    }
}
</pre>



<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">1. MouseHover (IComponentData)</h3>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">
// 관리되는 객체이므로 Class를 사용 
public class MouseHover : IComponentData
{
    public bool IgnoreTriggers;
    public bool IgnoreStatic;
    public Entity PreviousEntity; // 이전 프레임에서 Hover 상태의 Entity
    public Entity CurrentEntity; // 현재 프레임에서 Hover 상태의 Entity
    public Entity HoverEntity; // Hover 상태일 때 적용할 메쉬/머터리얼이 있는 Entity
    public MaterialMeshInfo OriginalMeshInfo; // 원래의 렌더링 정보를 저장
    public RenderMeshArray OriginalRenderMeshes; //   "
}
</pre>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">2. MouseHoverBaker (Baker)</h3>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">
[DisallowMultipleComponent]
public class MouseHoverAuthoring : MonoBehaviour
{
    public GameObject HoverPrefab;
    public bool IgnoreTriggers = true;
    public bool IgnoreStatic = true;

    // 참고: 편집기에서 구성 요소를 비활성화하려면 OnEnable을 재정의합니다
    protected void OnEnable() { }
}

// MouseHover 컴포넌트를 Entity에 추가
class MouseHoverBaker : Baker&lt;MouseHoverAuthoring>
{
    public override void Bake(MouseHoverAuthoring authoring)
    {
        var entity = GetEntity(TransformUsageFlags.None);

        AddComponentObject(entity, new MouseHover()
        {
            PreviousEntity = Entity.Null,
            CurrentEntity = Entity.Null,
            IgnoreTriggers = authoring.IgnoreTriggers,
            IgnoreStatic = authoring.IgnoreStatic,
            HoverEntity = GetEntity(authoring.HoverPrefab, TransformUsageFlags.Dynamic), // HoverPrefab을 HoverEntity로 변환하여 저장
        });
    }
}
</pre>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">3. MouseHoverSystem (SystemBase)</h3>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">    // Applies any mouse spring as a change in velocity on the entity's motion component
    // Limitations: works only if the physics objects in the scene come from the same subscene as MouseHoverAuthoring
    // Will be fixable if there is a Unity.Rendering API that lets you get the UnityEngine.Mesh that an entity is using for renderin.
    
    [UpdateInGroup(typeof(InitializationSystemGroup))]
    public partial class MouseHoverSystem : SystemBase
    {
        [BurstCompile]
        public struct WorldRaycastJob : IJob
        {
            public RaycastInput RayInput;
            [ReadOnly] public CollisionWorld CollisionWorld;
            [ReadOnly] public bool IgnoreTriggers;
            [ReadOnly] public bool IgnoreStatic;

            public NativeReference&lt;RaycastHit> RaycastHitRef;

            public void Execute()
            {
                var mousePickCollector = new MousePickCollector(CollisionWorld.NumDynamicBodies)
                {
                    IgnoreTriggers = IgnoreTriggers,
                    IgnoreStatic = IgnoreStatic
                };

                if (CollisionWorld.CastRay(RayInput, ref mousePickCollector))
                {
                    RaycastHitRef.Value = mousePickCollector.Hit;
                }
            }
        }

        protected override void OnCreate()
        {
            base.OnCreate();
            RequireForUpdate&lt;MouseHover>();
        }

        // Find the Entity holding the Graphical representation of a Physics Shape.
        // It may be that the Physics and Graphics representation are on the same Entity.
        public Entity FindGraphicsEntityFromPhysics(Entity bodyEntity) => FindGraphicsEntityFromPhysics(bodyEntity, ColliderKey.Empty);
        public Entity FindGraphicsEntityFromPhysics(Entity bodyEntity, ColliderKey leafColliderKey)
        {
            if (bodyEntity.Equals(Entity.Null))
            {
                // No Physics so no Graphics
                return Entity.Null;
            }

            // Set the Graphics Entity to the supplied Physics Entity
            var renderEntity = bodyEntity;

            // Check if we have hit a leaf node
            if (!leafColliderKey.Equals(ColliderKey.Empty))
            {
                // Get the Physics Collider
                var rootCollider = EntityManager.GetComponentData&lt;PhysicsCollider>(bodyEntity).Value;

                // If we hit a CompoundCollider we need to find the original Entity associated
                // the actual leaf Collider that was hit.
                if (rootCollider.Value.Type == ColliderType.Compound)
                {
                    #region Find a Leaf Entity and ColliderKey
                    var leafEntity = Entity.Null;
                    unsafe
                    {
                        var rootColliderPtr = rootCollider.AsPtr();

                        // Get the leaf Collider and check if we hit was a PolygonCollider (i.e. a Triangle or a Quad)
                        rootColliderPtr->GetLeaf(leafColliderKey, out var childCollider);
                        leafEntity = childCollider.Entity;

                        // PolygonColliders are likely to not have an original Entity associated with them
                        // So if we have a Polygon and it has no Entity then we really need to check for
                        // the higher level Mesh or Terrain Collider instead.
                        var childColliderType = childCollider.Collider->Type;
                        var childColliderIsPolygon = childColliderType == ColliderType.Triangle || childColliderType == ColliderType.Quad;
                        if (childColliderIsPolygon &amp;&amp; childCollider.Entity.Equals(Entity.Null))
                        {
                            // Get the ColliderKey of the Polygon's parent
                            if (TryGetParentColliderKey(rootColliderPtr, leafColliderKey, out leafColliderKey))
                            {
                                // Get the Mesh or Terrain Collider of the Polygon
                                TryGetChildInHierarchy(rootColliderPtr, leafColliderKey, out childCollider);
                                leafEntity = childCollider.Entity;
                            }
                        }
                    }
                    #endregion

                    // The Entities recorded in the leaves of a CompoundCollider may have been correct
                    // at the time of conversion. However, if the Collider blob is shared, or came up
                    // through a sub scene, we cannot assume that the baked Entities in the
                    // CompoundCollider are still valid.

                    // On conversion Entities using a CompoundCollider have an extra dynamic buffer added
                    // which holds a list of Entity/ColliderKey pairs. This buffer should be patched up
                    // automatically and be valid with each instance, at least until you start messing
                    // with the Entity hierarchy yourself e.g. by deleting Entities.

                    #region Check the Leaf Entity is valid
                    // If the leafEntity was never assigned in the first place
                    // there is no point in looking up any Buffers.
                    if (!leafEntity.Equals(Entity.Null))
                    {
                        // Check for an Key/Entity pair buffer first.
                        // This should exist if the Physics conversion pipeline was invoked.
                        var colliderKeyEntityPairBuffers = GetBufferLookup&lt;PhysicsColliderKeyEntityPair>(true);
                        if (colliderKeyEntityPairBuffers.HasBuffer(bodyEntity))
                        {
                            var colliderKeyEntityBuffer = colliderKeyEntityPairBuffers[bodyEntity];
                            for (int i = 0; i &lt; colliderKeyEntityBuffer.Length; i++)
                            {
                                var bufferColliderKey = colliderKeyEntityBuffer[i].Key;
                                if (leafColliderKey.Equals(bufferColliderKey))
                                {
                                    renderEntity = colliderKeyEntityBuffer[i].Entity;
                                    break;
                                }
                            }
                        }
                        else
                        {
                            // We haven't found a Key/Entity pair buffer so the compound collider
                            // may have been created in code.

                            // We'll assume the Entity in the CompoundCollider is valid
                            renderEntity = leafEntity;

                            // If this CompoundCollider was instanced from a prefab then the entities
                            // in the compound children would actually reference the original prefab hierarchy.
                            var rootEntityFromLeaf = leafEntity;
                            while (SystemAPI.HasComponent&lt;Parent>(rootEntityFromLeaf))
                            {
                                rootEntityFromLeaf = SystemAPI.GetComponent&lt;Parent>(rootEntityFromLeaf).Value;
                            }

                            // If the root Entity found from the leaf does not match the body Entity
                            // then we have hit an instance using the same CompoundCollider.
                            // This means we can try and remap the leaf Entity to the new hierarchy.
                            if (!rootEntityFromLeaf.Equals(bodyEntity))
                            {
                                // This assumes there is a LinkedEntityGroup Buffer on original and instance Entity.
                                // No doubt there is a more optimal way of doing this remap with more specific
                                // knowledge of the final application.
                                var linkedEntityGroupBuffers = GetBufferLookup&lt;LinkedEntityGroup>(true);

                                // Only remap if the buffers exist, have been created and are of equal length.
                                bool hasBufferRootEntity = linkedEntityGroupBuffers.HasBuffer(rootEntityFromLeaf);
                                bool hasBufferBodyEntity = linkedEntityGroupBuffers.HasBuffer(bodyEntity);
                                if (hasBufferRootEntity &amp;&amp; hasBufferBodyEntity)
                                {
                                    var prefabEntityGroupBuffer = linkedEntityGroupBuffers[rootEntityFromLeaf];
                                    var instanceEntityGroupBuffer = linkedEntityGroupBuffers[bodyEntity];

                                    if (prefabEntityGroupBuffer.IsCreated &amp;&amp; instanceEntityGroupBuffer.IsCreated
                                        &amp;&amp; (prefabEntityGroupBuffer.Length == instanceEntityGroupBuffer.Length))
                                    {
                                        var prefabEntityGroup = prefabEntityGroupBuffer.AsNativeArray();
                                        var instanceEntityGroup = instanceEntityGroupBuffer.AsNativeArray();

                                        for (int i = 0; i &lt; prefabEntityGroup.Length; i++)
                                        {
                                            // If we've found the renderEntity index in the prefab hierarchy,
                                            // set the renderEntity to the equivalent Entity in the instance
                                            if (prefabEntityGroup[i].Value.Equals(renderEntity))
                                            {
                                                renderEntity = instanceEntityGroup[i].Value;
                                                break;
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                    #endregion
                }
            }

            // Finally check to see if we have a graphics redirection on the shape Entity.
            if (SystemAPI.HasComponent&lt;PhysicsRenderEntity>(renderEntity))
            {
                renderEntity = SystemAPI.GetComponent&lt;PhysicsRenderEntity>(renderEntity).Entity;
            }

            // If no render info is found on the located render entity, we try to find any child which has render info
            if (renderEntity != Entity.Null &amp;&amp; !EntityManager.HasComponent&lt;MaterialMeshInfo>(renderEntity))
            {
                // No render information on this entity. Try to find the actual render entity in the hierarchy.
                if (EntityManager.HasBuffer&lt;Child>(renderEntity))
                {
                    var children = EntityManager.GetBuffer&lt;Child>(renderEntity);
                    foreach (var childElement in children)
                    {
                        // find the first child with render info
                        if (EntityManager.HasComponent&lt;MaterialMeshInfo>(childElement.Value))
                        {
                            renderEntity = childElement.Value;
                        }
                    }
                }
            }

            return renderEntity;
        }

        protected override void OnUpdate()
        {
            var collisionWorld = SystemAPI.GetSingleton&lt;PhysicsWorldSingleton>().CollisionWorld;
            Vector2 mousePosition = Input.mousePosition;
            UnityEngine.Ray unityRay = Camera.main.ScreenPointToRay(mousePosition);
            var rayInput = new RaycastInput
            {
                Start = unityRay.origin,
                End = unityRay.origin + unityRay.direction * MousePickSystem.k_MaxDistance,
                Filter = CollisionFilter.Default,
            };

            var mouseHover = SystemAPI.ManagedAPI.GetSingleton&lt;MouseHover>();

            RaycastHit hit;
            using (var raycastHitRef = new NativeReference&lt;RaycastHit>(Allocator.TempJob))
            {
                var rcj = new WorldRaycastJob()
                {
                    CollisionWorld = collisionWorld,
                    RayInput = rayInput,
                    IgnoreTriggers = mouseHover.IgnoreTriggers,
                    IgnoreStatic = mouseHover.IgnoreStatic,
                    RaycastHitRef = raycastHitRef
                };
                rcj.Run();
                hit = raycastHitRef.Value;
            }

            var graphicsEntity = FindGraphicsEntityFromPhysics(hit.Entity, hit.ColliderKey);

            // If still hovering over the same entity then do nothing.
            if (mouseHover.CurrentEntity.Equals(graphicsEntity)) return;

            mouseHover.PreviousEntity = mouseHover.CurrentEntity;
            mouseHover.CurrentEntity = graphicsEntity;

            bool hasPreviousEntity = !mouseHover.PreviousEntity.Equals(Entity.Null);
            bool hasCurrentEntity = !mouseHover.CurrentEntity.Equals(Entity.Null);

            if (hasPreviousEntity &amp;&amp; EntityManager.HasComponent&lt;MaterialMeshInfo>(mouseHover.PreviousEntity))
            {
                // restore render info to original in the last entity we were hovering over
                EntityManager.SetComponentData(mouseHover.PreviousEntity, mouseHover.OriginalMeshInfo);
                EntityManager.SetSharedComponentManaged(mouseHover.PreviousEntity, mouseHover.OriginalRenderMeshes);
            }

            if (hasCurrentEntity &amp;&amp; EntityManager.HasComponent&lt;MaterialMeshInfo>(mouseHover.CurrentEntity) &amp;&amp; EntityManager.HasComponent&lt;RenderMeshArray>(mouseHover.CurrentEntity))
            {
                mouseHover.PreviousEntity = mouseHover.CurrentEntity;
                mouseHover.CurrentEntity = graphicsEntity;
                mouseHover.OriginalMeshInfo = EntityManager.GetComponentData&lt;MaterialMeshInfo>(mouseHover.CurrentEntity);
                mouseHover.OriginalRenderMeshes = EntityManager.GetSharedComponentManaged&lt;RenderMeshArray>(mouseHover.CurrentEntity);

                // get render info from the hover entity
                var hoverMeshInfo = EntityManager.GetComponentData&lt;MaterialMeshInfo>(mouseHover.HoverEntity);
                var hoverRenderMeshes = EntityManager.GetSharedComponentManaged&lt;RenderMeshArray>(mouseHover.HoverEntity);

                // create new render info for the current entity that we hover over:

                // use the materials from the hover entity, but the meshes from the current entity
                var newRenderMeshes = new RenderMeshArray(hoverRenderMeshes.MaterialReferences, mouseHover.OriginalRenderMeshes.MeshReferences);

                // use the material id from the hover entity, but the mesh id from the current entity
                var newMeshInfo = MaterialMeshInfo.FromRenderMeshArrayIndices(hoverMeshInfo.Material, mouseHover.OriginalMeshInfo.Mesh);

                // apply the new render info to the current entity
                EntityManager.SetComponentData(mouseHover.CurrentEntity, newMeshInfo);
                EntityManager.SetSharedComponentManaged(mouseHover.CurrentEntity, newRenderMeshes);
            }
        }
    }</pre>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading">1) InitializationSystemGroup</h4>



<figure class="wp-block-image size-full"><img fetchpriority="high" decoding="async" width="590" height="153" src="https://lycos7560.com/wp-content/uploads/2025/02/image-2.png" alt="" class="wp-image-39540" srcset="https://lycos7560.com/wp-content/uploads/2025/02/image-2.png 590w, https://lycos7560.com/wp-content/uploads/2025/02/image-2-300x78.png 300w" sizes="(max-width: 590px) 100vw, 590px" /></figure>



<p>초기화 단계에서 실행됨</p>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading">2) WorldRaycastJob (IJob)</h4>



<figure class="wp-block-image size-full"><img decoding="async" width="886" height="585" src="https://lycos7560.com/wp-content/uploads/2025/02/image-3.png" alt="" class="wp-image-39541" srcset="https://lycos7560.com/wp-content/uploads/2025/02/image-3.png 886w, https://lycos7560.com/wp-content/uploads/2025/02/image-3-300x198.png 300w, https://lycos7560.com/wp-content/uploads/2025/02/image-3-768x507.png 768w" sizes="(max-width: 886px) 100vw, 886px" /></figure>



<p>마우스 위치에서 Raycast를 실행하는 <code>IJob</code></p>



<ul class="wp-block-list">
<li><code>CollisionWorld.CastRay()</code>를 사용하여 마우스가 클릭한 지점의 물리 객체를 찾음</li>



<li><code>mousePickCollector</code>를 사용하여 필터링 (<code>IgnoreTriggers</code>, <code>IgnoreStatic</code>)</li>
</ul>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading">3) OnUpdate()</h4>



<figure class="wp-block-image size-full"><img decoding="async" width="1471" height="1593" src="https://lycos7560.com/wp-content/uploads/2025/02/image-4.jpg" alt="" class="wp-image-39542" srcset="https://lycos7560.com/wp-content/uploads/2025/02/image-4.jpg 1471w, https://lycos7560.com/wp-content/uploads/2025/02/image-4-277x300.jpg 277w, https://lycos7560.com/wp-content/uploads/2025/02/image-4-768x832.jpg 768w, https://lycos7560.com/wp-content/uploads/2025/02/image-4-1418x1536.jpg 1418w" sizes="(max-width: 1471px) 100vw, 1471px" /></figure>



<p></p>



<h5 class="wp-block-heading">1> CollisionWorld 가져오기</h5>



<figure class="wp-block-image size-full is-resized"><img decoding="async" width="946" height="55" src="https://lycos7560.com/wp-content/uploads/2025/02/image-4.png" alt="" class="wp-image-39543" style="width:946px;height:auto" srcset="https://lycos7560.com/wp-content/uploads/2025/02/image-4.png 946w, https://lycos7560.com/wp-content/uploads/2025/02/image-4-300x17.png 300w, https://lycos7560.com/wp-content/uploads/2025/02/image-4-768x45.png 768w" sizes="(max-width: 946px) 100vw, 946px" /></figure>



<ul class="wp-block-list">
<li><code>PhysicsWorldSingleton</code>은 Unity Physics 시스템에서 현재의 물리 월드(<code>CollisionWorld</code>)를 관리하는 싱글톤</li>



<li><code>CollisionWorld</code>는 물리적인 충돌을 검사하는 데 사용</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h5 class="wp-block-heading">2> 마우스 위치에서 레이(Ray) 생성</h5>



<figure class="wp-block-image size-full"><img decoding="async" width="977" height="65" src="https://lycos7560.com/wp-content/uploads/2025/02/image-5.png" alt="" class="wp-image-39544" srcset="https://lycos7560.com/wp-content/uploads/2025/02/image-5.png 977w, https://lycos7560.com/wp-content/uploads/2025/02/image-5-300x20.png 300w, https://lycos7560.com/wp-content/uploads/2025/02/image-5-768x51.png 768w" sizes="(max-width: 977px) 100vw, 977px" /></figure>



<ul class="wp-block-list">
<li><code>Input.mousePosition</code>을 사용하여 현재 마우스 커서의 화면 좌표를 가져오기</li>



<li><code>Camera.main.ScreenPointToRay(mousePosition)</code>을 통해 이 화면 좌표를 기준으로 3D 월드에서의 <code>Ray</code>를 생성</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h5 class="wp-block-heading">3> RaycastInput 설정</h5>



<figure class="wp-block-image size-full"><img decoding="async" width="946" height="196" src="https://lycos7560.com/wp-content/uploads/2025/02/image-6.png" alt="" class="wp-image-39545" srcset="https://lycos7560.com/wp-content/uploads/2025/02/image-6.png 946w, https://lycos7560.com/wp-content/uploads/2025/02/image-6-300x62.png 300w, https://lycos7560.com/wp-content/uploads/2025/02/image-6-768x159.png 768w" sizes="(max-width: 946px) 100vw, 946px" /></figure>



<p><code>RaycastInput</code>은 Unity Physics에서 사용되는 구조체로, 물리 엔진이 사용할 레이캐스트 정보를 포함</p>



<ul class="wp-block-list">
<li><code>Start</code>: 레이의 시작점 (<code>unityRay.origin</code>)</li>



<li><code>End</code>: 레이의 끝점 (<code>unityRay.origin + unityRay.direction * MousePickSystem.k_MaxDistance</code>)
<ul class="wp-block-list">
<li><code>MousePickSystem.k_MaxDistance</code>는 레이의 최대 탐색 거리로, 너무 멀리까지 체크하지 않도록 제한</li>
</ul>
</li>



<li><code>Filter</code>: <code>CollisionFilter.Default</code>는 기본적인 충돌 필터로, 충돌 검사를 수행할 물리 레이어를 정의</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h5 class="wp-block-heading">4> MouseHover 컴포넌트 가져오기</h5>



<figure class="wp-block-image size-full"><img decoding="async" width="801" height="55" src="https://lycos7560.com/wp-content/uploads/2025/02/image-7.png" alt="" class="wp-image-39546" srcset="https://lycos7560.com/wp-content/uploads/2025/02/image-7.png 801w, https://lycos7560.com/wp-content/uploads/2025/02/image-7-300x21.png 300w, https://lycos7560.com/wp-content/uploads/2025/02/image-7-768x53.png 768w" sizes="(max-width: 801px) 100vw, 801px" /></figure>



<ul class="wp-block-list">
<li><code>MouseHover</code>는 마우스 오버 이벤트를 관리하는 <code>IComponentData</code></li>



<li><code>SystemAPI.ManagedAPI.GetSingleton&lt;MouseHover>()</code>을 사용하여 현재 씬에 존재하는 <code>MouseHover</code> 컴포넌트를 가져옴</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h5 class="wp-block-heading">5> 레이캐스트 실행</h5>



<figure class="wp-block-image size-full"><img decoding="async" width="1016" height="429" src="https://lycos7560.com/wp-content/uploads/2025/02/image-8.png" alt="" class="wp-image-39547" srcset="https://lycos7560.com/wp-content/uploads/2025/02/image-8.png 1016w, https://lycos7560.com/wp-content/uploads/2025/02/image-8-300x127.png 300w, https://lycos7560.com/wp-content/uploads/2025/02/image-8-768x324.png 768w" sizes="(max-width: 1016px) 100vw, 1016px" /></figure>



<p><code>NativeReference&lt;RaycastHit></code>을 사용하여 레이캐스트 결과(<code>RaycastHit</code>)를 저장할 공간을 확보</p>



<p><code>WorldRaycastJob</code>을 생성하여 레이캐스트를 수행</p>



<ul class="wp-block-list">
<li><code>CollisionWorld</code>을 사용하여 실제 물리 충돌을 검사합니다.</li>



<li><code>IgnoreTriggers</code>, <code>IgnoreStatic</code> 값을 기반으로 충돌 조건을 설정합니다.</li>
</ul>



<p><code>rcj.Run();</code>을 실행하여 <code>Job</code>을 즉시 수행한 후 결과를 <code>hit</code>에 저장합니다.</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h5 class="wp-block-heading">5> 물리 Entity에서 그래픽 Entity 찾기</h5>



<figure class="wp-block-image size-full"><img decoding="async" width="863" height="57" src="https://lycos7560.com/wp-content/uploads/2025/02/image-9.png" alt="" class="wp-image-39548" srcset="https://lycos7560.com/wp-content/uploads/2025/02/image-9.png 863w, https://lycos7560.com/wp-content/uploads/2025/02/image-9-300x20.png 300w, https://lycos7560.com/wp-content/uploads/2025/02/image-9-768x51.png 768w" sizes="(max-width: 863px) 100vw, 863px" /></figure>



<p><code>hit.Entity</code>는 <code>RaycastHit</code> 결과에서 얻은 <strong>충돌한 물리 Entity</strong></p>



<p><code>FindGraphicsEntityFromPhysics(hit.Entity, hit.ColliderKey)</code>를 호출하여 <strong>실제 렌더링을 담당하는 Entity</strong>를 찾습니다.</p>



<ul class="wp-block-list">
<li>물리 Entity 와 그래픽 Entity 는 다를 수 있기 때문에 이 과정을 거칩니다.</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h5 class="wp-block-heading">6> 동일한 엔터티를 계속 가리키면 무시</h5>



<figure class="wp-block-image size-full"><img decoding="async" width="819" height="91" src="https://lycos7560.com/wp-content/uploads/2025/02/image-10.png" alt="" class="wp-image-39549" srcset="https://lycos7560.com/wp-content/uploads/2025/02/image-10.png 819w, https://lycos7560.com/wp-content/uploads/2025/02/image-10-300x33.png 300w, https://lycos7560.com/wp-content/uploads/2025/02/image-10-768x85.png 768w" sizes="(max-width: 819px) 100vw, 819px" /></figure>



<p>현재 마우스가 가리키는 <code>graphicsEntity</code>가 이전 프레임과 동일하면 추가 작업을 하지 않고 바로 반환합니다.</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h5 class="wp-block-heading">7> 이전 및 현재 마우스 오버 Entity 업데이트</h5>



<figure class="wp-block-image size-full"><img decoding="async" width="698" height="73" src="https://lycos7560.com/wp-content/uploads/2025/02/image-11.png" alt="" class="wp-image-39551" srcset="https://lycos7560.com/wp-content/uploads/2025/02/image-11.png 698w, https://lycos7560.com/wp-content/uploads/2025/02/image-11-300x31.png 300w" sizes="(max-width: 698px) 100vw, 698px" /></figure>



<ul class="wp-block-list">
<li><code>PreviousEntity</code>에 이전 프레임에서 마우스가 가리키던 Entity를 저장합니다.</li>



<li><code>CurrentEntity</code>에 이번 프레임에서 새롭게 감지된 Entity를 저장합니다.</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h5 class="wp-block-heading">8> 이전 Entity의 원래 상태 복구</h5>



<figure class="wp-block-image size-full"><img decoding="async" width="1081" height="247" src="https://lycos7560.com/wp-content/uploads/2025/02/image-12.png" alt="" class="wp-image-39552" srcset="https://lycos7560.com/wp-content/uploads/2025/02/image-12.png 1081w, https://lycos7560.com/wp-content/uploads/2025/02/image-12-300x69.png 300w, https://lycos7560.com/wp-content/uploads/2025/02/image-12-768x175.png 768w" sizes="(max-width: 1081px) 100vw, 1081px" /></figure>



<p><code>hasPreviousEntity</code>: 이전 프레임에서 마우스가 가리키던 Entity가 존재하는지 확인</p>



<p><code>EntityManager.HasComponent&lt;MaterialMeshInfo>(mouseHover.PreviousEntity)</code>: 이전 Entity가 <code>MaterialMeshInfo</code> 컴포넌트를 가지고 있는지 확인</p>



<p><strong>이전 Entity의 렌더링 데이터를 원래 값으로 복원</strong>:</p>



<ul class="wp-block-list">
<li><code>MaterialMeshInfo</code>: 이전에 저장해 둔 원래의 메쉬 정보를 복원</li>



<li><code>RenderMeshArray</code>: 이전에 저장해 둔 원래의 <code>Material</code> 배열을 복원</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h5 class="wp-block-heading">9> 새로운 Entity의 마우스 Hover 효과 적용</h5>



<figure class="wp-block-image size-full"><img decoding="async" width="1718" height="173" src="https://lycos7560.com/wp-content/uploads/2025/02/image-13.png" alt="" class="wp-image-39553" srcset="https://lycos7560.com/wp-content/uploads/2025/02/image-13.png 1718w, https://lycos7560.com/wp-content/uploads/2025/02/image-13-300x30.png 300w, https://lycos7560.com/wp-content/uploads/2025/02/image-13-768x77.png 768w, https://lycos7560.com/wp-content/uploads/2025/02/image-13-1536x155.png 1536w" sizes="(max-width: 1718px) 100vw, 1718px" /></figure>



<p>현재 프레임에서 감지된 <code>CurrentEntity</code>가 존재하는지 확인</p>



<p><code>MaterialMeshInfo</code> 및 <code>RenderMeshArray</code> 컴포넌트가 있는 경우, <strong>원래 상태를 저장</strong>:</p>



<ul class="wp-block-list">
<li><code>OriginalMeshInfo</code>: 원래 <code>MaterialMeshInfo</code> 값을 저장</li>



<li><code>OriginalRenderMeshes</code>: 원래 <code>RenderMeshArray</code> 값을 저장</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h5 class="wp-block-heading">10> 마우스 Hover 머티리얼 적용</h5>



<figure class="wp-block-image size-full"><img decoding="async" width="1419" height="420" src="https://lycos7560.com/wp-content/uploads/2025/02/image-14.png" alt="" class="wp-image-39554" srcset="https://lycos7560.com/wp-content/uploads/2025/02/image-14.png 1419w, https://lycos7560.com/wp-content/uploads/2025/02/image-14-300x89.png 300w, https://lycos7560.com/wp-content/uploads/2025/02/image-14-768x227.png 768w" sizes="(max-width: 1419px) 100vw, 1419px" /></figure>



<p><code>mouseHover.HoverEntity</code>는 마우스 Hover 효과를 표시하는 데 사용할 프리팹(Material 정보 포함)</p>



<p><code>hoverMeshInfo</code>와 <code>hoverRenderMeshes</code>를 가져와서 현재 마우스가 가리키는 Entity에 적용</p>



<p></p>
<p>The post <a href="https://lycos7560.com/unity/ecs-mousehoverauthoring/39539/">ECS &#8211; MouseHoverAuthoring</a> appeared first on <a href="https://lycos7560.com">어제와 내일의 나 그 사이의 이야기</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://lycos7560.com/unity/ecs-mousehoverauthoring/39539/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		<enclosure url="https://lycos7560.com/wp-content/uploads/2025/02/녹화_2025_02_05_19_00_37_49.mp4" length="14855134" type="video/mp4" />

			</item>
		<item>
		<title>ECS &#8211; BufferTypeHandle</title>
		<link>https://lycos7560.com/unity/ecs-buffertypehandle/39536/</link>
					<comments>https://lycos7560.com/unity/ecs-buffertypehandle/39536/#respond</comments>
		
		<dc:creator><![CDATA[lycos7560]]></dc:creator>
		<pubDate>Wed, 05 Feb 2025 06:26:40 +0000</pubDate>
				<category><![CDATA[Unity]]></category>
		<category><![CDATA[AsNativeArray]]></category>
		<category><![CDATA[AsNativeArray()]]></category>
		<category><![CDATA[BufferTypeHandle]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[Chunk components]]></category>
		<category><![CDATA[Cleanup components]]></category>
		<category><![CDATA[Component]]></category>
		<category><![CDATA[concept]]></category>
		<category><![CDATA[Data]]></category>
		<category><![CDATA[DOTS]]></category>
		<category><![CDATA[ECS]]></category>
		<category><![CDATA[Entity Component System]]></category>
		<category><![CDATA[IJobChunk]]></category>
		<category><![CDATA[job]]></category>
		<category><![CDATA[Job 시스템]]></category>
		<category><![CDATA[Managed Components]]></category>
		<category><![CDATA[OnUpdate]]></category>
		<category><![CDATA[OnUpdate()]]></category>
		<category><![CDATA[Physics]]></category>
		<category><![CDATA[Sample]]></category>
		<category><![CDATA[ScheduleParallel]]></category>
		<category><![CDATA[Shared Components]]></category>
		<category><![CDATA[study]]></category>
		<category><![CDATA[Tag components]]></category>
		<category><![CDATA[Unity Physics 101]]></category>
		<category><![CDATA[Unmanaged]]></category>
		<category><![CDATA[Unmanaged components]]></category>
		<guid isPermaLink="false">https://lycos7560.com/?p=39536</guid>

					<description><![CDATA[<p>ECS &#8211; BufferTypeHandle ECS에서 DynamicBuffer&#60;T>를 처리하려면 BufferTypeHandle&#60;T>를 사용해야 한다. 즉, &#8220;IComponentData가 아닌 버퍼 데이터를 읽고/쓰는 Handle&#8220; 1. BufferTypeHandle&#60;T>란? 2. 사용 예제 1) OnCreate()에서 초기화 2) OnUpdate()에서 매 프레임 업데이트 3. BufferTypeHandle 상세 사용법 1) 기본 사용법 (IJobChunk에서 사용) 2) OnUpdate()에서 ScheduleParallel 실행 3) IJobParallelFor에서 BufferTypeHandle를 사용할 때 일반적으로 IJobParallelFor에서는 직접 사용할 수 없고, AsNativeArray() 를 [&#8230;]</p>
<p>The post <a href="https://lycos7560.com/unity/ecs-buffertypehandle/39536/">ECS &#8211; BufferTypeHandle</a> appeared first on <a href="https://lycos7560.com">어제와 내일의 나 그 사이의 이야기</a>.</p>
]]></description>
										<content:encoded><![CDATA[				<div class="wp-block-uagb-table-of-contents uagb-toc__align-left uagb-toc__columns-1  uagb-block-189571fa      "
					data-scroll= "1"
					data-offset= "30"
					style=""
				>
				<div class="uagb-toc__wrap">
						<div class="uagb-toc__title">
							목차 테이블						</div>
																						<div class="uagb-toc__list-wrap ">
						<ol class="uagb-toc__list"><li class="uagb-toc__list"><a href="#ecs-buffertypehandle" class="uagb-toc-link__trigger">ECS &#8211; BufferTypeHandle</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#1-buffertypehandlet란" class="uagb-toc-link__trigger">1. BufferTypeHandle란?</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#2-사용-예제" class="uagb-toc-link__trigger">2. 사용 예제</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#1-oncreate에서-초기화" class="uagb-toc-link__trigger">1) OnCreate()에서 초기화</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#2-onupdate에서-매-프레임-업데이트" class="uagb-toc-link__trigger">2) OnUpdate()에서 매 프레임 업데이트</a></li></ul><li class="uagb-toc__list"><a href="#3-buffertypehandle-상세-사용법" class="uagb-toc-link__trigger">3. BufferTypeHandle 상세 사용법</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#1-기본-사용법-ijobchunk에서-사용" class="uagb-toc-link__trigger">1) 기본 사용법 (IJobChunk에서 사용)</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#2-onupdate에서-scheduleparallel-실행" class="uagb-toc-link__trigger">2) OnUpdate()에서 ScheduleParallel 실행</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#3-ijobparallelfor에서-buffertypehandle를-사용할-때" class="uagb-toc-link__trigger">3) IJobParallelFor에서 BufferTypeHandle를 사용할 때</a></li></ul><li class="uagb-toc__list"><a href="#4-buffertypehandle-요약" class="uagb-toc-link__trigger">4. BufferTypeHandle 요약</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#buffertypehandlet는-언제-사용" class="uagb-toc-link__trigger">BufferTypeHandle는 언제 사용?</a></ul></ul></ol>					</div>
									</div>
				</div>
			


<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>



<h1 class="wp-block-heading"><strong>ECS &#8211; BufferTypeHandle</strong></h1>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p>ECS에서 <code>DynamicBuffer&lt;T></code>를 처리하려면 <code>BufferTypeHandle&lt;T></code>를 사용해야 한다.</p>



<p>즉, <strong>&#8220;IComponentData가 아닌 버퍼 데이터를 읽고/쓰는 <strong>Handle</strong>&#8220;</strong></p>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading">1. <strong>BufferTypeHandle&lt;T>란?</strong></h2>



<ul class="wp-block-list">
<li><strong>ECS에서 <code>DynamicBuffer&lt;T></code>를 읽고/쓰기 위한 <strong><strong>Handle</strong></strong></strong></li>



<li><code><strong>IJobChunk</strong></code> 내부에서 사용됨</li>



<li><code><strong>GetBufferAccessor(ref BufferTypeHandle&lt;T>)</strong></code>를 이용해 버퍼 데이터 접근</li>



<li>쓰기 여부에 따라 <strong>읽기 전용(<code>true</code>) / 읽기+쓰기(<code>false</code>)</strong> 로 설정 가능</li>
</ul>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading">2. <strong>사용 예제</strong></h2>



<h3 class="wp-block-heading">1) <strong>OnCreate()에서 초기화</strong></h3>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">_BufferTypeHandle = state.GetBufferTypeHandle&lt;GridCellBufferComponent>(isReadOnly: false);
</pre>



<ul class="wp-block-list">
<li><code>isReadOnly: true</code> → 읽기 전용</li>



<li><code>isReadOnly: false</code> → 읽기 &amp; 쓰기 가능</li>
</ul>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">2) <strong>OnUpdate()에서 매 프레임 업데이트</strong></h3>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">_BufferTypeHandle.Update(ref state);
</pre>



<ul class="wp-block-list">
<li><code>Update(ref state)</code>를 호출해야 최신 데이터를 가져올 수 있다.</li>
</ul>



<div style="height:70px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading">3. <strong>BufferTypeHandle 상세 사용법</strong></h2>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">1) <strong>기본 사용법 (IJobChunk에서 사용)</strong></h3>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">[BurstCompile]
public struct CountJob : IJobChunk
{
    public BufferTypeHandle&lt;GridCellBufferComponent> BufferTypeHandle;

    public void Execute(in ArchetypeChunk chunk, int chunkIndex, bool useEnabledMask, in v128 chunkEnabledMask)
    {
        var buffers = chunk.GetBufferAccessor(ref BufferTypeHandle);

        for (int i = 0; i &lt; chunk.Count; i++)
        {
            DynamicBuffer&lt;GridCellBufferComponent> buffer = buffers[i];

            for (int j = 0; j &lt; buffer.Length; j++)
            {
                buffer[j] = new GridCellBufferComponent
                {
                    Position = buffer[j].Position,
                    IsOccupied = buffer[j].IsOccupied,
                    Count = buffer[j].Count + 1 // Count 증가
                };
            }
        }
    }
}
</pre>



<ul class="wp-block-list">
<li><code>chunk.GetBufferAccessor(ref BufferTypeHandle)</code>을 사용하여 <code>DynamicBuffer</code>에 접근</li>



<li>각 <code>chunk</code>에 속한 모든 <code>DynamicBuffer&lt;GridCellBufferComponent></code>에 대해 반복문 실행</li>
</ul>



<div style="height:70px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">2) <strong>OnUpdate()에서 ScheduleParallel 실행</strong></h3>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">public void OnUpdate(ref SystemState state)
{
    _BufferTypeHandle.Update(ref state);

    var job = new CountJob
    {
        BufferTypeHandle = _BufferTypeHandle
    };

    state.Dependency = job.ScheduleParallel(_GridQuery, state.Dependency);
}
</pre>



<ul class="wp-block-list">
<li><code>_BufferTypeHandle.Update(ref state);</code> → 최신 상태 반영</li>



<li><code>ScheduleParallel()</code> → 여러 개의 <code>chunk</code>를 병렬 실행</li>
</ul>



<div style="height:70px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><strong>3) IJobParallelFor에서 BufferTypeHandle를 사용할 때</strong></h3>



<p>일반적으로 <code>IJobParallelFor</code>에서는 직접 사용할 수 없고, <strong><code>AsNativeArray()</code></strong> 를 사용해야 함.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">[BurstCompile]
public struct CountJobParallelFor : IJobParallelFor
{
    public NativeArray&lt;GridCellBufferComponent> BufferNativeArray;

    public void Execute(int index)
    {
        BufferNativeArray[index] = new GridCellBufferComponent
        {
            Position = BufferNativeArray[index].Position,
            IsOccupied = BufferNativeArray[index].IsOccupied,
            Count = BufferNativeArray[index].Count + 1
        };
    }
}
</pre>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<p><strong><code>OnUpdate()</code>에서 실행:</strong></p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">public void OnUpdate(ref SystemState state)
{
    _BufferTypeHandle.Update(ref state);
    var chunks = _GridQuery.ToArchetypeChunkArray(Allocator.TempJob);

    for (int i = 0; i &lt; chunks.Length; i++)
    {
        BufferAccessor&lt;GridCellBufferComponent> bufferAccessor = chunks[i].GetBufferAccessor(ref _BufferTypeHandle);
        for (int j = 0; j &lt; bufferAccessor.Length; j++)
        {
            DynamicBuffer&lt;GridCellBufferComponent> buffer = bufferAccessor[j];

            var job = new CountJobParallelFor
            {
                BufferNativeArray = buffer.AsNativeArray()
            };

            job.Schedule(buffer.Length, 1).Complete(); // 즉시 실행
        }
    }
    chunks.Dispose();
}
</pre>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<p><strong>주의할 점</strong></p>



<ul class="wp-block-list">
<li><code>DynamicBuffer&lt;T></code>에서 <strong><code>AsNativeArray()</code></strong> 를 호출하여 <code>IJobParallelFor</code>에서 사용</li>



<li><code>.Complete()</code> 없이 <code>state.Dependency = job.Schedule();</code>을 하면 <strong>Write Conflict 에러 발생</strong></li>



<li><code>buffer.AsNativeArray()</code>는 <strong>해당 프레임에서만 유효</strong></li>
</ul>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading">4. <strong>BufferTypeHandle 요약</strong></h2>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>기능</th><th>설명</th></tr></thead><tbody><tr><td><code>BufferTypeHandle&lt;T&gt;</code></td><td><code>DynamicBuffer&lt;T></code>를 읽고/쓰기 위한 Handle</td></tr><tr><td><code>.Update(ref state)</code></td><td>매 프레임 최신 상태로 갱신</td></tr><tr><td><code>GetBufferAccessor(ref BufferTypeHandle&lt;T&gt;)</code></td><td><code>DynamicBuffer&lt;T&gt;</code>를 가져오는 함수</td></tr><tr><td><code>ScheduleParallel()</code></td><td>여러 <code>chunk</code>를 병렬 실행</td></tr><tr><td><code>AsNativeArray()</code></td><td><code>IJobParallelFor</code>에서 사용 가능</td></tr></tbody></table></figure>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading has-medium-font-size"><strong>BufferTypeHandle&lt;T>는 언제 사용?</strong></h4>



<ul class="wp-block-list">
<li>많은 데이터가 DynamicBuffer에 저장될 때</li>



<li>ECS에서 <code>IJobChunk</code> 또는 <code>IJobParallelFor</code>을 활용할 때</li>



<li>최대 성능을 끌어내기 위해 <code>ScheduleParallel</code>을 사용할 때</li>
</ul>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>
<p>The post <a href="https://lycos7560.com/unity/ecs-buffertypehandle/39536/">ECS &#8211; BufferTypeHandle</a> appeared first on <a href="https://lycos7560.com">어제와 내일의 나 그 사이의 이야기</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://lycos7560.com/unity/ecs-buffertypehandle/39536/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>ECS &#8211; System concepts</title>
		<link>https://lycos7560.com/unity/ecs-system-concepts/39534/</link>
					<comments>https://lycos7560.com/unity/ecs-system-concepts/39534/#respond</comments>
		
		<dc:creator><![CDATA[lycos7560]]></dc:creator>
		<pubDate>Tue, 04 Feb 2025 02:16:18 +0000</pubDate>
				<category><![CDATA[Unity]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[Chunk components]]></category>
		<category><![CDATA[Cleanup components]]></category>
		<category><![CDATA[Component]]></category>
		<category><![CDATA[concept]]></category>
		<category><![CDATA[Data]]></category>
		<category><![CDATA[DOTS]]></category>
		<category><![CDATA[ECS]]></category>
		<category><![CDATA[Entity Component System]]></category>
		<category><![CDATA[job]]></category>
		<category><![CDATA[Job 시스템]]></category>
		<category><![CDATA[Managed Components]]></category>
		<category><![CDATA[Physics]]></category>
		<category><![CDATA[Sample]]></category>
		<category><![CDATA[Shared Components]]></category>
		<category><![CDATA[study]]></category>
		<category><![CDATA[Tag components]]></category>
		<category><![CDATA[Unity Physics 101]]></category>
		<category><![CDATA[Unmanaged]]></category>
		<category><![CDATA[Unmanaged components]]></category>
		<guid isPermaLink="false">https://lycos7560.com/?p=39534</guid>

					<description><![CDATA[<p>ECS &#8211; System concepts https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/concepts-systems.html System concepts System의 역할 System의 유형 시스템 유형 설명 SystemBase C# 클래스 기반, 관리형 시스템 (GC 영향 있음) ISystem 구조체 기반, 비관리형 시스템 (성능 최적화) EntityCommandBufferSystem Entity 생성/삭제 등의 구조적 변경을 최적화하는 시스템 ComponentSystemGroup 여러 시스템을 그룹화하여 실행 순서 제어 System 실행 순서 (System Groups) 시스템 그룹 설명 InitializationSystemGroup 초기화 [&#8230;]</p>
<p>The post <a href="https://lycos7560.com/unity/ecs-system-concepts/39534/">ECS &#8211; System concepts</a> appeared first on <a href="https://lycos7560.com">어제와 내일의 나 그 사이의 이야기</a>.</p>
]]></description>
										<content:encoded><![CDATA[				<div class="wp-block-uagb-table-of-contents uagb-toc__align-left uagb-toc__columns-1  uagb-block-edbd8396      "
					data-scroll= "1"
					data-offset= "30"
					style=""
				>
				<div class="uagb-toc__wrap">
						<div class="uagb-toc__title">
							목차						</div>
																						<div class="uagb-toc__list-wrap ">
						<ol class="uagb-toc__list"><li class="uagb-toc__list"><a href="#ecs-system-concepts" class="uagb-toc-link__trigger">ECS &#8211; System concepts</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#system-concepts" class="uagb-toc-link__trigger">System concepts</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#systembase-예제-관리형-시스템" class="uagb-toc-link__trigger">SystemBase 예제 (관리형 시스템)</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#isystem-예제-비관리형-시스템" class="uagb-toc-link__trigger">ISystem 예제 (비관리형 시스템)</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#system-실행-순서-제어-system-group-활용" class="uagb-toc-link__trigger">System 실행 순서 제어 (System Group 활용)</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#inspecting-systems" class="uagb-toc-link__trigger">Inspecting systems</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#에디터에서-systems" class="uagb-toc-link__trigger">에디터에서 Systems</a></ul></ol>					</div>
									</div>
				</div>
			


<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading">ECS &#8211; System concepts</h2>



<p><a href="https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/concepts-systems.html" target="_blank" rel="noreferrer noopener">https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/concepts-systems.html</a></p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<figure class="wp-block-image size-full"><img decoding="async" width="695" height="278" src="https://lycos7560.com/wp-content/uploads/2025/02/image-1.png" alt="" class="wp-image-39532" srcset="https://lycos7560.com/wp-content/uploads/2025/02/image-1.png 695w, https://lycos7560.com/wp-content/uploads/2025/02/image-1-300x120.png 300w" sizes="(max-width: 695px) 100vw, 695px" /></figure>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><strong>System concepts</strong></h3>



<p><strong>System의 역할</strong></p>



<ul class="wp-block-list">
<li><code>System</code>은 컴포넌트 데이터를 읽고, 수정하여 <strong>프레임마다 업데이트</strong>하는 역할을 수행</li>



<li><code>OnUpdate()</code>가 <strong>매 프레임 호출</strong>되며, Entity 데이터를 갱신함</li>



<li><code>SystemBase</code>(관리형 시스템)와 <code>ISystem</code>(비관리형 시스템) 두 가지 유형이 있음</li>
</ul>



<div style="height:25px" aria-hidden="true" class="wp-block-spacer"></div>



<p><strong>System의 유형</strong></p>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>시스템 유형</th><th>설명</th></tr></thead><tbody><tr><td><code>SystemBase</code></td><td>C# 클래스 기반, 관리형 시스템 (GC 영향 있음)</td></tr><tr><td><code>ISystem</code></td><td>구조체 기반, 비관리형 시스템 (성능 최적화)</td></tr><tr><td><code>EntityCommandBufferSystem</code></td><td>Entity 생성/삭제 등의 구조적 변경을 최적화하는 시스템</td></tr><tr><td><code>ComponentSystemGroup</code></td><td>여러 시스템을 그룹화하여 실행 순서 제어</td></tr></tbody></table></figure>



<div style="height:25px" aria-hidden="true" class="wp-block-spacer"></div>



<p><strong>System 실행 순서 (System Groups)</strong></p>



<ul class="wp-block-list">
<li>ECS는 시스템을 <strong>Auto Bootstrap</strong>하며, 기본적으로 <strong>3가지 그룹</strong>으로 실행됨.<br>(Auto Bootstrap &#8211; 시스템을 자동으로 생성하고 실행하는 과정)</li>
</ul>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>시스템 그룹</th><th>설명</th></tr></thead><tbody><tr><td><code>InitializationSystemGroup</code></td><td>초기화 관련 시스템 실행 (프레임 시작 시)</td></tr><tr><td><code>SimulationSystemGroup</code></td><td>대부분의 게임 로직이 실행되는 기본 그룹</td></tr><tr><td><code>PresentationSystemGroup</code></td><td>렌더링과 관련된 시스템 실행 (프레임 마지막)</td></tr></tbody></table></figure>



<ul class="wp-block-list">
<li>특정 그룹에서 실행되도록 하려면 <code>[UpdateInGroup(typeof(그룹))]</code> 속성을 사용.</li>
</ul>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">SystemBase 예제 (관리형 시스템)</h3>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">using Unity.Burst;
using Unity.Entities;
using Unity.Transforms;
using Unity.Mathematics;
using UnityEngine;

// [UpdateInGroup(typeof(SimulationSystemGroup))]  // 특정 그룹에서 실행 가능
public partial class MoveSystem : SystemBase
{
    protected override void OnUpdate()
    {
        float deltaTime = SystemAPI.Time.DeltaTime; // 현재 프레임의 Delta Time

        Entities.ForEach((ref LocalTransform transform) =>
        {
            transform.Position += new float3(1, 0, 0) * deltaTime; // X축 이동
        }).ScheduleParallel();
    }
}
</pre>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">ISystem 예제 (비관리형 시스템)</h3>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">using Unity.Burst;
using Unity.Entities;
using Unity.Transforms;
using Unity.Mathematics;

// 성능 최적화를 위해 [BurstCompile] 적용
[BurstCompile]
public partial struct MoveSystem_ISystem : ISystem
{
    public void OnCreate(ref SystemState state) { }  // 초기화

    public void OnDestroy(ref SystemState state) { } // 시스템 종료 시 실행

    public void OnUpdate(ref SystemState state)
    {
        float deltaTime = SystemAPI.Time.DeltaTime;

        foreach (var transform in SystemAPI.Query&lt;RefRW&lt;LocalTransform>>())
        {
            transform.ValueRW.Position += new float3(1, 0, 0) * deltaTime;
        }
    }
}
</pre>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">System 실행 순서 제어 (System Group 활용)</h3>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">using Unity.Entities;

[UpdateInGroup(typeof(InitializationSystemGroup))] // 초기화 단계에서 실행
public partial class InitSystem : SystemBase
{
    protected override void OnUpdate()
    {
        UnityEngine.Debug.Log("초기화 시스템 실행됨!");
    }
}
</pre>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading" id="inspecting-systems">Inspecting systems</h3>



<p>시스템 창을 사용하여 각 세계의 시스템 업데이트 순서를 검사하고 시스템 그룹의 전체 계층을 볼 수 있습니다. </p>



<p>자세한 내용은 <a href="https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/editor-systems-window.html" target="_blank" rel="noreferrer noopener">Systems window reference</a>를 참조하세요.</p>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading" id="systems-in-the-editor">에디터에서 Systems</h3>



<p>편집기에서 다음 아이콘은 다양한 유형의 시스템을 나타냅니다. </p>



<p> <a href="https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/editor-workflows.html" target="_blank" rel="noreferrer noopener">Entities windows와 Inspectors</a>를 사용하면 이를 확인할 수 있습니다.</p>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th><strong>Icon</strong></th><th><strong>Represents</strong></th></tr></thead><tbody><tr><td><img decoding="async" src="https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/images/editor-system-group.png" alt=""></td><td>A system group</td></tr><tr><td><img decoding="async" src="https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/images/editor-system.png" alt=""></td><td>A system</td></tr><tr><td><img decoding="async" src="https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/images/editor-system-start-step.png" alt=""></td><td><a href="https://docs.unity3d.com/Packages/com.unity.entities@1.3/api/Unity.Entities.UpdateInGroupAttribute.OrderFirst.html" target="_blank" rel="noreferrer noopener">OrderFirst 인수</a>를 사용하여 시스템 그룹의 시작 시 실행되도록 설정된 Entity 명령 버퍼 시스템</td></tr><tr><td><img decoding="async" src="https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/images/editor-system-end-step.png" alt=""></td><td><a href="https://docs.unity3d.com/Packages/com.unity.entities@1.3/api/Unity.Entities.UpdateInGroupAttribute.OrderLast.html" target="_blank" rel="noreferrer noopener">OrderLast 인수</a>를 사용하여 시스템 그룹의 끝에서 실행되도록 설정된 엔티티 명령 버퍼 시스템입니다.</td></tr></tbody></table></figure>



<p></p>
<p>The post <a href="https://lycos7560.com/unity/ecs-system-concepts/39534/">ECS &#8211; System concepts</a> appeared first on <a href="https://lycos7560.com">어제와 내일의 나 그 사이의 이야기</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://lycos7560.com/unity/ecs-system-concepts/39534/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>ECS – Component concepts (3)</title>
		<link>https://lycos7560.com/unity/ecs-component-concepts-3/39523/</link>
					<comments>https://lycos7560.com/unity/ecs-component-concepts-3/39523/#respond</comments>
		
		<dc:creator><![CDATA[lycos7560]]></dc:creator>
		<pubDate>Sun, 02 Feb 2025 11:27:51 +0000</pubDate>
				<category><![CDATA[Unity]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[Chunk components]]></category>
		<category><![CDATA[Cleanup components]]></category>
		<category><![CDATA[Component]]></category>
		<category><![CDATA[concept]]></category>
		<category><![CDATA[Data]]></category>
		<category><![CDATA[DOTS]]></category>
		<category><![CDATA[ECS]]></category>
		<category><![CDATA[Entity Component System]]></category>
		<category><![CDATA[job]]></category>
		<category><![CDATA[Job 시스템]]></category>
		<category><![CDATA[Managed Components]]></category>
		<category><![CDATA[Physics]]></category>
		<category><![CDATA[Sample]]></category>
		<category><![CDATA[Shared Components]]></category>
		<category><![CDATA[study]]></category>
		<category><![CDATA[Tag components]]></category>
		<category><![CDATA[Unity Physics 101]]></category>
		<category><![CDATA[Unmanaged]]></category>
		<category><![CDATA[Unmanaged components]]></category>
		<category><![CDATA[공부]]></category>
		<guid isPermaLink="false">https://lycos7560.com/?p=39523</guid>

					<description><![CDATA[<p>4. 여러 종류의 Component (6) Chunk components https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/components-chunk.html Chunk Component는 개별 엔터티(Entity)가 아닌 Chunk 단위로 데이터를 저장하는 컴포넌트 즉, Chunk 전체에 대한 공통 데이터를 저장하는 데 사용됨 1) Chunk Component의 주요 특징 https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/components-chunk-introducing.html 2) Shared Component와 차이점 특징 Shared Component Chunk Component 저장 위치 Chunk 내의 모든 Entity가 같은 값을 공유 Chunk 자체에 값이 저장됨 [&#8230;]</p>
<p>The post <a href="https://lycos7560.com/unity/ecs-component-concepts-3/39523/">ECS – Component concepts (3)</a> appeared first on <a href="https://lycos7560.com">어제와 내일의 나 그 사이의 이야기</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<figure class="wp-block-embed is-type-wp-embed"><div class="wp-block-embed__wrapper">
<blockquote class="wp-embedded-content" data-secret="IETUbvRVMC"><a href="https://lycos7560.com/unity/ecs-component-concepts-1/39513/">ECS &#8211; Component concepts (1)</a></blockquote><iframe loading="lazy" class="wp-embedded-content" sandbox="allow-scripts" security="restricted"  title="&#8220;ECS &#8211; Component concepts (1)&#8221; &#8212; 어제와 내일의 나 그 사이의 이야기" src="https://lycos7560.com/unity/ecs-component-concepts-1/39513/embed/#?secret=LruWguojbP#?secret=IETUbvRVMC" data-secret="IETUbvRVMC" width="600" height="338" frameborder="0" marginwidth="0" marginheight="0" scrolling="no"></iframe>
</div></figure>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<figure class="wp-block-embed is-type-wp-embed"><div class="wp-block-embed__wrapper">
<blockquote class="wp-embedded-content" data-secret="Yt9bIGGyDe"><a href="https://lycos7560.com/unity/ecs-component-concepts-2/39517/">ECS – Component concepts (2)</a></blockquote><iframe loading="lazy" class="wp-embedded-content" sandbox="allow-scripts" security="restricted"  title="&#8220;ECS – Component concepts (2)&#8221; &#8212; 어제와 내일의 나 그 사이의 이야기" src="https://lycos7560.com/unity/ecs-component-concepts-2/39517/embed/#?secret=xQqIeiKh6v#?secret=Yt9bIGGyDe" data-secret="Yt9bIGGyDe" width="600" height="338" frameborder="0" marginwidth="0" marginheight="0" scrolling="no"></iframe>
</div></figure>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>


				<div class="wp-block-uagb-table-of-contents uagb-toc__align-left uagb-toc__columns-1  uagb-block-16bcca86      "
					data-scroll= "1"
					data-offset= "30"
					style=""
				>
				<div class="uagb-toc__wrap">
						<div class="uagb-toc__title">
							ECS – Component concepts						</div>
																						<div class="uagb-toc__list-wrap ">
						<ol class="uagb-toc__list"><li class="uagb-toc__list"><a href="#4-여러-종류의-component" class="uagb-toc-link__trigger">4. 여러 종류의 Component</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#6-chunk-components" class="uagb-toc-link__trigger">(6) Chunk components</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#1-chunk-component의-주요-특징" class="uagb-toc-link__trigger">1) Chunk Component의 주요 특징</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#2-shared-component와-차이점" class="uagb-toc-link__trigger">2) Shared Component와 차이점</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#3-chunk-component의-사용-예시" class="uagb-toc-link__trigger">3) Chunk Component의 사용 예시</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#4-chunk-component의-장점과-단점" class="uagb-toc-link__trigger">4) Chunk Component의 장점과 단점</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#5-chunk-component-활용법" class="uagb-toc-link__trigger">5) Chunk Component 활용법</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#1-chunk-component-추가-방법" class="uagb-toc-link__trigger">1&gt; Chunk Component 추가 방법</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#2-chunk-component-조회query-방법" class="uagb-toc-link__trigger">2&gt; Chunk Component 조회(Query) 방법</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#3-chunk-component-데이터-가져오기-설정하기" class="uagb-toc-link__trigger">3&gt; Chunk Component 데이터 가져오기 &amp; 설정하기</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#4-entity를-통해-chunk-component-값-가져오기-설정하기" class="uagb-toc-link__trigger">4&gt; Entity를 통해 Chunk Component 값 가져오기 &amp; 설정하기</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#5-job에서-chunk-component-사용하기" class="uagb-toc-link__trigger">5&gt; Job에서 Chunk Component 사용하기</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#6-chunk-component-관련-주의사항" class="uagb-toc-link__trigger">6&gt; Chunk Component 관련 주의사항</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#정리" class="uagb-toc-link__trigger">정리</a></li></ul></li></ul><li class="uagb-toc__list"><a href="#7-enableable-component" class="uagb-toc-link__trigger">(7) Enableable Component</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#1-enableable-component의-특징" class="uagb-toc-link__trigger">1) Enableable Component의 특징</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#2-enableable-component-생성-방법" class="uagb-toc-link__trigger">2) Enableable Component 생성 방법</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#3-enableable-component-활성화비활성화-방법" class="uagb-toc-link__trigger">3) Enableable Component 활성화/비활성화 방법</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#4-enableable-component와-entityquery" class="uagb-toc-link__trigger">4) Enableable Component와 EntityQuery</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#5-job-system에서-enableable-component-사용하기" class="uagb-toc-link__trigger">5) Job System에서 Enableable Component 사용하기</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#6-enableable-component-관련-주의사항" class="uagb-toc-link__trigger">6) Enableable Component 관련 주의사항</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#정리" class="uagb-toc-link__trigger">정리</a></li></ul><li class="uagb-toc__list"><a href="#8-singleton-component" class="uagb-toc-link__trigger">(8) Singleton Component</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#1-singleton-component의-특징" class="uagb-toc-link__trigger">1) Singleton Component의 특징</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#2-singleton-component-생성-방법" class="uagb-toc-link__trigger">2) Singleton Component 생성 방법</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#3-singleton-component-접근-방법" class="uagb-toc-link__trigger">3) Singleton Component 접근 방법</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#4-singleton-api-비교" class="uagb-toc-link__trigger">4) Singleton API 비교</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#5-singleton-component와-systemapi" class="uagb-toc-link__trigger">5) Singleton Component와 SystemAPI</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#6-singleton-사용-시-주의점" class="uagb-toc-link__trigger">6) Singleton 사용 시 주의점</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#정리" class="uagb-toc-link__trigger">정리</a></ul></ul></ol>					</div>
									</div>
				</div>
			


<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading">4. 여러 종류의 Component</h2>



<h3 class="wp-block-heading">(6) Chunk components</h3>



<p><a href="https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/components-chunk.html" target="_blank" rel="noreferrer noopener">https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/components-chunk.html</a></p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p><strong>Chunk Component</strong>는 개별 엔터티(Entity)가 아닌 <strong>Chunk 단위로 데이터를 저장하는 컴포넌트</strong></p>



<p>즉, <strong>Chunk 전체에 대한 공통 데이터</strong>를 저장하는 데 사용됨</p>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading">1) Chunk Component의 주요 특징</h4>



<p><a href="https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/components-chunk-introducing.html" target="_blank" rel="noreferrer noopener">https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/components-chunk-introducing.html</a></p>



<ul class="wp-block-list">
<li>Chunk에 데이터를 저장 → 개별 Entity가 아닌 Chunk 자체에 속하는 데이터</li>



<li>빠른 처리 가능 → Chunk 단위로 한 번만 검사하여 성능 최적화</li>



<li>구조적 변경(Structural Change)이 발생하지 않음</li>



<li>Shared Component와 비슷하지만, 중복 제거(Deduplication) 없음</li>



<li>Unmanaged 타입만 사용 가능</li>
</ul>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading">2) Shared Component와 차이점</h4>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>특징</th><th>Shared Component</th><th>Chunk Component</th></tr></thead><tbody><tr><td><strong>저장 위치</strong></td><td>Chunk 내의 모든 Entity가 같은 값을 공유</td><td>Chunk 자체에 값이 저장됨</td></tr><tr><td><strong>값 변경 시</strong></td><td><strong>새로운 Chunk 로 이동 필요</strong> (구조적 변경 발생)</td><td><strong>Chunk  내부에서 값 변경 가능</strong> (구조적 변경 없음)</td></tr><tr><td><strong>값 공유 방식</strong></td><td>같은 값이 있는 Chunk는 <strong>중복 제거</strong>됨</td><td>각 Chunk 가 <strong>자신만의 값</strong>을 가짐</td></tr><tr><td><strong>지원 타입</strong></td><td>Managed &amp; Unmanaged 가능</td><td><strong>Unmanaged만 가능</strong></td></tr></tbody></table></figure>



<p>Chunk Component는 구조적 변경 없이 특정 Chunk의 데이터를 조작할 때 유용</p>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading"><strong>3) Chunk Component의 사용 예시</strong></h4>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p><strong>Chunk 단위로 바운딩 박스 저장</strong></p>



<ul class="wp-block-list">
<li>각 Chunk의 AABB(바운딩 박스)를 저장하여 카메라 시야 안에 있는 Chunk만 처리 가능</li>



<li>최적화 가능:각 Entity를 검사하는 대신 Chunk 단위로 필터링 가능</li>
</ul>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">using Unity.Entities;
using Unity.Mathematics;

// Chunk 단위로 AABB (Axis-Aligned Bounding Box) 저장
public struct ChunkBounds : IComponentData
{
    public float3 Min;
    public float3 Max;
}
</pre>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<p><strong>Chunk Component를 추가하는 시스템</strong></p>



<ul class="wp-block-list">
<li>Chunk 내의 모든 엔터티가 공유하는 바운딩 박스 업데이트 가능</li>



<li>Chunk가 카메라 안에 있는 경우만 Entity를 처리하도록 최적화 가능</li>
</ul>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">using Unity.Entities;
using Unity.Mathematics;

public partial struct ChunkBoundsSystem : ISystem
{
    public void OnUpdate(ref SystemState state)
    {
        foreach (var chunk in SystemAPI.QueryBuilder().WithAll&lt;ChunkBounds>().Build().GetChunks(ref state))
        {
            // 예제: 바운딩 박스를 업데이트하는 로직
            chunk.SetChunkComponentData(new ChunkBounds { Min = new float3(-1, -1, -1), Max = new float3(1, 1, 1) });
        }
    }
}
</pre>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<p><strong>특정 Chunk만 처리하는 시스템</strong></p>



<ul class="wp-block-list">
<li>Chunk  단위로 화면에 보이는지 확인 후, 보이는 Chunk 만 처리</li>



<li>Chunk  내부의 모든 Entity를 개별 검사하지 않고 빠르게 필터링 가능</li>
</ul>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">using Unity.Entities;
using UnityEngine;

public partial struct ChunkCullingSystem : ISystem
{
    public void OnUpdate(ref SystemState state)
    {
        foreach (var (chunkBounds, chunk) in SystemAPI.Query&lt;ChunkBounds>().WithChunkAccess())
        {
            if (IsChunkVisible(chunkBounds))
            {
                Debug.Log("Processing entities in visible chunk");
                // Chunk 안의 Entity 처리 로직
            }
        }
    }

    private bool IsChunkVisible(ChunkBounds bounds)
    {
        // 간단한 AABB 검사 로직 (예제)
        return bounds.Min.x &lt; 10 &amp;&amp; bounds.Max.x > -10;
    }
}
</pre>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading"><strong>4) Chunk Component의 장점과 단점</strong></h4>



<p><strong>장점</strong></p>



<ul class="wp-block-list">
<li><strong>Chunk</strong> 단위로 데이터 저장 → 빠른 필터링 가능</li>



<li>구조적 변경이 발생하지 않음 → 성능 최적화</li>



<li>엔터티 개별 검사 없이 <strong>Chunk</strong> 레벨에서 빠른 판단 가능</li>
</ul>



<p><strong>단점</strong></p>



<ul class="wp-block-list">
<li>각 <strong>Chunk</strong>마다 별도로 값을 저장하므로 메모리 사용 증가 가능</li>



<li>같은 값이라도 공유되지 않음 (Shared Component처럼 Deduplication 없음)</li>



<li>Managed 타입 사용 불가 (Unmanaged 타입만 가능)</li>
</ul>



<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading">5) Chunk Component 활용법</h4>



<p><a href="https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/components-type.html" target="_blank" rel="noreferrer noopener">https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/components-type.html</a></p>



<p>Chunk Component는 일반적인 IComponentData와는 <strong>다른 방식의 API</strong>를 사용하여 추가, 제거, 가져오기 및 설정을 수행해야함</p>



<p>특히 <strong>EntityManager의 API</strong>를 사용할 때 주의</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h5 class="wp-block-heading"><strong>1&gt; Chunk Component 추가 방법</strong></h5>



<p><strong>특정 Entity의 Chunk에 Chunk Component 추가</strong></p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">EntityManager.AddChunkComponentData&lt;ExampleChunkComp>(entity);
</pre>



<ul class="wp-block-list">
<li><strong>특정 <strong>Entity</strong>가 속한 Chunk에 Chunk Component 추가</strong></li>



<li><strong>이때, Chunk Component는 해당 <strong>Chunk</strong>의 모든 <strong><strong>Entity</strong></strong>가 공유하게 됨</strong></li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h5 class="wp-block-heading"><strong>2&gt; Chunk Component 조회(Query) 방법</strong></h5>



<p>Chunk Component를 포함하는 Chunk를 찾을 때 <code>ComponentType.ChunkComponent&lt;T&gt;</code> 사용</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">EntityQuery query = GetEntityQuery(
    typeof(ExampleComponent), 
    ComponentType.ChunkComponent&lt;ExampleChunkComp>() // Chunk Component 쿼리
);
NativeArray&lt;ArchetypeChunk> chunks = query.ToArchetypeChunkArray(Allocator.Temp);
</pre>



<ul class="wp-block-list">
<li><code>ExampleComponent</code>가 포함된 Chunk 중에서 <code>ExampleChunkComp</code>도 있는 Chunk를 찾음</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h5 class="wp-block-heading"><strong>3&gt; Chunk Component 데이터 가져오기 &amp; 설정하기</strong></h5>



<p>특정 Chunk의 Chunk Component 값 설정</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">EntityManager.SetChunkComponentData&lt;ExampleChunkComp>(
    chunks[0], 
    new ExampleChunkComp { Value = 6 }
);
</pre>



<ul class="wp-block-list">
<li>첫 번째 Chunk에 대해 <code>ExampleChunkComp</code> 값을 6으로 설정</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p>특정 Chunk의 Chunk Component 값 가져오기</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">ExampleChunkComp exampleChunkComp = EntityManager.GetChunkComponentData&lt;ExampleChunkComp>(chunks[0]);
Debug.Log(exampleChunkComp.Value); // 6 출력
</pre>



<ul class="wp-block-list">
<li><strong>첫 번째 Chunk의 <code>ExampleChunkComp</code> 값을 가져옴</strong></li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h5 class="wp-block-heading"><strong>4&gt; Entity를 통해 Chunk Component 값 가져오기 &amp; 설정하기</strong></h5>



<p>특정 Entity가 속한Chunk의 Chunk Component 값을 변경</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">EntityManager.SetChunkComponentData&lt;MyChunkComp>(
    entity, 
    new MyChunkComp { Value = 6 }
);
</pre>



<ul class="wp-block-list">
<li>특정 Entity가 속한 Chunk의 <code>MyChunkComp</code> 값을 6으로 설정</li>
</ul>



<p>특정 Entity가 속한 Chunk의 Chunk Component 값을 가져오기</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">MyChunkComp myChunkComp = EntityManager.GetChunkComponentData&lt;MyChunkComp>(entity);
Debug.Log(myChunkComp.Value); // 6 출력
</pre>



<ul class="wp-block-list">
<li>해당 Entity가 속한 Chunk의 <code>MyChunkComp</code> 값을 가져옴</li>
</ul>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h5 class="wp-block-heading"><strong>5&gt; Job에서 Chunk Component 사용하기</strong></h5>



<p><code>EntityManager</code>는 Job 내부에서 사용할 수 없으므로 <code>ComponentTypeHandle&lt;T&gt;</code>을 활용해야 한다!</p>



<p><strong>IJobChunk에서 Chunk Component 접근하기</strong></p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">struct MyJob : IJobChunk
{
    public ComponentTypeHandle&lt;ExampleChunkComponent> ExampleChunkCompHandle;

    public void Execute(ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex)
    {
        // 청크의 Chunk Component 데이터 가져오기
        ExampleChunkComponent myChunkComp = chunk.GetChunkComponentData(ExampleChunkCompHandle);

        // 청크의 Chunk Component 데이터 설정
        chunk.SetChunkComponentData(ExampleChunkCompHandle, new ExampleChunkComponent { Value = 7 });
    }
}
</pre>



<ul class="wp-block-list">
<li>IJobChunk 내부에서는 <code>chunk.GetChunkComponentData()</code>와 <code>chunk.SetChunkComponentData()</code>를 사용</li>



<li>EntityManager 없이 직접 Chunk 데이터를 조작 가능</li>
</ul>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h5 class="wp-block-heading"><strong>6&gt; Chunk Component 관련 주의사항</strong></h5>



<ul class="wp-block-list">
<li>Chunk Component는 Chunk에 속하지만, 추가/제거 시 Entity의 아키타입이 변경되어 구조적 변경(Structural Change)이 발생</li>



<li>새로 생성된 Chunk Component는 기본값(Default)으로 초기화됨</li>



<li>읽기 전용으로 사용할 경우 <code>ComponentType.ChunkComponentReadOnly&lt;T&gt;</code>를 사용하면 Job 스케줄링 최적화 가능</li>
</ul>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h5 class="wp-block-heading"><strong>정리</strong></h5>



<ul class="wp-block-list">
<li><strong>Chunk Component는 특정 Chunk 자체의 상태를 저장하는데 유용</strong></li>



<li><strong>EntityManager의 <code>AddChunkComponentData()</code>, <code>SetChunkComponentData()</code>, <code>GetChunkComponentData()</code>를 사용하여 접근</strong></li>



<li><strong>Job에서는 <code>ComponentTypeHandle&lt;T&gt;</code>을 사용하여 접근해야 함</strong></li>



<li><strong>구조적 변경이 발생하지만, 개별 Entity가 아닌 Chunk 단위의 데이터를 관리하는 데 최적</strong></li>
</ul>



<p><strong>Chunk 단위 데이터 최적화 및 효율적인 필터링에 적합</strong></p>



<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">(7) Enableable Component</h3>



<p><a href="https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/components-enableable-use.html" target="_blank" rel="noreferrer noopener">https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/components-enableable-use.html</a></p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p>Enableable Component은 <strong>Entity의 특정 컴포넌트를 활성화/비활성화할 수 있도록 하는 기능</strong></p>



<p>이는 <strong>구조적 변경(Structural Change) 없이</strong> 컴포넌트의 상태를 빠르게 변경할 수 있어, 자주 변하는 상태(State)를 다룰 때 유용</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading"><strong>1) Enableable Component의 특징</strong></h4>



<ul class="wp-block-list">
<li><code>IEnableableComponent</code>을 구현한 <code>IComponentData</code> 또는 <code>IBufferElementData</code>만 사용 가능</li>



<li>활성화/비활성화해도 <strong>아키타입(Archetype)이 변경되지 않음 → 구조적 변경 없음</strong></li>



<li><strong>값을 그대로 유지한 채 비활성화 가능</strong></li>



<li>비활성화된 컴포넌트는 <code>EntityQuery</code>에서 존재하지 않는 것처럼 동작</li>



<li><strong>멀티스레드(Job System)에서 안전하게 활성화/비활성화 가능</strong></li>



<li><strong>기본적으로 모든 Enableable Component는 활성화된 상태로 생성됨</strong></li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading"><strong>2<strong>) </strong>Enableable Component 생성 방법</strong></h4>



<p>Enableable Component를 만들려면 <strong><code>IEnableableComponent</code> 인터페이스를 구현</strong>해야 한다.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">using Unity.Entities;

public struct Health : IComponentData, IEnableableComponent
{
    public int Value;
}
</pre>



<ul class="wp-block-list">
<li><code>IEnableableComponent</code>을 추가하면 해당 컴포넌트는 활성화/비활성화 가능 </li>



<li>일반적인 <code>IComponentData</code>와 동일하게 사용 가능</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading"><strong>3<strong>) </strong>Enableable Component 활성화/비활성화 방법</strong></h4>



<p><strong>특정 Entity의 Enableable Component 상태 확인</strong></p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">bool isEnabled = EntityManager.IsComponentEnabled&lt;Health>(entity);
Debug.Log(isEnabled);  // true (기본적으로 활성화됨)
</pre>



<ul class="wp-block-list">
<li><code>IsComponentEnabled&lt;T&gt;(Entity)</code>을 사용하여 현재 컴포넌트 상태 확인 가능</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p><strong>특정 <strong>Entity</strong>의 Enableable Component 비활성화</strong></p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">EntityManager.SetComponentEnabled&lt;Health>(entity, false);
</pre>



<ul class="wp-block-list">
<li><code>SetComponentEnabled&lt;T&gt;(Entity, bool)</code>을 사용하여 컴포넌트 활성화/비활성화</li>



<li>컴포넌트를 비활성화해도 값은 그대로 유지됨</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p><strong>비활성화된 컴포넌트의 값 가져오기</strong></p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Health h = EntityManager.GetComponentData&lt;Health>(entity);
Debug.Log(h.Value);  // 비활성화 상태여도 값은 유지됨
</pre>



<ul class="wp-block-list">
<li>비활성화 상태에서도 값은 유지됨</li>



<li>단, EntityQuery에서는 존재하지 않는 것처럼 동작</li>
</ul>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading"><strong>4<strong>) </strong>Enableable Component와 EntityQuery</strong></h4>



<p>Enableable Component가 <strong>비활성화된 경우, 기본적으로 Query에서 제외</strong>된다.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">EntityQuery query = GetEntityQuery(typeof(Health), typeof(Translation));
</pre>



<ul class="wp-block-list">
<li><code>Health</code>가 비활성화된 Entity는 이 쿼리에 포함되지 않음</li>



<li><code>Health</code>가 활성화된 Entity만 결과에 포함됨</li>
</ul>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<p>Enableable Component가<strong> 비활성화된 상태에서도 포함</strong>시키는 방법</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">EntityQuery queryIgnoreEnableable = GetEntityQuery(new EntityQueryDesc
{
    All = new ComponentType[] { typeof(Health), typeof(Translation) },
    Options = EntityQueryOptions.IgnoreComponentEnabledState
});
</pre>



<ul class="wp-block-list">
<li> <code>EntityQueryOptions.IgnoreComponentEnabledState</code>를 사용하면 <strong>비활성화된 컴포넌트도 포함</strong></li>



<li>구조적 변경이 발생한 경우에만 동작이 변경되므로 <strong>더 효율적</strong></li>
</ul>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading"><strong>5<strong>) </strong>Job System에서 Enableable Component 사용하기</strong></h4>



<p>Enableable Component는 <strong>멀티스레드(Job System)에서 안전하게 활성화/비활성화 가능</strong>하다.</p>



<p><strong>Job 내부에서 Enableable Component 사용하기</strong></p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">struct EnableableJob : IJobEntity
{
    public ComponentLookup&lt;Health> HealthLookup;

    public void Execute(Entity entity)
    {
        if (HealthLookup.IsComponentEnabled(entity))
        {
            // Health가 활성화된 경우
            Health h = HealthLookup[entity];
            h.Value += 10;
            HealthLookup[entity] = h;
        }
    }
}
</pre>



<ul class="wp-block-list">
<li><code>ComponentLookup&lt;T&gt;</code>를 사용하여 <strong>Job 내부에서 안전하게 활성화/비활성화 확인 가능</strong></li>



<li><code>IsComponentEnabled&lt;T&gt;(Entity)</code>를 활용하여 조건부 실행 가능</li>
</ul>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading"><strong>6<strong>) </strong>Enableable Component 관련 주의사항</strong></h4>



<ul class="wp-block-list">
<li><strong>비활성화된 컴포넌트는 <code>EntityQuery</code>에서 기본적으로 존재하지 않는 것으로 간주됨</strong></li>



<li><strong>멀티스레드 환경에서 다른 Job이 같은 컴포넌트를 변경할 경우, Race Condition 발생 가능</strong> (경쟁)</li>



<li><strong><code>EntityQueryOptions.IgnoreComponentEnabledState</code>를 사용하면 비활성화 상태도 포함 가능</strong></li>



<li><strong>구조적 변경을 피하기 위해 자주 변경되는 상태(State)는 Enableable Component를 활용하는 것이 유리</strong></li>
</ul>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading"><strong>정리</strong></h4>



<ul class="wp-block-list">
<li><strong>Enableable Component는 <code>IEnableableComponent</code>을 구현한 <code>IComponentData</code> 또는 <code>IBufferElementData</code>만 사용 가능</strong></li>



<li><strong>비활성화해도 값은 유지되지만, 기본적으로 EntityQuery에서 제외됨</strong></li>



<li><strong><code>IsComponentEnabled&lt;T&gt;(Entity)</code>와 <code>SetComponentEnabled&lt;T&gt;(Entity, bool)</code>을 사용하여 활성화/비활성화 가능</strong></li>



<li><strong>구조적 변경 없이 상태(State)를 관리할 수 있어 성능 최적화에 유리</strong></li>



<li><strong>Job System에서도 <code>ComponentLookup&lt;T&gt;</code>를 사용하여 안전하게 활용 가능</strong></li>
</ul>



<p><strong>상태 변화가 자주 발생하는 경우 Enableable Component를 사용하면 최적화에 도움 </strong></p>



<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">(8) Singleton Component</h3>



<p><a href="https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/components-singleton.html" target="_blank" rel="noreferrer noopener">https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/components-singleton.html</a></p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p><strong>Singleton Component</strong>는 <strong>특정 월드(World)에서 오직 하나의 인스턴스만 존재하는 컴포넌트</strong>를 의미한다.</p>



<p>즉, <strong>한 개의 Entity만 특정 컴포넌트를 가지고 있으면 Singleton Component로 간주</strong>된다.</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading"><strong>1) Singleton Component의 특징</strong></h4>



<ul class="wp-block-list">
<li><strong>특정 월드에서 단 하나의 인스턴스만 존재</strong></li>



<li><strong>다른 Entity에 같은 컴포넌트를 추가하면 더 이상 Singleton이 아님</strong></li>



<li><strong>다른 월드에 존재하는 동일한 컴포넌트는 별도로 관리됨 (다른 월드의 Singleton 상태에 영향 없음)</strong></li>



<li><strong>System에서 쉽게 접근할 수 있도록 Singleton 전용 API 제공</strong></li>



<li><strong>서버 기반 아키텍처에서 특정 클라이언트 데이터를 관리할 때 유용</strong></li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading"><strong>2<strong>) </strong>Singleton Component 생성 방법</strong></h4>



<p>Singleton Component는 <strong>일반적인 IComponentData와 동일하게 생성</strong>하지만, <strong>오직 하나의 <strong>Entity</strong>만 해당 컴포넌트를 가지도록 관리</strong>해야 한다.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">using Unity.Entities;

public struct GameSettings : IComponentData
{
    public float Gravity;
}
</pre>



<ul class="wp-block-list">
<li>일반적인 <code>IComponentData</code>처럼 정의 가능</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p><strong>Singleton Component 추가</strong></p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">EntityManager.CreateSingleton&lt;GameSettings>();
</pre>



<ul class="wp-block-list">
<li><code>CreateSingleton&lt;T&gt;()</code>을 사용하면 자동으로 <strong>단 하나의 Entity에만 추가</strong></li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading"><strong>3) Singleton Component 접근 방법</strong></h4>



<p>Singleton API를 사용하면 특정 Entity를 직접 찾지 않아도 단일 인스턴스를 가져올 수 있음.</p>



<p><strong>Singleton Entity 가져오기</strong></p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Entity singletonEntity = EntityManager.GetSingletonEntity&lt;GameSettings>();
</pre>



<ul class="wp-block-list">
<li><code>GetSingletonEntity&lt;T&gt;()</code>을 사용하면 Singleton을 가진 엔터티 직접 가져오기 가능</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p><strong>Singleton 값 가져오기</strong></p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">GameSettings settings = EntityManager.GetSingleton&lt;GameSettings>();
Debug.Log(settings.Gravity);
</pre>



<ul class="wp-block-list">
<li> <code>GetSingleton&lt;T&gt;()</code>을 사용하면 컴포넌트 데이터 직접 가져오기 가능</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p><strong>Singleton 값 변경하기</strong></p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">GameSettings settings = EntityManager.GetSingleton&lt;GameSettings>();
settings.Gravity = 9.8f;
EntityManager.SetSingleton(settings);
</pre>



<ul class="wp-block-list">
<li> <code>SetSingleton&lt;T&gt;()</code>을 사용하여 값 수정 가능</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p><strong> Singleton 여부 확인</strong></p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">bool exists = EntityManager.HasSingleton&lt;GameSettings>();
Debug.Log(exists);  // true or false
</pre>



<ul class="wp-block-list">
<li> <code>HasSingleton&lt;T&gt;()</code>을 사용하여 Singleton이 존재하는지 확인 가능</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading"><strong>4) Singleton API 비교</strong></h4>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>API</th><th>설명</th></tr></thead><tbody><tr><td><code>CreateSingleton&lt;T&gt;()</code></td><td>Singleton Component 생성</td></tr><tr><td><code>GetSingletonEntity&lt;T&gt;()</code></td><td>Singleton을 포함한 엔터티 가져오기</td></tr><tr><td><code>GetSingleton&lt;T&gt;()</code></td><td>Singleton 데이터 가져오기</td></tr><tr><td><code>SetSingleton&lt;T&gt;()</code></td><td>Singleton 데이터 설정하기</td></tr><tr><td><code>HasSingleton&lt;T&gt;()</code></td><td>Singleton이 존재하는지 확인</td></tr><tr><td><code>TryGetSingleton&lt;T&gt;(out T value)</code></td><td>Singleton을 가져오되, 없을 경우 안전하게 처리</td></tr><tr><td><code>GetSingletonRW&lt;T&gt;()</code></td><td>Read/Write 가능한 참조 반환 (주의 필요)</td></tr></tbody></table></figure>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading"><strong>5) Singleton Component와 SystemAPI</strong></h4>



<p>Singleton Component는 <strong>SystemAPI에서도 편리하게 접근 가능</strong></p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p><strong>SystemAPI를 이용한 Singleton 접근</strong></p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">public partial struct GameSystem : ISystem
{
    public void OnUpdate(ref SystemState state)
    {
        GameSettings settings = SystemAPI.GetSingleton&lt;GameSettings>();
        Debug.Log(settings.Gravity);
    }
}
</pre>



<ul class="wp-block-list">
<li><code>SystemAPI.GetSingleton&lt;T&gt;()</code>을 사용하면 더 간결한 코드 가능</li>
</ul>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading"><strong>6) Singleton 사용 시 주의점</strong></h4>



<p><strong>Singleton API는 자동으로 Job Dependency를 해결하지 않음</strong></p>



<ul class="wp-block-list">
<li><code>EntityManager.GetComponentData&lt;T&gt;()</code>는 자동으로 Job을 완료하지만, <code>EntityManager.GetSingleton&lt;T&gt;()</code>은 <strong>자동으로 Job을 완료하지 않음</strong></li>



<li><strong>Job이 실행 중일 때 Singleton에 접근하면 오류 발생 가능</strong></li>



<li>해결 방법: <code>EntityManager.CompleteDependencyBeforeRO()</code> 또는 <code>CompleteDependencyBeforeRW()</code> 사용</li>
</ul>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">EntityManager.CompleteDependencyBeforeRO&lt;GameSettings>();  
GameSettings settings = EntityManager.GetSingleton&lt;GameSettings>();
</pre>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<p> <strong><code>GetSingletonRW&lt;T&gt;()</code> 사용 시 주의</strong></p>



<ul class="wp-block-list">
<li><code>GetSingletonRW&lt;T&gt;()</code>은 <strong>참조(Reference)를 반환</strong>하므로,<br><strong>Job과 동시에 접근하면 Race Condition 발생 가능</strong></li>



<li><strong>안전한 사용 방법</strong>
<ul class="wp-block-list">
<li><strong>네이티브 컨테이너(NativeContainer) 내부 데이터만 수정</strong></li>



<li><strong>Jobs Debugger에서 오류가 없는지 반드시 확인</strong></li>
</ul>
</li>
</ul>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading"><strong>정리</strong></h4>



<ul class="wp-block-list">
<li><strong>Singleton Component는 특정 월드에서 단 하나만 존재하는 컴포넌트</strong></li>



<li><strong>SystemAPI 및 EntityManager를 통해 쉽게 접근 가능</strong></li>



<li><strong>구조적 변경 없이 특정 데이터를 쉽게 관리할 수 있음</strong></li>



<li><strong>Job System과 함께 사용할 때는 Dependency 관리 필요</strong></li>



<li><strong>서버 기반 시스템이나 특정 설정 값을 관리할 때 매우 유용</strong></li>
</ul>



<p><strong><code>GameSettings</code>, <code>PlayerController</code>, <code>ServerTimestamp</code> 등의 전역 설정을 관리할 때 Singleton을 사용하면 편리</strong></p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th><strong>Namespace</strong></th><th><strong>Method</strong></th></tr></thead><tbody><tr><td><strong><a href="https://docs.unity3d.com/Packages/com.unity.entities@1.3/api/Unity.Entities.EntityManager.html">Entity<wbr>Manager</a></strong></td><td><a href="https://docs.unity3d.com/Packages/com.unity.entities@1.3/api/Unity.Entities.EntityManager.CreateSingleton.html">Create<wbr>Singleton</a></td></tr><tr><td><strong><a href="https://docs.unity3d.com/Packages/com.unity.entities@1.3/api/Unity.Entities.EntityQuery.html">Entity<wbr>Query</a></strong></td><td><a href="https://docs.unity3d.com/Packages/com.unity.entities@1.3/api/Unity.Entities.EntityQuery.GetSingletonEntity.html#Unity_Entities_EntityQuery_GetSingletonEntity">Get<wbr>Singleton<wbr>Entity</a></td></tr><tr><td></td><td><a href="https://docs.unity3d.com/Packages/com.unity.entities@1.3/api/Unity.Entities.EntityQuery.GetSingleton.html">Get<wbr>Singleton</a></td></tr><tr><td></td><td><a href="https://docs.unity3d.com/Packages/com.unity.entities@1.3/api/Unity.Entities.EntityQuery.GetSingletonRW.html">Get<wbr>Singleton<wbr>RW</a></td></tr><tr><td></td><td><a href="https://docs.unity3d.com/Packages/com.unity.entities@1.3/api/Unity.Entities.EntityQuery.TryGetSingleton.html">Try<wbr>Get<wbr>Singleton</a></td></tr><tr><td></td><td><a href="https://docs.unity3d.com/Packages/com.unity.entities@1.3/api/Unity.Entities.EntityQuery.HasSingleton.html">Has<wbr>Singleton</a></td></tr><tr><td></td><td><a href="https://docs.unity3d.com/Packages/com.unity.entities@1.3/api/Unity.Entities.EntityQuery.TryGetSingletonBuffer.html">Try<wbr>Get<wbr>Singleton<wbr>Buffer</a></td></tr><tr><td></td><td><a href="https://docs.unity3d.com/Packages/com.unity.entities@1.3/api/Unity.Entities.EntityQuery.TryGetSingletonEntity.html">Try<wbr>Get<wbr>Singleton<wbr>Entity</a></td></tr><tr><td></td><td><a href="https://docs.unity3d.com/Packages/com.unity.entities@1.3/api/Unity.Entities.EntityQuery.GetSingletonBuffer.html">Get<wbr>Singleton<wbr>Buffer</a></td></tr><tr><td></td><td><a href="https://docs.unity3d.com/Packages/com.unity.entities@1.3/api/Unity.Entities.EntityQuery.SetSingleton.html">Set<wbr>Singleton</a></td></tr><tr><td><strong><a href="https://docs.unity3d.com/Packages/com.unity.entities@1.3/api/Unity.Entities.SystemAPI.html">System<wbr>API</a></strong></td><td><a href="https://docs.unity3d.com/Packages/com.unity.entities@1.3/api/Unity.Entities.SystemAPI.GetSingletonEntity.html">Get<wbr>Singleton<wbr>Entity</a></td></tr><tr><td></td><td><a href="https://docs.unity3d.com/Packages/com.unity.entities@1.3/api/Unity.Entities.SystemAPI.GetSingleton.html">Get<wbr>Singleton</a></td></tr><tr><td></td><td><a href="https://docs.unity3d.com/Packages/com.unity.entities@1.3/api/Unity.Entities.SystemAPI.GetSingletonRW.html">Get<wbr>Singleton<wbr>RW</a></td></tr><tr><td></td><td><a href="https://docs.unity3d.com/Packages/com.unity.entities@1.3/api/Unity.Entities.SystemAPI.TryGetSingleton.html">Try<wbr>Get<wbr>Singleton</a></td></tr><tr><td></td><td><a href="https://docs.unity3d.com/Packages/com.unity.entities@1.3/api/Unity.Entities.SystemAPI.HasSingleton.html">Has<wbr>Singleton</a></td></tr><tr><td></td><td><a href="https://docs.unity3d.com/Packages/com.unity.entities@1.3/api/Unity.Entities.SystemAPI.TryGetSingletonBuffer.html">Try<wbr>Get<wbr>Singleton<wbr>Buffer</a></td></tr><tr><td></td><td><a href="https://docs.unity3d.com/Packages/com.unity.entities@1.3/api/Unity.Entities.SystemAPI.TryGetSingletonEntity.html">Try<wbr>Get<wbr>Singleton<wbr>Entity</a></td></tr><tr><td></td><td><a href="https://docs.unity3d.com/Packages/com.unity.entities@1.3/api/Unity.Entities.SystemAPI.GetSingletonBuffer.html">Get<wbr>Singleton<wbr>Buffer</a></td></tr><tr><td></td><td><a href="https://docs.unity3d.com/Packages/com.unity.entities@1.3/api/Unity.Entities.SystemAPI.SetSingleton.html">Set<wbr>Singleton</a></td></tr></tbody></table></figure>



<p></p>
<p>The post <a href="https://lycos7560.com/unity/ecs-component-concepts-3/39523/">ECS – Component concepts (3)</a> appeared first on <a href="https://lycos7560.com">어제와 내일의 나 그 사이의 이야기</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://lycos7560.com/unity/ecs-component-concepts-3/39523/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>ECS – Component concepts (2)</title>
		<link>https://lycos7560.com/unity/ecs-component-concepts-2/39517/</link>
					<comments>https://lycos7560.com/unity/ecs-component-concepts-2/39517/#comments</comments>
		
		<dc:creator><![CDATA[lycos7560]]></dc:creator>
		<pubDate>Sat, 01 Feb 2025 18:18:56 +0000</pubDate>
				<category><![CDATA[Unity]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[Cleanup components]]></category>
		<category><![CDATA[Component]]></category>
		<category><![CDATA[concept]]></category>
		<category><![CDATA[Data]]></category>
		<category><![CDATA[DOTS]]></category>
		<category><![CDATA[ECS]]></category>
		<category><![CDATA[Entity Component System]]></category>
		<category><![CDATA[job]]></category>
		<category><![CDATA[Job 시스템]]></category>
		<category><![CDATA[Managed Components]]></category>
		<category><![CDATA[Physics]]></category>
		<category><![CDATA[Sample]]></category>
		<category><![CDATA[Shared Components]]></category>
		<category><![CDATA[study]]></category>
		<category><![CDATA[Tag components]]></category>
		<category><![CDATA[Unity Physics 101]]></category>
		<category><![CDATA[Unmanaged]]></category>
		<category><![CDATA[Unmanaged components]]></category>
		<category><![CDATA[공부]]></category>
		<guid isPermaLink="false">https://lycos7560.com/?p=39517</guid>

					<description><![CDATA[<p>4. 여러 종류의 Component (5) Dynamic buffer components https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/components-buffer.html 1) Dynamic buffer 구성 요소 소개 https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/components-buffer-introducing.html Dynamic buffer components는 관리되지 않는 구조체들의 크기 조절이 가능한 배열처럼 작동하는 컴포넌트 예를 들어 Entity가 이동할 경로점(waypoint) 위치들과 같은 배열 데이터를 저장하는 데 사용할 수 있음 Dynamic buffer의 구성 요소 Dynamic buffer는 데이터와 함께 길이(Length), 용량(Capacity), 내부 포인터(pointer)를 저장 [&#8230;]</p>
<p>The post <a href="https://lycos7560.com/unity/ecs-component-concepts-2/39517/">ECS – Component concepts (2)</a> appeared first on <a href="https://lycos7560.com">어제와 내일의 나 그 사이의 이야기</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<figure class="wp-block-embed is-type-wp-embed"><div class="wp-block-embed__wrapper">
<blockquote class="wp-embedded-content" data-secret="R9N8TQQK3t"><a href="https://lycos7560.com/unity/ecs-component-concepts-1/39513/">ECS &#8211; Component concepts (1)</a></blockquote><iframe loading="lazy" class="wp-embedded-content" sandbox="allow-scripts" security="restricted"  title="&#8220;ECS &#8211; Component concepts (1)&#8221; &#8212; 어제와 내일의 나 그 사이의 이야기" src="https://lycos7560.com/unity/ecs-component-concepts-1/39513/embed/#?secret=N7zRuNy8Fh#?secret=R9N8TQQK3t" data-secret="R9N8TQQK3t" width="600" height="338" frameborder="0" marginwidth="0" marginheight="0" scrolling="no"></iframe>
</div></figure>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>


				<div class="wp-block-uagb-table-of-contents uagb-toc__align-left uagb-toc__columns-1  uagb-block-16bcca86      "
					data-scroll= "1"
					data-offset= "30"
					style=""
				>
				<div class="uagb-toc__wrap">
						<div class="uagb-toc__title">
							ECS – Component concepts						</div>
																						<div class="uagb-toc__list-wrap ">
						<ol class="uagb-toc__list"><li class="uagb-toc__list"><a href="#4-여러-종류의-component" class="uagb-toc-link__trigger">4. 여러 종류의 Component</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#5-dynamic-buffer-components" class="uagb-toc-link__trigger">(5) Dynamic buffer components</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#1-dynamic-buffer-구성-요소-소개" class="uagb-toc-link__trigger">1) Dynamic buffer 구성 요소 소개</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#dynamic-buffer의-구성-요소" class="uagb-toc-link__trigger">Dynamic buffer의 구성 요소</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#dynamic-buffer-용량" class="uagb-toc-link__trigger">Dynamic buffer 용량</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#구조적-변경structural-changes" class="uagb-toc-link__trigger">구조적 변경(Structural Changes)</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#네이티브-컨테이너와의-비교" class="uagb-toc-link__trigger">네이티브 컨테이너와의 비교</a></li></ul><li class="uagb-toc__list"><a href="#2-dynamic-buffer-컴포넌트-생성-및-사용-방법" class="uagb-toc-link__trigger">2) Dynamic buffer 컴포넌트 생성 및 사용 방법</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#ibufferelementdata를-상속한-구조체-생성" class="uagb-toc-link__trigger">IBufferElementData를 상속한 구조체 생성</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#entity에-dynamic-buffer-추가하기" class="uagb-toc-link__trigger">Entity에 Dynamic buffer 추가하기</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#dynamic-buffer-읽기" class="uagb-toc-link__trigger">Dynamic buffer 읽기</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#동적-버퍼에서-요소-제거" class="uagb-toc-link__trigger">동적 버퍼에서 요소 제거</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#동적-버퍼의-사용-예시" class="uagb-toc-link__trigger">동적 버퍼의 사용 예시</a></li></ul><li class="uagb-toc__list"><a href="#3-dynamic-buffer-용량-설정" class="uagb-toc-link__trigger">3) Dynamic Buffer 용량 설정</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#dynamic-buffer의-초기-용량-default-capacity" class="uagb-toc-link__trigger">Dynamic Buffer의 초기 용량 (Default Capacity)</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#dynamic-buffer의-메모리-저장-방식" class="uagb-toc-link__trigger">Dynamic Buffer의 메모리 저장 방식</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#dynamic-buffer의-내부-용량-설정-internalbuffercapacity" class="uagb-toc-link__trigger">Dynamic Buffer의 내부 용량 설정 (InternalBufferCapacity)</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#동적으로-용량-조절-ensurecapacity-trimexcess" class="uagb-toc-link__trigger">동적으로 용량 조절 (EnsureCapacity &amp; TrimExcess)</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#dynamic-buffer보다-더-나은-대안" class="uagb-toc-link__trigger">Dynamic Buffer보다 더 나은 대안?</a></li></ul><li class="uagb-toc__list"><a href="#4-chunk-내-dynamic-buffer-접근-방법" class="uagb-toc-link__trigger">4) Chunk 내 Dynamic Buffer 접근 방법</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#5-여러-entity에서-동일한-dynamic-buffer-재사용" class="uagb-toc-link__trigger">5) 여러 Entity에서 동일한 Dynamic Buffer 재사용</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#6-job에서-dynamicbuffer-접근-및-활용" class="uagb-toc-link__trigger">6) Job에서 DynamicBuffer 접근 및 활용</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#7-entitycommandbufferecb에서-dynamicbuffer-수정" class="uagb-toc-link__trigger">7) EntityCommandBuffer(ECB)에서 DynamicBuffer 수정</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#8-reinterpret을-이용한-버퍼-변환" class="uagb-toc-link__trigger">8) Reinterpret()을 이용한 버퍼 변환</a></ul></ul></ol>					</div>
									</div>
				</div>
			


<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading">4. 여러 종류의 Component</h2>



<h3 class="wp-block-heading">(5) Dynamic buffer components</h3>



<p><a href="https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/components-buffer.html" target="_blank" rel="noreferrer noopener">https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/components-buffer.html</a></p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading">1) Dynamic buffer 구성 요소 소개</h4>



<p><a href="https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/components-buffer-introducing.html" target="_blank" rel="noreferrer noopener">https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/components-buffer-introducing.html</a></p>



<p>Dynamic buffer components는 관리되지 않는 구조체들의 크기 조절이 가능한 배열처럼 작동하는 컴포넌트</p>



<p>예를 들어 Entity가 이동할 경로점(waypoint) 위치들과 같은 배열 데이터를 저장하는 데 사용할 수 있음</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h5 class="wp-block-heading"><strong>Dynamic buffer의 구성 요소</strong></h5>



<p>Dynamic buffer는 데이터와 함께 <strong>길이(Length)</strong>, <strong>용량(Capacity)</strong>, 내부 포인터(pointer)를 저장</p>



<ol class="wp-block-list">
<li> <strong>길이 (Length)</strong>: <br>buffer 에 저장된 요소의 개수, 처음에는 0이며, 값을 추가할 때마다 증가</li>



<li><strong>용량 (Capacity)</strong>: <br>buffer 에 저장할 수 있는 요소의 최대 개수<br>처음에는 내부 버퍼 용량과 동일하게 시작하고, 용량을 설정하여 버퍼 크기를 조정</li>



<li><strong>포인터 (Pointer)</strong>: <br>Dynamic buffer 데이터의 위치를 나타냄<br>처음에는 데이터가 Entity의 Chunk 내에 있을 때 null로 설정되며, <br>Unity가 데이터를 Chunk 밖으로 이동시키면 포인터가 새 배열을 가리키도록 설정됩니다.</li>
</ol>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h5 class="wp-block-heading"><strong>Dynamic buffer 용량</strong></h5>



<p><strong>Dynamic buffer</strong>의 초기 용량은 버퍼가 저장하는 타입에 의해 정의됩니다. </p>



<p>기본적으로 용량은 128바이트에 맞게 설정됩니다. </p>



<p>만약 더 큰 용량이 필요하면 <a href="https://docs.unity3d.com/Packages/com.unity.entities@1.3/api/Unity.Entities.InternalBufferCapacityAttribute.html" target="_blank" rel="noreferrer noopener"><code>InternalBufferCapacity</code></a> 속성을 사용하여 커스터마이즈할 수 있습니다.</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h5 class="wp-block-heading">구조적 변경(Structural Changes)</h5>



<p><strong>구조적 변경(Structural Changes) </strong>: <strong>Entity의 생성, 삭제, 또는 컴포넌트 추가/제거와 같은 작업이 수행될 때</strong> Unity의 <strong>ECS 시스템 내부에서 구조적 변화가 발생함</strong></p>



<p><strong>Structural Changes</strong>는 <strong>Dynamic buffer</strong>가 참조하는 배열을 파괴하거나 이동시킬 수 있기 때문에, 구조적 변화 후에는 <strong>Dynamic buffer</strong>에 대한 Handle이 무효화됩니다. </p>



<p><strong>Structural Changes</strong> 후에는 <strong>Dynamic buffer</strong>를 다시 가져와야 합니다. </p>



<p>예를 들어:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">public void DynamicBufferExample(Entity e)
{
    // MyElement 타입의 동적 버퍼를 가져옵니다.
    DynamicBuffer&lt;MyElement> myBuff = EntityManager.GetBuffer&lt;MyElement>(e);

    // 이 Structural Changes는 이전에 얻은 DynamicBuffer를 무효화시킵니다.
    EntityManager.CreateEntity();

    // 안전 검사로 인해 버퍼에 대한 읽기/쓰기 작업에서 예외가 발생합니다.
    var x = myBuff[0];

    // Structural Changes 후 동적 버퍼를 다시 가져옵니다.
    myBuff = EntityManager.GetBuffer&lt;MyElement>(e);
    var y = myBuff[0];
}
</pre>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h5 class="wp-block-heading">네이티브 컨테이너와의 비교</h5>



<p><strong>Dynamic buffer</strong>는 네이티브 컨테이너가 컴포넌트에서 사용하는 작업 스케줄링 제한이 없으므로, 가능한 경우 코드에서 <strong>Dynamic buffer</strong>를 사용하는 것이 더 나은 선택입니다. </p>



<p>또한, <strong>Dynamic buffer</strong>는 Chunk 내에 인라인으로 저장될 수 있어 메모리 대역폭 사용을 줄이는 데 도움을 줍니다.</p>



<p>일반적으로, 여러 Entity가 동일한 컬렉션을 필요로 한다면 <strong>Dynamic buffer</strong>를 사용하는 것이 좋습니다. </p>



<p>만약 하나의 Entity만 필요하다면 네이티브 컨테이너를 가진 싱글톤 컴포넌트로 처리하는 것이 좋을 수 있습니다.</p>



<ul class="wp-block-list">
<li><strong>Dynamic buffer</strong>는 <strong>작업 스케줄링 제한이 없고</strong>, <strong>메모리 사용 측면에서도 효율적</strong></li>
</ul>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>비교 항목</th><th>동적 버퍼 (<code>DynamicBuffer&lt;T&gt;</code>)</th><th><code>NativeContainer</code> (<code>NativeArray&lt;T&gt;</code>, <code>NativeList&lt;T&gt;</code>)</th></tr></thead><tbody><tr><td><strong>ECS와의 연동</strong></td><td><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Entity에 직접 부착 가능</td><td><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/274c.png" alt="❌" class="wp-smiley" style="height: 1em; max-height: 1em;" /> ECS Entity와 직접 연결 불가</td></tr><tr><td><strong>Job 시스템 사용</strong></td><td><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 가능 (<code>ReadOnly</code> 또는 <code>ParallelForEach</code> 가능)</td><td><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 가능 (<code>[NativeContainer]</code> 필요)</td></tr><tr><td><strong>메모리 위치</strong></td><td><strong>Chunk 내 저장 (최적화됨)</strong> → 크기 초과 시 자동 이동</td><td><strong><strong>Chunk</strong> 외부 할당</strong></td></tr><tr><td><strong>구조적 변화 영향</strong></td><td><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f6a8.png" alt="🚨" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 구조적 변화 시 무효화됨 → <strong>재할당 필요</strong></td><td><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 영향 없음</td></tr></tbody></table></figure>



<ul class="wp-block-list">
<li>여러 개의 엔티티가 배열 데이터를 가질 필요가 있다면 <code>DynamicBuffer&lt;T&gt;</code>를 사용하는 것이 좋음</li>



<li>싱글톤 데이터처럼 전역적으로 관리할 경우 <code>NativeContainer</code>를 사용하는 것이 유리함</li>
</ul>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading">2) Dynamic buffer 컴포넌트 생성 및 사용 방법</h4>



<p><a href="https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/components-buffer-create.html" target="_blank" rel="noreferrer noopener">https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/components-buffer-create.html</a></p>



<h5 class="wp-block-heading"><strong>IBufferElementData를 상속한 구조체 생성</strong></h5>



<p>Dynamic buffer의 개별 요소를 정의하는 구조체를 만들고, <code>IBufferElementData</code>를 상속받아야 합니다.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">using Unity.Entities;

// Dynamic buffer의 요소가 되는 구조체 정의
// 기본적으로 16개의 요소를 저장할 수 있도록 설정
// 만약 16개를 초과하면 Unity가 자동으로 Chunk 외부 메모리로 확장
[InternalBufferCapacity(16)]
public struct ExampleBufferComponent : IBufferElementData
{
    public int Value; // 버퍼 요소 값
}
</pre>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h5 class="wp-block-heading"><strong>Entity에 Dynamic buffer 추가하기</strong></h5>



<p><code>DynamicBuffer&lt;T&gt;</code>를 사용하여 엔티티에 동적 버퍼를 추가하고, 값을 삽입</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Entity entity = EntityManager.CreateEntity();
DynamicBuffer&lt;ExampleBufferComponent> buffer = EntityManager.AddBuffer&lt;ExampleBufferComponent>(entity);

// 데이터 추가
buffer.Add(new ExampleBufferComponent { Value = 10 });
buffer.Add(new ExampleBufferComponent { Value = 20 });

// 현재 길이 확인 (2개 요소가 들어있음)
UnityEngine.Debug.Log("Buffer Length: " + buffer.Length);
</pre>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h5 class="wp-block-heading"><strong><strong><strong>Dynamic buffer</strong> 읽기</strong></strong></h5>



<p><strong>Dynamic buffer</strong>는 배열처럼 접근할 수 있습니다.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">DynamicBuffer&lt;ExampleBufferComponent> buffer = EntityManager.GetBuffer&lt;ExampleBufferComponent>(entity);

for (int i = 0; i &lt; buffer.Length; i++)
{
    UnityEngine.Debug.Log("Buffer Element " + i + ": " + buffer[i].Value);
}
</pre>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h5 class="wp-block-heading"><strong>동적 버퍼에서 요소 제거</strong></h5>



<p>요소를 제거할 때는 <code>RemoveAt()</code> 또는 <code>Clear()</code>를 사용할 수 있습니다.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">buffer.RemoveAt(0); // 첫 번째 요소 제거
buffer.Clear(); // 모든 요소 제거
</pre>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h5 class="wp-block-heading"><strong>동적 버퍼의 사용 예시</strong></h5>



<ul class="wp-block-list">
<li><strong>웨이포인트(Waypoints) 저장하기</strong></li>
</ul>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">using Unity.Entities;
using Unity.Mathematics;

// 웨이포인트를 저장하는 동적 버퍼
[InternalBufferCapacity(4)]
public struct WaypointBuffer : IBufferElementData
{
    public float3 Position; // 좌표 정보
}
</pre>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">// 엔티티에 웨이포인트 버퍼 추가
Entity entity = EntityManager.CreateEntity();
DynamicBuffer&lt;WaypointBuffer> waypoints = EntityManager.AddBuffer&lt;WaypointBuffer>(entity);

// 웨이포인트 추가
waypoints.Add(new WaypointBuffer { Position = new float3(0, 0, 0) });
waypoints.Add(new WaypointBuffer { Position = new float3(1, 0, 0) });
waypoints.Add(new WaypointBuffer { Position = new float3(2, 0, 0) });
</pre>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<ul class="wp-block-list">
<li><strong>시스템에서 동적 버퍼 활용</strong></li>
</ul>



<p><strong><code>ISystem</code>을 활용하여 동적 버퍼를 검색하고 처리하는 시스템을 구현</strong></p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">using Unity.Entities;
using Unity.Burst;
using Unity.Collections;
using Unity.Mathematics;

[BurstCompile]
public partial struct WaypointSystem : ISystem
{
    public void OnUpdate(ref SystemState state)
    {
        foreach (var (waypoints, entity) in SystemAPI.Query&lt;DynamicBuffer&lt;WaypointBuffer>>().WithEntityAccess())
        {
            foreach (var waypoint in waypoints)
            {
                UnityEngine.Debug.Log("Waypoint Position: " + waypoint.Position);
            }
        }
    }
}
</pre>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading">3) Dynamic Buffer 용량 설정</h4>



<p><a href="https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/components-buffer-set-capacity.html" target="_blank" rel="noreferrer noopener">https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/components-buffer-set-capacity.html</a></p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h5 class="wp-block-heading"><strong>Dynamic Buffer의 초기 용량 (Default Capacity)</strong></h5>



<ul class="wp-block-list">
<li>Dynamic Buffer는 <strong>초기 용량</strong>을 가지고 있으며, 기본적으로 <strong>128바이트</strong> 크기로 설정</li>



<li>128바이트는 <code>TypeManager.DefaultBufferCapacityNumerator</code>를 통해 결정</li>



<li>예를 들어, <code>int</code>(4바이트) 값을 저장하는 버퍼라면 <strong>기본적으로 32개의 요소</strong>를 저장할 수 있음</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h5 class="wp-block-heading"><strong><strong>Dynamic Buffer</strong>의 메모리 저장 방식</strong></h5>



<p>초기 상태</p>



<ul class="wp-block-list">
<li>Entity의 <strong>아키타입 청크(Archetype Chunk)</strong> 내부에 저장됨.</li>



<li>일반적인 <code>IComponentData</code>처럼 빠르게 접근 가능.</li>
</ul>



<p>버퍼 크기가 초기 용량을 초과할 경우</p>



<ul class="wp-block-list">
<li><strong>Chunk 외부에 새로운 메모리 공간을 할당하고 데이터를 복사함.</strong></li>



<li>이후 해당 버퍼는 <strong>항상 <strong>Chunk </strong>외부 메모리를 사용</strong>하게 됨.</li>



<li><strong>단점:</strong>
<ul class="wp-block-list">
<li>메모리 할당 비용 증가</li>



<li>CPU 캐시 효율 저하 (<strong><strong>Chunk </strong></strong>내부에 있던 데이터가 캐시에서 빠짐)</li>



<li><strong><strong>Chunk </strong></strong> 내부에 빈 공간(fragmentation)이 생겨서 메모리 낭비 발생</li>
</ul>
</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h5 class="wp-block-heading"><strong><strong><strong>Dynamic Buffer</strong></strong>의 내부 용량 설정 (<code>InternalBufferCapacity</code>)</strong></h5>



<p>초기 용량을 커스텀 설정하려면 <code>InternalBufferCapacity</code> 속성을 사용</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">// 청크 내부에 최대 42개의 요소를 저장 가능
// 43개 이상 추가 시, 자동으로 청크 외부 메모리로 이동됨
[InternalBufferCapacity(42)]
public struct MyBufferElement : IBufferElementData
{
    public int Value;
}
</pre>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<p>초기 용량을 0으로 설정하면?</p>



<ul class="wp-block-list">
<li><strong>버퍼의 모든 데이터가 청크 외부 메모리에 저장됨.</strong></li>



<li><strong>버퍼 크기가 계속 변하는 경우 유리</strong> (즉, 초기에 작은 용량을 두지 않음).</li>
</ul>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">[InternalBufferCapacity(0)]
public struct MyBufferElement : IBufferElementData
{
    public int Value;
}
</pre>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<p><strong>최적화</strong></p>



<ul class="wp-block-list">
<li><strong>버퍼 크기를 예상할 수 있다면 <code>InternalBufferCapacity</code>를 적절히 설정</strong></li>



<li><strong>버퍼 크기가 자주 변경되면 <code>InternalBufferCapacity(0)</code>을 사용하여 메모리 재할당 비용을 줄임</strong></li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h5 class="wp-block-heading"><strong>동적으로 용량 조절 (EnsureCapacity &amp; TrimExcess)</strong></h5>



<ul class="wp-block-list">
<li>실행 중에 버퍼 용량을 설정하려면 <code>EnsureCapacity()</code> 사용</li>
</ul>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">DynamicBuffer&lt;MyBufferElement> buffer = entityManager.GetBuffer&lt;MyBufferElement>(entity);
buffer.EnsureCapacity(100); // 100개 요소를 저장할 수 있도록 미리 용량 할당
</pre>



<ul class="wp-block-list">
<li>불필요한 메모리를 줄이려면 <code>TrimExcess()</code> 사용</li>
</ul>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">buffer.TrimExcess(); // 현재 길이에 맞게 불필요한 메모리 정리
</pre>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p><strong>최적화</strong></p>



<ul class="wp-block-list">
<li>자주 <code>Add()</code>로 데이터를 추가하는 경우 <code>EnsureCapacity()</code>를 미리 호출하면 성능 향상</li>



<li>더 이상 사용하지 않는 메모리를 줄이려면 <code>TrimExcess()</code> 사용</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h5 class="wp-block-heading"><strong><strong><strong><strong>Dynamic Buffer</strong></strong></strong>보다 더 나은 대안?</strong></h5>



<ul class="wp-block-list">
<li><strong><strong><strong><strong>Dynamic Buffer</strong></strong></strong>의 용량 제한이 문제라면, 다음과 같은 대안 고려 가능</strong>
<ol class="wp-block-list">
<li><strong>Blob Assets</strong>
<ul class="wp-block-list">
<li>불변(읽기 전용) 데이터를 저장하는 데 적합</li>



<li>여러 엔티티가 공유 가능</li>



<li><strong>멀티스레딩 접근 가능</strong></li>
</ul>
</li>



<li><strong><code>NativeContainer</code> + <code>IComponentData</code> 조합</strong>
<ul class="wp-block-list">
<li><code>NativeArray&lt;T&gt;</code> 또는 <code>NativeList&lt;T&gt;</code> 등을 사용하여 별도로 관리 가능</li>
</ul>
</li>
</ol>
</li>
</ul>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading">4) Chunk 내 <strong><strong><strong><strong>Dynamic Buffer</strong></strong></strong></strong> 접근 방법</h4>



<p><strong>모든 동적 버퍼를 한 번에 가져오려면 <code>ArchetypeChunk.GetBufferAccessor()</code> 사용</strong></p>



<p><code>GetBufferAccessor&lt;T&gt;()</code>는 <code>BufferAccessor&lt;T&gt;</code>를 반환하며, 이를 통해 Chunk 내 모든 Entity의 버퍼에 접근 가능</p>



<p><strong>필요한 요소</strong></p>



<ol class="wp-block-list">
<li><code>EntityQueryBuilder</code>로 Entity<strong> 쿼리 생성</strong></li>



<li><code>EntityQuery</code>에서 <strong>Chunk 배열(NativeArray&lt;ArchetypeChunk&gt;) 가져오기</strong></li>



<li><code>BufferTypeHandle&lt;T&gt;</code>을 사용하여 <strong><strong><strong><strong><strong>Dynamic Buffer</strong></strong></strong></strong> 핸들 생성</strong></li>



<li><code>GetBufferAccessor&lt;T&gt;()</code>로 <strong>Chunk 내 모든 버퍼 가져오기</strong></li>



<li><strong>반복문을 통해 버퍼 데이터 처리</strong></li>
</ol>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p><strong><strong><strong><strong><strong>Dynamic Buffer</strong></strong></strong></strong> 접근 시스템 (<code>SystemBase</code> 기반)</strong></p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">using Unity.Burst;
using Unity.Collections;
using Unity.Entities;

[InternalBufferCapacity(16)] // 내부 용량을 16개 요소로 설정 (초과 시 Chunk 외부 메모리로 이동)
public struct ExampleBufferComponent : IBufferElementData
{
    public int Value;
}


public partial class ExampleSystem : SystemBase
{
    protected override void OnUpdate()
    {
        // EntityQuery를 생성하여 ExampleBufferComponent를 가진 Entity 필터링
        var query = new EntityQueryBuilder(Allocator.Temp)
            .WithAllRW&lt;ExampleBufferComponent>() // 읽기/쓰기 가능한 버퍼 포함
            .Build(EntityManager);

        // 쿼리를 통해 ArchetypeChunk 배열 가져오기
        NativeArray&lt;ArchetypeChunk> chunks = query.ToArchetypeChunkArray(Allocator.Temp);

        // Chunk 순회하면서 처리
        for (int i = 0; i &lt; chunks.Length; i++)
        {
            UpdateChunk(chunks[i]);
        }
        
        // 메모리 해제
        chunks.Dispose();
    }

    private void UpdateChunk(ArchetypeChunk chunk)
    {
        // ExampleBufferComponent에 대한 BufferTypeHandle 가져오기
        BufferTypeHandle&lt;ExampleBufferComponent> bufferTypeHandle = GetBufferTypeHandle&lt;ExampleBufferComponent>();

        // Chunk에서 BufferAccessor 가져오기
        BufferAccessor&lt;ExampleBufferComponent> buffers = chunk.GetBufferAccessor(ref bufferTypeHandle);

        //  Chunk 내 모든 Entity 의 Dynamic Buffer 접근
        for (int i = 0, chunkEntityCount = chunk.Count; i &lt; chunkEntityCount; i++)
        {
            DynamicBuffer&lt;ExampleBufferComponent> buffer = buffers[i];

            // 각 Entity의 동적 버퍼 요소 접근
            for (int j = 0; j &lt; buffer.Length; j++)
            {
                UnityEngine.Debug.Log($"Buffer Element {j}: {buffer[j].Value}");
            }
        }
    }
}
</pre>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<p><strong><strong><strong><strong><strong>Dynamic Buffer</strong></strong></strong></strong> 접근 시스템 (<code>ISystem</code> 기반)</strong></p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">using Unity.Burst;
using Unity.Collections;
using Unity.Entities;

[InternalBufferCapacity(16)]
public struct ExampleBufferComponent : IBufferElementData
{
    public int Value;
}

[BurstCompile] // 성능 최적화
public partial struct ExampleSystem : ISystem
{
    public void OnUpdate(ref SystemState state)
    {
        // EntityQuery를 생성하여 ExampleBufferComponent를 가진 Entity 필터링
        EntityQuery query = SystemAPI.QueryBuilder()
            .WithAllRW&lt;ExampleBufferComponent>() // 읽기/쓰기 가능한 버퍼 포함
            .Build();

        // 쿼리를 통해 ArchetypeChunk 배열 가져오기
        NativeArray&lt;ArchetypeChunk> chunks = query.ToArchetypeChunkArray(Allocator.Temp);

        // BufferTypeHandle 가져오기
        BufferTypeHandle&lt;ExampleBufferComponent> bufferTypeHandle = state.GetBufferTypeHandle&lt;ExampleBufferComponent>();

        // 모든 Chunk 순회하면서 Dynamic Buffer 처리
        foreach (var chunk in chunks)
        {
            UpdateChunk(chunk, ref bufferTypeHandle);
        }

        // NativeArray 해제
        chunks.Dispose();
    }

    private void UpdateChunk(ArchetypeChunk chunk, ref BufferTypeHandle&lt;ExampleBufferComponent> bufferTypeHandle)
    {
        // Chunk에서 BufferAccessor 가져오기
        BufferAccessor&lt;ExampleBufferComponent> buffers = chunk.GetBufferAccessor(ref bufferTypeHandle);

        // Chunk 내 모든 Entity의 Dynamic Buffer 접근
        for (int i = 0; i &lt; chunk.Count; i++)
        {
            DynamicBuffer&lt;ExampleBufferComponent> buffer = buffers[i];

            // 각 Entity의 Dynamic Buffer 요소 접근
            for (int j = 0; j &lt; buffer.Length; j++)
            {
                UnityEngine.Debug.Log($"Buffer Element {j}: {buffer[j].Value}");
            }
        }
    }
}
</pre>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading">5) 여러 Entity에서 동일한 <strong><strong><strong><strong>Dynamic Buffer</strong></strong></strong></strong> 재사용</h4>



<p><a href="https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/components-buffer-reuse.html" target="_blank" rel="noreferrer noopener">https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/components-buffer-reuse.html</a></p>



<p><strong>여러 Entity에서 동일한 동적 버퍼 사용하기</strong></p>



<ul class="wp-block-list">
<li><code>IJobEntity</code> 내에서 <strong>같은 DynamicBuffer를 공유</strong>하는 방법</li>



<li>동일한 <strong>동적 버퍼를 여러 엔티티에 적용</strong>하는 경우, <strong>메인 스레드에서 먼저 버퍼를 가져온 후 Job에 전달</strong></li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p><strong><code>SystemBase</code> 기반 예제</strong></p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">using Unity.Entities;
using Unity.Burst;
using Unity.Jobs;

public struct MyElement : IBufferElementData
{
    public int Value;
}

public struct OtherComponent : IComponentData {}

public partial class ExampleSystem : SystemBase
{
    protected override void OnUpdate()
    {
        // 특정 엔티티에서 DynamicBuffer 가져오기
        Entity e = GetSingletonEntity&lt;MyElement>();
        DynamicBuffer&lt;MyElement> myBuffer = SystemAPI.GetBuffer&lt;MyElement>(e);

        // Job에 버퍼 전달하여 여러 엔티티에서 사용
        new MyJobEntity { MyBuffer = myBuffer }.Schedule();
    }

    [BurstCompile]
    public partial struct MyJobEntity : IJobEntity
    {
        public DynamicBuffer&lt;MyElement> MyBuffer;

        public void Execute(in OtherComponent other)
        {
            // MyBuffer를 모든 엔티티에서 공유
            for (int i = 0; i &lt; MyBuffer.Length; i++)
            {
                UnityEngine.Debug.Log($"Shared Buffer Element: {MyBuffer[i].Value}");
            }
        }
    }
}
</pre>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<p><strong><code>ScheduleParallel</code>을 사용할 때 주의할 점</strong></p>



<ul class="wp-block-list">
<li><code>ScheduleParallel</code>을 사용하면 <strong>다중 스레드 환경에서 <code>DynamicBuffer</code>를 직접 수정할 수 없음</strong></li>



<li><strong>EntityCommandBuffer.ParallelWriter</strong>를 활용해야 함</li>



<li>그러나 <strong>구조적 변경(Structural Change)</strong>이 발생하면 버퍼가 무효화됨</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p><strong><code>ScheduleParallel</code>을 사용할 때 EntityCommandBuffer 활용 예제</strong></p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">protected override void OnUpdate()
{
    Entity e = GetSingletonEntity&lt;MyElement>();
    DynamicBuffer&lt;MyElement> myBuffer = SystemAPI.GetBuffer&lt;MyElement>(e);

    EntityCommandBuffer ecb = new EntityCommandBuffer(Allocator.TempJob);
    EntityCommandBuffer.ParallelWriter ecbParallel = ecb.AsParallelWriter();

    Entities
        .WithAll&lt;OtherComponent>()
        .ForEach((Entity entity, int entityInQueryIndex) =>
        {
            // EntityCommandBuffer를 사용하여 동적 버퍼에 변경 적용
            var buffer = ecbParallel.AddBuffer&lt;MyElement>(entityInQueryIndex, entity);
            buffer.CopyFrom(myBuffer);
        }).ScheduleParallel();

    Dependency.Complete();
    ecb.Playback(EntityManager);
    ecb.Dispose();
}
</pre>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<p><strong>최적화</strong></p>



<ul class="wp-block-list">
<li>같은 버퍼를 여러 Entity에서 사용하면 메모리 절약 가능</li>



<li><code>ScheduleParallel</code>을 사용할 때는 <code>EntityCommandBuffer.ParallelWriter</code> 필요</li>



<li>Dynamic Buffer 변경 시 구조적 변화(Structural Changes) 발생 가능 → 버퍼 재획득 필요</li>



<li>캐시 효율을 고려하여 버퍼 크기 최적화 (<code>InternalBufferCapacity</code>)</li>
</ul>



<p> 동일한 Dynamic Buffer를 재사용하면 성능과 메모리 사용량을 최적화</p>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading">6) Job에서 DynamicBuffer 접근 및 활용</h4>



<p><a href="https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/components-buffer-jobs.html" target="_blank" rel="noreferrer noopener">https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/components-buffer-jobs.html</a></p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p><strong>Job에서 DynamicBuffer 접근</strong></p>



<ul class="wp-block-list">
<li><code>IJobEntity</code>에서 <strong>여러 엔티티의 Dynamic Buffer를 조회</strong>하려면 <code>BufferLookup&lt;T&gt;</code>를 사용해야 함</li>



<li><code>BufferLookup&lt;T&gt;</code>는 <strong>EntityManager에서 <strong>Dynamic Buffer</strong>를 효율적으로 검색</strong>할 수 있도록 하는 <strong>Lookup Table</strong></li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p><strong><code>BufferLookup&lt;T&gt;</code>를 활용한 Job 예제</strong></p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">using Unity.Entities;
using Unity.Burst;
using Unity.Collections;

[InternalBufferCapacity(16)]
public struct ExampleBufferComponent : IBufferElementData
{
    public int Value;
}

[BurstCompile]
public partial struct AccessDynamicBufferJob : IJobEntity
{
    // ReadOnly BufferLookup 멤버 변수를 추가
    [ReadOnly] public BufferLookup&lt;ExampleBufferComponent> BufferLookup;

    public void Execute(Entity entity)
    {
        if (BufferLookup.HasBuffer(entity))
        {
            DynamicBuffer&lt;ExampleBufferComponent> buffer = BufferLookup[entity];
            for (int i = 0; i &lt; buffer.Length; i++)
            {
                UnityEngine.Debug.Log($"Entity {entity.Index} - Buffer[{i}] = {buffer[i].Value}");
            }
        }
    }
}
</pre>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p>System에서 <code>BufferLookup&lt;T&gt;</code> 관리하기 (<code>ISystem</code> 버전)</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">public partial struct AccessDynamicBufferFromJobSystem : ISystem
{
    private BufferLookup&lt;ExampleBufferComponent> _bufferLookup;

    public void OnCreate(ref SystemState state)
    {
        _bufferLookup = state.GetBufferLookup&lt;ExampleBufferComponent>(true);
    }

    public void OnUpdate(ref SystemState state)
    {
        _bufferLookup.Update(ref state);

        var job = new AccessDynamicBufferJob { BufferLookup = _bufferLookup };
        job.ScheduleParallel();
    }
}
</pre>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p> <code>BufferLookup&lt;T&gt;</code>는 매 프레임 <code>Update()</code>를 호출해야 최신 데이터를 유지할 수 있음<br><code>ScheduleParallel()</code>을 사용하면 여러 Entity에서 병렬 처리 가능</p>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading">7) EntityCommandBuffer(ECB)에서 DynamicBuffer 수정</h4>



<p><a href="https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/components-buffer-command-buffer.html" target="_blank" rel="noreferrer noopener">https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/components-buffer-command-buffer.html</a></p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p><strong> ECB(EntityCommandBuffer)</strong> 는 <strong>미래에 실행될 변경 사항을 기록</strong>하는 역할을 하며, <strong>DynamicBuffer에 대한 특별한 API</strong>를 제공</p>



<ul class="wp-block-list">
<li><a href="https://docs.unity3d.com/Packages/com.unity.entities@1.3/api/Unity.Entities.EntityCommandBuffer.SetBuffer.html" target="_blank" rel="noreferrer noopener"><strong>SetBuffer&lt;T&gt;()</strong> </a>→ 버퍼 내용을 덮어씀</li>



<li><a href="https://docs.unity3d.com/Packages/com.unity.entities@1.3/api/Unity.Entities.EntityCommandBuffer.AppendToBuffer.html" target="_blank" rel="noreferrer noopener"><strong>AppendToBuffer&lt;T&gt;()</strong> </a>→ 기존 버퍼에 요소 추가 (버퍼 없으면 예외 발생)</li>



<li><strong><a href="https://docs.unity3d.com/Packages/com.unity.entities@1.3/api/Unity.Entities.EntityCommandBuffer.AddBuffer.html" target="_blank" rel="noreferrer noopener">AddBuffer&lt;T&gt;()</a></strong> → 버퍼가 없으면 추가 (버퍼가 있으면 SetBuffer처럼 동작)</li>



<li><strong><a href="https://docs.unity3d.com/Packages/com.unity.entities@1.3/api/Unity.Entities.EntityCommandBuffer.RemoveComponent.html" target="_blank" rel="noreferrer noopener">RemoveComponent&lt;T&gt;()</a></strong> → 버퍼 제거</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">using Unity.Entities;
using Unity.Collections;

public struct MyElement : IBufferElementData
{
    public int Value;
}

public partial struct ExampleECBSystem : ISystem
{
    public void OnUpdate(ref SystemState state)
    {
        var ecb = new EntityCommandBuffer(Allocator.TempJob);
        
        Entity entity = SystemAPI.GetSingletonEntity&lt;MyElement>(); 
        Entity otherEntity = SystemAPI.GetSingletonEntity&lt;MyElement>();

        // 버퍼 삭제
        ecb.RemoveComponent&lt;MyElement>(entity);

        // 새 버퍼 추가 및 초기화
        DynamicBuffer&lt;MyElement> myBuff = ecb.AddBuffer&lt;MyElement>(entity);
        myBuff.Add(new MyElement { Value = 5 });
        myBuff.Add(new MyElement { Value = -9 });

        // 기존 버퍼 덮어쓰기 (없으면 예외 발생)
        DynamicBuffer&lt;MyElement> otherBuf = ecb.SetBuffer&lt;MyElement>(otherEntity);
        otherBuf.Add(new MyElement { Value = 10 });

        // 버퍼에 요소 추가 (버퍼가 없으면 예외 발생)
        ecb.AppendToBuffer(otherEntity, new MyElement { Value = 12 });

        ecb.Playback(state.EntityManager);//  Playback()이 호출될 때 실제 변경이 적용됨
        ecb.Dispose();
    }
}
</pre>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<ul class="wp-block-list">
<li><strong>ECB는 <code>Playback()</code>이 호출될 때 실제 변경이 적용됨</strong></li>



<li><strong>SetBuffer&lt;T&gt;()</strong> → 기존 버퍼가 없으면 예외 발생</li>



<li><strong>AppendToBuffer&lt;T&gt;()</strong> → 버퍼가 없으면 예외 발생 (안전하게 사용하려면 <code>AddComponent&lt;T&gt;()</code> 선행)</li>



<li><strong>AddBuffer&lt;T&gt;()</strong> → 없으면 추가하고, 있으면 기존 내용을 덮어씀</li>
</ul>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading">8) Reinterpret()을 이용한 버퍼 변환</h4>



<p><a href="https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/components-buffer-reinterpret.html" target="_blank" rel="noreferrer noopener">https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/components-buffer-reinterpret.html</a></p>



<p><code>Reinterpret&lt;T&gt;()</code>을 사용하면 <strong>같은 크기의 다른 타입으로 변환 가능</strong></p>



<p>단, <strong>메모리 크기가 같은 경우에만 가능</strong> (<code>int &#x2194; float</code>, <code>struct &#x2194; int</code> 등)</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">public partial class ExampleSystem : SystemBase
{
    private void ReinterpretEntitysChunk(Entity e)
    {
        DynamicBuffer&lt;MyElement> myBuff = EntityManager.GetBuffer&lt;MyElement>(e);

        // MyElement의 Value 필드가 int이므로 int 버퍼로 변환 가능
        DynamicBuffer&lt;int> intBuffer = myBuff.Reinterpret&lt;int>();

        intBuffer[2] = 6;  // 동일한 메모리를 참조하므로 myBuff[2]도 변경됨

        MyElement myElement = myBuff[2];
        Debug.Log(myElement.Value);    // 6
    }
}
</pre>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p><code>Reinterpret&lt;T&gt;()</code>은 <strong>메모리를 공유</strong>하므로, <strong>한쪽을 수정하면 다른 쪽도 변경됨</strong><br><strong>안전성 검토 필수</strong> (<code>struct</code> 내부 메모리 크기를 고려해야 함)</p>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<p><code>Reinterpret&lt;T&gt;()</code>를 사용하는 주요 이유</p>



<ol class="wp-block-list">
<li>데이터 변환 효율성</li>
</ol>



<ul class="wp-block-list">
<li>실제 메모리 복사 없이 같은 메모리를 다른 타입으로 해석할 수 있음</li>



<li>큰 데이터 셋을 다룰 때 성능상 이점이 큼</li>



<li>메모리 할당 오버헤드 방지</li>
</ul>



<ol start="2" class="wp-block-list">
<li>데이터 호환성</li>
</ol>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">// 예: 네트워크나 파일에서 받은 바이트 데이터를 직접 구조체로 해석
DynamicBuffer&lt;byte> rawData = ...;
DynamicBuffer&lt;MyStruct> structData = rawData.Reinterpret&lt;MyStruct>();</pre>



<ol start="3" class="wp-block-list">
<li>저수준 최적화</li>
</ol>



<ul class="wp-block-list">
<li>예를 들어 float 배열을 int 비트 패턴으로 직접 조작해야 할 때</li>



<li>SIMD 연산이나 특수한 비트 연산을 수행할 때 유용</li>
</ul>



<ol start="4" class="wp-block-list">
<li>다양한 데이터 뷰 제공</li>
</ol>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">// 같은 메모리를 다른 관점에서 접근 가능
DynamicBuffer&lt;Vector3> positions = ...;
DynamicBuffer&lt;float> rawFloats = positions.Reinterpret&lt;float>();</pre>



<p>주의사항:</p>



<ul class="wp-block-list">
<li>메모리 크기가 정확히 일치해야 함</li>



<li>타입 안전성이 깨질 수 있으므로 신중히 사용</li>



<li>데이터 정렬(alignment) 고려 필요</li>
</ul>



<p></p>
<p>The post <a href="https://lycos7560.com/unity/ecs-component-concepts-2/39517/">ECS – Component concepts (2)</a> appeared first on <a href="https://lycos7560.com">어제와 내일의 나 그 사이의 이야기</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://lycos7560.com/unity/ecs-component-concepts-2/39517/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>ECS &#8211; Component concepts (1)</title>
		<link>https://lycos7560.com/unity/ecs-component-concepts-1/39513/</link>
					<comments>https://lycos7560.com/unity/ecs-component-concepts-1/39513/#comments</comments>
		
		<dc:creator><![CDATA[lycos7560]]></dc:creator>
		<pubDate>Sat, 01 Feb 2025 16:18:24 +0000</pubDate>
				<category><![CDATA[Unity]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[Cleanup components]]></category>
		<category><![CDATA[Component]]></category>
		<category><![CDATA[concept]]></category>
		<category><![CDATA[Data]]></category>
		<category><![CDATA[DOTS]]></category>
		<category><![CDATA[ECS]]></category>
		<category><![CDATA[Entity Component System]]></category>
		<category><![CDATA[job]]></category>
		<category><![CDATA[Job 시스템]]></category>
		<category><![CDATA[Managed Components]]></category>
		<category><![CDATA[Physics]]></category>
		<category><![CDATA[Sample]]></category>
		<category><![CDATA[Shared Components]]></category>
		<category><![CDATA[study]]></category>
		<category><![CDATA[Tag components]]></category>
		<category><![CDATA[Unity Physics 101]]></category>
		<category><![CDATA[Unmanaged]]></category>
		<category><![CDATA[Unmanaged components]]></category>
		<category><![CDATA[공부]]></category>
		<category><![CDATA[기초]]></category>
		<guid isPermaLink="false">https://lycos7560.com/?p=39513</guid>

					<description><![CDATA[<p>1. Component란? https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/concepts-components.html Unity의 DOTS (Data-Oriented Technology Stack)에서 ECS(Entity Component System)의 핵심 요소 중 하나 ComponentData는 ECS에서 &#8220;데이터&#8221;를 저장하는 구조체 위의 예시에서 Speed, Direction, Position, Renderer가 Component 특징 사용 예시 2. IComponentData를 사용하여 Entity에 데이터 추가 IComponentData를 사용하려면 엔터티(Entity)에 추가해야 함 사용 예시 3. IComponentData를 읽고 수정하는 방법 ECS에서는 시스템(System)을 이용하여 데이터를 처리 사용 예시 [&#8230;]</p>
<p>The post <a href="https://lycos7560.com/unity/ecs-component-concepts-1/39513/">ECS &#8211; Component concepts (1)</a> appeared first on <a href="https://lycos7560.com">어제와 내일의 나 그 사이의 이야기</a>.</p>
]]></description>
										<content:encoded><![CDATA[				<div class="wp-block-uagb-table-of-contents uagb-toc__align-left uagb-toc__columns-1  uagb-block-fbe9f9d6      "
					data-scroll= "1"
					data-offset= "30"
					style=""
				>
				<div class="uagb-toc__wrap">
						<div class="uagb-toc__title">
							ECS &#8211; Component						</div>
																						<div class="uagb-toc__list-wrap ">
						<ol class="uagb-toc__list"><li class="uagb-toc__list"><a href="#1-component란" class="uagb-toc-link__trigger">1. Component란?</a><li class="uagb-toc__list"><a href="#2-icomponentdata를-사용하여-entity에-데이터-추가" class="uagb-toc-link__trigger">2. IComponentData를 사용하여 Entity에 데이터 추가</a><li class="uagb-toc__list"><a href="#3-icomponentdata를-읽고-수정하는-방법" class="uagb-toc-link__trigger">3. IComponentData를 읽고 수정하는 방법</a><li class="uagb-toc__list"><a href="#4-여러-종류의-component" class="uagb-toc-link__trigger">4. 여러 종류의 Component</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#1-unmanaged-components" class="uagb-toc-link__trigger">(1) Unmanaged components</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#2-managed-components" class="uagb-toc-link__trigger">(2) Managed Components</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#3-shared-components" class="uagb-toc-link__trigger">(3) Shared Components</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#4-cleanup-components" class="uagb-toc-link__trigger">(4) Cleanup components</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#5-tag-components" class="uagb-toc-link__trigger">(5) Tag components</a></ul></ol>					</div>
									</div>
				</div>
			


<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading">1. Component란?</h2>



<p><a href="https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/concepts-components.html" target="_blank" rel="noreferrer noopener">https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/concepts-components.html</a></p>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<p>Unity의 DOTS (Data-Oriented Technology Stack)에서 ECS(Entity Component System)의 핵심 요소 중 하나</p>



<p><code><strong>ComponentData</strong></code>는 ECS에서 <strong>&#8220;데이터&#8221;</strong>를<strong> 저장</strong>하는 <strong>구조체</strong></p>



<figure class="wp-block-image size-full"><img decoding="async" width="706" height="293" src="https://lycos7560.com/wp-content/uploads/2025/02/image.png" alt="" class="wp-image-39514" srcset="https://lycos7560.com/wp-content/uploads/2025/02/image.png 706w, https://lycos7560.com/wp-content/uploads/2025/02/image-300x125.png 300w" sizes="(max-width: 706px) 100vw, 706px" /><figcaption class="wp-element-caption"><a href="https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/concepts-components.html" target="_blank" rel="noreferrer noopener">https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/concepts-components.html</a></figcaption></figure>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p>위의 예시에서 <em>Speed, Direction, Position, Renderer</em>가 <code>Component</code></p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p>특징</p>



<ul class="wp-block-list">
<li>일반적으로 값 타입(struct) 선언</li>



<li><strong>OOP의 클래스 필드와 비슷한 역할</strong>을 하지만, <code>GameObject</code>나 <code>MonoBehaviour</code> 대신 <strong>Entity에 데이터를 부착하는 방식</strong></li>



<li>Unity의 ECS는 데이터와 로직을 분리하여 성능을 극대화하는 구조이므로, <code>IComponentData</code>는 <strong>순수 데이터만 포함</strong>하는 것이 좋음</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p>사용 예시</p>



<ul class="wp-block-list">
<li><strong>Entity의 위치</strong>를 저장하는 PositionComponent를 선언</li>
</ul>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">using Unity.Entities;
using Unity.Mathematics;

// 위치 정보를 저장하는 컴포넌트
public struct PositionComponent : IComponentData
{
    public float3 Value;
}
</pre>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading">2. IComponentData를 사용하여 Entity에 데이터 추가</h2>



<p><code>IComponentData</code>를 사용하려면 <strong>엔터티(Entity)에 추가</strong>해야 함</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p>사용 예시</p>



<ul class="wp-block-list">
<li><code>MonoBehaviour</code>에서 ECS <strong>Entity</strong>로 데이터를 변환하는 <strong>Baker</strong>를 사용하여 PositionComponent를 <strong>Entity</strong>에 추가</li>
</ul>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">using Unity.Entities;
using UnityEngine;

public class PositionAuthoring : MonoBehaviour
{
    public Vector3 Position;

    // Baker 클래스는 이를 ECS의 PositionComponent로 변환하여 엔터티에 추가
    class Baker : Baker&lt;PositionAuthoring>
    {
        public override void Bake(PositionAuthoring authoring)
        {
            var entity = GetEntity(TransformUsageFlags.Dynamic);
            AddComponent(entity, new PositionComponent { Value = authoring.Position });
        }
    }
}
</pre>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading">3. IComponentData를 읽고 수정하는 방법</h2>



<p>ECS에서는 <strong>시스템(System)을 이용하여 데이터를 처리</strong></p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p>사용 예시</p>



<ul class="wp-block-list">
<li><code>IComponentData</code>는 <code>ComponentLookup&lt;T&gt;</code> 또는 <code>SystemAPI</code>를 사용해 접근</li>



<li><code>Entities.ForEach</code> 또는 <code>IJobEntity</code>를 사용해 <strong>병렬 처리</strong></li>
</ul>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">using Unity.Burst;
using Unity.Entities;
using Unity.Jobs;
using Unity.Mathematics;

// BurstCompile을 사용하여 성능 최적화
[BurstCompile]
public partial struct MoveSystem : ISystem
{
    public void OnUpdate(ref SystemState state)
    {
        // SystemAPI.Query&lt;RefRW&lt;T>>()를 사용하여 컴포넌트에 접근하고 데이터를 수정함
        foreach (var position in SystemAPI.Query&lt;RefRW&lt;PositionComponent>>())
        {
            position.ValueRW.Value += new float3(1, 0, 0); // X 방향으로 이동
        }
    }
}
</pre>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading" id="component-types">4. 여러 종류의 Component</h2>



<p><code>Component</code>는 여러 다른 유형이 있음</p>



<p>프로젝트에서 데이터를 관리하는 방법에 따라 특정 <code>Component</code>를 사용하면 애플리케이션 성능을 보다 세밀하게 제어할 수 있음</p>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th><strong>Component</strong></th><th><strong>Description</strong></th></tr></thead><tbody><tr><td><a href="https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/components-unmanaged.html" target="_blank" rel="noreferrer noopener">Unmanaged components</a></td><td>가장 일반적인 컴포넌트 유형이지만, 특정 유형의 필드만 저장할 수 있음<br>(<code>struct</code> 기반의 <strong>값 타입(Value Type)</strong> 만 저장 가능)</td></tr><tr><td><a href="https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/components-managed.html" target="_blank" rel="noreferrer noopener">Managed components</a></td><td>모든 유형의 필드를 저장할 수 있는 <strong>관리형(Managed) 컴포넌트</strong><br>(<strong>가비지 컬렉션(GC) 영향</strong>을 받을 수 있어 <strong>성능에 주의</strong>)</td></tr><tr><td><a href="https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/components-shared.html" target="_blank" rel="noreferrer noopener">Shared components</a></td><td><strong>Entity</strong>를 <strong>컴포넌트 값에 따라 Chunk단위로 그룹화</strong>함<br>동일한 값을 가지는 엔터티들이 같은 메모리 <strong>Chunk</strong>에 저장되어 <strong>쿼리 성능을 향상</strong></td></tr><tr><td><a href="https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/components-cleanup.html" target="_blank" rel="noreferrer noopener">Cleanup components</a></td><td><strong><strong>Entity</strong>가 파괴될 때 정리(Cleanup)가 필요한 경우 유용</strong><br><strong>Entity</strong>가 삭제될 때 <strong>Cleanup 컴포넌트를 제외한 모든 컴포넌트가 제거</strong>됨</td></tr><tr><td><a href="https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/components-tag.html" target="_blank" rel="noreferrer noopener">Tag components</a></td><td><strong>데이터를 저장하지 않으며 메모리를 차지하지 않는</strong> 특수한 컴포넌트<br><strong><strong>Entity</strong></strong> 쿼리에서 필터링할 때 유용. (<code>IComponentData</code>를 빈 <code>struct</code>으로 선언)</td></tr><tr><td><a href="https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/components-buffer.html" target="_blank" rel="noreferrer noopener">Buffer components</a> (Dynamic buffer components)</td><td><strong>가변 크기 배열(Resizable Array) 역할을 하는 컴포넌트</strong><br>예를 들어, <strong>경로 점(Path Points) 목록이나 충돌 이벤트 리스트</strong> 등을 저장하는 데 사용됨</td></tr><tr><td><a href="https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/components-chunk.html" target="_blank" rel="noreferrer noopener">Chunk components</a></td><td><strong><strong>Entity</strong></strong>가 아니라 <strong><strong>Chunk</strong> 전체에 값을 저장하는 컴포넌트</strong><br>같은 <strong>Chunk</strong>에 속한 <strong>모든 엔터티가 동일한 값</strong>을 공유</td></tr><tr><td><a href="https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/components-enableable.html" target="_blank" rel="noreferrer noopener">Enableable components</a></td><td><strong>런타임 중에 활성화/비활성화</strong>할 수 있는 컴포넌트<br><strong>구조적 변경(Structural Change) 없이</strong> 상태를 변경할 수 있어 성능 비용을 절약 가능.</td></tr><tr><td><a href="https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/components-singleton.html" target="_blank" rel="noreferrer noopener">Singleton components</a></td><td><strong>월드(World) 내에서 단 하나의 인스턴스만 존재하는 컴포넌트</strong><br><strong>전역 상태 저장용</strong>으로 자주 사용됨</td></tr></tbody></table></figure>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<p><strong>Editor에서 Component types</strong></p>



<p>에디터에서 다음 아이콘은 다양한 컴포넌트 유형을 나타냅니다. 이는 관련 <a href="https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/editor-workflows.html" target="_blank" rel="noreferrer noopener">Entities windows and Inspectors</a>에 나타납니다.</p>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th><strong>Icon</strong></th><th><strong>Component type</strong></th></tr></thead><tbody><tr><td><img decoding="async" src="https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/images/editor-managed-component.png" alt=""></td><td>A managed component.</td></tr><tr><td><img decoding="async" src="https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/images/editor-shared-component.png" alt=""></td><td>A shared component.</td></tr><tr><td><img decoding="async" src="https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/images/editor-tag-component.png" alt=""></td><td>A tag component.</td></tr><tr><td><img decoding="async" src="https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/images/editor-buffer-component.png" alt=""></td><td>A buffer component.</td></tr><tr><td><img decoding="async" src="https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/images/editor-chunk-component.png" alt=""></td><td>A chunk component.</td></tr></tbody></table></figure>



<p>Unity는 목록에 없는 구성 요소 유형에 대해 일반적인 구성 요소 아이콘을 사용합니다.</p>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">(1) Unmanaged components</h3>



<p><a href="https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/components-unmanaged.html" target="_blank" rel="noreferrer noopener">https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/components-unmanaged.html</a></p>



<p><code>Unmanaged components</code>는 일반적인 데이터 유형을 저장하는 데 사용하며 아래 유형의 필드를 저장할 수 있음</p>



<ul class="wp-block-list">
<li><a href="https://docs.microsoft.com/en-us/dotnet/framework/interop/blittable-and-non-blittable-types" target="_blank" rel="noreferrer noopener">Blittable types</a></li>



<li><code>bool</code> <br><code>true</code> 또는 <code>false</code> 값을 가지는 <strong>논리형(Boolean) 타입</strong></li>



<li><code>char</code><br>단일 문자(Character)를 저장, 일반적으로 <strong>UTF-16 코드 단위</strong></li>



<li><code>BlobAssetReference&lt;T&gt;</code>&nbsp;<br>Blob 데이터 구조에 대한 참조, 대량의 <strong>불변(Immmutable) 데이터</strong>를 효율적으로 저장하고 공유하는 데 사용</li>



<li><code>Collections.FixedString</code>&nbsp;<br>고정 크기(Fixed-Size) 문자열 버퍼<br>가변 크기 문자열인 <code>string</code> 대신 <strong>GC(가비지 컬렉션) 없이</strong> 문자열을 저장할 때 사용</li>



<li><code>Collections.FixedList</code><br><strong>고정 크기 리스트(Fixed-Size List)</strong>, 일반 <code>List&lt;T&gt;</code> 대신, 할당 없이 고정된 크기의 배열을 사용하는 리스트 구조.</li>



<li><a href="https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/fixed-statement" target="_blank" rel="noreferrer noopener">Fixed array</a>&nbsp;(unsafe context에서만 사용 가능)</li>



<li>기타 Blittable 조건을 충족하는 구조체(Structs)<br>Blittable 타입만 포함하는 <strong>다른 struct도 Blittable 타입이 될 수 있음</strong><br>즉, <strong>모든 필드가 Blittable 타입으로 구성된 struct</strong>는 Blittable로 간주됨.</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<blockquote class="wp-block-quote is-style-default is-layout-flow wp-container-core-quote-is-layout-8a368f38 wp-block-quote-is-layout-flow" style="border-style:none;border-width:0px">
<p class="has-medium-font-size"><strong>Blittable 타입</strong></p>



<p>Blittable(블리터블)이란, 메모리에서 직접 복사(Copy)할 수 있는 데이터 타입을 의미</p>



<p><strong>쉽게 말해, CPU가 메모리를 변환 없이 그대로 읽고 쓸 수 있는 데이터 타입</strong></p>



<p>ECS(Entity Component System)에서 IComponentData는 Blittable 타입을 요구함</p>



<p>Blittable 타입은 관리되지 않는 네이티브 메모리에서 안전하게 사용할 수 있어 성능이 뛰어남</p>



<p>가비지 컬렉션(GC) 영향을 받지 않아 메모리 효율성이 높아짐</p>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th><strong>Blittable 타입</strong></th><th>CPU가 변환 없이 직접 복사 가능</th></tr></thead><tbody><tr><td><code>bool</code>, <code>char</code>, <code>int</code>, <code>float</code>, <code>double</code></td><td><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 기본적인 값 타입(Primitive Type)</td></tr><tr><td><code>float2</code>, <code>float3</code>, <code>float4</code> (Unity.Mathematics)</td><td><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> SIMD 연산 최적화 가능</td></tr><tr><td><code>BlobAssetReference&lt;T&gt;</code></td><td><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Blob 데이터 구조체</td></tr><tr><td><code>FixedString</code>, <code>FixedList</code></td><td><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 고정 크기의 문자열 및 리스트</td></tr></tbody></table></figure>



<figure class="wp-block-table"><table class="has-fixed-layout"><tbody><tr><td><strong>Non-Blittable 타입</strong></td><td><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/274c.png" alt="❌" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 직접 복사할 수 없고 변환이 필요함</td></tr><tr><td><code>string</code></td><td><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/274c.png" alt="❌" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 가변 길이 문자열 (Heap 할당)</td></tr><tr><td><code>List&lt;T&gt;</code> / <code>Dictionary&lt;K, V&gt;</code></td><td><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/274c.png" alt="❌" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 동적 크기 컨테이너 (Heap 할당)</td></tr><tr><td><code>class</code> (참조 타입)</td><td><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/274c.png" alt="❌" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 힙 메모리에 저장되며, 포인터를 가짐</td></tr><tr><td><code>object</code></td><td><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/274c.png" alt="❌" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 박싱(Boxing) 필요</td></tr></tbody></table></figure>
</blockquote>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">using Unity.Burst;
using Unity.Entities;
using Unity.Mathematics;

// Blittable한 IComponentData 구조체 정의
public struct MoveSpeed : IComponentData
{
    public float Value; // Blittable 타입(float) 사용
}

// 엔터티에 MoveSpeed 컴포넌트를 추가하는 Authoring 스크립트
using UnityEngine;

public class MoveSpeedAuthoring : MonoBehaviour
{
    public float speed;

    class Baker : Baker&lt;MoveSpeedAuthoring>
    {
        public override void Bake(MoveSpeedAuthoring authoring)
        {
            var entity = GetEntity(TransformUsageFlags.Dynamic);
            AddComponent(entity, new MoveSpeed { Value = authoring.speed });
        }
    }
}

// MoveSpeed 값을 사용하여 Entity를 이동시키는 시스템
[BurstCompile] // 성능 최적화
public partial struct MoveSystem : ISystem
{
    public void OnUpdate(ref SystemState state)
    {
        foreach (var moveSpeed in SystemAPI.Query&lt;RefRW&lt;MoveSpeed>>())
        {
            moveSpeed.ValueRW.Value += 0.1f; // X 방향으로 이동 속도 증가
        }
    }
}
</pre>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">(2) Managed Components</h3>



<p><a href="https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/components-managed.html" target="_blank" rel="noreferrer noopener">https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/components-managed.html</a></p>



<p>Managed Components(관리되는 컴포넌트)는 <strong>어떤 타입의 필드라도 저장할 수 있는</strong> <code>IComponentData</code>이지만, 성능상 단점이 있음.</p>



<ul class="wp-block-list">
<li><code>class</code> 타입으로 선언하며, <strong>GC(가비지 컬렉션)의 영향을 받음</strong></li>



<li><strong>Job System과 Burst 컴파일러에서 사용 불가</strong></li>



<li><strong>직렬화(Serialization)를 위해 매개변수가 없는 생성자 필수</strong></li>
</ul>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th><strong>특징</strong></th><th><strong>Managed Components</strong></th><th><strong>Unmanaged Components</strong></th></tr></thead><tbody><tr><td><strong>저장 가능한 데이터</strong></td><td>모든 타입 사용 가능</td><td>Blittable 타입만 가능</td></tr><tr><td><strong>메모리 저장 방식</strong></td><td>개별 배열(Array) 관리</td><td>ECS의 Chunk에 직접 저장</td></tr><tr><td><strong>Burst 지원</strong></td><td><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/274c.png" alt="❌" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 지원되지 않음</td><td><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 지원됨</td></tr><tr><td><strong>Job System 사용 가능 여부</strong></td><td><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/274c.png" alt="❌" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 불가능</td><td><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 가능</td></tr><tr><td><strong>GC 영향</strong></td><td><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/274c.png" alt="❌" class="wp-smiley" style="height: 1em; max-height: 1em;" /> GC 발생</td><td><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> GC 없음</td></tr><tr><td><strong>직렬화 필요 여부</strong></td><td><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 매개변수 없는 생성자 필수</td><td><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/274c.png" alt="❌" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 필요 없음</td></tr></tbody></table></figure>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p><strong>Managed Component 생성 예시</strong></p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">using Unity.Entities;

// 클래스(class) 타입으로 Managed Component 생성
public class ExampleManagedComponent : IComponentData
{
    public int Value; // 관리되는 타입(int) 저장 가능
}
</pre>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p><strong>Managed Component를 <strong>Entity</strong>에 추가하는 Authoring 스크립트 예시</strong></p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">using UnityEngine;
using Unity.Entities;

public class ExampleManagedComponentAuthoring : MonoBehaviour
{
    public int Value;

    class Baker : Baker&lt;ExampleManagedComponentAuthoring>
    {
        public override void Bake(ExampleManagedComponentAuthoring authoring)
        {
            var entity = GetEntity(TransformUsageFlags.Dynamic);
            AddComponent(entity, new ExampleManagedComponent { Value = authoring.Value });
        }
    }
}
</pre>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p><strong>Managed Component를 조회하고 수정하는 시스템 예시</strong></p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">using Unity.Entities;
using Unity.Burst;

// Managed Component는 Burst를 사용할 수 없음
// [BurstCompile] 사용 불가능
public partial struct ExampleManagedComponentSystem : ISystem
{
    public void OnUpdate(ref SystemState state)
    {
        
        foreach (var managedComponent in SystemAPI.Query&lt;ExampleManagedComponent>())
        {
            // 주의:Managed Component는 RefRW&lt;T>로 접근할 수 없으며, 직접 접근 방식을 사용해야 함.
            managedComponent.Value += 1; // 값 증가
        }
    }
}
</pre>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p><strong>외부 리소스를 관리하는 Managed Component (ICloneable &amp; IDisposable)</strong></p>



<ul class="wp-block-list">
<li>Managed Component가 <strong>외부(UnityEngine.Object) 리소스를 참조</strong>할 경우, 복제 및 제거 시 주의해야 함.</li>
</ul>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">using UnityEngine;
using Unity.Entities;
using System;

// 외부 리소스의 수명 주기를 관리
public class ManagedComponentWithResource : IComponentData, IDisposable, ICloneable
{
    public ParticleSystem ParticleSystem; // Unity Object 참조

    // 엔터티가 삭제될 때 ParticleSystem도 파괴되도록 설정
    public void Dispose()
    {
        UnityEngine.Object.Destroy(ParticleSystem);
    }

    // 엔터티를 복제할 때 새로운 ParticleSystem을 생성하도록 설정
    public object Clone()
    {
        return new ManagedComponentWithResource
        {
            ParticleSystem = UnityEngine.Object.Instantiate(ParticleSystem)
        };
    }
}
</pre>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<p><strong>Managed Component를 이용한 최적화</strong></p>



<p>Managed Component는 성능이 낮기 때문에, <strong>가능한 경우 Unmanaged Component를 사용해야 </strong>함</p>



<p>그러나, 아래와 같은 상황에서는 Managed Component를 사용할 수밖에 없음</p>



<ul class="wp-block-list">
<li><strong>Unmanaged Component로 표현할 수 없는 데이터 저장</strong> (<code>string</code>, <code>List&lt;T&gt;</code>, <code>class</code>)</li>



<li><strong>UnityEngine.Object를 직접 참조해야 하는 경우</strong> (<code>GameObject</code>, <code>ParticleSystem</code>)</li>



<li><strong>GC 부하를 감수하고라도 가변 데이터 구조 필요할 때</strong></li>
</ul>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">(3) Shared Components</h3>



<p><a href="https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/components-shared.html" target="_blank" rel="noreferrer noopener">https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/components-shared.html</a></p>



<p>Shared Components(공유 컴포넌트)는 <strong>같은 값을 가진 <strong>Entity</strong>들을 같은 Chunk로 그룹화</strong>하는 <code>ISharedComponentData</code> 타입</p>



<p>일반 <code>IComponentData</code>와 다르게, <strong><strong>Chunk</strong> 단위로 저장</strong>되며, 동일한 값의 <strong><strong>Entity</strong></strong>들은 같은 <strong><strong>Chunk</strong></strong>에 배치됩니다.</p>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th><strong>항목</strong></th><th><strong>Shared Components</strong></th><th><strong>Unmanaged Components</strong></th></tr></thead><tbody><tr><td><strong>저장 단위</strong></td><td><strong><strong>Chunk</strong> 단위 저장</strong></td><td>개별 <strong><strong>Entity</strong></strong>별 저장</td></tr><tr><td><strong>Burst 지원</strong></td><td><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/274c.png" alt="❌" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 지원되지 않음</td><td><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 지원됨</td></tr><tr><td><strong>Job System 사용 가능 여부</strong></td><td><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/274c.png" alt="❌" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 불가능</td><td><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 가능</td></tr><tr><td><strong>GC 영향</strong></td><td><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/274c.png" alt="❌" class="wp-smiley" style="height: 1em; max-height: 1em;" /> GC 발생 가능</td><td><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> GC 없음</td></tr><tr><td><strong>직렬화 필요 여부</strong></td><td><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 필요함</td><td><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/274c.png" alt="❌" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 필요 없음</td></tr><tr><td><strong>데이터 변경 시 영향</strong></td><td>값이 바뀌면 <strong><strong>Chunk</strong>가 재배치됨</strong></td><td>구조적 변경 없음</td></tr></tbody></table></figure>



<p><code>Shared Components</code>는 같은 값을 가진 <strong><strong>Entity</strong></strong>들이 같은 Chunk에 배치되므로 <strong>메모리 캐시 적중률을 높이는 데 유용</strong></p>



<p>그러나, <strong>값을 변경할 경우 <strong><strong>Entity</strong></strong>가 다른 <strong><strong>Chunk</strong></strong>로 이동</strong>해야 하므로 <strong>빈번한 변경이 필요할 경우 적합하지 않음</strong></p>



<p><code>Managed Components</code>와 달리, <code>struct</code> 타입으로 선언하지만 내부적으로 <code>class</code>를 포함할 수 있음</p>



<p>Burst 및 Job System을 사용할 수 없으므로 성능이 중요한 곳에서는 <code>Unmanaged Components</code>를 사용하는 것이 좋음</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p><strong>Shared Component 생성 예시</strong></p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">using Unity.Entities;

// ISharedComponentData를 상속받아 Shared Component 생성
public struct ExampleSharedComponent : ISharedComponentData
{
    public int GroupId; // 같은 GroupId를 가진 엔터티들은 같은 Chunk에 저장됨
}
</pre>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p><strong>Shared Component를 <strong><strong>Entity</strong></strong>에 추가하는 Authoring 스크립트</strong></p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">using UnityEngine;
using Unity.Entities;

public class ExampleSharedComponentAuthoring : MonoBehaviour
{
    public int groupId;

    class Baker : Baker&lt;ExampleSharedComponentAuthoring>
    {
        public override void Bake(ExampleSharedComponentAuthoring authoring)
        {
            var entity = GetEntity(TransformUsageFlags.Dynamic);
            // AddComponent가 아닌 AddSharedComponent()를 사용해야 함
            // 같은 groupId 값을 가진 엔터티들이 같은 Chunk에 배치됨
            AddSharedComponent(entity, new ExampleSharedComponent { GroupId = authoring.groupId });
        }
    }
}
</pre>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<p><strong>Shared Component를 조회하는 시스템</strong></p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">using Unity.Entities;
using Unity.Burst;

// Shared Component는 Burst를 사용할 수 없음
// [BurstCompile] 사용 불가능
public partial struct ExampleSharedComponentSystem : ISystem
{
    public void OnUpdate(ref SystemState state)
    {
        foreach (var (sharedComponent, entity) in SystemAPI.Query&lt;ExampleSharedComponent>().WithEntityAccess())
        {
            UnityEngine.Debug.Log($"Entity {entity.Index} has GroupId {sharedComponent.GroupId}");
        }
    }
}
</pre>



<p><code>Shared Component</code>는 일반적인 <code>IComponentData</code>처럼 <code>RefRW&lt;T&gt;</code>로 접근할 수 없음</p>



<p>대신 <strong>직접 값 복사 방식</strong>으로 접근해야 함</p>



<p><code>WithEntityAccess()</code>를 사용하면 어떤 엔터티가 해당 <code>Shared Component</code>를 가지고 있는지 확인 가능</p>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<p><strong>Shared Component 값 변경</strong></p>



<ul class="wp-block-list">
<li><code>Shared Components</code>는 값이 변경되면 새로운 Chunk가 생성되므로, 자주 변경되는 값에는 적합하지 않음.</li>
</ul>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">public partial struct UpdateSharedComponentSystem : ISystem
{
    public void OnUpdate(ref SystemState state)
    {
        foreach (var (entity, sharedComponent) in SystemAPI.Query&lt;Entity, ExampleSharedComponent>())
        {
            if (sharedComponent.GroupId == 1)
            {
                state.EntityManager.SetSharedComponent(entity, new ExampleSharedComponent { GroupId = 2 });
            }
        }
    }
}
</pre>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<p><strong>Shared Component를 활용하는 예제</strong></p>



<ul class="wp-block-list">
<li>렌더링 레이어 그룹화</li>
</ul>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">// Layer 값이 같은 엔터티들을 같은 Chunk에 배치
// 같은 레이어의 엔터티들을 한 번에 처리하여 렌더링 최적화 가능
public struct RenderLayer : ISharedComponentData
{
    public int Layer;
}
</pre>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<ul class="wp-block-list">
<li> AI 행동 그룹화</li>
</ul>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">// StateId 값이 같은 적(AI)들은 같은 Chunk에 배치됨
// 특정 AI 그룹을 한 번에 업데이트할 때 유용
public struct AIState : ISharedComponentData
{
    public int StateId;
}
</pre>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<ul class="wp-block-list">
<li>팀 구분</li>
</ul>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">using Unity.Entities;

// 팀 정보를 저장하는 Shared Component
public struct TeamComponent : ISharedComponentData
{
    public int TeamId; // 같은 TeamId를 가진 Entity들은 같은 Chunk에 저장됨
}
</pre>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">using UnityEngine;
using Unity.Entities;

// 팀을 설정하는 Authoring
public class TeamComponentAuthoring : MonoBehaviour
{
    public int teamId;

    class Baker : Baker&lt;TeamComponentAuthoring>
    {
        public override void Bake(TeamComponentAuthoring authoring)
        {
            var entity = GetEntity(TransformUsageFlags.Dynamic);
            AddSharedComponent(entity, new TeamComponent { TeamId = authoring.teamId });
        }
    }
}
</pre>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">using Unity.Entities;
using Unity.Burst;
using UnityEngine;

// Burst 사용 불가능 (Shared Component는 Job System에서 직접 접근 불가)
public partial struct TeamSystem : ISystem
{
    public void OnUpdate(ref SystemState state)
    {
        foreach (var (teamComponent, entity) in SystemAPI.Query&lt;TeamComponent>().WithEntityAccess())
        {   
            // WithEntityAccess()를 사용하면 특정 엔터티가 어느 팀에 속해 있는지 확인 가능
            Debug.Log($"Entity {entity.Index} belongs to Team {teamComponent.TeamId}");
        }
    }
}
</pre>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">public partial struct TeamFilterSystem : ISystem
{
    public void OnUpdate(ref SystemState state)
    {
        // 특정 팀 ID(예: TeamId == 1)를 가진 엔터티들만 가져오기
        var query = SystemAPI.QueryBuilder().WithAll&lt;TeamComponent>().Build();
        state.EntityManager.SetSharedComponent(query, new TeamComponent { TeamId = 1 });

        foreach (var (teamComponent, entity) in SystemAPI.Query&lt;TeamComponent>().WithEntityAccess())
        {
            if (teamComponent.TeamId == 1)
            {
                Debug.Log($"Entity {entity.Index} is in Team 1");
            }
        }
    }
}
</pre>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<p><strong>정리</strong></p>



<ul class="wp-block-list">
<li>같은 값을 가진 Entity들을 같은 Chunk에 저장하여 메모리 접근 최적화 가능</li>



<li>자주 변경되지 않는 데이터에 적합 (예: 렌더링 그룹, AI 상태, 네트워크 동기화 등)</li>



<li>값이 변경될 경우 새로운 Chunk로 이동해야 하므로 변경이 잦다면 비효율적</li>



<li>Burst 및 Job System을 사용할 수 없으므로 성능이 중요한 경우 Unmanaged Component 사용이 더 적합</li>
</ul>



<p>&#8220;자주 변경되지 않는&#8221; 데이터를 기준으로 Entity를 그룹화할 때 <code>Shared Component</code>를 사용</p>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">(4) Cleanup components</h3>



<p><a href="https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/components-cleanup.html" target="_blank" rel="noreferrer noopener">https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/components-cleanup.html</a></p>



<p><strong>Cleanup Components</strong>는 일반 컴포넌트와 유사하지만, <strong>특정 Entity가 삭제될 때 추가적인 정리 작업을 수행할 수 있도록 설계된 특수한 컴포넌트</strong></p>



<p>이 컴포넌트는 Entity가 삭제될 때&nbsp;<strong>모든 일반 컴포넌트를 제거</strong>하고, Entity 자체는 Cleanup Components가 모두 제거될 때까지 남아 있음</p>



<p> Entity가 삭제될 때 추가적인 정리 작업(예: 리소스 해제, 이벤트 처리 등)이 필요한 경우에 유용</p>



<ul class="wp-block-list">
<li>ICleanupComponentData : 각 Entity별로 독립적인 정리 작업을 수행하며, 엔티티가 삭제될 때 개별적으로 동작</li>



<li>ICleanupSharedComponentData : 여러 Entity가 공유하는 데이터를 정리할 때 사용 (같은 리소스를 여러 Entity가 사용할 때, 리소스를 관리하고 정리하는 역할)</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>항목</th><th>설명</th></tr></thead><tbody><tr><td><strong>목적</strong></td><td>엔티티가 삭제될 때 추가적인 정리 작업을 수행하기 위해 사용</td></tr><tr><td><strong>동작 방식</strong></td><td>엔티티가 삭제될 때 모든 일반 컴포넌트를 제거하고, Cleanup Components만 남깁니다. <br>엔티티는 Cleanup Components가 모두 제거될 때까지 존재합니다.</td></tr><tr><td><strong>사용 사례</strong></td><td>&#8211; 리소스 해제<br>&#8211; 이벤트 처리<br>&#8211; 네트워크 동기화 정리<br>&#8211; 엔티티 삭제 시 추가 로직 실행</td></tr><tr><td><strong>Burst 지원</strong></td><td><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 지원됨</td></tr><tr><td><strong>Job System 사용 가능 여부</strong></td><td><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 가능</td></tr><tr><td><strong>GC 영향</strong></td><td><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> GC 없음 (Unmanaged Components와 동일)</td></tr><tr><td><strong>직렬화 필요 여부</strong></td><td><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/274c.png" alt="❌" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 필요 없음</td></tr></tbody></table></figure>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p><strong>Cleanup Components의 동작 원리</strong></p>



<ol start="1" class="wp-block-list">
<li><strong>엔티티 삭제 시 동작</strong>:
<ul class="wp-block-list">
<li>엔티티가 삭제되면 모든 일반 컴포넌트가 제거</li>



<li>Cleanup Components는 남아 있으며, 엔티티는 여전히 존재</li>
</ul>
</li>



<li><strong>Cleanup Components 제거</strong>:
<ul class="wp-block-list">
<li>Cleanup Components가 모두 제거되면 엔티티가 완전히 삭제</li>
</ul>
</li>



<li><strong>정리 작업 수행</strong>:
<ul class="wp-block-list">
<li>Cleanup Components를 사용하여 엔티티 삭제 시 필요한 추가 작업(예: 리소스 해제, 이벤트 처리 등)을 수행</li>
</ul>
</li>
</ol>



<p><strong>Cleanup component 생명주기</strong></p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">// Cleanup 컴포넌트를 포함하는 엔티티를 생성합니다.
Entity e = EntityManager.CreateEntity(
    typeof(Translation), typeof(Rotation), typeof(ExampleCleanup));

// 엔티티를 삭제하려고 시도하지만, 엔티티가 Cleanup 컴포넌트를 가지고 있기 때문에 Unity는 실제로 엔티티를 삭제하지 않습니다.
// 대신, Unity는 Translation 및 Rotation 컴포넌트만 제거합니다.
EntityManager.DestroyEntity(e);

// 엔티티는 여전히 존재하므로, 엔티티를 정상적으로 사용할 수 있음을 보여줍니다.
EntityManager.AddComponent&lt;Translation>(e);

// 엔티티에서 남아 있는 모든 컴포넌트를 제거합니다.
// 마지막 Cleanup 컴포넌트(ExampleCleanup)를 제거하면 엔티티가 자동으로 삭제됩니다.
EntityManager.RemoveComponent(e, new ComponentTypeSet(typeof(ExampleCleanup), typeof(Translation)));

// 엔티티가 더 이상 존재하지 않음을 보여줍니다. entityExists는 false입니다.
bool entityExists = EntityManager.Exists(e);</pre>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<ul class="wp-block-list">
<li><strong>Cleanup Components 생성 예시</strong></li>
</ul>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">using Unity.Entities;

// ICleanupComponentData를 상속받아 Cleanup Component를 정의
// 엔티티가 삭제될 때 추가적인 정리 작업을 수행하는 데 사용
public struct CleanupComponent : ICleanupComponentData
{
    public int ResourceId; // 정리할 리소스 ID
}</pre>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<ul class="wp-block-list">
<li><strong>Cleanup Components를 Entity에 추가하는 Authoring 스크립트</strong></li>
</ul>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">using UnityEngine;
using Unity.Entities;

public class CleanupComponentAuthoring : MonoBehaviour
{
    public int resourceId;

    class Baker : Baker&lt;CleanupComponentAuthoring>
    {
        public override void Bake(CleanupComponentAuthoring authoring)
        {
            var entity = GetEntity(TransformUsageFlags.Dynamic);
            // AddComponent를 사용하여 Cleanup Component를 엔티티에 추가
            AddComponent(entity, new CleanupComponent { ResourceId = authoring.resourceId });
        }
    }
}</pre>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">(5) Tag components</h3>



<p><a href="https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/components-tag.html" target="_blank" rel="noreferrer noopener">https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/components-tag.html</a></p>



<p><code>Tag components</code>는 특별한 컴포넌트 유형으로, 주로 <strong>Entity</strong>를 분류하거나 특정 상태를 나타내는 데 사용</p>



<p><code>Tag components</code>는 데이터를 저장하지 않고, 그 자체로 &#8220;표식&#8221; 역할 </p>



<p>이는 예를 들어 <strong>Entity</strong>가 특정 범주에 속하는지, 아니면 특정 조건을 만족하는지를 나타내는 데 유용</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p><code>Tag components</code>는 <strong>Entity</strong>의 동작에 대한 부가적인 의미를 제공하기 위해 사용</p>



<p>이 컴포넌트는 상태를 나타내는 단순한 &#8220;플래그&#8221;로, 해당 컴포넌트가 존재하는지 여부만 확인하면 됩니다. </p>



<p>예를 들어, <code>Enemy</code>나 <code>Player</code>와 같은 태그 컴포넌트를 사용하여 <strong>Entity</strong>가 어떤 종류의 객체인지를 표시할 수 있습니다.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">using Unity.Entities;

// Tag Component 정의 (데이터를 가지지 않음)
public struct PlayerTag : IComponentData { }

public struct EnemyTag : IComponentData { }
</pre>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">using Unity.Entities;
using Unity.Transforms;
using UnityEngine;

public struct TagComponentSearchSystem : ISystem
{
    public void OnCreate(ref SystemState state) { }

    public void OnDestroy(ref SystemState state) { }

    public void OnUpdate(ref SystemState state)
    {
        // PlayerTag를 가진 엔티티를 찾아서 처리
        var playerQuery = SystemAPI.QueryBuilder().WithAll&lt;PlayerTag, Translation>().Build();

        foreach (var (playerTag, translation) in playerQuery)
        {
            // 플레이어 엔티티가 있을 때 처리할 코드
            Debug.Log("Player found at position: " + translation.Value);
        }

        // EnemyTag를 가진 엔티티를 찾아서 처리
        var enemyQuery = SystemAPI.QueryBuilder().WithAll&lt;EnemyTag, Translation>().Build();

        foreach (var (enemyTag, translation) in enemyQuery)
        {
            // 적 엔티티가 있을 때 처리할 코드
            Debug.Log("Enemy found at position: " + translation.Value);
        }
    }
}
</pre>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">using Unity.Entities;
using Unity.Transforms;

public class TagComponentSearchSystem : SystemBase
{
    protected override void OnUpdate()
    {
        // PlayerTag를 가진 엔티티를 찾아서 처리
        Entities.ForEach((in PlayerTag playerTag, in Translation translation) =>
        {
            // 플레이어 엔티티가 있을 때 처리할 코드
            UnityEngine.Debug.Log("Player found at position: " + translation.Value);
        }).Run();

        // EnemyTag를 가진 엔티티를 찾아서 처리
        Entities.ForEach((in EnemyTag enemyTag, in Translation translation) =>
        {
            // 적 엔티티가 있을 때 처리할 코드
            UnityEngine.Debug.Log("Enemy found at position: " + translation.Value);
        }).Run();
    }
}
</pre>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<figure class="wp-block-embed is-type-wp-embed"><div class="wp-block-embed__wrapper">
<blockquote class="wp-embedded-content" data-secret="NsWJjWqn52"><a href="https://lycos7560.com/unity/ecs-component-concepts-2/39517/">ECS – Component concepts (2)</a></blockquote><iframe loading="lazy" class="wp-embedded-content" sandbox="allow-scripts" security="restricted"  title="&#8220;ECS – Component concepts (2)&#8221; &#8212; 어제와 내일의 나 그 사이의 이야기" src="https://lycos7560.com/unity/ecs-component-concepts-2/39517/embed/#?secret=peykJZsOel#?secret=NsWJjWqn52" data-secret="NsWJjWqn52" width="600" height="338" frameborder="0" marginwidth="0" marginheight="0" scrolling="no"></iframe>
</div></figure>
<p>The post <a href="https://lycos7560.com/unity/ecs-component-concepts-1/39513/">ECS &#8211; Component concepts (1)</a> appeared first on <a href="https://lycos7560.com">어제와 내일의 나 그 사이의 이야기</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://lycos7560.com/unity/ecs-component-concepts-1/39513/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>ECS &#8211; Entity concepts</title>
		<link>https://lycos7560.com/unity/ecs-entity-concepts/39529/</link>
					<comments>https://lycos7560.com/unity/ecs-entity-concepts/39529/#respond</comments>
		
		<dc:creator><![CDATA[lycos7560]]></dc:creator>
		<pubDate>Sat, 01 Feb 2025 01:58:48 +0000</pubDate>
				<category><![CDATA[Unity]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[Chunk components]]></category>
		<category><![CDATA[Cleanup components]]></category>
		<category><![CDATA[Component]]></category>
		<category><![CDATA[concept]]></category>
		<category><![CDATA[Data]]></category>
		<category><![CDATA[DOTS]]></category>
		<category><![CDATA[ECS]]></category>
		<category><![CDATA[Entity Component System]]></category>
		<category><![CDATA[job]]></category>
		<category><![CDATA[Job 시스템]]></category>
		<category><![CDATA[Managed Components]]></category>
		<category><![CDATA[Physics]]></category>
		<category><![CDATA[Sample]]></category>
		<category><![CDATA[Shared Components]]></category>
		<category><![CDATA[study]]></category>
		<category><![CDATA[Tag components]]></category>
		<category><![CDATA[Unity Physics 101]]></category>
		<category><![CDATA[Unmanaged]]></category>
		<category><![CDATA[Unmanaged components]]></category>
		<category><![CDATA[공부]]></category>
		<guid isPermaLink="false">https://lycos7560.com/?p=39529</guid>

					<description><![CDATA[<p>ECS &#8211; Entity concepts https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/concepts-entities.html 1. Entity란? 2. EntityManager (Entity 관리) EntityManager는 Entity를 생성, 삭제, 수정하는 다양한 메서드를 제공 메서드 설명 CreateEntity() 새로운 Entity 생성 Instantiate(Entity e) 기존 Entity를 복사하여 새로운 Entity 생성 DestroyEntity(Entity e) 특정 Entity 삭제 AddComponent&#60;T&#62;(Entity e) Entity에 특정 컴포넌트 추가 RemoveComponent&#60;T&#62;(Entity e) Entity에서 특정 컴포넌트 제거 GetComponent&#60;T&#62;(Entity e) Entity의 특정 컴포넌트 [&#8230;]</p>
<p>The post <a href="https://lycos7560.com/unity/ecs-entity-concepts/39529/">ECS &#8211; Entity concepts</a> appeared first on <a href="https://lycos7560.com">어제와 내일의 나 그 사이의 이야기</a>.</p>
]]></description>
										<content:encoded><![CDATA[				<div class="wp-block-uagb-table-of-contents uagb-toc__align-left uagb-toc__columns-1  uagb-block-e42d0765      "
					data-scroll= "1"
					data-offset= "30"
					style=""
				>
				<div class="uagb-toc__wrap">
						<div class="uagb-toc__title">
							목차						</div>
																						<div class="uagb-toc__list-wrap ">
						<ol class="uagb-toc__list"><li class="uagb-toc__list"><a href="#ecs-entity-concepts" class="uagb-toc-link__trigger">ECS &#8211; Entity concepts</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#1-entity란" class="uagb-toc-link__trigger">1. Entity란?</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#2-entitymanager-entity-관리" class="uagb-toc-link__trigger">2. EntityManager (Entity 관리)</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#3-entity와-archetype-아키타입" class="uagb-toc-link__trigger">3. Entity와 Archetype (아키타입)</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#4-entity-관리-구조" class="uagb-toc-link__trigger">4. Entity 관리 구조</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#요약" class="uagb-toc-link__trigger">요약</a></ul></ol>					</div>
									</div>
				</div>
			


<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading">ECS &#8211; Entity concepts</h2>



<p><a href="https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/concepts-entities.html" target="_blank" rel="noreferrer noopener">https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/concepts-entities.html</a></p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<figure class="wp-block-image size-full"><img decoding="async" width="695" height="278" src="https://lycos7560.com/wp-content/uploads/2025/02/image-1.png" alt="" class="wp-image-39532" srcset="https://lycos7560.com/wp-content/uploads/2025/02/image-1.png 695w, https://lycos7560.com/wp-content/uploads/2025/02/image-1-300x120.png 300w" sizes="(max-width: 695px) 100vw, 695px" /></figure>



<div style="height:25px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><strong>1. Entity란?</strong></h3>



<ul class="wp-block-list">
<li>프로그램 내에서 특정 개체(캐릭터, 이펙트, UI 요소 등)를 나타내는 <strong>단순한 ID</strong> 역할 </li>



<li>기존 <code>GameObject</code>와 유사하지만, 코드나 데이터를 포함하지 않고 <strong>컴포넌트들의 조합</strong>을 통해 기능을 정의</li>



<li>개별 Entity는 <strong>World</strong>에 속하며, <code>EntityManager</code>가 이를 관리</li>
</ul>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><strong>2. EntityManager (Entity</strong> <strong>관리)</strong></h3>



<p><code>EntityManager</code>는 <strong>Entity를 생성, 삭제, 수정</strong>하는 다양한 메서드를 제공</p>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>메서드</th><th>설명</th></tr></thead><tbody><tr><td><code>CreateEntity()</code></td><td>새로운  Entity 생성</td></tr><tr><td><code>Instantiate(Entity e)</code></td><td>기존 Entity를 복사하여 새로운 Entity 생성</td></tr><tr><td><code>DestroyEntity(Entity e)</code></td><td>특정 Entity 삭제</td></tr><tr><td><code>AddComponent&lt;T&gt;(Entity e)</code></td><td>Entity에 특정 컴포넌트 추가</td></tr><tr><td><code>RemoveComponent&lt;T&gt;(Entity e)</code></td><td>Entity에서 특정 컴포넌트 제거</td></tr><tr><td><code>GetComponent&lt;T&gt;(Entity e)</code></td><td>Entity의 특정 컴포넌트 값 가져오기</td></tr><tr><td><code>SetComponent&lt;T&gt;(Entity e, T data)</code></td><td>Entity의 특정 컴포넌트 값 변경</td></tr></tbody></table></figure>



<p><strong><strong>Entity</strong>의 생성/삭제는 구조적 변경(Structural Change)을 일으켜 성능에 영향을 줄 수 있음</strong></p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading">예시</h4>



<ul class="wp-block-list">
<li><strong>Entity 생성 &amp; 삭제</strong></li>
</ul>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">using Unity.Entities;
using Unity.Collections;
using UnityEngine;

public class EntityManagerExample : MonoBehaviour
{
    private EntityManager entityManager;
    private Entity entity;

    private void Start()
    {
        // 현재 월드의 EntityManager 가져오기
        entityManager = World.DefaultGameObjectInjectionWorld.EntityManager;

        // 새로운 Entity 생성 (빈 상태)
        entity = entityManager.CreateEntity();
        Debug.Log($"Entity 생성됨: {entity.Index}");

        // Entity 삭제
        entityManager.DestroyEntity(entity);
        Debug.Log("Entity 삭제됨");
    }
}
</pre>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<ul class="wp-block-list">
<li><strong>컴포넌트 추가 &amp; 제거</strong></li>
</ul>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">using Unity.Entities;
using UnityEngine;

// [IComponentData]를 상속받은 간단한 컴포넌트
public struct Health : IComponentData
{
    public int Value;
}

public class AddRemoveComponentExample : MonoBehaviour
{
    private EntityManager entityManager;
    private Entity entity;

    private void Start()
    {
        entityManager = World.DefaultGameObjectInjectionWorld.EntityManager;
        entity = entityManager.CreateEntity();

        // 컴포넌트 추가
        entityManager.AddComponentData(entity, new Health { Value = 100 });
        Debug.Log("Health 컴포넌트 추가됨");

        // 컴포넌트 제거
        entityManager.RemoveComponent&lt;Health>(entity);
        Debug.Log("Health 컴포넌트 제거됨");
    }
}
</pre>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<ul class="wp-block-list">
<li><strong>컴포넌트 값 읽기(Get) &amp; 수정(Set)</strong></li>
</ul>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">using Unity.Entities;
using UnityEngine;

public class GetSetComponentExample : MonoBehaviour
{
    private EntityManager entityManager;
    private Entity entity;

    private void Start()
    {
        entityManager = World.DefaultGameObjectInjectionWorld.EntityManager;
        entity = entityManager.CreateEntity();

        // Health 컴포넌트 추가 (초기 값 100)
        entityManager.AddComponentData(entity, new Health { Value = 100 });
        Debug.Log($"초기 체력: {entityManager.GetComponentData&lt;Health>(entity).Value}");

        // Health 값 변경
        entityManager.SetComponentData(entity, new Health { Value = 50 });
        Debug.Log($"변경된 체력: {entityManager.GetComponentData&lt;Health>(entity).Value}");
    }
}
</pre>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><strong>3. Entity와 <strong>Archetype</strong> (아키타입)</strong></h3>



<ul class="wp-block-list">
<li>Entity 자체에는 타입이 없으며, <strong>Entity를 구성하는 컴포넌트 조합</strong>이 그 특성을 결정</li>



<li><strong>같은 컴포넌트 조합을 가진 Entity들은 동일한 아키타입(Archetype)을 공유</strong></li>



<li><code>EntityManager</code>는 현재 존재하는 Entity들의 고유한 컴포넌트 조합(Archetype)을 추적</li>
</ul>



<p><strong><strong><strong>Archetype</strong></strong>을 활용하면 ECS 데이터 구조를 최적화하고 성능을 향상</strong>시킬 수 있음</p>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><strong>4. <strong><strong><strong>Entity</strong></strong></strong> 관리 구조</strong></h3>



<ul class="wp-block-list">
<li>모든 <strong>Entity</strong>는 <strong>World</strong> 내에서 관리됨</li>



<li><code>EntityManager</code>는 <strong><strong>Entity</strong>의 생성, 삭제 및 컴포넌트 조작을 담당</strong></li>



<li><strong>Entity</strong>는 <strong>컴포넌트들의 조합(Archetype)으로 정의됨</strong></li>
</ul>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><strong>요약</strong></h3>



<ul class="wp-block-list">
<li>Entity는 단순한 ID 역할을 하며, 실제 기능은 컴포넌트(Component)를 통해 정의됨</li>



<li><strong>EntityManager</strong>를 사용하여 Entity를 <strong>생성(Create), 삭제(Destroy), 수정(Set, Get)</strong> 가능</li>



<li>Entity는 <strong>컴포넌트 조합(Archetype) 단위로 그룹화</strong>되며, 동일한 Archetype을 가진 Entity 는 메모리에서 최적화됨</li>



<li>Entity의 생성/삭제는 구조적 변경(Structural Change)을 일으켜 성능에 영향을 줄 수 있음</li>
</ul>



<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>



<p></p>
<p>The post <a href="https://lycos7560.com/unity/ecs-entity-concepts/39529/">ECS &#8211; Entity concepts</a> appeared first on <a href="https://lycos7560.com">어제와 내일의 나 그 사이의 이야기</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://lycos7560.com/unity/ecs-entity-concepts/39529/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>ECS – Unity Physics Stateful Event Study</title>
		<link>https://lycos7560.com/unity/ecs-unity-physics-stateful-event-study/39489/</link>
					<comments>https://lycos7560.com/unity/ecs-unity-physics-stateful-event-study/39489/#respond</comments>
		
		<dc:creator><![CDATA[lycos7560]]></dc:creator>
		<pubDate>Fri, 31 Jan 2025 12:57:08 +0000</pubDate>
				<category><![CDATA[Unity]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[Component]]></category>
		<category><![CDATA[Data]]></category>
		<category><![CDATA[DOTS]]></category>
		<category><![CDATA[ECS]]></category>
		<category><![CDATA[Entity Component System]]></category>
		<category><![CDATA[job]]></category>
		<category><![CDATA[Job 시스템]]></category>
		<category><![CDATA[Physics]]></category>
		<category><![CDATA[Sample]]></category>
		<category><![CDATA[study]]></category>
		<category><![CDATA[Unity Physics 101]]></category>
		<category><![CDATA[공부]]></category>
		<category><![CDATA[기초]]></category>
		<guid isPermaLink="false">https://lycos7560.com/?p=39489</guid>

					<description><![CDATA[<p>StatefulTriggerEventBufferAuthoring.cs 코드 설명 이 코드는 TriggerEvent를 StatefulTriggerEvent로 변환하여 Dynamic Buffer에 저장하는 역할을 합니다. 특정 엔티티에 StatefulTriggerEventBufferAuthoring 컴포넌트를 추가하면 Trigger 이벤트의 상태(Enter, Stay, Exit)를 추적할 수 있습니다. Unity의 GameObject를 ECS(Entity-Component-System) 엔티티로 변환하는 역할 Baker 클래스에서 StatefulTriggerEvent를 저장할 Dynamic Buffer를 추가함 이를 통해 Trigger 이벤트를 저장하는 버퍼를 ECS 엔티티에 추가 가능 해당 엔티티는 StatefulTriggerEvent 버퍼를 가지게 됨. [&#8230;]</p>
<p>The post <a href="https://lycos7560.com/unity/ecs-unity-physics-stateful-event-study/39489/">ECS – Unity Physics Stateful Event Study</a> appeared first on <a href="https://lycos7560.com">어제와 내일의 나 그 사이의 이야기</a>.</p>
]]></description>
										<content:encoded><![CDATA[				<div class="wp-block-uagb-table-of-contents uagb-toc__align-left uagb-toc__columns-1  uagb-block-8cb6da3a      "
					data-scroll= "1"
					data-offset= "30"
					style=""
				>
				<div class="uagb-toc__wrap">
						<div class="uagb-toc__title">
							목차 테이블						</div>
																						<div class="uagb-toc__list-wrap ">
						<ol class="uagb-toc__list"><li class="uagb-toc__list"><a href="#statefultriggereventbufferauthoringcs" class="uagb-toc-link__trigger">StatefulTriggerEventBufferAuthoring.cs</a><li class="uagb-toc__list"><a href="#statefultriggereventbuffersystemcs" class="uagb-toc-link__trigger">StatefulTriggerEventBufferSystem.cs</a><li class="uagb-toc__list"><a href="#istatefulsimulationeventcs" class="uagb-toc-link__trigger">IStatefulSimulationEvent.cs</a><li class="uagb-toc__list"><a href="#statefuljobscs" class="uagb-toc-link__trigger">StatefulJobs.cs</a><li class="uagb-toc__list"><a href="#statefulsimulationeventbufferscs" class="uagb-toc-link__trigger">StatefulSimulationEventBuffers.cs</a><li class="uagb-toc__list"><a href="#statefulcollisioneventbufferauthoringcs" class="uagb-toc-link__trigger">StatefulCollisionEventBufferAuthoring.cs</a><li class="uagb-toc__list"><a href="#statefulcollisioneventbuffersystemcs" class="uagb-toc-link__trigger">StatefulCollisionEventBufferSystem.cs</a><li class="uagb-toc__list"><a href="#충돌-위치-쿼리" class="uagb-toc-link__trigger">충돌 위치 쿼리</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#1-충돌-이벤트에서-충돌-위치world-position-가져오기" class="uagb-toc-link__trigger">1. 충돌 이벤트에서 충돌 위치(World Position) 가져오기</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#2-여러-개의-접촉점contact-points-활용하기" class="uagb-toc-link__trigger">2. 여러 개의 접촉점(Contact Points) 활용하기</a></ul></ol>					</div>
									</div>
				</div>
			


<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><strong>StatefulTriggerEventBufferAuthoring.cs</strong></h3>



<p><strong>코드 설명</strong></p>



<p>이 코드는 <strong>TriggerEvent를 StatefulTriggerEvent로 변환하여 Dynamic Buffer에 저장</strong>하는 역할을 합니다.</p>



<p>특정 엔티티에 <code>StatefulTriggerEventBufferAuthoring</code> 컴포넌트를 추가하면 <strong>Trigger 이벤트의 상태(<code>Enter</code>, <code>Stay</code>, <code>Exit</code>)를 추적</strong>할 수 있습니다.</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">using Unity.Assertions;
using Unity.Entities;
using UnityEngine;

namespace Unity.Physics.Stateful
{
    // StatefulTriggerEventBufferAuthoring 컴포넌트
    // GameObject에 추가하면 해당 오브젝트는 ECS Entity로 변환될 때 StatefulTriggerEvent 버퍼를 갖게 됨.
    public class StatefulTriggerEventBufferAuthoring : MonoBehaviour
    {
        // Baker 클래스: GameObject를 ECS Entity로 변환하는 역할
        class Baker : Baker&lt;StatefulTriggerEventBufferAuthoring>
        {
            public override void Bake(StatefulTriggerEventBufferAuthoring authoring)
            {
                // 현재 GameObject를 ECS Entity로 변환 (Transform 사용 방식: 동적)
                var entity = GetEntity(TransformUsageFlags.Dynamic);

                // StatefulTriggerEvent를 저장할 Dynamic Buffer 추가
                AddBuffer&lt;StatefulTriggerEvent>(entity);
            }
        }
    }

    // StatefulTriggerEvent 구조체
    // Trigger 이벤트를 저장할 수 있는 데이터 구조 (IStatefulSimulationEvent 인터페이스 구현)
    public struct StatefulTriggerEvent : IBufferElementData, IStatefulSimulationEvent&lt;StatefulTriggerEvent>
    {
        // 트리거 이벤트에 연관된 두 개의 엔티티
        public Entity EntityA { get; set; }
        public Entity EntityB { get; set; }

        // 물리 본체(Body)의 인덱스
        public int BodyIndexA { get; set; }
        public int BodyIndexB { get; set; }

        // 충돌한 Collider의 키
        public ColliderKey ColliderKeyA { get; set; }
        public ColliderKey ColliderKeyB { get; set; }

        // 현재 이벤트 상태 (Enter, Stay, Exit)
        public StatefulEventState State { get; set; }

        // 생성자: 기존의 TriggerEvent 데이터를 StatefulTriggerEvent로 변환
        public StatefulTriggerEvent(TriggerEvent triggerEvent)
        {
            EntityA = triggerEvent.EntityA;
            EntityB = triggerEvent.EntityB;
            BodyIndexA = triggerEvent.BodyIndexA;
            BodyIndexB = triggerEvent.BodyIndexB;
            ColliderKeyA = triggerEvent.ColliderKeyA;
            ColliderKeyB = triggerEvent.ColliderKeyB;
            State = default; // 초기 상태 설정 (Enter, Stay, Exit 없음)
        }

        // 특정 엔티티에 대해, 이벤트에 연관된 '다른 엔티티'를 반환하는 메서드
        public Entity GetOtherEntity(Entity entity)
        {
            // 전달된 엔티티가 EntityA 또는 EntityB인지 검증 (디버그용)
            Assert.IsTrue((entity == EntityA) || (entity == EntityB));

            // EntityA가 입력되면 EntityB를 반환하고, 반대로 EntityB가 입력되면 EntityA 반환
            return (entity == EntityA) ? EntityB : EntityA;
        }

        // 이벤트 비교를 위한 메서드 (정렬, 검색 등에 사용 가능)
        public int CompareTo(StatefulTriggerEvent other)
        {
            return ISimulationEventUtilities.CompareEvents(this, other);
        }
    }

    // StatefulTriggerEvent를 제외하는 컴포넌트
    // 이 컴포넌트가 추가된 엔티티는 StatefulTriggerEventBufferSystem에서 이벤트를 수집하지 않음
    public struct StatefulTriggerEventExclude : IComponentData { }
}
</pre>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<figure class="wp-block-image size-full"><img decoding="async" width="706" height="348" src="https://lycos7560.com/wp-content/uploads/2025/01/image-141.png" alt="" class="wp-image-39490" srcset="https://lycos7560.com/wp-content/uploads/2025/01/image-141.png 706w, https://lycos7560.com/wp-content/uploads/2025/01/image-141-300x148.png 300w" sizes="(max-width: 706px) 100vw, 706px" /></figure>



<p><strong>Unity의 GameObject를 ECS(Entity-Component-System) 엔티티로 변환</strong>하는 역할</p>



<p><code>Baker</code> 클래스에서 <code>StatefulTriggerEvent</code>를 저장할 <strong>Dynamic Buffer</strong>를 추가함</p>



<p>이를 통해 <strong>Trigger 이벤트를 저장하는 버퍼를 ECS 엔티티에 추가 가능</strong></p>



<p>해당 엔티티는 <code>StatefulTriggerEvent</code> 버퍼를 가지게 됨.</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<figure class="wp-block-image size-full"><img decoding="async" width="906" height="710" src="https://lycos7560.com/wp-content/uploads/2025/01/image-142.png" alt="" class="wp-image-39491" srcset="https://lycos7560.com/wp-content/uploads/2025/01/image-142.png 906w, https://lycos7560.com/wp-content/uploads/2025/01/image-142-300x235.png 300w, https://lycos7560.com/wp-content/uploads/2025/01/image-142-768x602.png 768w" sizes="(max-width: 906px) 100vw, 906px" /></figure>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<ul class="wp-block-list">
<li><code>TriggerEvent</code> 정보를 저장하는 <strong>Stateful 버전</strong></li>



<li><code>StatefulEventState</code>를 추가하여 <strong>Trigger 이벤트의 상태(Enter, Stay, Exit)를 추적</strong></li>



<li><strong>기존 <code>TriggerEvent</code>는 한 프레임 동안만 존재</strong> → 이를 <code>StatefulTriggerEvent</code>로 변환하여 지속적인 상태 추적 가능</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>필드명</th><th>설명</th></tr></thead><tbody><tr><td><code>EntityA</code>, <code>EntityB</code></td><td>트리거 이벤트가 발생한 두 개의 엔티티</td></tr><tr><td><code>BodyIndexA</code>, <code>BodyIndexB</code></td><td>각각의 물리 본체(Body)의 인덱스</td></tr><tr><td><code>ColliderKeyA</code>, <code>ColliderKeyB</code></td><td>충돌한 <code>Collider</code>의 키</td></tr><tr><td><code>State</code></td><td><strong>Enter, Stay, Exit</strong> 상태를 저장</td></tr></tbody></table></figure>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<figure class="wp-block-image size-full"><img decoding="async" width="796" height="227" src="https://lycos7560.com/wp-content/uploads/2025/01/image-143.png" alt="" class="wp-image-39493" srcset="https://lycos7560.com/wp-content/uploads/2025/01/image-143.png 796w, https://lycos7560.com/wp-content/uploads/2025/01/image-143-300x86.png 300w, https://lycos7560.com/wp-content/uploads/2025/01/image-143-768x219.png 768w" sizes="(max-width: 796px) 100vw, 796px" /></figure>



<ul class="wp-block-list">
<li>특정 엔티티가 <strong>트리거 이벤트에 포함된 다른 엔티티를 가져옴</strong></li>



<li><code>EntityA</code>를 전달하면 <code>EntityB</code>를 반환하고, 반대로 <code>EntityB</code>를 전달하면 <code>EntityA</code>를 반환</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<figure class="wp-block-image size-full"><img decoding="async" width="785" height="108" src="https://lycos7560.com/wp-content/uploads/2025/01/image-144.png" alt="" class="wp-image-39494" srcset="https://lycos7560.com/wp-content/uploads/2025/01/image-144.png 785w, https://lycos7560.com/wp-content/uploads/2025/01/image-144-300x41.png 300w, https://lycos7560.com/wp-content/uploads/2025/01/image-144-768x106.png 768w" sizes="(max-width: 785px) 100vw, 785px" /></figure>



<p><code>StatefulTriggerEventBufferSystem</code>이 <strong>특정 엔티티의 트리거 이벤트를 저장하지 않도록</strong> 제외 처리 (쿼리에서 제외)</p>



<hr class="wp-block-separator has-alpha-channel-opacity is-style-wide" style="margin-top:var(--wp--preset--spacing--80);margin-bottom:var(--wp--preset--spacing--80)"/>



<h3 class="wp-block-heading"><strong>StatefulTriggerEventBufferSystem.cs</strong></h3>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p><strong>코드 설명</strong></p>



<ol class="wp-block-list">
<li><strong>트리거 이벤트를 <strong>StatefulTriggerEvent</strong>로 변환하여 <strong>Dynamic Buffer</strong>에 저장</strong></li>



<li><code>Enter</code>, <code>Stay</code>, <code>Exit</code> 이벤트를 추적하여 충돌 지속 여부를 확인 가능</li>



<li><code>OnCreate()</code>
<ul class="wp-block-list">
<li><code>StatefulSimulationEventBuffers</code>를 생성하고 이벤트 저장을 위한 버퍼를 할당</li>



<li><code>StatefulTriggerEvent</code>를 포함하고 <code>StatefulTriggerEventExclude</code>가 없는 엔티티를 찾는 쿼리 설정</li>
</ul>
</li>



<li><code>OnUpdate()</code>
<ul class="wp-block-list">
<li>기존 이벤트 버퍼 초기화</li>



<li><code>TriggerEvent</code> 데이터를 수집하고, <strong>StatefulTriggerEvent로 변환하여 저장</strong></li>
</ul>
</li>



<li><code>OnDestroy()</code>
<ul class="wp-block-list">
<li>사용했던 메모리를 해제하여 최적화</li>
</ul>
</li>
</ol>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">using Unity.Entities;
using Unity.Jobs;
using Unity.Physics.Systems;
using Unity.Collections;
using Unity.Burst;

namespace Unity.Physics.Stateful
{
    // 이 시스템은 TriggerEvent의 스트림을 StatefulTriggerEvent로 변환하여 Dynamic Buffer에 저장할 수 있도록 합니다.
    // 이를 통해 충돌 상태(Enter, Stay, Exit)를 추적할 수 있습니다.
    [UpdateInGroup(typeof(PhysicsSystemGroup))] // PhysicsSystemGroup 내에서 실행됨
    [UpdateAfter(typeof(PhysicsSimulationGroup))] // PhysicsSimulationGroup 이후에 실행됨
    public partial struct StatefulTriggerEventBufferSystem : ISystem
    {
        // 현재 및 이전 프레임의 Trigger 이벤트를 저장하는 버퍼
        private StatefulSimulationEventBuffers&lt;StatefulTriggerEvent> m_StateFulEventBuffers;
        // 이벤트 처리에 필요한 컴포넌트 조회 핸들
        private ComponentHandles m_ComponentHandles;
        // 트리거 이벤트를 처리할 엔티티 쿼리
        private EntityQuery m_TriggerEventQuery;

        // 이벤트를 저장할 컴포넌트 핸들 구조체
        struct ComponentHandles
        {
            // 특정 이벤트를 제외할 엔티티 조회
            public ComponentLookup&lt;StatefulTriggerEventExclude> EventExcludes;
            // StatefulTriggerEvent를 저장할 Dynamic Buffer 조회
            public BufferLookup&lt;StatefulTriggerEvent> EventBuffers;

            // 컴포넌트 핸들 초기화
            public ComponentHandles(ref SystemState systemState)
            {
                EventExcludes = systemState.GetComponentLookup&lt;StatefulTriggerEventExclude>(true);
                EventBuffers = systemState.GetBufferLookup&lt;StatefulTriggerEvent>();
            }

            // 최신 상태로 업데이트
            public void Update(ref SystemState systemState)
            {
                EventExcludes.Update(ref systemState);
                EventBuffers.Update(ref systemState);
            }
        }

        [BurstCompile]
        public void OnCreate(ref SystemState state)
        {
            // 트리거 이벤트를 처리할 엔티티 쿼리 생성
            // (StatefulTriggerEvent가 있으며, StatefulTriggerEventExclude가 없는 엔티티)
            EntityQueryBuilder builder = new EntityQueryBuilder(Allocator.Temp)
                .WithAllRW&lt;StatefulTriggerEvent>() // StatefulTriggerEvent를 읽고 쓸 수 있는 엔티티 선택
                .WithNone&lt;StatefulTriggerEventExclude>(); // StatefulTriggerEventExclude가 없는 엔티티만 선택

            // Stateful 이벤트 버퍼를 생성하고 할당
            m_StateFulEventBuffers = new StatefulSimulationEventBuffers&lt;StatefulTriggerEvent>();
            m_StateFulEventBuffers.AllocateBuffers();

            // 쿼리 설정 및 시스템 업데이트 필요성 명시
            m_TriggerEventQuery = state.GetEntityQuery(builder);
            state.RequireForUpdate(m_TriggerEventQuery);

            // 컴포넌트 핸들 초기화
            m_ComponentHandles = new ComponentHandles(ref state);
        }

        [BurstCompile]
        public void OnDestroy(ref SystemState state)
        {
            // Stateful 이벤트 버퍼 해제
            m_StateFulEventBuffers.Dispose();
        }

        // 트리거 이벤트 버퍼를 초기화하는 Job
        [BurstCompile]
        public partial struct ClearTriggerEventDynamicBufferJob : IJobEntity
        {
            // 이벤트 버퍼 초기화
            public void Execute(ref DynamicBuffer&lt;StatefulTriggerEvent> eventBuffer) => eventBuffer.Clear();
        }

        [BurstCompile]
        public void OnUpdate(ref SystemState state)
        {
            // 최신 상태로 컴포넌트 핸들 업데이트
            m_ComponentHandles.Update(ref state);

            // 기존 트리거 이벤트 버퍼 초기화 (병렬 실행)
            state.Dependency = new ClearTriggerEventDynamicBufferJob() .ScheduleParallel(m_TriggerEventQuery, state.Dependency);

            // 이전 프레임과 현재 프레임의 이벤트 버퍼를 교체
            m_StateFulEventBuffers.SwapBuffers();

            var currentEvents = m_StateFulEventBuffers.Current; // 현재 프레임의 이벤트
            var previousEvents = m_StateFulEventBuffers.Previous; // 이전 프레임의 이벤트

            // 새로운 Trigger 이벤트를 수집하는 Job 실행
            state.Dependency = new StatefulEventCollectionJobs.CollectTriggerEvents
            {
                TriggerEvents = currentEvents
            }.Schedule(SystemAPI.GetSingleton&lt;SimulationSingleton>(), state.Dependency);

            // 트리거 이벤트를 StatefulTriggerEvent로 변환하여 Dynamic Buffer에 저장하는 Job 실행
            state.Dependency = new StatefulEventCollectionJobs.ConvertEventStreamToDynamicBufferJob&lt;StatefulTriggerEvent, StatefulTriggerEventExclude>
            {
                CurrentEvents = currentEvents, // 현재 프레임의 이벤트
                PreviousEvents = previousEvents, // 이전 프레임의 이벤트
                EventLookup = m_ComponentHandles.EventBuffers, // 이벤트 버퍼 조회

                UseExcludeComponent = true, // 제외할 컴포넌트(StatefulTriggerEventExclude) 사용 여부
                EventExcludeLookup = m_ComponentHandles.EventExcludes // 제외할 이벤트 조회
            }.Schedule(state.Dependency);
        }
    }
}
</pre>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<figure class="wp-block-image size-full"><img decoding="async" width="745" height="146" src="https://lycos7560.com/wp-content/uploads/2025/01/image-145.png" alt="" class="wp-image-39495" srcset="https://lycos7560.com/wp-content/uploads/2025/01/image-145.png 745w, https://lycos7560.com/wp-content/uploads/2025/01/image-145-300x59.png 300w" sizes="(max-width: 745px) 100vw, 745px" /></figure>



<figure class="wp-block-image size-full"><img decoding="async" width="801" height="502" src="https://lycos7560.com/wp-content/uploads/2025/01/image-146.png" alt="" class="wp-image-39496" srcset="https://lycos7560.com/wp-content/uploads/2025/01/image-146.png 801w, https://lycos7560.com/wp-content/uploads/2025/01/image-146-300x188.png 300w, https://lycos7560.com/wp-content/uploads/2025/01/image-146-768x481.png 768w" sizes="(max-width: 801px) 100vw, 801px" /></figure>



<p><code>ComponentHandles</code>: <strong>StatefulTriggerEventExclude</strong> 및 <strong>StatefulTriggerEvent</strong> 버퍼를 조회하는 핸들을 보유하는 구조체</p>



<ul class="wp-block-list">
<li><strong><code>EventExcludes</code></strong>: 트리거 이벤트가 <strong>수집되지 않도록 제외할 엔티티</strong>를 조회합니다.</li>



<li><strong><code>EventBuffers</code></strong>: <strong>StatefulTriggerEvent</strong>를 저장하는 <strong>Dynamic Buffer</strong>를 조회합니다.</li>
</ul>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<figure class="wp-block-image size-full"><img decoding="async" width="947" height="442" src="https://lycos7560.com/wp-content/uploads/2025/01/image-147.png" alt="" class="wp-image-39497" srcset="https://lycos7560.com/wp-content/uploads/2025/01/image-147.png 947w, https://lycos7560.com/wp-content/uploads/2025/01/image-147-300x140.png 300w, https://lycos7560.com/wp-content/uploads/2025/01/image-147-768x358.png 768w" sizes="(max-width: 947px) 100vw, 947px" /></figure>



<p><strong>트리거 이벤트를 처리할 쿼리 생성</strong>:</p>



<ul class="wp-block-list">
<li><code>StatefulTriggerEvent</code> 컴포넌트를 <strong>읽고 쓸 수 있는 엔티티</strong>를 선택하고,</li>



<li><code>StatefulTriggerEventExclude</code>가 없는 엔티티만 선택하여 트리거 이벤트를 수집합니다.</li>
</ul>



<p><code>m_StateFulEventBuffers</code>를 초기화하고, <strong>버퍼를 할당</strong>합니다.</p>



<p><code>m_TriggerEventQuery</code> 쿼리를 설정하여 이 쿼리가 <strong>시스템 업데이트에 필요</strong>함을 명시합니다.</p>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<figure class="wp-block-image size-full"><img decoding="async" width="1260" height="668" src="https://lycos7560.com/wp-content/uploads/2025/01/image-148.png" alt="" class="wp-image-39498" srcset="https://lycos7560.com/wp-content/uploads/2025/01/image-148.png 1260w, https://lycos7560.com/wp-content/uploads/2025/01/image-148-300x159.png 300w, https://lycos7560.com/wp-content/uploads/2025/01/image-148-768x407.png 768w" sizes="(max-width: 1260px) 100vw, 1260px" /></figure>



<p><strong>컴포넌트 핸들 업데이트</strong>: <code>m_ComponentHandles</code>를 최신 상태로 업데이트합니다.</p>



<p><strong>버퍼 초기화</strong>: <code>ClearTriggerEventDynamicBufferJob</code>을 병렬로 실행하여 <strong>이전 트리거 이벤트 버퍼를 초기화</strong>합니다.</p>



<p><strong>이벤트 버퍼 교체</strong>: <code>SwapBuffers</code>를 사용하여 <strong>현재 프레임과 이전 프레임의 버퍼를 교체</strong>합니다.</p>



<p><strong>Trigger 이벤트 수집</strong>: <code>StatefulEventCollectionJobs.CollectTriggerEvents</code>를 통해 <strong>새로운 트리거 이벤트를 수집</strong>합니다.</p>



<p><strong>StatefulTriggerEvent 변환</strong>: 수집한 트리거 이벤트를 <strong>StatefulTriggerEvent</strong>로 변환하고 <strong>Dynamic Buffer</strong>에 저장합니다.</p>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<hr class="wp-block-separator has-alpha-channel-opacity is-style-wide" style="margin-top:var(--wp--preset--spacing--80);margin-bottom:var(--wp--preset--spacing--80)"/>



<h3 class="wp-block-heading">IStatefulSimulationEvent.cs</h3>



<p>충돌 상태 변화에 대한 추적을 가능하게 하여, <strong>물체 간 상호작용의 상태를 세분화</strong>하고, 이를 <strong>이벤트 시스템</strong>을 통해 관리</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">using Unity.Entities;

namespace Unity.Physics.Stateful
{
    // 이벤트 상태를 설명합니다.
    // 이벤트 상태는 다음과 같이 설정됩니다:
    //    0) Undefined (정의되지 않음): 상태가 불명확하거나 필요하지 않을 때
    //    1) Enter (진입): 두 물체가 현재 프레임에서 상호작용하고 있지만, 이전 프레임에서는 상호작용하지 않았을 때
    //    2) Stay (유지): 두 물체가 현재 프레임에서 상호작용 중이고, 이전 프레임에서도 상호작용했을 때
    //    3) Exit (탈출): 두 물체가 현재 프레임에서 상호작용하지 않지만, 이전 프레임에서는 상호작용했을 때
    public enum StatefulEventState : byte
    {
        Undefined,  // 상태가 정의되지 않음
        Enter,      // 두 물체가 현재 프레임에서 상호작용 중이지만 이전 프레임에서는 상호작용하지 않았을 때
        Stay,       // 두 물체가 현재 프레임과 이전 프레임에서 모두 상호작용하고 있을 때
        Exit        // 두 물체가 현재 프레임에서 상호작용하지 않지만 이전 프레임에서는 상호작용했을 때
    }

    // 추가적인 StatefulEventState를 포함하여 ISimulationEvent를 확장합니다.
    public interface IStatefulSimulationEvent&lt;T> : IBufferElementData, ISimulationEvent&lt;T>
    {
        public StatefulEventState State { get; set; } // 이벤트 상태
    }
}
</pre>



<hr class="wp-block-separator has-alpha-channel-opacity is-style-wide" style="margin-top:var(--wp--preset--spacing--80);margin-bottom:var(--wp--preset--spacing--80)"/>



<h3 class="wp-block-heading">StatefulJobs.cs</h3>



<p>Unity에서 물리 시뮬레이션의 충돌 이벤트를 상태 기반으로 추적하고, 이를 <code>Dynamic Buffer</code>에 저장하는 시스템을 구현하는 데 사용됩니다. </p>



<p>여기서는 <code>StatefulEventCollectionJobs</code>라는 이름의 클래스가 주요한 역할을 합니다. </p>



<p>여러 개의 Job을 정의하여 충돌 이벤트를 수집하고, 이를 상태 기반 이벤트로 변환하여 저장합니다.</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">using Unity.Burst;
using Unity.Collections;
using Unity.Entities;
using Unity.Jobs;

namespace Unity.Physics.Stateful
{
    public static class StatefulEventCollectionJobs
    {
        // Trigger 이벤트를 StatefulTriggerEvent로 변환하여 NativeList에 추가하는 Job
        [BurstCompile]
        public struct CollectTriggerEvents : ITriggerEventsJob
        {
            // 저장할 Trigger 이벤트를 담을 리스트
            public NativeList&lt;StatefulTriggerEvent> TriggerEvents;

            // TriggerEvent를 StatefulTriggerEvent로 변환하여 리스트에 추가
            public void Execute(TriggerEvent triggerEvent) => TriggerEvents.Add(new StatefulTriggerEvent(triggerEvent));
        }

        // Collision 이벤트를 StatefulCollisionEvent로 변환하여 NativeList에 추가하는 Job
        [BurstCompile]
        public struct CollectCollisionEvents : ICollisionEventsJob
        {
            // 저장할 Collision 이벤트를 담을 리스트
            public NativeList&lt;StatefulCollisionEvent> CollisionEvents;

            // CollisionEvent를 StatefulCollisionEvent로 변환하여 리스트에 추가
            public void Execute(CollisionEvent collisionEvent) => CollisionEvents.Add(new StatefulCollisionEvent(collisionEvent));
        }

        // Collision 이벤트와 함께 세부 정보를 계산하여 StatefulCollisionEvent로 변환하고 NativeList에 추가하는 Job
        [BurstCompile]
        public struct CollectCollisionEventsWithDetails : ICollisionEventsJob
        {
            // 저장할 Collision 이벤트를 담을 리스트
            public NativeList&lt;StatefulCollisionEvent> CollisionEvents;

            // 물리 월드와 세부 사항 계산에 사용할 컴포넌트 조회
            [ReadOnly] public PhysicsWorld PhysicsWorld;
            [ReadOnly] public ComponentLookup&lt;StatefulCollisionEventDetails> EventDetails;

            // 세부 사항 계산을 강제할지 여부
            public bool ForceCalculateDetails;

            // CollisionEvent를 처리하고 세부 사항을 계산하여 StatefulCollisionEvent로 변환
            public void Execute(CollisionEvent collisionEvent)
            {
                var statefulCollisionEvent = new StatefulCollisionEvent(collisionEvent);

                // 세부 사항 계산 여부 결정
                bool calculateDetails = ForceCalculateDetails;
                if (!calculateDetails &amp;&amp; EventDetails.HasComponent(collisionEvent.EntityA))
                {
                    calculateDetails = EventDetails[collisionEvent.EntityA].CalculateDetails;
                }
                if (!calculateDetails &amp;&amp; EventDetails.HasComponent(collisionEvent.EntityB))
                {
                    calculateDetails = EventDetails[collisionEvent.EntityB].CalculateDetails;
                }

                // 세부 사항을 계산하고 상태에 저장
                if (calculateDetails)
                {
                    var details = collisionEvent.CalculateDetails(ref PhysicsWorld);
                    statefulCollisionEvent.CollisionDetails = new StatefulCollisionEvent.Details(
                        details.EstimatedContactPointPositions.Length,
                        details.EstimatedImpulse,
                        details.AverageContactPointPosition);
                }

                // 계산된 CollisionEvent를 리스트에 추가
                CollisionEvents.Add(statefulCollisionEvent);
            }
        }

        // 이벤트 스트림을 Dynamic Buffer로 변환하는 Job
        // T: StatefulSimulationEvent 타입
        // C: 제외할 컴포넌트 타입
        [BurstCompile]
        public struct ConvertEventStreamToDynamicBufferJob&lt;T, C> : IJob
            where T : unmanaged, IBufferElementData, IStatefulSimulationEvent&lt;T>  // T는 IStatefulSimulationEvent를 구현해야 함
            where C : unmanaged, IComponentData  // C는 IComponentData를 구현해야 함
        {
            // 이전과 현재의 이벤트 리스트
            public NativeList&lt;T> PreviousEvents;
            public NativeList&lt;T> CurrentEvents;

            // 이벤트를 저장할 Dynamic Buffer 조회
            public BufferLookup&lt;T> EventLookup;

            // 제외할 컴포넌트를 사용할지 여부
            public bool UseExcludeComponent;

            // 제외할 컴포넌트를 조회하는 컴포넌트 핸들
            [ReadOnly] public ComponentLookup&lt;C> EventExcludeLookup;

            // 이벤트를 변환하고 Dynamic Buffer에 추가하는 작업
            public void Execute()
            {
                // 이벤트를 저장할 임시 리스트
                var statefulEvents = new NativeList&lt;T>(CurrentEvents.Length, Allocator.Temp);

                // 이전 및 현재 이벤트에서 상태를 기반으로 상태 변경된 이벤트를 가져옴
                StatefulSimulationEventBuffers&lt;T>.GetStatefulEvents(PreviousEvents, CurrentEvents, statefulEvents);

                // 상태가 변경된 이벤트를 처리
                for (int i = 0; i &lt; statefulEvents.Length; i++)
                {
                    var statefulEvent = statefulEvents[i];

                    // 이벤트가 추가될 엔티티 A와 B가 동적으로 버퍼를 가지고 있는지 확인
                    var addToEntityA = EventLookup.HasBuffer(statefulEvent.EntityA) &amp;&amp;
                        (!UseExcludeComponent || !EventExcludeLookup.HasComponent(statefulEvent.EntityA));
                    var addToEntityB = EventLookup.HasBuffer(statefulEvent.EntityB) &amp;&amp;
                        (!UseExcludeComponent || !EventExcludeLookup.HasComponent(statefulEvent.EntityA));

                    // 엔티티 A와 B에 이벤트를 추가
                    if (addToEntityA)
                    {
                        EventLookup[statefulEvent.EntityA].Add(statefulEvent);
                    }

                    if (addToEntityB)
                    {
                        EventLookup[statefulEvent.EntityB].Add(statefulEvent);
                    }
                }
            }
        }
    }
}
</pre>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<figure class="wp-block-image size-full"><img decoding="async" width="1037" height="250" src="https://lycos7560.com/wp-content/uploads/2025/01/image-149.png" alt="" class="wp-image-39499" srcset="https://lycos7560.com/wp-content/uploads/2025/01/image-149.png 1037w, https://lycos7560.com/wp-content/uploads/2025/01/image-149-300x72.png 300w, https://lycos7560.com/wp-content/uploads/2025/01/image-149-768x185.png 768w" sizes="(max-width: 1037px) 100vw, 1037px" /></figure>



<p> <code>TriggerEvent</code> 객체를 받아 <code>StatefulTriggerEvent</code>로 변환한 후 <code>TriggerEvents</code> 리스트에 추가하는 작업을 수행합니다.</p>



<p><strong><code>TriggerEvent</code></strong>: 두 개체 간의 충돌 여부를 나타내는 기본적인 이벤트입니다.</p>



<p><strong><code>StatefulTriggerEvent</code></strong>: 이 구조체는 충돌 상태를 추적할 수 있도록 <code>StatefulEventState</code> (Enter, Stay, Exit, Undefined) 상태를 추가하여 <code>TriggerEvent</code>를 확장한 것입니다.</p>



<p><code>Execute</code> 메서드는 주어진 <code>TriggerEvent</code>를 <code>StatefulTriggerEvent</code>로 변환하여 <code>TriggerEvents</code> 리스트에 추가합니다.</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<figure class="wp-block-image size-full"><img decoding="async" width="1059" height="248" src="https://lycos7560.com/wp-content/uploads/2025/01/image-150.png" alt="" class="wp-image-39500" srcset="https://lycos7560.com/wp-content/uploads/2025/01/image-150.png 1059w, https://lycos7560.com/wp-content/uploads/2025/01/image-150-300x70.png 300w, https://lycos7560.com/wp-content/uploads/2025/01/image-150-768x180.png 768w" sizes="(max-width: 1059px) 100vw, 1059px" /></figure>



<p><code>CollisionEvent</code>를 받아 <code>StatefulCollisionEvent</code>로 변환한 후 <code>CollisionEvents</code> 리스트에 추가합니다.</p>



<p><strong><code>CollisionEvent</code></strong>: 두 개체 간의 충돌을 나타내는 이벤트입니다.</p>



<p><strong><code>StatefulCollisionEvent</code></strong>: 충돌 상태를 추적하기 위해 <code>StatefulEventState</code>를 추가한 <code>CollisionEvent</code>입니다.</p>



<p><code>Execute</code> 메서드는 <code>CollisionEvent</code>를 <code>StatefulCollisionEvent</code>로 변환하여 <code>CollisionEvents</code> 리스트에 추가합니다.</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<figure class="wp-block-image size-full"><img decoding="async" width="788" height="824" src="https://lycos7560.com/wp-content/uploads/2025/01/image-151.png" alt="" class="wp-image-39501" srcset="https://lycos7560.com/wp-content/uploads/2025/01/image-151.png 788w, https://lycos7560.com/wp-content/uploads/2025/01/image-151-287x300.png 287w, https://lycos7560.com/wp-content/uploads/2025/01/image-151-768x803.png 768w" sizes="(max-width: 788px) 100vw, 788px" /></figure>



<p>충돌 이벤트와 함께 세부 정보를 계산하고 이를 <code>StatefulCollisionEvent</code>로 변환하여 <code>CollisionEvents</code> 리스트에 추가합니다.</p>



<p><strong><code>ForceCalculateDetails</code></strong>: 세부 사항을 강제로 계산할지 여부를 나타내는 플래그입니다.</p>



<p><strong><code>EventDetails</code></strong>: 각 개체에 대한 세부 사항을 계산할지 여부를 결정하는 <code>StatefulCollisionEventDetails</code> 컴포넌트의 조회입니다.</p>



<ul class="wp-block-list">
<li><code>Execute</code> 메서드는 <code>CollisionEvent</code>를 <code>StatefulCollisionEvent</code>로 변환한 후, 해당 이벤트에 대해 세부 사항을 계산합니다.</li>



<li><code>CalculateDetails</code> 메서드는 충돌 세부 사항을 계산하고, 그 결과를 <code>StatefulCollisionEvent</code>의 <code>CollisionDetails</code>에 저장합니다.</li>



<li>세부 사항을 계산할 필요가 없다면 기본적으로 세부 사항을 계산하지 않습니다.</li>
</ul>



<p>세부 사항 계산</p>



<p><strong>Estimated Contact Point Positions (예상 접촉 지점 위치)</strong>:</p>



<ul class="wp-block-list">
<li>충돌이 발생했을 때 예상되는 접촉 지점의 위치들</li>



<li>이는 충돌이 발생한 표면에서 실제로 물리적으로 접촉한 지점들</li>



<li>예를 들어, 두 물체가 충돌하면서 여러 접촉 지점이 생길 수 있다.</li>
</ul>



<p><strong>Estimated Impulse (예상 충격량)</strong>:</p>



<ul class="wp-block-list">
<li>두 객체 간의 충돌로 발생하는 충격량입니다. 충돌 후 물체가 받을 힘을 측정하는 값</li>



<li>충돌 전후의 물리적 특성(속도 변화, 질량 등)을 바탕으로 계산</li>



<li>이는 두 객체 간의 충돌로 인해 변화하는 운동량</li>
</ul>



<p><strong>Average Contact Point Position (평균 접촉 지점 위치)</strong>:</p>



<ul class="wp-block-list">
<li>여러 접촉 지점이 있다면, 그 중에서 평균적인 접촉 지점 위치를 계산한 것</li>



<li>이는 충돌이 발생한 여러 지점들 중에서, 전체적인 충격을 대표할 수 있는 중간 지점을 제공하는 역할</li>
</ul>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<figure class="wp-block-image size-full"><img decoding="async" width="907" height="872" src="https://lycos7560.com/wp-content/uploads/2025/01/image-152.png" alt="" class="wp-image-39502" srcset="https://lycos7560.com/wp-content/uploads/2025/01/image-152.png 907w, https://lycos7560.com/wp-content/uploads/2025/01/image-152-300x288.png 300w, https://lycos7560.com/wp-content/uploads/2025/01/image-152-768x738.png 768w" sizes="(max-width: 907px) 100vw, 907px" /></figure>



<p>상태 기반 이벤트 스트림을 <code>Dynamic Buffer</code>로 변환하는 작업을 수행합니다.</p>



<p><strong><code>EventLookup</code></strong>: <code>StatefulTriggerEvent</code>와 <code>StatefulCollisionEvent</code>와 같은 상태 기반 이벤트를 저장할 <code>Dynamic Buffer</code>의 조회입니다.</p>



<p><strong><code>UseExcludeComponent</code></strong>: 특정 컴포넌트를 제외할지 여부를 결정하는 플래그입니다.</p>



<ul class="wp-block-list">
<li><code>PreviousEvents</code>와 <code>CurrentEvents</code>에서 상태 기반 이벤트를 비교하여 상태 변화가 있는 이벤트를 가져옵니다.</li>



<li>변환된 이벤트를 <code>EventLookup</code>에 해당하는 엔티티의 <code>Dynamic Buffer</code>에 추가합니다.</li>



<li><code>EventExcludeLookup</code>를 사용하여 제외할 컴포넌트가 있으면 해당 엔티티에 대한 이벤트 추가를 방지합니다.</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity is-style-wide" style="margin-top:var(--wp--preset--spacing--80);margin-bottom:var(--wp--preset--spacing--80)"/>



<h3 class="wp-block-heading">StatefulSimulationEventBuffers.cs</h3>



<p><code>StatefulSimulationEventBuffers</code>라는 구조체로, 상태를 추적할 수 있는 이벤트 버퍼를 관리합니다. </p>



<p>정의된 함수들은 물리 시뮬레이션 이벤트의 상태를 추적하고 이를 처리하는데 필요한 버퍼 관리 및 이벤트 비교 기능을 제공합니다. (주로 충돌이나 상호작용 이벤트를 처리 )</p>



<p>각 이벤트가 이전 프레임과 비교하여 어떤 상태(Enter, Stay, Exit)에 해당하는지 판단하고, 이를 기반으로 상태를 변경하여 결과를 반환합니다.</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">using Unity.Collections;

namespace Unity.Physics.Stateful
{
    // StatefulSimulationEventBuffers는 이벤트의 상태를 추적하는 두 버퍼를 관리하는 구조체입니다.
    // 이 구조체는 지난 프레임과 현재 프레임의 이벤트를 저장하며, 충돌 이벤트에 대한 상태 변경을 추적합니다.
    public struct StatefulSimulationEventBuffers&lt;T> where T : unmanaged, IStatefulSimulationEvent&lt;T>
    {
        // 이전 프레임의 이벤트를 저장하는 리스트
        public NativeList&lt;T> Previous;
        // 현재 프레임의 이벤트를 저장하는 리스트
        public NativeList&lt;T> Current;

        // 이벤트 버퍼 할당
        public void AllocateBuffers()
        {
            // 이전 및 현재 프레임 이벤트를 저장할 버퍼를 할당
            Previous = new NativeList&lt;T>(Allocator.Persistent);
            Current = new NativeList&lt;T>(Allocator.Persistent);
        }

        // 이벤트 버퍼를 해제
        public void Dispose()
        {
            // 버퍼가 생성되었으면 해제
            if (Previous.IsCreated) Previous.Dispose();
            if (Current.IsCreated) Current.Dispose();
        }

        // 이전 프레임과 현재 프레임의 버퍼를 교환
        public void SwapBuffers()
        {
            // 이전과 현재 이벤트 버퍼를 교환
            var tmp = Previous;
            Previous = Current;
            Current = tmp;
            // 현재 버퍼를 비움 (새로운 이벤트로 덮어쓰기 위해)
            Current.Clear();
        }

        /// &lt;summary>
        /// 이전 프레임과 현재 프레임의 이벤트를 결합하여 하나의 정렬된 리스트로 반환합니다.
        /// 반드시 SortBuffers를 먼저 호출하여 정렬된 상태로 만들어야 합니다.
        /// &lt;/summary>
        /// &lt;param name="statefulEvents">결합된 이벤트 리스트&lt;/param>
        /// &lt;param name="sortCurrent">현재 이벤트 리스트를 정렬해야 하는지 여부&lt;/param>
        public void GetStatefulEvents(NativeList&lt;T> statefulEvents, bool sortCurrent = true) =>
            GetStatefulEvents(Previous, Current, statefulEvents, sortCurrent);

        /// &lt;summary>
        /// 두 개의 정렬된 이벤트 버퍼를 결합하여 하나의 리스트로 반환합니다.
        /// 각 이벤트에는 적절한 StatefulEventState가 설정됩니다.
        /// &lt;/summary>
        /// &lt;param name="previousEvents">이전 프레임의 이벤트 버퍼&lt;/param>
        /// &lt;param name="currentEvents">현재 프레임의 이벤트 버퍼&lt;/param>
        /// &lt;param name="statefulEvents">결합된 이벤트 리스트&lt;/param>
        /// &lt;param name="sortCurrent">현재 이벤트 리스트를 정렬해야 하는지 여부&lt;/param>
        public static void GetStatefulEvents(NativeList&lt;T> previousEvents, NativeList&lt;T> currentEvents, NativeList&lt;T> statefulEvents, bool sortCurrent = true)
        {
            // 현재 이벤트 리스트가 정렬되지 않았다면 정렬
            if (sortCurrent) currentEvents.Sort();

            // 새로운 리스트를 비움
            statefulEvents.Clear();

            int c = 0; // 현재 이벤트 리스트의 인덱스
            int p = 0; // 이전 이벤트 리스트의 인덱스

            // 현재 이벤트와 이전 이벤트를 비교하며 상태 변경
            while (c &lt; currentEvents.Length &amp;&amp; p &lt; previousEvents.Length)
            {
                // 두 이벤트를 비교
                int r = previousEvents[p].CompareTo(currentEvents[c]);
                if (r == 0)
                {
                    // 현재 이벤트가 이전 이벤트와 동일하다면 'Stay' 상태로 추가
                    var currentEvent = currentEvents[c];
                    currentEvent.State = StatefulEventState.Stay;
                    statefulEvents.Add(currentEvent);
                    c++;
                    p++;
                }
                else if (r &lt; 0)
                {
                    // 이전 이벤트가 현재 이벤트보다 먼저 왔다면 'Exit' 상태로 추가
                    var previousEvent = previousEvents[p];
                    previousEvent.State = StatefulEventState.Exit;
                    statefulEvents.Add(previousEvent);
                    p++;
                }
                else //(r > 0)
                {
                    // 현재 이벤트가 이전 이벤트보다 먼저 왔다면 'Enter' 상태로 추가
                    var currentEvent = currentEvents[c];
                    currentEvent.State = StatefulEventState.Enter;
                    statefulEvents.Add(currentEvent);
                    c++;
                }
            }

            // 현재 이벤트가 다 처리되었고, 이전 이벤트가 남았다면 'Exit' 상태로 추가
            if (c == currentEvents.Length)
            {
                while (p &lt; previousEvents.Length)
                {
                    var previousEvent = previousEvents[p];
                    previousEvent.State = StatefulEventState.Exit;
                    statefulEvents.Add(previousEvent);
                    p++;
                }
            }
            // 이전 이벤트가 다 처리되었고, 현재 이벤트가 남았다면 'Enter' 상태로 추가
            else if (p == previousEvents.Length)
            {
                while (c &lt; currentEvents.Length)
                {
                    var currentEvent = currentEvents[c];
                    currentEvent.State = StatefulEventState.Enter;
                    statefulEvents.Add(currentEvent);
                    c++;
                }
            }
        }
    }
}
</pre>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<figure class="wp-block-image size-full"><img decoding="async" width="868" height="785" src="https://lycos7560.com/wp-content/uploads/2025/01/image-153.png" alt="" class="wp-image-39504" srcset="https://lycos7560.com/wp-content/uploads/2025/01/image-153.png 868w, https://lycos7560.com/wp-content/uploads/2025/01/image-153-300x271.png 300w, https://lycos7560.com/wp-content/uploads/2025/01/image-153-768x695.png 768w" sizes="(max-width: 868px) 100vw, 868px" /></figure>



<p><strong>AllocateBuffers</strong>()</p>



<p><code>Previous</code>와 <code>Current</code> 두 개의 이벤트 버퍼를 할당합니다.</p>



<p>이 함수는 <code>NativeList&lt;T></code> 타입의 <code>Previous</code>와 <code>Current</code> 버퍼를 할당합니다. </p>



<p>이 버퍼들은 각 프레임의 이벤트들을 저장하는 데 사용됩니다. </p>



<p><code>Allocator.Persistent</code>는 이 메모리가 여러 프레임에 걸쳐 유지될 수 있도록 해줍니다.</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p><strong>Dispose()</strong></p>



<p>할당된 <code>Previous</code>와 <code>Current</code> 이벤트 버퍼를 해제합니다.</p>



<p><code>Dispose()</code>는 <code>AllocateBuffers()</code>에서 할당한 <code>Previous</code>와 <code>Current</code> 버퍼를 메모리에서 해제하는 역할을 합니다. </p>



<p><code>IsCreated</code>를 통해 버퍼가 생성되었는지 확인한 후, 생성된 버퍼에 대해 <code>Dispose()</code>를 호출하여 메모리 관리를 수행합니다.</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p><strong>SwapBuffers()</strong></p>



<p><code>Previous</code>와 <code>Current</code> 버퍼를 교환하고, <code>Current</code> 버퍼를 비웁니다.</p>



<p>이 함수는 이전 프레임의 이벤트(<code>Previous</code>)와 현재 프레임의 이벤트(<code>Current</code>)를 교환합니다. </p>



<p>그 후, <code>Current</code> 버퍼를 비워서 다음 프레임의 데이터를 받을 준비를 합니다. </p>



<p>교환하는 이유는 이벤트가 프레임 간에 이동하면서 <strong>상태를 추적하고 업데이트하는 방식</strong> 때문입니다.</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<figure class="wp-block-image size-full"><img decoding="async" width="825" height="210" src="https://lycos7560.com/wp-content/uploads/2025/01/image-154.png" alt="" class="wp-image-39505" srcset="https://lycos7560.com/wp-content/uploads/2025/01/image-154.png 825w, https://lycos7560.com/wp-content/uploads/2025/01/image-154-300x76.png 300w, https://lycos7560.com/wp-content/uploads/2025/01/image-154-768x195.png 768w" sizes="(max-width: 825px) 100vw, 825px" /></figure>



<p><code>Previous</code>와 <code>Current</code> 이벤트 리스트를 결합하여 상태별 이벤트 리스트를 반환합니다.</p>



<p>이 함수는 두 개의 이벤트 리스트인 <code>Previous</code>와 <code>Current</code>를 결합하는 작업을 수행하는 <code>GetStatefulEvents()</code>의 래퍼(wrapper) 함수입니다. </p>



<p><code>sortCurrent</code> 파라미터를 사용하여 <code>Current</code> 리스트가 정렬되어야 하는지를 결정합니다. </p>



<p>기본적으로 <code>sortCurrent</code>는 <code>true</code>로 설정되어 있어 <code>Current</code> 리스트가 정렬됩니다.</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<figure class="wp-block-image size-full"><img decoding="async" width="1165" height="1262" src="https://lycos7560.com/wp-content/uploads/2025/01/image-155.png" alt="" class="wp-image-39506" srcset="https://lycos7560.com/wp-content/uploads/2025/01/image-155.png 1165w, https://lycos7560.com/wp-content/uploads/2025/01/image-155-277x300.png 277w, https://lycos7560.com/wp-content/uploads/2025/01/image-155-768x832.png 768w" sizes="(max-width: 1165px) 100vw, 1165px" /></figure>



<p>두 개의 정렬된 이벤트 리스트를 결합하여 하나의 상태별 이벤트 리스트로 반환합니다.</p>



<p>주어진 두 개의 이벤트 리스트(<code>previousEvents</code>와 <code>currentEvents</code>)를 비교하고, 각 이벤트에 적절한 <code>StatefulEventState</code>를 설정합니다. </p>



<p>각 상태는 <code>Enter</code>, <code>Stay</code>, <code>Exit</code>으로 나뉩니다.</p>



<ul class="wp-block-list">
<li><strong><code>Enter</code></strong>: 현재 프레임에 새롭게 등장한 이벤트.</li>



<li><strong><code>Stay</code></strong>: 이전과 현재 프레임 모두에서 존재하는 이벤트.</li>



<li><strong><code>Exit</code></strong>: 이전 프레임에는 존재했지만 현재 프레임에는 없는 이벤트.</li>
</ul>



<p>이 함수는 각 이벤트를 비교하여 상태를 설정하고, <code>statefulEvents</code>에 추가합니다.</p>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<hr class="wp-block-separator has-alpha-channel-opacity is-style-wide" style="margin-top:var(--wp--preset--spacing--80);margin-bottom:var(--wp--preset--spacing--80)"/>



<h3 class="wp-block-heading">StatefulCollisionEventBufferAuthoring.cs</h3>



<p></p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">using System.Runtime.InteropServices;
using Unity.Assertions;
using Unity.Entities;
using Unity.Mathematics;
using UnityEngine;

namespace Unity.Physics.Stateful
{
    // StatefulCollisionEventBufferAuthoring은 Unity의 MonoBehaviour로, 
    // 물리 충돌 이벤트가 포함된 버퍼를 정의하고, 해당 엔티티에 대해 세부 사항을 계산할지 여부를 결정합니다.
    public class StatefulCollisionEventBufferAuthoring : MonoBehaviour
    {
        [Tooltip("선택하면 이 엔티티의 충돌 이벤트 동적 버퍼에서 세부 정보가 계산됩니다")]
        public bool CalculateDetails = false; // 충돌 이벤트에 세부 사항을 계산할지 여부를 결정하는 변수

        // Baker 클래스는 Authoring 컴포넌트에서 데이터를 추출하고 Entity로 변환하는 역할을 합니다.
        class StatefulCollisionEventBufferBaker : Baker&lt;StatefulCollisionEventBufferAuthoring>
        {
            public override void Bake(StatefulCollisionEventBufferAuthoring authoring)
            {
                // TransformUsageFlags.Dynamic 플래그를 사용하여 해당 엔티티를 생성합니다.
                var entity = GetEntity(TransformUsageFlags.Dynamic);

                // CalculateDetails가 true일 경우, StatefulCollisionEventDetails를 엔티티에 추가합니다.
                if (authoring.CalculateDetails)
                {
                    var dynamicBufferTag = new StatefulCollisionEventDetails
                    {
                        CalculateDetails = authoring.CalculateDetails
                    };

                    AddComponent(entity, dynamicBufferTag); // StatefulCollisionEventDetails 컴포넌트 추가
                }

                // 충돌 이벤트를 저장할 버퍼 추가
                AddBuffer&lt;StatefulCollisionEvent>(entity);
            }
        }
    }

    // StatefulCollisionEventDetails 컴포넌트는 엔티티가 세부 사항을 계산할지를 설정하는 플래그입니다.
    public struct StatefulCollisionEventDetails : IComponentData
    {
        public bool CalculateDetails; // 세부 사항을 계산할지 여부를 설정하는 변수
    }

    // StatefulCollisionEvent는 DynamicBuffer에 저장할 수 있는 충돌 이벤트 구조체입니다.
    public struct StatefulCollisionEvent : IBufferElementData, IStatefulSimulationEvent&lt;StatefulCollisionEvent>
    {
        public Entity EntityA { get; set; } // 첫 번째 엔티티
        public Entity EntityB { get; set; } // 두 번째 엔티티
        public int BodyIndexA { get; set; } // 첫 번째 엔티티의 바디 인덱스
        public int BodyIndexB { get; set; } // 두 번째 엔티티의 바디 인덱스
        public ColliderKey ColliderKeyA { get; set; } // 첫 번째 엔티티의 충돌체 키
        public ColliderKey ColliderKeyB { get; set; } // 두 번째 엔티티의 충돌체 키
        public StatefulEventState State { get; set; } // 이벤트 상태 (Enter, Stay, Exit 등)
        public float3 Normal; // 충돌 법선 벡터

        // CalculateDetails가 체크된 경우, 이 필드는 유효한 값이 있으며 그렇지 않으면 기본값으로 초기화됩니다.
        internal Details CollisionDetails;

        // 생성자: CollisionEvent를 StatefulCollisionEvent로 변환합니다.
        public StatefulCollisionEvent(CollisionEvent collisionEvent)
        {
            EntityA = collisionEvent.EntityA;
            EntityB = collisionEvent.EntityB;
            BodyIndexA = collisionEvent.BodyIndexA;
            BodyIndexB = collisionEvent.BodyIndexB;
            ColliderKeyA = collisionEvent.ColliderKeyA;
            ColliderKeyB = collisionEvent.ColliderKeyB;
            State = default;
            Normal = collisionEvent.Normal;
            CollisionDetails = default;
        }

        // 충돌 세부 사항을 설명하는 구조체
        public struct Details
        {
            internal bool IsValid; // 세부 사항이 유효한지 여부

            // 1이면 정점 충돌, 2이면 모서리 충돌, 3 이상이면 면 충돌
            public int NumberOfContactPoints;

            // 추정된 임펄스
            public float EstimatedImpulse;
            // 평균 접촉 지점 위치
            public float3 AverageContactPointPosition;

            // 생성자: 세부 사항을 초기화합니다.
            public Details(int numContactPoints, float estimatedImpulse, float3 averageContactPosition)
            {
                IsValid = (0 &lt; numContactPoints); // 유효한 접촉점이 있을 경우에만 세부 사항이 유효함
                NumberOfContactPoints = numContactPoints;
                EstimatedImpulse = estimatedImpulse;
                AverageContactPointPosition = averageContactPosition;
            }
        }

        // 주어진 엔티티와 다른 엔티티를 반환하는 함수
        public Entity GetOtherEntity(Entity entity)
        {
            Assert.IsTrue((entity == EntityA) || (entity == EntityB)); // 엔티티 A 또는 B만 유효함
            return entity == EntityA ? EntityB : EntityA;
        }

        // 주어진 엔티티에서 다른 엔티티로 향하는 법선 벡터를 반환합니다.
        public float3 GetNormalFrom(Entity entity)
        {
            Assert.IsTrue((entity == EntityA) || (entity == EntityB)); // 엔티티 A 또는 B만 유효함
            return math.select(-Normal, Normal, entity == EntityB); // EntityB에 대해서는 반대 방향 법선을 반환
        }

        // 충돌 세부 사항이 유효한지 확인하고, 유효한 경우 세부 사항을 반환합니다.
        public bool TryGetDetails(out Details details)
        {
            details = CollisionDetails;
            return CollisionDetails.IsValid; // 세부 사항이 유효한지 여부를 반환
        }

        // 이벤트를 비교하는 함수
        public int CompareTo(StatefulCollisionEvent other) => ISimulationEventUtilities.CompareEvents(this, other);
    }
}
</pre>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<figure class="wp-block-image size-full"><img decoding="async" width="836" height="619" src="https://lycos7560.com/wp-content/uploads/2025/01/image-156.png" alt="" class="wp-image-39507" srcset="https://lycos7560.com/wp-content/uploads/2025/01/image-156.png 836w, https://lycos7560.com/wp-content/uploads/2025/01/image-156-300x222.png 300w, https://lycos7560.com/wp-content/uploads/2025/01/image-156-768x569.png 768w" sizes="(max-width: 836px) 100vw, 836px" /></figure>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p><code>StatefulCollisionEventBufferAuthoring</code>은 <code>MonoBehaviour</code>를 상속받는 클래스</p>



<p>이 클래스는 <code>CalculateDetails</code>라는 변수를 통해 세부 사항 계산 여부를 설정할 수 있도록 합니다.</p>



<p><code>StatefulCollisionEventBufferBaker</code>는 <code>Baker</code> 클래스로, <code>StatefulCollisionEventBufferAuthoring</code>의 값을 기반으로 Entity를 생성합니다. </p>



<p>이 클래스에서 <code>CalculateDetails</code> 값이 <code>true</code>일 경우, <code>StatefulCollisionEventDetails</code> 컴포넌트를 해당 엔티티에 추가합니다.</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<figure class="wp-block-image size-full"><img decoding="async" width="787" height="132" src="https://lycos7560.com/wp-content/uploads/2025/01/image-157.png" alt="" class="wp-image-39508" srcset="https://lycos7560.com/wp-content/uploads/2025/01/image-157.png 787w, https://lycos7560.com/wp-content/uploads/2025/01/image-157-300x50.png 300w, https://lycos7560.com/wp-content/uploads/2025/01/image-157-768x129.png 768w" sizes="(max-width: 787px) 100vw, 787px" /></figure>



<p><code>StatefulCollisionEventDetails</code>는 <code>IComponentData</code>를 구현한 구조체로, </p>



<p>충돌 이벤트에 대해 세부 사항을 계산할지 여부를 설정하는 <code>CalculateDetails</code> 변수만을 포함하고 있습니다.</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<figure class="wp-block-image size-full"><img decoding="async" width="849" height="668" src="https://lycos7560.com/wp-content/uploads/2025/01/image-158.png" alt="" class="wp-image-39509" srcset="https://lycos7560.com/wp-content/uploads/2025/01/image-158.png 849w, https://lycos7560.com/wp-content/uploads/2025/01/image-158-300x236.png 300w, https://lycos7560.com/wp-content/uploads/2025/01/image-158-768x604.png 768w" sizes="(max-width: 849px) 100vw, 849px" /></figure>



<p><code>StatefulCollisionEvent</code>는 충돌 이벤트를 나타내는 구조체로, <code>EntityA</code>와 <code>EntityB</code>를 비롯한 충돌 관련 정보를 포함합니다. </p>



<p><code>State</code>는 충돌 상태를 추적하며, <code>CollisionDetails</code>는 선택적으로 세부 충돌 정보를 포함합니다.</p>



<p><code>Details</code> 구조체는 충돌의 세부 사항을 설명하며, 접촉 지점 수, 추정 임펄스 및 평균 접촉 지점 위치를 저장합니다.</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<figure class="wp-block-image size-full"><img decoding="async" width="865" height="505" src="https://lycos7560.com/wp-content/uploads/2025/01/image-159.png" alt="" class="wp-image-39510" srcset="https://lycos7560.com/wp-content/uploads/2025/01/image-159.png 865w, https://lycos7560.com/wp-content/uploads/2025/01/image-159-300x175.png 300w, https://lycos7560.com/wp-content/uploads/2025/01/image-159-768x448.png 768w" sizes="(max-width: 865px) 100vw, 865px" /></figure>



<hr class="wp-block-separator has-alpha-channel-opacity is-style-wide" style="margin-top:var(--wp--preset--spacing--80);margin-bottom:var(--wp--preset--spacing--80)"/>



<h3 class="wp-block-heading">StatefulCollisionEventBufferSystem.cs</h3>



<p><code>CollisionEvent</code>를 <code>StatefulCollisionEvent</code>로 변환하여 동적 버퍼에 저장하는 역할</p>



<div style="height:23px" aria-hidden="true" class="wp-block-spacer"></div>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">using Unity.Burst;
using Unity.Entities;
using Unity.Jobs;
using Unity.Physics.Stateful;
using Unity.Physics;
using Unity.Physics.Systems;

// 이 시스템은 CollisionEvent 스트림을 StatefulCollisionEvent로 변환하여 Dynamic Buffer에 저장할 수 있도록 합니다.
// 변환을 위해서는 아래의 조건을 만족해야 합니다:
//    1) PhysicsShapeAuthoring 컴포넌트의 'Collision Response' 속성에서 'Collide Raise Collision Events' 옵션을 활성화하고,
//    2) StatefulCollisionEventBufferAuthoring 컴포넌트를 엔티티에 추가하여 (세부 사항 계산 여부도 선택),
//    또는, 만약 Character Controller에서 이를 원한다면:
//    1) CharacterControllerAuthoring 컴포넌트에서 'Raise Collision Events' 플래그를 활성화합니다.
[UpdateInGroup(typeof(PhysicsSystemGroup))] // 물리 시스템 그룹에 포함
[UpdateAfter(typeof(PhysicsSimulationGroup))] // 물리 시뮬레이션 그룹 후에 실행
public partial struct StatefulCollisionEventBufferSystem : ISystem
{
    private StatefulSimulationEventBuffers&lt;StatefulCollisionEvent> m_StateFulEventBuffers; // StatefulCollisionEvent 버퍼
    private ComponentHandles m_Handles; // 컴포넌트 핸들

    // 아무런 작업을 하지 않는 컴포넌트. 일반적인 job을 사용하기 위해 만들어졌습니다. OnUpdate() 메서드에서 설명
    internal struct DummyExcludeComponent : IComponentData { };

    // 컴포넌트 핸들 구조체
    struct ComponentHandles
    {
        public ComponentLookup&lt;DummyExcludeComponent> EventExcludes; // 이벤트 제외를 위한 컴포넌트 조회
        public ComponentLookup&lt;StatefulCollisionEventDetails> EventDetails; // 이벤트 세부 사항을 위한 컴포넌트 조회
        public BufferLookup&lt;StatefulCollisionEvent> EventBuffers; // 이벤트 버퍼 조회

        // 시스템 상태에 맞는 컴포넌트 핸들을 초기화합니다.
        public ComponentHandles(ref SystemState systemState)
        {
            EventExcludes = systemState.GetComponentLookup&lt;DummyExcludeComponent>(true);
            EventDetails = systemState.GetComponentLookup&lt;StatefulCollisionEventDetails>(true);
            EventBuffers = systemState.GetBufferLookup&lt;StatefulCollisionEvent>(false);
        }

        // 시스템 상태에 맞는 컴포넌트 핸들을 업데이트합니다.
        public void Update(ref SystemState systemState)
        {
            EventExcludes.Update(ref systemState);
            EventBuffers.Update(ref systemState);
            EventDetails.Update(ref systemState);
        }
    }

    // 시스템 초기화
    [BurstCompile]
    public void OnCreate(ref SystemState state)
    {
        m_StateFulEventBuffers = new StatefulSimulationEventBuffers&lt;StatefulCollisionEvent>(); // StatefulCollisionEvent 버퍼 할당
        m_StateFulEventBuffers.AllocateBuffers(); // 버퍼 할당
        state.RequireForUpdate&lt;StatefulCollisionEvent>(); // 시스템이 업데이트 되도록 설정

        m_Handles = new ComponentHandles(ref state); // 컴포넌트 핸들 초기화
    }

    // 시스템 종료
    [BurstCompile]
    public void OnDestroy(ref SystemState state)
    {
        m_StateFulEventBuffers.Dispose(); // 버퍼 해제
    }

    // 충돌 이벤트 동적 버퍼를 지우는 작업을 수행하는 Job
    [BurstCompile]
    public partial struct ClearCollisionEventDynamicBufferJob : IJobEntity
    {
        public void Execute(ref DynamicBuffer&lt;StatefulCollisionEvent> eventBuffer) => eventBuffer.Clear(); // 버퍼 초기화
    }

    // 시스템 업데이트 메서드
    [BurstCompile]
    public void OnUpdate(ref SystemState state)
    {
        m_Handles.Update(ref state); // 컴포넌트 핸들 업데이트

        // 충돌 이벤트 동적 버퍼를 지우는 작업을 병렬로 스케줄
        state.Dependency = new ClearCollisionEventDynamicBufferJob()
            .ScheduleParallel(state.Dependency);

        // 이전/현재 버퍼를 교환
        m_StateFulEventBuffers.SwapBuffers();

        var currentEvents = m_StateFulEventBuffers.Current; // 현재 이벤트
        var previousEvents = m_StateFulEventBuffers.Previous; // 이전 이벤트

        // 충돌 이벤트와 세부 사항을 수집하는 작업을 스케줄
        state.Dependency = new StatefulEventCollectionJobs.
            CollectCollisionEventsWithDetails
        {
            CollisionEvents = currentEvents,
            PhysicsWorld = SystemAPI.GetSingleton&lt;PhysicsWorldSingleton>().PhysicsWorld, // 물리 월드
            EventDetails = m_Handles.EventDetails // 이벤트 세부 사항
        }.Schedule(SystemAPI.GetSingleton&lt;SimulationSingleton>(), state.Dependency);

        // 이벤트 스트림을 동적 버퍼로 변환하는 작업을 스케줄
        state.Dependency = new StatefulEventCollectionJobs.
            ConvertEventStreamToDynamicBufferJob&lt;StatefulCollisionEvent, DummyExcludeComponent>
        {
            CurrentEvents = currentEvents,
            PreviousEvents = previousEvents,
            EventLookup = m_Handles.EventBuffers, // 이벤트 버퍼 조회
            UseExcludeComponent = false, // 제외 컴포넌트 사용 안함
            EventExcludeLookup = m_Handles.EventExcludes // 이벤트 제외 조회
        }.Schedule(state.Dependency);
    }
}
</pre>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<figure class="wp-block-image size-full"><img decoding="async" width="822" height="698" src="https://lycos7560.com/wp-content/uploads/2025/01/image-160.png" alt="" class="wp-image-39511" srcset="https://lycos7560.com/wp-content/uploads/2025/01/image-160.png 822w, https://lycos7560.com/wp-content/uploads/2025/01/image-160-300x255.png 300w, https://lycos7560.com/wp-content/uploads/2025/01/image-160-768x652.png 768w" sizes="(max-width: 822px) 100vw, 822px" /></figure>



<p><strong>세부 작업</strong>:</p>



<ol class="wp-block-list">
<li><strong>컴포넌트 핸들 업데이트</strong>: <br><code>m_Handles.Update(ref state)</code>를 호출하여 컴포넌트 핸들을 최신 상태로 업데이트합니다.</li>



<li><strong>충돌 이벤트 버퍼 초기화</strong>: <br><code>ClearCollisionEventDynamicBufferJob</code>을 병렬로 스케줄하여 충돌 이벤트 버퍼를 지웁니다.</li>



<li><strong>이벤트 버퍼 교환</strong>: <br><code>SwapBuffers()</code>를 호출하여 현재와 이전 프레임의 이벤트 버퍼를 교환합니다. <br>이후 새로운 이벤트를 <code>currentEvents</code>에 기록하고, 이전 이벤트는 <code>previousEvents</code>로 저장됩니다.</li>



<li><strong>충돌 이벤트와 세부 사항 수집</strong>: <br><code>StatefulEventCollectionJobs.CollectCollisionEventsWithDetails</code>를 스케줄하여 충돌 이벤트와 관련된 세부 사항을 수집합니다. <br>물리 월드 및 이벤트 세부 사항도 이 작업에서 처리됩니다.</li>



<li><strong>이벤트 스트림을 동적 버퍼로 변환</strong>: <br><code>StatefulEventCollectionJobs.ConvertEventStreamToDynamicBufferJob</code>을 호출하여 충돌 이벤트를 동적 버퍼로 변환하고, <code>StatefulCollisionEvent</code> 버퍼에 기록합니다.</li>
</ol>



<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>



<hr class="wp-block-separator has-alpha-channel-opacity is-style-wide" style="margin-top:var(--wp--preset--spacing--80);margin-bottom:var(--wp--preset--spacing--80)"/>



<h3 class="wp-block-heading">충돌 위치 쿼리</h3>



<h4 class="wp-block-heading">1. <strong>충돌 이벤트에서 충돌 위치(World Position) 가져오기</strong></h4>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">public partial struct CollisionPositionLoggerSystem : ISystem
{
    public void OnUpdate(ref SystemState state)
    {
        foreach (var (collisionEvents, entity) in SystemAPI.Query&lt;DynamicBuffer&lt;StatefulCollisionEvent>>()
                     .WithEntityAccess())
        {
            foreach (var collisionEvent in collisionEvents)
            {
                if (collisionEvent.TryGetDetails(out var details))
                {
                    float3 worldPosition = details.AverageContactPointPosition;
                    Debug.Log($"Collision at {worldPosition} between {collisionEvent.EntityA} and {collisionEvent.EntityB}");
                }
            }
        }
    }
}
</pre>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<p><strong>Query로 <code>StatefulCollisionEvent</code> 버퍼를 가져옴</strong></p>



<ul class="wp-block-list">
<li><code>SystemAPI.Query&lt;DynamicBuffer&lt;StatefulCollisionEvent>>()</code>를 사용해 현재 프레임의 충돌 이벤트를 가져옴.</li>
</ul>



<p><strong>각 충돌 이벤트의 세부 사항 가져오기</strong></p>



<ul class="wp-block-list">
<li><code>collisionEvent.TryGetDetails(out var details)</code>를 호출하여 충돌 세부 정보를 가져올 수 있는지 확인.</li>



<li><code>details.AverageContactPointPosition</code>은 충돌 지점들의 평균 좌표로, 월드 좌표계에서의 위치를 제공.</li>
</ul>



<p><strong>디버그 로그 출력</strong></p>



<ul class="wp-block-list">
<li><code>Debug.Log()</code>를 이용해 충돌 위치와 관련된 엔티티 정보를 출력.</li>
</ul>



<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading">2. <strong>여러 개의 접촉점(Contact Points) 활용하기</strong></h4>



<p>만약 여러 개의 접촉점을 각각 확인하려면, <code>CalculateDetails</code>를 활성화하고 <code>details.NumberOfContactPoints</code> 값을 사용해야 합니다.</p>



<p>하지만, 현재 <code>StatefulCollisionEvent</code>는 <code>AverageContactPointPosition</code>만을 제공하므로, 개별 접촉점의 좌표를 얻으려면 <code>PhysicsWorld</code>를 직접 사용해야 합니다.</p>



<p>이 경우, <code>collisionEvent.CalculateDetails(ref PhysicsWorld)</code>를 호출하여 접촉점 데이터를 직접 가져올 수 있습니다.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">if (collisionEvent.TryGetDetails(out var details))
{
    Debug.Log($"Collision has {details.NumberOfContactPoints} contact points.");
}
</pre>



<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>



<p></p>
<p>The post <a href="https://lycos7560.com/unity/ecs-unity-physics-stateful-event-study/39489/">ECS – Unity Physics Stateful Event Study</a> appeared first on <a href="https://lycos7560.com">어제와 내일의 나 그 사이의 이야기</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://lycos7560.com/unity/ecs-unity-physics-stateful-event-study/39489/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
