본문 바로가기
C#

[C#]유니티 스크립팅 완전 정복: 게임에 생명을 불어넣는 핵심 문법✨

by 다다면체 2025. 4. 8.
728x90
반응형

안녕하세요, 게임 개발 여정에 다시 오신 것을 환영합니다! 👋 지난번 [유니티 설치 및 인터페이스 기초](이전 글 링크 삽입) 글에서 유니티라는 강력한 도구와 친해지는 시간을 가졌다면, 이제부터는 그 도구에 생명을 불어넣는 마법, 바로 C# 스크립팅의 세계로 함께 떠나보겠습니다.

유니티에서 C# 스크립트는 단순히 코드를 작성하는 것을 넘어, 여러분의 게임 오브젝트(캐릭터, 몬스터, 아이템 등)가 스스로 생각하고 움직이며 상호작용하게 만드는 핵심 열쇠입니다. 🔑 처음에는 조금 낯설 수 있지만, 이 글을 통해 C#의 기본 문법부터 유니티에서 실제로 활용하는 방법까지 차근차근 정복해 나갈 수 있도록 도와드릴게요. 자, 이제 코딩의 즐거움에 빠져볼 준비 되셨나요? 😉

반응형

1. C# 기본 문법 마스터하기: 게임 개발의 언어 배우기 💻

C#은 유니티가 공식적으로 지원하는 메인 스크립팅 언어입니다. 현대적이고 강력하며 배우기 비교적 쉬운 언어죠. 게임 개발에 필요한 기본적인 C# 문법 요소들을 먼저 살펴봅시다.

✅ 변수와 자료형: 데이터 보관함 만들기

  • 변수(Variable): 데이터를 저장하는 '이름표가 붙은 상자'라고 생각하면 쉽습니다. 점수, 체력, 이름 등 게임 내 다양한 정보를 저장하는 데 사용됩니다.
  • 자료형(Data Type): 변수라는 상자에 어떤 종류의 데이터를 담을지 지정하는 규칙입니다.
    • int: 정수 (소수점 없는 숫자)를 저장합니다. 예: 플레이어 점수(int score = 100;), 남은 총알 수(int ammoCount = 30;)
    • float: 소수점 있는 숫자를 저장합니다. 예: 이동 속도(float moveSpeed = 5.5f;), 캐릭터 체력(float currentHealth = 85.0f;) (값 뒤에 f를 붙여 float임을 명시합니다.)
    • bool: 참(true) 또는 거짓(false) 값을 저장합니다. 예: 생존 여부(bool isAlive = true;), 문 열림 상태(bool isDoorOpen = false;)
    • string: 문자열 (텍스트)을 저장합니다. 예: 플레이어 이름(string playerName = "용감한 개발자";), 아이템 설명(string itemDescription = "강력한 검입니다.";) (텍스트는 큰따옴표 ""로 감싸줍니다.)
    • 왜 중요할까요? 정확한 자료형을 사용해야 메모리를 효율적으로 사용하고 예상치 못한 오류(예: 정수만 필요한 곳에 소수점 계산)를 방지할 수 있습니다.

✅ 연산자: 데이터 계산하고 비교하기

  • 산술 연산자: 더하기(+), 빼기(-), 곱하기(*), 나누기(/), 나머지(%) 등 기본적인 수학 계산을 합니다. 예: score = score + 10;, totalDamage = attackPower * 1.5f;
  • 비교 연산자: 두 값의 관계를 비교하여 true 또는 false 결과를 반환합니다. 같다(==), 같지 않다(!=), 크다(>), 작다(<), 크거나 같다(>=), 작거나 같다(<=). 예: if (health <= 0) { ... }, if (ammoCount == 0) { ... }
  • 논리 연산자: 여러 조건을 조합할 때 사용합니다. 그리고(&&), 또는(||), 아니다(!). 예: if (isAlive && canMove) { ... } (살아있고 움직일 수 있다면), if (isGrounded || isJumping) { ... } (땅에 있거나 점프 중이라면)

✅ 조건문: 상황에 따라 다른 행동하기 (if, else if, else)

특정 조건이 참(true)일 때만 코드를 실행하거나, 조건에 따라 다른 코드를 실행하게 합니다.

int playerLevel = 5;

if (playerLevel >= 10) {
    Debug.Log("고급 스킬 사용 가능!"); // Debug.Log는 유니티 콘솔에 메시지를 출력합니다. (잠시 후 자세히!)
} else if (playerLevel >= 5) {
    Debug.Log("중급 스킬 사용 가능!");
} else {
    Debug.Log("초급 스킬만 사용 가능합니다.");
}
// 출력 결과: "중급 스킬 사용 가능!"

✅ 반복문: 같은 일 반복하기 (for, while, foreach)

  • for: 정해진 횟수만큼 코드를 반복 실행할 때 유용합니다. (예: 몬스터 5마리 생성)
  • while: 특정 조건이 참(true)인 동안 코드를 계속 반복 실행합니다. (예: 게임 오버 상태가 아닐 동안 계속 게임 로직 실행)
  • foreach: 배열이나 리스트 같은 데이터 집합의 모든 항목에 대해 코드를 반복 실행할 때 편리합니다. (예: 인벤토리의 모든 아이템 효과 적용)
// for문 예시: 0부터 4까지 5번 출력
for (int i = 0; i < 5; i++) {
    Debug.Log("현재 숫자: " + i);
}

// while문 예시: (실제 게임에서는 Update 함수 등을 더 많이 사용)
bool isLoading = true;
float loadProgress = 0f;
while (isLoading) {
    loadProgress += 0.1f;
    Debug.Log("로딩 중... " + loadProgress * 100 + "%");
    if (loadProgress >= 1.0f) {
        isLoading = false; // 로딩 완료 조건
    }
    // 실제 게임에서는 프레임 대기 등 필요
}

✅ 함수 (메서드): 코드 묶음 만들고 재사용하기

특정 작업을 수행하는 코드 블록을 만들어 이름을 붙여두고, 필요할 때마다 이름만 불러서 실행할 수 있습니다. 코드 중복을 줄이고 프로그램을 구조화하는 데 필수적입니다.

// 함수 정의 (Definition)
void PrintPlayerInfo() { // void는 반환값이 없음을 의미
    string playerName = "모험가";
    int level = 3;
    Debug.Log("이름: " + playerName + ", 레벨: " + level);
}

// 함수 호출 (Call) - 예시 (Start 함수 등에서 호출)
void Start() {
    PrintPlayerInfo(); // 함수 이름으로 호출!
    PrintPlayerInfo(); // 여러 번 재사용 가능
}

2. 유니티에서 C# 스크립트 사용하기: 엔진과 코드의 만남 🤝

이제 C# 기본 문법을 알았으니, 유니티 안에서 실제로 스크립트를 만들어 적용해 봅시다.

  1. 새 C# 스크립트 생성:
    • 유니티 에디터의 프로젝트(Project) 창에서 마우스 오른쪽 클릭 > Create > C# Script 선택.
    • 스크립트 이름을 지정합니다. (예: PlayerMovement) 주의: 이름 변경 시 유니티 에디터 내에서 변경해야 클래스 이름과 파일 이름이 일치합니다. 공백이나 특수문자는 피하는 것이 좋습니다.
  2. 스크립트를 게임 오브젝트에 연결 (컴포넌트 화):
    • 스크립트는 그 자체로 실행되지 않고, **게임 오브젝트(GameObject)**에 컴포넌트(Component) 형태로 부착되어야 작동합니다.
    • 하이어라키(Hierarchy) 창에서 스크립트를 적용할 게임 오브젝트를 선택합니다. (예: 'Player' 오브젝트)
    • 프로젝트 창에서 생성한 C# 스크립트 파일을 드래그하여, 선택된 게임 오브젝트의 인스펙터(Inspector) 창으로 놓습니다.
    • 또는, 인스펙터 창 하단의 Add Component 버튼을 누르고 스크립트 이름을 검색하여 추가할 수도 있습니다.
    • 핵심: 이제 이 스크립트는 해당 게임 오브젝트의 '두뇌' 또는 '행동 지침서' 역할을 하게 됩니다! 🧠
  3. 유니티의 기본 이벤트 함수: Start와 Update
    • 방금 생성한 스크립트를 열어보면 Start() 와 Update() 라는 함수가 미리 정의되어 있을 겁니다. 이것들은 유니티가 특정 시점에 자동으로 호출해주는 특별한 함수(이벤트 함수 또는 라이프사이클 함수)입니다.
      • void Start(): 게임이 시작될 때 단 한 번 호출됩니다. 주로 변수 초기화, 다른 오브젝트 찾기 등 초기 설정 코드를 넣습니다.
      • void Update(): 게임의 매 프레임마다 호출됩니다. (컴퓨터 성능에 따라 초당 수십~수백 번) 플레이어 입력 처리, 오브젝트 이동, 시간 경과에 따른 변화 등 지속적인 로직 처리에 사용됩니다. 매우 중요! ⭐
  4. 디버깅의 필수! Debug.Log 사용하기:
    • 코드에 문제가 있거나, 변수 값이 어떻게 변하는지 확인하고 싶을 때 Debug.Log() 함수를 사용합니다. 괄호 안의 내용을 유니티 에디터의 콘솔(Console) 창에 출력해줍니다.
    • 예: Debug.Log("현재 체력: " + currentHealth);
    • 개발자의 눈과 귀가 되어주는 고마운 기능이니 꼭 익숙해지세요! 👀👂

3. 변수와 속성: 데이터 관리와 외부 소통의 비밀 🔑

스크립트 안의 데이터를 어떻게 관리하고, 유니티 에디터(특히 인스펙터 창)와 어떻게 소통하는지 알아봅시다.

  • 접근 제한자 (public vs private):
    • public: '공개'를 의미합니다. 스크립트 외부에서도 접근 가능하며, 가장 큰 특징은 유니티 인스펙터 창에 해당 변수가 노출되어 값을 직접 수정할 수 있게 된다는 점입니다! 🎉 디자이너나 다른 개발자가 코드를 열지 않고도 쉽게 값을 조절할 수 있게 해주죠.
    • private: '비공개'를 의미합니다. 해당 변수는 오직 그 스크립트 내부에서만 접근 가능하며, 인스PECTOR 창에 노출되지 않습니다. 기본적으로 변수는 private으로 선언하는 것이 코드의 안정성과 구조를 위해 권장됩니다 (캡슐화). 다른 곳에서 함부로 값을 바꾸는 것을 방지하죠.
    • 아무것도 안 쓰면? 기본적으로 private으로 간주됩니다.
using UnityEngine; // 유니티 기능을 사용하기 위해 필요

public class PlayerStats : MonoBehaviour { // MonoBehaviour를 상속받아야 유니티 컴포넌트로 작동
    public string playerName = "Hero"; // public: 인스펙터에 노출됨!
    public float moveSpeed = 5.0f;    // public: 인스펙터에서 속도 조절 가능
    private int score = 0;            // private: 외부 접근 불가, 인스펙터에 안 보임
    private float health = 100f;      // private

    void Start() {
        Debug.Log(playerName + "의 게임 시작!");
        // score나 health는 이 스크립트 안에서만 사용
    }

    void Update() {
        // 예시: 인스펙터에서 조정한 moveSpeed 값 사용
        // transform.Translate(Vector3.forward * moveSpeed * Time.deltaTime);
    }
}
  • [SerializeField] 속성: private 변수를 인스펙터 창에 노출시키고 싶지만, 외부 스크립트에서의 직접 접근은 막고 싶을 때 사용합니다. private 앞에 [SerializeField]를 붙여주면 됩니다. 좋은 습관 중 하나! 👍
[SerializeField] private float attackPower = 10f; // private이지만 인스펙터에는 보임!

4. 함수 (메서드) 심화 활용: 코드 재사용과 게임 로직 구현 ⚙️

함수를 좀 더 유용하게 사용하는 방법을 알아봅시다.

  • 매개변수(Parameter)를 받는 함수: 함수를 호출할 때 데이터를 전달하여, 함수가 그 데이터를 가지고 작업을 수행하게 합니다. 함수를 더욱 유연하게 만들어 줍니다.
public class Enemy : MonoBehaviour {
    private float health = 50f;

    // 데미지를 받는 함수 (매개변수로 받은 데미지 양만큼 체력 감소)
    public void TakeDamage(float damageAmount) {
        health -= damageAmount;
        Debug.Log("적 체력: " + health);
        if (health <= 0) {
            Die(); // 체력이 0 이하면 죽는 함수 호출
        }
    }

    private void Die() {
        Debug.Log("적이 쓰러졌다!");
        // 여기에 적 제거 로직 추가 (예: 오브젝트 비활성화)
        // gameObject.SetActive(false);
    }
}

// 다른 스크립트에서 이 함수를 호출하는 예시
// Enemy enemyScript = targetEnemy.GetComponent<Enemy>(); // 적 오브젝트에서 Enemy 스크립트 가져오기
// enemyScript.TakeDamage(20f); // 20만큼 데미지를 입힘
  • 값을 반환(Return)하는 함수: 함수가 작업을 수행한 후, 그 결과를 호출한 곳으로 돌려줍니다. 함수의 계산 결과를 다른 곳에서 활용할 수 있게 해줍니다. (반환값이 없으면 void 사용)
public class GameController : MonoBehaviour {
    private int score = 0;
    private bool isGameOver = false;

    public void AddScore(int amount) {
        score += amount;
        Debug.Log("점수 획득! 현재 점수: " + score);
    }

    // 현재 게임오버 상태인지 알려주는 함수 (bool 값 반환)
    public bool IsGameOver() {
        // 예시: 플레이어 체력이 0 이하면 게임오버라고 가정
        // if (playerHealth <= 0) isGameOver = true;
        return isGameOver; // isGameOver 변수의 현재 값을 반환
    }

    void Update() {
        if (IsGameOver()) { // 반환된 bool 값으로 조건 확인
            Debug.Log("게임 오버!");
            // 게임 오버 처리 로직...
        }
    }
}

마무리하며 & 다음 단계 예고 🏁

와우! 여기까지 오셨다면 C#의 기본적인 문법부터 유니티에서 스크립트를 생성하고 게임 오브젝트에 적용하는 방법, 그리고 변수와 함수를 활용하는 핵심적인 내용까지 배우신 겁니다! 🎉 Debug.Log로 궁금한 점을 확인하고, public 변수를 인스펙터에서 조절하며 코드가 게임 세상과 상호작용하는 것을 직접 경험해보세요.

 

처음에는 모든 것이 낯설고 어렵게 느껴질 수 있지만, 작은 기능이라도 직접 만들어보는 것이 중요합니다. 예를 들어, 오늘 배운 내용으로 간단하게 "플레이어 체력 관리" 스크립트나 "클릭하면 점수가 오르는" 스크립트를 만들어보는 것은 어떨까요?

다음 시간에는 유니티 개발의 또 다른 핵심 축인 '게임 오브젝트와 컴포넌트'에 대해 더 깊이 파고들어 보겠습니다. 게임 오브젝트는 무엇이고, 컴포넌트는 어떻게 조합하여 원하는 기능을 만들어내는지 자세히 알아볼 예정이니 많이 기대해주세요! 😊

 

💬 오늘 배운 C# 문법으로 어떤 간단한 게임 기능을 만들어보고 싶으신가요? 댓글로 여러분의 아이디어를 공유해주세요!

728x90
반응형