블로그 이미지
Every unexpected event is a path to learning for you. blueasa

카테고리

분류 전체보기 (2795)
Unity3D (852)
Programming (478)
Server (33)
Unreal (4)
Gamebryo (56)
Tip & Tech (185)
협업 (61)
3DS Max (3)
Game (12)
Utility (68)
Etc (98)
Link (32)
Portfolio (19)
Subject (90)
iOS,OSX (55)
Android (14)
Linux (5)
잉여 프로젝트 (2)
게임이야기 (3)
Memories (20)
Interest (38)
Thinking (38)
한글 (30)
PaperCraft (5)
Animation (408)
Wallpaper (2)
재테크 (18)
Exercise (3)
나만의 맛집 (3)
냥이 (10)
육아 (16)
Total
Today
Yesterday


[링크] https://trello.com/b/UTc6X4FS/-

반응형
Posted by blueasa
, |


[링크] https://bitbucket.org/Unity-Technologies/



[참조] https://youtu.be/iRZoYI1NUmM

반응형
Posted by blueasa
, |

개발 흐름


이 프로세스 문서의 현행화는 Github에서 진행되니 추후에는 Github을 방문해 주시면 고맙겠습니다.

개발 프로세스(Trello, Github, Slack)

1. Trello Card 만들기
1.1 기본적인 Trello 흐름

  • 먼저 Trello에서 개발해야 할 기능을 [To Do(Story)]라는 이름의 리스트에 카드로 만들고,
  • 해당 스토리(카드)를 개발자가 구현에 들어가면 [Doing(WIP)] 리스트에 카드를 옮기고,
  • 리뷰에 들어가면 [Review(Sprint1)] 리스트로 옯기고,
  • 개발 브랜치가 병합하여 테스트를 완료하면 [Done(Sprint1)] 리스트에 카드를 옮기고 해당 기능을 클로즈한다.



1.2 Trello카드 내용은 Description란에는 이슈 링크를 걸어주거나 wiki 링크를 걸어줘, 해당 스토리의 정보를 알 수 있도록 해준다. 그리고 Spec을 참조하여 Checklist를 추가해 완료조건을 기술해 개발해가면서 하나씩 처리해 나간다.


1.3 카드 처리 및 이동시 Slack Alert를 줘 실시간으로 처리가 가능하도록 설정한다.


2. Branch 만들고 Pull Request, Merge 하기
2.1 소스 리모트와 동기화

> git checkout master && git pull origin master && git fetch -p origin

2.2 브랜치명을 만들고 브랜치로 이동.

> git checkout -b dev_standard

2.3 작업 후 커밋.

> git commit -a -m "[VOY-201] README git 사용법 추가"
# 커밋 작성은 issue ID를 넣고 내용은 구체적으로 제시한다.

2.4 작업을 완료 후 master branch로 변경하여 remote에 새로운 변경 사항을 master에 반영

> git checkout master
> git pull origin master && git fetch -p origin

2.5 변경사항이 있다면 dev_standard branch에서 rebase를 수행

> git checkout dev_standard
> git rebase master

- rebase 중 충돌이 발생하면 아래 수행하고 아니면 넘어감.

> git add .
> git rebase --continue

2.6 Pull Request 요청하기 전에 Trello 카드도 [Review] 리스트로 옮기고 Checklist 하나를 더 만들어서 Review 내용(리뷰 담당자 포함)을 기술하면 Trello 알림을 통해 담당자가 리뷰어임을 알게 된다. 아니면 Trello 카드를 만들때 Review Checklist를 미리 만들어서 리뷰 대상자를 등록한다.
 

2.7 Pull Request 요청

> git push origin dev_standard

