GURU_unity_1주차 C#스크립트 익히기 3

2023. 6. 29. 22:59SWU_프로젝트/GURU - unity

지난 시간에 이어서 , 오늘 학습하고자 할 내용은 다음과 같다.

4.메서드 만들기
5.클래스 만들기
6.Vector 클래스 사용하기

 

4. 메서드 만들기

 

메서드에 대해 알아보기 전에, 이해를 돕고자 아래의 그림을 준비했다.

 

  • 스크립트가 끝없이 길어질 때가 있는데 길어진 처리를 의미가 있는 처리 블록으로 분해하고 이름을 붙이는  구조를 만드는데, 이렇게 분해한 각 처리를 메서드(또는 함수)라고 한다.
  • 긴 처리A를 처리A, 처리B, 처리 C로 분해하고 이름을 붙혀서 구분함으로써 유용하게 사용할 수있다.
  • 메서드는 처리를 기능 단위로 묶는 역할 외에도 메서드에 값을 전달해서 계산하거나 계산 결과를 돌려받을 수 있도록 하는 역할을 한다.
  • 메서드로 건네는 값을 인수, 메서드에서 돌려받는 값을 반환값이라고 부른다.
  • 인수는 여러개 건넬 수 있지만 반환값은 한개로 정해져있다.
  • C에 익숙한 사람이라면 함수=메서드 라고 이해해도 될 것같다.

 

메서드를 만드는 방법은 다음과 예시와 같다.

void Start()
{

     int answer;
     answer = Add(3,4);

}
Add- 메서드명, ()괄호안 숫자는 인수이다.
int  Add( int a, int b)
{
     int c = a + b
     return c;
}

int - 반환값의 데이터형 , Add- 메서드명, ()괄호안 숫자는 데이터형 인수, C는 반환값이다.
  • 반환값의 데이터형 에는 호출자의 메서드로 반환하는 값의 데이터형을 지정한다.
  • 지정할 데이터형은 변수의 데이터형과 같아야한다.
  • 값을 반환하지 않는 메서드에는 void를 지정한다.
  • void란 '반환값이 없음'을 뜻한다.
  • 인수는 호출자의 메서드에서 받은 값이다.
  • 메서드는 인수 값을 사용해서 처리를 실행한다.
  • 인수를 갖지 않는 메서드도 있는데 이때는 메서드명 뒤에 붙는 괄호안을 공백으로 비운다.

이제부터는 메서드를 만들어볼 것이다.

 

아래 그림은 가장 간단한 인수도 반환값도 없는 메서드의 예시이다.

SayHello라는 메서드를 만들어주면서 반환값이 없기에 void 인수를 가지지 않기에 ()공백으로 둔다. 

  void     SayHello   (공백)
반환값X  메서드명    인수X

 Console창을 통해 확인할 수있다.

 

다음은 반환값은 없지만 인수는 있는 예시이다.

 void     CallName    (string name)
반환값X   메서드명    string형의 인수 한개

   메서드 호출과정은 다음과 같다.

 

  • 17번째 줄에서 CallName 메서드를 호출한다.
  • 메서드명 CallName에 이어 괄호 안에 메서드로 전달하는 이름(문자열)을 쓴다.
  • CallName메서드를 호출하면 인수의 문자열인 "Tom"이 자동으로 메서드 안의 name 변수에 대입된다.
  • name 메서드는 메서드에서 변수처럼 쓸 수 있으므로 Debug.Log를 써서 name변수 값을 출력한다.
  • 여기서 주의할점은 17번째줄에서 인수를 지정하지 않고 CallName();으로 메서드를 호출하면 오류가 발생한다는 점이다.
  • 기본적으로 인수 수는 호출되는 쪽과 호출하는 쪽이 일치해야한다.

 

다음 예시는 인수와 반환값이 모두 있는 메서드이다.

인수로 변수를 두개 받고 두 변수의 합계를 반환하는 Add메서드이다.

 

Add 메서드는 인수가 두개이므로 각 인수를 쉼표(,)로 구분해서 선언해야한다.

