카테고리 없음

Unity 본캠프 33일차

julesnow 2025. 5. 23. 03:22

ଘ(੭ 목차 )੭

더보기

1️⃣ 꿀팁

  ੈ ✩ Git 커밋 태그

  ੈ ✩ Rider 단축키

2️⃣ 코드 공부

3️⃣ 트러블 슈팅

  ੈ ✩ 문. 시. 해. 알.  

    ① 싱글톤 Instance 오류

    ② PlayerCondition.cs 배고픔 감소 오류

4️⃣ 추가로 알게된 점

5️⃣ 복습

6️⃣  레벨업 세션

 


1️⃣ 꿀팁

 

  ੈ ✩ Git 커밋 태그 (Conventional Commits)

feat 기능 추가 feat: Add enemy chase system
fix 버그 수정 fix: Resolve null reference on player
refactor 리팩토링 (동작 안 바꾸고 코드만 개선) refactor: Clean up score logic
style 코드 포맷, 세미콜론, 들여쓰기 style: Apply consistent spacing
docs 문서 작업 (README 등) docs: Update project intro
test 테스트 코드 추가 test: Add enemy AI test case
chore 기타 변경 (빌드 설정, 패키지 업데이트 등) chore: Update Unity version
perf 성능 개선 perf: Optimize draw calls
ci CI 설정 변경 ci: Fix GitHub Actions workflow
build 빌드 관련 수정 build: Change build target to Android
asset 🎨 에셋 추가/수정 (씬, 사운드, 이미지 등) asset: Add mountain background and river prefab

 

  ੈ ✩ Rider 단축키

  • command option L      -> 줄 정리
  • command D                 -> 아래로 복붙
  • command 누르고 클릭  -> 자동 이동 ~ ଘ

 

  ੈ ✩ 그 외 자주 쓰는 Rider 단축키 (Mac)

자동 완성 ⌃ + Space 코드 자동 완성 (Ctrl + Space)
이름 변경 (변수, 메서드 등) Shift + F6 리팩토링 이름 변경
전체 탐색 (파일, 클래스 등) Double Shift 모든 항목 검색
메서드 간 빠르게 이동 ⌘ + F12 현재 파일 내 심볼 구조 보기
최근 수정한 코드로 이동 ⌘ + Shift + Backspace 마지막 편집 위치로 점프

2️⃣  코드 공부

 

  ੈ ✩  영어 문장 쓰듯이 코드로 바꿔보기

"플레이어가 앞으로 간다" player.Move(forward);
"적이 나를 쏜다" enemy.Shoot(player);
"점수가 100보다 크면 승리" if(score > 100) Win();
 

  ੈ ✩  연습 방법

  • 코드 짤 때 "영어 문장처럼 말로 먼저 말해보고 → 코드로 바꾸기" 연습해 보자
  • 누가 → 뭘 → 어떻게를 기준

 

"고양이가 플레이어를 따라온다"

 

public class Cat

{

      public void Follow(Player player)

      {

            Debug.Log("Cat follows player joyfully.");

      }


3️⃣  트러블 슈팅

 ‧₊˚ 싱글톤 Instance 오류 ‧₊˚ 

 

  ੈ ✩  문제 (Problem)

Player.cs에서 CharacterManager.Instance.Player = this;를 호출하려 했으나,
Instance가 정의되지 않았다는 컴파일 오류 발생

  ੈ ✩  시도한 점 (What was tried)

  • CharacterManager 클래스 내부를 확인
  • public static CharacterManager InstanceManager 라는 속성이 정의되어 있었고
  • Instance라는 이름은 없음 → 네이밍 불일치 확인

  ੈ ✩  해결 (Solution)

  1. CharacterManager.cs 내부의 싱글톤 속성 이름을


       ➤


       수정하여 Player.cs에서 접근 가능한 형태로 통일


  ੈ ✩ 알게 된 점 (What was learned)

  • 싱글톤 패턴의 Instance 명칭은 반드시 통일되어야 외부에서 사용할 수 있음
  • 이름 하나 다르게 썼다고 코드 전체가 작동하지 않음 → 네이밍 규칙 매우 중요!

 ‧₊˚ PlayerCondition.cs 배고픔 감소 오류 ‧₊˚

 

 

  ੈ ✩ 문제 (Problem)

배고픔 (hunger)이 줄지 않아 체력 (helath) 도 줄지 않음 !!

ੈ ✩  시도한 점 (What was tried)