- push 후 Github의 repository로 이동해서 Compare & pull request 버튼 클릭하고 코멘트[To close VOY-201(jira issue 번호), 혹은 github issue 사용하면 #1234로] 남기고 Create pull request 버튼 클릭한다. 


- 이때 Pull Request 정보가 Slack을 통해 담당자에게 보내지게 되고 리뷰를 수행한다. 


- 리뷰 수행 후 수정사항이 있으면 수정 후 Pull Request를 다시 보내고, 수정 사항이 없으면 Github에서 Merge pull request를 클릭하고 Confirm merge 버튼을 클릭해서 merge를 완료한다. 

- Github에서 merge가 완료되면, Delete branch 버튼을 클릭하거나 아래 로컬에서 커맨드로 원격 브랜치를 삭제한다.(선택사항)

> git push origin :dev_standard

2.8 Review 후 수정사항이 있는 경우 수정한 다음 2.7을 재 수행한다.

2.9 로컬 master 동기화 

> git checkout master && git pull origin master && git fetch -p origin

- 로컬 브랜치 삭제(선택사항)

> git branch -d dev_standard

3. 배포하기
- 테스트 코드를 돌리고, jenkins나 자체 배포 도구를 활용하여 운영 서버에 소스를 배포한다.

4. 배포후 확인
- 기능 테스트를 눈으로 확인하면서 화면의 깨짐, 데이터의 정확성, 브라우저 호환성 등을 점검한다.
- Selenium 도구를 통해 브라우저단에서 테스트를 할 수 있는데, 이 Selenium이 구동한 브라우저의 결과 화면을 아이컨텍해서 봄으로써 어느 정도 테스트 자동화를 할 수 있다.

5. Trello 카드 Done 리스트로 이동
- 배포후 확인에서 이상이 없다면 Trello의 카드를 [Done(Sprint1 - 날짜기간)] 리스트에 이동시키고, 이슈를 close한다.




[출처] http://www.mimul.com/pebble/default/2015/06/08/1433751662702.html

반응형
Posted by blueasa
, |


[링크] http://ljs93kr.tistory.com/44

반응형
Posted by blueasa
, |


[링크] http://ljs93kr.tistory.com/43

반응형
Posted by blueasa
, |

안녕하세요. 오늘도 신선한 '기업 커뮤니케이션' 관련 소식을 전해드리러 온 정은킴입니다 :D


여러분들은 업무를 볼 때 팀원들과 의견을 나누고, 파일을 공유하기 위해 어떤 '메신저' 를 사용하시

나요? 이번 주, 혹은 다음 주에 해야 할 업무와 계획 등을 효과적으로 관리해주는 'To do list' 로는 

어떤 것을 사용하고 계신가요? 우리는 이렇게 우리의 업무를 보다 빠르게, 성공적으로 치기 위해

다양한 '비즈니스 커뮤니케이션 툴'을 사용하고 있습니다.


'비즈니스 커뮤니케이션 툴' 시장은 최근 몇 년 사이 비약적으로 성장해 좀 더 특별한 강점을 내세운 다양한 도구들이 출시되고 있습니다. 특히 업무를 볼 때, 내가 '해야 할 일'을 효과적으로 관리해주는 GTD(Getting Things Done) 기반의 '할 일' 관리 툴은 요즘 같이 각자의 업무가 많아지고, 복잡한 

업무 상황 속에서 모든 직장인들에게 꼭 필요한 툴이라고 볼 수 있습니다. 이미 해외 시장을 비롯, 

국내에서도 다양한 '업무 관리 툴'들이 등장하고 있는데, 그래서 저는 오늘부터 매주 화요일마다 

'비즈니스 커뮤니케이션 툴'에 대해 하나씩 살펴보는 특집 기사를 포스팅 하려고 합니다. 


지난 8월, 사진 공유 사이트로 유명한 '플리커(flikr)'의 공동 창업자인 스튜어트 버터필드가 업무용 

커뮤니케이션 툴인 '슬랙(slack)'을 공개했습니다. 슬랙은 '느슨한, 늘어진'이라는 의미로, 과중한 



업무를 한 시라도 빨리 처리하려는 현대인들에게 슬랙을 사용함으로써 '느슨한' 여유를 가질 수 있게

하자는 뜻을 내포하고 있습니다. 사용자들은 웹을 포함해 모바일에서도 슬랙을 사용할 수 있는데, 웹에서든, 모바일에서든, 동일한 환경으로 쉽게 슬랙을 사용할 수 있어 언제, 어디서든 사용자의 업무 

반경을 넓혀줍니다. 그럼 이제, 슬랙만이 가지고 있는 색깔을 확인해볼까요?




《 '느슨한' 여유를 가지다 》


[Good Point] 저는 이번에 슬랙을 체험하면서 슬랙이 가지고 있는 많은 기능들 중, 특히 자주 사용

했던 두 가지 기능이 있습니다. 그 첫 번째 기능은 바로 '파일 전송' 기능입니다. 슬랙은 웹에서든 

모바일에서든 자유롭게 대화를 나눌 수 있는 메신저 기반의 '비즈니스 커뮤니케이션 툴'인데요, 이

성격에 맞춘 슬랙의 파일 전송 기능은 사용자들이 쉽고 빠르게 다양한 업무용 파일을 공유할 수 

게 해줍니다. 웹에서 슬랙을 실행했을 때, 웹 브라우저 창의 왼쪽 영역에서는 팀원들과 대화를 

나누는 동시에, 오른쪽 영역에서는 내가 보냈거나, 찾고자 하는 파일을 바로 찾을 수 있습니다. 



또한, 파일의 제목과 확장자명 등으로도 쉽게 파일 검색이 가능하고, 이미지나 PDF 등이 파일 속성 별로도 내가 찾고자 하는 파일을 바로 찾을 수 있습니다. 아이디어 회의 같은 간단한 회의를 슬랙에서 진행했을 때, 팀원들과 대화를 나누며 다양한 이미지나 업무와 관련된 기사를 공유할 수 있어 편리합니다. 슬랙의 '파일 전송' 기능은 모바일에서도 동일한 구성으로 이용할 수 있어서 사용자들이 



웹과 모바일 등의 디바이스에 상관 없이 쉽게 사용하기 좋습니다. 내가 찾고자 하는 파일의 속성과 제목 등을 검색할 수 있는 세부 검색 아이콘도 상단바에 달려 있어 사용자들의 눈에 익숙합니다.


슬랙은 '비즈니스 커뮤니케이션 툴'답게 업무의 흔적을 남길 수 있는 기능이 존재합니다. 제가 두 번째로 많이 사용했던 '별표 대화(Starred Items)' 기능인데요, 상대방의 아이디 옆에 있는 별 모양




을 누르면, 해당 대화가 별표 처리되어 웹 브라우저의 오른쪽 영역에서 별표 친 대화만 골라서 볼 수 있습니다. 다른 팀과의 협조가 필요한 업무를 진행하거나, 상사의 중요 공지 사항을 한 데 묶어서 볼 수 있어서 편리합니다. 이 기능은 내가 어떤 대화창에서 업무와 관련해 어떻게 말했는지 확인할 수 있기 때문에 향후 업무 상 커뮤니케이션에 문제가 생겨 '나'와 상대방이 어떻게 대화를 나누고, 어떤 방식으로 업무를 처리하기로 했는지 확인할 수 있습니다. 이는 곧 서로간의 업무 기록이 되어 보다 책임감있게 업무를 처리할 수 있는 작은 도움이 되기도 합니다. 


[Bad Point] 슬랙은 '웹 메신저'의 성격을 띄는 업무 관리 도구인만큼, 왠만한 기능 사용에 있어서 

모든 사용자가 동일하게 쓸 수 있습니다. 하지만, 대화방 생성을 아무나 할 수 있고, 각 대화방의 



주제를 설정하는 것도 권한에 상관 없이 바꿀 수 있는 점은 보안에 민감한 회사들이 사용하기에 적당

한 것 같지 않습니다. 또, 대화방 및 파일함 내에 너무 많은 아이콘과 텍스트(첨부 파일 설명 문구, 

댓글 문구)들이 있어 화면이 정돈되어있지 않다는 느낌이 드는 것도 슬랙이 가진 단점이라고 할 수 있겠습니다.


《 '친절함'을 입히다 》


[Good Point] 슬랙은 타 업무 관리 도구와는 다르게, 세세한 기능에도 신경을 많이 써 사용자들이 

최대한 많은 기능들을 많이 써볼 수 있도록 했습니다. 그래서 사용자들이 많이 클릭하는 메뉴 세 개의 하위 메뉴에 'Help&Feedback'이라는 도움말 기능을 넣어 사용자들이 느낀 불편함을 곧바로 찾을 수 있도록 배려했습니다.



사실 슬랙과 비슷한 업무 관리 도구들을 보면, 도움말 기능이 환경설정 메뉴에만 있거나 그 제품의 

해당 홈페이지로 이동해야 도움말을 볼 수 있는데, 슬랙은 세 곳에 있는 주 메뉴에 도움말 기능을 

동일하게 둠으로써 어떤 기능을 사용하더라도 사용자가 도움말을 접할 수 있는 빠른 환경을 제공합니다.


여러분, 여러분들께서는 현재 사용하고 있는 웹메신저를 처음 사용했을 때를 기억하시나요? 상대방과 대화를 나누려면 어떤 아이콘을 눌러야 하고, 어떤 버튼을 눌러야 파일이 전송되는지 조금은 헤매셨을 것입니다. 하지만, 슬랙에서는 사용자가 처음 슬랙을 실행했을 때부터 헤매지 않게 도와주는 '친절한' 기능이 있습니다. 이 기능은 슬랙의 주요 메뉴 세 곳(대화방 개설, 사용자 정보, 대화 전송)에서 '살아 움직이는' 버튼으로 사용자들의 시선을 끕니다. '살아 움직이는' 동그라미 모양의 이

 

 

버튼은 사용자가 해당 메뉴를 누를 때까지 계속 움직입니다. 사용자들은 무심코 이 버튼을 눌렀다가

해당 메뉴를 어떻게 사용해야 하는지 학습하게 되는데, 해당 메뉴를 설명해주는 도움말을 읽고 난 후

'Done(마침)'이라는 버튼을 클릭하지 않으면 계속 움직이는 버튼이 사용자의 주의를 끕니다.

이 버튼은 사용자들에게 슬랙의 다양한 메뉴를 어떻게 사용하는지 익혀두고, 이를 업무 관리에 곧바로 도입할 수 있도록 했습니다.

 

슬랙은 웹메신저를 기반으로 하는 '업무 관리 도구'로서 특별한 기능이 하나 있습니다. 그것은 바로 '대화 내용 수정' 기능입니다. 이 기능에서는 상대방과 대화를 나눈 후, 내가 했던 대화 내용을 수정할 수 있는데, 타 웹메신저에서는 찾아볼 수 없는 특이한 기능입니다. 저는 처음에 이 기능을 써보 



고 의문이 들었습니다. 왜냐하면, 나중에 내 마음대로 대화 내용을 수정할 수 있기 때문입니다.  

사용자들은 수정하고 싶은 자신의 대화의 '설정' 아이콘을 누른 뒤, 'edit' 메뉴로 들어가 곧바로 



해당 대화 내용을 수정할 수 있습니다. 이렇게 대화 내용 수정이 끝나면 해당 대화 옆에 'edited'라는 문구가 뜨면서 언제 대화 내용을 수정했는지 날짜와 시간까지 구체적으로 확인할 수 있습니다. 


제가 슬랙에서 발견한 또 다른 '친절함'은 사용자의 프로필을 설정해주는 '슬랙봇'에서 찾을 수 있었습니다. 슬랙봇은 사용자의 대화자 리스트 내, 첫 번째로 위치하고 있는데 처음 슬랙을 쓰는 사용자 



들과 1:1로 대화를 하는 형식으로 사용자의 프로필을 설정할 수 있도록 도와줍니다. 이름을 비롯, 부서명, 프로필 사진 등을 슬랙봇과 대화하면서 입력할 수 있어 재미있게 프로필 설정을 할 수 있습니다. 웹 브라우저의 오른쪽 영역에서도 친절한 슬랙봇을 만날 수 있습니다.


[Bad Point] 제가 이번에 슬랙을 체험하면서 가장 아쉬웠던 점은 한국어 지원이 안 된다는 것이었습니다. 사용자 이름 및 닉네임, 대화방 이름까지 영어로만 설정이 가능해 조금 불편합니다. 물론 우리나라에 정식 출시되거나, 한국어 버전이 나온다는 말은 없지만, 우리나라의 '업무 관리 툴' 시장도 

더욱 넓어지고 있는 만큼 더 많은 사람들이 슬랙을 많이 사용해봄으로써 우리나라의 색깔에 맞는 

'업무 관리 툴'이 출시되었으면 좋겠습니다.


또 다른 슬랙의 단점으로는 IE(Internet Explorer)에서는 슬랙을 사용할 수 없다는 것입니다. 이 점은

슬랙이 가진 최대의 단점이라고 볼 수 있는데, 특히 우리나라에서는 IE를 기본 웹브라우저를 설정해

인터넷을 사용하는 사람들이 77%에 달하기때문입니다. (자료 출처: StatCounter, 10월 기준)  

IE에서 슬랙을 실행해서 메시지를 입력하고, 엔터를 누르는 순간 연결이 되지 않았다는 메시지가 

뜹니다. 사용자는 분명히 슬랙에 로그인되어있는데도, 슬랙은 오프라인 상태로 인식합니다. 크롬

이나 파이어폭스 등의 다른 웹브라우저들을 사용하지 않는 사람들에게는 큰 불편함을 초래할 것 같네요.




여러분, 슬랙을 자신의 회사에 도입한 고객들은 3일 만에 이메일 수신량이 75%나 감소했다는 놀라운 결과를 얻었다고 합니다. 그만큼 시간이 오래 걸리고 불필요한 이메일 업무를 깔끔하게 정리해

준 '착한' 도구가 되었다는 뜻이겠죠? 과연 슬랙은 점점 더 치열해지고 있는 '비즈니스 커뮤니케이션 툴' 시장에서 선두 자리를 차지할 수 있을까요? 마냥 '착한' 도구로만 있기에는 '비즈니스 커뮤니케이션 툴' 시장은 너무 냉정합니다.



[출처] http://blog.mailplug.com/391

반응형
Posted by blueasa
, |


[링크] http://ttend.tistory.com/251

반응형
Posted by blueasa
, |

[링크] https://github.com/pumperer/InfiniteList_NGUI



Description

A component that runs on top of NGUI's UIScrollView & UITable classes for Unity3D (i.e. It requires NGUI & Unity3D) can be used with dynamic data.

Instead of instantiating a Prefab for each row in the list we are instantiating a fixed pool of objects that will be reused according to the scroll direction.

Best suited for Mobile (tested on both iOS & Android).

Features

  • Infinite scrolling with clipping panel
  • Only fixed height cells are supported for now
  • Basic Scroll indicator
  • Quick jump (selecting a start point)
  • Simple sections implementation (section height need to be equal to the item height)
  • Click item callbacks
  • Tested on Unity v4.2.2 & Unity v4.3 and NGUI v3.0.9f7

The Demo

This demo package requires both Unity3D http://unity3d.com and NGUI http://www.tasharen.com/?page_id=140 to be installed. video: https://www.youtube.com/watch?v=5xFVJqzp0kY

To run the demo:

  1. Create a new Unity Project (preferably mobile i.e. iOS or Android)
  2. Import NGUI including its examples as the demo uses the atlas's from the examples
  3. Import InfiniteListDemo folder or simply double click on InfiniteListDemoPackage
  4. Run the scene (InfiniteListDemo)

The demo package is released under the MIT License: http://opensource.org/licenses/MIT

Example of app using this component is Avatar Messenger for Android (Contacts list view) which is a free app on Google Play: https://play.google.com/store/apps/details?id=com.orange.labs.avachat

Main Classes & Methods in the Demo

InfiniteListPopulator

The main controller script that can be attached to a gameobject (e.g. the panel)

Some of the main methods included:

Initializes the list (also can be called to refresh the list with new data)

public void InitTableView(ArrayList inDataList, List<int> inSectionsIndices, int inStartIndex)

Parameters:

  • inDataList: the generic list of our data (you can change it to any type you want… just make sure to change the member variables types for dataList & OriginalData)* inNumberOfSections: number of sections (int)
  • inSectionIndices: List of integers. The start index of each section (not as fancy as indexpath in iOS but did the job for me)
  • inStartIndex: where to start

Refresh the list without changing the data (list start at startIndex value)

public void RefreshTableView()

Individual methods for changing the parameters if needed

public void SetStartIndex(int inStartIndex)
public void SetOriginalData(ArrayList inDataList)
public void SetSectionIndices(List<int> inSectionsIndices)

You can include section titles values.. or if you have more detailed sections seperators you can change the implementation of PopulateListSectionWithIndex

string GetTitleForSection(int i)
void PopulateListSectionWithIndex(Transform item, int index)

You can do your implementation of what to populate your row item with (in the demo we simply set a label to string value from our datalist array). Simply change InfiniteItemBehaviour (mentioned later below) to include more items as you want.

void PopulateListItemWithIndex(Transform item, int dataIndex)

Events that can be listened to.

public event InfiniteItemIsPressed InfiniteItemIsPressedEvent;
public event InfiniteItemIsClicked InfiniteItemIsClickedEvent;

InfiniteItemBehaviour and InfiniteSectionBehaviour

Scripts attached to the row item prefab & section prefab (Note: the item prefab need to be tagged as "listItem" and the section prefab as "listSection")

Both checks for the visiblity of the item when the list start scrolling and notifiy the InfiniteListPopulator when the items becomes invisible. you can change use them as a template and include more labels, sprites or textures.

반응형

'Unity3D > NGUI' 카테고리의 다른 글

[펌] Blur filter for UITexture in NGUI  (0) 2017.02.09
[펌] NGUI 쉽게 말풍선 만들기  (0) 2017.02.03
NGUI CoverFlow(with Reflection Shader)  (0) 2016.10.11
[Link] Unity-NGUIExtension  (0) 2016.10.10
[펌] Smart Localization with NGUI  (0) 2016.09.26
Posted by blueasa
, |

Parallax scrolling

For the moment, we have created a static scene with a player and some enemies. It’s a bit boring. Time to enhance our background and scene.

An effect that you find in every single 2D game for 15 years is “parallax scrolling”.

To make it short, the idea is to move the background layers at different speeds (i.e., the farther the layer is, the slower it moves). If done correctly, this gives an illusion of depth. It’s a cool, nice and easy-to-do effect.

Moreover, many shmups use a scrolling in one — or more — axis (except the original one, Space Invaders).

Let’s implement that in Unity.

Theory: defining the scrolling in our game

Adding a scrolling axis need a bit of thinking on how we will make the game with this new aspect.

It’s good to think before coding. :)

