로비씬에는 해당 유저의 인벤토리, 조합표,아아이템상점 3가지의 컨텐츠가있다.

로비에서 해당직업의 공격력을 업그레이드 시키면 인게임에서 그 직업의 해당하는 히어로가 스폰될때 공격력이 업그레이드 된 상태로 스폰된다. 

조합창

 

상점

상점에는 2가지가 있다. 골드로 살 수 있는 직업별 버프 업그레이드, 인겜에서 사용하는 아이템

상점_골드상점

구매를 하면 돈이 10골드씩 상승하도록 했다. 

상점_보석상점

상점같은 경우 해당하는 아이템을 클릭 할 경우 이펙트를 줬다. 만약 이게 없다면 샀는지 안샀는지 알 수가 없기 때문에 

 

인벤토리

인벤토리 클릭할때 마다 정보 상태를 업데이트를 해준다.

업데이트해야할 항목들은 버프업그레이드상점에서 어떤 직업의 몇업이 됐는지 그리고 보석상점에서 어떤 아이템과 몇개를 보유하고 있는지이다. 아이템같은 경우는 현재의 총 아이템개수, 그리고 각각 몇개의 아이템이 보유하고 있는지를 표시해준다.

랜덤박스컨텐츠

아이템이 두가지 아이템중 랜덤으로 1개의 아이템을 받을 수 있다. 

인게임 컨텐츠

그룹 지정 컨텐츠

구현 결과 : 그룹지정. 히어로들을 그룹을 지정하여 캐릭터를 한꺼번에 이동할 수 있도록 하였다. 그룹은 'A' ,'B' 두 가지로 나누었고 그룹 지정된 히어로를 이동 시 한꺼번에 같은 위치로 이동하도록 한다.

싱글턴으로 그룹 A, B List를 만들었고 거기서 그룹에서 빼거나 추가하거나 반복하도록 처리했다.

1. 그룹지정

하단 왼쪽 캐릭터 상태창을 보면 '그룹 A로'가 있다.  현재 그룹 상태가 'N' 한번 클릭하면 'A' 한 번 더 클릭하면 'B'로 변경된다.

즉 버튼의 Lable내용은 현재 그룹의 클릭 다음 상태에 그룹을 표시하도록 한다.  

위 그림처럼 '그룹 A로'클릭과 동시에 그 히어로는 그룹 A에 추가된다. 상태창 위에 보면 현재 캐릭터가 무슨 그룹인지 표시해주고 그 그룹에 어떤 히어로들이 추가됐는지 알 수 있도록 했다.

1. 그룹 이동

같은 그룹에 속한 히어로들은 일정한 위치로 그룹 이동하도록 했다.

개별 이동

그룹 지정한 히어로들을 한 캐릭터만 이동할 경우도 있다. 그럴 때는 개별 이동을 클릭하여 이동 처리한다.

 

히어로 판매

게임을 진행하다 보면 필요 없는 히어로를 판매할 수 있다. 판매하면 인게임에서 사용할 수 있는 soul이라는 재화를 얻게 된다. 해당 히어로의 가치 1성 50 soul을 이펙트를 보여준다.

인게임 속 히어로가 사라지면 히어로 딕셔너리에 Remove와 해당 히어로의 soul값을 표시해주며 비활성화 처리한다.

soul은 여러 군데에서 많이 사용하기에 풀링 처리하였고 특정 부분에서 위치를 변경하여 게임 씬에 보이도록 하였다. 히어로가 현재 인게임상 태에서 사리 지면 처리해야 할 부분들이 있다. 

1. 인게임 히어로 딕셔너리에서 지워주기

2. 해당하는 soul을 추가하기(인게임 soul표시 ui창 업데이트)

3. 현재 존재하는 히어로 개수 차감하기

 

인게임 컨텐츠

조합컨텐츠

자동으로 생성되는 히어로는 1성으로 등급이 가장 낮은 히어로들만 스폰된다. 이 1성 등급의 히어로들 가지고 2성으로 업그레이드가 가능하도록 하였다.

이런 식으로 인게임에 추가된 히어로들을 탐색하여 조합 여부를 표시해주는데 알파 값을 조절하여 현재 조합이 가능/불가능한지를 표시하고 조합이 불가능한 상태면 버튼 자체를 비활성화된다. 위 그림을 보면 워리어와 위자드가 있기 때문에 2성 등급의 소드맨으로 조합이 가능하다. 

소드맨버튼을 클릭하면 재료가 되는 메카닉과 워리어는 맵에서 사라지고 화랑이라는 히어로가 스폰된다.

오른쪽 하단 조합 버튼의 HeroList. 스크립트는 현재 자기 나열된 조합 목록들을 가지고 있다. 그렇기 때문에 그 리스트들을 가지고 인게임의 히어로들을 탐색하여 삭제 처리하도록 한다.

 

인게임 히어로 등급별 버프 상점 컨텐츠

모든 UI를 그냥 활/비활성화 처리하지 않았다. UI를 살아있듯 유저가 누를 때 작은 재미를 주기 위함과 생동감을 표현해주고 싶었다. 그렇기에 상점을 클릭하면 강화창이 위치 이동한다.

강화창은 1성 2성 3성 각각 soul재화 값이 다르며 클릭하면 해당하는 등급의 히어로들의 공격력을 증가된다.

1성 등급 버튼을 누르면 인게임에 존재하는 1성 히어로들은 레벨업과 동시에 공격력이 상승된다.(초록색 글씨로 표시) 마찬 가지로 2,3성 업그레이드 클릭 시 해당하는 등급의 히어로들은 업그레이드 처리가 된다.

 

타일프리팹을 복사하여 원하는 크기의 맵을만든다.

로비씬에서 스테이지를 선택하고 거기에 선택한 스테이지의 맞는 타일스프라이트로 변경하도록 했다. 타일의 스프라이트 뿐만 아니라 몬스터들이 일정한 위치를 회전하도록 몬스터 경로포인트도 위치도 타일은 알고있어야한다.

그런다음 몬스터는 생성될 때 스테이지의 맞는 이동경로를 받도록 했다. 

목표위치의 가깝게 오면 다른 포인트로 목표위치가 바뀌도록 했다.

 

 

