전체 글 (195)

  • 2024.02.02
  • 2024.02.02
  • 2024.02.02
  • 2024.02.01
  • 2024.02.01
  • 2024.01.31
  • 2024.01.30
  • 2024.01.30
  • 2024.01.27
  • 2024.01.25
  • 2024.01.24
  • 2024.01.23
  • 02
    02

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

    전체진행도 : 29일차

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

    작성일자 : 2024.02.02(금)

    개발일지 목록 : 클릭


    1. 진행중인 과정에 대해

    오는 주 수요일까지, 개인과제 및 지급된 강의를 흡수하고 있다.

    우선 지급된 유티니 3D 프로젝트 강의를 보고 있는데, 보면서 조금이라도 의문이 드는 점은 매번 찾아 주석으로 남기고 있기 때문에, 무척 시간이 지체되고 있다.

    대충 이런 느낌으로 주석을 작성해가며 진행중

    주말도 열심히 활용해서 진도를 빼야 될 것 같다.

    2일차인데 과제까지 다 끝냈다는 수강생들도 많다는 게 괴담이다.

    2. 오늘 학습에 대해

    인코딩 문제 해결

    유니티 강의를 들으며 기능 구현을 할 때마다 커밋을 하며 진행중인데, 잠깐 지나치며 File Changes 내용을 보니 아래와 같이 인코딩 문제가 있었다.

    한글로 작성한 부분이 Github Desktop엥서 깨져보인다.

    유니티 프로젝트를 시작할 때 아래와 같은 에디터 설정을 해 주었어야 했는데 잊고있었다.

    프로젝트 루트 폴더에 .editorconfig 파일 마련

    .sin 폴더가 있는 루트 폴더에 위와 같은 내용을 가진 .editorconfig 파일을 마련해주고, 스크립트 파일을 다시 저장(Ctrl+S) 해 주면 해결이지만

    Github Desktop에서 파일 Changes를 확인 할 수 있다

    기능적으로 바뀐 부분 이외에 한글 주석을 달았던 부분이 모두 바뀌기 때문에 커밋 내용이 지저분해진다.

    가능한 빠른 시점에 .editorconfig 파일을 마련하도록 하는 것이 좋다.

    Code Solve

    매 문제를 풀 때마다 검색을 다섯 번 이상 한다. 마찬가지로 주석부분에 내용을 정리하였다.

    코딩테스트 연습 > 연습문제 > 공원 산책

    using System;
    using System.Collections.Generic;
    
    public class Solution {
        public int[] solution(string[] park, string[] routes) {
            int[] answer = new int[2];
    
            // 솔루션 : 주어진 내용을 정말 그대로 수행만 하면 될 것으로 보임
            // 0-1. NSWE의 튜플값 마련
            // 1. park를 한차례 순회하며(최대 2500회) S의 위치 찾기
            // 2. routes의 원소를 foreach순회
            // 2-1. 현재자리 기억
            // 2-2. for 반복문으로 0-1에서 마련한 튜플값만큼 이동 
            // 2-2-1. 이동 불가조건 만족 시 원래자리로 복귀
            // 3. 현재 자리(index) 반환
    
            // 0-1. NSWE의 튜플값 마련
            // 찾아보기. 제네릭으로 튜플값 사용하는 방법
            Dictionary<char,(int X,int Y)> going = new Dictionary<char, (int, int)>(){
                {'N',(0,-1)}, // (x, y) 순
                {'S',(0,1)},
                {'W',(-1,0)},
                {'E',(1,0)}
            };
            // 1. park를 한차례 순회하며(최대 2500회) S의 위치 찾기
            // 찾아보기. 튜플 변수 선언과 초기화
            // 찾아보기. (int, int) index와 같이 사용하는것은 불가능
            //          (int X, int Y) 로 수정
            //          이후 코드에서도 Item1 -> X, Item2 -> Y로 수정
            (int X, int Y) index = (-1,-1);
            for(int y=0;y<park.Length;y++)
                for(int x=0;x<park[0].Length;x++)
                    if(park[y][x]=='S'){
                        index = (x,y);
                        x = park[0].Length;
                        y = park.Length;
                    }
            // 2. routes의 원소를 foreach순회
            foreach(string directionInfo in routes){
                // 찾아보기: char로의 형변환
                char direction = char.Parse(directionInfo.Split(' ')[0]);
                int moveNum = int.Parse(directionInfo.Split(' ')[1]);
                // 2-1. 현재자리 기억
                // 찾아보기: 튜플값은 값 형식으로 다른 튜플에 할당하면 내용복사
                (int X, int Y) preIndex = index;
                // 2-2. for 반복문으로 0-1에서 마련한 튜플값만큼 이동 
                for(int i=0;i<moveNum;i++){
                    // 찾아보기: 튜플은 아래와 같이 +=는 사용 할 수 없다.
                    //index += going[direction];
                    index = (index.X + going[direction].X, index.Y + going[direction].Y);
                    // 2-2-1. 이동 불가조건 만족 시 원래자리로 복귀
                    // 찾아보기: 튜플 요소에 접근
                    if(
                        index.X<0 ||
                        index.Y<0 ||
                        index.X>= park[0].Length ||
                        index.Y>= park.Length
                    ){
                        index = preIndex;
                        break;
                    }
                    else if(park[index.Item2][index.Item1] == 'X'){
                        index = preIndex;
                        break;
                    }
                    // 이동 확인 구문
                    // Console.WriteLine($"{i}:({index.X}, {index.Y})");
                }
            }
    
            // 3. 현재 자리(index) 반환
            // 아래 구문은 왠지 잘 되지 않음.
            // (answer[0], answer[1]) = index;
            // 배열의 인덱스 위치에 직접 값을 할당하는 형태는 
            // 지원이 되는경우도 있고 안되는 경우도 있다는 듯
            answer[0] = index.Y;
            answer[1] = index.X;
    
            return answer;
        }
    }
    • 제네릭 형태로 튜플 값을 사용
      • 처음 시도 해 본 내용인데, 이게 되네 싶어서 놀랐다.
      • Dictionary<char,(int X,int Y)> going = new Dictionary<char, (int, int)>();
    • 튜플 변수
      • 마찬가지로 튜플 변수라는 것도 처음 사용 해 보았다. 배우거나 찾아본 거 없이 적은 구문이 맞는 구문이라 놀랐다..
      • (int, int) index = ... 형태는 C# 7.0 버전 이상에서 사용할 수 있다고 하고, 프로그래머스의 컴파일러도 이를 만족하지만 어째서인지 되지 않았다. 대신 아래와 같은 형태는 사용 가능했다.
      • (int X, int Y) index = ... 는 사용 가능. index.X 와 같이 사용 가능
      • 위와 같은 경우 값 참조이기 때문에, (int X, int Y) preIndex = index; 의 경우 값이 복사되며 두 튜플은 별개로 작동한다.
      • 튜플끼리 덧셈 연산자는 사용할 수 없다. index += going[direction];
      • 튜플 내부 변수명으로도 튜플 내의 값을 사용할 수도 있지만 .Item1, .Item2... 로도 사용 가능하다.
    • 한 글자로 된 문자열의 char 로의 형 변환
      • 왠지 사용했던 기억이 없어 익숙하지 않아 찾아보았다.
      • char direction = char.Parse("C"); 와 같이 char.Parse()를 사용 가능하고, "C"[0]과 같이 사용도 가능하다.

    3. 과제에 대해

    • 강의 흡수 및 개인 과제 수행(남은시간 주말 포함 5일)
    반응형
    COMMENT
     
    02
    02

    복귀 한 건 아니지만 오랫만에 통계 사이트에서 내 캐릭터를 확인 해 보았다.

    1년 반 정도 전 기점으로 활동을 하지 않아 업적점수는 동결중

    당시 시점에는 월드 랭킹 2위를 한동안 유지했었다.

    현재 15위 Ecchi Clone. 2년 전 당시에도 자주 보였던 닉네임들이 보인다.

    파판을 접은 동안 랭킹이 많이 떨어졌을 줄 알았는데, 아직 서버 15위에 위치중이다.

    Aegis 월드 순위권 점수

    현재 1위의 점수는 2만 6천점 정도이다. 모르는 닉네임인 걸 보면 단시간에 빡세게 올린 것으로 예상된다.

    당시 컨텐츠도 같이 했던 모모무츠상(전 1위), 타마토라상(전 3위), Kp상은 꾸준히 게임 하는 듯.

     

    복귀 하면 그 동안 쌓인 컨텐츠에서 뽑아먹을 게 정말 많아서 점수를 쭉쭉 올릴 수 있지 않을까 싶다.

    분신

    미코테 귀엽다

    반응형

    'Game > FFXIV' 카테고리의 다른 글

    [FFXIV] 탐험수첩 18번  (0) 2018.07.27
    [FFXIV] 탐험수첩 3번  (0) 2018.07.27
    [FFXIV] 탐험수첩 9번  (0) 2018.07.27
    [FFXIV] 탐험수첩 19번  (0) 2018.07.26
    [FFXIV] 탐험수첩 1번  (0) 2018.07.26
    COMMENT
     
    02
    02

    개요

    AlphaSlayer1964/kemono-dl

    kemono.su / coomer.su 에서 쉽게 일괄 다운로드를 할 수 있게 해 주는 파이썬 프로젝트이다.

    예전에 로컬에 Clone해서 사용하고 있었던 이게 오랫만에 쓰려고 보니 작동하지 않아, 여러번의 시행착오를 거쳐 잘 작동하도록 수정하였다.

    해당 깃허브 리파지토리의 Issues 목록을 확인 해 보니 역시 관련된 문제를 제기하는 유저들이 많았다.

    해결한 내용을 혼자만 알고 있기 아까워 처음으로 다른 개발자의 프로젝트를 손대보았다.

    사실 '오픈소스 프로젝트에 참여한다'는 게 어떤 행동까지 포함되는지 잘 알지 못하는데, 해당 프로젝트의 원래 개발자가 이 프로젝트를 public으로 던져두고 2년간 잠적한 상태라서 외부 개발자의 PR이 10개 가까이 쌓여있고, 마찬가지로 내가 수정한 코드가 반영 될 일은 없을 것 같다. 이걸 오픈소스 프로젝트라고 할 수 있을지는 잘 모르겠다.

    그래도 PR을 올려두면 다른 유저들도 수정사항을 조회 할 수 있기 때문에 의미없는 행동은 아닐 것이라고 생각한다.

    프로젝트 참여 과정

    해당 리포지토리의 우측 상단에서 Fork 기능을 사용하여 나의 리포지토리 목록에 해당 프로젝트를 복사 해 온다.

    정상적으로 Fork하였다면, 아래와 같이 새로운 리포지토리가 생성되어있고, forked from ... 의 메시지도 확인 할 수 있다.

    새로 생긴 리포지토리를 로컬에 Clone 하여 샥샥 수정 후 Commit과 Push를 하면 내 깃허브 리포지토리에 반영이 되는데

    내 리포지토리에서 Pull requests를 누르면 위와 같이 Fork했던 기존 프로젝트에 Pull Request를 할 수 있다.

    PR 메시지를 작성 후 올려두면, 아래와 같이 기존 프로젝트의 Pull Requests 목록에 올려진 것을 확인 할 수 있다.

    문제가 되었던 내용

    1. Kemono와 Coomer의 도메인 변경
    2. Kemono와 Coomer의 API 주소 변경
    3. Datetime - API 서버에서 반환되는 날짜 및 시간 문자열의 형식이 ISO 8601로 조정됨
    4. Datetime - 정수 및 문자열 데이터 타입의 혼동. 일부 데이터의 전달이 문자열(str) 타입에서 정수(int) 타입으로 바뀜

    Kemono와 Coomer의 도메인 변경

    두 서비스의 도메인이 kemono.party 와 coomer.party 에서 kemono.su 와 coomer.su 로 바뀌었다.

    세개 정도 소스코드에서 '.party'를 '.su' 문자열로 모두 찾아바꾸는 것으로 해결하였는데, 이 부분은 운좋게 해결 된 감이 없지않다.

    Kemono와 Coomer의 API 주소 변경

    기존에 요청을 보내던 API 주소가 제대로 응답을 주지 않았다. 설마 요청할 API의 주소가 바뀌었을 것이라고는 예상하기 어려울 것 같다.

    브라우저 창에서 기존에 요청하던 주소인 https://coomer.su/api/creators/ 에 접속 해 보아도 Not Found 를 받아오는 것을 보고 해당 주소의 파일구조를 확인 해 봐야겠다고 생각했다.

    루트 경로인 coomer.su 에서 개발자 도구를 통해 api로 향하는 파일구조를 확인할 수 있을 줄 알았지만, 찾아보니 이러한 방법으로는 구조를 확인 할 수 없다고 한다. coomer에서 api의 가이드를 해 주지 않는다면 알아내는 것은 어렵다고 한다.

    구글 검색을 하던 중, coomer가 제공하는 API 가이드를 발견하였다. 정말 기대조차 못했던 수확이다.

    확인 해 보니 아무래도 coomer는 API의 개선을 진행하고 있는 듯 하다. 기존 사용하던 API는 주소 뒤에 /v1라는 경로가 추가되어 있는 것을 확인했다.

    즉 소스코드에 쓰이고 있던 경로 문자열에서 .../api/....../api/v1/...로 바꿔주면 되었고, 모든 경로를 찾아 바꿔 해결 할 수 있었다.

    Datetime 관련 문제

    날짜에 관련된 문자열 처리 과정에서 문제가 있었다. 갈피를 잡지 못해 ChatGPT에게 소스코드를 보여주며 원인을 찾아보도록 하였는데, GPT는 API에 직접 접근하지 못하기 때문에 방향을 제시 해 주는 정도였다.

    입력으로 주는 날짜 정보 등을 출력해가며 이를 함께 제시 해 주니, 원인을 찾을 수 있었다.

    날짜 및 시간 형식의 불일치: API 서버의 주소 변경으로 인해 반환되는 날짜 및 시간 문자열의 형식이 ISO 8601로 조정되었습니다. 기존 코드는 특정 형식(예: %a, %d %b %Y %H:%M:%S %Z)을 기대했으나, API의 조정으로 인해 이 형식이 변경되었고, 이로 인해 기존의 datetime.strptime 함수로는 새로운 형식의 날짜 및 시간 문자열을 올바르게 파싱할 수 없게 되었습니다.

    즉 예전과는 다른 날짜형식을 API가 반환하고 있기 때문에 파싱 과정에서 문제가 되었다는 내용.

    coomer의 입장에서 보면 standard에 가까운 형식으로 반환하도록 수정한 것이기 때문에 방향성으로는 올바른 것 같다.

    그래서 문제가 된 것은, datetime.strptime함수로 새로운 형식의 날짜 및 시간 문자열을 올바르게 파싱할 수 없게 된 것이다.

    ISO 8601 형식에 대응 할 수 있도록 새로운 함수 parse_date_string를 마련하였다. 해당 함수는 여러 날짜 형식을 시도하고, 올바르게 파싱할 수 있는 형식을 찾으면 해당 형식으로 날짜 및 시간 문자열을 처리한다.

    또한, 문자열 타입(str)의 인자를 전달해야 하는 곳에 정수형 변수(int)가 전달되는 문제도 생겨, 필요한 경우 문자열로 변환하는 로직을 추가하였다.

    (위 작성한 내용의 절반 이상이 날아가 다시 작성함.. ㅠㅠ)

    Pull Request

    모든 것을 마치고 아래와 같이 PR을 작성하였다.

    다른 개발자의 프로젝트에 리퀘스트를 넣어보는 것은 처음이기도 하고 열심히 작성 해 보았다.

    반응형
    COMMENT
     
    02
    01

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

    전체진행도 : 28일차

    부분진행도 : Chapter4.1 - 1일차

    작성일자 : 2024.02.01(목)

    개발일지 목록 : 클릭


    1. 진행중인 과정에 대해

    다시 개인과제 주차가 시작되었다.

    새로운 강의의 지급이 되어 해당 강의를 흡수하고, 개인 과제를 해결하여 제출하는 데에 주말 포함 7일이 주어졌다.

    또한 새로운 팀 편성을 하며 팀 스페이스를 새로 작성하였다.

    스크럼 로그
    트러블 슈팅 탬플릿
    오늘 먹은 거 자랑

    • 매일 회의를 간단하게 기록하거나, 세션에 대한 메모를 하는 스크럼 로그
    • 프로젝트 중 버그나 개선할 점 또는 추가하고 싶은 아이디어를 작성하는 데이터베이스
    • 쓸 사람만 쓰는 먹은 것 자랑 TIA

    팀에 활기를 불어넣어줄 것을 기대하며 위와 같은 내용들을 작성하였다.

    새롭게 주어진 강의는 3D 프로젝트에 관한 내용이었다. 오늘은 개발 환경을 구성하기 시작하는 단계까지만 진행하였다.

    저녁에는 메모리 관리에 관한 특강 세션이 있었고, 유익하게 배운 내용이 많았다.

    2. 오늘 학습에 대해

    Git Commit Message에 대해

    실제 여러 개발자들이 직접 개발에 참여하고 있는 프로젝트들을 살펴보았다.

    그 안에서도 많은 개발자들이 동시에 진행중인 PR(Pull Request) 목록들을 살펴보았는데, 기능 추가에 대한 내용을 예로 들면 소문자와 축약형의 단어인 feat보다 대문자로 시작하고 단어 전체의 Feature로 작성하는 개발자가 훨씬 많았다. 비율로 보면 1:5정도거나 그 이상 차이가 나는 정도.

    더 Standard한 메시지 형식을 사용하고 싶기 때문에, 이를 반영하여 앞으로 위와 같은 형식을 사용하도록 할 것이다.

    Unity3D - Skybox 만들기

    Material 생성

    • Project에서 Material로 생성

    Material의 Inspector에서 Shader 설정 및 파라미터 세팅

    • Inspector에서 Shader를 Skybox - Procedural(...etc)로 변경

    Lightning 윈도우. 에디터 우측 아래 아이콘 또는 Ctrl+9로도 열 수 있다.

    • Window - Rendering - lighting 의 Environment 탭에서 Skybox Material에 위에서 만든 Skybox를 사용

    특강: C# 메모리 관리

    메모리 : 프로그램/어플리케이션이 실행하는 데 필요한 데이터를 저장

    메모리는 아래와 같은 영역을 가진다.

    • Code : 프로그래머가 작성한 코드를 보관
    • Data : 어플리케이션 전반에 필요한 데이터 저장(static, const, 전역변수)
    • Heap : 참조 데이터(객체) 저장
    • Stack : 어플리케이션 실행 순서에 필요한 데이터 보관(로컬 변수, 매개 변수)

    일반적인 변수는 Stack에 저장된다.(int, bool, struct, enum ...)
    클래스 변수도 Stack에 저장된다. 다만 클래스 변수의 값은 '주소값'을 가진다.
    클래스 내부에 초기화된 데이터들은 Heap에 저장된다. 클래스 변수 내부의 주소값은, 이 데이터들이 저장된 Heap의 위치를 가리킨다.

    C#은 GC(Garbage Collection)라는 메모리 관리 기법으로 Heap에서 사용되지 않는 데이터들을 할당 해제하여 메모리의 공간을 확보한다.

    가비지를 가능한 적게 하는 프로그래밍이 좋은 개발이다.

    문자열을 계속 +=로 잇는 것은 해당 연산을 할 때마다 매번 새로운 공간을 할당받아 효율적이지 못하며 '문자열 파편화'라고 한다.
    문자열 파편화를 방지하는 방법은 아래와 같다.

    • string을 한번에 제작(보간문자열, 스트링 포맷, +연산자)
    • StringBuilder 사용
      • 처음부터 큰 메모리 공간을 할당받아 문자열을 채워나가는 방식
      • 물론 이 공간을 넘어가는 경우에는 더 큰 공간을 다시 할당받는다.

    만약 면접에서 구조체와 클래스의 차이를 물어보는 문제가 나온다면

    • 메모리 동작에 대한 잉해를 갖고 있는지를 물어보는 의도이다.
    • 구조체는 Stack에 데이터를 저장하는 '값형', 클래스는 Heap에 데이터를 저장하는 '레퍼런스형'이다.
    • 코드의 구조에 따라 어느 형태를 취하느냐에 따라 데이터 관리를 더 효율적으로 할 수 있다.

    static 데이터는 Data 영역에 저장

    • 프로그램 시작 시 생성
    • 프로그램 종료 시 삭제

    3. 과제에 대해

    • 이전 입문강의 및 이번 숙련강의 흡수
    • 개인과제 작성 및 제출
      • 내용 자체는 어렵지 않지만 디자인패턴을 활용하고 싶기 때문에 시간이 꽤 걸릴 것 같다
    반응형
    COMMENT
     
    02
    01

    대학교 자유 연구 때 다루던 Spleeter에 관한 내용이 생각나서 작성

    기본적으로 음원을 분리해주는 툴이라고 생각하며 좋다.

    2stems로 분리한다는 설정을 주면, 음원을 vocals.wavaccompaniment.wav로 분리해준다.

    엄청 깔끔하게 분리되는 편은 아니지만 나름 들을만 한 정도이다. (포스트 하단에 결과 첨부)

    로컬에서 Spleeter로 음원 분리 환경 구현

    python 환경에서 작동하며, 조금 검색을 해 보면 Google Colab에서도 사용할 수 있도록 마련된 환경이 보이는데, 어째서인지 런타임이 강제로 종료되는 등 잘 작동하지 않아 로컬 환경에서 구현하였다.

    선행하여 해 주어야 할 것이 두 가지 있다.

    1. pip install spleeter를 통한 spleeter 패키지의 설치
    2. FFmpeg의 설치. 공식 FFmpeg 웹사이트에서 운영체제에 맞는 설치 지침을 확인 할 수 있다.
      • Windows의 경우, FFmpeg의 바이너리(zip) 파일을 다운로드하고, 압축을 해제한 후, ffmpeg.exe가 포함된 디렉토리를 시스템의 환경 변수 Path에 추가한다.
      • 설치가 완료된 후, 터미널이나 커맨드 프롬프트에서 ffmpeg -version 명령어를 실행하여 FFmpeg가 올바르게 설치되었는지 확인할 수 있다.

    위 내용이 선행되었다면, 아래와 같은 내용의 Python 파일을 작성하여 실행한다.

    여러가지 옵션을 줄 수 있지만, 일단은 아래와 같이 작성하여 잘 작동하는지 확인한다.

    from spleeter.separator import Separator
    
    def main():
        # 오디오 파일 분리 설정
        separator = Separator('spleeter:2stems')
    
        # 분리할 오디오 파일 경로
        audio_path = './song.mp3'
    
        # 결과물을 저장할 디렉토리
        output_path = 'output/'
    
        # 오디오 파일 분리 실행
        separator.separate_to_file(audio_path, output_path)
    
        # 결과 확인 등의 추가 코드
    
    if __name__ == '__main__':
        main()

    if __name__ == '__main__':의 형식을 사용하지 않는다면 Python의 multiprocessing 모듈을 사용할 때 발생하는 문제를 겪게 된다.

    Windows 환경에서 multiprocessing을 사용할 때, 스크립트의 메인 블록이 올바르게 보호되지 않기 때문이라고 한다.

    다른 변수가 없다면, main.pysong.mp3 이외에 아래와 같은 파일구조가 만들어졌을 것이다.

    output/음원이름/ 위치에 결과물이 위치한다

    음원소스는 다음의 유튜브 동영상을 사용하였다.

    グッバイ宣言 / 百鬼あやめ cover

    song.mp3와 이로부터 생성된 vocals.wav(보컬), accompaniment.wav(반주)는 아래와 같다.

    성능을 보여주기 위해 첨부하는데, 출력된 파일 크기가 각각 20메가를 넘어, 부득이하게 크기를 줄여 첨부하였다.

    song.mp3
    2.64MB
    vocals_out.mp3
    6.57MB
    accompaniment_out.mp3
    6.57MB

    사용한다면 vocal쪽보다 반주쪽이 더 사용하기에 자연스러울 것 같다.

     

    Colab 에서 Spleeter로 음원 분리 환경 시도

    더하여, Colab에서 시도했던 내용은 아래와 같다.(공유)

    더보기

    # 스플리터와 ffmpeg 설치

    # 필요한 패키지들 설치
    !apt install ffmpeg
    !pip install spleeter

    # Spleeter 사용하여 음원 분리

    # 파일 업로드
    from google.colab import files
    uploaded = files.upload()

    # 업로드한 파일 목록 중 첫 번째 파일의 이름을 가져옵니다.
    file_name = next(iter(uploaded))

    # spleeter를 사용하여 파일 분리
    !spleeter separate -o output/ "$file_name"

    from IPython.display import Audio, display

    # 보컬 파일 재생
    vocals_path = 'output/song/vocals.wav'  # spleeter가 저장한 보컬 파일 경로
    display(Audio(vocals_path))

    # # 반주 파일 재생
    # accompaniment_path = 'output/song/accompaniment.wav'  # spleeter가 저장한 반주 파일 경로
    # display(Audio(accompaniment_path))

    마지막 블럭을 실행하면, 런타임 연결이 끊겨 실행할 수가 없게 된다. 아무래도 Colab 유료 플랜을 사용하고 있지 않은것이 문제라고 예상된다.

    반응형
    COMMENT
     
    01
    31

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

    전체진행도 : 27일차

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

    작성일자 : 2024.01.31(수)

    개발일지 목록 : 클릭


    1. 진행중인 과정에 대해

    팀 과제 '탕후루를 부탁해' 의 과제 발표날이다. 13분의 발표를 마치면 튜터진들의 피드백을 받는다.

    2. 오늘 학습에 대해

    발표 시 피드백 내용들

    <A조-탕후루>

    • 일정 등의 프로젝트 매니징 역할
    • 결과에 대해 더 볼륨을 주거나, 스토리가 있으면 더 좋았을 것

    <B조-펜타곤>

    • FPS 일정하도록
    • 게임 이펙트(파티클) 등을 주어 더 알아보기 쉽도록

    <C조-블록>

    • 깃 README.md도 잘 적어두면 포트폴리오 활용에도 좋을 것
    • 씬 한개로 구성된 게 아쉬웠다. 오브젝트가 너무 많음. 스크립트로 생성 삭제를 관리하면 좋을 것

    <D조-슈퍼마리오>

    • 소리가 전체적으로 일정치 않음
    • 전 조와 마찬가지로 오브젝트가 많이 쌓임. 탄막이 사라지게 하거나 필요.
    • 저장기능 있으면 더 좋을듯
    • 낮/밤 시간표기가 있으면 좋겠음

    <E조-고양이>

    • 리소스 폴더에 불필요한 파일 줄이기

    <F조-디버거코난>

    • 유니티 AI 몹 : 애네레이터? 나중에 과정중에 배울지도
    • mac/windows의 혼용으로 주석이 깨짐. 프로젝트 시작 때 인코딩 설정 utf-8로 설정하기.

    <G조-뱀서 느낌의 닷지>

    • (긍정)이동과 사운드의 싱크가 잘 맞춰져있음

    <참고하면 좋을 내용들>

    • 몰랐던 유니티 기능 : 마리오 블럭 관리하던 무언가(타일 룰?)
    • 몰랐던 유니티 기능 : 오디오 믹서 기능
    • 몰랐던 유니티 기능 : 유니티의 카메라 클램프로 일정 구역 벗어나지 않도록 하기
    • 몰랐던 유니티 기능 : 스테이지 맵 랜덤 생성
    • 도트 패널(또는 버튼)모양의 스프라이트, 유용해보임
    • 발표 시 모티브 게임 소개
    • 코...루틴? > GPT에게 물어보기
    • 발표자라면, 발표 전에 꼭 사운드 체크 리허설 하기
    • 발표 내용에서 TMI 줄이기
    • 개발자 게임이면, 탄막을 'Console.WriteLine("Shoot");' 같은 걸 쏘면 좋을지도
    • 데이터만 관리할 목적이면 Monobehavior를 상속받지 않는 것이 좋다는 의견
    • 유니티 모노비헤비어 라이프사이클
    • 플레이어 발사체가 몬스터 발사체를 삭제
    • draw.io라는 좋은 툴을 소개받았다. 와이어 프레임이나 순서도를 그리기 좋아 보이는 곳

    프로젝트를 마치며

    • 옵저버 패턴, 구독 패턴을 포함한 여러가지 스킬들을 흡수할 시간이 부족해 팀 프로젝트에서 활용하지 못한 게 아쉬웠다.
    • GameManager.cs의 역할을 맡으면서, 다른 스크립트에서 잘 활용할 수 있도록 구성을 했어야 했는데 그러지 못했다.
    • 짬 내서 디자인 패턴 및 관련한 내용은 다음 팀 프로젝트 전에 어서 흡수해야겠다.
    • 트러블슈팅 테이블 등 이전 조에서 작성했던 시스템들을 이번 조에 잘 끌어오지 못했다. 정말 아쉬움.

    특강 세션 : 연봉 1억 대기업 개발자 되는 법 - 어떻게 학습하고, 적응하고, 일해야 할까

    들으면서 도움이 될 만한 내용들

    • 내가 아는 것과 모르는 것을 알자(메타인지)
    • 키워드라도 남기면서, 요구사항/문제사항을 메모(TIL/WIL)
    • 채용공고를 점검하며 내 무기와 스킬을 계속 확인하기
    • 알고리즘 공부 시, DP, DFS/BFS 등 각각의 A4지(시트)에 문제 풀기 전에 한번씩 볼 수 있을 정도로 정리. 문제를 못풀면 관련 내용을 Develope 해간다.
    • 항상 기능 관점이 아닌 서비스 관점에서도 생각하기.

    3. 과제에 대해

    • 다시 돌아오는 개인 프로젝트 주차 잘 소화하기.
    반응형
    COMMENT
     
    01
    30

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

    전체진행도 : 26일차

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

    작성일자 : 2024.01.30(화)

    개발일지 목록 : 클릭


    1. 진행중인 과정에 대해

    팀 프로젝트 '탕후루를 부탁해'를 마무리 지었지만, 해상도 관련 대응이 되지 않는다는 내용이 있어 관련한 버그를 고치고, 개인 학습 시간을 가졌다.

    저녁에는 event와 Action에 대한 특강이 있어 강의와 질의시간을 가졌다.

    2. 오늘 학습에 대해

    팀 프로젝트 중 이슈

    변동 해상도에 대응되지 않음

    • 고해상도나 저해상도의 기기에서 실행할 경우
    • 게임 중 해상도의 변경이 있을 경우

    현재 개발을 계속 760x1280 해상도에 맞춰 해왔기 때문에, 더 고해상도가 된다면 UI배치가 전체적으로 화면의 가운데에 몰리고, 저해상도의 경우는 UI가 화면 밖으로 나가버리는 이슈가 있었다.

    이러한 경우, 아래와 같이 Canvas 오브젝트에서 UI Scale Moce를 기본값인 Constant Pixel Size 에서 Scale With Screen Size 로 수정해주면 해결할 수 있었다.

    설정을 적용할 경우, 어느 해상도에서도 Canvas가 스케일링되어 나오게 된다.

    목표 탕후루

    그러나 현재 스크립팅 된 내용으로는, 목표 탕후루를 Canvas의 position 기준으로 생성 위치를 선정했기 때문에, 해상도가 달라지면 목표 탕후루의 이미지도 엉뚱한 곳에 위치하는 문제가 있었다.

    목표 탕후루의 생성 로직 변경

    현재 목표 탕후루의 오브젝트는 Canvas의 하위에 생성되며, position을 설정하기 때문에, 해상도의 변경이 있을 경우, 목표 탕후루의 생성 지점도 유동적으로 움직일 수 없다.

    그러한 문제를 해결하기 위해, 목표 탕후루 오브젝트의 생성을 아래의 패널의 하위에 생성되도록 하여 부모의 위치를 기준으로 y값만 조절하여 과일을 배치할 수 있도록 하였다.

    부모 패널

    // 'TargetTanghulu' GameObject 생성 및 'Image'의 자식으로 설정
    GameObject targetTanghuluObject = new GameObject("TargetTanghulu");
    Transform imageTransform = GameObject.Find("TanghuluUI").transform.Find("Image");
    targetTanghuluObject.transform.SetParent(imageTransform, false);

    결과적으로 목표 탕후루 이미지의 생성되는 위치를 정상화 할 수 있었고, 해상도의 문제도 해결되어 아래와 같이 극단적으로 정사각형의 해상도를 가질 경우에도 게임 플레이를 할 수 있게 되었다.

    모든 이슈를 해결한 뒤의 스크린샷

    Code Solve

    코딩테스트 연습 > 연습문제 > 달리기 경주

    using System;
    using System.Linq;
    using System.Collections.Generic;
    
    public class Solution {
        public string[] solution(string[] players, string[] callings) {
            string[] answer = new string[] {};
    
            // 솔루션 1
            // 1. 선수이름으로 등수를, 등수로부터 선수이름을 아는 사전형 두개를 준비
            // 2. 선수 이름이 불리면
            // 2-1. 해당 선수의 등수 확인 (ex. 5등)
            // 2-2. 5등과 4등의 value값을 서로 바꾸며 이름 확인
            // 2-3. 해당 이름을 가진 두 선수의 등수를 바꾼다
            // 3. 모든 호명이 끝나면, 1등부터 answer에 입력
    
            // 솔루션 2
            // 다른 솔루션으로 생각해 볼 수 있는 건, ('5등이름','4등이름') 과 같이
            // ('선수','앞선선수')의 key,value값을 가지는 사전형을 사용해 볼 수도 있을 듯.
            // 이렇게 하면, 공간복잡도를 줄일 수 있다.
            // => 파기. 실제 해보니 ('앞선수','선수','뒷선수')의 리스트형이 필요한데, 공간복잡도도 낮지 않고 복잡함.
    
            // 1. 선수이름으로 등수를, 등수로부터 선수이름을 아는 사전형 두개를 준비
            Dictionary<string,int> playerToNum = new Dictionary<string,int>();
            Dictionary<int,string> numToPlayer = new Dictionary<int,string>();
            for(int i=0;i<players.Length;i++){
                playerToNum.Add(players[i],i);
                numToPlayer.Add(i,players[i]);
            }
    
            // 2. 선수 이름 호명
            foreach(string call in callings){
    
                // 2-1. 해당 선수의 등수 확인
                int num = playerToNum[call];
    
                // 2-2. 해당 등수 선수와 앞등수 선수의 value값을 서로 바꾸며 이름 확인
                string prePlayer = numToPlayer[num-1];
                numToPlayer[num] = prePlayer;
                numToPlayer[num-1] = call;
    
                // 2-3. 해당 이름을 가진 두 선수의 등수를 바꾼다
                playerToNum[prePlayer] = num;
                playerToNum[call] = num-1;
            }
    
            // 3. 모든 호명이 끝나면, 1등부터 answer에 입력
            // 검색 : Linq를 이용한 한줄 표현
            answer = numToPlayer.OrderBy(kvp => kvp.Key).Select(kvp => kvp.Value).ToArray();
    
            return answer;
        }
    }
    • 솔루션 2번을 시도하려다가, '뒷 선수'의 정보도 리스트에 필요하다는 것을 알게 되어 포기
    • Linq를 이용하여 한줄 코드
      • answer = numToPlayer.OrderBy(kvp => kvp.Key).Select(kvp => kvp.Value).ToArray();
      • 람다 표현식(kvp => kvp.Key) 사용
      • kvp는 KeyValuePair<int, string> 타입의 각 요소를 나타내며, NumToPlayer 딕셔너리의 각 항목을 순회하며 사용
      • =>는 람다 연산자라고 하며, 이를 기준으로 왼쪽은 입력 파라미터, 오른쪽은 함수 몸체를 나타냄
      • OrderBy(kvp => kvp.Key)를 호출하면, NumToPlayer의 각 KeyValuePair<int, string> 요소(kvp)를 kvp.Key (등수)를 기준으로 오름차순 정렬

    event와 Action 특강

    delegate, event, Action에 대해

    delegate : 함수에 대한 참조 타입. 함수를 변수처럼 저장하거나 매개변수로 전달 할 수 있음.

    delegate 반환형 델리게이트이름(매개변수);

    event : delegate의 한 종류. 옵저버 패턴으로 활용 할 수 있다.

    event 델리게이트이름 변수이름;
    // 델리게이트 변수 앞에 event 키워드를 붙인다

    Action : C#에서 제공하는 내장 delegate. 아래와 같이 두 줄을 한 줄로 줄여 쓸 수 있게 해 준다.

    public delegate void MoveDelegateFunc(Vector2 moveVector);
    public delegate void LookDelegateFunc(Vector2 lookVector);
    public delegate void ShootDelegateFunc(bool fire);
    
    public event MoveDelegateFunc OnMoveEvent;
    public event LookDelegateFunc OnLookEvent;
    public event ShootDelegateFunc OnFireEvent;
    public event Action<Vector2> OnMoveEvent;
    public event Action<Vector2> OnLookEvent;
    public event Action<bool> OnFireEvent;

    단, Action은 반환형이 void인 델리게이트에만 사용이 가능하다.

    구독 시스템

    1. 이렇게 event로 만든 변수에 여러 함수들을 추가 해 준 뒤
    2. 옵저버 역할을 하는 다른 클래스에서 특정 이벤트를 관측하여 해당 event 변수에 추가했던 메서드들을 모두 실행하도록 하는 패턴.
    // Observer.cs
    void OnMove(){
        Controller.CallMoveEvent(inputVector2);
    }
    // Controller.cs
    public event Action<Vector2> OnMoveEvent;
    public event Action<Vector2> OnLookEvent;
    public event Action<bool> OnFireEvent;
    
    public void CallMoveEvent(Vector2 direction)
    { OnMoveEvent?.Invoke(direction); }
    // Player.cs
    // 플레이어를 움직이는 메서드를, OnMoveEvent에 구독
    void Awake(){ Controller.OnMoveEvent += Move(); }
    void Move(Vector2 moveDirection){
        // 플레이어를 이동하는 구문들
    }

    플레이어 메서드에서 Move 메서드를 Controller의 OnMoveEvent에 구독해두고, 옵저버에서 키보드 입력을 관측하여 OnMove 메서드를 통해 CallMoveEvent 메서드를 실행하면 OnMoveEvent에 구독했던 모든 메서드들을 실행한다.

    특강 중 질의응답 내용

    델리게이트도 +=이 되나요?

    • Yes

    event가 존재하는 스크립트를 싱글톤으로 사용해도 되나요?

    • Yes, 오히려 그렇게 하게 될 듯 함.

    그래도 키가 눌렸는지 확인하려면 결국 Update는 써야겠죠?

    • Yes, 예) Player Input

    만약 등록된 모든 스크립트에 OnMove를 둔다면 다 따로동작하게도 할수 있나요?

    • Yes

    event들을 한군데로 모아서 이벤트 매니저를 만들어서 사용하는게 좀 더 효율적일까요?

    • 케바케겠지만 일단 Yes

    아까의 GameOver같은 경우는 자주 일어나는 상황이 아닌데, 옵저버로 매순간 관측하는 게 좋은가요?

    • Yes, Update()형태가 아니게 할 수도 있고, 한다고 해도 오버헤드가 나지는 않을 것

    델리게이트를 이벤트 처럼 쓸수있는데 반대는 못 하는거네요?

    • Yes, 델리게이트가 이벤트를 포함하는 관계

    혹시 이벤트를 너무 많이 사용하면 안 좋은 점도 있나요?

    • 비상식적으로 과한게 아니라면 No

    이벤트나 델리게이트를 하는일이 별로 없어도 습관적으로 모든곳에 쓰는게 낫다고 보면 될까요?

    • 개발적으로 권장되는 사항이며, 특히 공부 단계에서는 Yes.
    • 다만 단순한 프로그램에서는 효율이 좋지 않을 수 있음.

    이벤트를 호출할때 Invoke로 호출하는 이유가 있을까요?

    • ? (널러블)를 사용하기 위해.
      action?.Invoke() // 이와 같이 action이 null 일 경우에도 대응되도록 하기 위해 사용.
      위 구문은 if(action != null) action(); 과 같음

    3. 과제에 대해

    • 내용 정리 후 발표 준비
    반응형
    COMMENT
     
    01
    30

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

    전체진행도 : 26일차

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

    작성일자 : 2024.01.29(월)

    개발일지 목록 : 클릭


    1. 진행중인 과정에 대해

    팀 플젝을 마무리하고 개인 공부 시간을 가지기로 했다.

    버그 픽스까지 마치고 피로함에 뻗어서 오늘은 넉다운 상태..

    2. 오늘 학습에 대해

    • 코테 연습 한 문제

    코딩테스트 연습 > 2023 KAKAO BLIND RECRUITMENT > 개인정보 수집 유효기간

    using System;
    using System.Collections.Generic;
    
    public class Solution {
        public int[] solution(string today, string[] terms, string[] privacies) {
            int[] answer = new int[] {};
    
            // 프로그래머스에서 메서드 사용하는 법 및 출력메시지 띄우는 법 확인
            // hello();
    
            // 예상 솔루션
            // 1. terms는 사전형 변수에 저장
            // 2. 년도에는 365, 달에는 28, 일에는 1의 가중치를 두어 각 정보와 today를 비교
            // 3. privacy가 같거나 작을 경우, result에 추가
            // 3-1. result는 값 갯수의 변동이 있으므로 리스트로 관리
            List<int> result = new List<int>();
    
            // 1. terms를 사전형 변수에 저장
            Dictionary<char,int> termsDict = new Dictionary<char,int>();
            foreach(string term in terms){
                // 검색: split 사용법(파이썬의 term.split())
                // -> 마찬가지로 term.Split(' '); 과 같이 사용 가능
                // 검색: 특정 인덱스 이후 모든 문자열을 사용하는법(파이썬의 term[2:])
                // -> term.Substring(2); 과 같이 사용 가능
                char key = term[0];
                int value = int.Parse(term.Substring(2));
    
                // Console.WriteLine($"{key}: {value}"); // 테스트 구문
    
                // 검색: 사전형에 요소 추가
                // -> termsDict.Add(key, value);
    
                termsDict.Add(key, value);
            }
    
            // 2. 년은 365, 달은 28, 일은 1의 가중치를 두어 각 정보와 today를 비교
            int today_value = 0;
            today_value = 
                int.Parse(today.Substring(0,4)) * 28*12 +
                int.Parse(today.Substring(5,2)) * 28 +
                int.Parse(today.Substring(8,2)) * 1
                ;
            Console.WriteLine($"Today value: {today_value}"); // 테스트 구문
            for(int i=0; i<privacies.Length; i++){
                string privacy = privacies[i];
                int limitDay_value = 
                    int.Parse(privacy.Substring(0,4)) * 28*12 +
                    int.Parse(privacy.Substring(5,2)) * 28 +
                    int.Parse(privacy.Substring(8,2)) * 1 +
                    termsDict[privacy[11]] * 28
                    ;
                Console.WriteLine($"privacy index[{i+1}] value: {limitDay_value}"); // 테스트 구문
                // 3. privacy가 작을 경우, result에 추가
                if(limitDay_value <= today_value)
                    result.Add(i+1);
            }
    
            answer = result.ToArray();
    
            return answer;
        }
    
        public void hello(){
            Console.WriteLine("Hello");
        }
    }
    • 프로그래머스에서 메서드 사용하는 법 및 출력메시지 띄우는 법
    • 문자열에서 특정 문자를 기준으로 쪼개는 법 // .Split(' ')
    • 문자열 내에서 특정 인덱스 구간의 문자열을 떼어 사용하는 법 // .Substring(0,4)
    • 사전형에 요소 추가하기 // .Add(key, value)

    3. 과제에 대해

    • 의견 모아 제출과 발표 마치기
    반응형
    COMMENT
     
    01
    27

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

    전체진행도 : 25일차

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

    작성일자 : 2024.01.26(금)

    개발일지 목록 : 클릭


    1. 진행중인 과정에 대해

    팀 과제 '탕후루 만들기' -> (개명)'탕후루를 부탁해!' 의 대부분 기능을 구현하였다. 사운드쪽만 월요일까지 작업하는 것으로 하고, 나머지 시간은 QA에 할애 할 것으로 보인다.

    팀 과제에 할애하느라 알고리즘 풀이를 거의 일주일만에 했는데 간단한 것도 생각이 안 나 찾아보며 진행했다.

    2. 오늘 학습에 대해

    오늘의 팀 프로젝트 커밋

    한 게 정말 많지만, 기록하기에 할애할 여유가 없어 매일 아쉽다.

    지금까지 진행하며 가장 아쉬운 점은 GameManager.cs에 너무 많은 량의 로직 코드를 작성했다는 것이다.

    보기에도 지저분하고, GameManager에 대한 의존도가 심각하게 높다.

    게임 전체를 총괄하는 역할은 맞지만, 세세한 공정까지 모두 직접 하는 애매한 포지션이 되어버렸다.

    이러한 생각을 하며 대화형 인공지능에게 코드리뷰를 맡겨 본 결과는 아래와 같다.


    유니티에서 GameManager와 같은 중앙 관리 클래스를 사용하는 것은 일반적인 패턴입니다. 이는 게임의 전반적인 상태 관리, 싱글톤 구현, 씬 전환, 점수 관리 등을 위해 사용될 수 있습니다. 하지만, 제공하신 코드를 보면 몇 가지 개선할 수 있는 부분이 보입니다:

    1. 책임 분리(Separation of Concerns): 현재 GameManager 클래스에는 여러 책임이 혼재되어 있습니다. 예를 들어, UI 관리, 게임 상태 관리, 씬 로딩, 점수 계산 및 고점수 관리, 사운드 관리 등이 모두 한 클래스 내에 구현되어 있습니다. 이러한 다양한 책임들을 별도의 클래스로 분리하여 각각의 관심사를 더 명확히 구분하는 것이 좋습니다. 예를 들어, UI 관련 로직은 UIManager, 점수 및 고점수 관리는 ScoreManager, 사운드 관리는 AudioManager와 같이 별도의 클래스로 분리할 수 있습니다.
    2. 긴 메소드 및 클래스: GameManager 클래스와 그 내부의 메소드들이 매우 길고 복잡합니다. 이는 코드를 읽고 이해하는 데 더 많은 시간이 소요되며, 유지 보수가 어려워질 수 있습니다. 각 메소드가 하나의 명확한 기능만 수행하도록 리팩토링하고, 필요하다면 복잡한 기능을 여러 개의 보조 메소드로 분리하는 것이 좋습니다.
    3. 정적 접근과 싱글톤: GameManager.Instance를 통해 전역적으로 접근 가능한 싱글톤 패턴을 사용하고 있습니다. 이는 편리할 수 있지만, 오버유즈는 전역 상태에 대한 의존성을 높이고 테스트와 유지보수를 어렵게 만듭니다. 싱글톤 사용을 최소화하고, 가능한 컴포넌트 간 의존성을 명시적으로 만드는 것이 좋습니다.
    4. 하드코딩된 값과 마법의 숫자: 코드 내에서 직접적으로 하드코딩된 값(예: "HighScore", "_MainScene" 등)과 마법의 숫자(예: 0.1f, 3 등)를 사용하고 있습니다. 이러한 값들은 상수로 정의하거나 구성 파일에서 불러오는 방식으로 관리하는 것이 좋습니다. 이렇게 하면 나중에 값을 변경할 때 한 곳에서만 수정하면 되므로 유지 보수가 용이해집니다.
    5. 리소스 관리: Resources.Load를 사용하여 리소스를 불러오는 방식은 유연하지만, 사용하지 않는 리소스가 메모리에 남아있을 수 있으므로 효율적인 리소스 관리를 위해 주의가 필요합니다. 가능하다면, 유니티의 Addressable Asset System 같은 더 효율적인 리소스 관리 시스템을 고려하는 것이 좋습니다.
    6. 게임 상태 관리: 게임 상태를 enum과 switch 문으로 관리하는 것은 간단한 프로젝트에는 적합할 수 있지만, 상태가 복잡해질수록 관리가 어려워집니다. 상태 패턴(State Pattern)을 적용하여 각 상태를 별도의 클래스로 구현하고, 이를 통해 상태 전환 로직을 더 명확하고 유지보수하기 쉽게 만들 수 있습니다.

    코드를 개선하려면, 위의 지점들을 고려하여 리팩토링을 진행하는 것이 좋습니다. 이는 코드의 가독성, 확장성, 유지 보수성을 향상시키고, 장기적으로 프로젝트의 품질을 높이는 데 도움이 됩니다.


    모든 씬에서 독립적인 클래스 상태를 가지는 싱글톤에 대해 배우며 적극적으로 활용하자고 생각했지만, 실제 프로젝트를 진행 해 보면 싱글톤이 마냥 좋은 것만은 아니었다. 특히 씬이 갱신 될 때마다 초기화를 해줘야 하는 번거로움을 싱글톤 스크립트에서 해결하려고 하면 코드가 무척 길어지고, 씬이 로드 되는 도중에 실행되지 않도록 별도의 작업도 하나하나 해 주어야 하는데 개발 과정에서 많은 버그를 일으키고 시간이 소비된다.

    프로젝트의 전체 스크립트 파일

    위와 같은 문제점들은 다른 스크립트에 책임분리를 함으로써 해결되는 부분이기 때문에, 스크립트 파일이 많아지는 것에 거부감을 갖지 않고 활용하면 좋겠다는 생각을 하였다.

    내가 맡은 파트에서 GameManager.csUIManager.cs 에서 대부분을 해결했는데, DisplayMainPanel.cs 스크립트를 하나 추가하여 사용한 것 만으로도 숨통이 트이는 듯한 기분을 느꼈다.

    알고리즘

    오늘은 가볍게 한 문제만 풀이하였다. 별도의 메모를 해 가며 풀이하는 건 너무 많은 에너지가 소모되어, 코드를 작성하며 생각하고 참고한 내용들을 주석에 적는 방식을 채택하였다.

    코딩테스트 연습 > 2022 KAKAO TECH INTERNSHIP > 성격 유형 검사하기

    using System;
    using System.Linq;
    using System.Collections.Generic;
    
    public class Solution {
        public string solution(string[] survey, int[] choices) {
            string answer = "";
    
            // 일주일 쉬어서 감 다 죽음
    
            // 찾아보기-1. choices의 각 원소를 3 빼서 새로운 배열에 담는 법(Linq)
            // int[] newChoices = choices.Select(choice => choice - 4).ToArray();
                // choices.Select(choice => choice - 4)를 사용했을 때의 결과 자료형은 IEnumerable<int>
                // LINQ의 Select 메서드는 컬렉션의 각 요소에 대해 지정된 변환 함수를 적용하고
                // 변환된 요소들을 포함하는 새로운 IEnumerable<T> 시퀀스를 반환
                // 변환 함수 choice => choice - 4는 정수를 반환하기 때문에, 결과 시퀀스의 타입은 IEnumerable<int>
    
            // 생각해보니, 위 방식은 설문결과의 분산(?)을 알 수 없게 만드는 요인이 되기 떄문에 사용하지 않도록 했다.
    
            // 찾아보기-2. 사전형식 자료형 초기화
            Dictionary<char, int> scores = new Dictionary<char, int>{
                {'R',0},{'T',0},{'C',0},{'F',0},
                {'J',0},{'M',0},{'A',0},{'N',0},
            };
                // var 사용과 Dictionary<char, int> 사용 중 자유롭게 선택. 장단점은 특별한 건 없고 상황이나 취향에 맞게.
    
            // 각 문항별 점수 집계
            int threshold = 4;
            for(int i = 0; i < choices.Length; i++){
                int selectedNum = choices[i];
                if(selectedNum<threshold)
                    scores[survey[i][0]] += (threshold - selectedNum);
                if(selectedNum>threshold)
                    scores[survey[i][1]] += (selectedNum - threshold);
            }
    
            // 성격유형 진단
            if(scores['R']>=scores['T'])    answer += 'R'.ToString();
            else                            answer += 'T'.ToString();
            if(scores['C']>=scores['F'])    answer += 'C'.ToString();
            else                            answer += 'F'.ToString();
            if(scores['J']>=scores['M'])    answer += 'J'.ToString();
            else                            answer += 'M'.ToString();
            if(scores['A']>=scores['N'])    answer += 'A'.ToString();
            else                            answer += 'N'.ToString();
    
            return answer;
        }
    }
    • 코드를 간결하게 하는 Linq, 사전형의 사용을 다시 찾아보았다.
    • 탭을 활용하여 반복되는 코드를 깔끔하게 정리했다.

    탭을 활용하는 건 처음 시도 한 방법인데, 이전 팀원 중 한명의 코드 작성 방식이 떠오른 겸 적용 해 보았는데 깔끔하게 작성된 것을 보고 흡족했다.

    3. 과제에 대해

    • 팀 과제 지속 리팩토링

    4. 참고자료

    • 없음 / ChatGPT-GPT4 는 잊어버렸던 내용이나 코드리뷰 등 적재적소에 활용중
    반응형
    COMMENT
     
    01
    25

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

    전체진행도 : 24일차

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

    작성일자 : 2024.01.25(목)

    개발일지 목록 : 클릭


    1. 진행중인 과정에 대해

    오늘의 악마, 절대 팝업되지 않는 설정 패널

    팀 과제 탕후루 만들기 2일차이다. 기본적인 게임진행 로직, 장면디자인과 상호작용을 오늘 대부분 완료한 것으로 보인다.

    다만 자잘한 버그가 정말 많고 하나하나 별 거 아닌 기능인데 해결이 안되어 아주 골칫거리이다.

    오는 수요일까지 과제제출 및 결과 발표라 생각보다 여유 시간이 될 지 모르겠다.

    오후에는 챌린지 클래스 세션이 있었다. 델리게이트에 관한 소개와 예시로 세션을 진행하였다. 전혀 몰랐던 내용이긴 하지만 파이썬에서도 비슷한 방식으로 함수를 사용했기 때문에 흡수하는 데에 무리는 없었다.

     

    2. 오늘 학습에 대해

    [Serialized] 에 대해 새로 배운 사용법

    팀원 한명이 에디터에서 플레이 버튼을 누르고 [Serialized]를 통해 변수가 바뀌는 걸 보며 디버깅을 한다는 얘기를 들었다. 생각 못 해본 방법이었는데 꽤나 괜찮다고 생각했다.

    싱글톤 오브젝트의 치명적인 단점

    예를 들어 ButtonManager.cs에서 '패널을 활성화하는 메서드'를 작성한 후, 다른 씬의 버튼에서 이걸 사용하려고 한다면 에디터의 버튼 이벤트로 작용하는 OnClick에 ButtonManager 오브젝트를 드래그 할 수가 없어, 스크립트로 할당하는 게 강제되는 것으로 보인다.

    구글링이랑 GPT를 통해 알아봐도 스크립트의 구성을 다르게 하는 것 말고 깔끔한 해결법은 없는 것으로 보인다.

    싱글톤 오브젝트를 사용 시 문제점 두번째

    씬 전환 등을 할 때 Start나 Awake가 실행되지 않기 때문에 다른 방안을 모색해야 했다.

    씬의 로딩이 완료되고 다른 오브젝트들이 만들어진 후 스크립트를 실행하는 방법의 예는 아래와 같다.

        private void Start()
        {
            SceneManager.sceneLoaded += OnSceneLoaded;
        }
    
        private void OnDestroy()
        {
            SceneManager.sceneLoaded -= OnSceneLoaded;
        }
    
        private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
        {
            FindAndAssignUIElements(scene);
        }
    
        private void FindAndAssignUIElements(Scene scene)
        {
            // 로드되는 씬 이름에 따라 UI를 로드
            if (scene.name == "_TitleScene")
            {
                GameObject canvasObject = GameObject.Find("Canvas");
                highscore = GameObject.Find("HighScore").GetComponent<Text>();
                settingsPanel = GameObject.Find("SettingUI");
            }
            else if (scene.name == "_MainScene")
            {
                GameObject canvasObject = GameObject.Find("Canvas");
                pausedPanel = canvasObject.transform.Find("PauseUI").gameObject;
                settingsPanel = GameObject.Find("SettingUI");
                score = canvasObject.transform.Find("ScoreUI").transform.Find("Score").GetComponent<Text>();
                resultPanel = canvasObject.transform.Find("ResultUI").gameObject;
                resultScore = canvasObject.transform.Find("ResultUI").transform.Find("ScoreText").GetComponent<Text>();
                resultHighScore = canvasObject.transform.Find("ResultUI").transform.Find("HighScoreText").GetComponent<Text>();
            }
    
            // 씬 로드 이벤트 발생할 때마다 초기 상태를 비활성화로 설정
            SetPanelActive(settingsPanel, false);
            SetPanelActive(pausedPanel, false);
            SetPanelActive(resultPanel, false);
        }

    씬의 이름에 따라 여러 UI 오브젝트들을 UIManager에 할당해준다. 다만 이런 방식은 UIManager가 처음 생성될 때는 작용하지 않는 것으로 보인다. SettingUI 오브젝트를 어째선지 다른 방법을 동원해도 UIManager에 할당하지 못하여 아직도 타이틀메뉴에서 세팅 창을 열지 못하는 상태이다. 정말 단순한 방법으로도 되지 않은 것으로 보아 무언가 알 수 없는 꼬임이 발생한 것 같다.

    3. 과제에 대해

    • 팀 프로젝트 버그픽스 위주로 계속 진행하기
    반응형
    COMMENT
     
    01
    24

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

    전체진행도 : 23일차

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

    작성일자 : 2024.01.24(수)

    개발일지 목록 : 클릭


    1. 진행중인 과정에 대해

    팀 과제 선정

    새로운 팀 과제를 시작하였다. 팀원들과 회의를 통해 '탕후루 만들기'라는 게임을 만들기로 정했다.

    주어진 레시피에 맞게 하늘에서 떨어지는 과일들을 받아, 10개의 탕후루를 만들면 게임 종료.

    오늘 작성한 컨셉기획서에서 퍼온 팀원의 그림. 묘사력이 굉장하다.

    매 탕후루마다 레시피와의 일치도에 따라 점수 부여를 하여 집계한다.

    역할분담을 하고, 각자 맡을 파트를 확실히 정해 과제를 시작하였다.

    나는 GameManager의 구현을 맡았다. 컨셉기획서의 작성 도중 GameManager에서 구현할 내용은 다음과 같다.

    마찬가지로 컨셉기획서의 구현 기능 목록 중 일부, 파워포인트를 수상할정도로 잘 활용하는 팀원이 작성했다.

    • 장면전환과 UI 로직 전반, 게임 규칙 로직 전반

    구현히 엄청 재밌을 것 같았고 우선해서 지원하는 팀원이 없는 것을 확인하고 바로 맡았다.

    첫날이지만 하루종일 불태워 GameManager에서 요구하는 기능의 70% 이상은 구현한 듯 하다.

    오늘의 마지막 PR

    팀 노션에도 오늘 먹은 것(TIA), 오늘의 특강(GIT)과 QnA의 정리, 스크럼 내용 정리 등 여러가지를 꾸미고, 버전 관리에 익숙해지기 위해 커밋메시지와 PR 등을 계속 다듬도록 노력했다.

    하루를 알차게 보내면 시간이 빠르게 간다는 생각이 드는데, 오늘은 그런 것 조차 신경 쓸 여유가 없을 정도로 활동한 것 같다. 살면서 가장 빛나고 있을지도.

    2. 오늘 학습에 대해

    구현 중에 어째선지 잘 되지 않던 내용

    버튼UI가 어째선지 눌리지 않았다. 원인부터 적자면 Scene 내에 EventSystem이 없었다.

    같은 이슈를 예전에 한 번 겪은 적이 있었지만, 이 원인을 알아채는 데에 많은 시간이 걸렸다.

    처음에는 메서드 자체가 문제라고 생각하여, 디버그를 수 차례 하였지만, 해당 메서드는 실행조차 되지 않았다.

    다음은 버튼을 클릭했을 때 실행되는 메서드가 잘 연결되지 않았는지 오브젝트 간 관계나 포함되는 컴포넌트들을 조사했지만 원인을 찾을 수 없었다.

    다른 씬과 비교해보는 도중 씬 내에 EventSystem 오브젝트가 없다는 것을 발견하여 원인을 찾을 수 있었다.

    해당 이슈가 발생했을 때의 대표적인 증상은 아래와 같다.

    • 버튼이 눌리는 모션이 발생하지 않는다.

    버튼이 아닌 그냥 이미지를 상대하는 듯하다는 느낌을 다음에 받는다면, EventSystem 오브젝트가 있는지 확인하는 것을 기억해야겠다.

    오늘 구현한 스크립트에 대해

    1. 게임 상태 관리 (GameManager.cs):

    • GameState 열거형을 정의하여 게임의 다양한 상태 (MainMenu, Playing, Paused, GameOver)를 관리한다.
    • GameManager 싱글톤 패턴을 구현하여 게임 전반에 걸쳐 하나의 인스턴스만 유지하도록 한다.
    • ChangeState 메서드를 통해 게임 상태를 전환하고, 각 상태에 따른 로직을 구현하였다. (예: 타임 스케일 조정, 사운드 관리).

    2. 게임 플레이 로직 (GameManager.cs):

    • StartGame, PauseGame, ResumeGame, RestartGame, BackMainMenu, GameOver 메서드를 구현하여 게임의 주요 행동을 제어하였다.

    3. 게임 데이터 초기화 및 로드 (GameManager.cs):

    • ResetData 메서드를 통해 특정 조건에서 게임 데이터를 초기화한다.
    • PlayerPrefs를 사용하여 최고 점수를 로드하고 저장한다.

    4. 게임 씬 관리 (GameManager.cs):

    • LoadMainSceneLoadTitleScene 메서드를 사용하여 게임의 메인 씬과 타이틀 씬을 로드한다.

    5. UI 관리 (UIManager.cs):

    • UIManager 싱글톤 패턴을 구현하였다.
    • SceneManager.sceneLoaded 이벤트를 사용하여 씬 로딩 시 UI 요소를 찾고 할당하였다.
    • 토글이 필요한 모든 UI 패널의 활성화/비활성화를 위한 메서드들을 제공한다.

    6. 버튼 상호작용 (ButtonManager.cs):

    • StartGame, PauseGame, BackMainMenu, ResumeGame, RestartGame 등의 UI 버튼에 응답하는 메서드들을 ButtonManager에 구현하였다.

    7. 목표 탕후루 생성 및 진행 업데이트 (GameManager.cs):

    • GenerateTargetTanghulu 메서드를 통해 랜덤한 목표 탕후루를 생성한다.
    • UpdateTanghuluProgress 메서드를 호출하여 플레이어가 쌓은 과일과 목표 탕후루를 비교하고 점수를 계산하며 종료조건을 얻는다.

    오늘의 작업을 통해 게임의 핵심 메커니즘과 흐름 제어를 위한 기반을 마련하였고, 각 컴포넌트의 기본적인 상호작용을 설정하였다.

    이후, StartDroppingFruits, CalculateAndUpdateScore, ShowTargetTanghulu 등의 메서드에 대한 구체적인 구현을 진행할 필요가 있다.

    대부분을 구현하였지만, Player를 담당하는 다른 팀원이 원활한 개발을 하도록 하기 위해서는 과일을 실제로 생성해주는 메서드의 구현이 가장 필요한 부분이다.

    3. 과제에 대해

    • StartDroppingFruits, CalculateAndUpdateScore, ShowTargetTanghulu 등의 메서드에 대한 구체적인 구현
    • GameManager에서 더 구현할 내용 찾기...당장 ExitGame 기능(게임 종료 버튼)을 구현하지 않았다는 것도 생각났다.

    4. GIT 특강 QnA 정리

    깃 특강 중에 나왔던 질의응답 내용이 무척 도움이 되어 정리 해 보았다. 하이라이트는 나의 질문

    Q&A

    작업 도중에 다른 브렌치나 이전 커밋으로 되돌아가서 확인 해 보고 싶을 때
    현재 작업 내용을 커밋해두고 이동하는 게 정상인가요?

    • Stash 사용(이후 설명)

     

    씬을 나눠서 작업하는경우 나눠서 작업하다가 마지막에 한씬에 합치는 방식인건가요?

    • (예시를 보여줌)
      • 즉, 같은 씬에서 여러 명이 기능구현을 하는 상태라면, 씬을 나눠서 하는 방식 대신 다른 방법으로.

     

    처음 레파지토리 생성할 때 깃 이그노어를 Unity로 설정하면 되나요?

    • Yes

     

    개인 적으로 궁금한건데 한명이 에셋을 받아서 적용한걸 올리면 다른사람이 그걸 받았을 때 적용이 되나요?

    • Yes

     

    메인에 합치는 작업은 팀장이 아니라 작업을 한 본인이 하는 편이 좋을까요?

    • 케바케. 지금 단계에서는 각자 Merge를 하되, 코드리뷰를 서로 잘 하는 것을 추천

     

    나중에 '게임매니저' 에 대해서 수정할 게 생기면 새로 또 브런치를 파서 작업하시는건가요?

    • Yes
    • 과거 이미 메인에 Merge한 브런치를 계속 이용하는 것은 튜터 입장에서 비추. 합친 뒤 하루이틀 뒤에 삭제하는 편.
    • 작업할 게 생길때마다 브런치를 열어서 작업
    • 히스토리에 계속 쌓이면 보기 안좋아서? 관리가 힘듬

    삭제한 브런치를 다시 가져오는 방법도 있나용?

    • No

     

    내용중에 GithubDesktop 및 유니티 인스펙터에서 한글깨짐 현상도 보여서 생각나서 질문드립니다
    /.editorconfig

    [*]
    charset = utf-8-bom

    위와 같이 프로젝트 루트 폴더에 .editorconfig 파일을 만들어 인코딩 문제를 해결하는 방법이 있던데, 현업에서도 이런 밑작업을 하고 시작하는 편인가요?
    몇 주 전에 팀 프로젝트에서 이슈(주석이나, '한글이름.png'를 불러오는 코드 등)가 있었을 때에는 코드 내에 한글 사용을 지양하는 걸로 진행했습니다.

    • 맥을 사용하는 사람이 없다면, 그냥 세팅 없이도 하는 편.
    • 실제로 한글 사용을 지양하는 편이 맞는듯.

     

    (제일 처음 질문에 이어)Stash에 관해

    • Branch - Stash all changes // 임시저장 후 다른 브런치로 이동(체크아웃) 등 할 수 있음
    • Restore을 통해 Stash했던 내용 가져오기 가능

     

    stash 공간은 하나인가요?

    • 확인 필요(확인 안되고 끝난듯)

     

    체리픽에 관해

    • 특정 커밋만 선택해서 가져오는법
    • Github Desktop에서도 가능

     

    혹시 이미 커밋해버린 친구들을 지우는 방법이 있나요?+커밋 이름도 커밋후에 수정이 가능한지 궁금합니다

    • Push가 된 건 못돌림.
    • 아직 커밋만 된 건 취소 가능 : Undo Commit
    • Amend commit(가장 마지막 푸시된 Commit만 가능) + Forced Push Origin : 이미 푸시한 커밋도 '커밋 메시지'만은 수정 가능
    • Revert?

     

    conflict 도중에 a작업물을 선택할거를 잘못해서 b를 선택해서 머지 되었을 경우 다시 conflict 작업으로 돌아가서 b작업물을 선택할 수 있는 방법이 있나요?

    • 미답변
    반응형
    COMMENT
     
    01
    23

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

    전체진행도 : 22일차

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

    작성일자 : 2024.01.23(화)

    개발일지 목록 : 클릭


    1. 진행중인 과정에 대해

    Unity를 사용하여, 캐릭터 외형과 이름선택을 하여 필드에서 돌아다닐 수 있도록 하는 개인과제 프로젝트를 진행중이다.

    제출기한이 오늘까지인데, 1차제출인 13시까지 밤을 새서 만들어 제출하고, 피곤해서 꾸벅꾸벅 졸면서 오후를 보내고, 추가 보수를 하여 21시에 다시 제출하였다.

    개발 초기에 스크립트 간 설계를 제대로 하지 않아 현재 시점에서는 보수가 크게 필요한 상황이라는 점, 특히 플레이어의 데이터가 여러 곳에 산재해 있어 이를 모아 하나의 클래스 객체화 하는 것이 남은 과제이다. 시간을 들여서 보수를 할 수 있을 것 같은데, 내일부터 할 팀 프로젝트는 아무래도 개인프로젝트에서 이어지지 않는다는 소문이 있어 추가 작업을 할 것 같지는 않다.

    2. 오늘 학습에 대해

    개발 중 이슈에 대해서는 리포지토리의 README.md에 모두 작성하였다. 그 내용은 아래와 같다.

    필수 요구사항

    1. 캐릭터 만들기
      • 입문 강의 중 사용했던 스프라이트 사용
    2. 캐릭터 이동 및 바라보기
      • InputSystem 사용
    3. 스테이지 만들기
      • 타일맵 사용
    4. 카메라가 캐릭터를 따라가기
      • 카메라 오브젝트를 Player의 하위 오브젝트로 넣음.
      • 스무스한 카메라 구현은 현재 방식으로 어려움, 이후 CameraPivot 오브젝트를 Player의 하위 오브젝트로 넣어 MainCamera가 정해진 규칙대로 CameraPivot을 따라가도록 하여 개선 가능
    5. 캐릭터 애니메이션
      • 현재 준비된 두 캐릭터에 대해 각각 idle상태, walk상태에 대한 애니메이션 마련
      • Animator의 파라미터로 int CharacterNum을 주어, 캐릭터의 외형을 선택 시 해당 CharacterNum 를 설정하고 그에 맞는 애니메이션을 Layers에서 선택하여 재생하려고 하였으나, 잘 작동하지 않아 한 개의 레이어에 두 캐릭터 외형에 대한 애니메이션을 모두 넣음. 캐릭터 외형 증가에 따라 애니메이터 동작 업데이트의 확장성 매우 낮음.
    6. 시작 시 이름 입력
      • 2~10자 이내로 이름 입력 가능
      • 캐릭터의 머리 위에 이름 반영
    7. 시작 시 캐릭터 선택
      • 두 종류의 캐릭터 마련, 캐릭터 종류가 늘어날 때마다 애니메이션을 추가해야함. 현재 애니메이터 구조로는 다수의 캐릭터 지원 어려움

    선택 요구사항(달성)

    1. 시간 표시
      • TMP와 LegacyText 어느쪽을 사용하더라도 시간 표시가 반영되도록 스크립팅
    2. 인게임 캐릭터 재선택

    개선 할 점

    • 타일 맵 분리 작업 : 현재 장식과 BackDesign이랑 같이 그려진 상태, 장식 타일만을 배치할 Decorate 타일맵 오브젝트 생성으로 개선 필요
    • 텍스트 폰트 단일화 : TMP에는 나눔글씨체를 사용하지 못하고 있음, Legacy로 바꾸던가 TMP용 폰트파일을 만들던가(시간 오래걸림) 해서 서체 단일화 필요.
    • 현재 산재한 플레이어 정보들을 모아 하나의 클래스 객체로 관리하기
    • 위와 동시에 스크립트도 정리하기
    • TMP와 LegacyText 어느쪽이라도 텍스트가 반영되도록 하는 전용 유틸 메서드 작성
    • Image와 SpriteRenderer 어느쪽이라도 스프라이트가 반영되도록 하는 전용 유틸 메서드 작성

    해야 할 일

    • Player의 클래스화
    • NPC와 Player의 클래스 리스트 관리
    • 선택과제 2번 : 인게임 이름 바꾸기(새로운 캔버스 또는 패널을 만들어야 할 듯)
    • 선택과제 3번 : 참석 인원 UI(NPC/Player의 리스트 관리 마련 후 가능)
    • 선택과제 5번 : NPC 대화(NPC 마련부터 해야함)

    개발이슈

    • 입문 강의를 소화하고 개인과제까지 하는 데에 너무 적은 시간이 부여됨. 강의시청을 도중 중단하고, 1일 이내의 시간을 개인과제에 할애하여 필수 요구사항만을 우선 구현.
      • 개인과제를 급하게 만들었기 때문에 강의에서 배운 내용을 적용하지 못한 부분이나 플레이어 데이터의 관리 등 여러 부분에서 결함이 많음.
    • TextMeshPro에서 새로운 폰트 적용이 번거로움. 한글 폰트를 새로 작성하고 적용하려 했으나 생성에 시간이 너무 오래 걸려 보류.

    3. 과제에 대해

    • 진행중인 과정에 대해 상기한대로, 추가적인 프로젝트 보수는 없을 것 같다. 내일부터 새로 주어지는 팀 과제에 착수.
    • 주어진 유니티 강의를 50%가량 소화하지 못했다. 해당 강의를 다시 보며 공부해야 할 것 같다. 디자인패턴에 대해 신경을 쓴 내용이라 필수적으로 알아야 할 스킬들이다.

    4. 참고자료

    • 없음

    5. GPT4 문답

    • 없음
    반응형
    COMMENT