What do we want to move?

We have a decision to take here :

  1. First choice: The player and the camera move. The rest is fixed.
  2. Second choice: The player and the camera are static. The level is a treadmill.

The first choice is a no-brainer if you have a Perspective camera. The parallax is obvious: background elements have a higher depth. Thus, they are behind and seems to move slower.

But in a standard 2D game in Unity, we use an Orthographic camera. We don’t have depth at render.

About the camera: remember the “Projection” property of your camera game object. It’s set to Orthographic in our game.

Perspective means that the camera is a classic 3D camera, with depth management. Orthographic is a camera that renders everything at the same depth. It’s particularly useful for a GUI or a 2D game.

In order to add the parallax scrolling effect to our game, the solution is to mix both choices. We will have two scrollings:

  • The player is moving forward along with the camera.
  • Background elements are moving at different speeds (in addition to the camera movement).

Note: you may ask: “Why don’t we just set the camera as a child of the player object?”. Indeed, in Unity, if you set an object (camera or not) as a sub-child of a game object, this object will maintain its relative position to its parent. So if the camera is a child of the player and is centered on him, it will stay that way and will follow him exactly. It could be a solution, but this would not fit with our gameplay.

In a shmup, the camera restricts the player movement. If the camera moves along with the player for both horizontal and vertical axis, then the player is free to go where he wants. We DO want to keep the player inside a restricted area.