또한 인수의 합계를 반환값으로 돌려줘야 하므로  return문을 쓴다.

        int                 Add         (int a, int b)
int 형의 반환값     메서드명     int형의 인수 두개

 

메서드 호출방법은 다음과 같다.

  • 메서드를 호출하는 쪽에서는 메서드명에 이어 인수를 두개(2와 3) 전달한다.
  • 메서드 호출과 동시에 a와 b 변수에는 각각 2와 3이 대입된다.
  • a와 b 변수에는 메서드를 호출하는 곳에서 지정한 순서대로 값이 대입된다.
  • 따라서, 만약 Add(2,3)이 아닌 Add(3,2)를 쓴다면 a변수에 3이 대입되고, b변수에 2가 대입된다.
  • 메서드는 a와 b변수를 합한 값을 c변수에 대입하고 return문을 사용해서 합계를 호출한 곳으로 반환한다.
  • 메서드를 실행한 후에는 메서드의 호출 부분이 반환값으로 바뀌는 형태이다.
  • Add(2,3)부분이 반환값 c값으로 바뀌기 때문에 answer=Add(2,3);은 answer=c;이 되고 answer에 c값이 대입된다.

 

5. 클래스 만들기

 

  • 메서드는 처리를 모아 둔 것이고, 클래스는 메서드와 변수를 모아 둔 것이다.
  • 유니티로 게임을 만들려면 플레이어, 적, 무기, 아이템 등 물체마다 그 움직임을 정의하는 스크립트를 작성해야한다.
  • 이때는 '처리 단위'보다는 '물건 단위'로 스크립트를 작성하는 것이 편리하다.
  •  변수와 메서드를 하나로 합치지 않고 따로따로 구현하면 어느 변수와 메서드가 연결되어 있는지 알기 어렵다.
  • 클래스를 사용하면 관계가 있는 변수와 메서드를 하나로 합칠 수 있으므로 스크립트를 관리하기가 쉬워진다.
  • class 키워드 다음에 클래스명을 쓰고, 그 안에 클래스에서 사용하는 변수와 메서드를 쓴다.
  • 클래스에서 사용한 변수를 멤버 변수, 클래스에서 사용한 메서드를 멤버 메서드라고 한다.
class 클래스명
{
   멤버 변수 선언;
   멤버 메서드 구현;
}

 

  • 작성한 클래스는 int나 string등 데이터 형으로 사용할 수 있다.
  • 즉 Player 클래스를 만들면 Player 형을 사용할 수 있게된다.
  •  Player myPlayer;을 쓰면 Player형의 myPlayer변수를 만들 수있는데, 이상태에서 myPlayer변수의 상자안은 비어있다.
  • int 형의 num 변수가 있다고 하면 그 변수에는 정수형 숫자를 대입한다.
  • 마찬가지로, Player형의 myPlayer변수에는 플레이어의 실체를 대입하는 데 이 실체를 인스턴스라고 한다.
  • myPlayer변수가 가진 멤버 메서드나 멤버 변수를 사용하려면 myPlayer.멤버 메서드명(또는 멤버 변수명)을 쓴다.
  • OO.xx가 나온다면 OO클래스가 갖는 xx메서드(또는 변수)를 사용한다고 이해해두면 된다.
  • 참고로 직접 클래스를 만들수도 있지만 유니티가 처음부터 제공하는 클래스도 있다.
  • 예를 들자면 vector클래스와 로그표시를 할때 쓰는 Debug클래스 등이다.
  • 하지만 굳이 따지자면 vector는 구조체라고 말하는 것이 맞다.
  • 유니티를 잘 사용하기 위해 클래스 개념을 제대로 이해하는 것이 필요하다.

 

 

   간단히 위 내용을 살펴보자면,

  • 6번째 줄에서 Player클래스를 선언했다.
  • 8-9번째 줄에서 플레이어의 hp를 나타내는 멤버 변수(hp)와 공격력을 나타내는 멤버 변수 (power)를 선언했다.
  • 11-20번째 줄에서는 공격하는 멤버 메서드(Attack)와 데미지를 받는 멤버 메서드(Demage)를 작성했다.
  • 28-30번째 줄에서는 위에  작성 된 Player클래스의 인스턴스를 만들어서 사용하고 있다.
  • 28번째 줄에서는 Player형의 myPlayer변수를 선언한다.
  • 이 단계에서는 myPlayer라는 상자만 만들었을 뿐이고, Player형의 실체인 인스턴스를 작성해 대입해야한다.
  • 인스턴스를 만들려면 new키워드 다음에 클래스 이름()을 써야한다.
  • Player클래스의 인스턴스가 만들어지고 이것을 myPlayer변수 안에 대입한다.
  • 29번째 줄에서는 인스턴스를 가진 Attack 메서드를 myPlayer.Attack()인 변수명, 멤버 메서드명() 형태로 호출한다.
  • 또한, 30번째 줄에서는 Damage메서드를 호출한다.

   여기서 상속에 대한 개념을 살짝 추가하자면,

  • Test 클래스의 선언부분 뒤에 붙은: MonoBehavior 를 상속이라고 한다.
  • 이부분은 유니티가 미리 준비한 MonoBehavior 클래스의 기능을 Test클래스에 집어넣겠다고 선언하는 과정이다.
  •  MonoBehavior 클래스는 게임 오브젝트를 구성하는 기본 기능을 멤버 변수와 멤버 메서드로 준비한 클래스이다.
  • 게임 오브젝트에 붙여 실행하는 스크립트는  MonoBehavior 클래스(또는  MonoBehavior를 바탕으로 한 클래스) 를 상속해야한다.

