캐릭터 구성하기 - 캐릭터 스탯 관리하기

Published: Jul 30, 2021 by BeatChoi

선수포스팅 :: Pre-requisite

네비게이션 메시(/br)

캐릭터 스텟 관리 :: Character Stats

게임에는 수많은 캐릭터들이 존재하고 각 캐릭터들은 고유의 스텟을 가지고 있습니다. 흔하게는 공격력, 방어력, 체력 등이 있을 수 있을 것이고 더 나아가서는 리스폰 시간이나 이동 속도 등 여러가지 항목들을 가지고 있을 수 있습니다.

이번시간에는 Stat 클래스를 직접 작성하여 캐릭터의 공격 및 죽음을 구현해 보도록 하겠습니다.

유니티3D 에디터에서

스크립트 생성

이번 포스팅에서는 총 4개의 스크립트를 작성해야 합니다.

먼저 Stat.cs 스크립트를 생성하고 다음과 같이 작성합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[System.Serializable]
public class Stat
{
    [SerializeField]
    private int baseStat;

    public int GetStat()
    {
        return baseStat;
    }
}
  • Stat 클래스는 Monobehaviour를 상속받지 않습니다.
  • 클래스 자체를 System.Serializable 해줌으로서 인스펙터 창에서 해당 값이 나타날 수 있도록 합니다.
  • 다른 스크립트에서 Stat 클래스로 변수를 선언할 수 있게 됩니다.
  • GetStat()함수를 통해 타 해당 값에 접근할 수 있습니다.

그 다음 CharStats.cs 스크립트를 생성하고 다음과 같이 작성합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CharStats : MonoBehaviour
{
    public int maxHealth = 100;
    public int curHealth { get; private set; }

    public Stat damage;


    private void Awake()
    {
        curHealth = maxHealth;
    }

    public void TakeDamage(int damage)
    {
        curHealth -= damage;
    }
}

  • CharacterStats클래스는 게임의 캐릭터들이 공통으로 가질 수 있는 스탯을 정리합니다.
  • 각 캐릭터들의 최대체력, 현재체력, 공격력을 선언합니다.
  • currentHealth 변수는 Get, Private Set 으로 선언되었으며 이는 다른 클래스에서 이 변수에 접근할 수 있지만 값 변경은 현재 클래스에서만 가능하다는 뜻입니다.
  • TakeDamage 함수는 자신의 현재체력에서 인자 데미지를 빼주는 역할을 합니다. 중요한것은 자신의 현재체력에서 상대방의 공격력만큼을 뺀다는 것입니다.
  • 이는 다음 스크립트에서 설명드리겠습니다.

그 다음 CharCombat.cs 스크립트를 생성하고 다음과 같이 작성합니다.

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
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[RequireComponent(typeof(CharStats))]
public class CharCombat : MonoBehaviour
{
    public float atkSpd = 1f;
    private float atkCdw = 0f;

    CharStats myStats;

    private void Start()
    {
        myStats = GetComponent<CharStats>();
    }

    private void Update()
    {
        atkCdw -= Time.deltaTime;
    }

    public void Attack(CharStats targetStats)
    {
        if (atkCdw <= 0)
        {
            targetStats.TakeDamage(myStats.damage.GetStat());
            atkCdw = 1f / atkSpd;
        }
    }
}
  • CharCombat.cs 스크립트는 캐릭터의 전투에 관련된 행동들이 정리되어 있습니다.
  • 공격 속도 및 공격딜레이 변수를 선언하고
  • Attack 함수에서 인자로 받은 타겟의 CharStat 클래스의 TakeDamage함수를 호출시켜 상대방의 체력에 자신의 공격력을 깎습니다.
  • targetStats = 상대방의 스탯 클래스
  • myStats = 나의 스탯 클래스

마지막으로 Monster.cs스크립트를 생성하여 해당 캐릭터(몬스터)의 행동 구조를 작성합니다.
이전 포스팅중 하나인 Navigation Mesh 포스팅을 이하하고 오셔야 합니다.

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
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;