게임을 진행하다가 조합 표를 누르면 전체 조합 표를 볼 수 있도록 만들었지만 게임을 진행하다가 사용자가 더 편히 현재 어떤 히어로가 존재하며 어떤 클릭한 히어로만의 조합 표를 보여준다.

현재 진행상황으로는 히어로가 스폰될때마다 히어로 테이블에 저장된  데이터를 입력받는다. 그리고 던전에 추가됐다는 것을 인게임 매니저에게 알리고 있다. (딕셔너리의 자기 자신의 스크립트와 오브젝트를 추가한다) 

 

조합표 데이터 로드_7

그런데 히어로 조합 표는 히어로를 클릭했을 때 자기 자신이 베이스 재료인 경우인 조합 표들을 나열해야 한다.  

조합표테이블

즉 클릭한 히어로가 nMaterial1인 경우의 조합표들을 보여주면 된다. 2번인 warrior를 클릭하면 nID가 9번, 11번, 18번, 19번 조합 표를 출력하면 된다.

사실 현재 던전에 있는 히어로들은 전에 작업해서 쉽게 받아 올수 있었지만 나 자신이 베이스 재료인지는 테이블 다운로드할 때 자료구조를 조금 추가가 필요했다.

인게임 시작할때 조합표를 업데이트하는 함수. (시작할 때 한 번 실행됨)

추가적으로 위 처럼 만들어주고 베이스 재료만 따로 또 저장했다. 

조합테이블

그리고 히어로를 클릭하면 클릭한 히어로의 sprite이미지와 이름, 그리고 이미지의 알파 값을 높여 현재 존재하고 있다는 것을 알려준다. 클릭한 히어로는 인게임 매니저에서 따로 찾지 않는다 (있으니까 클릭한 것이기 때문)

그리고 두번째 재료가 존재하는지 찾는다. 여기서 문제점이 발생했다. 두 번째 재료를 찾는 과정으로 인게임 매니저에서 현재 보유하고 있는 히어로들을 찾는데, 만약 여기서 베이스 재료가 세 번째 재료가 같은 히어로라면?

그러면 현재 보유한 헤어로가 1개여도 중복된 히어로들은 모두 있다고 표시해줄 것이다. 그것을 막기 위해서 히어로가 생성하때 각 오브젝트의 새로운 고윳값을 생성하게 했다. 그렇게 그 고윳값을 제외한 히어로들을 찾도록 했다.

그리고 조합표가 상위등급을 만드는 재료가 2개가 필요한 히어로도 있고 3개가 필요한 히어로도 있다. 각각 필요한 히어로 조합만 표시해주기 위해!=0이 아닐 경우에 밑에 재료를 업데이트해주도록 했다. 그리고 그 조합 리스트의 위치 값을 변경해 오른쪽 정렬하도록 했다.

 

-진행상황

해당하는 히어로가 조합표 재료로 두개일때 
해당하는 히어로가 베이스인 조합표 모두 로드
상위등급 히어로 조합가능
조합재료가 3개일때 2개일때 다른 오른쪽 정렬
클릭하면 보임

 

-미완료 상황

- 추가적으로 상위등급 활성화된 결과 sprite는 조합이 가능하다고 알리는 이펙트효과를 주고

- 결과를 제외한 2성이상 조합표리스트에 마우스를 올리면 그조합표의 어떤 조합표로 제작됐는지 표시해주는 미니 조합표도

아직~작업중

- 완료된 조합표를 클릭하면 캐릭터 교체작업 모든재료를 풀에 넣고 조합결과 히어로를 생성하도록

문제점 : 빨간색 / 해결방법 : 파란색 / 느낀 점 : 녹색 

갑자기 잘되던 부분에서 오류가났다. 일단 처음보는 오류였다. 아마 오브젝트를 프리팹화 시키고 해제하고 하는 과정에서 오류를 발생시키는 것 같았다.  

찾아보았다. 역시 프리팹화관련 문제였다. 프리팹을 생성하는 과정에 인스턴싱하는 오브젝트의 부모를 변경해주는 부분이 있는데 그 그 부모가 프리팹화되어 있어 수정이 안된다는 오류였다.

내가 UI Root자체를 프리팹화 시켰었다. 이렇게 해놓으니 그 밑에 있는 오브젝트를 수정할 수 가 없었다.  프리팹화를 해제하고 나니오류가 해결됐다. 

내 생각하는 오브젝트들을 프리팹화시키면 개발하는데 조금 안전하다고 생각한다. 하지만 최종부모의 오브젝트를 통째로 프리팹화 시키고 그 아래 있는 오브젝트들도 프리팹화 시켰었던 것 같다. 그래서 오류가 발생한 것일 수도 있다. 

 

 

 

히어로들이 공격할 때 와 몬스터가 죽을때 효과를 넣었다.

-몬스터가 피격 당했을 때

1. 머테리얼값 조정, 2. 체력 40%이하 체력바 초록색 -> 빨간색

- 몬스터가 죽었을 때

