Unity Chapter 3. C# 프로그래밍 [기본]

Date:     Updated:

카테고리:

태그:

인프런에 있는 이제민님의 레트로의 유니티 C# 게임 프로그래밍 에센스 강의를 듣고 정리한 필기입니다. 😀
🌜 [레트로의 유니티 C# 게임 프로그래밍 에센스] 강의 들으러 가기!


Chapter 3. C# 프로그래밍 : 기본

  • Microsoft C# 공식문서 보러가기
  • 스크립트를 실행시키려면 스크립트를 적용할 hierarchy창의 오브젝트에 스크립트를 드래그 앤 드롭 해준 후 플레이 버튼을 누른다


변수와 함수의 이해

  • 🚓 변수(variable) 🚓
    • 값이 할당되는 이름
      • 그 값을 나중에 다시 쓰기 위해 이름을 붙임.
      • 그 자체로 값이 아닌 값을 가리키는 이름표
      • int gold = 1000 일 때 gold 변수 이름만 기억하면 1000 값을 찾아올 수 있다.
    • 데이터 타입을 써주어야 한다. ex) int, float, bool, string
    • 변수에 할당된 데이터는 게임 도중(런타임)에 얼마든지 접근하여 수정이 가능하다.
  • 🚓 함수(Funcion) 🚓
    • 미리 정해진 동작을 수행하는 코드 묶음
      • 공격 기능을 수행하는 코드를 미리 만들어두면 이 기능을 필요로 하는 여러 곳에서 재사용할 수 있다. 이 코드가 바로 함수가 되는 것.
      // 몬스터를 공격할 때 수행할 기능
      void Attack(Monster target, int damage, int point) 
      {
          playAnimation();
          playSound();
          target.hp = target.hp - damage; // 몬스터의 체력
          exp = exp + point; // 경험치
      }
      
      Monster ork;  // 몬스터 종류 > 오크
      Attack(ork, 5, 30); // 오크는 공격할 때마다 체력이 5밖에 안줄어 들지만 경험치를 많이 준다.
      
      Monster goblin; // 몬스터 종류 > 고블린
      Attack(goblin, 20, 5); // 고블린은 공격할 때마다 체력이 20이나 줄어 쉽지만 경험치를 5로 적게 준다.
      
    • 함수의 Return 반환값
      • 함수 내부에서 일어나는 일은 밖에서 알 수가 없다.
      • 함수를 사용하는 쪽에다가 결과값을 꺼내주는 것이 바로 리턴 값
        • int num = getNumber();
      • 함수는 리턴한 직후 종료 됨.
      • 반환하는 값의 데이터 타입을 앞에 명시해주어야 한다. int getNumber() {...}
        • 리턴 값이 없을땐 void. (굳이 결과를 꺼내주지 않아도 된다.)


콘솔 출력 + C# 기본 변수

C# 스크립트 만들기

  • Project 창에서 Create - C# Script
  • 새 스크립트를 만든 후 열어준다.
  • 스크립트 또한 컴포넌트다. 사용자 지정 컴포넌트나 마찬가지.

  • 스크립트 생성하면 있는 디폴트 코드

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;  
    
    public class NewBehaviourScript : MonoBehaviour 
    {
    
      void Start()
      {
            
      }
    
      void Update()
      {
            
      }
    }
    
    • using UnityEngine;
      • 유니티 엔진에서 제공하는 코드를 사용할 것.
    • public class NewBehaviourScript : MonoBehaviour
      • 모든 컴포넌트들은 MonoBehaviour을 상속 받아야 유니티로부터 메세지를 받을 수 있다.
    • void Start()
      • 게임이 실행될 때 유니티는 가장 먼저 Start 메세지부터 브로드캐스팅 한다.
      • 게임이 실행되자마자 이 컴포넌트가 실행할 내용.
      • 게임 시작될 때 딱 한번 실행 됨.
      • 따라서 컴포넌트를 초기화하는 내용이 주로 들어간다.
    • void Update()
      • 매 프레임마다 계속해서 실행되는 부분
      • 게임 내내 계~속 실행되야 하는 컴포넌트라면 이 부분을 작성해야 함.

