반응형

Unity 컴포넌트 패턴 — 조립으로 끝내는 설계

📌 개념 (TL;DR)

컴포넌트 패턴 = 기능 블록 조립.

"원하는 동작에 대한 기능을
블록 단위로 나누고
필요할 때 가져와서 사용하고 수정하고 하는 방식"

❌ 상속 방식
Player → Character → Entity (수정 지옥)
✅ 컴포넌트 방식
GameObject + Move + Attack + Health (조립/교체 자유)
Unity = 컴포넌트 패턴 엔진.
Transform, Rigidbody, Collider 전부 컴포넌트다.

상속 대신 GameObject에 Move/Attack/Health 같은 기능을 붙여서 조합으로 만든다.


💬 실제 사용 예시

🥉 신입 레벨 — 기본 조립

목표: 이동/공격/체력을 분리해서 플레이어 만들기

// 1. 이동 컴포넌트
public class MoveComponent : MonoBehaviour
{
    [SerializeField] float speed = 5f;

    void Update()
    {
        float h = Input.GetAxis("Horizontal");
        float v = Input.GetAxis("Vertical");
        transform.Translate(new Vector3(h, 0, v) * speed * Time.deltaTime);
    }
}
// 2. 체력 컴포넌트
public class HealthComponent : MonoBehaviour
{
    [SerializeField] int maxHp = 100;
    int currentHp;

    void Start() => currentHp = maxHp;

    public void TakeDamage(int damage)
    {
        currentHp -= damage;
        if (currentHp <= 0) Die();
    }

    void Die() => gameObject.SetActive(false);
}
// 3. 공격 컴포넌트
public class AttackComponent : MonoBehaviour
{
    [SerializeField] int damage = 10;

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
            Attack();
    }

    void Attack()
    {
        // 레이캐스트로 타겟 찾아서 데미지
        Debug.Log("공격!");
    }
}

조립

  • GameObject에 3개 컴포넌트 붙이면 끝
  • 또는 에디터에서 Add Component로 추가

🥈 미들 레벨 — 인터페이스 + 이벤트

목표: 컴포넌트끼리 느슨하게 통신

// 1. 인터페이스 정의
public interface IDamageable
{
    void TakeDamage(int damage);
}
// 2. 체력 컴포넌트 (이벤트 추가)
public class HealthComponent : MonoBehaviour, IDamageable
{
    [SerializeField] int maxHp = 100;
    int currentHp;

    public event System.Action<int> OnDamaged;  // hp 변경 알림
    public event System.Action OnDied;          // 사망 알림

    void Start() => currentHp = maxHp;

    public void TakeDamage(int damage)
    {
        currentHp = Mathf.Max(0, currentHp - damage);
        OnDamaged?.Invoke(currentHp);

        if (currentHp == 0)
        {
            OnDied?.Invoke();
            Die();
        }
    }

    void Die() => gameObject.SetActive(false);
}
// 3. 공격 컴포넌트 (인터페이스 사용)
public class AttackComponent : MonoBehaviour
{
    [SerializeField] int damage = 10;
    [SerializeField] float attackRange = 2f;
    [SerializeField] LayerMask targetLayer;

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
            Attack();
    }

    void Attack()
    {
        Collider[] hits = Physics.OverlapSphere(transform.position, attackRange, targetLayer);

        foreach (var hit in hits)
        {
            if (hit.TryGetComponent<IDamageable>(out var target))
                target.TakeDamage(damage);
        }
    }
}
// 4. UI 연결 컴포넌트
public class HealthUI : MonoBehaviour
{
    [SerializeField] HealthComponent health;
    [SerializeField] Slider hpBar;

    void Start()
    {
        health.OnDamaged += UpdateUI;
        health.OnDied += ShowDeadScreen;
    }

    void UpdateUI(int hp) => hpBar.value = hp;
    void ShowDeadScreen() => Debug.Log("You Died");
}

핵심

  • 인터페이스로 타입 체크
  • 이벤트로 통신 (직접 참조 최소화)

🥇 시니어 레벨 — ScriptableObject + 데이터 분리

목표: 수치는 SO로 분리, 재사용/확장 극대화

// 1. 데이터 정의 (ScriptableObject)
[CreateAssetMenu(menuName = "Stats/AttackData")]
public class AttackData : ScriptableObject
{
    public int damage = 10;
    public float cooldown = 1f;
    public float range = 2f;
    public LayerMask targetLayer;
}
[CreateAssetMenu(menuName = "Stats/HealthData")]
public class HealthData : ScriptableObject
{
    public int maxHp = 100;
    public float regenRate = 0f;
}
// 2. 체력 컴포넌트 (데이터 주입)
public class HealthComponent : MonoBehaviour, IDamageable
{
    [SerializeField] HealthData data;
    int currentHp;