  • Subtract() 메서드와 noHungerHealthDecay 값 확인
  • hunger.curValue <= 0f 조건 사용

 


  ੈ ✩  해결 (Solution)

  • hunger, health 모두 == 0f → <= 0f로 변경
    (float 값은 정확히 0이 되기 어려워 == 비교는 실패함)
  • 결과적으로 체력도 정상적으로 감소했고 Die()가 호출됨

  ੈ ✩ 알게 된 점 (What was learned)

  • float 비교는 <=, >= 같은 안전한 비교 방식 사용해야 함 !
  • ==로 비교하면 아주 작은 소수점 오차 때문에 조건문이 통과되지 않음

4️⃣  new realization

  • Awake() 내부의 싱글톤 초기화 조건은 반드시 null 체크 → 할당 → 중복 제거 순으로 작성해야 함
  • new GameObject().AddComponent<T>() 방식의 싱글톤은 씬에 오브젝트가 없을 때 예외적 생성 방식임

 

  ੈ ✩ 1. 방어 코드 (Defensive Code)

예상치 못한 상황에서도 프로그램이 멈추지 않고 정상 작동하도록 막아주는 코드


 ‧₊˚ 예시 1: 오브젝트가 없을 수도 있음

if (enemy != null)
 
{
 
      enemy.TakeDamage(10);
 
}
적이 죽어서 enemy가 없어졌는데도 TakeDamage()를 호출하면 에러!
null 검사로 방어(예외 예방)

 ‧₊˚ 예시 2: UI 버튼 연결 안 돼 있을 때

 
if (myButton != null)
 
{
 
      myButton.onClick.AddListener(DoSomething);
 
}

‧₊˚ 비유:

“문이 열려 있을 수도, 잠겨 있을 수도 있다”
→ 무작정 열지 말고, 먼저 문이 있는지 확인하고 여는 것


 
ੈ ✩ 2. Fallback 코드

다운타임 방지 코드
  • 하이러키에 오브젝트가 없어도 강제로 만들어서 진행되게 하는 코드
  • 프로그램 멈추지 않게 대체 경로를 마련하는 예외적 대처 코드

 
ੈ ✩
 3. 싱글톤(Singleton)

‧₊˚ 의미

프로젝트 전체에서 단 하나만 존재하는 객체를 만들고, 어디서든 접근 가능하게 하는 디자인 패턴

예: GameManager, UIManager, AudioManager

 

public class GameManager : MonoBehaviour
 
{
 
      public static GameManager Instance;
 
   
      void Awake()
 
      {
            if (Instance == null) Instance = this;
 
            else Destroy(gameObject);
 
      }
 
}
 
 

➡ 나중에 어디서든 GameManager.Instance로 접근 가능!


‧₊˚ 비유

학교에 딱 한 분 계신 교장선생님
누구나 “교장선생님.Instance”로 부를 수 있다.
2명 이상 존재하면 안 되니까 조건문으로 방어


  ੈ ✩ 4. new GameObject().AddComponent<T>() 싱글톤 예외 생성

 
if (_instance == null)
 
{
 
      _instance = new GameObject("Manager").AddComponent<MyManager>();
 
}
 

‧₊˚ 의미

  • 씬에 MyManager 오브젝트가 없을 수도 있음
  • 그러면 코드가 에러 나니까 → 강제로 새 게임오브젝트 만들고, 컴포넌트 붙임!

‧₊˚ 비유

“교장선생님이 학교에 안 왔으면 → 새로 불러와서 자리 앉히는 것”

➡ 게임에 꼭 필요한 매니저가 빠져 있어도 게임이 안 멈추고 진행되게 만드는 방어 코드

 


5️⃣ 복습

 

  ੈ ✩용어 정리

클래스 Cat, Player 설계도, 타입
객체 cat, player 내용물 (인스턴스)
메서드 Follow() 행동, 동작
매개변수 (Player player) 메서드에게 전달되는 값
생성 new Cat() 설계도로 실제 물건 만들기
 

ੈ ✩ 1. class = 설계도

  • 클래스는 보통 PascalCase (= 단어 첫 글자 모두 대문자)
  • 설계도니까 눈에 띄게 대문자로 쓰는 게 규칙
 
public class Player { ... }
public class Cat { ... }

 

 


ੈ ✩ 2. 객체, 변수

