대부분의 게임은 캐릭터에게 기본 데미지가 있고 치명타 데미지가 있다.

치명타 확률이 n이라 치면, n%확률로 치명타 데미지를 가해야 하는데

 

이 계산법을 코드로 한번 작성해보도록 하자!

 

- 크리티컬 확률을 구현해보자 (C#)

만약 크리티컬 확률이 30%이라 가정하면 어떻게 구현해야 할까?

 

생각보다 쉽다 1 ~ 100까지의 난수를 일단 생성한다.

Random.Range(0, 99)

 

왜 0 ~ 99 까지인가?  30%확률은 100번 실행했을때 30번이 평균적으로 나와야 한다.

cirt을 int형으로 했을 경우에는 난수 < 30 으로 해야 0 ~ 29 이므로 30개가 된다.

 

Random.Range(1,100)로 한 다음 난수 <= 30으로 해도 된다!

 

난수cirt보다(30보다) 작은 경우에만 치명타가 발생한다!

bool isCrit = Random.Range(0, 99) < cirt;

 

isCirt은 난수 값이 cirt보다 크면 false, 작으면  true 가 된다.

 

    public class AttackSkill : ISkillExecuteable
    {
        public void ExecuteSkill(BasePlayer player, BaseUnit target, int damage, int cirt)
        {
            bool isCrit = Random.Range(0, 99) < cirt;
            int finalDamage = isCrit ? Mathf.CeilToInt(damage * 1.5f) : damage;

            target.TakeDamage(finalDamage);
        }
    }

 

target(지정대상)이 받는 대미지는 일반 데미지일 경우 기본 데미지를,

치명타 데미지일 경우 1.5배 (소수점이면 무조껀 올림처리)를 가한다

 

게임 프로그래밍을 하면서 확장 코딩 작업을 해야할 때 있는데

 

확장할 때 기존에 정상작동 하는 부분들은 그대로 동작하면서 확장시켜야 한다.

 

확장할 때마다 매번 새롭게 모든걸 작업할 수는 없는 노릇이니...ㅎㅎㅎ

 

그럴때 유용한 Try-Catch문이 있다! 오늘은 이 문법을 공부하면서 개발을 진행해보자.


1. 먼저 기존에 UI 작업 -> 새로운 확장 메서드 추가 -  ShowActionUI( )

    // 플레이어 턴 UI 표시
    public void ShowActionUI(System.Action attackCallback, System.Action moveCallback)
    {
        playerActionUI.SetActive(true);
        onAttackSelected = attackCallback;
        onMoveSelected = moveCallback;  
    }

 

이제는 UI 표시가 공격, 이동 뿐 아니라 스킬들 까지 추가해야 되서 오버로딩으로 메서드를 만들었다!

 // UI 표시 버튼 리스트 버전
 public void ShowActionUI(BasePlayer player, SkillManager skillManager,
 				System.Action attackCallback, System.Action moveCallback)
 {
     playerActionUI.SetActive(true);
     onAttackSelected = attackCallback;
     onMoveSelected = moveCallback;

     // 기존 스킬 버튼 삭제
     ClearSkillButtons();

     // 플레이어가 선택한 스킬 가져오기
     if (skillManager.playerSkills.TryGetValue(player, out List<Player_SkillData> selectedSkills))
     {
         foreach (var skill in selectedSkills)
         {
             CreateSkillButton(skill);
         }
     }
 }

 // 스킬 버튼 동적 생성
 private void CreateSkillButton(Player_SkillData skill)
 {
     GameObject newButtonObj = Instantiate(skillButtonPrefab, skillButtonParent);
     Button newButton = newButtonObj.GetComponent<Button>();
     newButton.GetComponentInChildren<Text>().text = skill.skill_Name; // 버튼 텍스트 설정
     newButton.onClick.AddListener(() => UseSkill(skill));
     skillButtons.Add(newButton);
 }


 // 스킬 버튼 삭제 (UI 초기화)
 private void ClearSkillButtons()
 {
     foreach (var button in skillButtons)
     {
         Destroy(button.gameObject);
     }
     skillButtons.Clear();
 }

 

ShowActionUI() 메서드를 확인하면 매개변수들을 추가한 확장된 메서드로 개발할려고 한다.


2. 호출받는 메서드 확인: StartPlayerTurn( )

    // 플레이어 턴 시작
    public void StartPlayerTurn()
    {
        playerActionUI.ShowActionUI(currentPlayer, skillManager, OnAttackSelected, OnMoveSelected);
        MouseClick();
    }

해당 UI 메서드는 플레이어의 턴이 시작되면 호출받는다. 

(MouseClick() 은 임의로 만들었던 마우스 클릭 메서드이다.)

 

이제 오버로딩으로 확장한 메서드를 테스트할 차례이다.


3. Try-Catch 문을 사용하기: StartPlayerTurn()

    // 플레이어 턴 시작
    public void StartPlayerTurn()
    {
        try
        {
            playerActionUI.ShowActionUI(currentPlayer, skillManager, OnAttackSelected, OnMoveSelected);
        }
        catch (System.Exception e)
        {
            Debug.LogWarning($"오류 발생: {e.Message}, 기본 UI를 호출.");
            playerActionUI.ShowActionUI(OnAttackSelected, OnMoveSelected);
        }
        finally
        {
            MouseClick();
        }
    }

 

try { } : 내부에 있는 코드를 실행해본다. 오류가 발생하지 않으면 정상적으로 작동한다.

catch { } : 오류가 발생하면 Catch문의 내용이 실행된다.

finally { } : try-catch문과 관계없이 항상 실행된다.


테스트 결과

try-catch문 결과

 

아직 UI 관련된 내용을 하이어라키에 만들지 않았으므로

오류가 발생하기 때문에 Catch문을 통해서 기존 UI 메서드로 실행된다.

 

개발함에 있어서 확장이라는 개념에서 기존에 정상작동 하는 기능들은 건들지 말아야 하기 때문에

유용한 방법 중 하나인 try-catch 문을 학습해 보았다!

 

게임에서 플레이어 캐릭터들은 자신만의 기술을 가지고 있다.

베기, 찌르기, 이동하기, 총으로 공격하기 등등... 많은 기술들을 관리하기 위해

Builder 패턴을 활용해서 만들어보자!

 

 Bilder 패턴이 무엇인지는 이 글에서 확인해보자!

https://twilightchicken.tistory.com/entry/c-%EA%B2%8C%EC%9E%84%EC%9C%BC%EB%A1%9C-%EC%9D%B4%ED%95%B4%ED%95%98%EB%8A%94-%EC%83%9D%EC%84%B1-%ED%8C%A8%ED%84%B4-04-%EB%B9%8C%EB%8D%94Builder


1. 우선 캐릭터  스킬의 타입을 정의한다! 

public enum Player_SkillType
{
    type_Melee,  // 근접
    type_Range,  // 원거리
    type_Heal,   // 치유
    type_Buff    // 버프
}

 

캐릭터 기술의 타입은 근접형, 원거리형, 치유형, 버프형이 있다.


2. 캐릭터 스킬에 필요한 변수들을 정의한다!

    public bool skill_Seleted = false;  // 선택 유무
    public string skill_Name;   // 스킬명
    public int skill_Damage;    // 데미지
    public int skill_Crit;      // 치명타율
    public float skill_Heal;    // 힐량

    public Player_SkillType skill_Type; // 스킬 타입
    public Sprite skill_sprite;     // 스킬 아이콘

    public List<Vector2Int> skill_useablePos = new List<Vector2Int>(); // 사용 위치 리스트
    public List<Vector2Int> skill_targetPos = new List<Vector2Int>();  // 공격 대상 리스트

 

레퍼런스가 다키스트 던전이므로 필요한 변수들만 정의해보았다!


3-1. 빌더(Builder) 예시 코드

public skill_Builder SetName(string name) { skill.skill_Name = name; return this; }
public skill_Builder SetDamage(int damage) { skill.skill_Damage = damage; return this; }

이런식으로 스킬 변수들을 빌더 패턴에 적용시키면 된다!

 

3-1. 스킬 빌더(skill_Builder) 코드

// 스킬 빌더 패턴
public class skill_Builder 
{ 
    private Player_SkillData skill = new Player_SkillData();

    public skill_Builder SetName(string name) { skill.skill_Name = name; return this; }
    public skill_Builder SetDamage(int damage) { skill.skill_Damage = damage; return this; }
    public skill_Builder SetHeal(float heal) { skill.skill_Heal = heal; return this; }
    public skill_Builder SetCrit(int crit) { skill.skill_Crit = crit; return this; }
    public skill_Builder SetType(Player_SkillType type) { skill.skill_Type = type; return this; }
    public skill_Builder SetIcon(Sprite icon) { skill.skill_sprite = icon; return this; }

    public skill_Builder SetUseablePositions(params Vector2Int[] positions)
    {
        skill.skill_useablePos.AddRange(positions);
        return this;
    }

    public skill_Builder SetTargetPositions(params Vector2Int[] positions)
    {
        skill.skill_targetPos.AddRange(positions);
        return this;
    }

    public Player_SkillData Build() { return skill; }
}

4. 플레이어 캐릭터 클래스에서 빌더 패턴으로 스킬을 추가해보자!

 // 테스트용 기술 생성해보기
 public void InitializeSkills()
 {
     allSkills.Add(new Player_SkillData.skill_Builder()
         .SetName("내려찍기")
         .SetDamage(10)
         .SetCrit(15)
         .SetType(Player_SkillType.type_Melee)
         .SetUseablePositions(new Vector2Int(0, 1), new Vector2Int(1, 1))  // 사용 가능 위치
         .SetTargetPositions(new Vector2Int(0, 2), new Vector2Int(1, 2))   // 두 위치 중 지정 가능
         .Build()
         );

     allSkills.Add(new Player_SkillData.skill_Builder()
         .SetName("Gun 공격")
         .SetDamage(12)
         .SetCrit(10)
         .SetType(Player_SkillType.type_Range)
         .SetUseablePositions(new Vector2Int(0, 0), new Vector2Int(1, 0))  // 사용 가능 위치
         .SetTargetPositions(new Vector2Int(0, 2), new Vector2Int(0, 3), new Vector2Int(1, 2), new Vector2Int(1, 3)) // 대상 위치
         .Build()
         );

     allSkills.Add(new Player_SkillData.skill_Builder()
        .SetName("자힐")
        .SetHeal(10)
        .SetCrit(5)
        .SetType(Player_SkillType.type_Heal)
        .SetUseablePositions(new Vector2Int(0, 0), new Vector2Int(0, 1), new Vector2Int(1, 0), new Vector2Int(1, 1))
        .Build()
        );

     allSkills.Add(new Player_SkillData.skill_Builder()
       .SetName("자가 버프형")
       .SetDamage(0)
       .SetCrit(0)
       .SetType(Player_SkillType.type_Buff)
       .SetUseablePositions(new Vector2Int(0, 0), new Vector2Int(1, 0), new Vector2Int(0, 1), new Vector2Int(1, 1))
       .Build()
       );

     allSkills.Add(new Player_SkillData.skill_Builder()
      .SetName("필살기 전체공격형")
      .SetDamage(13)
      .SetCrit(20)
      .SetType(Player_SkillType.type_Melee)
      .SetUseablePositions(new Vector2Int(0, 1), new Vector2Int(1, 1))
      .SetTargetPositions(  // 광역기: 전체 공격 가능
         new Vector2Int(0, 2), new Vector2Int(0, 3), new Vector2Int(1, 2), new Vector2Int(1, 3))
      .Build()
      );

 

빌더 패턴의 장점은 변수가 아직 생성되지 않아도 테스트가 가능하다

(스킬 아이콘 변수를 아직 사용하지 않았다!)


- 테스트 결과

빌더 테스트 결과

플레이어에게 스킬이 등록된 것을 확인할 수 있었다!

- Delegate란?

메서드를 가리키는 포인터로 이해하면 쉽다.

다시말해 델리게이트특정 함수를(메서드를) 변수처럼 다룰 수 있게 해준다!

 

- 공격 또는 이동 UI 표시 예제

	// UI 표시 메서드
    public void ShowActionUI(System.Action attackCallback, System.Action moveCallback)
    {
        actionPanel.SetActive(true);
        onAttackSelected = attackCallback;
        onMoveSelected = moveCallback;
    }

	// ui 숨김 메서드
    public void HideActionUI()
    {
        actionPanel.SetActive(false);
    }

    public void OnAttackPressed()
    {
        onAttackSelected?.Invoke();
        HideActionUI();
    }

    public void OnMovePressed()
    {
        onMoveSelected?.Invoke();
        HideActionUI();
    }

 

게임 개발을 하면서 델리게이트를 사용해본 예시이다.

턴제 게임에서 캐릭터의 차례가 오면 이제 무엇을 할건지 UI가 표시된다.

 

두 개의 콜백 함수(attackCallback, moveCallback)를 파라미터로 받고,

이 파라미터들은 Action 델리게이트 타입이기 때문에, 반환값이 없는 메서드를 참조한다는 것!

// UI 호출 예시
ShowActionUI(Attack, Move);  // 공격과 이동의 메서드를 전달

 

공격할 것인지? 아님 이동할 것인지?를 호출하기에 플레이어가 공격 또는 이동을 택할 수 있는 것이다! 

 

델리게이트(Delegate)는 

메서드를 동적으로 변경할 수 있기 때문에, 게임에선 UI와 이벤트 시스템 등에서 자주 활용되는 방식이다!


- 테스트 결과

플레이어의 Turn차례가 오면 공격 또는 이동 ui가 표시된다!

턴제 게임에서 플레이어 캐릭터의 차례가 오면 크게 4가지 행동을 취할 수가 있다.

 

1. 공격 하기 

2. 자리 교체(이동하기)

3. 아이템 사용

4. 가만히 있기 (할 수 있는게 없을 때)

 

이렇게 행동 상태를 저장해서 마우스 클릭 시 선택된 행동을 실행할 수 있게 해보자!


1. 먼저 행동 상태를 저장하는 변수들을 선언한다.

    // 필요한 정보
    private enum PlayerAction { None, Attack, Move, Item }
    private PlayerAction currentAction = PlayerAction.None; // 기본상태는 일단 None;

2. 행동 상태에 따라 실행하는 코드 틀을 잡는다.

switch (currentAction)
{
    case PlayerAction.Attack:
        break;
    case PlayerAction.Move:
        break;
    case PlayerAction.Item:
        break;
    default:
        Debug.Log("행동을 선택해주세요");
        break;
}

2-2. 현재 만들고 있는 게임 프로젝트에 적용한다면 이렇게 적용시킬 수 있다.

// 마우스 클릭 -> 정보 넘기기
private void MouseClick()
{
    if (Input.GetMouseButtonDown(0) && selectArea != null)
    {
        switch (currentAction)
        {
            case PlayerAction.Attack:
                if (!isFriend) {
                    DirectPlayer_AttackToEnemy(selectArea);    // 적군 공격 명령
                    currentAction = PlayerAction.None;         // 행동 후 초기화
                }
                break;
            case PlayerAction.Move:
                if (isFriend)
                {
                    DirectPlayer_Move(selectArea);      // 아군 지형 이동 명령
                    currentAction = PlayerAction.None;  // 행동 후 초기화
                }
                break;
            case PlayerAction.Item:	// 아직 아이템은 미구현
                break;
            default:
                Debug.Log("행동을 선택해주세요");
                break;
        }
    }
}

 

마우스를 클릭했을 때 행동 상태에 따라서 PlayerController가 명령을 내릴 수 있다

 

저번 시간에 영역을 만들었으니 이번 시간에는

영역을 선택하고 아군영역이면 해당 영역으로 이동하는 기능을 만들어보자.

 

01 - 플레이어 컨트롤러와 플레이어 캐릭터 코드의 관계

 

 

플레이어 캐릭터가 직접 명령을 받고 즉시 기능을 수행하는 방법도 있지만

사용자에게 입력을 받는 클래스수행하는 클래스로 나누면 추후 작업하기 편하고

여러 개의 플레이어블 캐릭터가 있으면 관리하기도 편하기에 플레이어 컨트롤러 클래스는 필요하다!


02 - 준비하기: TestPlayer 스크립트, PlayerController 스크립트

 

우선 Create Empty를 생성하고 이름을 Player Controller 지정해

플레이어 컨트롤러 오브젝트를 만들고 PlayerController.cs 스크립트를 추가하자

 

Player는 에셋 폴더에 Prefab에 있는 Player를 하이어라키에 넣고 플레이어 스크립트를 넣자

(이때 Tag값도 수정해두자)


02 - 1.  Player.cs

public class TestPlayer : MonoBehaviour
{
    [System.NonSerialized] public bool isMove = false;
    private float walkSpeed = 50f;
    [System.NonSerialized] public Vector3 targetPos;       // 이동할 위치

    // Update is called once per frame
    void Update()
    {
        if (isMove)
        {
            Move();
        }
    }

    public void MovePosition(Vector3 position)
    {
        this.targetPos = position;
        isMove = true;
    }

    private void Move()
    {
        transform.position = Vector3.MoveTowards(transform.position, targetPos, walkSpeed * Time.deltaTime);
        if (Vector3.Distance(transform.position, targetPos) < 0.01f)
        {
            isMove = false;
        }
    }

}

 

    플레이어 캐릭터 코드는 이동할 위치 정보를 받는다.

  1.  MovePostion() : targetPos를  position 값으로 변경하고 isMovetrue로 변경함.
  2.  Move() : Update 함수에 호출되며 isMove true일때만 targetPos로 이동
                    이동이 완료되면 isMove 값을 false로 전환.

 

02 - 2.  PlayerController.cs

public class PlayerController : MonoBehaviour
{

    [SerializeField] private TestPlayer player;
    [SerializeField] private AreaManager areaManager;

    // 필요한 정보
    private Ray ray;
    private RaycastHit hit;
    private Area selectArea;        // 선택한 영역
    private bool isFriend = false;   // 선택한 영역이 아군 영역?


    // Update is called once per frame
    void Update()
    {
        ray = Camera.main.ScreenPointToRay(Input.mousePosition);
        InfoUpdate();

        MouseClick();
    }

    private void InfoUpdate()
    {
        if (Physics.Raycast(ray, out hit))
        {
            selectArea = hit.collider.GetComponent<Area>();

            if (selectArea != null)
            {
                isFriend = CheckArea(selectArea);
            }
            else
            {
                isFriend = false;
            }
        }
    }

    // 마우스로 클릭
    private void MouseClick()
    {
        if (Input.GetMouseButtonDown(0) && selectArea != null)
        {
            if (isFriend)
            {
                DirectPlayer_Move(selectArea);
            }
            else
            {
                Debug.Log($"이곳은 적군의 영역입니다!\n선택한 좌표: {selectArea.areaIndex}");
            }
        }
    }

    // 태그 값으로 영역 확인
    private bool CheckArea(Area selectArea)
    {
        return selectArea.CompareTag("Area_Friend");
    }

    // 캐릭터에게 이동 명령
    private void DirectPlayer_Move(Area selectArea)
    {
        Vector3 areaPos = areaManager.GetAreaPosition(selectArea);
        Debug.Log($"선택한 좌표로 이동!: {selectArea.areaIndex}");
        player.MovePosition(new Vector3(areaPos.x, player.transform.position.y, areaPos.z));
    }

}

 

이 스크립트의 역할을 간단히 이해하자면 마우스가 좌클릭하면 선택한 부분에 있는

영역 정보를 가져와서 아군 영역이면 해당 위치로 이동하라고 명령을 내린다.

 

명령을 내리는 부분에서 선택한 Area가 3D 게임상에서 위치한 곳이 어디에 있는지

AreaManager에게 요청하는 내용이 있다! 

 

02 - 3.  AreaManager.cs 에서 GetAreaPostion( ) 메서드 추가

    public Vector3 GetAreaPosition(Area area) 
    {
        return area.transform.position;
    }

 

AreaManger.cs 에 해당 메서드를 추가하자!

 


3. Unity 에서 값 조정

 

저번에 만들었던 영역 오브젝트들을 Tag 값들을 설정해주자!

아군 영역은 Area_Friend, 적군 영역은 Area_Enemy로 Tag를 추가해서 값을 지정하면 된다!

 

4. 테스트 결과

animator 까지 적용한 내용이므로 블로그 코드만으로는 가만히 움직이는게 정상이다

 

이런 식으로 플레이어 캐릭터가 선택한 영역에 움직이고 적군 영역이면 움직이지 않으면 끝!

 

 

 

 

레퍼런스가 다키스트 던전이기에 아군측 진형 최대 4명, 적군 측 진형에 최대 4마리가 있다.

 

이 말은 진형의 크기가 4칸+4칸 = 즉 8칸으로 존재하고 각 영역을 아군진형, 적군 진형으로 나누면 된다.

 

수업이나 강의든 어디서 많이 본 내용이 떠오를 것이다 ㅋㅋㅋ

 

각 칸(Area)은 자신이 몇 번째 방인지 정보(areaIndex)를 가지고 있고

이 정보를 토대로 진형이 1차원 배열 AreaManger에게 관리되고 있으면 어떨까?

 

1. 하이어라키 > 3D Object > Cube로 각 영역 만들기

진형 아군 진형 적군 진형
방 이름 Area1 Area2 Area3 Area4 Area5 Area6 Area7 Area8
인덱스 0 1 2 3 4 5 6 7
위치(x좌표) -17.5 -12.5 -7.5 -2.5 2.5 7.5 12.5 17.5

 

이렇게 만들어져 있으면 편하다.

 

Create Empty를 3개 추가해서 모든 영역 < 아군 영역 전체 < 아군 영역 1, 2, 3, 4

                                                                  < 적군 영역 전체 < 적군 영역 1, 2, 3, 4 

이런 식으로 하이어라키에 배치해 두자

 

추후 공격같은 기능을 구현할때 전체 영역에 영향을 주는 기술이 있을 수 있기 위함으로 이렇게 만든다!

 


 

2. 영역 스크립트 (Area.cs), 영역 관리자 스크립트 (AreaManager.cs) 생성 

추후 관리하기 편하게 스크립트 별로 폴더를 나누면 편하다

Area.cs

public interface IArea
{
    int GetPosition();
}

public class Area : MonoBehaviour, IArea
{
    // 나는 areaIndex 번째 방이야!
    public int areaIndex;

    // 저장된 좌표 반환
    public int GetPosition()
    {
        return areaIndex;
    }

}

 

영역 클래스에는 자신이 몇 번째 방인지를 저장하고 저장된 좌표를 반환하는 메서드를 만든다

 

AreaManager.cs

public class AreaManager : MonoBehaviour
{

    [SerializeField] private Transform areaFriend;  // 아군 영역
    [SerializeField] private Transform areaEnemy;   // 적군 영역

    private IArea[] areaGrid; // 1차원 배열로 관리

    private void Awake()
    {
        InitializeArea();
    }

    private void InitializeArea()
    {
        // 0~3: 아군 진형, 4~7: 적군진형
        areaGrid = new Area[8];

        SetArea(areaFriend);
        SetArea(areaEnemy);
    }

    private void SetArea(Transform parent)
    {
        foreach (Transform child in parent)
        {
            Area area = child.GetComponent<Area>();
            if (area != null)
            {
                int position = area.GetPosition();

                // 1차원 배열에 저장
                areaGrid[position] = area;
                Debug.Log($"영역: {child.name}, 좌표: [{position}]");
            }
        }
    }
}

 

영역 관리자 클래스는 영역을 배열로 관리해서 각 영역이 몇 번째 배열에 속해있는지를 지정한다!

 


3. Unity 창에서 조정

 

1. 영역 오브젝트에 Area.cs를 Add Component로 추가한다.

2. 각 영역 오브젝트의 Inspector 창에서 areaIndex를 지정한다.

3. 하이어라키에 Create Empty로 AreaManager 오브젝트로 만들고 AreaManager.cs도 추가한다

4. AreaManager의 Inspector 창에서 아군영역과 적군영역의 부모 오브젝트를 넣는다.

 

 


 

4. 실행 결과

 

유니티를 실행했을 때 이런식으로 영역 정보가 잘 표시된다면 끝난다!

 

 

이 방법의 한계점?

해당 방식으로는 고정된 크기의 배열을 담당하기에

최대 몬스터 수, 최대 아군 캐릭터 수가 정해져 있어야만 적용되는 방식이다.

 

맵 마다 전투 영역 크기가 다르게 설계한다면

areaIndex를 어떤 기준점을 잡아서 자동으로 index가 결정하고

areaGrid의 크기도 Area.cs를 가지고 있는 오브젝트의 갯수로 지정되도록 설계할 수 있을거 같다.

 

 

다음 시간에는 Player 코드를 통해서 아군 영역 안에서만 움직이도록 만들어보자! 

 

게임 개발자가 목표이기에 배우는 것도 좋지만

복습과 기록이 더 중요하다고 생각하기 때문에 앞으로 개발일지를 작성하기로 했다!

 

턴제 게임을 목표로 잡았냐 하면 프로그래밍에 있어 '코드 흐름이 가장 중요하다'고 생각하고

게임 진행을 순차적으로, 시각적으로 보여주는 것이 턴제 게임이라 때문에 결정했다.


1. 레퍼런스 게임 - Darkest Dungeon

 

수많은 턴제 게임 중에서 Darkest Dungeon2레퍼런스로 잡았다.

 

해당 게임을 레퍼런스로 잡고 구현할 내용

1. 매 라운드마다 캐릭터의 턴 순서가 결정되고

2. 턴 순서가 온 게임 유닛은 턴을 진행한다.

3. 턴 대상이 아군 측 유닛이면 플레이어가 스킬을 선택해 행동하며,

    적군 측 유닛이면 몬스터 AI가 판단해서 행동을 진행한다.

4. 양측 진형 중 게임 유닛이 1명이라도 없으면 라운드가 종료(게임이 종료)된다.

 

그리고 해당 게임의 경우 플레이어의 공격력을 기준으로 피해량이 결정되는게 아니라

스킬 자체에 피해량이 결정되어 있어 좋은 아이디어라고 생각한다 ㅎㅎㅎ (밸런스 잡기 편할듯?)

 


2. 사용할 에셋 - Goldmetal:  Quarter View Action Assets

 

그리고 게임 에셋은 골든 메탈님의 Quarter View Action Assets 을 사용했다.

많은 유니티 에셋이 있지만 게임 개발 입문용으로는 이 에셋이 최고인거 같다.

 


3. 생성할 프로젝트: Unity: 3D 프로젝트 

 


4. 바닥 만들기 (3D Object > Plane), 영역을 시각적으로!

 

바닥은 다른 블로그나 강의영상을 보면 만들기 쉽다.

Hierachy 우클릭 > 3D Object > Plane 생성하고 Inspector 창에서 Scale을 정해주면 끝난다.

추후 영역 이라는 게임 오브젝트를 만들기 위해 구별하기 쉽도록 타일링까지 넣어주면 완벽하다. 

 

 

에셋에서 텍스처 < _Tile 적용. 타일링 크기는 10x10 정도 해줬다.

 


5. 원하는 스타일대로 정리하기

 

하다보니 바닥 스케일이 큰거 같으면 줄이거나

카메라 위치도 조정해두고, 유닛도 한번 세워봐서 게임이 어떻게 보이나 테스트도 해보면서 적용하면 된다!

 

 

FHD(1920x1080) 기준 이정도로 보여지면 괜찮을 거 같다.

 

이정도까지 준비하면 게임을 개발할 준비가 거의 완료되었다!

이제 영역을 만들고 아군 진형과 적군 진형을 나누면 될 거 같다!

+ Recent posts