    public event System.Action<int, int> OnDamaged;  // (current, max)
    public event System.Action OnDied;

    void Start()
    {
        currentHp = data.maxHp;
        if (data.regenRate > 0)
            InvokeRepeating(nameof(Regen), 1f, 1f);
    }

    public void TakeDamage(int damage)
    {
        currentHp = Mathf.Max(0, currentHp - damage);
        OnDamaged?.Invoke(currentHp, data.maxHp);

        if (currentHp == 0)
        {
            OnDied?.Invoke();
            gameObject.SetActive(false);
        }
    }

    void Regen()
    {
        if (currentHp < data.maxHp)
        {
            currentHp = Mathf.Min(data.maxHp, currentHp + Mathf.RoundToInt(data.regenRate));
            OnDamaged?.Invoke(currentHp, data.maxHp);
        }
    }
}
// 3. 공격 컴포넌트 (데이터 주입)
public class AttackComponent : MonoBehaviour
{
    [SerializeField] AttackData data;
    float cooldownTimer;

    void Update()
    {
        cooldownTimer -= Time.deltaTime;

        if (Input.GetKeyDown(KeyCode.Space) && cooldownTimer <= 0)
        {
            Attack();
            cooldownTimer = data.cooldown;
        }
    }

    void Attack()
    {
        Collider[] hits = Physics.OverlapSphere(transform.position, data.range, data.targetLayer);

        foreach (var hit in hits)
        {
            if (hit.TryGetComponent<IDamageable>(out var target))
                target.TakeDamage(data.damage);
        }
    }
}
// 4. 버프 시스템 (데이터 교체)
public class BuffComponent : MonoBehaviour
{
    [SerializeField] AttackComponent attack;
    [SerializeField] AttackData normalAttack;
    [SerializeField] AttackData buffedAttack;
    [SerializeField] float buffDuration = 5f;

    public void ApplyBuff()
    {
        // 데이터만 교체 (리플렉션 또는 public setter 필요)
        StartCoroutine(BuffRoutine());
    }

    System.Collections.IEnumerator BuffRoutine()
    {
        // buffedAttack으로 교체
        yield return new WaitForSeconds(buffDuration);
        // normalAttack으로 복구
    }
}

핵심

  • 밸런스 수치는 SO로 분리 → 리빌드 없이 수정
  • 같은 컴포넌트 코드로 다양한 캐릭터 생성 (데이터만 교체)

Q. 전역 상수 방식으로 사용하면 되지 않을까? 왜 굳이 Scriptable Object를 사용할까?
1️⃣ 전역 상수 방식

public class BossHealth : MonoBehaviour
{
    const int MAX_HP = 10000;  // ← 이거 수정하려면?, 각 오브젝트마다 복사
}

// 100개 생성 → 100개의 damage 변수 (메모리 낭비)

작업 과정:

  1. 코드 열기
  2. 10000 → 8000 수정
  3. 저장 후 리컴파일 대기 (10초~1분)
  4. 플레이 버튼 눌러서 테스트
  5. 또 조정 필요? → 1번부터 반복

문제점:

  • 수정할 때마다 리컴파일 (시간 낭비)
  • 기획자가 직접 못 바꿈 (프로그래머 의존)
  • 보스마다 다른 체력? → 코드 여러 개 필요
프로그래머: 코드 수정 중...
기획자: "슬라임 체력 올려주세요!"
프로그래머: "지금 다른 작업 중이라 30분 후에요"
기획자: 대기...

기획자: "고블린도요!"
프로그래머: 또 코드 열고 수정...
→ 반복 (병목 발생)

2️⃣ ScriptableObject 방식

[CreateAssetMenu(menuName = "Data/BossHealth")]
public class BossHealthData : ScriptableObject
{
    public int maxHp = 10000;
}

public class BossHealth : MonoBehaviour
{
    [SerializeField] BossHealthData data;  // ← 에셋 참조만 저장 (8바이트)
    int currentHp;

    void Start() => currentHp = data.maxHp;
}

작업 과정:

  1. Project 창에서 에셋 클릭
  2. Inspector에서 10000 → 8000 입력
  3. 즉시 플레이 버튼 (리컴파일 없음)
  4. 테스트
  5. 또 조정? → 1번부터 (3초 컷)

이점:

  • 리컴파일 없음 (즉시 테스트)
  • 기획자가 직접 수정 가능
  • 보스마다 다른 에셋 할당 (코드 1개로 끝)
프로그래머: 코드 수정 중...
기획자: Slime.asset 클릭 → 체력 수정 → 즉시 테스트 ✅
기획자: Goblin.asset 클릭 → 체력 수정 → 즉시 테스트 ✅
→ 프로그래머 방해 없음


