Post

C# Hexa Village - Framework Composition

C# Console에서 게임 엔진 Unity처럼 동작하는 방법을 구성할려고 한다. 아주 간단한 구현 방법을 생각했는데
유니티에서 제공되는 MonoBehaviour의 메인 기능을 Scene으로 추상화 시키는 것이다.

추상 클래스의 구성

  • 가장 먼저 할 일은 Scene들이 가지고 있는 중복된 동작들을 요약/정리 하는 것이다.

Scene은 Start()Update()로 기본 구성 되고, 초기화와 업데이트를 진행한다.
위 내용을 토대로 abstract Scene class를 추상화만 시켜주면 완성이다.


 Scene Class

1
2
3
4
5
6
7
8
/*
** Title, Game, Lobby등의 씬이 상속받게 될 추상 클래스
*/
public abstract class Scene
{
	public abstract void Start();
	public abstract void Update();
}

추상 클래스는 상속받는 자식 클래스가 abstract로 지정된 멤버 필드 또는 메서드를 명시적으로 구현해야 한다.


 Sub Class

1
2
3
4
5
6
7
8
9
10
11
12
public class Title : Scene
{
	public override void Start()
	{
		// Member Variables Init
	}

	public override void Update()
	{
		// break가 없는 while(true)라고 생각.
	}
}

부모(추상) 클래스를 통해 자식 클래스들을 제어할 수 있는 형태가 구성 됐다.


 Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static void Main()
{
	/*
	* Scene 클래스를 상속받는 Title을 동적 할당 생성 *
	* Start() 함수를 통해 단 한번 초기화를 시켜준다. *
	*/
	Title titleScene = new Title();
	titleScene.Start();
	
	Scene currentScene = titleScene; // 자식 titleScene을 받는다.
	
	while(true)
	{
		// currentScene은 추상 클래스지만 안에 내용은 자식이 들어가있다.
		// 즉 Update를 돌리면 안에 자식 내용이 돌아간다.
		currentScene.Update();
	}
}

Scene Interact


프레임 워크 구현

  • 위 내용을 기반으로 하여 SceneManager만 구현하면 기초 프레임워크의 완성이다.
  • 씬 매니저의 구성은 currentScene이 존재하고 이를 관리를 해줘야 한다.

 Singleton Super Class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
namespace HexaCoreVillage.Framework;

/// <summary>
/// # 싱글톤화를 위한 상속용 클래스
/// ## 사용 예제
/// ### public class Manager : Singleton<Manager>
/// ### 위와 같이 생성된 클래스는 싱글톤이 되며 Manager.Instance 사용 가능
/// </summary>
public class Singleton<T> where T : class, new()
{
    // 싱글톤화된 객체 인스턴스
    private static T? _instance;
    // 혹시 모를 비동기 작업을 위해 Lock 제약조건
    // 아예 무시하고 넘어가셔도 무방합니다.
    private static readonly object Lock = new object();

    #region Properties
    public static T Instance
    {
        get
        {
            lock(Lock)
            {
                if(_instance == null)
                    _instance = new T();

                return _instance;
            }
        }
    }
    #endregion

}

 SceneManager Class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
/// <summary>
/// # Scene을 관리하는 매니저 클래스
/// ## Scene by Scene : 씬을 이동 시키는 매개 클래스
/// </summary>
public class Manager_Scene
{
    #region Member Variables

    private Scene? _currentScene = null;

    #endregion



    #region Properties (Getter)
    public Scene? GetCurrentScene() { return _currentScene; }
    #endregion

    

    #region Main Methods
    public void LoadScene(SCENE_NAME sceneKey)
    {
        // 추후 리소스 관리쪽으로 넘어갔을 때를 위한 처리 (아직은 기능 없음)
        if (_currentScene != null)
            _currentScene = null;

        // switch Lambda, _currentScene에 SCENE_NAME에 해당하는 SCENE으로 동적 할당
        _currentScene = sceneKey switch
        {
            SCENE_NAME.TITLE => new TitleScene(),
            SCENE_NAME.LOGIN => new LoginScene(),
            SCENE_NAME.LOBBY => new LobbyScene(),
            SCENE_NAME.STATUS => new StatusScene(),
            SCENE_NAME.STORE => new StoreScene(),
            SCENE_NAME.BATTLE => new BattleScene(),
            SCENE_NAME.REWARD => new RewardScene(),
            _ => throw new ArgumentException("Invalid [SceneKey]")
        };

        // Change the 'scene' and initalize(Start)
        _currentScene.Start();
    }
    #endregion
}

나중에 수정 시 참고해야 될 사항

  • 리소스를 절약할 수 있다. 씬을 버리지 않고 재활용 한다면!
    1. List<Scene> _sceneList
    2. Scene 추상 클래스에 abstract void Reset()` 메서드 추가
    3. if(_currentScene != null) 부분에 _currentScene.Reset()을 해준다.
    4. switch Lambda식에서 new()를 통한 생성이 아닌 List[Index]로 꺼내온다.
  • 이로써 간단한 Scene기반 프레임워크가 구현되었다.
  • Main Procedure의 반복문은 currentScene의 업데이트만 진행하면 된다.

 해보고자 하는 것

[닌텐도 1세대와 같이 4방향 움직이는 게임]

  • 필요 프레임워크 구상

    • Scene은 foreach를 통해 항시 업데이트 및 렌더링이 되어야한다.
    • Scene → Update() = foreach(var GameObject in \_gameObjects)
    • class : GameNode → Scene → UserScene
    • class : GameNode → GameObject (Player)

    • 캐릭터 이동 및 화면 그리기
      • 더블버퍼링 기법 + 더티 플래그 기법을 합쳐서 구현
      • 캐릭터의 입력을 받을 때 = FrontBuffer (그려지는 화면)
      • 캐릭터의 입력을 받은 후 = BackBuffer (뒤에서 작업할 화면)

This post is licensed under CC BY 4.0 by the author.