<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>쑥밭</title>
    <link>https://suyb.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Sun, 5 Jul 2026 21:01:32 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>리네엔</managingEditor>
    <item>
      <title>[Unity] Canvas Component</title>
      <link>https://suyb.tistory.com/131</link>
      <description>&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Render Mode&lt;/b&gt;: UI가 화면에 렌더링되는 방식을 설정.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Screen Space - Overlay: UI가 항상 화면 위에 표시되며, 카메라와 무관하게 렌더링.&lt;/li&gt;
&lt;li&gt;Screen Space - Camera: 특정 카메라를 통해 UI가 화면에 렌더링.&lt;/li&gt;
&lt;li&gt;World Space: UI가 3D 공간 내에서 위치하며, 다른 오브젝트와 함께 렌더링.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Pixel Perfect&lt;/b&gt;: 활성화하면 UI가 픽셀 단위로 정확히 렌더링되도록 조정. 저해상도 디스플레이에서 UI가 흐릿해지는 것을 방지할 수 있음.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;주의) 성능을 엄청 먹음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Sort Order&lt;/b&gt;: Canvas의 렌더링 순서를 결정. 값이 낮을수록 먼저 렌더링됨.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Target Display&lt;/b&gt;: UI가 출력될 디스플레이를 선택.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Additional Shader Channels&lt;/b&gt;: 쉐이더에서 사용 가능한 추가 채널(Normal, Tangent 등)을 설정.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Canvas에서 랜더링되는 UI요소가 추가적인 쉐이더 데이터 채널을 사용할 수 있도록 설정&lt;/li&gt;
&lt;li&gt;엄청난 UI를 만드는게 아니면 Nothing이 디폴트 인 듯.&lt;/li&gt;
&lt;li&gt;Normal : Normal map을 사용할 수 있도록 함&lt;/li&gt;
&lt;li&gt;Tangent : 노멀 맵을 사용하는 경우 표면의 방향 벡터를 제공&lt;/li&gt;
&lt;li&gt;Texcood 1,2,3 UV맵 1, 2, 3&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Vertex Color Always In Gamma&lt;/b&gt;: 이 옵션을 활성화하면 Vertex Color가 감마 색 공간에서 계산.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;감마 색 공간&lt;/b&gt;: 디스플레이의 밝기 특성(감마 곡선)을 고려한 색 공간&lt;/li&gt;
&lt;li&gt;&lt;b&gt;선형 색 공간&lt;/b&gt;: 감마 보정이 제거된 색 공간.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Unity&amp;amp;C#</category>
      <author>리네엔</author>
      <guid isPermaLink="true">https://suyb.tistory.com/131</guid>
      <comments>https://suyb.tistory.com/131#entry131comment</comments>
      <pubDate>Mon, 23 Dec 2024 23:04:41 +0900</pubDate>
    </item>
    <item>
      <title>[내배캠 최종 프로젝트] 스테이지 및 캐릭터 데이터 구성</title>
      <link>https://suyb.tistory.com/130</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1048&quot; data-origin-height=&quot;324&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6sKj6/btsLgGAPRN2/XmmTarkhNrsysUZA0Z3zY0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6sKj6/btsLgGAPRN2/XmmTarkhNrsysUZA0Z3zY0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6sKj6/btsLgGAPRN2/XmmTarkhNrsysUZA0Z3zY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6sKj6%2FbtsLgGAPRN2%2FXmmTarkhNrsysUZA0Z3zY0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1048&quot; height=&quot;324&quot; data-origin-width=&quot;1048&quot; data-origin-height=&quot;324&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Json으로 기본 데이터 테이블을 구성하고, 이를 Info클래스로 받아서 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞의 Json데이터는 서버가 붙을 경우 서버에서 받아올 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 데이터로 유저가 사용하는 데이터 클래스로 감싸고, 이 묶음을 유저 데이터에서 관리해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 그 유저데이터를 다시 Json으로 구성하여 저장하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 구성하는 부분은 데이터 테이블 부터 InstanceClass와 로드까지하고, 뒤의 저장하는 부분은 다른 분이 맡아 주셨다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1523&quot; data-origin-height=&quot;178&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cuelwt/btsLfHm7dR4/Ens8r0gkRZWP8Ipk6KZkCk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cuelwt/btsLfHm7dR4/Ens8r0gkRZWP8Ipk6KZkCk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cuelwt/btsLfHm7dR4/Ens8r0gkRZWP8Ipk6KZkCk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcuelwt%2FbtsLfHm7dR4%2FEns8r0gkRZWP8Ipk6KZkCk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1523&quot; height=&quot;178&quot; data-origin-width=&quot;1523&quot; data-origin-height=&quot;178&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1233&quot; data-origin-height=&quot;124&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EHxjb/btsLe8rZUSP/yW8DZAIhKGdS2jVFAT4FbK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EHxjb/btsLe8rZUSP/yW8DZAIhKGdS2jVFAT4FbK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EHxjb/btsLe8rZUSP/yW8DZAIhKGdS2jVFAT4FbK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEHxjb%2FbtsLe8rZUSP%2FyW8DZAIhKGdS2jVFAT4FbK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1233&quot; height=&quot;124&quot; data-origin-width=&quot;1233&quot; data-origin-height=&quot;124&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위는 캐릭터 테이블이고, 아래는 스테이지 테이블이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;id로 조인하여 사용 할 수 있게 만들어놨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 json와 info클래스로 변환해주는 프로그램을 사용하여 json과 infoclass로 만들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;InstanceClass에서는 Info클래스와 여러 기타 정보를 갖고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1733932023556&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[Serializable]
public class CharacterInstance 
{
	public int Key;
	public int Level;
	public int CharacterInfoKey;
	[JsonIgnore] public CharacterInfo CharacterInfo {  get; private set; }
	[JsonIgnore] public SupportSkillInfo SupportSkillInfo { get; private set; }
	[JsonIgnore] public CharacterStat CharacterStat { get; private set; }

	// public ItemInstance[] EquipedItems;

	public void LoadInfos()
	{
		this.CharacterInfo = Managers.Instance.Data.CharacterLoader.GetByKey(CharacterInfoKey);
		SupportSkillInfo = Managers.Instance.Data.SupportSkillLoader.GetByKey(CharacterInfo.SupportSkillID);
	}

