새로 생성한 스크립트
- Player : 수정
- IDamage : 수정
- Player_AnimEvent : 수정 / Movement 클래스에서 Stop과 Move 함수 받아서 공격시 움직임 막기 등
- Movement : 에트리뷰트 사용한 움직임 / speed 상태 변경을 enum으로 사용하는 예시
- Test_Object : 스크립트나 그걸 가진 게임오브젝트 찾기 / 리플렉션 / 복사생성자 / 얕은 복사 ‘ 깊은 복사 / explicit operator 사용 / 업 다운 캐스팅 / try - catch - finally 간단 사용 예시
타격감을 구성하는 요소
- 애니메이션 : 피격 모션
- 사운드
- 이펙트
- 카메라 효과 : 카메라 흔들림
- 진동(PC에서는 구현 못함)
- Hit Stop
- 피격시 경직
- 피격 직전 슬로우모션
프로그래밍의 역사
- 절차지향(구조적) → 정보공학(데이터중심) → 객체지향(클래스, 또는 인터페이스 중심) → CBD(컴포넌트 중심)
- 객체지향 : GameObject / CBD : Transform ‘ Rigidbody ‘ Animator …
- 간단한 물리 기능 및 대상의 정보 전달(협업시 다른사람에게 공개해줘야 할(해줘도 될) 부분) : 컴포넌트로 해결 / 상세한 기능(피격, 프로그램 상에 필요한 기능) : 인터페이스로 해결
에트리뷰트
- 에트리뷰트 : 클래스의 추가적인 속성을 정할때 쓰는 기능
- RequireComponent) : 얘는 반드시 포함해야합니다 를 가리킬때 사용한다.
- [RequireComponent(typeof(Animator))] [SerializeField]
- 이게 사용된 스크립트를 오브젝트에 넣는다면 typeof에 적힌 컴포넌트가 없다면 자동으로 생성된다.
애트리뷰티 설명
| SerializeField | private, protected 변수를 인스펙터에 표시 |
| Serializable | 커스텀 클래스를 인스펙터에 표시 |
| Header | 변수위에 타이틀을 설정해 카테고리 분류 가능, 인스펙터 외관 정리에 사용 |
| HideInInspector | public 변수를 인스펙터에서 숨길 수 있다. |
| RqruireComponent | 필수 컴포넌트를 추가 할 수 있다. |
| Range | int, float 변수를 슬라이드바로 표시하고 범위를 제한함 |
| Space | 변수와 변수 사이에 간격 주가 |
| CreateAssetMenu | ScriptableObject Asset을 생성할때 사용하는 메뉴를 추가 할 때 사용 |
| MenuItem | 임의의 함수 (static) 실행을 메뉴 항목으로 추가 |
| ContextMenu | 임의의 함수 (non-static) 실행을 컴포넌트 톱니 메뉴에 추가 |
| AddComponentMenu | 인스펙터의 AddComponent 메뉴 항목으로 컴포넌트를 추가 할때 사용 |
| ExecuteInEditMode | 에디터가 플레이 모드가 아니더라도 컴포넌트가 동작하도록 할떄 사용 |
| Multiline | string 변수를 여러줄 입력 가능하게 만들때 사용 |
| TextArea | Multiline과 비슷, 폭에 맞춰 자동으로 줄바꿈과 슬라이드바 표시 |
| Tooltip | 인스펙터에 표시되는 변수에 설명을 추가 할때 사용 |
클래스에 기본적으로 생성되는 요소
- 기본생성자
- 대입 연산자(=)
- 최상위 클래스인 오브젝트 클래스로부터 받는 4가지 함수
- bool Equals(object obj); ⇒ ==연산이 기본적으로 Equals를 호출한다.
- int GetHashCode() ⇒ Equals를 호출할때 매개변수에 들어오는 변수를 해쉬 코드로 읽어 비교한다. / 이 해쉬코드와 비교하는 변수의 해쉬코드가 같다면 두 변수는 같은 변수다.
- Type GetType() ⇒ 리플렉션 기능을 한다. , 비교하는 변수의 타입을 가져와 자료형을 탐색한다.
- string ToString()
리플렉션
- 컴파일 시에 알 수 없었던 타입이나 멤버들을 찾아내고 사용할 수 있게 해주는 메커니즘이다.
- 즉, 객체의 이름, 모든 멤버, 이벤트 목록 등등 객체의 정보들을 런타임 중에 가져와 분석하고 사용할 수 있다.
- C++에는 없고 C#에만 있는 기능이다.
private void Reflection()
{
//GameObject.Find("Paladin"); //팔라딘을 찾는 기능
Enemy enemy = GameObject.FindObjectOfType<Enemy>();
//print(enemy != null ? "T" : "F"); //enemy 스크립트를 찾는 코드, 있으면 T 없으면 F 출력
//print(enemy.gameObject.name); //에너미가 들어간 오브젝트 이름 출력
//enemy 타입을 추적하는 코드
Type type = enemy.GetType();
MemberInfo[] members = type.GetMembers();
foreach (MemberInfo info in members)
{
print($"{info.Name}, {info.MemberType}");
}
}
복사 생성자
- 복사 생성자 (C#에서는 기본 복사 생성자가 생성되지 않기에 직접 기본 복사 생성자를 생성해야한다.)
- 동일한 클래스의 객체를 매개변수로 사용하는 경우 복사 생성자라고 한다.
public Copy_Character(int hp, int speed) //매개변수가 들어간 생성자를 만들면 { Hp = hp; Speed = speed; ItemList = new int[3]; ItemList[0] = 10; } public Copy_Character(Copy_Character character) //복사 생성자 { this.Hp = character.Hp; this.Speed = character.Speed; //this.ItemList = character.ItemList; //얕은 복사 this.ItemList = (int[])character.ItemList.Clone(); //아이템 리스트를 복사해라 라는 함수 //깊은 복사 Debug.Log("Copy_Character"); } - 복사 생성자 필요성
- 깊은 복사를 위해 사용한다.
- 객체를 다른 객체에 복사하고 싶을 때마다 복사 생성자를 사용할 수 있다.
앝은 복사 문제
class A{
int[] a = new int[3];
A(A copy){
this.a = copy.a;
}
}
A ob = new A();
A ob2 = new A(ob);
ob.a[0] = 100;
- 복사 생성자를 사용하여 매개변수로 들어간 객체의 값을 변경하면 복사 생성자의 객체도 값이 바뀌는 문제다.
- 정리
- copy.a의 값이 담긴 주소가 ob2에 저장된다.
- 즉, 값이 담긴 주소를 저장한다.
깊은 복사 문제
class A{
int[] a = new int[3];
A(A copy){
this.a = copy.a; => Clone()사용
//(int[])character.ItemList.Clone()
}
}
A ob = new A();
A ob2 = new A(ob);
ob.a[0] = 100;
- Clone함수는 값이 담긴 공간의 주소와 똑같은 공간을 만들고 복사한 공간의 주소를 준다.
- 즉, 복사하는 주소의 공간을 똑같이 만드는 기능이다.
- 정리
- Clone함수가 새 공간을 만들고 넣은 값을 복사해서 새 공간의 주소에 넣는다.
- 즉, 값 자체를 복사한다.
람다식
- 함수의 축약형을 쓰겠다는 기능이다.
⇒ 연산자
- 2가지의 의미를 가진다.
- 람다(LinQ)
- 식 본문 멤버 : 식 본문을 ⇒ 이후에 나오는 내용으로 정의하겠다는 의미다.
public void Print(string s) { print(s); } -> public void Print(string s) => print(s); - private int number; private int Number { set => number = value; get => number; }
try - catch - finally 예외처리
- try : 실행할 코드를 작성한다.
- catch : 예외가 발생했을때 처리하는 실행문을 넣는다.
- finally : 예외가 발생했을때도 반드시 실행되어야하는 기능을 작성한다.
try
{
Copy_Character2 mario2 = (Copy_Character2)o; //
print("예외발생"); //출력 안나옴 이유 : 바로 위에서 예외가 나오면 바로 catch영역으로 넘어가기 때문이다.
}
catch(Exception e) //무슨 에러가 날지 모를떄 사용한다.
{
print(e.Message);
}
finally //예외가 발생해도 반드시 필요한 코드를 실행시킬때 사용하는 함수
{
print("예외처리 완료");
}
is - as 예외 발생 처리수단\
- 명시적 캐스팅 보단 as사용
- 가독성이 좋다.
- cast보다 as가 빠르다. (녹음 41분 들어보자)
- 명시적 캐스팅은 캐스팅 연산자를 모두 체크해 본다,.(캐스팅 연산자 콜이 있다)
- 캐스팅 연산자 콜을 해서 하나하나 확인한다.
- as는 변환하고자하는 타입만 확인한다.(캐스팅 연산자 콜이 없다)
- 바뀔수 있는지만 확인.
- 명시적 캐스팅은 캐스팅 연산자를 모두 체크해 본다,.(캐스팅 연산자 콜이 있다)
- 항상 동일한 결과를 보장한다
- 명시적 캐스팅은 캐스팅 연산자 오버로딩이 가능하기 때문이다.