콘솔 출력

  • Debug.Log를 사용한다. using한 UnityEngine에 들어있다. 유니티 인터페이스의 Console창에 문자열이 출력된다.
  • Unity Console창에서 Clear on Play하면 이전에 쌓여있던 과거의 로그들이 정리된다.
  • Unity 에서 플레이 버튼을 누르면 Debug.Log로 입력한 문자열들이 콘솔에 출력될 것이다!
Debug.Log("유니티의 Console 창에 출력할 내용");
Debug.Log("Hello, World!");

스크립트를 오브젝트에 붙이기

스크립트를 드래그 해서 Hierarchy창의 해당 오브젝트에, 혹은 오브젝트 클릭 후 Inspector 창의 오브젝트에 대고 드롭 해주면 된다. 스크립트도 컴포넌트기 때문에 이렇게 오브젝트에 붙여주면 스크립트 내용대로 오브젝트가 기능하게 된다.

C# 기본 변수

void Start() 
{
  int age = 23;
  int money = -1000;

  Debug.Log(age);
  Debug.Log(money);

  float height = 169.1234567f; // float 4byte. 소수저

  double pi = 3.14159265359;

  bool isBoy = true;
  bool isGril = false;

  char grade = 'A'; // 한개의 문자
  string movieTitle = "Love Letter"; // 여러개의 문자

  Debug.Log("내 나이는! " + age); // "내 나이는! 23"
  Debug.Log("내가 가진 돈은!" + money); // "내가 가진 돈은! -1000"
  Debug.Log("원주율은! " + pi);  // "원주율은! 3.14159265359"
}
  • Start() 함수 안에 구현했으니 이 스크립트(컴포넌트)가 붙어진 오브젝트들은 플레이 버튼을 눌러 시작하자마자 1회 실행되어 콘솔창에 Debug.Log 내용을 출력할 것

  • 소숫점 가진 실수

    • float
      • 값의 끝에 f를 붙여주어 double과 구분한다.
      • 4byte
        • 소숫점 아래 7자리까지만 정확하다. 그 이상은 근삿값 처리.
    • double
      • 8byte
        • 소숫점 아래 15자리까지 정확하다. 그 이상은 근삿값 처리.
    • 게임은 정확성보다 성능이 더 중요하므로 용량을 더 적게 차지하는 float을 주로 사용한다.

var 키워드

var myName = "So Hyun";
// string var myName = "So Hyun"; 와 같다.

var는 마치 auto같은 것. 대입된 값의 자료형으로 데이터 타입을 추론한다. 추론하여 자동으로 타입을 결정해준다.


사칙연산 + 복합연산자

  • +, -, *, /, %
  • ++num, num++, --num, num--
  • a += 2a = a + 2와 같다.


함수 + 스코프

함수 : 미리 만들어 놓고 필요할 때마다 재사용할 수 있는 코드 묶음

  using System.Collections;
  using System.Collections.Generic;
  using UnityEngine;  

  public class NewBehaviourScript : MonoBehaviour 
  {

    void Start()
    {
        float sizeOfCircle = 30f;

        float radius = GetRadius(sizeOfCircle); // 함수 사용하여 계산

        Debug.Log("원의 사이즈: " + sizeOfCircle + " 원의 반지름: " + radius);
    }

    float GetRadius(float size)
    {
      float pi = 3.14f;

      float tmp = size/pi;

      float radius = Mathf.Sqrt(tmp);

      return radius; // float 리턴
    }
  }

image

  • Mathf
    • UnityEngine에서 제공하는
    • 수학과 관련된 함수들이 미리 들어있는 함수 집합
    • Mathf.Sqrt(tmp);
      • tmp의 제곱근을 구해주는 Mathf 내부의 함수
  • GetRadius 함수 내부의 float radius와 Start 함수 내부의 float radius는 이름만 같을 뿐 별개의 함수다.
  • 둘이 서로 scope가 다르기 때문