⚒️ 성능 최적화 - 필수 3가지

1. 참조 캐싱

// ❌ 매번 검색
void Update() => GetComponent<Rigidbody>().AddForce(Vector3.up);

// ✅ 캐싱
Rigidbody rb;
void Awake() => rb = GetComponent<Rigidbody>();
void Update() => rb.AddForce(Vector3.up);

2. Update 최소화

// ❌ 매 프레임
void Update() => CheckHealth();

// ✅ 이벤트 기반
public event Action OnHealthChanged;

3. 오브젝트 풀링

// 총알/이펙트는 생성/파괴 대신 활성화/비활성화
public class BulletPool : MonoBehaviour
{
    Queue<GameObject> pool = new();

    public GameObject Get()
    {
        if (pool.Count > 0)
        {
            var obj = pool.Dequeue();
            obj.SetActive(true);
            return obj;
        }
        return Instantiate(bulletPrefab);
    }

    public void Return(GameObject obj)
    {
        obj.SetActive(false);
        pool.Enqueue(obj);
    }
}

⚠️ 실습

  1. 가장 복잡한 스크립트 1개 열기
  2. 기능 나열 (이동/공격/체력/AI...)
  3. 각 기능을 별도 파일로 분리
  4. GetComponent로 참조 연결
  5. 테스트

🎯 목표

  • 핵심 컴포넌트 3개 작성 (Move, Health, Attack)
  • 데이터 1개 ScriptableObject로 분리
  • 기존 코드 1개 리팩터링

시작은 작게: Move + Health만 분리해도 효과가 보인다.

반응형
반응형
#include <iostream>
#include <ctime>
#include <cstdlib>
#include <string>


int main()
{
    srand(static_cast<unsigned int>(time(0)));

    const int SIZE = 25;
    const int INTMAX = INT_MAX;

    int numsArr[SIZE] = {};
    int aiNumsArr[SIZE] = {};
    int bingoCount = 0, aiBingoCount = 0;;
    bool isPlayerTurn = true;

    for (int i = 0; i < SIZE; ++i)
    {
        numsArr[i] = i + 1;
        aiNumsArr[i] = i + 1;
    }

    // 섞기
    for (int i = 0; i < SIZE; ++i)
    {
        std::swap(numsArr[rand() % SIZE], numsArr[rand() % SIZE]);
        std::swap(aiNumsArr[rand() % SIZE], aiNumsArr[rand() % SIZE]);
    }

    while (true)
    {
        system("cls");

        // 5x5 빙고판 출력
        std::cout << "============ Player ============" << std::endl;
        for (int i = 0; i < SIZE; ++i)
        {
            std::cout << (numsArr[i] == INTMAX ? "*" : std::to_string(numsArr[i])) << "\t";

            if ((i + 1) % 5 == 0)
                std::cout << std::endl;
        }
        std::cout << "Bingo Line: " << bingoCount << "\n\n";


        std::cout << "============== AI ==============" << std::endl;
        for (int i = 0; i < SIZE; ++i)
        {
            std::cout << (aiNumsArr[i] == INTMAX ? "*" : std::to_string(numsArr[i])) << "\t";

            if ((i + 1) % 5 == 0)
                std::cout << std::endl;
        }
        std::cout << "AI Bingo Line: " << aiBingoCount << "\n\n";


        // 5 빙고 체크
        if (bingoCount >= 5 || aiBingoCount >= 5)
        {
            std::cout << (bingoCount >= 5 ? "Player 승리!" : "AI 승리!") << std::endl;
            system("pause");
            break;
        }

        std::cout << "1~25 사이의 숫자를 입력하세요(0 종료): ";
        int input = isPlayerTurn ? (std::cin >> input, input) : rand() % 25 + 1;    // (std::cin >> input, input), input 값을 입력 받고 input을 반환한다.


        // 0 입력 시 게임 종료
        if (input == 0)
        {
            std::cout << "게임을 종료합니다." << std::endl;
            break;
        }


        // 유효범위 체크
        if (input < 1 || input > 25)
        {
            std::cout << "1~25 사이의 숫자를 입력하세요!" << std::endl;
            system("pause");
            continue;
        }


        // 이미 선택된 숫자 처리
        bool alreadySelected = true;
        for (int i = 0; i < SIZE; ++i) 
        {
            if (numsArr[i] == input || aiNumsArr[i] == input) 
            {
                alreadySelected = false;
                break;
            }
        }
        if (alreadySelected) 
        {
            std::cout << "이미 선택된 숫자입니다. 다른 숫자를 입력하세요!\n";
            system("pause");
            continue;
        }


        // 입력 숫자 처리
        for (int i = 0; i < SIZE; ++i)
        {
            if (numsArr[i] == input)
                numsArr[i] = INTMAX;

            if (aiNumsArr[i] == input)
                aiNumsArr[i] = INTMAX;
        }


        // 빙고 체크
        bingoCount = 0, aiBingoCount = 0;

        for (int player = 0; player < 2; ++player)
        {
            int* board = (player == 0) ? numsArr : aiNumsArr;

            for (int i = 0; i < 5; ++i) 
            {
                bool isBingo = true;

                // 가로
                for (int j = 0; j < 5; ++j)
                {
                    if (board[i * 5 + j] != INTMAX)
                    {
                        isBingo = false;
                        break;
                    }
                }
                if (isBingo)
                    (player == 0 ? bingoCount : aiBingoCount)++;

                // 세로
                isBingo = true;
                for (int j = 0; j < 5; ++j)
                {
                    if (board[i + j * 5] != INTMAX)
                    {
                        isBingo = false;
                        break;
                    }
                }
                if (isBingo)
                    (player == 0 ? bingoCount : aiBingoCount)++;
            }
        }


        // 대각선
        for (int player = 0; player < 2; ++player)
        {
            int* board = (player == 0) ? numsArr : aiNumsArr;

            bool isBingo = true;
            for (int i = 0; i < 5; ++i)
            {
                if (board[i * 6] != INTMAX)
                    isBingo = false;
            }
            if (isBingo)
                (player == 0 ? bingoCount : aiBingoCount)++;

            isBingo = true;
            for (int i = 0; i < 5; ++i)
            {
                if (board[(i + 1) * 4] != INTMAX)
                    isBingo = false;
            }
            if (isBingo)
                (player == 0 ? bingoCount : aiBingoCount)++;
        }

        isPlayerTurn = !isPlayerTurn;
    }

    return 0;
}

 

