구현 내용
1. GameScene 생성

using UnityEngine;
public class GameScene : MonoBehaviour
{
[SerializeField] private GameObject Root_Camera = null;
[SerializeField] private GameObject Root_Environment = null;
[SerializeField] private GameObject Root_UI = null;
[SerializeField] private GameObject Root_Character = null;
private void Start()
{
UIManager.Instance.SetUIRoot(Root_UI);
UIManager.Instance.SetActiveRoot(UI.BackGround, false);
UIManager.Instance.Open<IngameWindow>(UI.Main, "Prefabs/UI/Window/IngameWindow");
}
}
다른 Scene과 동일하게 Window 프리팹을 생성해준다.
2. BattleModule
https://kkln2486.tistory.com/526
BattleModule
BattleModule GameScene에서 전투 관련 로직 관리BattleModule을 부모 클래스로 컨텐츠 마다 자식 Module을 생성해서 상속 받음공통적으로 사용하는 함수는 virtual 함수로 선언하여 자식 Module에서 재정의ex) P
kkln2486.tistory.com
using UnityEngine;
public class GameScene : MonoBehaviour
{
[SerializeField] private GameObject Root_Camera = null;
[SerializeField] private GameObject Root_Environment = null;
[SerializeField] private GameObject Root_UI = null;
[SerializeField] private GameObject Root_Character = null;
private void Start()
{
// Module 생성
BattleModule.CreateModule<PVPModule>();
BattleModule.Instance.SetRootObject(Root_Camera, Root_Environment, Root_Character);
await BattleModule.Instance.StartGame();
// IngameWindow 생성
UIManager.Instance.SetUIRoot(Root_UI);
UIManager.Instance.SetActiveRoot(UI.BackGround, false);
UIManager.Instance.Open<IngameWindow>(UI.Main, "Prefabs/UI/Window/IngameWindow");
}
}
Module 생성 코드를 추가한다.
3. GameScene 맵 추가

https://kkln2486.tistory.com/527
Sky box 설정
설정 방법1. Sky box 에셋 다운로드직접 만들 수도 있지만 에셋스토어에 무료 버젼이 많기 때문에 왠만하면 에셋스토어를 이용하자. 2. Window - Rendering - Lighting 6000.0.40f1 기준 위 경로로 들어간다. 3.
kkln2486.tistory.com
맵 에셋과 Skybox 에셋을 에셋스토어를 통해 다운로드 하고 Skybox를 설정해준다.
4. GameScene AI 데이터 생성 및 UI 연결

랜덤하게 AI 유저를 만들어서 나의 유저 데이터와 적 유저 데이터를 UI에 표시하고, Round 별로 시간을 체크하고 UI에 표시하도록 하였다.
트러블 슈팅
1. BattleModule