몬스터의 soul 값을 출력해주고, 파티클을 실행한다. (죽음과 동시에 InGameMng -> soul값 갱신

몬스터는 풀링으로 돌아가기 때문에 파티클과 soul표시 애니메이션도 같이 풀링스크립트에서 죽었다고 판단되는 몬스터의 위치에서 실행한다. 

-히어로 이팩트

궁수
마법사

원거리 공격 히어로들 각각 특징의 맞는 파티클을 넣줬다. 궁수는 방향계산으로 적의 위치를 계산해서 화살의 방향을 바꿔준다.

- 스테이지(던전 선택) 씬

팝업오브젝트는 한개로 공유하고 선택하는 내용과 제목을 변경하도록 설정했다.

 

 

 

-추가적으로 맵을 타일을 던전 진입 시 생성하는데 몬스터의 경로를 그려줬다. 그리고 나머지는 타일 이미지는 랜덤으로 배열된다. 

5. 히어로 랜덤생성

던전을 입장하면 캐릭터가 가장 낮은 등급의 히어로들이 웨이드마다 랜덤으로 스폰된다.  인 게임 매니저(인게임의 진행되는 상황을 알 고 있는 클래스)에서 스킵버튼을 누르거나 웨이브시간이 지나면 자동적으로 몬스터 생성과 히어로를 랜덤 생성한다. 그 스크립트에서 아래 In_CreateObj클래스(히어로,몬스터들을 직접적으로 생성하는 클래스)의 함수를 호출한다.

대충 진행되는 구조 순서는 이렇게 된다.

1. 웨이브스킵 or 웨이브 시간 히어로 생성 호출(히어로 몇명 만들 건 지)

2. ExtensionMethodw.class의 접근해서 스폰할 히어로의 랜덤 값(랜덤스폰이기 때문 / 히어로의 넘버)

3. 실제 생성 캐릭터가 자식으로 들어갈 부모위치

4. 던전 중앙에서 스폰되니 다시 한번 위치를 잡아준다. (정해 놓은 위치4가지 중 랜덤으로)

5. 인 게임의 히어로가 추가 되었음을 알리기 위해 히어로 딕셔너리의 추가해준다. 

 

In_CreateObj.class

이 메서드를 호출 할때 일단 몇명의 히어로들을 생성할 것인가, 그리고 웨이브마다 어떤 특정 이벤트를 넣을 것인가(나중의 특정 상황이 주어질 수 도 있으니 일단 넣어놨다)

히어로 생성할때 그 넘버값도 같이 보내는 이유는 히어로의 생성과 동시에 오브젝트의 이름을 같이 변경해 주기 위함이다.

네 번째순서의 히어로 위치를 정할 때 코루틴으로 사용한 이유는 캐릭터가 생성될때 이펙트의 진행과 동시에 캐릭터가 생성되는 느낌을 주기 위해 약간의 딜레이를 주었다. 

Define.class
히어로의 생성위치 4개를vector배열로 가지고 있음(랜덤 위치)

 

-현재까지 진행상황

4. 인게임_히어로이동 처리

사실 NGUI로 캐릭터를 이동하는것이 그렇게 까다롭지 않아 보였다. 히어로자체를 NGUI스크립트인 버튼으로 만들어서 히어로를 클릭하면 저번에 만들었던 히어로 상태창을 업데이트 해주고 그리고 땅을 찍으면 해당하는 위치로 캐릭터를 움직이면 된다.

UGUI를할때는 마우스로 클릭 한 스크린좌표를 월드좌표로 변환해서 오브젝트를 클릭한 위치로 움직이도록 했다. 이번에도 이 방법으로 하려고 시도해봤다.

게임을 제작하기 전에 리소스를 찾아보다가 알맞는 2D리소스를 찾았는데 sprite가 아니었다.(2D가아님.2.5D?) 그래서 NGUI좌표에 움직이는게 난해 했다. 위치를 기본적인 NGUI로 만들어진 UI들은 위치가 표준화?되어 위치잡기가 쉽지만 이 캐릭터들은 위치가 저멀리 딴 세상?으로 위치해 있었다. 그래서 기본적인 sprite의 Depth가 쉽게 조절이 가능한 반면 캐릭터들은 조절하기 좀 까다로웠다. 

그래서 문제점 =>  캐릭터를 클릭하고 원하는 위치를 클릭하면 갑자기 맵뒤로 사라지기도 하고 예측하지 못한 위치로 이동하는 경우가 발생했다. 

해결방법 : 캐릭터의 위치를 이리저리 움직여봤더니 z값을 음수가 되는 순간 카메라 제일 앞에 위치됐다. 

 

일단 캐릭터가 화면에 사라지는것은 잡았고, 또 다른  문제점 =>  문제는  캐릭터가 이동은 잘 되는데 클릭이 잘안된다.  이것 역시도 캐릭터가 sprite가 아니라 ngui의 boxCollider로 인식을 하지 못했다.  방법은 캐릭터의 sprite를 넣었다. 처음에는 비어있는 이미지를 넣고 알파값을 0으로 만들어서 안보이도록 하려했으나,  자식 오브젝트로 들어가 있는 캐릭터의 공격범위를 보여주는 circle의  sprite도 알파값이 0으로 변경된다.

해결방법 : 캐릭터의 sprite를 그림자로 만들었다. 

 

사실 그림자가없을때 살짝 떠 있는지 느낌이어서 조금 아쉬웠는데 덕분에 이 부분도 해결됐다.

그리고  

문제점 =>  클릭한 위치로 이동하는데 캐릭터가 약간 중구난방에 서 있는 느낌이 들었다.  

해결방법 : 클릭한 좌표가 아닌 맵타일의 위치 값을 받아와서 이동하도록 처리 

캐릭터 대칭이동 : 마지막으로 캐릭터가 클릭한 좌표(움직 일 좌표)와 지금 서 있는 좌표랑 x값을 비교하여 좌우 대칭 이동을 처리했다.

ExtensionMethod.class

 

- 현재까지 진행상황

3_인 게임 캐릭터 상태 업데이트

게임자체가 공격하는 유닛이 움직일 수가 있다. 그렇기 때문에 플레이어가 클릭한 캐릭터에 대한 정보를 표시해줄 필요가 있다.

여러 종류의 캐릭터가 있기 때문에 각 캐릭터가 생성될 때 데이터를 입력받도록 했다.

캐릭터에 정보를 가지고 있는 스크립트

- 캐릭터 사정거리(공격범위)

클릭하면 캐릭터의 공격범위를 표시되도록 했다.

- 지금까지 진행상황

각 캐릭터에 대한 정보 업데이트

각 히어로는 테이블을 통해 받아오는 데이터(이름,타입,공격력,공격범위,공격속도 등등)와 스테이지 진행상황의 데이터(레벨,그룹지정)등을 나뉘어진다.

 

스프레드시트를 이용해 테이블을 가져오는것 까지 완료했다. 이제 데이터를 통해 게임씬에 적용시켜야한다. 

2. 조합표 구성

첫 번째로 구현 한것이 조합표 (어떤 캐릭터와 캐릭터가 있어야 조합을 할 수 있는지를 유저에게 알려주기 위한 표이다.) 이 조합표는 인게임에서 던전에서 게임중에 언젠든지 클릭하면 볼 수 있다. 

일단 캐릭터 등급은 기본적으로 생성되는 1성 캐릭터와 조합으로 만들 수 있는 2성과 3성으로 구성되어있다.

조합표이기 때문에 1성은 제외하고 2성과 3성으로 sprite를 구성

조합표(테이블 로드되기 전)

구성은 위 이미지처럼 이렇게 되고 당연히 조합리스트들이 한 배경에 담지 못하기 때문에 UI Panel(Sofr Clip), UI Scroll View를 통해 드래그하여 다음 리스트들을 볼 수 있도록 처리했다. 테이블이 로드되고 캐릭터의 해당하는 조합구성이 넣어지도록 했다.

캐릭터sprite이름을Herotable nID값을 통일하여 해당 데이터의 맞게 고유값으로 처리했기 때문에  이미지 이름을 또 찾을 필요가 없다.

enum eHero

-실제 테이블 데이터와 비교

Comb Table
데이터 로드된 조합표

또 2성은 3번째 조합재료가 없다. 그렇기 때문에 데이터값이 0이되면 오브젝트를 비활성화 하도록 처리했다.

 

-현재까지 진행상황

 

 

NGUI 2D 조합디펜스게임 

예상 개발기간 : 3주~4주

게임소개 :  랜덤으로 생성되는 캐릭터를 조합(강화)하는 디펜스게임

 

1. 테이블구성

해당 타입의 맞게 스프레드시트를 통해 데이터를 불러와 표시하려고 한다.

config : 버전 등

stage : 스테이지 데이터들을 가지고 있는 테이블

Hero : 캐릭터 능력치

캐릭터 테이블

Comb: 조합테이블

조합표 테이블

캐릭터들은 nID를 통해 관리하고 싶어 hero,comb 테이블 각각 다르지만 ID를 고유값으로 설정했다.

사실 이렇게 초기셋팅은 해놨지만 구현하다가 바뀔수도 있을것 같다.

파일은 스프레드시트의 키값과 주소를 문자열을 파싱해여 Json형식으로 받아 데이터형식으로 받는다.

이 테이블은 각각 Table.class를 생성했고, TableMng.class를 통해 테이블을 요청했을때 해당하는 테이블 데이터를 리턴하도록 만들었다. 

그리고 게임시작할때 한번 모든 테이블과 데이터들은 로드된다. 이 테이블이 다운로드되는 시간을 로딩신으로 구현할것이다. 

게임시작 시 실행

 

리소스를 임포트를 하려고 했는데 이런 에러가났다.

알아보니 이런 오류가나는 몇가지 이유들이 있었다.

1. 파일경로에 영어외의 문자가 있을경우

2. 패키지작성자와 패키지수신자의 버전이 맞지 않는 경우

3. 패키지를 export하는 압축 과정중에 아직 끝나지 않은 경우에 유니티를 종료했을 경우

 

나는 3번째 경우였다.

 

-형상관리툴

소프트웨어 버전 관리 툴이라고 생각하면 될듯하다. 

-형상관리는 소스의 변화를 끊임없이 관리하는 것이다. 개발과정에서 소스를 수정하거나 특정 시점으로 되돌릴 수 있기 때문에 특히 협업프로젝트는 필히

형상관리 툴로 깃허브,빗버킷,깃랩,SVN등이 있지만 이 프로젝트는 깃랩을 사용하기로 했다.

이유는 pravite저장소에 10GB용량을 무료로 지원해주기 때문이다.

-소스트리

사실 깃을 사용하기에는 쉽지않다. 깃에서 사용되는 명령어들도 알고 있어야한다.  그래서 좀 더 쉽게 사용하기 위해 소스트리 https://www.sourcetreeapp.com/ 를 이용하기로 했다.

소스트리

연동방법:  Clone탭 -> 첫 번째칸에는 깃랩 프로젝트의 주소를 넣고 두 번째 경로에는 내 로컬저장소 위치를 넣어주면된다.

유니티는 Asset,Packages,ProjectSettings 이 3개의 폴더만 푸쉬해야한다.

그렇기 때문에 이런식으로 무시 해두자. 한번 무시하면 저폴더의 하위목록들은 커밋창에 뜨지 않는다.

소스트리는 커밋으로 로컬 저장소에 올린 뒤에 푸쉬를해서 서버에 올리는 방식이다. 이중저장같은 느낌

 

유의점

- 작업하는 유니티버전 맞추기(버전마다 리소스의 메타파일 ID가 다를 수 있음)

- Edit - Project Settings - Asset Serializationg - Force Text로 변경

씬에 변경된 내용을 추적이 가능하다고 한다. 아직 해본적이 없지만 일단 이렇게 셋팅!

파란색 : 풀면서 어려웠던 부분 or 고려해야 될 부분

빨간색 : 해결방법

초록색 : 느낀점

문제

문자열 배열을 받아서 위에서 부터 차례대로 받는다고 생각하고, 입력된 문자열중에 같은 문자열이 있으면 문자열 끝에 '(1)'을 추가해준다. ex) a라는 문자열이 존재한 상태에서 a를 추가하면 새로 추가한 문자열은 a(1)이된다. 그 다음 다시 a를 넣으면  a가 있기 때문에 a(1)으로 바꿔준다. 그런데 또 a(1)이 있기 때문에 a(2)로 바꿔준다. 