1. <string> 헤더란?
std::string 클래스:
   - 가변 길이 문자열을 관리하며, C 스타일 문자열과 달리 자동으로 메모리를 관리한다.
   - 문자열 추가/제거/비교 등 고급 기능 제공.
문자열 조작 함수:
   - 문자열 크기/삽입/삭제/치환/검색/부분 문자열 추출 등을 위한 다양한 메서드 포함.
C 스타일 문자열과의 호환성:
   - c_str() 메서드를 통해 C 스타일 문자열로 변환 가능.
문자열 변환:
   - std::to_string 을 사용하여 숫자 데이터를 문자열로 변환 가능.

1) 문자열 생성 및 조작

#include <iostream>
#include <string>

int main() {
    std::string str = "Hello";
    str += " World!"; // 문자열 추가
    std::cout << str << std::endl;

    std::cout << "문자열 길이: " << str.size() << std::endl;
    std::cout << "부분 문자열: " << str.substr(0, 5) << std::endl; // "Hello"

    return 0;
}​
2) C 스타일 문자열과 호환

#include <iostream>
#include <string>

int main() {
    std::string str = "Hello, World!";
    const char* cStr = str.c_str(); // C 스타일 문자열로 변환
    std::cout << cStr << std::endl;

    return 0;
}
3) 문자열 비교

#include <iostream>
#include <string>

int main() {
    std::string str1 = "Apple";
    std::string str2 = "Banana";

    if (str1 < str2) {
        std::cout << str1 << "은(는) " << str2 << "보다 작습니다." << std::endl;
    }

    return 0;
}
4) std::to_string 함수
• C++11에서 도입된 함수, 숫자 데이터 -> 문자열로 변환
C+11 이전 환경에서는 std::stringstream 을 사용하여 숫자 -> 문자열로 변환
• 다양한 타입을 문자열로 변환 가능.
int, long, long long, unsigned, unsigned long, unsigned long long, float, double, long double

#include <iostream>
#include <string>

int main() {
    int number = 42;
    double pi = 3.14159;

    std::string str1 = std::to_string(number); // 정수 -> 문자열
    std::string str2 = std::to_string(pi);     // 실수 -> 문자열

    std::cout << "정수 변환: " << str1 << std::endl; // "42"
    std::cout << "실수 변환: " << str2 << std::endl; // "3.141590"

    return 0;
}

 

반응형

'Programming Languages > C++' 카테고리의 다른 글

[C++] 5x5 빙고게임  (1) 2024.12.19
[C++] 야구게임  (3) 2024.12.18
[C++] 기초문법 - 3편  (1) 2024.12.13
[C++] 기초문법 - 2편  (1) 2024.12.13
[C++] 기초 문법 - 1편  (3) 2024.12.06
반응형
[숫자 빙고 게임]
1~25까지의 숫자가 있고, 이 숫자를 골고루 섞어서 5x5 로 출력한다.
플레이어는 숫자를 입력받는다.
1~25 사이의 숫자를 입력받아야 한다.
0을 입력하면 게임을 종료한다.
숫자를 입력받았으면 숫자목록중 입력받은 숫자를 찾아서 *로 만들어준다.
숫자를 * 로 만든 후에 빙고 줄 수를 체크한다.
5x5이기 때문에 가로 5줄 세로 5줄 대각선 2줄이 나올 수 있다.
빙고줄이 몇 줄인지를 체크해서 화면에 보여준다.
5줄 이상일 경우 게임을 종료한다.

 

