[Unity] 7. 추상클래스

치치·2025년 2월 25일
0

Unity

목록 보기
9/27
post-thumbnail

지난 수업에서 객체를 동적 생성하고, 생성할 위치를 정하는 Instantiate에 대해서 배웠다

오늘 배운 것

  • 게임 오브젝트 칼, 권총, 라이플이 존재하고, 게임을 실행했을때 칼 오브젝트가 활성화되면 다른 오브젝트들은 비활성화된다
  • 셋 중 하나의 오브젝트만 활성화되어 무기 장착을 바꾸는 듯하게 구현하였다


전체 코드

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class WeaponManager : MonoBehaviour
{
    [SerializeField] int count;
    [SerializeField] GameObject[] weapons;
    [SerializeField] List<GameObject> weaponList;

    [SerializeField] bool check;

    GameObject clone;

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            Equip();

            Shift();
        }

        if(clone != null) 
        {
            if(Input.GetMouseButtonDown(0))
            {
                clone.GetComponent<Weapon>().Attack();
            }  
        }
    }

    public void Equip()
    {
        if (check == true)
        {
            return;
        }

        // 배열의 길이 <= 현재 count
        // 카운트가 배열의 끝에 도달했다는 의미 (다 체크했다면 반환)
        if (weapons.Length <= count)
        {
            check = true;
            return;
        }

        // 생성하고 count++
        clone = Instantiate(weapons[count++]);

        if(weaponList.Count >= 1)
        {
            // 마지막으로 추가된(이전) 무기를 비활성화
            weaponList[weaponList.Count-1].gameObject.SetActive(false);
        }
        // 새로 생성된 무기 활성화
        clone.gameObject.SetActive(true);

        weaponList.Add(clone);
    }

    public void Shift()
    {
        // 모든 오브젝트들이 생성되었다면
        if (check)
        {
            // 모든 오브젝트들 비활성화
            for(int i = 0; i < weaponList.Count; i++) 
            {
                weaponList[i].gameObject.SetActive(false);
            }

            // 3번째 오브젝트가 활성화상태니까 비활성화 하고, 1번째 오브젝트가 활성화 되야함
            weaponList[count++ % weaponList.Count].gameObject.SetActive(true);

        }
    }


}


구현 순서

  1. 스페이스바를 누르면 게임 오브젝트가 하나씩 번갈아 가면서 생성된다.
  2. 현재 오브젝트가 활성화 상태라면, 이전 오브젝트는 비활성화 상태이다.
  3. 3개의 오브젝트를 다 확인했을경우, 3번째 오브젝트가 활성화 상태이고 제일 처음 오브젝트가 비활성화 되야한다.

  • count : 현재 무기를 활성화 한 인덱스

  • [] weapons : 동적 생성할 게임 오브젝트를 담은 배열

  • List <> weaponList : 생성한 오브젝트를 담아둘 배열

  • check : 배열을 다 확인했는지 판단하는 bool 변수


  • Equip() : 게임 오브젝트를 생성하는 함수

  • Shift() : 번갈아가면서 게임 오브젝트를 활성화하는 함수

  • GetMouseButtonDown(0) : 마우스 왼쪽 버튼 클릭 시 호출되는 함수 (마우스 입력과 키 입력은 외에도 다양하게 있다)

  • 마우스의 왼쪽 키 입력을 받으면, 동적으로 생성한 게임 오브젝트를 참조 변수를 통해
    Weapon 컴포넌트를 가져와서 Attack()함수를 호출한다


📌 주의점

동적으로 생성된 게임 오브젝트 내부의 스크립트를 사용하고 싶어서 컴포넌트를 가져오려면?

  • Instantiate 함수는 복제된 객체를 반환
    -> clone은 이 복제된 객체를 저장한다!!

  • ex) 현재 cloneGameObject타입의 참조변수이다
    -> weapons배열 내의 객체 타입이 게임 오브젝트가 아닌 다른 타입이라면, 참조변수의 타입도 바껴야한다

즉, 참조 변수의 타입은 복제하려는 객체의 타입에 따라 결정된다