가장 이해하기 좋은 테스트 문제

풀이 : 내가 작성한 코드

배열을 통째로 받지만 함수안에서 위에서부터 처음부터 받는다고 생각해야한다. 일단 문자열을 관리 할 수있는 컨테이너를 만든 뒤, 문자열을 하나 추가하면 그 컨테이너에서 새로 추가한 문자열이 중복이 있는지 체크한다. 그런데 여기서 한번 더 생각해야 할 것이 처삽입될 문자열을 바꾸고, 또 다시 컨테이너를 탐색해야한다. 컨테이너의 같은 문자열이 있을 수는 없기 떄문이다.   for문으로 체크하기에는 배열의 인덱스와 요소 체크하기가 까다로울 것 같고, while문으로 삽입된 리스트들을 통째로 탐색하도록 한다.

count를 통해 같은 문자열의 갯수를 체크하고 문자열의 값을 변경하면서 while문을 통해 list를 탐색한다.

풀이 : 다른 사람이 작성한 코드

while(!set.Add(cade))를 통해 중복된 문자가있으면 카운트를 증가시키고 그렇지 않으면 set에 추가 하게된다. HashSet은 잘 사용하지 않았었는데 HashSet.Add에는 bool반환형이 있다. 추가되었으면 ture, 요소가 이미 있으면 false를 리턴한다.

 

문제 : https://codesignal.com/

3D물체를 움직이면 캐릭터에 가까운 지형은 활성화 멀리있는 지형은 비활성화 하도록 풀링을 해보았다.

