게임은 그냥 강의를 보면서 만들면 완성된다. 그런 학습은 남는게 없다. 따라서 궁금했던 점이나 혼자 이해한 내용을 바탕으로 정리해보았다.
Object를 재사용 가능한 형태로 만든 것이 prefab.
간단하게 그냥 드래그해서 유니티 화면 하단의 Assets
에 끌어다 넣으면 prefab으로 변한다.
이 prefab은 보통 스크립트를 이용해 재사용하게된다. 추후 GameObject목차 참고 바람.
Inspector
내부 다양한 설정들을 가진 채 재사용이 되니 같은 Object를 반복해서 사용해야 한다면, Prefab만한 기능이 없다.
또한 런타임 시점에 오브젝트를 생성할때도 prefab으로 생성한다.
GameObject클래스 : unity에서 게임 오브젝트를 끌어다 연결할 수 있음
Instantiate
: 게임오브젝트를 코드로 생성함
Time.time
게임 시작부터 현재까지 흐른 시간
Destroy
: 오브젝트 인스턴스를 사라지게 할 수 있음
gameObject
: 현재 인스턴스
FindAnyObjectByType
:강의에서 사용한 Find
메서드 대체
적을 만들때, 강의에서 코루틴을 사용해서 몬스터(재활용품)를 스폰했다.
코루틴은 상호 연계 프로그램, 즉 일시정지 할 수 있는 함수를 의미한다.
좌측은 일반적인 서브루틴(함수), 우측은 코루틴이다.
js에서는 generator
와 yield
를 이용해서 코루틴을 만들 수 있었다.
function* coroutine() {
console.log("코루틴 시작");
const input1 = yield "Step 1 완료"; // 첫 번째 중단점
console.log(`받은 입력: ${input1}`);
const input2 = yield "Step 2 완료"; // 두 번째 중단점
console.log(`받은 입력: ${input2}`);
const input3 = yield "Step 3 완료"; // 세 번째 중단점
console.log(`받은 입력: ${input3}`);
return "코루틴 종료";
}
// 코루틴 실행
const co = coroutine();
console.log(co.next().value); // 코루틴 시작 -> Step 1 완료
console.log(co.next("첫 번째 입력").value); // 받은 입력: 첫 번째 입력 -> Step 2 완료
console.log(co.next("두 번째 입력").value); // 받은 입력: 두 번째 입력 -> Step 3 완료
console.log(co.next("세 번째 입력").value); // 받은 입력: 세 번째 입력 -> 코루틴 종료
위처럼 함수간 Parallelism(병렬)이 아닌 concurrency(동시성)으로 진행하는 것이 코루틴이다.
매 프레임간 Udpate()
메서드를 호출하는 대신, 코루틴을 이용하여 적들을 스폰하였다.
당연하게도 매 프레임간 생성할 필요가 없기에 최적화된 코루틴을 이용한 것이다.
다만 아주 짧은시간마다 지속되어 실행되야 하는 로직은 Update()
에서 처리하는게 더 최적화가 잘 되어있으니 참고해야겠다.
js에서는 생성자함수나 클래스에서 사용 시 생성될 객체를 가리켰다. C#에서도 생성될 인스턴스를 가리키는 키워드다.
Js의 this 바인딩은 호출방식에 따라 달라진다.
function showThis() {
console.log(this);
}
const obj = {
method: function() {
console.log(this); // obj를 참조
},
arrowMethod: () => {
console.log(this); // 외부 `this`를 캡처 (전역 객체)
}
};
showThis(); // 전역 객체 또는 undefined
obj.method(); // obj
obj.arrowMethod(); // 전역 객체
C#에서는 항상 생성될 인스턴스를 가리키게 된다.
class Example {
private int value;
public Example(int value) {
this.value = value; // this는 현재 객체를 참조
}
public void ShowValue() {
Console.WriteLine(this.value);
}
}
또한 Js에서는 특정 메서드apply,call
등을 이용하여 바인딩을 변경할 수 있는데 비해 C#은 그런거 없다!
function greet() {
console.log(this.name);
}
const obj = { name: 'Alice' };
greet.call(obj); // Alice
greet.apply(obj); // Alice
const boundGreet = greet.bind(obj);
boundGreet(); // Alice
C#에서는 this 바인딩을 걱정하지 않아도 되서 좋구나!
GameObject enemyObject = Instantiate(enemies[index], spawnPos, Quaternion.identity);
Enemy enemy = enemyObject.GetComponent<Enemy>(); //제네릭에 들어간 Enemy는 클래스를 의미함 (c#파일)
//Monobehaviour를 상속한 C#파일은 하나의 컴포넌트임
GameObject란 씬(Scene) 내에 존재할 수 있는 모든 것을 가리킨다. 위처럼 스크립트를 통해 접근할 수도 있다.
오브젝트를 코드상으로 생성할땐 Instantiate(object, position, rotate)
메서드를 이용한다.
컴포넌트란 모듈이다. C#스크립트 또한 컴포넌트다.
위처럼 GameObject에 다양한 컴포넌트를 사용할 수 있다.
따라서 스크립트 내에서 오브젝트에 적용된 다른 스크립트를 참조해야 하는 경우 GameObject.GetComponent<type>()
메서드를 이용해서 참조한다.
주의할 점은 참조된 스크립트가 클래스가 아닌 인스턴스라는 점이다.
=> 유니티가 내부적으로 GameObject에 C#컴포넌트를 주입할 경우 알아서 인스턴스화 시켜준다.
유니티에서 스크립팅을 위해 MonoBehaviour 스크립트를 생성하면, 기본적인 두가지 메서드Start(), Update()
가 딸려온다.
using UnityEngine;
public class NewMonoBehaviourScript : MonoBehaviour
{
// Start is called once before the first execution of Update after the MonoBehaviour is created
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}
전자는 게임 시작시, 후자는 프레임당 실행된다. 다른 메서드도 존재하나 싶어 공식문서를 뒤져봤다.
출처 : https://docs.unity3d.com/kr/2023.2/Manual/ExecutionOrder.html
유니티의 이벤트 함수 실행 순서를 나타낸 그래프인데, 뭐가 엄청 많다. 마치 Class시절 리액트의 생명주기같다.
실제로 lifecycle라는 용어를 사용하기도 한다.
일단 이러한 메서드들이 각 시점마다 호출된다는 것만 파악하고 가자. 모두 알기에는 너무나 많고, 모르는 것이 너무나 많다!
강의가 막바지에 달할때쯤 GameManager
라는 스크립트를 만들었다. 게임의 전반적인 기능을 담당하는 메인 클래스. 객체지향 프로그래밍의 전역변수라고 생각하면 된다.
전역변수는 모든 모듈이 참조 가능한 최상단 변수이자 같은 값을 공유한다. 그렇기에 인스턴스가 오직 하나여야 한다.
=> 싱글톤 패턴을 적용한다.
using TMPro;
using UnityEngine;
using UnityEngine.SceneManagement;
public class GameManager : MonoBehaviour
{
public static GameManager instance = null;
void Awake() //생명주기 첫 단계에서 한번 실행된다
{
if (instance == null)
{
instance = this;
}
}
//...기타로직들
}
기존 인스턴스인 GameManager
대신 멤버변수 GameManager.instance
에 접근하여 사용한다.
확실히 객체지향 언어라서 그런지 디자인 패턴이 바로바로 등장한다. 물론 리액트에서도 옵저버, 싱글톤같은 디자인패턴을 사용하긴 하지만 클래스 템플릿과 사뭇 달라서 바로 이해하기가 어려웠다.
디자인패턴을 학습하고 새로운 게임을 만들어볼지, 게임을 만들면서 학습해야할지 고민되는구만?
한 언어에 익숙해지면 다른 언어를 쉽게 배울 수 있다했는데, 그 말이 사실일지도 모르겠다.
JS를 처음 배웠던 날들에 비하면 훨씬 쉽게 느껴졌다. 특히 로우레벨의 지식들과 C#에서 배운 내용이 연결되니 더 빠른속도로 배울 수 있는듯 했다. 물론 C#을 전문적으로 다루는 분들에 비하면 도토리 키 재기겠지만, 과거의 나보다 발전했다는 게 중요하니까...😅
항상 기본기는 중요하다!