경일 메타버스 20220721 16주차 4일 수업내용. 유니티 & C# - 좀비 서바이버 : 열거형, 반복자 메소드, 코루틴, 애니메이션 - 전진운동학 & 역운동학, 레이캐스트, 확장 메소드, C#의 다형성
열거형으로 만든 기호상수 사용 시 꼭 열거형을 앞에 적어줘야 함. 즉, 이름 공간이 따로 있음.
ex) State.Ready
int 타입으로 암시적 형 변환 불가능. 명시적 형 변환 해야 함.
C++보다 좀 더 풍성한 기능 지원
IEnumerable / IEnumerator를 반환 타입으로 가지는 메서드
반복자 메서드에서는 yield문 사용 가능. yield문을 만나면 실행을 중단한다.
MoveNext()가 호출될 때마다 이전에 중단됐던 지점부터 실행하며, 메서드가 끝나거나 yield문을 만날 때까지 실행한다.
반복자 메소드를 활용해 함수의 명시적 실행 없이도 내부적으로 알아서 실행 시켜주는 시스템
시간 혹은 순차적 처리를 구현할 때 직관적으로 코드를 작성할 수 있어 유용하다.
WaitForSeconds : ~초 동안 중단
순운동학(FK; Forward Kinematics) :
상위 조인트의 변환에 따라 하위 조인트도 같이 변환이 되는 것
역운동학(IK; Inverse Kinematics) :
하위 조인트의 변환에 따라 상위 조인트도 같이 변환이 되는 것
OnAnimatorIK() : IK가 적용될 때마다 호출
레이어는 씬에 존재하는 게임 오브젝트를 구분할 수 있게 해주는 도구
충돌 검출에 활용
레이어 마스크를 이용하면 성능 향상의 이점이 있음.
Ray를 기반으로 충돌 검출을 하는 것
Physics.Raycast()
확장 메소드(Extension Method)는 기존 타입에 메소드를 추가할 수 있게 한다.
정적 메소드로 구현하며, 첫 번째 매개변수에 this 키워드와 함께 확장시킬 타입을 적는다.
확장 메소드는 확장하는 타입의 private에 접근할 수 없다.
즉, 캡슐화의 원칙을 위배할 수 없다.
가상 함수는 virtual 키워드를 붙인다.
오버라이딩은 override 키워드를 붙인다.
추상 클래스 / 메소드를 만들려면 abstract 키워드를 붙인다.
문법은 C++과 거의 같으나 몇 가지 차이점이 있다.
열거형의 선언 시 반드시 이름을 지어주어야 한다.
열거형으로 만든 기호상수를 사용할 때 꼭 열거형의 이름을 앞에 적어줘야 한다.
즉, 이름 공간이 따로 있다.
ex) State.Ready
int 타입으로 암시적 형 변환이 불가
반드시 캐스팅(명시적 형 변환)을 해야 한다.
C++보다 좀 더 풍성한 기능 지원
열거형 형식 예시 코드
public enum State
{
One,
Two,
Three
}
State.One;
public State state { get; private set };
IEnumerable / IEnumerator를 반환 타입으로 가지는 메소드
yield문 사용 가능
yield : 영어로 “양보하다”는 뜻
yield return :
반환 값과 함께 중단
yield break :
반환 값 없이 중단
MoveNext() 메소드가 호출될 때마다 이전에 중단됐던 지점부터 실행하며,
메소드가 끝나거나 yield문을 만날 때까지 실행한다.
예시 코드
IEnumerator<int> TestIteratorMethod()
{
yield return 1;
yield return 2;
yield return 3;
yield return 4;
yield return 5;
yield return 6;
yield return 7;
yield return 8;
yield return 9;
yield return 10;
}
IEnumerator<int> enumerator = TestIteratorMethod();
while(enumerator.MoveNext())
{
Console.WriteLine(enumerator.Current);
}
위 코드를 실행해보자.
결과 :
컨테이너처럼 가상의 배열에 값을 저장하고,
enumerator가 값을 순회한다.
위의 IEnumerator 타입 enumerator에 할당할 때에도,
TestIteratorMethod 함수 본문은 실행되지 않는다.
enumerator.MoveNext()는 호출되면서
이전에 실행한 위치(마지막 실행)을 기억하고,
그 곳으로 돌아가 yield에 걸리면 "양보”하고
다음 yield로 넘어가 return을 실행하면서 값을 반환한다.
이러한 반복자 메소드를 응용한 것이 코루틴(Coroutine)이다.
Coroutine : 도와주는 함수라는 의미.
명시적 실행 없이도 내부적으로 알아서 실행을 시켜주는 함수
코루틴을 StartCoroutine으로 등록만 해준다면 호출 없이도 알아서 실행한다.
시간 혹은 순차적 처리를 구현할 때 직관적으로 코드를 작성할 수 있어 유용하다.
C#의 반복자 메소드를 이용해 구현되었다.
⇒ yield문의 활용 등
코루틴은 멀티스레드가 아니며,
싱글스레드인 유니티 이벤트 스크립트 라이프사이클 안에 포함되어있다.
StartCoroutine(코루틴) :
코루틴을 시작한다, 등록한다.
WaitForSeconds() :
~초 동안 중단
기존 델타타임을 활용한 코드와 코루틴을 활용한 코드의 비교
// 델타타임 활용
void Update()
{
_elapsedTime += Time.deltaTime;
if(_elapsedTime >= 0.5f)
{
_elapsedTime = 0f;
GameObject bullet = Instantiate(BulletPrefab, transform);
bullet.transform.LookAt(Player);
}
}
// 코루틴 활용
StartCoroutine(Shot());
IEnumerator Shot()
{
while (true)
{
GameObject bullet = Instantiate(BulletPrefab, transform);
bullet.transform.LookAt(Player);
yield return new WaitForSeconds(0.5f);
}
}
특수한 충돌 처리
발사된 광선이 다른 콜라이더와 충돌하는 지 검사하는 처리
Ray 타입
RaycastHit 타입 오브젝트
bool Physics.Raycast // 오버로딩이 매우 많다. 아래는 한 얘시
(Vector3 origin, Vector3 direction, out RaycastHit hitInfo, float maxDistance, int layerMask, QueryTriggerInteraction queryTriggerInteraction);
origin :
레이의 시작점
direction :
레이의 방향
out hitInfo :
레이가 충돌한 경우 out 키워드로 간접 참조된 충돌 정보
maxDistance :
레이 충돌을 검사할 최대 거리
layerMask :
충돌 처리를 무시할 레이어
레이어 기반 충돌 감지 :
오브젝트 인스펙터 상단, tag의 옆 layer는 tag처럼 오브젝트들을 묶어주는 역할이지만,
그 묶어진 그룹 내에서만 충돌할 수 있게 설정할 수 있다.
전진 운동학(순운동학; FK; Forward Kinematics) :
큰 관절의 변화에 의해서 작은 관절들의 위치, 회전이 영향을 받는다.
역운동학(IK; Inverse Kinematics) :
작은 관절들의 변화에 의해서 큰 관절의 위치, 회전이 영향을 받는다.
C#에서 지원하는 확장 메소드로 외부 패키지(타입)에 사용자가 정의한 메소드를 추가할 수 있다.
조건
정적 메소드로 구현한다.
첫 번째 매개변수에 this 키워드와 함께 확장시킬 타입을 적는다.
확장하는 타입의 private에 접근할 수 없다.
⇒ 캡슐화의 원칙을 위배할 수 없다.
C#에서의 가상 함수
가상 함수 키워드 virtual
오버라이딩 키워드 override
순수 가상 함수 (추상 클래스) 키워드 abstract
상속 받으면 구현(오버라이딩)을 강제한다.
강제성을 부각한다.
인터페이스와 유사
인터페이스와의 차이점
문법적인 관점에서,
추상 클래스는 클래스이고
인터페이스는 클래스가 아닌 인터페이스이다.
때문에
추상함수 :
하나만 상속 가능
메소드 뿐만 아니라 필드 등의
다양한 데이터
객체 역할을 정의
인터페이스 :
여러 개를 상속 가능
메소드만 보유
메시지만을 정의
예시 코드
class Base
{
public virtual void Foo() { Console.WriteLine("Base의 Foo"); }
}
class Derived : Base
{
public override void Foo()
{
base.Foo();
}
}
abstract class AbstractBase
{
public abstract void Foo();
}
class Derived2 : AbstractBase
{
public override void Foo()
{
}
}