public class Monster : MonoBehaviour
{
    CharCombat combat;
    public Transform target;
    NavMeshAgent nav;

    // Start is called before the first frame update
    void Start()
    {
        nav = GetComponent<NavMeshAgent>();
        combat = GetComponent<CharCombat>();
    }

    // Update is called once per frame
    void Update()
    {
        MonsterAI();
    }


    void MonsterAI()
    {
        float distance = Vector3.Distance(target.transform.position, transform.position);
        if (distance > 1.3f)
        {
            nav.SetDestination(target.transform.position);
        }
        else if (distance <= 1.3f)
        {
            CharStats targetStats = target.GetComponent<CharStats>();
            if (targetStats != null)
            {
                combat.Attack(targetStats);//Do melee attack when near target
            }
        }
        else
        {
            nav.SetDestination(transform.position);
        }
    }
}
  • CharCombat형의 combat 변수를 선언하고 해당 변수에 자신의 컴포넌트를 추가합니다.
  • MonsterAI 함수를 작성합니다. 해당 함수는
    • 상다방과의 거리가 1.3 이하가 될때까지 네비게이션 메시의 SetDestination 함수를 통해 서로를 찾아 이동합니다.
    • 거리가 1.3 이하가 되면 자신의 Attack 함수를 호출합니다. 해당 함수의 인자로는 상대방의 CharStat 클래스에 접근한 targetStats 변수를 넣습니다.
  • 상대방 캐릭터가 없다면 제자리에 머무릅니다.

프로젝트 생성 및 세팅

유니티 프로젝트를 생성하고 유니티 기본도형을 활용하여 기본 스테이지를 생성해 봅시다.
캐릭터가 걸어다닐 수 있는 길은 Plane오브젝트로, 캐릭터 오브젝트는 Capsule로 간단하게 생성합니다.
잘 보일 수 있도록 색상도 넣어주고 다음과 같이 씬을 세팅합니다.


<01. 스테이지 구성>

두 캐릭터 오브젝트인 Capsule 모두에 우리가 작성한 CharStats, CharCombat, Monster 스크립트를 인스턴스화 시켜줍니다.
그리고 Nav Mesh Agent 컴포넌트 역시 추가해줍니다.
Capsule의 스크립트 컴포넌트의 변수들에 다음과 같이 값을 넣어줍니다.
CharStatsDamage 항목은 10, (상대방의 데미지는 20으로 설정하여 차이를 주었습니다.)
CharCombatAtkSpd 항목은 1, MonsterTarget 항목은 상대방 Capsule 오브젝트를 넣어주시면 되겠습니다.
아래 이미지는 Capsule 오브젝트의 인스펙터 창입니다.


<02. 캐릭터 생성>

테스트 :: Test

상단 플레이 버튼을 눌러 두 Capsule 오브젝트들의 행동을 살펴봅니다.
다음과 같이 서로 접근을 하게되고 일정 거리 내로 서로가 들어가면 공격을 시작합니다.
씬 창이나 게임 창에서는 결과를 확인할 수 없기 때문에 인스펙터 창을 Debug모드로 변경하여 확인합니다.
(인스펙터 창 우측 상단 땡땡땡 버튼 클릭, Debug 선택)


<03. 캐릭터의 전투>

다음은 인스펙터 창 상에서의 값 변화 입니다.
curHealth의 값이 상대방의 공격력 만큼 줄어드는 것을 확인할 수 있습니다.
공격속도 및 딜레이 값도 변경을 하여 값의 변경을 확인해봅시다.


<04. 캐릭터의 스탯 변화>

정리

이번 포스팅에서 진행한 부분을 정리해 보겠습니다.
먼저 Stat 이라는 클래스는 우리가 활용할 자료형을 직접 만들었다고 생각하시면 됩니다.
Stat 이라는 자료형을 활용하여 공격력이나 방어력 체력 등 숫자 수치 상 표현할 수 있는 값을 표현합니다.