#include <iostream>
#include <ctime>
#include <cstdlib>

int main()
{
    srand(static_cast<unsigned int>(time(0)));
    
    const int SIZE = 25;
    const int INTMAX = INT_MAX;

    int numsArr[SIZE] = {};
    int bingoCount = 0;

    for (int i = 0; i < SIZE; ++i)
    {
        numsArr[i] = i + 1; 
    }

    for (int i = 0; i < SIZE; ++i)
    {
        int idx1 = rand() % SIZE;
        int idx2 = rand() % SIZE;

        int temp = numsArr[idx1];
        numsArr[idx1] = numsArr[idx2];
        numsArr[idx2] = temp;
    }

    while (true)
    {
        system("cls");

        // 5x5 빙고판 출력
        for (int i = 0; i < SIZE; ++i)
        {
            if (numsArr[i] == INTMAX)
                std::cout << "*" << "\t";
            else
                std::cout << numsArr[i] << "\t";

            if ((i + 1) % 5 == 0)
                std::cout << std::endl;
        }

        // 5줄 빙고 체크
        if (bingoCount >= 5)
        {
            std::cout << "5줄 빙고를 다 맞추셨습니다! 축하합니다!!" << std::endl;
            system("pause");
            break;
        }

        std::cout << "1~25 사이의 숫자를 입력하세요(0 입력 시 게임이 종료됩니다): ";

        int input;
        std::cin >> input;
        
        // 0 입력 시 게임 종료
        if (input == 0)
        {
            std::cout << "게임을 종료합니다." << std::endl;
            break;
        }
        
        // 유효범위 체크
        if (input < 1 || input > 25)
        {
            std::cout << "1~25 사이의 숫자를 입력하세요!" << std::endl;
            system("pause");
            continue;
        }

        // 중복숫자 체크
        for (int i = 0; i < SIZE; ++i)
        {
            if (numsArr[i] == input)
            {
                numsArr[i] = INTMAX;
                break;
            }
        }

        // 빙고 체크
        bingoCount = 0;

        // 가로
        for (int i = 0; i < 5; ++i)
        {
            bool isBingo = true;

            for (int j = 0; j < 5; ++j)
            {
                if (numsArr[i * 5 + j] != INTMAX)
                {
                    isBingo = false;
                    break;
                }
            }

            if (isBingo == true)
                bingoCount++;
        }

        // 세로
        for (int i = 0; i < 5; ++i)
        {
            bool isBingo = true;

            for (int j = 0; j < 5; ++j)
            {
                if (numsArr[i + j * 5] != INTMAX)
                {
                    isBingo = false;
                    break;
                }
            }

            if (isBingo == true)
                bingoCount++;
        }

        // 대각선
        bool isBingo = true;
        for (int i = 0; i < 5; ++i)
        {
            if (numsArr[i * 6] !== INTMAX)
            {
                isBingo = false;
                break;
            }
        }

        if (isBingo == true)
            bingoCount++;

        isBingo = true;
        for (int i = 0; i < 5; ++i)
        {
            if (numsArr[(i + 1) * 4] != INTMAX)
            {
                isBingo = false;
                break;
            }
        }

        if (isBingo == true)
            bingoCount++;
    }
    
    return 0;
}

 

반응형

'Programming Languages > C++' 카테고리의 다른 글

[C++] 5x5 빙고게임 (AI 추가)  (2) 2024.12.19
[C++] 야구게임  (3) 2024.12.18
[C++] 기초문법 - 3편  (1) 2024.12.13
[C++] 기초문법 - 2편  (1) 2024.12.13
[C++] 기초 문법 - 1편  (3) 2024.12.06
반응형
[야구게임]
• 1~9 사이의 랜덤한 숫자 3개를 입력한다. (단, 숫자는 중복되어서는 안된다.)
• 3개의 숫자를 맞추는 게임이다. 사용자는 3개의 숫자를 맞출때까지 계속해서 3개씩 숫자를 입력한다.
ex)
"정답"
7 3 8

"입력1"
1 2 4
Out 출력

"입력2"
7 5 9
1 Strike 출력

"입력3"
7 8 5
1 Striker 1 Ball

"출력"
3 Strike 정답입니다!

 

#include <iostream>
#include <sstream>
#include <ctime>