형변환 + 조건문

형변환

  using System.Collections;
  using System.Collections.Generic;
  using UnityEngine;  

  public class NewBehaviourScript : MonoBehaviour 
  {

    void Start()
    {
       int height = 170;
       float heightDetail = 170.3f;

        heightDetail = height; // 에러 x 
        height = heightDetail; // 에러 o 

        height = (int)heightDetail; // 잃어버리는 정보 감수하고 직접 명시해 형변환
    }
  }
  • 암묵적 형변환
    • heightDetail = height;
      • 에러 X
      • 잃어버리는 정보가 없으면 컴파일러가 자동 형변환 해준다
        • 170을 float에 넣는건 정보 손실될게 딱히 없음.
    • height = heightDetail;
      • 에러 O
      • heightDetail은 소수점이 있기 때문에 이를 int인 height에 넣으려고 하면 소수점을 버려야 하는 정보 손실이 일어나기 떄문
      • 따라서 이와 같이 정보손실이 일어나는 경우에는 자동 형변환을 해주지 않는다.
        • 프로그래머가 직접 명시적 형변환을 해주어야 함
  • 명시적 형변환
    • height = (int)heightDetail;
      • 소수점 잃어버리는 것을 감수하더라도 int에 넣겠다는 명시적 형변환
      • 170.3f 에서 0.3 소수점이 잘리고 170 값만 height에 대입된다.

조건문

  using System.Collections;
  using System.Collections.Generic;
  using UnityEngine;  

  public class NewBehaviourScript : MonoBehaviour 
  {

    void Start()
    {
       bool isGirl = true;

        if(isGirl != false)
        {
            Debug.Log("나는 여자야"); // 얘 출력
        }
        else
        {
            Debug.Log("나는 남자야");
        }

        isGirl = false;

        if (isGirl == true)
        {
            Debug.Log("나는 여자야"); 
        }
        else
        {
            Debug.Log("나는 남자야");  
            Debug.Log("나는 남자야");  // 얘 출력
        }

        int age = 17;

    }
  }
  • if 조건문에는 확실하게 ture, false bool 값이 나올 수 있는 조건식이 들어가야 한다.
  • ==, !=, >, <, >=, <=
  • and : && (이항. 두 조건식을 엮음)
  • or : || (이항)
  • not : ! (단항)
  • else if : if 조건문이 false임과 동시에 또 새로운 조건문을 따짐
  • else : if문도, else if문도 다 false일 때 해당됨

bool타입을 정수 1, 0 으로 처리했던 C/C++과는 다르게 C#에서는 bool값을 `True`, `False`로 출력한다.


분기문 + 반복문

  using System.Collections;
  using System.Collections.Generic;
  using UnityEngine;  

  public class NewBehaviourScript : MonoBehaviour 
  {

    void Start()
    {
        int year = 2017;

        // 분기문 switch -> if-else문과 일맥상통 한다
        switch(year)   // 기준이 될 변수 
        {
            case 2012:  // year = 2012인 경우
                Debug.Log("레미제라블");
                break; 
            case 2016:  // year = 2016인 경우
                Debug.Log("곡성");
                break;
            case 2017:  // year = 2017인 경우
                Debug.Log("트랜스포머5");
                break;
            default:    // 어떠한 케이스에도 해당되지 않을 때.
                Debug.Log("년도가 해당사항 없음");
                break;
        }

        // 반복문
        for (int i = 0; i < 10; i = i + 2)      // 1. for    i = 0, 2, 4, 6, 8 이렇게 5번 반복
        {
            Debug.Log("현재 순번: " + i);
        }
        Debug.Log("루프 끝");

        bool isShot = false;
        int index = 0;
        int luckyNumber = 4;

        while (isShot == false)           // 2. while
        {
            index = index + 1;
            if (index == luckyNumber)
            {
                Debug.Log("총알에 맞았다!");
                isShot = true;
            }
            else
            {
                Debug.Log("총알에 맞지 않았다.");
            }
        }

        do    // 3. do-while. 처음 한번은 무조건 실행한다. while문은 조건문에 처음부터 맞지 않으면 아예 실행되지 않을 수도 있다.
        {
            Debug.Log("Do-while");
        } while (isShot == false);
    }
  }