  • 변수, 매개변수는 camelCase (= 첫 글자 소문자)
Player player = new Player();
Cat myCat =
new Cat();

 

‧₊˚ 예시

 public PlayerController controller;

  • PlayerController는 클래스(설계도)
  • controller는 이 설계도로 만든 변수
PlayerController 클래스 타입의 controller라는 객체 변수를 선언함

ੈ ✩ 3. new Cat() → class로 객체 생성

  • new -> class를 바탕으로 객체를 만드는 키워드
 
Cat cat = new Cat();      // Cat class로 cat이라는 고양이 한 마리 생성

ੈ ✩ 4. Method

  • 메서드 이름도 보통 PascalCase로 씀 (대문자 시작)
 
public void Follow(Player player) { ... }
public void Eat() { ... }

ੈ ✩ 5. 매개변수

  • 함수 괄호 안에 들어가는 값이 매개변수
 
public void Follow(Player player) { ... }
Player 매개변수 타입 (클래스)
player 매개변수 이름 (소문자 변수)
  • 메서드는 "명확한 동작을 나타내는 "명사형 동사" 이기 때문에
    → Jump(), TakeDamage()처럼 PascalCase(대문자)로 작성

‧₊˚ Usage Timing 

다른 객체의 정보를 함수 안으로 가져와야 할 때
“고양이가 플레이어를 따라가게 하려면 → 플레이어의 위치를 알아야 함”

 

ੈ ✩ 6. 모든 변수는 소문자인가? -> 상수(Constant) 제외하고!

클래스 대문자 시작 Player, Cat
변수/객체 소문자 시작 player, cat, score
매개변수 소문자 시작 player, damage
함수/메서드 대문자 시작 Follow(), TakeDamage()
 
일반적으로는
  • 클래스/메서드 → PascalCase (대문자 시작)
  • 변수/매개변수 → camelCase (소문자 시작)
  • 상수(Constant)는 예외적으로 대문자 + 언더바

 

‧₊˚ 예시

const int MAX_HEALTH = 100;

‧₊˚  요약

클래스 설계도 = 대문자
객체/변수 소문자로 시작
new Cat() 클래스로 객체 만들기
Follow() 동작(함수) = 대문자
player 매개변수(소문자 변수)
모든 변수는 소문자? 일반적으로 맞음

 


(공사 중..)

ੈ ✩ 메서드와 함수

함수(function) 단독으로 존재 가능함 (C언어 같은 데서)
메서드(method) 클래스 안에 들어 있는 함수 -> C#은 다 method임!
 
C#에서는 모든 함수가 클래스 안에 있어야 하므로 → 전부 "메서드"라고 부름.
 

‧₊˚ 예시

public void Jump() { ... }

 

ੈ ✩ 예제

플레이어가 적을 공격하면 적 체력이 줄어든다.
 
Player 플레이어 클래스 (공격 주체)
Enemy 적 클래스 (체력 보유)
Attack() 플레이어가 적을 공격하는 method
TakeDamage() 적이 데미지를 받는 method
enemy 매개변수: 공격당하는 대상
amount 매개변수: 데미지 양
 

ੈ ✩ GetComponent

현재 오브젝트에 붙어 있는 컴포넌트를 코드에서 가져오고 싶을 때

 
Rigidbody rb = GetComponent<Rigidbody>();

→ 이 스크립트가 붙어 있는 오브젝트에서 Rigidbody를 찾아서 가져옴.

 

📚 예:

"이 게임오브젝트에 리지드바디 붙어 있으면 가져와서 쓸게~"


✅ | 와 || 차이

` ` OR (Bitwise)
`   `
 

실전에서 조건문에 쓰는 건 ||

 
if (isJumping || isFalling) { Debug.Log("공중에 있음"); }

✅ 싱글톤(Singleton) 초등학생도 이해하게

📚 비유:

게임에 딱 하나만 존재하는 사령탑

예: 게임 매니저는 하나만 있어야 함 (중복되면 망함)


🔧 싱글톤 코드 예시

 
public class GameManager : MonoBehaviour { public static GameManager Instance; private void Awake() { if (Instance == null) { Instance = this; // 내가 대표야! DontDestroyOnLoad(gameObject); // 씬 바껴도 살아있음 } else { Destroy(gameObject); // 중복이면 제거 } } }

쓰는 법:

GameManager.Instance.DoSomething();

 

클래스 Player, Enemy 설계도 (대문자 시작)
변수 player, enemy 실제 객체 이름 (소문자 시작)
메서드 Jump(), Attack() 동작. 클래스 안에서만 사용 (대문자 시작)
매개변수 (Enemy enemy) 메서드가 받는 값 (소문자 시작)
new Cat() Cat 설계도로 새 객체 만듦  
GetComponent<T>() 오브젝트에 붙은 컴포넌트 가져오기  
`   `
싱글톤 GameManager.Instance 프로그램에 딱 하나만 존재하게

6️⃣  레벨업 세션