int main()
{
    const int SIZE = 3;
    int numbers[SIZE] = {};
    int inpuNums[SIZE] = {};
    
    srand(static_cast<unsigned>(time()));
    for(int i = 0; i < SIZE; ++i)
    {
    	numbers[i] = rand() % 9 + 1;		// 1~9 사이 숫자 랜덤 출력
        for(int j = 0; j < i; ++j)
        {// 
        	if(numbers[i] == numbers[j])
            	{
			--i;
            		break;
		}	
        }
    }
    
    while(true)
    {
    	std::cout << "1~9 사이의 3개 숫자 입력: ";
        
        // 입력 처리
        std::string inputLine;
 	std::getline(std::cin, inputLine);
 	std::stringstream ss(inputLine);
	int count = 0;
    
    	// 입력한 수가 3개가 넘어가는지 체크
    	while(ss >> inputNums[count])
        {
        	count++;
        
            if(count > SIZE)
            	break;
        }
        
        if(count != SIZE)
        {
            std::cout << "1~9 사이의 3개의 숫자만 입력하세요!" << std::endl;
            continue;
        }
        
        bool isValid = true;
        for(int i = 0; i < SIZE; ++i)
        {
            // 1~9 유효범위 체크
            if(inputNums[i] < 1 || inputNums[i] > 9)
            {
            	isValid = false;
                break;
            }
            
            // 중복 체크
            for(int j = 0; j < i; ++j)
            {
            	if(inputNums[i] == inputNums[j])
                {
                	isValid = false;
                    break;
                }
            }
        }
        
        if(isValid == false)
        {
        	std::cout << "1~9 사이의 숫자를 중복 없이 입력하세요!" << std::endl;
		continue;
        }
        
        // Strike, Ball, Out 체크
         int strike = 0, ball = 0;
         for (int i = 0; i < SIZE; ++i)
         {
             if (inputNumbers[i] == numbers[i]) 
             {
                 strike++;
             }
             else
             {
                 for (int j = 0; j < SIZE; ++j)
                 {
                     if (inputNumbers[i] == numbers[j])
                     {
                         ball++;
                         break;
                     }
                 }
             }
         }
         
         
        if (strike == 0 && ball == 0)
        {
            std::cout << "Out!" << std::endl;
        }
        else if (strike == SIZE)
        {
            std::cout << "3 Strike! 정답입니다!" << std::endl;
        }
        else
        {
            std::cout << "Strike: " << strike << ", Ball: " << ball << std::endl;
        }
    }
}

1. C++ std::getline 함수 정리

[필요 헤더]
#include <iostream>
#include <string>
[문법 형태]
std::getline(istream& input, std::string& str, char delimier = '\n');

input: 입력 스트림 객체 std::cin
str: 입력받은 데이터를 저장할 std::string
delimiter: (선택) 데이터를 나누는 구분자. 기본값은 \n

 

[std::cin과 차이점]
• std::cin
	• 공백 기준으로 데이터를 읽음
	• 공백 뒤의 문자열은 무시
• std::getline
	• 줄바꿈(\n)을 기준으로 한 줄 전체를 읽음
    	• 공백과 기호를 포함한 문자열을 처리 가능

 

• 기본 구분자는 줄바꿈 문자(\n)
• 구분자는 세 번째 매개변수로 변경 가능

std::getline(std::cin, str, ',');		// 쉼표(',')를 구분자로 사용

 

2. C++ std::stringstream 이란?
• 문자열 스트림 클래스
• 문자열(std::string)을 스트림처럼 다룰 수 있어, 문자열에서 데이터를 읽기/쓰기 작업에 유용.
• std::cin 과 유사한 방식으로 문자열을 처리하거나, 데이터를 조합하여 문자열을 생성할 때 사용.

[필요 헤더]
#include <sstream>​
[문법 형태]

std::stringstream ss;
ss << "Hello " << "World!" << 123;		// 데이터를 스트림에 쓰기
std::string result = ss.str();			// 스트림 내용을 문자열로 가져오기​
[주요 메서드]
• str(): 스트림의 내용을 문자열로 반환하거나 초기화.
ss.str("New Content");		// 내용 초기화

• clear(): 스트림의 상태 플래그 초기화.
ss.clear();		// 에러 플래그 초기화

• <<: 스트림에 데이터를 쓰기.
#include <iostream>
#include <sstream>
#include <string>

int main() 
{
    std::stringstream ss;

    ss << "Name: " << "Alice" << ", Age: " << 25;
    std::string result = ss.str(); // 스트림 내용을 문자열로 변환

    std::cout << result << std::endl;
    return 0;
}
/*
출력: Name: Alice, Age: 25
*/

• >>: 스트림에서 데이터를 읽기.
#include <iostream>
#include <sstream>