We would also recommend to always keep the camera independent in a 2D game. Even in a platformer, the camera isn’t strictly linked to the player: it follows him under some restrictions. Super Mario World has probably one the best camera possible for a platformer. You may have a look at how it is done.

Spawning enemies

Adding a scrolling to our game has consequences, especially concerning enemies. Currently, they are just moving and shooting as soon as the game starts. However, we want them to wait and be invincible until they spawn.

How do we spawn enemies? It depends on the game, definitely. You could define events that spawn enemies when they are triggered, spawn points, pre-determined positions, etc.

Here is what we will do: We position the Poulpies on the scene directly (by dragging the Prefab onto the scene). By default, they are static and invincibles until the camera reaches and activates them.

Camera usage

The nice idea here is that you can use the Unity editor to set the enemies. You read right: without doing anything, you already have a level editor.

Once again, it’s a choice, not science. ;)

Note: on a bigger project, you may need a dedicated level editor such as “Tiled” or a custom one you made. Your levels can be text files (plain text, XML, JSON, etc.) that you read in Unity for example.

Planes

First, we must define what our planes are and for each, if it’s a loop or not. A looping background will repeat over and over again during the level execution. E.g., it’s particularly useful for things like the sky.

Add a new layer to the scene for the background elements.

We are going to have:

Layer  Loop  
Background with the skyYes
Background (1st row of flying platforms)No
Middleground (2nd row of flying platforms)No
Foreground with players and enemiesNo

Planes

We could add as many layers of background objects as we want.

Careful with that axe, Eugene: if you add layers ahead of the foreground layer, be careful with the visibility. Many games do not use this technique because it reduces the clearness of the game, especially in a shmup where the gameplay elements need to be clearly visible.

Practice: Diving into the code

Okay, we saw how implementing a parallax scrolling affects our game.

Did you know? “Scrolling shooters” is another name used for the shmups.

But enough thoughts, time to practice!

Unity has some parallax scrolling scripts in its standard packages (take a look at the 2D platformer demo on the Asset Store). You can of course use them, but we found it would be interesting to build one from scratch the first time.

Standard packages: these are practicals, but be careful to not abuse of them. Using standard packages can block your thoughts and will not make your game stand out of the crowd. They give a Unity feel to your gameplay.

Remember all the flash game clones?

Simple scrolling

We will start with the easy part: scrolling backgrounds without looping.

Remember the “MoveScript” we used before? The basis is the same: a speed and a direction applied over time.

Create a new “ScrollingScript” script:

using UnityEngine;

/// <summary>
/// Parallax scrolling script that should be assigned to a layer
/// </summary>
public class ScrollingScript : MonoBehaviour
{
  /// <summary>
  /// Scrolling speed
  /// </summary>
  public Vector2 speed = new Vector2(2, 2);

  /// <summary>
  /// Moving direction
  /// </summary>
  public Vector2 direction = new Vector2(-1, 0);

  /// <summary>
  /// Movement should be applied to camera
  /// </summary>
  public bool isLinkedToCamera = false;