땅을 생성하면 자동적으로 랜덤으로 땅위 바위와 나무들이 랜덤으로 위치한다.

클릭 하면 gif 재생

처음에는 지형이 가지고 있는 스크립트에서 캐릭터와의 거리를 체크해주려고 했는데 오브젝트가 꺼져있을때 체크가 불가능하기 때문에 지형이 생성될 때 위치를 관리하고 있다가 그 거리를 캐릭터와 비교 하도록 했다.

m_lipool : 풀링에 들어온 오브젝트

m_liActive : 활성화 된 오브젝트

유니티 실행하면 초기에 땅을 생성하는 함수이다. 생성과 동시에 리스트에 지형오브젝트의 위치를 추가해준다. 

캐릭터와 멀리있는 지형에 있는 오브젝트를 비 활성화 해주기 위한 함수이다. 

m_liTemp는 멀리있는 지형을 비 활성화 해주기 위한 리스트이다. PlanSet스크립트를 통해 멀리있으면 m_liTemp에 담아 두고 꺼주도록 한다. m_liActive에는 삭제해주고 다시 m_liPool에 넣어둔다. 

무한이 로딩되는 맵에 사용하도록 하면 좋을 듯하다.

파란색 : 풀면서 어려웠던 부분 or 고려해야 될 부분

빨간색 : 해결방법

초록색 : 느낀점

문제

','를 구분한 문자열중 가장 긴 문자열을 구하는 문제이다. 특수문자 제외,  쉼표(',')제외

풀이 : 내가 작성한 코드

빈 문자열 변수를 만들어 놓고 a~z || A <Z 사이에 포함되는 문자들만 입력한다. else는 ','를 받게되면 다음 문자열의 길이를 비교하여 다음 문자열을 만들 준비를 한다.  고려할 부분은 띄어쓰기와 특수문자를 제외하는 방법 이 두가지 정도가 있을 수 있다. C#문법만 하더라도 여러가지 방법이 있겠지만 나는 문자의 범위를 비교하였다. 

풀이 : 다른 사람이 작성한 코드

Regex.Matches 문법을 사용했다. 입력 문자열에 있는 정규식을 모두 검색하고 일치 항목을 모두 반환한다고 한다. 그러면 자동적으로 띄어쓰기와 , 특수문자를 제외 시킨 문자가 m 입력된다. C#에는 다양한 문법이 있지만 게임에서 적용은 하기 힘들듯하다.

https://docs.microsoft.com/ko-kr/dotnet/api/system.text.regularexpressions.regex.matches?view=netframework-4.8

문제 : https://codesignal.com/

파란색 : 풀면서 어려웠던 부분 or 고려해야 될 부분

빨간색 : 해결방법

초록색 : 느낀점

문제

문제는 길지만 테스트문제를 보면 쉽게 이해 할 수 있었다. 

문자열을 체크하여 숫자만 뽑아내어 그 숫자들의 합을 리턴하는 문제이다.

풀이 : 내가 작성한 코드

단순히 문자열을 for문을 통해 char요소를 하나씩 체크하여 숫자가 나오면 새로 생성한 int형 변수에 차근차근 더하여 계산 하려했지만, 숫자가 붙어있는 경우(즉 1자리를 넘는 경우)에도 모두 1의자리로 계산이된다.  그러면 숫자를 그때마다 int형으로 바꿔서 더하지 말고 십의자리 그 이상의 자리까지를 대비가 필요하다. char형 문자를 체크하면서 숫자가 나오면 string변 수에 넣어준다. 다음 문자가 숫자가 아닐때 까지, 숫자가 아닐 때 까지 모두 더한 string변수를 int형으로 변경하여 값을 더 해준다.

두번째 if문에서 temp !=""를 체크하는 이유 : 첫 번째 if문은 숫자가 아닐 경우 temp에 있는 값을  sum 변수에 더해주고 있다. 문자열 마지막요소의 형식이 숫자라면 그 전까지 더한 temp를 넘기게된다. 그래서 마지막 체크를 한번 더 해줬다. 

풀이 : 다른 사람이 작성한 코드

