지난 수업에서 객체를 동적 생성하고, 생성할 위치를 정하는
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);
}
}
}
count : 현재 무기를 활성화 한 인덱스
[] weapons : 동적 생성할 게임 오브젝트를 담은 배열
List <> weaponList : 생성한 오브젝트를 담아둘 배열
check : 배열을 다 확인했는지 판단하는 bool 변수

Equip() : 게임 오브젝트를 생성하는 함수
Shift() : 번갈아가면서 게임 오브젝트를 활성화하는 함수
GetMouseButtonDown(0) : 마우스 왼쪽 버튼 클릭 시 호출되는 함수 (마우스 입력과 키 입력은 외에도 다양하게 있다)
마우스의 왼쪽 키 입력을 받으면, 동적으로 생성한 게임 오브젝트를 참조 변수를 통해
Weapon 컴포넌트를 가져와서 Attack()함수를 호출한다

동적으로 생성된 게임 오브젝트 내부의 스크립트를 사용하고 싶어서 컴포넌트를 가져오려면?
Instantiate 함수는 복제된 객체를 반환
-> clone은 이 복제된 객체를 저장한다!!
ex) 현재 clone은 GameObject타입의 참조변수이다
-> weapons배열 내의 객체 타입이 게임 오브젝트가 아닌 다른 타입이라면, 참조변수의 타입도 바껴야한다

즉, 참조 변수의 타입은 복제하려는 객체의 타입에 따라 결정된다
Equip()함수check가 true라면 리턴하고 Shift()함수로 이동
weapons 배열의 길이가 현재 count와 같거나 작으면 check를 true로 갱신하고 리턴한다
-> 배열의 요소를 모두 확인했다는 뜻
-> 리턴하고 Shift()함수를 실행하여 게임 오브젝트 활성화 갱신
동적으로 배열 안의 게임 오브젝트를 생성
처음 count가 0 일때는 동적 생성하고, 리스트에 넣기 전이기 때문에 조건문을 건너뛰고 게임 오브젝트를 활성화한다
순서 :
1. 동적생성 - count++ - 게임 오브젝트 활성화 - 리스트에 추가
2. 동적생성 - count++ - 만약 리스트에 1개 이상 들어있다면, 바로 이전 게임 오브젝트를 비활성화 - 현재 게임 오브젝트 활성화 - 리스트에 추가 (반복)

Shift()함수모든 배열을 다 확인하여 check가 true인 상태일때(count값이 배열과 동일해졌을때) , 모든 게임 오브젝트를 비활성화
weaponList[count++ % weaponList.Count].gameObject.SetActive(true);
-> [count % 3] 게임 오브젝트를 활성화 (값은 무조건 0, 1, 2 값만 나올 수 있다)

부모 클래스에서 정의만 해두고 자식 클래스에서 재정의 해서 사용할 수 있는 클래스
참고 : https://velog.io/@yangju058/C-12.-추상화-Abstract
타워디펜스게임 : https://velog.io/@yangju058/타워디펜스게임-6.-유닛-클래스
위에서 생성한 무기 종류 3가지를 하나의 추상클래스를 이용하여 공통된 함수들과 변수들을 묶을 것이다
부모 추상 클래스는 게임 오브젝트에 직접적으로 스크립트를 할당해줄 수 없다
-> 정의만 해두는 곳
변수 direction과 rotation을 선언해두었는데, 모든 자식들에게서 사용할 수 있다
(외부에서 미리 자식들의 값을 지정한다)
추상 클래스 내부의 추상함수는 꼭 존재해야한다. 외의 다른 일반 함수도 정의할 수 있다
-> 접근지정자 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 : WeaponAttack())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]를 사용하여 인스펙터창에서 값을 지정할 수 있다
clone 변수를 통해, 생성된 게임 오브젝트의 Weapon 클래스를 참조하여 Attack()함수를 호출한다
Weapon 클래스 타입의 currentWeapon 변수를 만들고, 생성된 게임 오브젝트 내부의 Weapon 클래스 타입의 컴포넌트를 가져온다currentWeapon을 통해 Attack()함수 호출
즉, 사용자가 정의한 클래스도 게임 오브젝트에 스크립트로 넣어주면 하나의 컴포넌트라는 것


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

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