Equip()함수

  • checktrue라면 리턴하고 Shift()함수로 이동

  • weapons 배열의 길이가 현재 count와 같거나 작으면 checktrue로 갱신하고 리턴한다
    -> 배열의 요소를 모두 확인했다는 뜻
    -> 리턴하고 Shift()함수를 실행하여 게임 오브젝트 활성화 갱신

  • 동적으로 배열 안의 게임 오브젝트를 생성

  • 처음 count가 0 일때는 동적 생성하고, 리스트에 넣기 전이기 때문에 조건문을 건너뛰고 게임 오브젝트를 활성화한다

순서 :
1. 동적생성 - count++ - 게임 오브젝트 활성화 - 리스트에 추가
2. 동적생성 - count++ - 만약 리스트에 1개 이상 들어있다면, 바로 이전 게임 오브젝트를 비활성화 - 현재 게임 오브젝트 활성화 - 리스트에 추가 (반복)


Shift()함수

  • 모든 배열을 다 확인하여 checktrue인 상태일때(count값이 배열과 동일해졌을때) , 모든 게임 오브젝트를 비활성화

  • weaponList[count++ % weaponList.Count].gameObject.SetActive(true);

-> [count % 3] 게임 오브젝트를 활성화 (값은 무조건 0, 1, 2 값만 나올 수 있다)


추상클래스



부모 클래스 - Weapon

  • 부모 추상 클래스는 게임 오브젝트에 직접적으로 스크립트를 할당해줄 수 없다
    -> 정의만 해두는 곳

  • 변수 directionrotation을 선언해두었는데, 모든 자식들에게서 사용할 수 있다
    (외부에서 미리 자식들의 값을 지정한다)

  • 추상 클래스 내부의 추상함수는 꼭 존재해야한다. 외의 다른 일반 함수도 정의할 수 있다
    -> 접근지정자 abstract 자료형 함수이름;

  • Start()함수가 public이거나 protected이고, 자식이 재정의 된 게 아니라면 부모가 정의 해둔 값으로 알아서 호출된다

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public abstract class Weapon : MonoBehaviour
{
    // 외부에서 미리 값을 지정 (자식의 값들)
    [SerializeField] protected Vector3 direction;
    [SerializeField] protected Vector3 rotation;
    
    // 추상함수로 정의만 해두기
    public abstract void Attack();


    // public이나 protected로 하면 자식이 재정의 된 게 아니면 부모꺼로 알아서 호출된다
    public void Start()
    {
        transform.position = direction;
        transform.rotation = Quaternion.Euler(rotation);
    }
}

자식 클래스 Knife : Weapon

  • 부모에 선언해둔 추상함수를 자식에게서 재정의 한다 (Attack())
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Knife : Weapon
{
    public override void Attack()
    {
        Debug.Log("칼 찌르기");
    }

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

에디터 내의 자식클래스

  • 부모에서 정의한 변수들이 자식 클래스에게 동일하게 적용된다
  • [SerializeField]를 사용하여 인스펙터창에서 값을 지정할 수 있다
  • 지정한 값이 해당 게임 오브젝트의 위치값,회전값으로 지정되어진다



마우스 입력받기

내가 사용한 코드1

  • 동적으로 생성한 게임 오브젝트를 참조하고 있는 clone 변수를 통해, 생성된 게임 오브젝트의 Weapon 클래스를 참조하여 Attack()함수를 호출한다

내가 사용한 코드2

  • Weapon 클래스 타입의 currentWeapon 변수를 만들고, 생성된 게임 오브젝트 내부의 Weapon 클래스 타입의 컴포넌트를 가져온다
  • 가져오려는 컴포넌트 타입과 참조변수의 타입은 같아야한다!=
  • 참조변수 currentWeapon을 통해 Attack()함수 호출

즉, 사용자가 정의한 클래스도 게임 오브젝트에 스크립트로 넣어주면 하나의 컴포넌트라는 것


강사님 코드

  • 강사님은 리스트 내부에 넣어둔 게임 오브젝트의 컴포넌트를 참조하였다

  • 참조변수가 Null을 가리키고 있다면 아직 리스트에 게임 오브젝트가 들어오지 않았다는 뜻
    -> 즉, 생성된 게 아무것도 없다면 Attack()함수는 호출되지 않는다


마우스 버튼 입력

결과물

profile
뉴비 개발자

0개의 댓글