foreach(Match match in Regex.Matches(inputString, "[0-9]+") 지정한 정규식을 통해 stirng 숫자값만 딱 뽑아진다.

 

문제 : https://codesignal.com/

이쪽 카테고리는 학습하면서 삽질 + 복습용으로 사용해야겠다.

문법중에 제네릭인데 구조적으로 자주 사용하는게 좋은데 안쓰다보니 계속 안쓰게된다.... 다시 복습겸 기본 예제 코드를 만들어서 복습했다.

public class Test<T>
{
	public T field;
	
	public Test(T t)
	{
		field = t;
	}
}

제네릭 Test.class를 만들고 클래스 인스턴스를 생성할때 생성자를 통해 변수 field에 할당 하도록 했다.  

public class m_test : MonoBehaviour
{
	public Test<int> m_int;
	public GameObject obj_test;
	public Test<GameObject> m_gobj;

	void Start()
    {
		m_int = new Test<int>(10);
		Debug.Log(m_int.field);


		m_gobj = new Test<GameObject>(obj_test);
		Debug.Log(m_gobj.field.gameObject.name);
	}
}

예로 <int>, <GameObject>두 가지로 클래스를 생성했다. Test<int>는 10을 넣고 Test<GameObject>는 TestObj라는 이름 인 GameObject를 인스펙터창에서 넣고    Test<GameObject> 클래스 생성할 때 생성자에 넣었다.

유니티 디버그 콘솔창

where T : 제약조건  (인터페이스

where T : <인터페이스 이름> 형식 인수가 지정된 인터페이스이거나 지정된 인터페이스를 구현해야 합니다. 여러 인터페이스 제약 조건을 지정할 수 있습니다. 제한하는 인터페이스는 제네릭이 될 수도 있습니다.
public interface IMonster
{
	void print();
}
public class MessagePrinter : IMonster
{
	string Message;

	public MessagePrinter(string message)
	{

		this.Message = message;
	}

	public void print()
	{
		Debug.Log(this.Message);
	}
}

public class Test<T>  where T : IMonster
{
	public T[] Array { get; set; }

	public Test(int size)
	{
		Array = new T[3];
	}

}

 

public class m_test : MonoBehaviour 
{

	Test<IMonster> test = new Test<IMonster>(3);
	void Start()
    {
		test.Array[0] = new MessagePrinter("하나");
		test.Array[1] = new MessagePrinter("둘");
		test.Array[2] = new MessagePrinter("셋");


		for (int i = 0; i < 3; i++)
			test.Array[i].print();
	}
}

NGUI를 공부하다가  간단하게 뭐라도 만들면 좋을것 같다고 생각하다가 예~전에 윈도우에 설치되어있던 카드게임을 만들게 되었다. 솔리테어인데 사실 이 게임 룰도 몰랐다. 생각보다 만들다 보니 룰이 복잡했다. 아직 카드를 놓을 수 있는 룰 제한은 넣지 않았다. 

1.카드 나열

순서대로 1번째는 1장,2번째는 2장 ~~~7번째는 7장의 카드가 놓여지고 그 배열의 맨 마지막 카드만 뒤집어짐

2. 나열되지 않은 카드배열 뒤집기

밑에 나열된 카드를 제외하고 남은 카드배열을 가지고있다가 버튼을 누르면 위에서부터 하나씩 뒤집어준다.

3. 마지막 위치한 카드 뒤집기 (아직 룰 적용 X)

카드를 선택하고 이동할 위치에 클릭하면 카드가 선택한 카드의 위치의 카드정렬은 마지막카드를 뒤집어준다.

미처리 부분 

- A로 적혀있는 빈공간에는 A부터 차례대로 놓을 수 있게 처리

- 밑에 뒤집어져있는 7개의 카드에는 제일 위에있는 카드 숫자보다 1 큰 카드만 올 수있고 다른 색의 카드가 와야한다 ex) 7클로버(검      은색)카드가 있다면 8하트(빨간색)or8다이아(빨간색)..빨간색의 숫자가 8인 카드

- 맨 오른쪽위의 카드는 7개의 깔려있는 카드 외의 카드들이다. 모두 뒤집었을 경우 다시 반대로 뒤집어 지도록 

- 모든 카드들은 클릭될 수가 있으므로, 뒤집어져 있는 카드만 클릭이 되도록 설정 예를들어 5장의 카드가 뒤집어져있는 카드배열을 클릭하면 모두 움직이도록 설정이 필요

- 추가적으로  생각나지 않는것들..

간단하게 연습삼아 만들어봤는데 생각보다 복잡해서 당황했다..

 

'유니티 > NGUI' 카테고리의 다른 글

NGUI) UI 사용법[버튼 클릭처리,텍스트(Label)]  (0) 2019.04.30
NGUI) Sprite,아틀라스 활용하기  (0) 2019.04.28

문제점 : 빨간색 / 해결방법 : 파란색 / 느낀 점 : 녹색 

 

위 box, 밑 bomb 

폭탄이 터지면 4방향(좌,위,우,하)를 레이캐스트로 확인해주려고 한다.

오브젝트 종류 : box,wall,player,enemy

4방향의 wall있으면 wall이있는 방향에는 폭탄이 터지는 애니메이션 발동X

box는 box를 애니메이션 발동과 box를파괴를 하고싶었다.

이런식으로 레이가 4방향으로 돌면서 콜라이더를 검출하도록 했다.

우선 충돌처리를 하기 위해 boxcollider2D와 rigidbody2D컴포넌트를 추가했다.

이상하게 계속 검출이 안됐다. 이유는 rigidbody2D의 Simulated체크를 안했었다.

유니티도큐 : 리지드바디 2D 및 연결된 모든 콜라이더 2D와 조인트 2D가 런타임에 물리 시뮬레이션과 상호작용하도록 하려면 Simulated 를 활성화합니다(체크박스를 선택합니다). 비활성화되면(체크박스가 선택 해제되면) 이들 컴포넌트가 시뮬레이션과 상호작용하지 않습니다. 

느낀 점 : 충돌처리를 할때는 생각해야 할 부분들이 있다 . Coliider,Rigidbody, 스크립트 상의 OnTrigger,On ColiiderOnCollisi

onEnter등 차이점과 활성화와 비활성화 했을때 각각 다른점들을 다시 한번에 체크해봐야겠다.

Label 은 폰트를 설정하여 글씨를 표시할 수 있는 객체

Texture 는 PNG, JPG 등의 파일을 불러와 이미지를 표시

Sprite 는 PSD 등의 Atlas파일을 불러와 이미지를 표시하는 오브젝트

Widget 은 기본적인 공간 상의 설정만을 가지는 빈 오브젝트

 

일단 작업전에 셋팅해주면 좋은거

원활한 UI작업을 위해 기즈모 사이즈를 줄여서하는 좋을 듯하다.

NGUI를 사용하려면 알아야 할 기본적인 것들

1. 모든 UI들은 UI Root안에 있어야한다. (UGUI의 canvas처럼)

2. 보통 UGUI와 NGUI를 같이 사용하지 않는다.

3. Depth기준을 어느정도 정해놓고 작업 진행하는게 좋다.

3. UI Panel Clipping -> softclip으로 변경 시

 

버튼 기능만들기

UGUI는 Button컴포넌트가 있다면 NGUI는 EventTrigger가 있다

1. 셋팅

UGUI버튼의 OnClickButton이랑 비슷한 기능이다. On Press는 마우스 클릭했을 때 발동된다. 

클릭하면 스프라이트이미지가 변경된다.

BoxCollider 추가하기

마우스처리를 하려면 콜라이더를 추가해야한다. 그리고 Collider auto-adjust to macth를 클릭해준다. 이게 무엇이냐 NGUI장점 스프라이트 크기만큼 콜라이더의 사이즈가 맞춰진다. 굿!

UI Camera 셋팅 

2D UI로 변경해준다.

텍스트 만들기 Label

Label을 처음 만들면 font를 지정해줘야한다. 같은 객체 안에 UI sprite, UI Label을 같이 사용하지 않도록한다.

확실히 다른 컴포넌트를 추가하지 않아도 기본적인 NGUI 기능들이 많다. 다 쓸까? 오히려 복잡해보이기도 하고 빌트인된 UGUI가 아직 더 편한거 같다.

'유니티 > NGUI' 카테고리의 다른 글

NGUI연습) 솔리테어 카드게임_작업중  (1) 2019.05.03
NGUI) Sprite,아틀라스 활용하기  (0) 2019.04.28

문제점 : 빨간색 / 해결방법 : 파란색 / 느낀 점 : 녹색 