	private void SetCharacterStatBase()
	{
		CharacterStat = new CharacterStat(CharacterInfo);
	}
}
public class CharacterInstanceLoader
{
	public List&amp;lt;CharacterInstance&amp;gt; ItemList { get; private set; }
	public Dictionary&amp;lt;int, CharacterInstance&amp;gt; ItemDict { get; private set; }

	public CharacterInstance GetbyKey(int key)
	{
		return ItemDict[key];
	}

	public void LoadInstance(List&amp;lt;CharacterInstance&amp;gt; list)
	{
		ItemList = list;
		for(int i = 0; i &amp;lt; ItemList.Count; i++)
		{
			ItemList[i].LoadInfos();
		}
	}
	public void SetDict()
	{
		ItemDict = new Dictionary&amp;lt;int, CharacterInstance&amp;gt;();
		for (int i = 0; i &amp;lt; ItemList.Count; i++)
		{
			ItemDict.Add(ItemList[i].Key, ItemList[i]);
		}
	}

	public CharacterInstance CreateInstance(int key)
	{
		CharacterInstance item = new CharacterInstance();
		item.Key = Utils.CreateId(ItemDict.Keys.ToList&amp;lt;int&amp;gt;());
		item.CharacterInfoKey = key;
		item.LoadInfos();
		item.Level = 1;
		return  item;
	}