 1. 고정형 데이터 vs 변동형 데이터

고정형 데이터 (Static Data) 불변의 기준 데이터를 의미. 매번 새로 생성할 필요 없이 공유됨. 아이템 이름, 등급, 설명 등
변동형 데이터 (Dynamic Data) 게임 도중 변경되며 유저별로 다른 데이터를 의미. 현재 내구도, 강화 수치, 소유 여부 등
 

고정형 정보는 기준 테이블로 따로 저장하고, 변동형 정보는 인스턴스 단위로 관리하는 것이 핵심!


 2. JSON 직렬화 & 기획 테이블 관리

🔹 직렬화(Serialization)란?

  • 객체를 저장 가능한 포맷(JSON, Binary 등)으로 변환하는 과정
  • 단순 저장/로드가 아니라 데이터의 구조화가 핵심!
  • 게임에서는 주로 설정 파일 저장, 서버와의 통신, 데이터 테이블 로딩 등에 사용됨

 기획 테이블 → JSON 구조화 방식

 작업 흐름

  1. Excel에서 기획 데이터 작성
    • 변수명 / 자료구조 / 설명 반드시 포함
    • ExcelDataSample.xlsx 같은 형식 사용
    • 필드 예시: key, name, grade, durability, availableOptions, description
  2. ExcelToJsonWizard 사용 (x64.zip 설치 필요)
    • 반드시 Excel 종료 후 실행!
    • .json 파일 자동 생성됨
    • enum 필드도 대응 가능
  3. Unity에서 ScriptableObject로 불러오기
    • JSON → ScriptableObject or DataContainer 형태로 역직렬화하여 사용
  4. 데이터 접근 방식
    • GetByKey() or GetByIndex() 사용
    • 멀티 시트는 지양 (관리 복잡도 증가)

 구조 설계 팁

🔹 불변 데이터 + 변동 데이터 분리 예시

 
// 고정 데이터 public class ItemInfo { public string key; public string name; public int grade; public float baseDurability; public List<OptionInfo> availableOptions; } // 유저에게 발급 시 public class ItemInstance { public string itemKey; public float currentDurability; public bool isEquipped; }

→ ItemInfo는 그대로 두고, 유저별 상태는 ItemInstance에서 관리!

🔹 옵션 구조

 
public class OptionInfo { public string optionName; public float value; public string description; }

→ Option을 별도로 관리하면 유지보수 & 밸런싱이 매우 편해짐!


🧭  Rider 기준 단축키 정리


코드 참조 찾기 ⌘ + B or ⌘ + Click 정의/참조로 이동
심볼 검색 ⇧ + ⌘ + O 클래스, 함수 등 빠른 검색
파일 검색 ⌘ + ⇧ + F 전체 파일 내용 검색
빠른 수정 ⌘ + ⏎ 자동완성 및 리팩토링
변수 이름 변경 ⇧ + F6 리팩토링 - 이름 변경
디버그 실행 ⌃ + D 브레이크포인트 따라가며 실행
한 줄 삭제 ⌘ + X or ⌘ + Delete 현재 줄 삭제
줄 이동 ⌘ + G 원하는 줄로 이동
직접 실행창 ⌥ + F12 Evaluate Expression
솔루션 탐색기 ⌘ + ; 파일, 구조 등 탐색
코드 블럭 정리 ⌘ + ⌥ + L 자동 정렬 & 들여쓰기
 

🧪 디버깅 팁 (꼭 기억하기!)

  • Debug.Log()만 쓰지 말고 중단점(Breakpoint) 적극 활용
  • Rider에서 디버깅 시 Evaluate Expression 창 활용 가능
  • **호출 스택(Call Stack)**을 반드시 확인
  • NullReferenceException이 발생하면:
    1. . (점) 앞을 보자!
    2. 그 값이 null임 → 곧바로 원인 파악 가능