C# 윈폼으로 포폴 소개용 툴을 제작 중에 오류이다.

일단 C#문법을 익힐 때 공부했던 내용이다. static클래스는 모든 멤버가 static멤버로 되어 있으며, 클래스 명 앞에 static이라는 키워드를 정의해주고 사용해야한다. 그리고 Static클래스는 public 생성자를 가질 수 없다.  

Static클래스가 생성자를 가질 수 없는 이유는?

static클래스는 객체를 생성할 수 없기 때문이다. 그럼 또 다시 static클래스의 내용을 살펴보면 인스턴스를 생성해서 메서드를 접근하는 방법과 다르게 static클래스로 부터 직접 호출하는 메서드이다. (클래스이름.메서드이름)

인스턴스를 생성하지 않는다?

일반  클래스는 인스턴스를 생성할 때마다 메모리에 매번 새로 생성되는데, static필드는 프로그램 실행 후 해당 클래스가 처음으로 사용될 때 한 번 초기화되어 계속 동일한 메모리를 사용하게된다.

Static생성자를 이용할때는?

주로 static필드들을 초기화 하는데 사용한다.  생성자가 실행되는 시점은?

1) 클래스의 정적필드에 접근하기 직전에 호출된다.

2) 클래스의 인스턴스를 생성하기 직전에 호출된다.

3) 모든 클래스의 정적 생성자는 최초 한번만 호출된다.


처음에 C#문법을 공부할때는 Static클래스,생성자 등 그냥 그런가보다 외우고 이걸 언제 사용할까? 생각했는데 프로젝트를 만들고 오류에 직접 부딪혀보고 해결하다 보니 이해가 된다. 

	public UISprite sprite;
    void Start()
    {
		sprite.spriteName = "key4";
	}

UGUI에서 아틀라스 공부했을때 드로우콜을 줄여 성능에 도움이된다고 학습했다. NGUI에서도 아틀라스를 다루기위해 학습 포스팅하게 되었다.

(UGUI아틀라스) : https://funfunhanblog.tistory.com/44

UGUI와 사용 방법이 다르다.

1.아틀라스 생성

Create -> Atlas

이름을 지정하고 생성해준다.

성공적으로 생성이 됐다면 Assets폴더에 생긴다.

 2. sprite추가

Open -> Atlas Maker

Atlas Maker를 누르면 위 그림과 같은 창이 뜨는데, 1번 그림 처럼 Atlas를 누르면 2번 그림 처럼 새로운 창이 뜬다. 그러면 현재 만들어져있는 아틀라스 목록들이 나오는데 여기서 사용할 아틀라스를 클릭해준다.

이미지를 추가할 아틀라스를 선택하고 이미지를 클릭하면 자동으로 sprite목록을 추가된다.  그 다음 Add/Update를 누르면 실제 아틀라스에 묶이게 된다.

삭제
추가

삭제한 다음에 이미지를 클릭하면 Add로 상태 보여준다. 같은 방법으로 Add/Update 버튼을 눌러준다.

3. sprite적용방법

sprite객체를 만들고

UI Sprite스크립트에서 생성한 아틀라스와 스프라이트를 설정해준다.

3. sprite적용방법2_스크립트적용

spritename = coin3

    public UISprite sprite;
    void Start()
    {
		sprite.spriteName = "key4";
	}

sprite.sprtieName = "이미지 이름"; 이러면 끝이다

ugui보다 간편하게 적용이 가능하다.

 

그래프 알고리즘 중에 깊이 우선 탐색(DFS) 과정을 유니티로 구현해봤다.

깊이 우선 탐색은 먼저 해당하는 모든 정점을 탐색하고, 넓게(wide) 탐색하기 전에 깊게(deep) 탐색하는 것이다. 인접 노드를 먼저 탐색하고 인접 노드를 모두 방문하면 그다음 정점으로 탐색한다.

 

알파벳 박스들은 정점, 보라 색선은 간선이다.  Clear 버튼을 누르면 데이터를 가지고 있는 컨테이너들을 비워주고 위치 또한 다시 랜덤으로 설정하도록 했다.

오른쪽에 긴 사각형이 stack으로 실제 정점데이터가 stack으로 push 또는 pop 되면, 정점에 해당하는 이미지박스 역시 채워지고 비워진다.

노란색으로 바뀌는 박스는 stack에 들어있는 정점이고, 파란색은 방문한 정점이다. (방문한 정점은 다시 방문하지 않음)

파란색 : 풀면서 어려웠던 부분 or 고려해야 될 부분

빨간색 : 해결방법

초록색 : 느낀점

문제

올바른 시간을 표현하고 있는지 여부를 체크하는 문제이다. 시간 00부터 ~23까지/ 분은 0부터 59까지 표현이 가능하다는 부분

풀이 : 내가 작성한 코드

C#에는 Split라는 함수 덕분에 ':'기준으로 나눈 문자열을 배열로 쉽게 받을 수 있다. 시간을 나타내는 time[0]과 분을 나나태는 time[1]로 나뉘게 된 것이다.  int.Parse를 통해 숫자로 바꾸어주고 24와 59를 비교해준다. 

풀이 : 다른 사람이 작성한 코드

어렵지 않은 문제라 C#함수를 사용한 짧은 코드보다 다른 방법으로 접근한 코드들을 찾아봤다. 

디버깅 해보니 DateTime dummy에는 현재 날짜와 매개변수로 받은 time이 시간,분으로 입력되었다. ParseExact함수 두 번째 문자열은 시간,분을 받는 형식을 지정해준다. 이 형식또는 시간규칙에 맞지 않으면 catch해주도록 한다.

 

문제 : https://codesignal.com/

문제점 : 빨간색 / 해결방법 : 파란색 / 느낀 점 : 녹색 

C# 윈폼으로 포폴 소개용 툴을 제작 중에 오류이다.

저 드랍다운 리스트를 클릭하면 드롭다운으로 선택한 항목의 목록들이 출력되는데

리스트 드랍다운버튼을 클릭하면 실행되는 코드

저 리스트 드랍다운버튼을 클릭하면 실행되는 코드 dataget이라는 data를 가지고 있는 클래스를 받는다( Define클래스를 통해 가져옴) 그런 뒤 listBox1.Item을 채워주면 밑에 목록들이 노출된다. 클릭할 때마다 해당하는 목록이 출력해주기 위해 클릭하는 목록들을 각각 0부터~인덱스를 매겼다.

