전체 글 (195)

  • 2024.01.22
  • 2024.01.19
  • 2024.01.18
  • 2024.01.17
  • 2024.01.16
  • 2024.01.15
  • 2024.01.13
  • 2024.01.12
  • 2024.01.11
  • 2024.01.10
  • 2024.01.10
  • 2024.01.08
  • 01
    22

    과정명 : 내일배움캠프 Unity 게임개발 3기

    전체진행도 : 21일차

    부분진행도 : Chapter3.1 - 3일차

    작성일자 : 2024.01.22(월)

    개발일지 목록 : 클릭


    1. 진행중인 과정에 대해

    강의 난이도가 너무 높아 50% 부근까지만 소화한 후, 개인과제에 착수하였다. 내일 정오까지 제출을 해야하기 때문에 시간 부족으로 어쩔 수 없는 선택.

    개인과제도 커스텀하기는 어려울 것 같고, 필수 구현요소 7개만 일단 제대로 구현하기로 생각했다.

    캐릭터 만들기, 타일맵으로 플레이 공간 만들기, 카메라가 캐릭터를 따라가기, 캐릭터 이름 입력하기, 캐릭터 외형 선택하기의 구현을 마쳤고, 캐릭터이동과 idle 애니메이션을 구현하면 일단 필수요소의 구현은 끝이다. 현재 오후 9시, 남은 요소는 내일 오전까지의 시간을 활용해서 구현해야 할 듯 하다.

    강의에서 배운 내용을 활용하기에는 시간이 촉박하여 스크립트 간 디자인 없이 깡으로 구현하였다.

    2. 오늘 학습에 대해

    유니티 인스펙터에서 한글 깨짐 현상

    유니티 인스펙터에서 한글이 깨져보인다

    위 처럼 유니티에서 깨져보이는 현상 이외에도, 이대로 깃허브에 올리고 팀원에 공유를 하면 거기에서는 편집기에서조차 깨져보이기도 한다.

    해결방법은, 해당 프로젝트의 루트 폴더(.sin 파일이 위치한 곳)에 인코딩을 지정해주는 파일 하나를 생성하면 된다.

    [*]
    charset = utf-8-bom

    메모장을 열어 위와 같이 작성 후, .editorconfig 라는 이름으로 루트 폴더에 저장 해 준다.

    .editorconfig가 저장된 모습

    인코딩 설정을 이렇게 하고, Visual Studio 편집기에서 한 번 더 저장을 해준다.

    파일을 다시 한 번 저장한다
    유니티 인스펙터에서 한글이 제대로 보인다

    설정된 인코딩으로 저장을 완료하면, 유니티 인스펙터에서 한글이 제대로 보인다.

    3. 과제에 대해

    • 개인과제 제출(내일 13:00 까지)
    • 코드 및 구조 정돈하기

    4. 참고자료

    5. GPT4 문답

    • 없음
    반응형
    COMMENT
     
    01
    19

    과정명 : 내일배움캠프 Unity 게임개발 3기

    전체진행도 : 20일차

    부분진행도 : Chapter3.1 - 2일차

    작성일자 : 2024.01.19(금)

    개발일지 목록 : 클릭


    1. 진행중인 과정에 대해

    C#문법과 콘솔게임 주차에서 Unity로 다시 돌아와 개인과제주차를 진행하고있다. 입문 과정이라고 지급된 유니티 강의 영상이 생각보다 어려운 내용이었다. Docs문서를 확인하면 어제 하루간 이 강의를 완강하고 나머지 3일간 개인과제를 하는 것을 염두에 둔 계획을 적어두었는데, 오늘 늦은 오후에 강의 완료표시만 모두 찍어두고 다시 이어서 소화하자고 생각하여 진행도를 100% 찍었는데 

    어.. 아무도 완강을 못한 상태인가보다. 하루는 커녕 이틀이 끝나가는데 이 상태라면, 운영측의 스케쥴 계획에서 큰 미스가 아닌가 생각했다.

    어쨌든 오늘도 유니티 강의를 들으며 하루를 보냈다. 어렵긴 하지만 게임 개발을 위한 오브젝트와 스크립트 구조에 대해 심도가 무척 깊어 유익한 내용이다.

    그리고 챌린지/노말/이지 였나로 수강생의 역량에 맞춰 따로 세션을 진행한다고 한다. 화요일과 목요일 주 2회 예정인데, 구체적으로 어떤 방식으로 진행하는지는 모르겠지만 최적화와 메모리 관리 등 듣기만해도 무서운 내용이 기다리고 있는 것은 확실한 듯.

    2. 오늘 학습에 대해

    스크립트 관련하여 들은 내용이 거의 새롭게 배운 부분이기 때문에, 주어진 시간이 촉박하여 정리하면서 듣기는 좀 어려웠나 생각한다.

    타일맵

    RPG 메이커 비슷한 느낌으로 유니티에서도 맵을 구성하는 타일과 벽 등을 쉽게 그리는 기능이 있었다.

    위와 같이 생긴 윈도우에서 색깔을 선택하듯이 타일을 선택하고, 하이에라키에 추가했던 오브젝트에 도트 아트를 하듯이 타일을 그려 구성하는 느낌.

    하이에라키에서는 2D Object - Tilemap - Rectangular 등을 통해 타일 오브젝트를 만들고

    상단메뉴의 Window - 2D - Tile Palette 윈도우를 열어, 새로운 팔레트를 생성, 바닥 등의 스프라이트 등을 팔레트에 추가하여 사용한다.

    새로운 팔레트를 위와 같이 만들면, 해당 팔레트를 어디에 저장할지 폴더를 지정하도록 한다. 나는 강의를 따라하며 Assets/Artworks/Level/ 을 지정해주었다.

    Tile Palette에 타일 스프라이트를 드래그하여 추가하였다
    타일을 추가하며 저장된 데이터

    그렇게 만들어진 팔레트에, 앞으로 사용할 바닥이나 벽 길가의 풀숲 등 타일로 사용할 스프라이트들을 드래그하여 추가한다. 여러개 선택해서 드래그하면, 적당히 정사각형에 가까운 예쁜 배치를 해 준다. 스프라이트를 드래그하는 시점에 마찬가지로 저장 위치를 지정하도록 하는데, 역시 강의를 따라하며 Assets/Artworks/Level/Tiles/ 위치에 저장하도록 하였다.

    타일 오브젝트 Floor에 바닥을 를 그리고있다
    BackDesign
    ForeDesign

    하이에라키에서 2D Object - Tilemap - Rectangular 를 통해 타일맵 오브젝트를 만들고, 여러 종류의 타일들을 작성해준다.

    어느 타일 오브젝트가 Player를 가리는지, 타일 오브젝트끼리 서로 가려지는 관계에 따라 Floor, BackDesign, ForeDesign 오브젝트를 작성하였고, 각각 높이(Order in Layer)를 0, 2, 20 으로 설정하였다.

    그리고 플레이어가 지나가지 못하도록 하기 위해 Collision 도 이 Tilemap을 통해 만든다.

    Order in Layer가 0이어서 사진에는 여러가지 타일을 쓴 것 같아 보이지만, 가려서 그렇게 보일 뿐 두세가지 종류의 타일을 사용하여 위와 같은 범위에 Collision 용 타일을 그려주었다. 그리고 Tilemap Collider 2D 컴포넌트를 추가해주고

    색을 투명하게 바꿔주어 충돌체의 역할만 수행하도록 한다. 마찬가지로 Player에게도 Box Collider 2D 컴포넌트를 추가한다.

    위와 같은 결과가 나오게 되었는데, 보니까 Player가 BackDesign에 가려지는 것이 보여, 0으로 설정되어있던 Player의 Order in Layer를 5로 바꿔주었다.

    같은 이름 변수명 이름 한번에 바꾸기

    Visual Studio 에서 위와 같이 같은 이름의 변수명 등을 한번에 바꿔줄 수 있다.

    단축키는 Ctrl R R

    최신 버전의 Visual Studio는 F2 키로 사용할 수 있다고 한다.

    3. 과제에 대해

    • 강의 내용 흡수
    • 개인과제 완수

    4. 참고자료

    • 없음

    5. GPT4 문답

    리스트에서 특정 인덱스 범위 삭제

    [You]

    List<int> stackAll = new List<int>();
    ...
    //현재 List : 1, 2, 3, 4, 5, 6, 7 ,8 ,9

    현재 리스트에서 6에서 9를 날려버리고 1에서 5만 남기도록 하는 법

    [ChatGPT]

    List<T>에서 특정 범위의 요소를 제거하려면 RemoveRange 메서드를 사용할 수 있습니다. 이 메서드는 지정된 인덱스에서 시작하여, 지정된 개수만큼의 요소를 리스트에서 제거합니다.

    예를 들어, 리스트 {1, 2, 3, 4, 5, 6, 7, 8, 9}에서 6부터 9까지의 요소를 제거하려면, 6의 인덱스를 찾고(여기서는 5), 그 인덱스부터 리스트 끝까지의 요소를 제거해야 합니다. List<int>의 인덱스는 0부터 시작하기 때문에, 6의 인덱스는 5가 됩니다.

    따라서, 다음과 같이 RemoveRange 메서드를 사용할 수 있습니다:

    List<int> stackAll = new List<int> {1, 2, 3, 4, 5, 6, 7, 8, 9};
    stackAll.RemoveRange(5, 4); // 5번 인덱스부터 4개의 요소를 제거합니다.

    여기서 5는 제거를 시작할 인덱스이고, 4는 제거할 요소의 개수입니다. 이렇게 하면 리스트에서 6, 7, 8, 9가 제거되고 1, 2, 3, 4, 5만 남게 됩니다.


    [You]

    stackAll.RemoveRange(5); 와 같이 사용해서 Index 5 이후의 원소는 모두 날려버리던가 하는 방법은 어렵니?

    [ChatGPT]

    List<T>RemoveRange 메서드는 두 개의 인자를 필요로 합니다: 제거를 시작할 인덱스와 제거할 요소의 개수입니다. RemoveRange 메서드는 인자 하나만 사용하는 오버로드가 없기 때문에, 인덱스 5 이후의 모든 원소를 제거하려면 다음과 같이 제거할 요소의 총 수를 계산해야 합니다:

    List<int> stackAll = new List<int> {1, 2, 3, 4, 5, 6, 7, 8, 9};
    int removeCount = stackAll.Count - 5; // 제거할 요소의 수를 계산합니다.
    stackAll.RemoveRange(5, removeCount); // 인덱스 5부터 removeCount만큼의 요소를 제거합니다.

    이 코드에서 stackAll.Count - 5는 리스트의 총 길이에서 5를 뺀 값이며, 이는 인덱스 5 이후의 모든 요소의 수를 의미합니다. 이렇게 계산한 값을 RemoveRange의 두 번째 인자로 사용하면 인덱스 5부터 리스트의 끝까지 모든 요소가 제거됩니다.


    반응형
    COMMENT
     
    01
    18

    과정명 : 내일배움캠프 Unity 게임개발 3기

    전체진행도 : 19일차

    부분진행도 : Chapter3 - 1일차

    작성일자 : 2024.01.18(목)

    개발일지 목록 : 클릭


    1. 진행중인 과정에 대해

    글자 서식을 정돈하기 위해, 티스토리의 글 작성법을 마크다운 위주로 바꾸었다. 특히 외부에서 마크다운 형식의 컨텐츠를 가져올 때 그대로 가져오기 위해서는 마크다운 모드를 쓰지 않으면 안되기 때문.

    기본모드/마크다운/HTML 모드를 오가는 도중에 손실되는 정보가 상당하기 때문에(특히 마크다운으로 전환 시, 이미지 속성등이 모두 사라지는 등, 마크다운에서 지원하지 않는 작성법은 모두 손실된다) 개발자로서 가장 필요로 하는 마크다운을 메인으로 하고, 필요 시 기본모드로 넘어가 추가적인 편집을 할 생각이다.

    새로운 6시간정도 분량의 새로운 유니티 입문 강의를 지급받아, 4일 간 강의를 보며 공부하고 개인과제를 하는 주차이다. 오늘 셔플한 새로운 조는 이전 조에 비해 많이 조용한듯.

    오후 3시정도까지는 너무 피곤하여 졸다 깨다를 반복하다가 이후에서야 강의를 확인하며 학습했다. 강의 난이도가 생각보다 높은 것을 보고 당황하였다. 저녁을 먹고 와서도 다들 조용히 자기 할 일 하는 중. 일단 강의를 두시간 분량(약 1/3정도?) 듣기는 했는데, 학습과 과제까지 하는 시간이 4일밖에 주어지지 않아 시간이 많이 빡빡한 편이다.

    이제 보니, 오늘은 강의를 수강하고 나머지 시간은 개인과제를 하도록 캠프 Docs에 적혀있다. 오늘 발제 세션이 끝난 뒤의 실 학습 가능한 시간이 식사시간과 일지 작성하는 시간 제외하고 6시간정도 였을텐데, 6시간의 강의를 이 시간에 소화하라는 것은 상당히 무리가 있지 않나 생각했다.

    2. 오늘 학습에 대해

    Input 요소의 설정 변경

    아래와 같은 키입력을 유니티 스크립트에서 사용 할 수 있는데

    float x = Input.GetAxis("Horizontal");

    Edit - Project Settings - Input Manager 에서 Horizontal, MouseX 등의 이름 변경이나 상세설정 등을 할 수 있다.

    GetAxis와 GetAxisRaw

    Input.GetAxis("Horizontal") 와는 달리 Input.GetAxisRaw("Horizontal") 는 스무스한 변화(-1 ~ 0 ~ 1) 없이 입력 순간부터 등속(-1,0,1)으로 움직이게 한다.

    *C# 문법, ?.

    OnMoveEvent?.Invoke(direction); 와 같이 ?. 을 사용하면, OnMoveEventNull이 아닐 경우, Invoke(direction)을 실행한다.

    유니티 라이센스 오류 해결

    수강을 위해 유니티 버전을 2022.3.2f로 낮춘 후, 패키지 관리자를 사용하려고 보니 라이센스 이슈가 있었다.

    라이센스 문제로 보여, 유니티 허브에서 라이센스 관리 메뉴로 이동.

    이처럼 Refresh 버튼을 누르면 라이센스의 갱신을 할 수 없다는 내용이 나온다.

    유니티 라이센스 Data 삭제 : 해당 스레드에서 유니티 데이터폴더를 지운 후 해결되었다는 덧글들이 많아 해보았지만 해결되지 않았다.

    라이센스 정보가 사라진 것은 덤.

    최하단 댓글 중 Unity Hub의 로그인을 다시 해서 해결되어다는 내용을 보고, 재로그인을 해 보았더니 해결되었다.

    라이센스 시작일이 꽤 오래전이라 뭔가 아깝다는 생각이 들어 복원방법을 찾아보다가 포기한 후, 새로 무료 라이센스를 얻기를 하였는데, 다행히 시작일이 예전에 쓰던 라이센스와 동일했다.

    라이센스 문제가 해결되어, 유니티에서의 패키지 관리자도 정상적으로 작동하였다.

    유니티 상태 현황판

    라이센스 문제를 해결하며 얻은 정보. 유니티 운영측의 상태를 표시해주는 서비스이다.

    유니티 상태 현황판

    Input System

    유저 입력에 대해 전체적으로 관리해주는 에셋.

    패키지 매니저에서 Input System을 설치하여 사용한다.

    Project 탭에서 우클릭 - CreateInput Actions를 생성해준 뒤, 더블클릭하여 열어준 후 새로운 스키마를 생성한다.

    생성한 스키마 예)Keyboard&Mouse 가 선택된 것을 확인 후, Action Maps에 새로운 항목을 추가한 후, 여러 입력과 그 입력에 대한 행동을 아래와 같이 작성한다.

    그리고, 이를 사용할 스크립트들도 만들어준다.

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.InputSystem;
    
    public class PlayerInputController : TopDownCharacterController
    {
        private Camera _camera;
        private void Awake()
        {
             _camera = Camera.main;
        }
        public void OnMove(InputValue value)
        {
            Debug.Log("OnMove"+value.ToString());
        }
        public void OnLook(InputValue value)
        {
            Debug.Log("OnLook" + value.ToString());
        }
        public void OnFire(InputValue value)
        {
            Debug.Log("OnFire" + value.ToString());
        }
    }

    Player오브젝트에 위 스크립트 컴포넌트를 추가.
    Player Input이라는 컴포넌트도 검색해 추가하며, Player InputActions 속성에 위에서 만들어둔 Input Action 도 추가한다.

    Behavior의 밑 내용을 보면, OnMove, OnLook, OnFire 등의 메서드가 스크립트 내에 있다면, 이벤트에 이 메서드들이 반응하도록 해 준다는 내용이라는 듯.

    해당 내용과 관련하여 너무 이해가 안되어 ChatGPT에게 여러번 질문하여 구조를 이해하였다.

    Player Input은 위에서 미리 매핑해둔 듯이(wasd는 Move), 'w'의 입력을 받으면 Send Messages 동작을 시도하는데, Player 오브젝트 내에 추가된 !!!모든 컴포넌트!!!를 뒤적이며 OnMove 메서드를 찾아 실행시킨다.

    PlayerInputController 내에 존재하는 OnMove 메서드를 도대체 어떻게 실행시키는지 연결점을 도무지 찾지 못했는데, 위와 같은 한 문장을 이해하고 나니 모든 의문점이 해결되었다.

    이어서 OnMove 메서드는 CallMoveEvent를 실행시키고

    CallMoveEvent는 미리 준비된 목록에 존재하는 메서드들을 모두 수행한다.

    이처럼 특정 이벤트가 발생하면 해당 이벤트를 필요로하는 모든 기능에 이벤트가 발생했다는 것을 알려주는 것을 옵저버 또는 구독 시스템 이라고 한다는 듯 하다.

    관련 QA 내용은 하단 GPT 문답에 적어두었다.

    스크립트 생성 시 내용 설정

    스크립트를 생성하면 주석과 함께 Start와 Update메서드가 적혀있는데, 이렇게 기본적으로 보이는 내용을 편집하는 방법이라고 한다.

    유니티 아이콘 우클릭 - 파일 위치 열기 - Data(폴더) - Resources(폴더) - ScriptTemplates(폴더) - 81-C# Script-NewBehaviourScript.cs

    해당 클래스의 내용을 수정한다.

    3. 과제에 대해

    • 강의 나머지 2/3분량 마치기
    • 개인 과제 진행하기

    4. 참고자료

    5. GPT4 문답

    유니티 프로젝트 이름 작명법

    [You]

    유니티 프로젝트 이름을 정하고 있는데, 보통의 경우 여러 단어로 이루어진 이름을 지을 때 대소문자 등 작명 규칙을 어떻게 사용하니?

    [ChatGPT]

    Unity 프로젝트의 이름을 정할 때, 일반적으로 "카멜 케이스(CamelCase)" 또는 "파스칼 케이스(PascalCase)"와 같은 작명 규칙을 사용합니다. 이러한 규칙은 여러 단어를 결합할 때 각 단어의 시작을 대문자로 표시하여 단어 간의 구분을 명확하게 합니다. 다음은 두 가지 주요 작명 스타일에 대한 설명입니다:

    1. 카멜 케이스 (CamelCase): 이 방식에서는 첫 단어를 소문자로 시작하고 이후의 각 단어는 대문자로 시작합니다. 예를 들어, myUnityProject와 같이 사용됩니다. 카멜 케이스는 주로 변수나 함수 이름에 사용됩니다.
    2. 파스칼 케이스 (PascalCase): 이 방식에서는 모든 단어를 대문자로 시작합니다. 예를 들어, MyUnityProject와 같이 사용됩니다. 파스칼 케이스는 클래스 이름, 네임스페이스, 프로젝트 이름 등에 자주 사용됩니다.

    프로젝트 이름을 지을 때는 일반적으로 파스칼 케이스를 사용하는 것이 좋습니다. 이는 프로젝트 이름이 종종 클래스나 네임스페이스와 유사한 형태로 사용되기 때문입니다. 또한, 프로젝트 이름은 의미를 명확하게 전달할 수 있도록 간결하고 기억하기 쉬운 이름을 선택하는 것이 좋습니다.

    예시:

    • SpaceAdventureGame
    • VirtualRealitySimulator
    • MobilePuzzleGame

    이러한 규칙은 프로젝트를 더욱 조직적이고 일관된 방식으로 관리하는 데 도움이 됩니다. 프로젝트 이름을 결정할 때는 프로젝트의 내용과 목적을 잘 나타낼 수 있는 이름을 선택하는 것이 중요합니다.


    Input System 에 대해

    [You]

    현재 Player에 다음 스크립트 컴포넌트가 추가되어 있어.

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.InputSystem;
    
    public class PlayerInputController : TopDownCharacterController
    {
        private Camera _camera;
        private void Awake()
        {
             _camera = Camera.main;
        }
        public void OnMove(InputValue value)
        {
            Vector2 moveInput = value.Get<Vector2>().normalized;
            CallMoveEvent(moveInput);
        }
        public void OnLook(InputValue value)
        {
            Vector2 newAim = value.Get<Vector2>();
            Vector2 worldPos = _camera.ScreenToWorldPoint(newAim);
            newAim = (worldPos - (Vector2)transform.position).normalized;
            if (newAim.magnitude >= .9f)
            {
                CallLookEvent(newAim);
            }
    
        }
        public void OnFire(InputValue value)
        {
            Debug.Log("OnFire" + value.ToString());
        }
    }

    그리고 Player에는 Player Input이라는 컴포넌트도 추가되어있는데, Actions 속성에 TopDownController2D 라는 InputAction 을 넣어놓은 상태야.

    Behavior은 Send Messages로 설정되어있고, 아래 적혀있는 설명을 보면
    Will SendMessage() to GameObject: OnDeviceLost, OnDeviceRegained, OnControlsChanged, OnMove, OnLook, OnFire
    라고 적혀있어.

    PlayerInputController라는 스크립트에 존재하는 OnLook, OnMove, OnFire 라는 메서드를 어떤 식으로 실행시키는지 연결고리가 어떻게 되어있는지 도무지 이해가 되지 않아.

    다른 스크립트도 참고삼아 적어둘게.

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class TopDownCharacterController : MonoBehaviour
    {
        public event Action<Vector2> OnMoveEvent;
        public event Action<Vector2> OnLookEvent;
    
        public void CallMoveEvent(Vector2 direction)
        {
            OnMoveEvent?.Invoke(direction);
        }
        public void CallLookEvent(Vector2 direction)
        {
            OnLookEvent?.Invoke(direction);
        }
    }
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class TopDownMovement : MonoBehaviour
    {
        private TopDownCharacterController _controller;
        private Rigidbody2D _rigidbody;
        private Vector2 _movementDirection = Vector2.zero;
    
        private void Awake()
        {
            _controller = GetComponent<TopDownCharacterController>();
            _rigidbody = GetComponent<Rigidbody2D>();
        }
        private void Start()
        {
            _controller.OnMoveEvent += Move; // 메서드를... OnMoveEvent에 추가하네
        }
        private void Move(Vector2 direction)
        {
            _movementDirection = direction;
        }
    }

    [ChatGPT]

    Unity의 PlayerInput 컴포넌트와 관련된 스크립트들이 어떻게 연결되어 있는지 궁금해하시는군요. Unity의 입력 시스템, 특히 PlayerInput 컴포넌트는 약간 복잡할 수 있지만, 기본적으로 이해하면 간단합니다. 각 부분을 단계별로 살펴보겠습니다.

    PlayerInput 컴포넌트

    PlayerInput 컴포넌트는 Unity의 새로운 입력 시스템의 핵심입니다. 이 컴포넌트는 입력을 처리하고, 입력에 대응하는 이벤트를 발생시킵니다. 여기서 중요한 것은 Behavior 설정입니다. "Send Messages" 옵션은 PlayerInput 컴포넌트가 입력 이벤트를 감지할 때, 이벤트에 해당하는 이름의 메서드를 해당 게임 오브젝트의 스크립트에서 호출하도록 합니다.

    PlayerInputController 스크립트

    이 스크립트에는 OnMove, OnLook, OnFire라는 메서드들이 정의되어 있습니다. 이 메서드들은 PlayerInput 컴포넌트에 의해 호출됩니다. 예를 들어, 사용자가 이동 키를 누르면 PlayerInput 컴포넌트는 OnMove 메서드를 호출합니다.

    OnMove 메서드는 입력 값을 받아서 이를 CallMoveEvent 메서드에 전달합니다. 이렇게 하면 TopDownCharacterController 클래스에 정의된 OnMoveEvent 이벤트가 발생합니다.

    TopDownCharacterController 스크립트

    이 스크립트는 OnMoveEventOnLookEvent라는 이벤트를 정의합니다. 다른 스크립트는 이 이벤트에 메서드를 등록할 수 있고, 이벤트가 발생하면 등록된 메서드가 호출됩니다.

    TopDownMovement 스크립트

    이 스크립트는 TopDownCharacterControllerOnMoveEventMove 메서드를 등록합니다. 그래서 PlayerInputController에서 OnMove 메서드를 호출하여 OnMoveEvent 이벤트를 발생시킬 때, TopDownMovementMove 메서드가 호출됩니다.

    연결 고리

    요약하면, 사용자의 입력은 PlayerInput 컴포넌트에 의해 감지되고, PlayerInputController 스크립트의 메서드를 호출합니다. 이 메서드들은 TopDownCharacterController의 이벤트를 발생시키고, 다른 스크립트에서 이 이벤트에 등록된 메서드들이 호출됩니다. 이 과정을 통해 입력이 실제 게임 내 행동으로 변환됩니다.

    [You]

    사용자의 입력은 PlayerInput 컴포넌트에 의해 감지되고, PlayerInputController 스크립트의 메서드를 호출합니다.
    라고 적혀있는데, PlayerInputController 가 해당 오브젝트의 컴포넌트로 추가되어있다는것 만으로, PlayerInputPlayerInputController의 안에있는 OnMove 등의 메서드에 바로 접근하는거야?

    [ChatGPT]

    네, 맞습니다. Unity의 새로운 입력 시스템에서 PlayerInput 컴포넌트는 입력과 관련된 이벤트를 감지하고, 이를 스크립트에 연결된 메서드로 전달합니다. 이 연결은 PlayerInput 컴포넌트의 설정과 스크립트에 정의된 메서드 이름에 의해 이루어집니다.

    PlayerInput 컴포넌트는 "Behavior" 설정을 "Send Messages"로 설정했을 때, 다음과 같이 작동합니다:

    1. 감지된 입력에 대응하는 메서드 이름 확인: 예를 들어, PlayerInput 컴포넌트의 "Actions" 설정에 "Move"라는 액션이 정의되어 있고, 사용자가 이동 입력을 하면, PlayerInput 컴포넌트는 "OnMove"라는 이름의 메서드를 찾습니다.
    2. 메서드 호출: 만약 PlayerInput 컴포넌트가 추가된 게임 오브젝트에 PlayerInputController 스크립트가 있고, 그 안에 "OnMove"라는 메서드가 정의되어 있다면, PlayerInput 컴포넌트는 이 메서드를 자동으로 호출합니다.

    이 과정은 PlayerInput 컴포넌트가 스크립트의 메서드 이름을 기반으로 자동으로 연결을 수행한다는 점에서 중요합니다. 따라서, PlayerInput 컴포넌트의 액션 이름과 스크립트의 메서드 이름이 일치해야 올바른 연결이 이루어집니다. 예를 들어, "Move" 액션이 있으면, 스크립트 내에 "OnMove"라는 메서드가 정의되어 있어야 합니다.

    [You]

    즉, PlayerInput은 게임 오브젝트 내의 모든 컴포넌트를 뒤적여서 OnMove 메서드를 찾는다는거지? 아니면 PlayerInputController라는 이름을 가진 컴포넌트 내만 탐색한다던가?

    [ChatGPT]

    Unity의 PlayerInput 컴포넌트는 "Send Messages" 동작 모드를 사용할 때, 해당 게임 오브젝트에 부착된 모든 컴포넌트를 대상으로 메서드를 탐색합니다. 즉, PlayerInput 컴포넌트는 특정한 스크립트 이름(예: PlayerInputController)을 찾는 것이 아니라, 게임 오브젝트에 부착된 모든 컴포넌트 안에서 해당 이벤트와 일치하는 메서드 이름(예: OnMove)을 찾습니다.

    이 과정은 다음과 같습니다:

    1. 입력 이벤트 감지: 사용자의 입력이 PlayerInput에 의해 감지됩니다.
    2. 메서드 탐색: PlayerInput 컴포넌트는 해당 게임 오브젝트에 부착된 모든 컴포넌트를 검사하여, 입력 이벤트와 일치하는 메서드 이름을 찾습니다. 예를 들어, 이동 입력에 대해서는 OnMove라는 메서드를 찾습니다.
    3. 메서드 실행: 일치하는 메서드가 발견되면, PlayerInput 컴포넌트는 그 메서드를 호출합니다.

    따라서, PlayerInputController라는 특정한 이름의 스크립트를 찾는 것이 아니라, 입력 이벤트에 대응하는 메서드를 게임 오브젝트에 부착된 모든 컴포넌트에서 찾아 호출합니다. 이렇게 하면, 여러 스크립트가 같은 게임 오브젝트에 존재하더라도 각각의 스크립트에서 필요한 입력 처리를 할 수 있습니다.


    반응형
    COMMENT
     
    01
    17

    과정명 : 내일배움캠프 Unity 게임개발 3기

    전체진행도 : 18일차

    부분진행도 : Chapter2.2 - 7일차

    작성일자 : 2024.01.17(수)

    개발일지 목록 : 클릭


    1. 진행중인 과정에 대해

    어제자 일지에, Velog로 환승한다는 내용을 적어놓았는데, 이제까지 티스토리에 적어놓은 것도 있고, 중간에 잘라서 Velog에도 도중부터 적혀있으면 모양새가 좀 그렇기 때문에 일단 티스토리를 계속 이용하기로 생각했다. 코드블럭 스타일이나 작성 중의 불편함은 이제까지도 어떻게든 잘 해왔기 때문에 조금 불편할 뿐이지 큰 문제는 아니다.

    오늘은 과제 발표날이다. 오전은 평소처럼 알고리즘 문제를 풀고, 오후 2시부터 발표를 시작하였다. 우리 팀의 발표는 다른 팀원분이 준비 및 발표를 하셨는데, 연습을 많이 하셨는지 리허설때보다 더 좋은 스피드와 딕션으로 프로젝트 소개를 잘 해주셨다. 팀 당 발표시간 7분정도를 준수하라고 공지가 되었었는데, 프로젝트 분량이 워낙 방대해서 10분, 15분을 넘기는 조가 대부분이었다. 개성적인 기능 구현 부분 등을 잘라내며 시간을 줄이고 소감 등도 다 쳐냈기 때문에 발표 내용이 부실해졌다는 느낌이 강했는데, 아쉽지만 발표시간을 준수했다는 사실에 대해 만족해야 할 듯.

    오후에는 치킨이랑 커피가 걸린 미니게임 이벤트와 '좋은 개발자란'을 주제로 강의 세션이 열렸다. 발표를 마치고 잠시 쉬어가며 보내는 시간이었다.

    팀이 바뀌기 전 마지막 밤, 팀원들과 진로 계획과 방향성, Unity나 프로젝트 관련하여 정보 공유 등 이런저런 얘기를 하며 하루를 마무리했다.

     

    2. 오늘 학습에 대해

    아래는 다른 조들의 발표를 보며 아이디어가 좋았거나 배울 점 등 인상적인 부분을 위주로 메모된 내용이다.

    <10조 독점 메모>
    리포지토리의 Wiki 탭 활용하여 작성 및 설명
    뭐야? 다이어그램 뭐쓴거임? 나중에 다시 돌려보자 > DB스키마?
    스토리 스크립트 재생 ㅋㅋㅋㅋㅋㅋㅋㅋㅋ 와......
    퀘스트 디테일 뭔데...? 이걸 구현했다고?
    전직 뭐임.....? 아니 진짜뭐임? 어케함?
    낚시 미니게임 뭔데...
    물고기 팔기는 또 뭔데....... 뭐임
    아이템 강화는 또 뭔데...
    똑같은 7일짜리 프로젝트가 맞는지 의심 될 정도의 아이디어와 퀄리티

    <13조 독점 메모>
    피피티에 기능별로 설명해놓는 게 무난한듯
    공방 외에 다양한 스탯 : Key Value 형태로 저장?
    가능한 글자색을 밝게 해야 발표에서는 보기 좋은 듯.
    장비에 따라 마이너스 스탯도 있음 ㅋㅋㅋ
    던전 내 같은 종류의 몬스터에 A,B...등을 이름 뒤에 붙여 구별
    속도 스탯도 있어, 플레이어보다 빠를 시 몬스터가 먼저 공격하는 등 행동
    몬스터도 스킬? 찢었다...
    기본적으로 폰트를 ConsoleColor.White로 한듯. 밝아서 보기 좋은듯? 구현 상으로 어려울 수 있음.
    바이너리 저장 -> 클래스 자체를 직렬화 할 수 있어 구현이 편함, 보안성 강화
    PPT 마지막화면 센스(코딩해요 콘)
    기능구현에 있어 모티브가 된 게임에 대해 소개

    <미스>
    인벤토리에 '회복약' 넣는 거 깜빡함

    <발표>
    PPT 예쁜 템플릿과 깔끔한 정리
    다른 수강생들에게 도움이 될만한 내용을 끼워넣으면 좋을듯
    회고, 어떤 시도를 했는지 등. 기술적 회고가 있으면 더 좋음.
    현장 시연을 위한 치트키
    깃로그 시각화

    <기술과 디자인>
    콘솔 내에 이모지 사용 가능한듯
    스킬명과 수치에 모두 색깔 넣어주기
    레벨업 시 스탯포인트 아이디어
    직업별 다른 상점물품 장비
    MP포션
    저장 슬롯 여러개

    <고급 용어 사용>
    예)던전 스케일링

    <이외 피드백>
    어택 등 플레이어가 사용하는 기능들은 플레이어 클래스에 모아두는 등 하는 게 좋음
    코드컨벤션 준수, 커밋 메시지 등도 신경쓰기
    메인브렌치에서 여러 브렌치로 나누어 사용하는 방법 좋음

    <???>
    30개 이상의 cs파일(17조 이미지)
    제비뽑기로 발표자를 정함
    진행을 맡은 *** 매니저님은 회고 파트를 좋아하시는듯

    <우리팀 피드백>
    화면공유를 해도 작아서 잘 보이지 않는 부분이 곳곳에 존재. 음량 세팅과 함께 체크하면 좋을 듯.

     

    3. 과제에 대해

    오는 주차는 다시 Unity로 복귀하여 개인과제를 진행한다. 앞으로도 대략 한 주 단위로 프로그램이 바뀌는 듯 하다.

    당장 마주하는 프로젝트도 잘 참여하고, 최대한 알찬 결과물을 뽑아가고싶다.

     

    반응형
    COMMENT
     
    01
    16

    과정명 : 내일배움캠프 Unity 게임개발 3기

    전체진행도 : 17일차

    부분진행도 : Chapter2.2 - 6일차

    작성일자 : 2024.01.16(화)

    개발일지 목록 : 클릭


    1. 진행중인 과정에 대해

    과제 제출과 발표에 대해 회의, 게임 내 장면의 사이클 위주로 발표를 진행하고, 시연에서 혹여나 빠진 내용은 슬라이드에 별첨하여 설명을 할 예정. 팀원 각자의 소감을 발표 마지막에 배치할 예정이다.

    티스토리 글작성이나 테마 그리고 코드블럭 등의부분에서 불편하다고 느껴져서 내일 글부터는 Velog를 사용 해 볼 생각이다.

     

    2. 오늘 학습에 대해

     

    (1) 알고리즘 풀이 : 풀이 중 검색을 했던 내용을 위주로  작성

     

    프로그래머스 - 두 개 뽑아서 더하기

    • 전체 코드
    using System;
    using System.Collections.Generic;
    
    public class Solution {
        public int[] solution(int[] numbers) {
            int[] answer = new int[] {};
            // 동적관리를 위한 List<int> 사용
            List<int> answerList = new List<int>();
            
            for(int i=0; i<numbers.Length-1; i++){
                for(int j=i+1; j<numbers.Length; j++){
                    int sum = numbers[i]+numbers[j];
                    if (!answerList.Contains(sum)) {
                        answerList.Add(sum);
                    }
                }
            }
            answerList.Sort();
            answer = answerList.ToArray();
            return answer;
        }
    }
    • 코드 특징
      • 좋은 로직이 떠오르지 않아 정공법 작성
    • 매번 나오는 값을 array에 추가하야 하는 경우 동적 관리를 위해 List로 바꾸어 사용하는 것이 좋다.
      • List와 그 메서드는 아래와 같이 사용 가능
    // 리스트를 사용하기 위해 using
    using System.Collections.Generic;
    
    // 리스트에 요소를 추가
    answerList.Add(value);
    
    // 리스트 내에 요소가 들어있는지 확인
    if (!answerList.Contains(value)) {}
    
    // 리스트 정렬
    answerList.Sort(); // 리스트 정렬
    
    // answerList라는 List<T> 객체의 모든 요소를 포함하는 새로운 배열을 생성 후, answer라는 배열 변수에 할당
    answer = answerList.ToArray();

     

    프로그래머스 - 가장 가까운 같은 글자

    • 전체 코드
    using System;
    using System.Collections.Generic;
    
    public class Solution {
        public int[] solution(string s) {
            int[] answer = new int[s.Length];
            
            // O(N^2)도 되겠지만, 더 효율적인 알고리즘으로...
            // s가 영어 소문자로만 이루어져 있다고 하니, 26개 소문자의 index 리스트를 작성하자
            Dictionary<char, int> alphaIndex = new Dictionary<char, int>();
            
            // a부터 z까지 -1 설정
            for (char c = 'a'; c <= 'z'; c++) {
                alphaIndex.Add(c, -1);
            }
            
            // s를 처음부터 순회하며 answer와 alphaIndex 갱신
            for (int i = 0; i<s.Length; i++){
                if(alphaIndex[s[i]]==-1) answer[i] = -1;
                else answer[i] = i-alphaIndex[s[i]];
                
                alphaIndex[s[i]] = i;
            }
            // 시간복잡도 O(N)
            return answer;
        }
    }
    • 코드 특징
      • 시간복잡도 O(N)으로 해결
      • 사전형에 26개의 알파벳을 키로 갖는 index를 마련하여, s를 순회하며 매 문자마다 즉각 갱신
    • C#에서 영문 소문자 26개를 key로, int형을 value로 쓰는 사전형을 사용하는 법
      • Dictionary<char, int> 타입을 사용하여 수행. 아래와 같이 사용 가능
    // 사전형을 사용하기 위해 using
    using System.Collections.Generic;
    
    // 선언과 초기화
    Dictionary<char, int> dict = new Dictionary<char, int>();
    
    // 키값 'a'부터 'z'까지, -1 값으로 로 초기화
    for (char c = 'a'; c <= 'z'; c++) {dict.Add(c, -1);}
    
    // 사전형 값 설정
    dict['a'] = 1; // 'a'의 값을 1로 설정
    
    // 미리 설정해주지 않은 키 값에 위 구문처럼 값을 대입하려고 하면, KeyNotFoundException 발생
    // TryGetValue, ContainsKey 메서드를 사용하여 키가 존재하는지 확인하는 조건문을 두면 좋음
    if (dict.TryGetValue('a', out value)) {}
    if (dict.ContainsKey(key)) {}
    
    // 사전 내용 출력
    foreach (KeyValuePair<char, int> entry in dict) {
        Console.WriteLine($"Key: {entry.Key}, Value: {entry.Value}");
    }

     

    3. 과제에 대해

    • 발표 돕기
    • Velog 환승작업

     

    4. 참고자료

    • 없음
    반응형
    COMMENT
     
    01
    15

    과정명 : 내일배움캠프 Unity 게임개발 3기

    전체진행도 : 16일차

    부분진행도 : Chapter2.2 - 5일차

    작성일자 : 2024.01.15(월)

    개발일지 목록 : 클릭


    1. 진행중인 과정에 대해

    콘솔 던전 RPG 게임 팀 과제의 개발부분은 오늘 마무리되었다. 큰 문제가 나오지 않는다면, 내일 발표에 관해서 회의를 진행하고 발표 자료와 준비를 할 예정으로 보인다.

    아침에 1시간씩 진행하는 CS는 Level1(캠프에서 문제 잔뜩 모아놓고 레벨 매겨줌)부터 시작해서 현재 Level3 문제들을 지나고 있는데, 슬슬 한 문제당 푸는 시간이 늘어나는 게 느껴진다. 아직 간단한 문제지만 문제당 이제 평균 10분을 넘어가는 듯 한 기분. 문제를 풀기보다 매번 내가 찾아보는 게 대체 무엇인지 메모해두는 게 좋을 것 같다. 오답노트 공부하듯이 나중에 CS부분만 몰아보면 좋을 듯. 유감이지만 오늘은 메모한 것이 없다.

     

    2. 오늘 학습에 대해

    콘솔 UI에 파티클 로직을 구현하였다. 모든 파티클이 랜덤으로 깜빡인다거나 하는 건 아니고, 주기적으로 파티클 문자 n개를 화면에 띄우고 지우고를 반복한다.

    콘솔 화면에 파티클 이펙트를 보여주는 코드의 진행

    string titleArt = "{아\n스\n키\n 아\n트}" // 콘솔에 출력할 내용
    InitializeScreenBuffer(titleArt); // 콘솔에 출력하기 전, 내용을 저장
    DrawAsciiArt(titleArt); // 콘솔에 WriteLine() 할 뿐인 메서드
    
    // '+' 문자를 화면에 {num}개 만큼 생겼다 없어지는 파티클 이펙트.
    // 너비 95, 높이 41의 범위에 800ms마다 갱신되도록 했다.
    int width = 95; int height = 41; int num = 15; int msDelay = 800;
    effectThread = new Thread(() => UpdateEffect(width, height, num, msDelay)); effectThread.Start();
    
    // 키 입력을 받으면 스레드를 종료하여 다음 장면으로 넘어갈 준비를 한다.
    Console.ReadKey();
    keepRunning = false; // 스레드에 종료 신호를 보냄
    effectThread.Join(); // 스레드가 종료될 때까지 대기
    
    // 다음 장면으로 넘어감


    (1) InitializeScreenBuffer
    해당 메서드의 목적은 콘솔 화면에 표시할 내용을 '버퍼(buffer)'라는 메모리 구조에 저장하는 것

    버퍼 : 데이터를 임시로 저장하는 메모리 공간

    static void InitializeScreenBuffer(string s) // 내용을 버퍼에 저장하기
    {
        int width = Console.WindowWidth;
        int height = Console.WindowHeight;
        screenBuffer = new char[height, width]; // screenBuffer는 함수 밖에 전역변수로 선언을 해두었다
        // 아스키 아트를 버퍼에 저장
        using (var reader = new StringReader(s))
        {
            string line;
            int y = 0;
            while ((line = reader.ReadLine()) != null)
            {
                for (int x = 0; x < line.Length; x++)
                    screenBuffer[y, x] = line[x];
                y++;
            }
        }
    }

    Console.WindowWidth 와 Console.WindowHeight 를 사용하여 현재 콘솔 창의 너비와 높이를 불러온다.
    콘솔 창의 너비와 높이에 해당하는 배열의 크기를 갖는 2차원 배열 screenBuffer 를 생성.
    screenBuffer 는 버퍼의 역할을 하며, 콘솔 화면에 표시될 각 문자의 위치를 저장.
    + Height를 콘솔의 크기보다 높게 설정해야 하는 경우도 생길 수 있지만, 일단은 콘솔 높이를 사용.

    StringReader를 사용하여 주어진 문자열 s 를 한 줄씩 읽는다.
    각 줄을 순회하며 각 문자를 screenBuffer 배열에 저장.
    y 변수는 현재 처리 중인 줄(행)의 번호. 
    x 변수는 줄 내의 문자(열) 위치.

    screenBuffer에는 asciiArt의 각 문자가 콘솔 화면상의 해당 위치에 매핑된다.
    이후에 콘솔 화면에 어떤 변화가 필요할 때, 이 버퍼를 참조하여 어떤 문자가 원래 어느 위치에 있었는지 알 수 있다.

    예시) "AB\nCD" 를 버퍼에 저장
    screenBuffer[0, 0] = 'A'
    screenBuffer[0, 1] = 'B'
    screenBuffer[1, 0] = 'C'
    screenBuffer[1, 1] = 'D'

    (2) 이후 콘솔에 내용을 출력하고( DrawAsciiArt(titleArt) )

     

    (3) 매개변수로 (1) 파티클이 나올 직사각형 범위, (2) 파티클 갯수, (3) 깜빡이는 주기 를 넘겨 일정 시간마다 랜덤한 위치에 파티클 효과가 보이도록 한다.

    + 지금 더 갱신된 코드는 파티클로 쓸 문자 char, 버퍼를 사용하지 않아도 작동하도록 하는 bool isBuffer 등을 추가하여 확장성을 높였다.

    파티클이 지워지는 타이밍에, 버퍼에 저장되어 있던 내용을 기반으로 원래 문자를 복원한다.

     

    (4) 마지막으로 키 입력을 받으면 스레드를 종료하도록 조건문을 넣어두었다.

     

    콘솔 내 전각문자의 처리

    위 내용의 구현 중 가장 큰 문제는 전각기호의 처리였는데, 반각문자인 파티클 '+'가 전각기호인 한글 문자 자리 등에 나타난다면, 한글문자가 이리밀리고 저리밀리고 해서 난장판이 되어버린다. 그래서 해당 위치가 원래 전각기호가 위치했는지 판별하는 동시에 전각기호의 왼쪽 혹은 오른쪽 중 어느 쪽에 '+'가 들어갔는지 판별하여 " +" 또는 "+ "와 같이 공백문자를 포함하여 해당 전각문자를 덮도록 설계하였다.

    또한 전각기호를 복원할 때에도, 한칸 밀린 방향에 복원되지 않도록 조건 분기를 잘 나누어 제대로 된 위치에 다시 복원되도록 신경썼다.

    위의 두 요소를 신경쓰니 전각문자가 이리저리 치이는 문제는 사라졌다.

    아래는 문자가 전각문자인지 판별하는 코드이다

    아마 모든 전각문자를 포함하고 있진 않을 것. 대표적인 범위만 설정.

    static bool IsFullWidth(char c) // 전각문자인지 판별
    {
        int[] ranges = { 
            0x1100, 0x115F, // 한글 자음과 모음
            0x2E80, 0x2EFF, // CJK(중국, 일본, 한국) 급진 부호
            0x3000, 0x303F, // CJK 심볼 및 구두점
            0x3200, 0x32FF, // CJK 호환성 음절
            0x3400, 0x4DBF, // CJK 통합 한자 확장
            0x4E00, 0x9FFF, // CJK 통합 한자
            0xAC00, 0xD7AF, // 한글 음절
            0xF900, 0xFAFF, // CJK 호환성 한자
            0xFE30, 0xFE4F, // CJK 호환성 형태
            0xFF01, 0xFFEF, // 하프와이드 및 풀와이드 형태
        };
        int code = (int)c;
    
        for (int i = 0; i < ranges.Length; i += 2)
        {
            if (code >= ranges[i] && code <= ranges[i + 1])
            {
                return true; // 전각
            }
        }
        return false; // 반각
    }

    전각문자 판별 메서드는 콘솔 한정 앞으로도 유용하게 써먹을 수 있을 것으로 보인다. 예를 들어 영어와 한글이 섞인 문자열끼리 폭을 같게 맞추도록 공백문자를 채워넣는 등 당장 생각나는 것도 몇 개 있다.

     

    실행파일 생성

    마지막으로 실행파일로 만들어서 다른 경로에서 직접 실행 해 보자고 갑자기 생각나서 해 본 내용.

    크게 과정이 복잡하진 않았고, 

    1. 빌드 세팅(1) - 프로젝트 속성

    2. 빌드 세팅(2) - 대상 OS 버전 대충 win7 등 고름

    3. 빌드 - Debug에서 Release로 바꿔준 후, 빌드

    4. exe 파일 기본 생성 위치, 다른 곳으로 가져갈 때 파일 세개만 챙기면 잘 실행되는 것으로 보임

    우리 조 슬랙에 설명용 쓰려고 대충 찍은 스크린샷이라 크기가 들쭉날쭉하다.

    저렇게 생성된 파일 중, 체크표시한 유형 세가지만 챙겨가면, 다른 환경에서도 잘 실행 될 것으로 예상된다.

     

    3. 과제에 대해

    • 내일부터 CS 중 검색하는 내용 생기면 무조건 적어두기
    • 발표 준비 돕기(본인 발표자가 아님)

     

    4. 참고자료

    반응형
    COMMENT
     
    01
    13

    C# 공부를 하며 계속 긴가민가 헷갈렸던 내용이 있어 정리.

    2차원 이상의 배열을 사용하려고 할 때, 두 가지 방식으로 배열을 사용 할 수 있다.

    int[,] array = new int[3, 2];
    
    int[][] jaggedArray = new int[3][];

    위의 방식처럼 [ , ] 한개의 대괄호 안에 쉼표가 들어가있는 모습과, 아래의 방식처럼 [ ][ ] 두 쌍의 대괄호를 사용하는 방식이다.

    (어느쪽이 맞는지 매번 헷갈리다가, 둘 다 사용한다는 걸 이제 알았음)

     

    1. 다차원 배열(Rectangular Arrays)

    int[,] array = new int[3, 2];

    괄호 내 쉼표를 사용하여 다차원 배열을 사용할 수 있다.

    특징으로는 모든 행이 동일한 수의 열을 갖는다는 것.

     

    2. 가변 배열(Jagged Arrays)

    int[][] jaggedArray = new int[3][];
    jaggedArray[0] = new int[4];
    jaggedArray[1] = new int[5];
    jaggedArray[2] = new int[3];

    가변 배열(Jagged Arrays) 또는 배열의 배열(Array of Arrays) 로 불리는 방식.

    위의 예시에서는 첫 번째 배열(jaggedArray[0])은 4개의 요소를 갖고, 두 번째 배열(jaggedArray[1])은 5개의 요소를 갖는 등 각 행마다 다른 수의 요소를 가질 수 있다.

     

    3. 두 배열의 차이점

    구조: 다차원 배열은 모든 행과 열의 크기가 같은 반면, 가변 배열은 각 행이 서로 다른 크기를 가질 수 있다.

    메모리 할당: 다차원 배열은 단일 연속된 메모리 블록에 할당되는 반면, 가변 배열은 각 행마다 별도의 메모리 블록에 할당된다.

    성능: 일반적으로 가변 배열은 다차원 배열보다 접근 속도가 빠르다. 이는 다차원 배열이 메모리에서 더 복잡하게 처리되기 때문.

    사용 용도: 다차원 배열은 데이터가 균일한 그리드 형태일 때 유용하고, 가변 배열은 각 행의 길이가 다를 수 있는 더 복잡한 데이터 구조를 다룰 때 유용하다.

    반응형
    COMMENT
     
    01
    12

    과정명 : 내일배움캠프 Unity 게임개발 3기

    전체진행도 : 15일차

    부분진행도 : Chapter2.2 - 4일차

    작성일자 : 2024.01.12(금)

    개발일지 목록 : 클릭


    1. 진행중인 과정에 대해

    팀 과제(콘솔 텍스트 던전 RPG) 진행률이 거의 막바지이다. 오는 월요일에 던전 내 전투 중 장면 구성을 손질하면 게임 내적인 부분은 완료가 되어있을 것 같다. 

    UI.cs 클래스를 새로 마련하여 플레이어의 선택지 패널, 아스키아트 프리셋, 텍스트와 콘솔에 관련된 편의기능을 압축한 메서드 등 여러가지를 구현하고, 디자인 개선을 마쳤다. 전후 이미지를 같이 보여주면 좋을 것 같은데 그렇게까지 하기에는 에너지가 모자라다. 기록으로 남기는게 무조건 좋을텐데 라고 생각 중.

    매일 그렇지만 요즘 다른 하는 일이 없어서 그런지 팀 과제랑 알고리즘 풀기에 모든 에너지를 쏟고 있다. 수면 시간도 너무 적어져서 그런지 두통이 심해졌다.

     

    2. 오늘 학습에 대해

    팀 과제

    Github Desktop으로 하루 종일 팀 과제를 같이하다보니 깃은 이제 수월하게 다루고 있는 것 같다. 특히 팀원들이 Pull Request를 수행하기 전 Main함수로부터 업데이트를 잘 한 뒤에 PR을 진행했기 때문에 충돌이 거의 없었던 편.

    오늘은 UI의 끝을 보자고 생각하여(갑자기 그런 기분이 들어) 구현을 많이 하였는데, 그 내용은 아래와 같다.

    1. 타이틀 구현과 전반적인 UI 스타일 구현

    타이틀이 필요하다고 개인적으로 종종 생각이 들었었기에, 오늘 UI 구현을 하며 타이틀 장면도 마련 해 두었다.

    아스키아트가 화면을 꽉 채우도록 하고, 특정 좌표로 커서를 옮겨 텍스트를 구현하였다. 이 상태에서 아무 키나 누르면

    데이터를 불러온다는 안내 메시지를 잠시 보여준 후, 다음 장면으로 넘어간다. 가운데 정렬이 말끔해보여서 좋았다.

    캐릭터 생성 장면은 더 화려하게 꾸밀 수 있었지만, 생성 단계는 처음이라는 느낌답게 조촐하게 보여주는 것도 좋지 않을까 생각하게 되어, 필요로 하는 간단한 텍스트만 표시하도록 하였다.

    그래도 왼쪽에 여백을 두거나 줄바꿈을 두고, '>>>' 문자를 두는 등 가독성에 신경을 썼다. 뒤따라 나오는 직업 선택이나 확인 장면도 거의 동일

    직업까지 선택을 하면 아래와 같은 메인 메뉴를 볼 수 있다.

    메인 메뉴이지만 현재 아이디어 고갈로 검소한 디자인을 하고있다.

    우측 빈 부분에 아트를 넣거나 하면 좋을듯.

    신경쓰이는 점은 상단 아스키아트가 너무 긴데, 내가 작업한 부분이 아니라 건드리기 조심스러운 부분도 있고 마땅한 대체재를 생각해 두지 않았기 때문에 일단은 현상유지이다.

    메인메뉴를 제외하면 모두 하단의 유저 입력 전용 패널과 동일한 폭으로 맞춰두었다. 예를 들어 회복아이템 메뉴는 아래와 같이 균형잡힌 배치를 가지고 있다.

    유저에게 보여지는 장면의 크기에 맞추어 콘솔창의 크기도 줄여보는 시도를 했었지만, 어째서인지 콘솔 폭을 120자 미만으로 줄이게 되면 콘솔창 하단에 좌우 스크롤바가 생겨버리기 때문에 일단 콘솔창은 넓게 쓰고 있다.

    메인 컨텐츠인 던전에 들어가는 입구 장면이다. 간단한 아스키아트를 넣어두었다.

    E 키를 통해 내 정보를 열어볼 수 있는데(정보열기는 다른 팀원이 구현)

    스테이터스는 밝은 노란색으로 표시되게 하여 가독성을 높였고, 길어진 높이만큼 빈 공간에 아트도 더 넣어두었다.

     

    2. UI.cs

    오늘 UI.cs 파일을 새로 작성하고, 구현한 메서드가 여럿 있다.

     

    (a) 유저 입력 패널

    public static string UserInput(string alert="", bool positive=false, string reqMsg= "다음 행동을 입력하세요.")
    {
        Console.WriteLine("");
        Console.WriteLine(".──────────────────────────────────────────────────────────────────── .");
        Console.WriteLine($"  {reqMsg}");
        Console.WriteLine("");
        Console.WriteLine(""); Console.ForegroundColor = (positive)?ConsoleColor.Green:ConsoleColor.Red;
        Console.WriteLine($"  {alert}"); Console.ResetColor();
        Console.WriteLine("  >>>"); 
        Console.WriteLine("'──────────────────────────────────────────────────────────────────── '");
        Console.SetCursorPosition(6, Console.CursorTop-2);
        return Console.ReadLine();
    }

    UserInput()을 사용하여 위와 같은 패널을 그린다.

    경고 메시지와, 경고메시지의 성격(true:녹색 / false:빨간색), 그리고 기본 메시지인 "다음 행동을 입력하세요." 도 바꾸어 매개변수로 전달할 수 있다.

     

    (b) 아스키아트

    게임 내 모든 아스키아트를 모아두었다. 열거형인 AsciiPreset에 각자 정해둔 이름을 사용하여 AsciiArt 메서드를 호출한다.

    // 열거형
    public enum AsciiPreset
    {
        TitleArt,
        CreateCharacter,
        SelectClass,
        MainMenu,
        Battle,
        Status,
    	...
    }
    
    // 아스키아트 호출
    public static void AsciiArt(AsciiPreset preset)
    {
        ConsoleColor statusColor = ConsoleColor.Gray;
        int x, y;
        switch (preset)
        {
            ...
            case AsciiPreset.PotionInventory:
                Console.WriteLine("                                                                     ");
                Console.WriteLine("    .-.   p--- .'~`. .-=~=-.   :~:      |~|   .-~~8~~-.  |~|  .-.    ");
                Console.WriteLine("  .'__( .'~`.  `. .'  )___(  .'   `.    | |   |~~---~~|  | |  )__`.  ");
                Console.WriteLine("  | l | | m |  .'n`. (  o  ) |  p  |] .' q `. |   r   | .'s`. | t |  ");
                Console.WriteLine("  |___| |___|  `._.'  `._.'  |_____|  `.___.' `._____.' `._.' |___|  ");
                Console.WriteLine("                                                                     ");
                Console.ForegroundColor = statusColor;
                Console.WriteLine("----------------------------[회복 아이템]----------------------------");
                Console.ResetColor();
                Console.WriteLine("");
                break;
            ...
            case AsciiPreset.DungeonEntrance3:
                x = 41; y = 25;
                DrawOnSpecificPos("" +
                    "      |\\      _,,,---,,_\n" +
                    "ZZZzz /,`.-'`'    -.  ;-;;,_\n" +
                    "     |,4-  ) )-,_. ,\\ (  `'-'\n" +
                    "    '---''(_/--'  `-'\\_)\n",
                    x, y);
                break;
            ...
        }
    }

    간단하게 줄여본 코드는 위와 같다. 그냥 적어둔 내용대로 Write를 하고 있기 때문에 복잡한 내용은 없다.

    고양이 아트를 예로 든 DrawOnSpecificPos(string, int x, int y) 메서드는 특정 좌표를 지정하여 그릴 수 있도록 마련해둔 메서드이다. 그 내용은 아래와 같다.

     

    (c) 특정 좌표에 그림을 그리는 DrawOnSpecificPos 메서드

    public static void DrawOnSpecificPos(string s, int xInput, int yInput)
    {
        // 커서 위치를 저장해두어, Draw가 끝나면 커서 위치를 원상복귀
        int ySave = Console.CursorTop;
        int xSave = Console.CursorLeft;
        int x = xInput;
        int y = yInput;
        string[] lines = s.Split('\n');
        foreach (string line in lines)
        {
            Console.SetCursorPosition(x, y);
            Console.Write(line);
            y++;
        }
        Console.SetCursorPosition(xSave, ySave);
    }

    진행중이던 커서 위치는 저장해두고, 특정 위치를 기점으로 아트를 그린 후, 원래의 커서 위치로 되돌아온다. 이렇게 만들어진 것이 아래의 고양이와 캠프장.

     

    (d) 텍스트에 색을 입히는 메서드들

    public static void ColoredWriteLine(string s, ConsoleColor color)
    {
        Console.ForegroundColor = color;
        Console.WriteLine(s);
        Console.ResetColor();
    }
    public static void ColoredWrite(string s, ConsoleColor color)
    {
        Console.ForegroundColor = color;
        Console.Write(s);
        Console.ResetColor();
    }
    public static void RandomColoredWrite(string s)
    {
        Random random = new Random();
        foreach (char c in s)
        {
            Console.ForegroundColor = (ConsoleColor)random.Next(9, 16);
            Console.Write(c);
        }
        Console.ResetColor();
    }

    메서드 이름만큼 직관적으로, 그리고 한 줄로 사용할 수 있어 나름 편리할 메서드이다.

    Console.Write 대신 사용할 수 있고, ConsoleColor.Red 와 같은 매개변수를 함께 넣어 색을 지정해준다.

    // 단일 사용
    UI.ColoredWriteLine("골드", ConsoleColor.Yellow);
    
    // 연계 사용
    Console.WriteLine($"골드를 {UI.ColoredWriteLine("500", ConsoleColor.Yellow);} 만큼 획득!");

    그냥 사용해도 편하고, 더 좋은 점은 문자열 도중에 특정 단어만 색을 입히는 것이 편하다. 위 내용을 메서드 없이 깡으로 구현한다면 아래와 같이 매우 길어지게 된다.

    // 예시1
    Console.ForegroundColor = color;
    Console.WriteLine("골드", ConsoleColor.Yellow);
    Console.ResetColor();
    
    // 예시2
    Console.Write($"골드를 ");
    Console.WriteLine("골드", ConsoleColor.Yellow);
    Console.Write($"{"500"}");
    Console.ResetColor();
    Console.WriteLine($" 만큼 획득!");

    Write와 WriteLine을 섞어 쓸 수 밖에 없게 되버리는 점도 문제점.

    RandomColoredWrite(string s) 메서드는 받은 문자열을 문자 단위로 랜덤한 색을 입히게 하는 신나는 메서드이다.

     

    알고리즘 풀이 중 자주 잊어버릴 만한 내용에 대해

    캠프에서 소개하는 알고리즘 문제 중 40문제 이상을 풀었지만, 자주 잊어버려 찾아보고 코드 작성을 했던 내용이다.

    복습하는 기분으로 문제들을 다시 열어보며 작성하였다.

     

    No.3[120805 몫 구하기]

        public int solution(int num1, int num2) {
            int answer = num1 / num2;
            return answer;
        }

     

    • int 형끼리의 나눗셈 계산은 int 형을 반환한다.

     

    No.7[120806 두 수의 나눗셈]

        public int solution(int num1, int num2) {
            int answer = 0;
            answer = (int)((float)num1/(float)num2*1000);
            return answer;
        }
    • float로 캐스트 된 수 끼리 나눗셈을 해야 제대로 된 결과가 나온다.
    • 현재 문제에서는 결과값의 소수 부분을 버리고 정수값만 출력.

     

    No.10[120817 배열의 평균값]

        public double solution(int[] numbers) {
            double answer = 0;
            for(int i=0; i<numbers.Length; i++){
                answer += numbers[i];
            }
            answer /= numbers.Length;
            return answer;
        }
    • 배열을 전달받을 때에는 int[] numbers 의 형식을 사용.
    • 배열은 .Length 를 사용하여 길이를 반환한다. 리스트의 경우는 .Count 가 길이를 반환한다. 둘 다 뒤에 ()가 없음에 주의.

     

    No.11[12937 짝수와 홀수]

    • (영단어)짝수는 'Even', 홀수는 'Odd' 

     

    No.12[12944 평균 구하기]

        public double solution(int[] arr) {
            double answer = 0;
            answer /= arr.Length;
            ...
        }
    • 위와 같이 double 형을 int 형으로 나눌 경우에는 오류가 발생하지 않음. 변수 간 포함관계에 따라서 허용되는 것으로 추정.

     

    No.13[12931 자릿수 더하기]

        public int solution(int n) {
            int answer = 0;
            string nStr = n.ToString();
            foreach(char c in nStr){
                answer += int.Parse(c.ToString());
            }
            return answer;
        }
    • '문자열' 을 int형으로 바꿀 경우에는 int.Parse() 사용. 문자열이 아닌 문자 는 사용할 수 없기 때문에 .ToString() 메서드를 통해 문자열로 변환 해 주어야 한다.
    • foreach문은 foreach(자료형 이름 in nStr){ 순회 내용 } 과 같이 작성. in 키워드 말고도 다른 방식도 여럿 있을 것으로 생각되어짐.

     

    No.16[12954 x만큼 간격이 있는 n개의 숫자]

        public long[] solution(int x, int n) {
            long[] answer = new long[n];
            for(int i=1;i<=n;i++){
                answer[i-1] = (long)i*(long)x;
            }
            return answer;
        }
    • 일부 테스트케이스에서 실패하는 경우, 오버플로가 일어나는지 확인하자.
    • 해당 문제는 int의 범위를 벗어나는 값을 다루기 때문에 long으로 캐스팅하여 해결.

     

    No.17[12932 자연수 뒤집어 배열로 만들기]

    int[] answer = new int[numStr.Length];
    • 새로운 배열의 선언을 해 줄 때의 형식
    answer[index++] = (int)(num%10);
    • 전치 및 후치 연산자를 활용하여 코드 길이 단축

     

    3. 과제에 대해

    • 전투 중 UI 디자인 수정
    • README.md 작성하기

     

    4. 참고자료

    없음

    반응형
    COMMENT
     
    01
    11

    과정명 : 내일배움캠프 Unity 게임개발 3기

    전체진행도 : 14일차

    부분진행도 : Chapter2.2 - 3일차

    작성일자 : 2024.01.11(목) 

    개발일지 목록 : 클릭


    1. 진행중인 과정에 대해

    캠프에서 그제와 어제 알고리즘 특강을 진행하고, 오늘부터 하루 한개 이상 알고리즘 문제 풀기를 아침 09시부터 10시까지 자율적으로 진행한다. 새벽에 잠이 안와서 먼저 시작했었는데, 계속 풀다보니 40문항정도 제출 완료. 한 두 줄로 해결 할 수 있는 기초 알고리즘 문제부터 시작하기 때문에, 사실 지금 시점에 제출한 문항 수는 크게 의미는 없어 보인다.

    팀 과제는 3일차인데 진행이 무척 빠른 것 같다. 하루 10시간씩 잡고있으니 당연하다면 당연한가 싶기도 하다. 스킬과 마나 시스템을 더 손보고, 나머지는 기존 기능에 대한 개선을 진행할 것으로 보인다.

     

    2. 오늘 학습에 대해

    오늘은 특별히 새로운 것을 배우거나 한 것은 없다. 개발파트가 순조로워 오늘 진행한 내용이 무척 많다. 오후 팀 스크럼에서는 내가 오늘 개발했던 내용도 많이 잊어버려 Github Desktop에 적혀있는 커밋 로그를 보며 프레젠테이션을 진행했다.

    오늘 내가 진행한 커밋 내용은 아래와 같다.

    Feat: Potion클래스 및 '회복 아이템' 장면 구현
    - Item 을 상속받는 Potion 클래스 구현
    - new Potion 객체로 [물] 추가(일단은 포션 역할로 사용, 포션 종류가 확장될 가능성이 있기 때문에 이름을 포션으로 두진 않음)
    - 메인메뉴 - 회복아이템 장면 구현

    Feat: 던전 승리 시 포션 및 골드를 획득

    Refactor: Potion 클래스 내 메서드 개편 및 해당 메서드를 사용했던 장면에서 코드 단축

    Design: 회복아이템 아트 수정

    Refactor: 경고문 시스템 도입

    Feat: 던전 전투 중 회복약 사용 기능 구현
    - 던전 전투 중 회복약 사용 기능 구현, isUseItem를 true상태로 주어 활성화.
    - 아이템 이름 [물]을 [회복약]으로 수정

    Feat: 전투 중 치명타, 몬스터회피 실적용

     

    그리고 오늘부터 알고리즘 문제를 풀어 하루 하나 이상씩 제출을 하는 시간을 갖게 되었는데, C#문법에서 헷갈리는 게 많아 많이 찾아보았는데, 이에 관해서도 이후에 문제를 다시 훑어보며 헷갈리는 부분을 정리 해 두면 좋을 것 같다.

     

    3. 과제에 대해

    - 디자인에 신경쓰며 팀 과제 개선해나가기

     

    4. 참고자료

    - 없음

     

    반응형
    COMMENT
     
    01
    10

    과정명 : 내일배움캠프 Unity 게임개발 3기

    전체진행도 : 13일차

    부분진행도 : Chapter2.2 - 2일차

    작성일자 : 2024.01.10(수)

    개발일지 목록 : 클릭


    1. 진행중인 과정에 대해

    콘솔 텍스트 던전 RPG 게임의 팀 과제 진행중.

    구현할 기능별로 역할을 분담하여 진행하는 방식을 택하였다. 수정할 파일이 서로 겹치는 경우가 빈번할 것 같아 Merge도중 충돌이 많이 일어날 것 같았는데, 걱정과는 달리 엄청 순조로운 진행을 보였다.

    나는 던전 전투 중 데미지 계산(일정 확률의 치명타, 일정 확률의 적 회피) 메서드의 작성을 먼저 맡아 완료한 후, Potion 클래스에 관해서도 새로 작성 한 후 포션과 관련된 게임 내 기능들을 작성중에 있다.

    필수적인 요소는 모두 마무리가 되어 각자 선택요소 중 원하는 기능들을 구현하고 있다.

    본인은 포션 관련한 기능을 모두 작성하면, 게임을 계속 돌려보며 디자인적으로 계속 개선해보는 역할도 맡았다.

     

    2. 오늘 학습에 대해

    오전에는 Stack, Queue, 정렬알고리즘(Quick 등)에 대한 내용의 특강이 있었다. 해당 요소들을 코드적으로 어떻게 구현하는지가 주된 내용이었는데, --index 처럼 전위연산을 수행한다던가 하는 자잘한 스킬로 구현을 더 쉽게 하는 부분이 인상적이었다. 함께 정리를 하고싶지만, 밤이 늦어 일단 팀 과제에 대한 내용을 먼저 주로 작성. 특강에 관해서는 복습한다던가 하면 이 포스트에 추가 작성을 할 것 같다.

    데미지 계산(일정 확률의 치명타, 일정 확률의 적 회피) 메서드 구현

    데미지 계산 메서드를 어떻게 구현할지 고민을 많이 하였고, 계속 정제를 한 끝에 편의성 측면에서 나름 정답을 찾은 것 같아 구현을 깔끔하게 할 수 있었다.

    public (bool, bool, int) CalculateExDamage(int originDamage, bool isSkill)
    {
        // 입력: 원래의 데미지, 스킬사용여부
        // 반환: 치명타 성공 여부, 회피 여부, 실 데미지
        
        // 반환값 목록 선언 및 초기화
        bool isCritical = false;
        bool isDodged = false;
        int calculatedDamage = originDamage;
    
        // 치명타 계산
        Random random = new Random();
        double chance = random.NextDouble();
        if (chance <= 0.15) // 15% 의 확률로 치명타 발생
        {
            isCritical = true;
            calculatedDamage = (int)(originDamage * 1.6f); // 160% 데미지
        }
        // 회피 계산
        chance = random.NextDouble();
        if (chance <= 0.1 && !isSkill) // 스킬공격이 아닐 시, 10% 의 확률로 회피
        {
            isDodged = true;
            calculatedDamage = 0; // 0의 데미지
        }
        
        return (isCritical, isDodged, calculatedDamage);
        // (bool val1, bool val2, int val3) = player.CalculateExDamage(originDamage, isSkill); 와 같이 사용
    }

    계산된 데미지 수치 이외에도, 치명타가 터지거나 적 몬스터가 회피할 경우 전투 장면 내에 이와 관련한 텍스트를 보여주어야 했기 때문에, isCritical 과 isDodged 의 반환도 필요했다. 즉 여러 개의 변수를 한번에 리턴하고 싶은상황.

    이러한 경우, 소괄호를 사용하여 return (값1, 값2, 값3); 과 같이 튜플값의 반환을 해 주는 걸로 해결을 할 수 있었다.

    매개변수로는 int의 기본공격력, bool의 스킬사용여부를 받아와, 두 변수로부터 치명타와 회피를 계산하도록 하였다.

    전투 장면에서는 아래 코드처럼 메서드를 호출, 세 개의 변수에 대입하여 사용 할 수 있다.

    (bool isCrit, bool isDodge, int calculatedDamage) = player.CalculateExDamage(originDamage, isSkill);

     

    포션 클래스 및 관련 기능 작성

    Item을 상속받는 Potion 클래스를 아래와 같이 작성했다.

        class Potion : Item
        {
            public int heal { get; private set; }
            public int count { get; private set; }
            public Potion(int id, string name, string desc, int price, int heal) : base(id, name, "체력회복", desc, price)
            {
                this.heal = heal;
                this.count = 3;
            }
            public void UsePotionOnDungeon()
            {
                // 포션 사용 구현.
                // 선택1. 플레이어 객체를 함께 받아, player.Hp += heal;
                // 선택2. heal값을 반환하여 장면 내에서 구현하기
    
                // 2번 선택. 이유가 있었는데, 밥먹다가 망각함... 곧 작성.
            }
            public void UsePotionOnInventory() => count--;
            public void GetPotion(int n) => count += n;
        }

    메인 메뉴에서 포션을 사용할 시에는 UsePotionOnInventory(), 던전 내에서 포션을 사용할 시에는 UsePotionOnDungeon() 의 메서드를 사용하도록 설계하였다. 현재 계속 구현 중인 부분이라 미완성이다.

    메인메뉴의 '회복 아이템' 메뉴로 진입하면 아래와 같은 사양의 장면을 구성한다.

    즉 당장 요구되는 사양으로는 한 종류의 포션만 구현하는 것.

    그렇기 때문에, items 배열에는 Potion클래스를 갖는 객체는 현재로써는 유일하기 때문에, 아래와 같이 FirstOrDefault() 메서드를 활용하여 포션 객체참조를 하였다.

    // 정말 만약에 Potion 객체가 shop.items에 없을 경우를 위해 대비한 로직
    // 참조 타입 변수에 null이 할당될 가능성이 있을 때 나오는 경고문 때문에 아래와 같이 라인 수 많아짐.
    Potion? potion = shop.items.OfType<Potion>().FirstOrDefault();
    if (potion != null)
    {
        Console.WriteLine($"포션을 사용하면 체력을 30 회복 할 수 있습니다. (남은 포션 : {potion.count}개)");
    }
    else
    {
        Console.WriteLine("게임에 포션이 구현되지 않아 갯수를 표시 할 수 없습니다.");
    }

    또한 아래와 같이 두줄 작성도 가능하긴 하였지만, 위 코드의 주석에도 적혀있듯 참조 타입 변수에 null이 할당 될 가능성이 있으면 보기 좋지 않은 경고문이 나오기 때문에 코드가 길어지더라도 위와 같이 작성하였다.

    또한 아래처럼 작성하더라도 items에 Potion객체가 하나도 없다면 "0"을 출력할 것이기 떄문에 큰 문제는 없을 것으로 보인다.

    Potion potion = items.OfType<Potion>().FirstOrDefault();
    Console.WriteLine($"{(potion != null ? potion.count.ToString() : "0")}")

    이외로는 사양에 맞추어 여러 조건식에 따라 기능을 구현한 내용이 대부분이기 때문에 이 정도로 마무리.

     

    3. 과제에 대해

    필수 구현 사양은 모두 만족한 것으로 보이고, 선택 구현사항을 각자 또 정하여 구현중에 있다. 큰 문제는 없을것으로 보여 계속 기능을 넣고 점검을 하며 완성도를 높이는 것이 좋겠다.

     

    4. 참고자료

    없음

    반응형
    COMMENT
     
    01
    10

    과정명 : 내일배움캠프 Unity 게임개발 3기

    전체진행도 : 12일차

    부분진행도 : Chapter2.2 - 1일차

    작성일자 : 2024.01.09(화)

    개발일지 목록 : 클릭


    1. 진행중인 과정에 대해

    어제까지 개인 프로젝트로 진행했던 콘솔 텍스트 던전 RPG 게임에 대해, 앞으로 일주일 간 팀 프로젝트로 기능구현을 추가로 진행한다. 필수 기능으로는 던전 내 배틀 시스템 구현, 선택적으로 이외 여러 기능을 구현하도록 가이드라인이 제시되었다.

    결과물 제출 기한은 오는 주 같은 요일인 1.16(화)의 21시이다.

    팀 회의에서 앞으로의 방향성을 위주로 회의를 하였다. 개인 프로젝트때와는 달리, 이번에는 커스텀은 최후순위에 두고, 필수 개발 구현에 집중하기로 하였다. 이전 팀 내에서 진행했던 코드 리뷰를 했던 느낌으로 결과물 발표를 하기로 일단 계획하였다. 또한 안내받은대로 팀 내 컨벤션을 착실히 두어, 코드 내 규칙과 Commit 메시지의 규칙, README의 작성도 스텐다드를 준수하도록 해 보기로 했다.

     

    2. 오늘 학습에 대해

    당연하다면 당연한 내용이지만 Git History 상 포함관계가 성립할 시, Merge 하였을 경우 Conflict는 발생하지 않는다는 것을 확인했다. 예를 들어, 아래와 같은 경우 BranchB와 Main에 다른 내용이 같은 라인에 적혀있지만, 컨플릭트가 발생하지 않는 예시.

    Main	: ""
    
    // Branch 2개 생성
    Main	: ""
    BranchA	: ""
    BranchB : ""
    
    // A와 B 각자 편집, A가 Commit, Push 후 Main에 Merge.
    Main	: "Im A"
    BranchA : "Im A"
    BranchB : "B is me"
    
    // B가 Update From Main을 하였고 Conflict 발생, B는 "Im A" 대신 "B is me"를 선택
    Main	: "Im A"
    BranchA : "Im A"
    BranchB : "B is me"
    
    // B가 Commit, Push 후 Main에 Merge. -> 같은 라인에 다른 내용이지만 Conflict 없음.
    Main	: "B is me"
    BranchA : "Im A"
    BranchB : "B is me"

    Main에 Merge된 BranchA의 로그 이후, BranchB의 히스토리에 "Im A"를 Deny하고 "B is me"를 선택했다는 기록 등이 남아 Conflict가 없었다고 생각해본다.

     

    3. 과제에 대해

    분담된 역할에 대해 팀 과제 수행, merge 후 정상적인 작동까지 확인하기

     

    4. 참고자료

    없음

    반응형
    COMMENT
     
    01
    08

    과정명 : 내일배움캠프 Unity 게임개발 3기

    전체진행도 : 11일차

    부분진행도 : Chapter2.1 - 6일차

    작성일자 : 2024.01.08(월)

    개발일지 목록 : 클릭


    1. 진행중인 과정에 대해

    개인과제 제출을 마치고, 부족한 부분은 재제출을 하여 마무리를 하는 날이다. 본인은 C# 기초강좌 중 부족한 부분을 복습하며 하루를 보냈다. 하루종일 피곤하여 엎어져서 자거나 졸기도 했다.

     

    2. 오늘 학습에 대해

    제너릭, ref 및 out 키워드 그리고 인터페이스에 대해 조금 정리

    제너릭

    • 클래스 또는 메서드를 일반화시켜 여러 자료형에 대응할 수 있게 한다.
    • C#에서는 <T> 키워드를 이용. (T 대신 다른 문자나 문자열도 맞춰줘도 상관 없어 보인다.)
    • 아래의 예시에서는 제너릭 클래스 Stack을 선언하여, int형을 관리하는 인스턴스를 만든다.
    // 제너릭 클래스 선언 예시
    class Stack<T, Ty> // 제너릭으로 사용할 문자열들을 명시
    {
        private T[] elements; // 자료형이 쓰일 자리에 사용
        private int top;
    
        public Stack()              // Stack 생성자. Push와 Pop도 구현하여 스택기능을 구현
        {
            elements = new T[100];  // 역시 자료형 대신 제너릭 문자열을 대신 사용 가능. 100 크기의 배열을 준비
            top = 0;                // 현재 index
        }
    
        public void Push(T item)    // Push 기능 구현. T형의 item을 매개변수로 전달받음
        {
            elements[top++] = item; // 현재 index에 입력받은 item을 저장하고, top을 1 증가(후위연산)
        }
    
        public T Pop()              // Pop 기능 구현. T형의 리턴값
        {
            return elements[--top]; // top을 1 감소시킨 후 해당 위치의 원소를 반환한다.(원소 삭제의 구현은 없음)
        }
    
        public Ty TyTest(Ty tyInput) // 스택 기능은 아니지만 제너릭으로 여러개를 쓸 수 있는지 테스트
        {
            Ty tyElement = tyInput; // 입력받은 Ty형을 그대로 반환하도록 했다. 아래 예시에서는 string으로 객체를 생성
            return tyElement;
        }
    }
    static void Main(string[] args)
    {
        Stack<int, string> intStack = new Stack<int, string>();     // 객체 생성 시, 제너릭 클래스에서 요구하는 수의 자료형을 입력
        intStack.Push(1);
        intStack.Push(2);
        intStack.Push(3);
        Console.WriteLine(intStack.Pop()); // 출력 결과: 3
    
        Console.WriteLine(intStack.TyTest("나는 intStack이오."));   // 출력 결과: "나는 intStack이오."
    
    }

     

    ref와 out 키워드

    • 매개변수 전달 시, 변수를 복사하는 것이 아닌 참조의 형태로 사용할 수 있게 한다
    // ref 키워드 사용 예시
    void Swap(ref int a, ref int b) //  매개변수로 두 변수를 전달받아 교환하는 메서드
    {
        int temp = a;
        a = b;
        b = temp;
    }
    
    int x = 1, y = 2;
    Swap(ref x, ref y); // x와 y를 참조형태로 전달한다
    Console.WriteLine($"{x}, {y}"); // 출력 결과: 2, 1
    • out은 ref와 비슷하지만, out 키워드를 사용한 변수를 메서드 내에서 값을 할당해주지 않으면 오류가 발생한다.
    • ref와는 달리 위와 같은 제약사항을 필요로 하는 경우에 사용.
    // out 키워드 사용 예시
    void Divide(int a, int b, out int quotient, out int remainder)
    {
        quotient = a / b; // quotient와 remainder에 값을 지정해주지 않으면 오류가 발생
        remainder = a % b;
    }
    
    int quotient, remainder;
    Divide(7, 3, out quotient, out remainder);
    Console.WriteLine($"{quotient}, {remainder}"); // 출력 결과: 2, 1
    • ref 와 out 매개변수는 값을 복사 없이 메서드 내에서 직접 접근을 할 수 있게 해주기 때문에 성능상의 이점이 있음.
    • out 매개변수는 메서드 내에서 반드시 값을 할당하기 때문에, 변수의 이전값이 유지되지 않음에 주의.

     

    인터페이스

    • 코드의 재사용성, 다중 상속 제공, 유연한 설계 등을 위해 인터페이스를 사용
    • 인터페이스는 클래스가 구현해야 하는 멤버들을 정의함. 클래스의 일종이 아니며, 클래스에 대한 제약조건을 명시해주는 역할.
    • 즉, 클래스가 인터페이스를 구현할(상속받을) 경우, 모든 인터페이스 멤버를 구현해야 한다.
    • 구현 시, [interface 인터페이스명{구현할 내용}] 의 형태를 가지며, 인터페이스명의 이름은 보통 대문자 I로 시작하는 것을 추천함.
     // 인터페이스 및 멤버를 정의하는 예시
    interface IMyInterface
    {
        void Method1();
        int Method2(string str);
    }
    
    // 인터페이스 구현
    class MyClass : IMyInterface
    {
        public void Method1()
        {
            // 구현
        }
    
        public int Method2(string str)
        {
            // 구현
            return 0;
        }
    }



    3. 참고자료

    없음

    반응형
    COMMENT