BattleModule은 인게임에서 전투 컨텐츠를 관리하고 전투 로직을 구성한다. BattleModule이라는 부모 클래스를 구성하고, 각각의 컨텐츠 Module은 이 BattleModule을 상속 받는 구조이다.
상속 구조를 사용한 이유?
- 이전 프로젝트까지는 상속 구조를 사용하지 않고, 코드를 복사해서 수정하는 방식으로 했음
- 중복 코드가 많아지고, 코드를 수정하게 되면 각각 스크립트에서 모두 수정해야 했음
- 각각 전투 컨텐츠는 대부분 동일한 방식으로 진행되기 때문에 공통 코드가 많음
- ex) StartGame, UILoad, MonsterLoad 등
- 새로운 전투 컨텐츠 추가 시, 특정 코드만 추가하면 되기 때문에 관리가 편함
어떤 식으로 했는가?
- BattleModule 클래스에서 공통적으로 사용하되, 전투 컨텐츠 별로 코드의 수정이 일부 필요한 경우에는 virtual로 함수를 선언
- ex) Initialize,StartGame, EndGame, LoadUI 등
- BattleModule 클래스에서 공통적으로 사용하고, 코드의 수정이 필요 없는 경우에는 public 함수로 선언
- ex) SetPause, IsModule 등
BattleModule.cs
using UnityEngine;
using Cysharp.Threading.Tasks;
public class BattleModule : MonoBehaviour
{
#region Member Property
protected GameObject m_CharacterRoot = null;
protected GameObject m_CameraRoot = null;
protected GameObject m_EnvironmentRoot = null;
#endregion
#region Instance
// 인스턴스
private static BattleModule m_Instance;
public static BattleModule Instance
{
get
{
return m_Instance;
}
}
public static T CreateModule<T>() where T : BattleModule
{
GameObject obj = GameObject.Find("BattleModule");
if (obj == null)
{
obj = new GameObject("BattleModule");
DontDestroyOnLoad(obj);
}
var Module = obj.GetComponent<T>();
if (Module != null)
{
DestroyModule();
}
Module = obj.AddComponent<T>();
m_Instance = Module;
return Module;
}
public static void DestroyModule()
{
DestroyImmediate(m_Instance);
m_Instance = null;
}
#endregion
// BattleModule을 상속받는 Module에서 대부분 공통으로 사용하는 기능을 구현
// 자식 Module에서 구현하지 않아도 된다.
#region Virtual Method
// 초기화
public virtual void Initialize()
{
StartGame();
}
// 게임 시작
// 로드 할 내용이 많이 때문에 Delay를 준다.
protected async virtual void StartGame()
{
// 1. UI
LoadUI();
await UniTask.Delay(100);
// 2.
await UniTask.Delay(100);
// 3.
await UniTask.Delay(100);
// ....
}
// 게임 끝
protected virtual void EndGame()
{
//if (MonsterMovementSystem != null)
// Destroy(MonsterMovementSystem);
//if (m_AutoPlayController != null)
// Destroy(m_AutoPlayController);
//SetStartGame(false);
//m_IsPause = true;
//m_IsEndGame = true;
// Module 제거
DestroyModule();
}
// UI 세팅
public virtual void LoadUI()
{
UIManager.Instance.CloseAll();
}
#endregion
// BattleModule을 상속받는 module에서 별다른 구현 없이 공통적으로 사용하는 기능을 구현
#region Public Method
public bool IsModule<T>() where T : BattleModule
{
return this is T;
}
public void SetRootObject(GameObject cameraRoot, GameObject environmentRoot, GameObject characterRoot)
{
m_CameraRoot = cameraRoot;
m_EnvironmentRoot = environmentRoot;
m_CharacterRoot = characterRoot;
}
public void SetPause(bool isOn, bool isShowPauseUI = false)
{
//m_IsPause = isOn;
//if (m_IsPause)
//{
// Time.timeScale = 0f;
// if (isShowPauseUI)
// {
// var arenaModule = Instance as PvPArenaModule;
// if (arenaModule != null)
// UIManager.Instance.Open<Popup_Arena_Pause>(UI.Popup, "UI/Popup/Popup_Arena_Pause");
// else
// UIManager.Instance.Open<PauseWindow>(UI.Main, "UI/Ingame/PauseWindow");
// arenaModule = null;
// }
//}
//else
//{
// Time.timeScale = 1f;
//}
}
#endregion
}
PVPModule.cs
public class PVPModule : BattleModule
{
#region Overrid Method
public override void Initialize()
{
base.Initialize();
}
protected override void StartGame()
{
base.StartGame();
}
protected override void EndGame()
{
base.EndGame();
}
#endregion
}
회고
기존 프로젝트까지는 상속 구조를 잘 활용하지 않았다. 그 이유는 아직까지 상속 구조가 어렵게만 느껴지고 딱히 굳이 사용하지 않아도 같은 코드를 복사해서 사용하면 충분히 구현이 가능했기 때문이다. 하지만 회사에서 큰 프로젝트를 진행하면서 상속 구조는 정말 필수적이라는 것을 알게 되었다. 그래서 작은 개인 프로젝트이지만 상속 구조를 사용해서 코드의 중복을 줄이고 깔끔한 구조를 만드는 것에 연습이 필요하다고 느껴서 상속 구조를 통해 전투 컨텐츠 Module을 구현하였다.
그리고 GameScene를 생성하고 전투를 하기전 기본적인 유저 데이터와 적 유저 데이터를 UI에 연결하는 작업을 했다. 멀티 대전을 구현하기 전, 일단 우선적으로 AI 적을 만들어 셋팅했다. 기본적으로 멀티 대전에 적을 찾을 수 없다면, AI 적과 대전을 하도록 할 예정이다. 다음으로는 매칭된 적과 실제로 전투를 하도록 구현할 것이다!
'1인 개발 > Real Fighter' 카테고리의 다른 글
| Real Fighter 개인 프로젝트 5일차 (0) | 2025.08.26 |
|---|---|
| Real Fighter 개인 프로젝트 4일차 (3) | 2025.08.18 |
| Real Fighter 개인 프로젝트 3일차 (0) | 2025.08.16 |
| Real Fighter 개인 프로젝트 2일차 (1) | 2025.08.14 |
| Real Fighter 개인 프로젝트 1일차 (1) | 2025.08.12 |