두 번째 클릭

문제는 각 항목들을 처음 클릭 했을때 잘 노출이 되는데 전에 눌렀던 항목을 다시 클릭하면 리스트가 출력되지 않았다. 이유를 찾아보니 list.Clear()를  하면  생성자를 통해 static으로 새로운 객체를 만들어주고 있기 때문에, Define클래스에 가져오려던 리스트가 Clear가 되는 상황이다. 그리고 항목을 매번 클릭할 때마다 Define에서 받아 오는 것은 좋지 않다.

해결방법 : 

프로그램 시작 시 한번실행되는 함수

처음 시작되는 함수에서 항목들의 데이터를 갖고있는 클래스를 리스트로 받고

항목을 선택 할때 리스트를 접근하여 해당하는 데이터 클래스를 불러오도록 수정했다.

 느낀 점 :  정적클래스와 정적 데이터의 관하여 자주 실수하는 부분이 있기 때문에 개념을 확실히 잡고 메모리 관리에도 신경 쓰면서 작업을 해야겠다. 

문제점 : 빨간색 / 해결방법 : 파란색 / 느낀 점 : 녹색 

오류내용

동적으로 오브젝트를 생성하려고 하자 생기는 ArgumentException..

메시지 내용은   ArgumentException : 인스턴스 화하려는 것이 null입니다.
Unity Engine.Object.CheckNullArgument (System.Object arg,
System.String.Message)

찾아보니 인스터스화하려는 오브젝트 null이라고 한다.

GraphSerach.class

이 코드는 밑에 클래스에 함수를 호출하는 부분

Stackbox.class

이 코드는 호출을 불리는 코드이다. Awake에서 프리 팹 로드할 경로를 받고 StackPush( )를 호출하면 그~동안 계속 잘해왔던 프리 팹이 생성되어야 하는데.

GraphSerach.class

문제는 여기였다. 그냥 클래스 new 만 해주고 있었다. stackBox클래스를 가지고있는 오브젝트를 가지고 있던가 새로운 오브젝트에 그 클래스를 추가하던가 해줘야지 클래스에 있는 함수를 호출할 수 있다.

 

느낀 점 : 너무 기초적인 오류였다. 생각해보면 당연한 거고 어려운 부분도 아니다. 하지만 이런 자잘한 오류인데 이유를 모르고  있다면 시간을 꽤 잡아먹을 수 있다 생각이 든다. 다시 안 하기 위해 작성했다.

인접 행렬법

단점 : 메모리낭비 그래프의 정점의 개수를 V라고 하면 V * V행렬을 이용하여 표현한다. Vi가 행렬 i행과 i열에 대응된다고 할 때 만일 Vi와 Vj간의 간선이 있을 경우 [i,j]와 [j,i]요소를 1로 하고, 아닌 경우는 0으로 한다.

 

인접 리스트법

단순 연결리스트를 이용하여 각 정점에 간선으로 연결된 정점들을 주렁주렁 달아놓는 방법을 말한다. 인접행렬법과 대조적으로 인접리스트법에서는 그래프를 반사적으로 구성하기 위해 명시적으로 정보를 저장하지 않는다. 모든 간선에 대한 정보를 저장하지는 않는다. 정점에서 가능한 필요한 간선만 기록하게 된다.

 

탐색

우선탐색(DFS)과 너비 우선 탐색(Breadth First Search,BFS)방법이 있다. 나무와 다르게 그래프는 방향성이 없기 때문에 순서가 매번 다르다.

인접행렬의 깊이 우선 탐색

시작 정점에서 시작하여 그 정점과 연결된 정점으로 다음에는 그 정점에서 다시 연결된 정점의 순으로 진행된다.(당연히 방문한 정점은 다시 방문하지 않는다)

=> 탐색의 위치가 간선을 따라 깊이 들어가면서 움직인다.

비재귀

스택을 사용, 유일한 순회 순서가 존재하지 않음

인접 리스트의 깊이 우선 탐색

정점의 연결 상태를 알아내는 방법이 인접행렬과 다르다. 인접리스트로 그래프를 구현할 경우에는 순회 순서가 간선의 입력 순서에 민감하다.

먼저 입력된 간선이 각 리스트에서 앞에 위치하게 된다.

방문하지 않은 모든 요소를 탐색 ver

 

인접 행렬의 너비 우선탐색

깊이 우선 탐색과 대조적으로 우선 시작하는 정점과 연결된 정점을 탐색하고 다시 시작 정점의 다른 연결된 정점을 찾는 순으로 탐색한다.  트리순회의 레벨순회와 비슷하다고 생각하면 된다.

그래프의 불규칙성과 여러 개의 연결 요소로 구성된 다는 점을 고려해야 하므로 방문한 정점에 대한 체크가 필요하다. 재귀 호출 부분은 하나의 연결 요소만을 탐색하게 되므로 탐색 부분의 바깥에서 각 연결 요소의 시작 정점을 찾아내어 탐색을 시켜주는 부분이 필요하다.

인접리스트의 너비 우선 탐색

 

학습참고 : c로배우는 알고리즘

파란색 : 풀면서 어려웠던 부분 or 고려해야 될 부분

빨간색 : 해결방법

초록색 : 느낀점

문제

중복된 문자를 개수를 파악해서 문자와 같이 표기해준다. 중복된 문자가 없을 경우 문자만 출력해야 한다.

풀이 : 내가 작성한 코드

어려운 문제는 아닌데 문자열 string을 char로 받아 현재 문자와 다음 문자의 중복 여부를 확인한 뒤 반복문으로 확인했다. 문제는 s [i+1]로 확인하려다 보니 다음 인덱스의 범위가 벗어나서 오류가 떴다. 원래는 while문을 (s[i] == s[i+1])로 했었는데 배열의 길이를 확인하는 것으로 바꿨다. 역시 if, else가 너무 많다. 

풀이 : 다른 사람이 작성한 코드

삼항 연산자를 사용해서 if, else 문도 적고 foreach를 사용하면 나처럼 인덱스 범위를 벗어나는 경우를 체크할 필요가 없었다.  알고리즘 문제를 풀 때 좀 더 여러 방법으로 접근하는 습관을 길러야겠다.

문제 : https://codesignal.com/

+ Recent posts