	public void AddInstance(CharacterInstance item)
	{
		ItemList.Add(item);
	}

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Infoclass가 갖고 있는 데이터는 JsonIgnore를 붙여서 저장되지 않도록 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Loaderclass를 만들어서 데이터를 불러오고, 새로운 데이터를 저장해야하면, key를 만들고 데이터를 알맞게 초기화 해주는 기능을 만들어주었다. 또한 key로 데이터를 한번에 접근해가져올 수 있도록 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Develop_Log</category>
      <author>리네엔</author>
      <guid isPermaLink="true">https://suyb.tistory.com/130</guid>
      <comments>https://suyb.tistory.com/130#entry130comment</comments>
      <pubDate>Wed, 11 Dec 2024 23:59:28 +0900</pubDate>
    </item>
    <item>
      <title>[내배캠 최종 프로젝트] 공격 및 공격 범위 표시</title>
      <link>https://suyb.tistory.com/129</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1206.gif&quot; data-origin-width=&quot;1804&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bb9MET/btsLbz9TzBt/e0G1twdOOs8Qwe4EmIdgp0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bb9MET/btsLbz9TzBt/e0G1twdOOs8Qwe4EmIdgp0/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bb9MET/btsLbz9TzBt/e0G1twdOOs8Qwe4EmIdgp0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bb9MET/btsLbz9TzBt/e0G1twdOOs8Qwe4EmIdgp0/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1804&quot; height=&quot;1080&quot; data-filename=&quot;1206.gif&quot; data-origin-width=&quot;1804&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;캐릭터의 스킬구조를 뜯어 고치고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스킬 버튼을 누르면 해당 스킬의 범위를 보여주고, 버튼을 뗄때 스킬이 발동된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;스킬 범위 구현&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데칼을 활용해서 보여주도록 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;411&quot; data-origin-height=&quot;165&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JzVaH/btsK9v2tU0e/9qCUaPAh5zll7T8pCNnzak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JzVaH/btsK9v2tU0e/9qCUaPAh5zll7T8pCNnzak/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JzVaH/btsK9v2tU0e/9qCUaPAh5zll7T8pCNnzak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJzVaH%2FbtsK9v2tU0e%2F9qCUaPAh5zll7T8pCNnzak%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;411&quot; height=&quot;165&quot; data-origin-width=&quot;411&quot; data-origin-height=&quot;165&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;728&quot; data-origin-height=&quot;557&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1xn1y/btsLaXQYQvM/diKcqciWJIkx9yIYYRujhk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1xn1y/btsLaXQYQvM/diKcqciWJIkx9yIYYRujhk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1xn1y/btsLaXQYQvM/diKcqciWJIkx9yIYYRujhk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1xn1y%2FbtsLaXQYQvM%2FdiKcqciWJIkx9yIYYRujhk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;425&quot; height=&quot;325&quot; data-origin-width=&quot;728&quot; data-origin-height=&quot;557&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;90도 부채꼴, 180도, 원형 세종류의 데칼을 준비하고, 이 데칼이 바닥에만 표시될 수 있도록 Rendering Layers를 LightLayer1을 지정해주고, 바닥도 LightLayer1을 지정해주었다.&lt;/p&gt;
&lt;pre id=&quot;code_1733492987920&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public virtual void OnInputDown()
{
	if (IsSkillAvailable)
	{
		if (stateMachine.Skill!=null &amp;amp;&amp;amp; stateMachine.Skill != this)
		{
			stateMachine.Skill.areaIndicator.gameObject.SetActive(false);
		}
		stateMachine.Skill = this;
		attackPosition = character.Pivots.AttackPivot.position + character.transform.forward * Info.ActivationRange;
		ShowArea();
            
		GameObject collider = SpawnManager.SpawnCollider(Info.AttackAngle,
			character.transform.forward * Info.ActivationRange, 
			Quaternion.Euler(new Vector3(90,  Info.AttackAngle==90? -45:0, 0)),
			character.transform);
		collider.transform.localScale = new Vector3(Info.AttackRange, Info.AttackRange, 1);
		areaCollider = collider.GetComponent&amp;lt;SkillAreaCollider&amp;gt;();
	}
}
    
public virtual void ShowArea()
{
	areaIndicator = SpawnManager.SpawnSkillIndicator(Info.AttackAngle,
					character.transform.forward * Info.ActivationRange,
					root: character.transform).GetComponent&amp;lt;AreaIndicator&amp;gt;();
	areaIndicator.Init(Info.AttackRange,Info.ActivationRange,character.transform);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스킬이 눌렸을 때 해당 스킬의 공격 위치와 범위정보를 가져와 적절한 위치와 크기로 생성해준다. SpawnManager를 통해 생성하는데, SpawnManger는 오브젝트를 생성할 때 자동으로 풀링을 관리해준다. 즉, 이 영역을 보여주는 데칼 오브젝트도 풀링이 적용되고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;스킬의 타격 구현&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 overlapsphere로 범위의 오브젝트를 찾은 후, 내적으로 원하는 공격 각도 내의 몬스터를 선별하게 했었다. 이렇게 하면 범위내의 몬스터를 훌륭히 찾을 수는 있지만.. 공격 범위에 매쉬가 애매하게 겹쳐있는 경우에는 매쉬는 범위안에 들어가 있지만, 포지션은 범위 밖이라 검출되지 않는 문제가 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 되면 몬스터가 조금 커져서 몬스터의 일부분을 타격하게 되는 경우 상당히 불쾌해질 것 같았기에 블랜더에서 매쉬를 몇개 만들어와서 콜라이더를 적용했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;287&quot; data-origin-height=&quot;263&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DNb2s/btsLa01eMlk/0PHIr3aIplHKZhBPQKfJOK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DNb2s/btsLa01eMlk/0PHIr3aIplHKZhBPQKfJOK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DNb2s/btsLa01eMlk/0PHIr3aIplHKZhBPQKfJOK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDNb2s%2FbtsLa01eMlk%2F0PHIr3aIplHKZhBPQKfJOK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;172&quot; height=&quot;158&quot; data-origin-width=&quot;287&quot; data-origin-height=&quot;263&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;325&quot; data-origin-height=&quot;212&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwR7w4/btsLaB8HYzA/kzKIpijeEWxfXdV7FG2YHk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwR7w4/btsLaB8HYzA/kzKIpijeEWxfXdV7FG2YHk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwR7w4/btsLaB8HYzA/kzKIpijeEWxfXdV7FG2YHk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbwR7w4%2FbtsLaB8HYzA%2FkzKIpijeEWxfXdV7FG2YHk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;207&quot; height=&quot;135&quot; data-origin-width=&quot;325&quot; data-origin-height=&quot;212&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;425&quot; data-origin-height=&quot;352&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bSFR1Y/btsLbOTkGy9/CKScfhzDIw1I5LcAa56z6K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bSFR1Y/btsLbOTkGy9/CKScfhzDIw1I5LcAa56z6K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bSFR1Y/btsLbOTkGy9/CKScfhzDIw1I5LcAa56z6K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbSFR1Y%2FbtsLbOTkGy9%2FCKScfhzDIw1I5LcAa56z6K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;263&quot; height=&quot;218&quot; data-origin-width=&quot;425&quot; data-origin-height=&quot;352&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;캡슐이나 박스보다 조금 부하가 먹겠지만...&amp;nbsp; 한두개씩만 생성 될 것이니 큰 문제 없을 것이라 생각한다. 나중에 좀더 네모나게,,, 폴리곤을 조금 줄이는 정도의 타협은 가능할 듯.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Collider를 Trigger로 설정하여 Trigger안에 들어온 collider들을 list로 갖고 있도록 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1733494855998&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;	public override void Excute()
	{
		base.Excute();

		int count = Mathf.Min(Info.TargetCount, areaCollider.colliders.Count);
		for(int i = 0; i&amp;lt; count; i++)
		{
			if (areaCollider.colliders[i].gameObject.TryGetComponentInParent&amp;lt;IDamagable&amp;gt;(out IDamagable component))
			{
				float damage = (character.StatHandler.CurrentStat.PhysicalAttack + Info.PhysicalFlat) * (Info.physicalMultiplier / 100);
				component.TakeDamage(damage);
			}
		}
		areaCollider.gameObject.SetActive(false);
	}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스킬을 발동시키는 부분에서 이렇게 콜라이더 내의 오브젝트가 맞을 수 있는 오브젝트인지 검사해서 맞을 수 있는 오브젝트일 때 데미지를 가하도록 하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Develop_Log</category>
      <author>리네엔</author>
      <guid isPermaLink="true">https://suyb.tistory.com/129</guid>
      <comments>https://suyb.tistory.com/129#entry129comment</comments>
      <pubDate>Fri, 6 Dec 2024 23:22:03 +0900</pubDate>
    </item>
    <item>
      <title>[Unity] Animation 관련 소소한 문제들</title>
      <link>https://suyb.tistory.com/128</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Can Transition To Self는 애니메이션 상태가 자기 자신으로 전이(Transition)할 수 있는지를 설정하는 것&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 이게 켜져있어서 대쉬 애니메이션이 꼬였음. 왜꼬였지..?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 진행중인 애니메이션 클립의 절대적 시간을 알 수 없음 대신 normalized된 시간을 알 수 있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Animator.GetCurrentAnimatorStateInfo(0).normalizedTime을 활용.&lt;/p&gt;
&lt;pre id=&quot;code_1733411382533&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;		if (stateMachine.Character.Animator.GetCurrentAnimatorStateInfo(0).IsTag(skill.GetAnim()))
		{
			normalizedTime = stateMachine.Character.Animator.GetCurrentAnimatorStateInfo(0).normalizedTime;
			if (normalizedTime &amp;lt; 0.2f)
			{
				stateMachine.Player.ForceReceiver.AddForce(stateMachine.Player.transform.forward * 0.7f);
			}
			if (normalizedTime &amp;gt;= 1f)
			{
				stateMachine.ChangeState(stateMachine.Player.StateMachine.IdleState);
			}
		}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상태머신에 위와 같이 활용.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AnyState에서 시작하는 애니메이션을 bool값으로 진입하게하면 상태에 반복해서 진입하는 문제가 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- anystate에서 시작하는 애니메이션은 trigger로 실행되는 애니메이션을 사용.&lt;/p&gt;</description>
      <category>Unity&amp;amp;C#</category>
      <author>리네엔</author>
      <guid isPermaLink="true">https://suyb.tistory.com/128</guid>
      <comments>https://suyb.tistory.com/128#entry128comment</comments>
      <pubDate>Fri, 6 Dec 2024 00:10:17 +0900</pubDate>
    </item>
    <item>
      <title>[내배캠 최종 프로젝트] 캐릭터 구현</title>
      <link>https://suyb.tistory.com/127</link>
      <description>&lt;h3&gt;캐릭터 테이블&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1454&quot; data-origin-height=&quot;91&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nQizt/btsK6kfyo1l/GmJ33HuFOlWVDoSzl2OsL1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nQizt/btsK6kfyo1l/GmJ33HuFOlWVDoSzl2OsL1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nQizt/btsK6kfyo1l/GmJ33HuFOlWVDoSzl2OsL1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnQizt%2FbtsK6kfyo1l%2FGmJ33HuFOlWVDoSzl2OsL1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1454&quot; height=&quot;91&quot; data-origin-width=&quot;1454&quot; data-origin-height=&quot;91&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;엑셀로 캐릭터 및 스킬 테이블을 관리, 튜터님의 ExceltoJsonWizard를 사용해서 json으로 변환해서 사용.&lt;/p&gt;
&lt;h3&gt;구현&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Character&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CharacterInfo 및 SkillInfo등 데이터를 Instance로 감싸서 갖고 있도록 함.&lt;ul&gt;
&lt;li&gt;~Instance클래스는 Info를 담고 있는 플레이어가 실제로 갖고 있는 정보의 클래스.&lt;/li&gt;
&lt;li&gt;캐릭터 강화 등을 확장이 있을 수 있어 Instance클래스로 관리하도록 함&lt;/li&gt;
&lt;li&gt;스킬에 강화가 가능하다고 하면.. 스킬Instance을 characterInstance에 넣어야할 것 같기도.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;스텟 및 hphandler등을 갖고 있어 캐릭터의 수치변화 상태를 갖고 있음.&lt;/li&gt;
&lt;li&gt;SkillBase를 상속받는 Skill들을 갖고 있음.&lt;ul&gt;
&lt;li&gt;Activator로 할당하고 있음. 리플랙션을 쓰는게 마음에 안듦. 고민중..&lt;ul&gt;
&lt;li&gt;이동 등의 물리적 상태 변화는 Player 클래스에서 관리.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;이 부분에 대해 고민 중... input 관리나 이동관리/상태관리, 현재 사용 캐릭터를 player 쪽으로 뺐는데.. 이동/상태관리를 character에 다시 집어 넣을까.. 그러기엔 케릭터간의 위치관리가 귀찮다. 그리고 Input 스크립트 때문에 초기화 타이밍 관리도 귀찮고.. 일단 이대로 두고 내일 튜터님께서 코드 부검 하실때 이게 문제면 알려주시지 않을까.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Player &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Input 및 Character의 상태 관리 &lt;/li&gt;
&lt;li&gt;캐릭터의 위치관리&lt;ul&gt;
&lt;li&gt;CharcterController를 활용. 딱히 rigidbody를 활용한 물리상호작용이 필요 없을 듯함. 그 물리는 좀 유니티 틱한 물리느낌이 나서 오히려 별로이기도 함.&lt;/li&gt;
&lt;li&gt;직접 vector 값을 컨트롤 해서 움직여주는게 보기 적당한 정도의 힘이나 속도 등을 컨트롤 해줄 수 있어서 좋음.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Skill&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;대쉬나 일반 공격도 Skill로 간주하여 관리하도록 함&lt;ul&gt;
&lt;li&gt;대쉬도 다양한 형태일 수 있고, 쿨타임을 갖고 있고.. 스킬로 묶어서 관리하기 충분한 조건이라고 생각. 굳이 대쉬롤 따로 찢을 이유도 없다.&lt;/li&gt;
&lt;li&gt;일반 공격도 다양한 형태가 있을 수 있어 Skill로 간주하여 관리.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;코드의 변경이 많이 일어나는 중이라 코드 상세는 다음에 첨부.&lt;/p&gt;</description>
      <category>Develop_Log</category>
      <author>리네엔</author>
      <guid isPermaLink="true">https://suyb.tistory.com/127</guid>
      <comments>https://suyb.tistory.com/127#entry127comment</comments>
      <pubDate>Wed, 4 Dec 2024 23:16:16 +0900</pubDate>
    </item>
    <item>
      <title>[내배캠 최종 프로젝트] 맵 장애물 구현</title>
      <link>https://suyb.tistory.com/126</link>
      <description>&lt;p&gt;쿠키런 : 모험의 탑을 레퍼런스로 개발중.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;정작 쿠키런 : 모험의 탑을 즐겨하는 팀원은 없다는게 웃음 벨&lt;/li&gt;
&lt;li&gt;구현 사항 본다고 내가 제일 많이 해본듯. 아니 기획한 사람은 왜 안하면서 왜 이걸 레퍼로 잡자고 한건데?&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;문과 버튼 구현&lt;/h3&gt;
&lt;h4&gt;버튼&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;쿠키런 플레이시 보면 밟으면 문이 열리는 기믹이 존재&lt;/li&gt;
&lt;li&gt;TriggerSource 클래스를 구현하여 트리거 되면 실행될 오브젝트를 받아서 실행시키는 기믹을 구현함&lt;/li&gt;
&lt;li&gt;버튼은 TriggerSource를 상속받아 OnTriggerEnter가 될때 실행되도록 함.&lt;/li&gt;
&lt;li&gt;니케 로스트 섹터를 보면 밟고 있어야 열리고 발을 떼면 닫히는 버튼이 존재함.&lt;ul&gt;
&lt;li&gt;Trigger될 오브젝트와 TriggerSource에 Cancle을 구현&lt;/li&gt;
&lt;li&gt;OnTriggerEnter일 때 Excute, OnTriggerExit일때 Cancle을 실행하도록 함.&lt;br&gt;상세 코드&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Trigger Source&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;public class TriggerSource : MonoBehaviour
{
    [SerializeField] protected GameObject triggerableObj;
    private ITriggerable triggerable;
    private void Awake()
    {
        if(triggerableObj.TryGetComponent&amp;lt;ITriggerable&amp;gt;(out ITriggerable componet) )
        {
            triggerable = componet;
        }
        else
        {
            Debug.Log(&amp;quot;잘못된 오브젝트 연결&amp;quot;);
        }
    }

    protected void Excute()
    {
        triggerable.Excute();
    }
    protected void Cancle()
    {
        triggerable.Cancle();
    }

}&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;ToggleButton&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;
public class ToggleButton : TriggerSource
{
    private bool isActivate = false;
    BattleZone zone;
    private void Start()
    {
        zone = triggerableObj.GetComponent&amp;lt;BattleZone&amp;gt;();
    }
    private void OnTriggerEnter(Collider collision)
    {
        if (!isActivate)
        {
            Excute();
            isActivate = true;
            zone.BattelStart = true;

        }

    }
}&lt;/code&gt;&lt;/pre&gt;&lt;h4&gt;문&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;ITriggerable 인터페이스를 상속받아 트리거가 실행될때 실행될 함수와 트리거가 취소 될때 실행될 함수를 선언해놨다&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;public interface ITriggerable
{
    public void Excute();
    public void Cancle();
}&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;문은 기본적으로 시작 위치와 종료 위치를 저장해두고, Trigger될때 시작위치에서 종료 위치로, 해제 될때 종료에서 시작 위치로 움직이도록 해두었다.&lt;/li&gt;
&lt;li&gt;근데 이제 스윙으로 열리는문과 슬라이딩으로 열리는 문 두 종류가 있을 수 있으므로...! Gate를 구현하고 SlidingGate와 SwingGate가 Gate를 상속받도록 하였다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Model (3).png&quot; data-origin-width=&quot;549&quot; data-origin-height=&quot;321&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qaImM/btsK6o8YQ52/FLqa0g1Fjk41zXO7CnkUy0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qaImM/btsK6o8YQ52/FLqa0g1Fjk41zXO7CnkUy0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qaImM/btsK6o8YQ52/FLqa0g1Fjk41zXO7CnkUy0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqaImM%2FbtsK6o8YQ52%2FFLqa0g1Fjk41zXO7CnkUy0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;549&quot; height=&quot;321&quot; data-filename=&quot;Model (3).png&quot; data-origin-width=&quot;549&quot; data-origin-height=&quot;321&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;또한 커스텀 에디터로 시작위치와 종료 위치를 오브젝트를 움직여서 저장 할 수 있도록 하였다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;682&quot; data-origin-height=&quot;229&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UsUYK/btsK68K3S6R/ppaIvkUExeKd041ag5T1RK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UsUYK/btsK68K3S6R/ppaIvkUExeKd041ag5T1RK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UsUYK/btsK68K3S6R/ppaIvkUExeKd041ag5T1RK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUsUYK%2FbtsK68K3S6R%2FppaIvkUExeKd041ag5T1RK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;682&quot; height=&quot;229&quot; data-origin-width=&quot;682&quot; data-origin-height=&quot;229&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;GateBase&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;public abstract class GateBase : MonoBehaviour, ITriggerable
{
    [SerializeField] protected float delayTime;
    [SerializeField] protected float duration;
    [SerializeField] protected Vector3 start;
    [SerializeField] protected Vector3 end;

    private Coroutine coroutine;

    public void Excute()
    {
        if (coroutine != null)
        {
            StopCoroutine(coroutine);
        }
        coroutine = StartCoroutine(Action(this.transform.localPosition, end));
    }
    public void Cancle()
    {
        if (coroutine != null)
        {
            StopCoroutine(coroutine);
        }
        coroutine = StartCoroutine(Action(this.transform.localPosition, start));
    }

    protected abstract IEnumerator Action(Vector3 start, Vector3 end);

    public abstract void SaveStartPosition();
    public abstract void SaveEndPosition();
}
&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;SlidingGate&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;public class SlidingGate : GateBase
{
    private void Awake()
    {
        this.transform.localPosition = start;
    }
    protected override IEnumerator Action(Vector3 start, Vector3 end)
    {
        float currentTime = 0;
        float actduration = duration * ((start - end).magnitude / (this.start - this.end).magnitude);
        while (currentTime&amp;lt;= actduration)
        {
            currentTime += Time.deltaTime;
            this.transform.localPosition = Vector3.Lerp(start, end, currentTime / actduration);
            yield return null;
        }

    }

    public override void SaveStartPosition()
    {
        start = this.transform.localPosition;
    }
    public override void SaveEndPosition()
    {
        end = this.transform.localPosition;
    }

}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Develop_Log</category>
      <author>리네엔</author>
      <guid isPermaLink="true">https://suyb.tistory.com/126</guid>
      <comments>https://suyb.tistory.com/126#entry126comment</comments>
      <pubDate>Tue, 3 Dec 2024 23:09:04 +0900</pubDate>
    </item>
    <item>
      <title>[Unity] Character Controller &amp;amp; Rigidbody</title>
      <link>https://suyb.tistory.com/125</link>
      <description>&lt;h3&gt;1. &lt;strong&gt;CharacterController&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;CharacterController&lt;/strong&gt;는 캐릭터의 움직임을 처리하기 위해 설계된 특수한 컴포넌트로, 주로 캐릭터의 이동을 수동으로 제어하는 데 사용&lt;/p&gt;
&lt;h4&gt;주요 특징&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;직접 이동 제어&lt;/strong&gt;: &lt;code&gt;Move()&lt;/code&gt; 또는 &lt;code&gt;SimpleMove()&lt;/code&gt; 메서드를 사용하여 캐릭터를 움직일 수 있습니다. 이동은 캐릭터가 물리적으로 기반한 힘에 의해 움직이는 것이 아니라 직접적으로 위치를 제어.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;충돌 처리&lt;/strong&gt;: CharacterController는 다른 콜라이더와의 충돌을 처리하지만, Rigidbody처럼 물리 기반의 반응을 자동으로 제공하지는 않음. 미는 등의 물리상호작용을 원한다면 OnControllerColliderHit() 함수를 사용.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;중력 및 물리 제어&lt;/strong&gt;: 중력은 자동으로 적용되지 않으며, 개발자가 직접 스크립트를 통해 중력을 적용해야 함.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. &lt;strong&gt;Rigidbody&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Rigidbody&lt;/strong&gt; 물리 법칙을 사용해 오브젝트의 움직임을 처리&lt;/p&gt;
&lt;h4&gt;주요 특징&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;물리 엔진 사용&lt;/strong&gt;: Rigidbody는 Unity의 물리 엔진에 의해 제어되며, 중력, 마찰력, 충돌 반응 등 물리적 상호작용을 자동으로 처리합니다. 물리 법칙에 따라 이동, 회전 및 충돌이 이루어짐.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;충돌 처리&lt;/strong&gt; : Rigidbody는 자동으로 다른 콜라이더와 물리 상호작용을 수행하여 밀거나 밀리림&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;중력&lt;/strong&gt;: Rigidbody는 기본적으로 중력을 자동으로 받음(&lt;code&gt;useGravity&lt;/code&gt; 옵션)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;strong&gt;CharacterController와 Rigidbody의 차이점 요약&lt;/strong&gt;&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;특성&lt;/th&gt;
&lt;th&gt;CharacterController&lt;/th&gt;
&lt;th&gt;Rigidbody&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;물리 엔진 사용&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;사용하지 않음&lt;/td&gt;
&lt;td&gt;사용함&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;중력 적용&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;직접 구현 필요 (&lt;code&gt;Move&lt;/code&gt;로 수동 이동)&lt;/td&gt;
&lt;td&gt;자동으로 중력 적용 (&lt;code&gt;useGravity&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;이동 방식&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;메서드(&lt;code&gt;Move()&lt;/code&gt;, &lt;code&gt;SimpleMove()&lt;/code&gt;)로 직접 제어&lt;/td&gt;
&lt;td&gt;물리 엔진을 통한 힘(&lt;code&gt;AddForce()&lt;/code&gt;) 사용 or valocity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;충돌 처리&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;충돌 감지는 가능, 반응은 직접 구현 필요&lt;/td&gt;
&lt;td&gt;충돌에 대한 물리적 반응을 자동 처리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;회전 제어&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;기본 회전 없음, 수동으로 구현 가능&lt;/td&gt;
&lt;td&gt;물리 엔진에 의해 회전 자동 적용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;사용 용도&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;캐릭터 움직임 제어, 플레이어/NPC&lt;/td&gt;
&lt;td&gt;물리적 상호작용 오브젝트 (박스, 장애물 등)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;</description>
      <category>Unity&amp;amp;C#</category>
      <author>리네엔</author>
      <guid isPermaLink="true">https://suyb.tistory.com/125</guid>
      <comments>https://suyb.tistory.com/125#entry125comment</comments>
      <pubDate>Mon, 2 Dec 2024 23:53:35 +0900</pubDate>
    </item>
    <item>
      <title>StateMachine / Behaviour Tree</title>
      <link>https://suyb.tistory.com/124</link>
      <description>&lt;h2&gt;Finite State Machine&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;AI를 유한한 상태로 정의하고, 상태간 전환을 통해 행동을 제어하는 방식&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Hierarchical Finite State Machine&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;FSM의 확장된 형태&lt;/li&gt;
&lt;li&gt;상태를 계층적으로 구성하여 상태간 전이를 좀더 효율 적으로 관리&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Behaviour Tree&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;트리구조로 설계한 계층적 의사결정 시스템&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Unity&amp;amp;C#</category>
      <category>오블완</category>
      <category>티스토리챌린지</category>
      <author>리네엔</author>
      <guid isPermaLink="true">https://suyb.tistory.com/124</guid>
      <comments>https://suyb.tistory.com/124#entry124comment</comments>
      <pubDate>Mon, 25 Nov 2024 23:19:00 +0900</pubDate>
    </item>
    <item>
      <title>[ScriptableObject Loader] Excel에서 SO로 만들기</title>
      <link>https://suyb.tistory.com/123</link>
      <description>&lt;p&gt;엑셀에서 데이터를 불러와서 ScriptableObject를 만드는 프로젝트를 만들었다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/1suyb/ScriptableObject-Maker&quot;&gt;1suyb/ScriptableObject-Maker:&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;[GitHub - 1suyb/ScriptableObject-Maker: Csv로부터 scriptableObject를 만드는 기능&lt;/p&gt;
&lt;p&gt;Csv로부터 scriptableObject를 만드는 기능. Contribute to 1suyb/ScriptableObject-Maker development by creating an account on GitHub.&lt;/p&gt;
&lt;p&gt;github.com](&lt;a href=&quot;https://github.com/1suyb/ScriptableObject-Maker&quot;&gt;https://github.com/1suyb/ScriptableObject-Maker&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;엑셀 데이터를 불러오는 것은 ExcelDataReader를 활용하였다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/ExcelDataReader/ExcelDataReader&quot;&gt;ExcelDataReader/ExcelDataReader: Microsoft Excel 파일을 읽기 위해 C#으로 작성된 가볍고 빠른 라이브러리&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;[GitHub - ExcelDataReader/ExcelDataReader: Lightweight and fast library written in C# for reading Microsoft Excel files&lt;/p&gt;
&lt;p&gt;Lightweight and fast library written in C# for reading Microsoft Excel files - ExcelDataReader/ExcelDataReader&lt;/p&gt;
&lt;p&gt;github.com](&lt;a href=&quot;https://github.com/ExcelDataReader/ExcelDataReader&quot;&gt;https://github.com/ExcelDataReader/ExcelDataReader&lt;/a&gt;)&lt;/p&gt;
&lt;h2&gt;구현상세&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;custom editor&lt;br&gt;Unity의 CustomEditor을 활용하여 Tool을 사용할 수 있는 Interface를 구현하였음.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;216&quot; data-origin-height=&quot;92&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pJ7FC/btsKVSBIOcg/HnKiLJ3qKZ4FsqlTl65uoK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pJ7FC/btsKVSBIOcg/HnKiLJ3qKZ4FsqlTl65uoK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pJ7FC/btsKVSBIOcg/HnKiLJ3qKZ4FsqlTl65uoK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpJ7FC%2FbtsKVSBIOcg%2FHnKiLJ3qKZ4FsqlTl65uoK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;216&quot; height=&quot;92&quot; data-origin-width=&quot;216&quot; data-origin-height=&quot;92&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;333&quot; data-origin-height=&quot;202&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/buSn11/btsKT9dGMqF/ZAo2Xtbsry3MojL7ecplnk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/buSn11/btsKT9dGMqF/ZAo2Xtbsry3MojL7ecplnk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/buSn11/btsKT9dGMqF/ZAo2Xtbsry3MojL7ecplnk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbuSn11%2FbtsKT9dGMqF%2FZAo2Xtbsry3MojL7ecplnk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;333&quot; height=&quot;202&quot; data-origin-width=&quot;333&quot; data-origin-height=&quot;202&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;br&gt;구현코드&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/1suyb/ScriptableObject-Maker/blob/main/CSVParser/Assets/ExceltoSO/Scripts/Editor/ExcelLoaderWindow.cs&quot;&gt;https://github.com/1suyb/ScriptableObject-Maker/blob/main/CSVParser/Assets/ExceltoSO/Scripts/Editor/ExcelLoaderWindow.cs&lt;/a&gt;&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;&lt;p&gt;scripts maker&lt;br&gt;using ExcelDataReader를 활용하여 Excel을 파싱하고, data클래스와 enum 클래스, so 클래스를 만들고 엑셀 데이터로부터 so클래스의 리스트를 채워줄 도우미 역할을 하는 loader 클래슬르 만들어준다.&lt;br&gt;구현 코드&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/1suyb/ScriptableObject-Maker/blob/main/CSVParser/Assets/ExceltoSO/Scripts/ExcelParser.cs&quot;&gt;https://github.com/1suyb/ScriptableObject-Maker/blob/main/CSVParser/Assets/ExceltoSO/Scripts/ExcelParser.cs&lt;/a&gt;&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Excel data loader&lt;br&gt;C#의 reflection을 활용하여 엑셀 데이터로부터 Data인스턴스를 만들고, list로 담아서 저장해 반환함&lt;br&gt;해당 list는 so 클래스에 담겨 저장되게됨.&lt;br&gt;구현코드&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/1suyb/ScriptableObject-Maker/blob/main/CSVParser/Assets/ExceltoSO/Scripts/ExcelDataLoader.cs&quot;&gt;https://github.com/1suyb/ScriptableObject-Maker/blob/main/CSVParser/Assets/ExceltoSO/Scripts/ExcelDataLoader.cs&lt;/a&gt;&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;사용법은 readme.md참조.&lt;/p&gt;</description>
      <category>Develop_Log</category>
      <category>오블완</category>
      <category>티스토리챌린지</category>
      <author>리네엔</author>
      <guid isPermaLink="true">https://suyb.tistory.com/123</guid>
      <comments>https://suyb.tistory.com/123#entry123comment</comments>
      <pubDate>Sun, 24 Nov 2024 22:18:50 +0900</pubDate>
    </item>
    <item>
      <title>[팀플젝 회고] 유니티 심화주차</title>
      <link>https://suyb.tistory.com/122</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;진짜 미친듯이 최선을 다한...까지는 아니였지만 (동생 실기로 인해 주말에 불참 ㅠ)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래도 누가 아파서 통으로 빠지거나하지않고 처음&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;슬라이드1.PNG&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bFq0Oc/btsKTnXn7jq/hoKhjsAfx91yuyDEFKMyc1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bFq0Oc/btsKTnXn7jq/hoKhjsAfx91yuyDEFKMyc1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bFq0Oc/btsKTnXn7jq/hoKhjsAfx91yuyDEFKMyc1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFq0Oc%2FbtsKTnXn7jq%2FhoKhjsAfx91yuyDEFKMyc1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;604&quot; height=&quot;340&quot; data-filename=&quot;슬라이드1.PNG&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;으로 모든 구성원이 열심히 시간 맥스로 채워서 달린 프로젝트 였던 것 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;슬라이드7.PNG&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bB92jf/btsKUeyM5bu/KkeRGi6CPIWO4lNUIYOnf1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bB92jf/btsKUeyM5bu/KkeRGi6CPIWO4lNUIYOnf1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bB92jf/btsKUeyM5bu/KkeRGi6CPIWO4lNUIYOnf1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbB92jf%2FbtsKUeyM5bu%2FKkeRGi6CPIWO4lNUIYOnf1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;720&quot; data-filename=&quot;슬라이드7.PNG&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 몬스터 전반 및 낮 과 밤 로직등을 맡아서 구현했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오브젝트 풀링이나 스폰매니저, CSVtoSO등은 내가 이전에 쓰던 코드를 가져다가 사용했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;점점 프로젝트를 하다보니 다시 사용할 수 있는 코드들은 재사용하게 되니 1시간정도는....빨라진 것 같기도.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;몬스터 구현은 강의의 FSM을 사용하여 구현하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;몬스터 상태가 몇가지 없어서 그냥 구현할까하다가 그래도 배운걸 적용해보고자 FSM을 사용하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에 chase랑 Attack 만 존재하는 상태라 아 이걸 왜 복잡하게 이렇게 한다고 했지... 싶었지만 몬스터가 chase상태에서 죽을 때 계속 이동하며 애니메이션을 재생하는 문제가 있었다. 이때 그냥 플래그로 이동을 막을 수도 있었겠지만, state machine을 구현해놨으니..! die 상태를 추가해줘서 die 애니메이션을 실행하게하였다. 아주 편안하게 멈춰서 죽는걸 볼 수 있었다..!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;die애니메이션후 disalbe상태로 만드는 것도 사실 die 상태에서 해주면 됬겠지만 처음에 애니메이션이벤트로 붙여놔서 그냥 냅두었다. 어쨌든 die상태를 추가하는게 매우 쉬워지는 것을 볼 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;몬스터 스폰은 이전 숙련 주차에서 구현했던 areaspawner와 유사하게 일정 반지름 안에서 생성되도록 하려고 했다가 그래도 베이스 캠프의 일정 범위 밖에서 생성되어야 했기 때문에, min max radius를 받아서 position을 정해서 스폰하도록 했다. 그리고 코루틴으로 일정 시간 간격으로 지속적으로 스폰되도록 하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;팀원분이 난이도 ui를 만들어 주셨기에, 난이도에 대응할 수 있도록 spawndata를 만들고, 난이도별로 so를 만들어 난이도에 따라 데이터를 갈아끼워 몬스터 스폰양을 조절 할 수 있도록 했다. 이렇게 되니 난이도가 현재처럼 3개가 아니라 5개로 늘어나도 so와 enum, ui만 추가해주면 난이도 조절은 쉽게 할 수 있게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;난이도에 따라 몬스터의 스폰량이 늘어나는데, 이를 처음에는 그냥 선형적으로 늘어나게 하고 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그랬더니 몇일만지나도 엄청나게 불어있는 몬스터를 볼 수 있었다. 이로인해 렉까지 걸릴정도..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;렉 뿐만아니라 난이도 면에서도 답이 없었기 때문에 log함수를 사용하여 몬스터의 증가량을 제어해주었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;몬스터를 디스폰해주는 것은 이 스포너 스크립트가 베이스 캠프에 붙어있기는 하지만..! 몬스터를 이 스크립트가 달린 오브젝트의 자식으로 스폰해서 자식오브젝트를 싹다 for문돌며 setactivefalse해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;풀링은 자동으로 되고있다! 오브젝트를 스폰할때 SpawnManager로 스폰한다면 해당 오브젝트를 비활성화할때 자동으로 풀에 들어가게 뒤의 프레임워크를 구현해두었다. 단점이라면 GameObejct밖에 풀링을 못한다는 것... 다른 클래스들을 풀링하려면 사실 classpool이라는 다른...풀을 만들어두었지만 일단 이번엔 사용하지 않았다. 이건 다른 프로젝트에서 사용했었는데 이를 SpawnManager로는 스폰할수가 없다.. ㅠ 다르게 방법을 고민해봐야겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;난이도에 따라 몬스터 스폰양이 달라지는 건 좋은데.. 구매한 에셋이 몬스터가 한종류라 아쉬웠다. 그래도.. 우린 scale을 바꿀 수 있다. scale을 바꾸어 몬스터를 3종류로 만들었다. 그리고 팀원들 item을 so로 한땀한땀 관리하는걸 보니 엄청 귀찮아보였고... 나는 저런걸 참을 수 없다. 예전에 숙련주차 개인과제때 만들었던 csv to so를 활용해서 csv로부터 몬스터 데이터를 로드해서 몬스터의 데이터를 so로 관리하도록 만들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 만들고나니 상위 몬스터의 등장 확률이 문제였는데. 이런 무한 스테이지에서 관리하는 방법이 있을까 하여 튜터님께 여쭤보러 갔더니 기획자의 영역이라 기획자마다 천차 만별이란다...! 뭔가 알고리즘 같은게 있지 않을까 했는데..! 근데 생각해보니 당연히 데이터를 관리하는 방법도 다를 것이고 몬스터의 종류 등등도 다를테니 그게 맞는 것 같기도하고.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 일단 작은 게임이고 테이블을 하나더 만들고 그걸 핸들링 하도록 하려면 사실 그냥 게임 전체의 데이터를 갖고 있는 데이터를 만들고 해야할 것 같은데 거기까진 시간견적이 절대 안나오니.. 몬스터의 아이디를 0부터 시작하게 해서 지난 날짜를 최대 id값까지 모듈러 값을 취해서 0일때 그 아이디의 생성 확률이 1/(id개수*10)만큼 증가하도록...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 지금까지 지난 날짜를 담은 변수가 day라고 할때,&lt;/p&gt;
&lt;pre id=&quot;code_1732269032322&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if day%id ==0 then
	if idspawncount[id]&amp;lt;=10 then
		spawnweight[id*10+idspawncount[id]+1] = id
		idspawncount[id]++&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;spawnweight는 저기서 random index를 뽑아서 들어있는 id의 몬스터를 생성하는 그런 array이고, idspawncount는 해당 id가 몇마리나오는지를 갖고있는 dictionary이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메모리 효율성이 좀 나쁠 수 있지만.. 빠르게 생각 할 수 있는 방법이었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;슬라이드8.PNG&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VHl19/btsKUCeQsi8/SogsPWmeEOm26UfZ2pcqtk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VHl19/btsKUCeQsi8/SogsPWmeEOm26UfZ2pcqtk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VHl19/btsKUCeQsi8/SogsPWmeEOm26UfZ2pcqtk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVHl19%2FbtsKUCeQsi8%2FSogsPWmeEOm26UfZ2pcqtk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;720&quot; data-filename=&quot;슬라이드8.PNG&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 낮과 밤의 변환은 그냥 간단하게 하루시간과 낮의 시간을 정해주면 코루틴을 돌며 낮 밤을 바꾸게 해놨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 event를 두고 타임이 바뀔때 실행되어야 하는 함수들을 구독받게 event도 열어놨다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이에 대해서 사실 하루가 바뀔때 일어나야하는 event와 낮밤이 바뀔때 일어나는 이벤트 둘로 나누어 놨는데.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하루가 바뀔때, 낮이될때, 밤이 될때로 나누어야 했었을 것 같기도 하다. 이렇게되면 몬스터 스폰해주는 부분에서 스위치문을 써야하므로..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기획이야기할 때&amp;nbsp; 밤에 베이스캠프에서 멀어지면 화면이 어두워지면서 정신력이 떨어지는.. 그런기능을 만들자고 이야기가 나왔었다. 그래서 정신력은 플레이어에서 해줘야하는데 플레이어를 갈아엎고 있으므로.. 거리에 따라 화면이 어두워지는 기능만 만들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;거리에 따라 시그모이드 함수를 따라 알파값을 조정하였다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;슬라이드9.PNG&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/22gLr/btsKUC0bQo3/QueOWimXDpkVJuZJlOix91/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/22gLr/btsKUC0bQo3/QueOWimXDpkVJuZJlOix91/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/22gLr/btsKUC0bQo3/QueOWimXDpkVJuZJlOix91/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F22gLr%2FbtsKUC0bQo3%2FQueOWimXDpkVJuZJlOix91%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;720&quot; data-filename=&quot;슬라이드9.PNG&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;플레이어 화면에 비해 오브젝트가 생성되는 범위가 너무 넓었다. 그래서 미니맵이 있으면 좋겠다는 이야기가 나와서 만들었다. 사실 기술적으로 별거 없는데 팀원분들이 되게 신기해하셔서... 재밌었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RenderTexture를 생성하고, 이 생성한 랜더텍스쳐를 카메라의 TargetTexture에 할당해주었다. 물론 이 카메라는 플레이어가 메인으로 보는 카메라가 아니다! 미니맵에만 보여야할 정보들로 컬링 마스크를 설정해주고, 이 카메라도 플레이어를 따라가게 해준다음 해당 RenderTexture를 UI를 만들어서 RawImage컴포넌트를 만들고, Texture로 할당해주었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아이템도 구조가 잘못잡혀있어서... 간단하게 수정해두었는데 내가 처음부터 개발한건아니므로 패스.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Develop_Log</category>
      <category>오블완</category>
      <category>티스토리챌린지</category>
      <author>리네엔</author>
      <guid isPermaLink="true">https://suyb.tistory.com/122</guid>
      <comments>https://suyb.tistory.com/122#entry122comment</comments>
      <pubDate>Fri, 22 Nov 2024 19:33:21 +0900</pubDate>
    </item>
  </channel>
</rss>