  void Update()
  {
    // Movement
    Vector3 movement = new Vector3(
      speed.x * direction.x,
      speed.y * direction.y,
      0);

    movement *= Time.deltaTime;
    transform.Translate(movement);

    // Move the camera
    if (isLinkedToCamera)
    {
      Camera.main.transform.Translate(movement);
    }
  }
}

Attach the script to these game objects with these values:

LayerSpeedDirectionLinked to Camera
Background(1, 1)(-1, 0, 0)No
Background elements(1.5, 1.5)(-1, 0, 0)No
Middleground(2.5, 2.5)(-1, 0, 0)No
Foreground(1, 1)(1, 0, 0)Yes

For a convincing result, add elements to the scene:

  • Add a third background part after the two previous ones.
  • Add some small platforms in the layer ` Background elements`.
  • Add platforms in the layer Middleground.
  • Add enemies on the right of the layer Foreground, far from the camera.

The result:

Scrolling effect

Not bad! But we can see that enemies move and shoot when they are out of the camera, even before they spawn!

Moreover, they are never recycled when they pass the player (zoom out in the “Scene” view, and look at the left of the scene: the Poulpies are still moving).

Note: experiment with the values. :)

We’ll fix these problems later. First, we need to manage the infinite background (the sky).

Infinite background scrolling

In order to get an infinite background, we only need to watch the child which is at the left of the infinite layer.

When this object goes beyond the camera left edge, we move it to the right of the layer. Indefinitely.

Infinite scrolling theory

For a layer filled with images, notice that you need a minimum size to cover the camera field, so we never see what’s behind. Here it’s 3 parts for the sky, but it’s completely arbitrary.

Find the correct balance between resource consumption and flexibility for your game.

In our case, the idea is that we will get all the children on the layer and check their renderer.

A note about using the renderer component: This method won’t work with invisible objects (e.g., the ones handling scripts). However, a use case when you need to do this on invisible objects is unlikely.

We will use an handy method to check whether an object’s renderer is visible by the camera. We’ve found it on the community wiki. It’s neither a class nor a script, but a C# class extension.

Extension: the C# language allows you to extend a class with extensions, without needing the base source code of the class.

Create a static method starting with a first parameter which looks like this: this Type currentInstance. The Type class will now have a new method available everywhere your own class is available.

Inside the extension method, you can refer to the current instance calling the method by using the currentInstance parameter instead of this.

The “RendererExtensions” script

Create a new C# file named “RendererExtensions.cs” and fill it with:

using UnityEngine;

public static class RendererExtensions
{
  public static bool IsVisibleFrom(this Renderer renderer, Camera camera)
  {
    Plane[] planes = GeometryUtility.CalculateFrustumPlanes(camera);
    return GeometryUtility.TestPlanesAABB(planes, renderer.bounds);
  }
}

Simple, isn’t it?

Namespaces: you might have already noted that Unity doesn’t add a namespace around a MonoBehaviour script when you create it from the “Project” view. And yet Unity does handle namespaces…

In this tutorial, we are not using namespaces at all. However, in your real project, you might consider to use them. If not, prefix your classes and behaviors to avoid a collision with a third-party library (like NGUI).

The real reason behind not using namespaces was that during the Unity 4 days (this tutorial was originally written for Unity 4.3), a namespace would prevent the use of default parameters. It’s not a problem anymore, so: use namespace!

We will call this method on the leftmost object of the infinite layer.

Full “ScrollingScript”

Observe the full “ScrollingScript” (explanations below):

using System.Collections.Generic;
using System.Linq;
using UnityEngine;

/// <summary>
/// Parallax scrolling script that should be assigned to a layer
/// </summary>
public class ScrollingScript : MonoBehaviour
{
    /// <summary>
    /// Scrolling speed
    /// </summary>
    public Vector2 speed = new Vector2(10, 10);

    /// <summary>
    /// Moving direction
    /// </summary>
    public Vector2 direction = new Vector2(-1, 0);

    /// <summary>
    /// Movement should be applied to camera
    /// </summary>
    public bool isLinkedToCamera = false;

    /// <summary>
    /// 1 - Background is infinite
    /// </summary>
    public bool isLooping = false;

    /// <summary>
    /// 2 - List of children with a renderer.
    /// </summary>
    private List<SpriteRenderer> backgroundPart;

    // 3 - Get all the children
    void Start()
    {
        // For infinite background only
        if (isLooping)
        {
            // Get all the children of the layer with a renderer
            backgroundPart = new List<SpriteRenderer>();

            for (int i = 0; i < transform.childCount; i++)
            {
                Transform child = transform.GetChild(i);
                SpriteRenderer r = child.GetComponent<SpriteRenderer>();

                // Add only the visible children
                if (r != null)
                {
                    backgroundPart.Add(r);
                }
            }

            // Sort by position.
            // Note: Get the children from left to right.
            // We would need to add a few conditions to handle
            // all the possible scrolling directions.
            backgroundPart = backgroundPart.OrderBy(
              t => t.transform.position.x
            ).ToList();
        }
    }