Console 창을 통해 출력되는 것을 확인할 수있다.

 

 

접근수식자란,

  • public 이나 private 처럼 클래스의 멤버 변수와 멤버 메서드 앞에 키워드가 붙어있는 것을 말한다.
  • public이 붙어있는 멤버는 다른 클래스에서도 호출할 수 있지만, private이 붙어 있는 멤버는 다른 클래스에서 호출할 수없다.
  • Attack 메서드에는 public이 붙어 있으므로 myPlayer.Attack()이라고 써서 Attack메서드를 호출할 수있다.
  • hp 변수에는 private이 붙어 있으므로 myPlayer.hp라고 써도 hp변수에 접근할 수 없다.
  • 접근 수식자를 생략하게되면 private으로 간주되므로 공개하고 싶은 변수와 메서드가 있다면, public 수식자를 붙이는 것이 좋다.
접근 수식자 접근 가능 클래스
public 모든 클래스에서 접근 가능
protected 같은 클래스와 해당 클래스의 서브클래스에서 접근 가능
private 같은 클래스에서만 접근 가능

 

this 키워드

  • this 는 자신의 인스턴트를 가리키는 키워드이다.
  • this.power는 자신의 인스턴스가 가진 power변수(Player클래스의 인스턴스가 가진 power 변수)를 나타낸다.
  • 하지만, this를 붙이지 않아도 자기 클래스의 멤버 변수를 사용할 수 있다.
  • 만약, Attack메서드에서 멤버 변수와 동일한 이름의 로컬변수(power)를 선언했다고 할 때, power라고만 쓰면 로컬 변수의 값이 우선해서 사용된다.(위의 예시로 확인해볼 수 있다.) 
  • 멤버 변수를 쓸 때는 명시적으로 this를 붙여야 오류를 막을 수 있다.
public void Attack()
{
    int power=9999;
    Debug.Log(power+"데미지를 입혔다");
    //로컬 변수가 우선 쓰이고, 9999 데미지를 주게 된다.

}

 

Debug.Log는 인스턴스 없이 사용할 수 있을까?

  • Player  클래스의 인스턴스를 만들고 '인스턴스 변수명.멤버.메서드명'형태로 멤버 메서드를 호출한다.
  • 계속해서 쓰고 있는 Debug.Log메서드는 '클래스명.멤버.메서드명'형태로 멤버 메서드를 직접호출한다.
  • Debug.Log 가 static메서드(인스턴스를 만들지 않고도 쓸 수있는 메서드)로 선언되기 때문이다.

 