CharStat 클래스를 통해 캐릭터, 유닛 등이 공통으로 가지고 있는 값, 행위 등을 설정합니다.
예를 들면 스타크래프트의 모든 유닛들은 공통적으로 공격력, 방어력, 체력 등의 값을 가지고 있고 이동, 정지 등의 움직임을 가지고 있습니다.
이번 포스팅에서는 체력과 공격력을 설정해 주었고 상대방의 공격력에 자신의 체력을 깎는 함수를 선언하였습니다.

CharCombat 클래스는 캐릭터의 전투에 관련된 함수를 작성한 클래스입니다.
Attack이라는 함수를 통해 어느정도의 빈도로 공격을 하고 데미지를 줄지 결정합니다.
활용방법으로는 추후 CharMove라는 클래스를 만들어 이동에 관련된 클래스를 작성할 수도 있겠습니다.

마지막으로는 Monster 스크립트 입니다.
Monster 스크립트는 각 캐릭터의 특성에 맞는 행위를 작성하기 위한 스크립트입니다.
현 포스팅에서는 동일한 Monster 스크립트를 두 캐릭터에 활용하였지만 나중에는 캐릭터 마다 하나씩 만들어 설정해야 할 수도 있습니다.
예를들면 스타크래프트의 질럿과 드라군은 공격의 형식이 다르기 때문에 다른 스크립트로 표현하는 것이 편할 수 있습니다.

본 포스팅에서 캐릭터의 기본적인 구조를 짜고 서로 공격을 하도록 해보았습니다.
다음 포스팅에서는 서로 공격을 하고 체력을 0으로 만들었을 때 사라지게 하는 죽음을 한번 구현해 보도록 하겠습니다.

Latest Posts

콘텐츠 개발 기법 - URP 환경에서 Bloom 및 Volume 포함한 화면 캡처
콘텐츠 개발 기법 - URP 환경에서 Bloom 및 Volume 포함한 화면 캡처

화면 캡처 :: Screen Capture

일반적으로 URP환경에서 Bloom 등의 Volume 이펙트들이 함께 캡처되지 않는 현상이 일어납니다.
두가지 원인이 있는데 첫번째는 저장할 Texture 및 Texture2D의 크기가 작아 모든 텍스쳐를 저장할 수 없는 경우와
두번째는 Linear color space의 픽셀을 텍스쳐에 저장 할 수 없는 경우가 있습니다.
이번 포스팅에서는 URP 환경에서 해당 이펙트들을 함께 캡쳐하는 방법을 알아봅니다.

콘텐츠 개발 기법 - UI 안나오게 화면 캡처(Screen Capture without UI)
콘텐츠 개발 기법 - UI 안나오게 화면 캡처(Screen Capture without UI)

화면 캡처 :: Screen Capture

UI 없이 화면을 캡쳐하는 방법을 알아봅니다.
해당 방법을 통해 사진 앱에서 사진을 찍거나 게임 내에서 UI를 제거한 스크린샷을 구현할 수 있습니다.

유니티3D 에디터에서

스크립트 작성

LightshipAR SDK 활용하기 - LightshipAR VPS의 활용
LightshipAR SDK 활용하기 - LightshipAR VPS의 활용

LightshipAR SDK

이번 강좌에서는 Visual Positioning System (VPS) 기능을 활용하여 콘텐츠를 개발해 봅니다.
VPS는 GPS정보와 타겟 매핑 정보를 정합하여 해당 타겟을 인식하는 기능입니다.
따라서 객체나 환경의 변화 및 이동이 잦지 않은 타겟이 스캔 대상이 됩니다.
주로 동상, 건물 입구, 가로등 등 위치의 변경이 없는 타겟들이 좋은 타겟입니다.

해당 타겟들을 인식시키고 주변에 가상의 오브젝트들을 배치하고 해당 위치를 저장하는 방법을 알아봅니다.

공간 매핑

Niantic Wayfarer

공간을 인식시키기 위해서 먼저 공간을 매핑해야 합니다.
이 과정은 IOS 어플리케이션으로 진행을 할 수 있는데 아래 링크에서

  1. Testflight
  2. Wayfarer

어플리케이션을 다운로드 및 설치합니다.

링크 : https://testflight.apple.com/join/VXu1F2jf