int main() 
{
    std::string input = "10 20 30";
    std::stringstream ss(input);

    int a, b, c, d; // 초기화되지 않은 변수
    ss >> a >> b >> c >> d; // 스트림에서 데이터 읽기

    std::cout << "a: " << a << ", b: " << b << ", c: " << c << ", d: " << d << std::endl;
    return 0;
}
/*
출력: a: 10, b: 20, c: 30, d: 12847392 (쓰레기 값)
*/

 

1. <ctime> 이란?
• C++ 표준 라이브러리의 헤더 파일로, "날짜"와 "시간"과 관련된 기능을 제공.
• C 표준 라이브러리의 <time.h>를 C++ 스타일로 확장한 것.
• 현재 시간 얻기, 시간 차이 계산, 시간 포맷 변환 등의 작업에 사용.

[주요 함수]
• time(): 현재 시간을 time_t 형식으로 반환.
• localtime() / gmtime(): time_t를 사람이 읽을 수 있는 구조체로 변환.​
1) 현재 시간 얻기

#include <iostream>
#include <ctime>

int main()
{
    time_t now = time(0);		// 현재 시간(초 단위)
    tm* local = localtime(&now);		// 로컬 시간으로 변환

    std::cout << "현재 시간: "
    	<< 1900 + lcoal->tm_year << "-"		// 연도
        << 1 + local->tm_mon << "-"		// 월 (0부터 시작)
        << local->tm_mday << " "
        << local->tm_hour << ":"
        << local->tm_min << ":"
        << local->tm_sec << std::endl;
            
     return 0;
}​
2) 시간 포맷팅
• strftime(): 시간을 특정 문자열 포맷으로 변환.

char buffer[80];
strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", local);
std::cout << "포맷된 시간: " << buffer << std::endl;
3) 시간 차이 계산
• difftime(): 두 시간(time_t)의 차이를 초 단위로 반환.

time_t start = time(0);
time_t end = time(0) + 3600;		// 1시간 후
std::cout << "시간 차이(초): " << difftime(end, start) << std::endl;

 


<ctime>은 C++에서 "날짜"와 "시간 작업"을 처리하는 기본 헤더.
주요 기능: 현재 시간 얻기(time), 시간 차이 계산(difftime), 포맷 변환(strftime).
• C++ 표준에 맞는 네임스페이스(std) 지원으로, C 스타일의 <time.h>보다 더 권장.

 

반응형

'Programming Languages > C++' 카테고리의 다른 글

[C++] 5x5 빙고게임 (AI 추가)  (2) 2024.12.19
[C++] 5x5 빙고게임  (1) 2024.12.19
[C++] 기초문법 - 3편  (1) 2024.12.13
[C++] 기초문법 - 2편  (1) 2024.12.13
[C++] 기초 문법 - 1편  (3) 2024.12.06
반응형
1. if, else if, else문
2. 난수
3. 사용자정의 변수타입
4. 열거형
5. switch문
6. for, while, do while 반목문
7. 배열
배열 실습: 랜덤 셔플

 

 

1. if, else if, else문
여러 조건 중 하나를 처리하는 기능이다.
"조건식"의 참/거짓 여부에 따라 코드가 동작한다.
if(조건식)		// 1번
{
      //동작할 코드
}
else if(조건식)		// 2번	
{
     //동작할 코드
}
else			// 3번
{
     //동작할 코드
}​

1번, "조건식"을 체크하여 true일 경우 코드블록 안의 코드가 동작한다.
2번, else if는 반드시 if와 함께 사용이 되어야 한다.
3번, else는 반드시 if와 함께 사용이 되어야 한다.
else if문의 조건이 false일 경우 동작한다.
하나의 if문에 else는 하나만 존재할 수 있다.

int BossHP = 29;

if (30 <= BossHP && BossHP <= 50)
{
	std::cout << "if문 조건식이 true 입니다." << std::endl;
}
else if (51 <= BossHP && BossHP <= 70)
{
	std::cout << "보스의 체력이 51~70 사이입니다." << std::endl;
}
else
{
	std::cout << "if문 조건식이 false 입니다." << std::endl;
}​

 

2. 난수

rand()
C++에서 난수를 발생시켜주는 "함수"이다.
표현 범위는 0~32767 사이의 "정수"이다.
srand(20);		// 20번 시드에 있는 난수를 가져온다.
time(0);		// time(0) 은 현재 시간을 사용하는 것.​

srand(time(0))		// 현재 시간을 기준으로 난수 생성
// "특정 최소~최대값"을 구하고 싶을 때
// rand() % (최대 - 최소 + 1) + 최소
std::cout << rand() % 101 + 101 << std::endl;			// 100~200
std::cout << rand() << std::endl;				// 0~32767
std::cout << rand() % 10001 * .01f << std::endl;		// 0.00~100.00

 