6.Vector 클래스 사용하기

3D게임을 만들려면 공간에서 오브젝트를 어디에 둘지, 어느쪽으로 옮길지, 어디로 힘을 보낼지 등을 정해야하므로

float형의 x, y, z값 세개를 사용한다.

 

Vector형 사용방법의 설명을 돕기위해 아래 그림을 넣었다.

  • C#에는 이러한 값을 하나로 합쳐서 다룰 수 있는 Vector3 클래스(정확히는 구조체5)가 준비되어있다.
  • 반면 2D게임용에는 float형의 x, y 값을 갖는 vector2 클래스가 있다. 

  vector3 클래스의 구조는 다음과 같다.

struct Vector3
{
     public float x;
     public float y;
     public float z;
    // Vector용 멤버 메서드가 아래에 이어진다.
}
  • Vector3 클래스에는 x,y,z 멤버 변수가 있고, Vector2 클래스에는 x,y 멤버 변수가 있다.
  • 둘 다 좌표나 벡터로 쓸 수 있다.
  • 예를 들어 x=3, y= 4를 좌표로 쓰면 오브젝트가 (3,4) 위치에 배치되었다는 것을 뜻한다.
  • 벡터로 쓰면 현재 위치에서 'X 방향으로 3, Y방향으로 5' 움직였다는 것을 뜻한다.

  위의 그림과 아래를 모두 참고하면 좋겠다.

 

본격적으로 Vector 클래스(구조체)를 사용해보자.

Vector2 클래스의 멤버 변수에 숫자를 더하는 코드인데,

이렇게 범위가 작은타입(int)은 범위가 더 넓은 타입(float)으로 넣을때 문제가 생기지 않아서 f를 붙히지 않고 해도 상관없다. 정수값은 실수에 자유롭게 넣을수 있고, 암묵적 형변환이 일어나게된다.

 

간단히 설명해본다면,

 

  • Vector2 클래스의 playerPos 변수를 선언한다.
  • 이 것은 Vector2형의 상자를 만든 것이고, 대입할 수 있는 인스턴스를 이어서 작성해야한다.
  • new Vector2(3.0f,4.0f)를 써서 Vector2클래스의 인스턴스를 작성해 대입한다.
  • new클래스명() 으로 클래스 인스턴스를 작성할 수 있다.
  • Vector2 클래스에서 new를 사용해 인스턴스를 만들 때는 인수를 전달하면서 멤버 변수를 초기화 할 수 있다.
  • 여기서 x를 3.0f, y를 4.0f 로 초기화한다.
  • 15-16번째 줄은 x,y좌표를 증가시키는 내용이다.
  • Vector 클래스 변수를 게임 오브젝트의 좌표로 사용하는 경우에는 값을 증가시키면 화면의 게임 오브젝트가 양수 방향(오른쪽 혹은 위)으로 이동한다.
  • 반대로 값을 감소시키면 음수방향(왼쪽 혹은 아래) 으로 이동한다.

 

Console창을 통해 잘 더해진 것을 확인 할 수있다.

 

 

이번에는 Vector 클래스끼리 뺄셈하기 예시를 확인해보자.

  • 이 예제에서는 startPos에서 endPos로 향하는 dir 벡터를 구한다.
  • 두 점의 좌표에서 벡터를 구하기 때문에 14번째 줄 endPos 에서 startPos를 뺀다.

  • 18번째 줄에서는 startPos부터 endPos까지 거리를 구한다.
  • 이거리는 dir벡터 길이와 같으므로 Vector2 클래스가 갖는 magnitude멤버 변수를 사용해서 dir 벡터의 길이를 구한다.

이렇게 Console창을 통해 결과값을 확인할 수 있다.

 

 

Vector 클래스를 응용하면  가속도, 힘, 이동 속도 같은 물리 수치로도 사용할 수 있다.

 

오늘은 어제에 비해서 조금 까다로운 내용을 다뤘다.

메서드까지는 C의 함수와 유사해서 익숙했지만, 클래스 개념부터는 생소해서  조금씩 어려워지는 것 같지만 열심히 배워봐야지!