    void Update()
    {
        // Movement
        Vector3 movement = new Vector3(
          speed.x * direction.x,
          speed.y * direction.y,
          0);

        movement *= Time.deltaTime;
        transform.Translate(movement);

        // Move the camera
        if (isLinkedToCamera)
        {
            Camera.main.transform.Translate(movement);
        }

        // 4 - Loop
        if (isLooping)
        {
            // Get the first object.
            // The list is ordered from left (x position) to right.
            SpriteRenderer firstChild = backgroundPart.FirstOrDefault();

            if (firstChild != null)
            {
                // Check if the child is already (partly) before the camera.
                // We test the position first because the IsVisibleFrom
                // method is a bit heavier to execute.
                if (firstChild.transform.position.x < Camera.main.transform.position.x)
                {
                    // If the child is already on the left of the camera,
                    // we test if it's completely outside and needs to be
                    // recycled.
                    if (firstChild.IsVisibleFrom(Camera.main) == false)
                    {
                        // Get the last child position.
                        SpriteRenderer lastChild = backgroundPart.LastOrDefault();

                        Vector3 lastPosition = lastChild.transform.position;
                        Vector3 lastSize = (lastChild.bounds.max - lastChild.bounds.min);

                        // Set the position of the recyled one to be AFTER
                        // the last child.
                        // Note: Only work for horizontal scrolling currently.
                        firstChild.transform.position = new Vector3(lastPosition.x + lastSize.x, firstChild.transform.position.y, firstChild.transform.position.z);

                        // Set the recycled child to the last position
                        // of the backgroundPart list.
                        backgroundPart.Remove(firstChild);
                        backgroundPart.Add(firstChild);
                    }
                }
            }
        }
    }
}

(The numbers in the comments refer to the explanations below)

Explanations

  1. We need a public variable to turn on the “looping” mode in the “Inspector” view.
  2. We also have to use a private variable to store the layer children.
  3. In the Start() method, we set the backgroundPart list with the children that have a renderer. Thanks to a bit of LINQ, we order them by their X position and put the leftmost at the first position of the array.
  4. In the Update() method, if the isLooping flag is set to true, we retrieve the first child stored in the backgroundPart list. We test if it’s completely outside the camera field. When it’s the case, we change its position to be after the last (rightmost) child. Finally, we put it at the last position of backgroundPartlist.

Indeed, the backgroundPart is the exact representation of what is happening in the scene.


Remember to enable the “Is Looping” property of the “ScrollingScript” for the 0 - Background in the “Inspector” pane. Otherwise, it will (predictably enough) not work.

Infinite scrolling

(Click on the image to see the animation)

Yes! We finally have a functional “parallax scrolling” implementation.

Note: why don’t we use the OnBecameVisible() and OnBecameInvisible() methods? Because they are broken.

The basic idea of these methods is to execute a fragment of code when the object is rendered (or vice-versa). They work like the Start() or Stop() methods (if you need one, simply add the method in the MonoBehaviour and Unity will use it).

The problem is that these methods are also called when rendered by the “Scene” view of the Unity editor. This means that we will not get the same behavior in the Unity editor and in a build (whatever the platform is). This is dangerous and absurd. We highly recommend to avoid these methods.

Bonus: Enhancing existing scripts

Let’s update our previous scripts.

Enemy v2 with spawn

We said earlier that enemies should be disabled until they are visible by the camera.

They should also be removed once they are completely off the screen.

We need to update “EnemyScript”, so it will:

  1. Disable the movement, the collider and the auto-fire (when initialized).
  2. Check when the renderer is inside the camera sight.
  3. Activate itself.
  4. Destroy the game object when it’s outside the camera.

(The numbers refer to the comments in the code)

using UnityEngine;

/// <summary>
/// Enemy generic behavior
/// </summary>
public class EnemyScript : MonoBehaviour
{
    private bool hasSpawn;
    private MoveScript moveScript;
    private WeaponScript[] weapons;
    private Collider2D coliderComponent;
    private SpriteRenderer rendererComponent;

    void Awake()
    {
        // Retrieve the weapon only once
        weapons = GetComponentsInChildren<WeaponScript>();

        // Retrieve scripts to disable when not spawn
        moveScript = GetComponent<MoveScript>();

        coliderComponent = GetComponent<Collider2D>();

        rendererComponent = GetComponent<SpriteRenderer>();
    }

    // 1 - Disable everything
    void Start()
    {
        hasSpawn = false;

        // Disable everything
        // -- collider
        coliderComponent.enabled = false;
        // -- Moving
        moveScript.enabled = false;
        // -- Shooting
        foreach (WeaponScript weapon in weapons)
        {
            weapon.enabled = false;
        }
    }

    void Update()
    {
        // 2 - Check if the enemy has spawned.
        if (hasSpawn == false)
        {
            if (rendererComponent.IsVisibleFrom(Camera.main))
            {
                Spawn();
            }
        }
        else
        {
            // Auto-fire
            foreach (WeaponScript weapon in weapons)
            {
                if (weapon != null && weapon.enabled && weapon.CanAttack)
                {
                    weapon.Attack(true);
                }
            }

            // 4 - Out of the camera ? Destroy the game object.
            if (rendererComponent.IsVisibleFrom(Camera.main) == false)
            {
                Destroy(gameObject);
            }
        }
    }

    // 3 - Activate itself.
    private void Spawn()
    {
        hasSpawn = true;

        // Enable everything
        // -- Collider
        coliderComponent.enabled = true;
        // -- Moving
        moveScript.enabled = true;
        // -- Shooting
        foreach (WeaponScript weapon in weapons)
        {
            weapon.enabled = true;
        }
    }
}

Start the game. Yes, there’s a bug.

Disabling the “MoveScript” as a negative effect: The player never reaches the enemies as they’re all moving with the Foreground layer scrolling:

camera_moving_along_gif

Remember: we’ve added a “ScrollingScript” to this layer in order to move the camera along with the player.

But there is a simple solution: move the “ScrollingScript” from the Foregroundlayer to the player!

Why not after all? The only thing that is moving in this layer is him, and the script is not specific to a kind of object.