배열

  • new 키워드를 이용하여 배열을 생성한다.
  • C++와는 다르게 배열 이름의 값을 바꿀 수 있나보다.
    int[] scores = new int[10];
    scores = new int[20]; //  이렇게 ! C++에서는 안되는데.. 
    
  using System.Collections;
  using System.Collections.Generic;
  using UnityEngine;  

  public class NewBehaviourScript : MonoBehaviour 
  {

    void Start()
    {
        int[] scores = new int[10];

        scores[0] = 90;
        scores[1] = 45;
        scores[2] = 60;
        scores[3] = 70;
        scores[4] = 56;
        scores[5] = 80;
        scores[6] = 90;
        scores[7] = 100;
        scores[8] = 45;
        scores[9] = 14;

        // scores[10] = 30;  이건 out of index

        for(int i=0; i<10; i++)
        {
            Debug.Log("학생 " + i + "번째의 점수 "+ scores[i]);
        }

        scores = new int[20];  // 이렇게 하면 기존 원소들 싹~~~ 날라가고 새로운 20개 크기의 빈 배열이 할당됨.
    }
  }


클래스와 오브젝트 ⭐

개념

사과의 이상적이고 추상적인 모습(= 클래스)이 있다. 살짝 하트 모양이고 빨갛고 초록 꼭다리를 가지고 있는 등등 이런 모습. 그러나 현실 세계의 사과들(오브젝트)은 이 이상적인 모습에서 조금씩 다 다르다. 좀 멍든 사과라던가 초록색이라던가.. 그러나 대략적으로 사과의 추상적인 모습과 비교하여 이것들이 모두 사과임을 알 수 있다.

  • 프로그래밍에서의 추상화(= `클래스`)구체화(= `오브젝트`)
    • 클래스를 만들어 추상화를 해둔 후 오브젝트로 찍어내어 구체화 시키는 방식.
    • 클래스는 붕어빵틀, 실제로 현실 세계에서 만들어진 붕어빵들은 오브젝트라고 생각하면 된다.
  • 클래스
    • 이상적인 세계에서 존재하는 원형, 단 하나의 기준
    • 실제로는 존재하지 않는다.
    • 가장 중요한 특성만 개략적으로 알려준다.
    • 구체적인 수치가 없다.
    • ex) Dog라는 클래스
      • 개의 추상적인 특징들을 묶어 담고 있음
  • 오브젝트
    • 실제 존재하는 것들
    • 자기 자신을 스스로 챙길 수 있다.
    • 하나의 온전한 단위
    • 하나의 원본에서 파생 되어도, 서로 구분 가능하다.
    • ex) Dog라는 클래스로 뽀삐, 몽자, 식빵이 라는 이름을 가진 각각의 실제 강아지들을 만든다. 이 세 마리의 강아지들은 공통적으로 Dog에서 파생됐지만 서로 연관이 없는 독립적인 개체이며 서로 구분된다..

코드


using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Animal     // Animal 클래스
{
    public string name;  // 동물은 이름을 가지고 있다. public을 꼭 붙어주어야 외부에서도 보고 사용할 수 있다. (디폴트 private)
    public string sound; // 동물은 소리를 가지고 있다. public을 꼭 붙어주어야 외부에서도 보고 사용할 수 있다. (디폴트 private)
    public float weight; // 동물은 무게를 가지고 있다. public을 꼭 붙어주어야 외부에서도 보고 사용할 수 있다. (디폴트 private)
}