3. 사용자정의 변수타입
struct, class, enum, union, typedef, using, 템플릿 등을 활용하여 복잡한 데이터 구조와 로직을 효과적으로 관리할 수 있다. 객체 지향 프로그래밍의 원칙을 잘 활용하면 더욱 견고하고 유지보수하기 쉬운 코드를 작성할 수 있다.

 

4. 열거형 (enum)
사용자정의 변수 타입을 만들어주기 위한 하나의 문법이다.
열거형은 연속된 숫자 상수를 만들어줄 수 있는 기능이다.
숫자에 이름을 부여해줄 수 있다.

열거형은 기본(Default)으로 4 byte의 크기를 가지고 있다.
// 4 byte
enum EFruit
{
   None,
   Apple = 10,
   Banna = 50,
   Grape
}

// 1 byte
enum class EDishType : unsigned char
{
    Asian,
    Western,
    Chinese
}​

 

5. switch 문
분기처리가 가능하다.
switch(변수)
{
	case 상수:
		break;

	case 상수:
		break;

	default:
		break;
}​

변수의 값에 따라 case의 상수를 찾아서 코드를 동작시키고, break를 만나면 switch문을 빠져나간다.
모든 case 상수 중에서 "변수"의 값과 같은 상수가 없을 경우 default가 동작된다.

int Number = 2;

switch (Number)
{
case 1:
{
	int Number1 = 10;
	std::cout << "숫자가 1입니다." << std::endl;
}
//Number1 = 500;
	break;

case 2:		
	// case2와 case3와 같이 switch문 안의 같은 블록을 공유하고 있는 경우
	// 중복 선언으로 취급하여 "컴파일 에러"를 발생시킨다.
	// int Number2: 재정의 오류
	
	//int Number2;
	//Number2 = 300;
	std::cout << "숫자가 2입니다." << std::endl;
	break;

case 3:
	//int Number2;
	//Number2 = 1000;
	std::cout << "숫자가 3입니다." << std::endl;
	break;

default:
	std::cout << "그 외의 숫자입니다." << std::endl;
	break;
}​

 

EFruit Fruit;

std::cout << sizeof(EFruit) << std::endl;		// 4 byte
std::cout << sizeof(int) << std::endl;			// 4 byte

//Fruit = 2342125;			// int값 형식을 -> enum값 형식에 대입X
int FruitInt = EFruit::Apple;		// enum값 형식을 -> int값 형식에 대입O​

// 반드시 열거형 소속과 함께 접근을 해야 대입 가능
EDishType DisType = EDishType::Apple;
DisType = EDishType::Banna;

 

6. for, while, do while 반복문

1. 조건식 체크
2. 코드 동작
3. 증감값
for(초기값; 조건식; 증감값)
{
   // 동작할 코드
}


// 무한 루프
for(;;)
{
}


while(조건식)
{
   // 동작할 코드, 증감값
}


// 최소 한 번 실행은 보장한다.
do
{
   // 동작할 코드, 증감값
} while(조건식);

for (int i = 0; i < 5; ++i) // 0, 1, 2, 3, 4
{
	for (int j = 0; j < 10; ++j) 
	{
		// i와 j의 값을 이용해서 0~49까지 출력될 수 있게
		// 코드를 작성하시오.
		// 세로줄번호 * 가로개수 + 가로번호
		std::cout << i * 10 + j << std::endl;
	}
}​

 

7. 배열
"배열"은 인덱스를 이용해서 접근한다.
인덱스는 0 ~ 개수-1 까지이다.

int Array[50] = {1, 2, 3, 4};		// 5~49번 인덱스까지는 모두 0으로 초기화

//Array[3] = 500;
++Array[49];

for (int i = 0; i < 50; ++i)
{
	std::cout << Array[i] << std::endl;
}​

 

8. 배열 랜덤 셔플
for (int i = 0; i < 100; ++i)
{
	// 0~19 랜덤 수 생성
	int Index1 = rand() % 20;
	int Index2 = rand() % 20;

	int Temp = Number[Index1];
	Number[Index1] = Number[Index2];
	Number[Index2] = Temp;
}​

for (int i = 0; i < 100; ++i)
	std::cout << Number[i] << std::endl;

 

반응형

'Programming Languages > C++' 카테고리의 다른 글

[C++] 5x5 빙고게임 (AI 추가)  (2) 2024.12.19
[C++] 5x5 빙고게임  (1) 2024.12.19
[C++] 야구게임  (3) 2024.12.18
[C++] 기초문법 - 2편  (1) 2024.12.13
[C++] 기초 문법 - 1편  (3) 2024.12.06

+ Recent posts