Push the “Play” button and observe: It works.

  1. Enemies are disabled until they spawn (i.e., until the camera reaches their positions).
  2. Then they disappear when they are outside the camera.

Enemy spawn

(Click on the image to see what happens)

Keeping the player in the camera bounds

You might have noticed that the player is not (yet) restricted to the camera area. “Play” the game, push the “Left Arrow” and watch him leaves the camera.

We have to fix that.

Open the “PlayerScript”, and add this at the end of the “Update()” method:

  void Update()
  {
    // ...

    // 6 - Make sure we are not outside the camera bounds
    var dist = (transform.position - Camera.main.transform.position).z;

    var leftBorder = Camera.main.ViewportToWorldPoint(
      new Vector3(0, 0, dist)
    ).x;

    var rightBorder = Camera.main.ViewportToWorldPoint(
      new Vector3(1, 0, dist)
    ).x;

    var topBorder = Camera.main.ViewportToWorldPoint(
      new Vector3(0, 0, dist)
    ).y;

    var bottomBorder = Camera.main.ViewportToWorldPoint(
      new Vector3(0, 1, dist)
    ).y;

    transform.position = new Vector3(
      Mathf.Clamp(transform.position.x, leftBorder, rightBorder),
      Mathf.Clamp(transform.position.y, topBorder, bottomBorder),
      transform.position.z
    );

    // End of the update method
  }

Nothing complicated, just verbose.

We get the camera edges and we make sure the player position (the center of the sprite) is inside the area borders.

Tweak the code to better understand what is happening.

Next step

We have a scrolling shooter!

We have just learned how to add a scrolling mechanism to our game, as well as a parallax effect for the background layers. However, the current code only works for right to left scrolling. But with your new knowledge, you should be able to enhance it and make it work for all scrolling directions (bonus: We did it as someone was stuck on the subject, click to see the code and an animation).

Still, the game really needs some tweaks to be playable. For example:

  • Reducing the sprite sizes.
  • Adjusting the speeds.
  • Adding more enemies.
  • Making it fun.

We will address these points in our upcoming chapter about gameplay tweaking (not released yet, unfortunately). For the moment, you can experiment. ;)

In the next chapter, we will focus our attention on how to make the game a bit more… flashy. With particles!




[출처] http://pixelnest.io/tutorials/2d-game-unity/parallax-scrolling/

반응형
Posted by blueasa
, |

최적화 Tips

GameObject를 비활성화

필요한 경우가 아니면 GameObject를 비활성화 해라. 개체수를 감소시켜라.

Scripts를 비활성화

필요한 경우가 아니면 Scripts를 비활성화 해라.

콜백(Update, OnEnable등) 함수의 사용을 자제하자

  • 새로운 스크립트를 생성시에 Update가 생성되는데 불필요한 경우 삭제하자. 그리고 Update에서 꼭 처리해야 하는 경우가 아니면 다른 방법으로 해결하라.
  • Coroutine을 적극 활용해라. Update함수 대신에 Coroutine을 사용할 수 있는 경우는 적극 활용하자. FadeIn/Out 같은 경우 단기간 필요한 기능이므로 코루틴으로 만들 경우 일시적으로 부하가 발생하므로 Update처럼 지속적으로 부하를 일으키지 않는다.

Find류 GetComponent류 함수의 호출 횟수를 줄이자

Find, FindByTag, GetComponent류의 사용을 자제해야 한다. 특히 Update, FixedUpdate같은 곳에서 지속적으로 사용하는 것은 퍼포먼스에 악영향이 있다. Awake, OnEnable에서 미리 찾아서 가지고 있는 것이 좋다.

유니티 엔진에서 제공하는 속성에 접근시 부하 발생

Transform.position, gameObject.transform, gameObject.camera 등 유니티 엔진에서 C# 프로퍼티로 랩핑하여 제공하는 변수 타입에 접근시에 부하와 임시 객체가 생성된다. 캐싱하여 사용하는 것이 좋다.

태그 비교시에 if ( go.tag == “myBot” ) 구문 대신에 go.compareTag(“myBot”)을 사용하는 것이 임시 객체 생성을 회피하는 방법이다.

LINQ 명령어 사용 자제

LINQ 명령어를 사용하는 경우 중간 버퍼가 할당된다. 이는 가비지로 남게 된다.

박싱 언박싱 회피

꼭 필요한 경우가 아니면 SendMessage를 사용하지 말라

SendMessage류의 사용을 자제해야 한다. 직접 함수를 호출하는 경우보다 SendMessage류를 사용하는 경우 100배 느리다. 그리고 호출시에 함수 이름을 넣게 되는데 코드 난독화가 필요한 경우 장애요소가 된다.

foreach() 사용을 자제하자

foreach()를 사용할 경우 enumerator를 생성하기 위해서 힙 메모리를 할당한다. for(;;)문을 활용하는 것이 좋다. 한번 돌 때마다 24bit의 가비지를 남긴다.

문자열

  1. string을 그대로 사용해서 조합 하는 것 보다 StringBuilder를 사용하는 것이 좀더 메모리 친화적이고 퍼포먼스 업에 도움이 된다.
  2. 혹은 String.Format을 사용하자.
  3. 상수 타입 문자열은 const, readonly를 지정하여 가비지 컬렉션에서 제외시키자.



[출처] http://www.antegg.com/wiki/doku.php?id=note:unity_3d:%EC%B5%9C%EC%A0%81%ED%99%94_tips

반응형
Posted by blueasa
, |