public class HelloClass : MonoBehaviour         
{
    void Start()
    {
        /* 각각의 동물 객체마다 name, sound, weight 속성을 가지고 있지만 값은 전부 다 다름.*/

        /*객체들은 서로 구분되고 독립적인 존재들*/

        Animal jack = new Animal();     // Animal 객체 "jack"을 찍어내어 탄생시킴
        jack.name = "JACK";     // name이 public이 아니였다면 에러
        jack.sound = "Bark";   // sound가 public이 아니였다면 에러
        jack.weight = 4.5f;   // weight이 public이 아니였다면 에러

        Animal annie = new Animal();     // Animal 객체 "annie"을 찍어내어 탄생시킴
        annie.name = "ANNIE";
        annie.sound = "Wee";
        annie.weight = 0.8f;

        Animal nate = new Animal();     // Animal 객체 "nate"을 찍어내어 탄생시킴
        nate.name = "NATE";
        nate.sound = "NYaa";
        nate.weight = 1.2f;

        nate = jack;         // jack이 가지고 있는 정보를 nate에 덮어 씌운다.
        nate.name = "JIMMY";  // ⭐ jack의 이름까지도 바뀐다! ⭐

        Debug.Log(jack.name);
        Debug.Log(nate.name);
    }
}

변수는 값을 가리키는 이름표임을 기억하자.

image

nate = jack;         // jack이 가지고 있는 정보를 nate에 덮어 씌운다.
nate.name = "JIMMY"; // ⭐ jack의 이름까지도 바뀐다! ⭐
  • 객체들은 이름이 없는데 우리가 변수로 이름표를 붙여준 것 뿐이다.
  • nate = jack
    • jack이 가지고 있는 정보를 nate에 덮어 씌운다.
    • 이제 두 변수 nate와 jack가 기존의 jack이 혼자 가리키던 그 동일한 인스턴스를 가리키게 된다.
    • 기존에 jack이 가리키던 인스턴스는 이름표(변수)가 2개가 됨. jack, nate 두개로 접근 가능해짐
      • 즉 이제 하나의 인스턴스를 두 변수가 가리키게 됨.
    • 기존에 nate가 가리키던 인스턴스는 이름표(변수)가 없어져 접근할 방법이 없는 미아(가비지)가 되버린다.
      • C++에서는 이 같은 가비지들이 메모리 누수 문제를 일으키지 않도록 프로그래머가 직접 관리해야 했지만
      • Java와 C#에서는 가비지 콜렉터가 알아서 작동하여 이런 가비지들을 정리하고 메모리 해제시켜 주므로 프로그래머가 신경 쓸 필요가 없다.
    • 변수는 3개지만 오브젝트는 2개인 현상이 되어버림
  • nate.name = "JIMMY"
    • 따라서 jack.name 또한 “JIMMY”가 된다.

Call by Reference

기존에 미리 만들어져 존재하는 것을 가져와서 쓴다는 개념.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class jump : MonoBehaviour
{

    public Rigidbody rb;    

    void Start()
    {
        rb.AddForce(0, 1000, 0);
    }

    void Update()
    {
        
    }
}
  • public Rigidbody rb;
    • 변수오브젝트 그 자체가 아니라 오브젝트를 가리키는 이름표라는 것을 기억하자.
    • public으로 해놓으면 유니티 인터페이스에서 Rigidbody 컴포넌트 객체를 드래그 앤 드롭할 수 있는 슬롯이 열린다.
      • 이 슬롯에 오브젝트에 실제로 현재 붙어있는 Rigidbody 컴포넌트를 드래그 앤 드롭하면 이제 rb라는 이름표가 가리키는 이 컴포넌트에 접근할 수 있게 된다.
  • rb.AddForce(0, 1000, 0);
    • 이제 rb 변수(이름표)로 실존하는 이 컴포넌트를 접근할 수 있다.
    • rb라는 이름으로 슬롯에 드래그 앤 드롭한 실존하는 이 컴포넌트에 접근하여 AddForce 기능을 하게끔 함.


🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우 
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄

맨 위로 이동하기

Unity Lesson 1 카테고리 내 다른 글 보러가기

댓글 남기기