04강) C# 프로그래밍 기초3 |
지난 시간에 이어서 마지막으로 C# 프로그래밍의 기초에 대해서 더 알아봅시다.
C#) 메소드(method) |
C언어에서는 함수라고 하고, C++과 C# 그리고 Java같은 객체지향형 프로그램에서는 메소드라고 일컫는 겁니다.
유니티에서 기본적으로 스크립트를 만들게 되면
using System.Collections; using System.Collections.Generic; using UnityEngine; public class NewScript : MonoBehaviour { // Use this for initialization void Start () { } // Update is called once per frame void Update () { } }
이렇게 "void Start()", "void Update()"라는 것이 자동으로 생기게 됩니다.
이것들을 보고 각각 "Start 메소드", "Update 메소드"라고 말하는 데요.
어떤 기능들을 정의하고 사용하는데 쓰입니다.
메소드 란? |
프로그램의 특정 기능을 하는 뭉치, 뭉텅이, 묶음 |
메소드는 다음과 같이 선언을 하게 됩니다.
접근_지시자 반환형 메소드이름(매개변수..) { 내용들... 반환내용; } // 예시1 private int PlusOne(int num) { return num + 1; } // 예시2 public void PrintTwice(int num) { Debug.Log(num); Debug.Log(num); }
접근 지시자는 뒤에서 배우기로 하고!!
반환형은 앞서 배운 "자료형"을 기준으로 합니다.
(물론 뒤에 배울 직접작성하는 클래스 기준으로도 가능합니다.)
그래서 8번줄의 PlusOne이라고 하는 메소드는 int형으로 반환하겠다라고 해서 num + 1을 반환하게 됩니다.
일반적인 값의 반환 |
반환하는 방법은 "return"이라는 키워드를 사용합니다. 메소드가 int형이라고 한다면 반환하는 형식도 무조건 int형이어야 합니다. |
하지만 14번줄의 PrintTwice 메소드에는 보지못한 void라는 형이 있습니다.
이것은 어떠한 값도 반환하지 않겠다라는 뜻입니다.
void형의 반환 |
일반적으로 void형은 "어떠한 값도 반환하지 않겠다"라고 하였을 때 사용하는 형입니다. 만약에 void에서 return을 사용하면 그냥 메소드가 종료되게 됩니다. |
여기서 또 처음본것이 "매개변수"일겁니다.
매개변수란? |
영어로는 파라미터(parameter)라고 하는 것으로 함수나 메소드에 외부로부터 입력되어 오는 특별한 변수를 뜻합니다. 이렇게 외부에서 매개변수로 대입되어 들어오는 값은 "전달인자(argument)", 흔히 알규먼트라고 하는 것이 됩니다. |
위에서 PlusOne에서는 int형 값을 매개로 해서 사용하게 되고, PrintTwice도 역시 int형 값을 매개로 해서 사용하게 됩니다.
이제 예제를 통해서 알아봅시다.
using UnityEngine; public class TestScript : MonoBehaviour { void Start () { int num = 10; PrintNum(num); num = PlusOne(num); PrintNum(num); } void PrintNum(int num) // 변수를 출력하는 메소드 { Debug.Log("num : " + num); } int PlusOne(int num) // 변수를 1더해서 반환하는 메소드 { return num + 1; } }
변수를 출력하는 "PrintNum 메소드"와 매개변수로 받은 값에 1을 더해서 반환하는 "PlusOne 메소드"를 만들었습니다.
그런데 "Start 메소드"에 대해서 궁금하지 않나요?
위의 예제에서는 PrintNum 메소드와 PlusOne 메소드를 구현하여 Start 메소드에서 실행되도록 써주었는데, Start 메소드는 그 어디에도 쓰여있지 않지만 바로 실행이 되네요.
그 이유는 바로 "Start 메소드"는 유니티에서 선언해놓은 "이벤트 함수(이벤트 메소드)" 이기 때문이죠.
(유니티 측에서는 메소드가 아닌 함수라고 표현하고 있습니다.)
이벤트 함수? 이벤트 메소드? |
유니티에서 미리 선언해놓은 함수로 게임중에 발생하는 이벤트를 수시로 식별하며 거기에 대응하는 이벤트를 처리하기 위한 함수입니다. 이벤트 함수에는 대표적으로 Update, Start, Awake, FixedUpdate, OnGUI, OnApplicationQuit 등이 있습니다. |
우리가 써온 Start 메소드 즉, Start 이벤트는 해당 스크립트가 적용된 객체가 처음 생성되면서 "딱 한번" 실행되는 이벤트입니다.
그래서 지난번에 처음 배울 때 "메인카메라"에 스크립트를 적용시켰었죠?
그 카메라가 처음 생성될 때 위의 Start 이벤트가 자동적으로 실행이 되는 것입니다.
C#) 변수의 스코프(유효 범위) |
프로그래밍 언어에서 정말로 중요한 스코프입니다.
유효 범위라고 쓰이지요.
그럼 다음과 같은 소스가 있다고 합시다.
using UnityEngine; public class TestScript : MonoBehaviour { void Start () { int num = 10; Debug.Log("num : " + num); Function1(); Function2(); } void Function1() { int num = 20; Debug.Log("num : " + num); } void Function2() { int num = 30; Debug.Log("num : " + num); } }
아마 C#프로그래밍1편에서 변수선언에 대해서 잘 배우셨다면 "오류"가 떠야한다고 생각하실 수 있습니다.
그때는 "변수의 이름은 고유해야한다." 라고 배웠기 때문이죠.
6번줄, 13번줄, 18번줄의 변수가 전부 중복되어서 "오류"가 뜬다라고 생각하실 수 있습니다.
하지만
그 어떠한 오류도 뜨지 않습니다.
여기서 바로 "스코프"라는 개념을 알아야 합니다.
일반적인 변수의 유효범위(스코프) |
변수는 선언된 지점 이외에서는 접근이 불가능 하다. (묶여있는 범위를 넘어선다면) |
다시 위의 소스를 되짚어 봅시다.
위의 소스에서 각각의 num변수의 유효범위는 다음과 같습니다.
자신이 묶여있는 "{}"이 범위를 벗어나지 못하게 됩니다.
그리하여 자신이 속해있는 메소드 범위를 벗어나면 그 변수의 효력은 없어지는 것이지요.
그럼 모든 범위에서 동일한 변수를 사용하려면 어떻게 해야할까요?
클래스 내부에 변수 선언 |
클래스 내부에(클래스 내부의 메소드가 아닌) 바로 변수를 선언하면 클래스 내부의 모든 메소드에서 해당 변수에 접근이 가능케 된다. 이렇게 클래스 내부에 바로 선언하는 변수를 "필드"라고 한다. |
클래스에 대해서는 바로 다음에 알아보도록 하고!!
이게 무슨말이냐 하면!!
using UnityEngine; public class TestScript : MonoBehaviour { int num = 10; void Start () { Debug.Log("num : " + num); Function1(); Function2(); } void Function1() { Debug.Log("num : " + num); } void Function2() { Debug.Log("num : " + num); } }
4번 줄과 같이 바로 아래 부분에 변수를 선언하면 해당하는 클래스의 모든 메소드에서 변수에 접근할 수 있습니다.
그래서 이렇게 출력이 가능케 됩니다.
C#) 클래스(class) |
정말 간단하게 말해서 "메소드와 변수들을 어떤 틀에 묶어놓은 것"이라고 할 수 있습니다.
그래서 객체지향 프로그램에서는 "프로그램 뭉치" 라고도 하고 "프로그램 단위"라고도 합니다.
클래스란? |
객체를 정의해놓은 것으로 "붕어빵 기계 틀"이라고 정의할 수 있다. |
객체란? |
클래스를 이용하여 실체화 시킨 것으로 "붕어빵"이라고 정의할 수 있다. |
즉, 클래스는 객체를 찍어내기 위한 "틀"이라고 생각하시면 정말 편합니다.
그리고 객체는 그 "틀"에서 찍어나온, 실체화 된 "제품"이라고 생각하시면 됩니다.
우리가 처음 스크립트를 생성하면 생성한 이름에 맞추어서
이렇게 "public class 이름 : MonoBehaviour"라고 자동으로 생성이 됩니다.
그리고 우리는 여기에 변수도 선언하고, 메소드도 정의해서 사용을 하는 것이지요.
(옆에 붙은 " : MonoBehaviour"는 상속의 개념인데 이것은 C# 관련 책이나 다른 블로그를 참고하세요)
(추후에 게임 만들면서 부가적으로 설명할겁니다.)
(참고로 Start 메소드나 Update 메소드는 MonoBehaviour을 상속해서 사용하는 것입니다.)
그리고 해당하는 클래스를 다른 클래스에서 사용하기 위해서는 객체로 만들어야 합니다.
클래스를 객체화하기 |
클래스_이름 객체이름 = new 클래스_이름(); |
만약에 "Original"이라는 클래스를 만들고 이걸 객체화 시키면?
Original ori = new Original();
Original이라는 클래스를 ori라는 이름으로 객체화 시켰습니다.
객체화를 시키는 이유는 간단합니다.
"클래스를 사용하기 위해서 선행하는 작업"이라고 생각하시면 됩니다.
(우리가 변수를 사용하기 위해서 먼저 선언하는 것 처럼)
C#) 접근 제한 |
접근 제한은 말 그대로 접근을 제한하는 겁니다.
C#에는 크게 4가지의 제한자가 있지만 유니티에서는 주로 2가지의 제한자를 사용하게 됩니다.
제한자 | 설명 |
public | 다른 클래스나 파생 클래스에서 접근이 가능하다. |
private | 해당 클래스 내부에서만 접근이 가능하다. |
제한자는 클래스나 메소드, 필드에 붙여 사용하게 됩니다.
그럼 예제를 통해서 알아봅시다.
using UnityEngine; public class TestScript : MonoBehaviour { Test test = new Test(); void Start () { int num1 = test.public_num; int num2 = test.private_num; } } // 이 클래스는 새로 스크립트를 만드는 것이 아니라 // 위의 클래스 아래에 그냥 그대로 쓰면 됩니다. class Test { public int public_num = 10; private int private_num = 5; }
이렇게 입력을 합니다.
그러면
public으로 선언한 public_num은 외부의 다른 클래스에서 접근이 가능하지만
private로 선언한 private_num은 외부의 다른 클래스에서 접근이 불가능하게 됩니다.
그래서 이렇게 보호수준때문에 접근할 수 없다고 뜹니다.
그런데 이런 에러가 보이지 않는다구요?
스크립트를 저장하고 유니티로 넘어오시면
이렇게 콘솔창에 똑같이 "protection level" 즉, 보호레벨때문에 접근이 안된다고 오류가 보이실 겁니다.
그럼 메소드에도 적용시켜봅시다.
역시나 접근이 안됩니다.
이렇게 접근 제한을 걸어서 외부에 공개해도 되는 데이터는 공개하고, 공개하면 안되는 데이터는 접근하지 못하도록 막아야 실수를 최대한으로, 혹시나 하는 변조로부터 보호할 수 있습니다.
C#) 배열 |
배열은 아주 간단합니다.
변수들을 모아놓은 아파트입니다.
배열이 있기 전의 변수 선언 |
정수형 변수 5개를 선언하라!! int num1, num2, num3, num4, num5; |
배열이 생긴 이후 변수 선언 |
//정수형 변수 5개를 선언하라!! int[] num = new int[5]; |
배열 선언하기 |
자료형[] 배열이름 = new 자료형[배열길이]; |
배열을 생성하게 되면 모든 배열은 각각의 고유 번호를 가지게 됩니다.
위에서 num이라는 배열을 5의 길이로 생성하였다면
num[0], num[1], num[2], num[3], num[4] 이렇게 각각의 위치를 나타내는 고유번호를 가지고 생성이 되죠.
주의점 |
배열의 길이를 5로 한다고해서 1,2,3,4,5 로 되는 것이 아닌 0,1,2,3,4 이렇게 됩니다. (항상 0부터 시작합니다.) |
그래서 배열에 값을 넣으려면
num[0] = 4; 이렇게 원하는 위치에 넣으면 됩니다.
그리고 해당하는 값에 접근할때도 num[0] 이렇게 접근하려는 위치를 제시하여 접근하면 됩니다.
주의점 |
선언한 배열의 길이를 넘어서는 부분에 접근하면 오류가 발생합니다. |
그래서 배열은 for문과 짝을 이뤄서 사용을 합니다.
using UnityEngine; public class TestScript : MonoBehaviour { void Start () { int[] num_arr = new int[5]; // num_arr.Length는 num_arr의 길이를 자동으로 반환해줍니다. for(int i = 0; i < num_arr.Length; i++) { num_arr[i] = i; } Debug.Log("배열 출력"); for(int i = 0; i < num_arr.Length; i++) { Debug.Log(num_arr[i]); } } }
for문은 0부터 4까지(num_arr.Length는 5) 반복하게 됩니다.
그래서 num_arr[0]부터 num_arr[4]까지 접근하여 0부터 4까지의 값을 각각 넣게 되죠.
출력할때는 각각의 값을 출력만 하죠.
C#) foreach 문 |
foreach문이라고 해서 for과 배열을 좀더 쉽게 사용하도록 만든것이 있습니다.
방금 위에서 봤던 예제를 foreach로 바꾸어보면
using UnityEngine; public class TestScript : MonoBehaviour { void Start () { int[] num_arr = new int[5]; // num_arr.Length는 num_arr의 길이를 자동으로 반환해줍니다. for(int i = 0; i < num_arr.Length; i++) { num_arr[i] = i; } Debug.Log("배열 출력"); foreach(int i in num_arr) { Debug.Log(i); } } }
14번줄 ~ 17번줄이 위와 같이 바뀌게 됩니다.
8번줄 ~ 11번줄 같이 값을 변경할 때는 foreach를 사용할 수 없는데 사실 이 foreach문은 for문에 비해서 무거운 함수입니다.
그리고 foreach는 전부 for문으로 나타낼 수 있습니다.
그래서 저는 foreach문을 가르쳐드리지 않겠습니다.
전부 for문으로 하겠습니다.
기본 프로그래밍 끝 |
드디어 기본 프로그래밍이 끝났습니다.
사실 방대한 C#을 최소한으로 줄이다 보니 내용도 많이 빈약하게 되었습니다.
이 정도는 아셔야 그래도 간단한 게임을 훨씬 수월하고, 최적화 되게 만들 수 있습니다.
그리고 소개 안한 것들도 많은데 이것을 사용할 때 조금씩 알려드리도록 하겠습니다.