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

카테고리

분류 전체보기 (2853)
Unity3D (895)
Programming (479)
Server (33)
Unreal (4)
Gamebryo (56)
Tip & Tech (189)
협업 (64)
3DS Max (3)
Game (12)
Utility (142)
Etc (99)
Link (34)
Portfolio (19)
Subject (90)
iOS,OSX (53)
Android (16)
Linux (5)
잉여 프로젝트 (2)
게임이야기 (3)
Memories (20)
Interest (38)
Thinking (38)
한글 (30)
PaperCraft (5)
Animation (408)
Wallpaper (2)
재테크 (19)
Exercise (3)
나만의 맛집 (3)
냥이 (10)
육아 (16)
Total
Today
Yesterday


[링크] http://lnz5.tistory.com/273

반응형
Posted by blueasa
, |

DeployGate App을 찾아보니 AOS는 있는데

[DeplayGate AOS App] https://play.google.com/store/apps/details?id=com.deploygate&hl=ko


 iOS App이 없어서 찾아보니 따로 설치해야 돼서 정리해 놓음.



[DeplayGate iOS App 설치]


1) 아이폰-사파리에서 아래 주소로 접속한다.(사파리에서 해야됨)

   [링크] https://deploygate.com/connect/iphones



2) 아직 로그인하지 않은 경우 로그인 화면이 뜬다. 로그인 해주자.

   (회원가입이 안했으면 우선 회원가입부터..)



2) 사파리에서 열면 아래 스샷처럼 나온다.

   'Install DeployGate'를 누르자.



3) 진행하면 '프로파일 설치' 화면이 뜬다.

   얘도 설치해주자.




4) 설치하고나면 아래와 같은 화면이 보인다.

   아직 올린게 없어서 아무것도 없는 상태다. 업로드는 홈페이지(https://www.deploygate.com) 가서 올리면 됨.






[iOS App 설치 참조]

https://deploygate.uservoice.com/knowledgebase/articles/418294-ios%E7%89%88%E3%81%AEdeploygate%E3%82%92%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E3%81%99%E3%82%8B%E6%89%8B%E9%A0%86

반응형
Posted by blueasa
, |

안드로이드에서 테스트 앱 배포 관련 좋은 방법이 없나 찾던중 아래 기사를 보고 deploygate 사이트를 찾았다.


http://techcrunch.com/2012/09/18/deploygate-simple-over-the-air-real-time-app-beta-testing-comes-to-android/


사용법도 간단하고 좀더 테스트 해봐야 알겠지만 일단 진행 중인 프로젝트에 적용해 보기로 했다.


https://deploygate.com/ <- 여기 사이트를 들어가면 상단에 Sign up 클릭 하여 가입을 한다.


기존에 GitHub, Google 계정이 있으면 해당계정을 아래 화면에 맞게 입력하고 가입 하면 된다. 나는 테스트 계정을 만들어서 가입했다.




가입 -> 로그인을 하면 아래 화면에서 Upload App을 선택해서 업로드 하면 끝! 정말 간단하다!





위에 절차를 다 하면 Gooogle Play Store 에서 DeployGate 검색해서 단말에 설치 하고 DeployGate 실행하면 설정 화면에서


가입한 계정으로 로그인 하면 기본 세팅은 완료!


앱 상단 메뉴에서 INSTALLED는 현재 단말에 설치되어 있는 목록을 보여주는거고 AVALABLE는 현재 deploygate 사이트에 현재 업로드된


파일 목록을 보여준다.







앱을 수정을 하고 다시 배포를 할려고 하면 


deploygate  https://deploygate.com 사이트에 들어가서 수정된 앱을 다시 Upload App 선택하여 업로드 하면 앱을 설치된 사용자 한테


PUSH가 간다~ 끝!





좀더 테스트 해봐야 알겠지만 급한대로 쓸만한거 같다~ 




[출처] http://devlsh.tistory.com/entry/DeployGate-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EC%95%B1-%EB%B0%B0%ED%8F%AC

반응형
Posted by blueasa
, |

[링크] http://www.sonniss.com/gameaudiogdc2016/


[사용권] http://www.sonniss.com/gdc-bundle-license/




[출처] http://gpgstudy.com/news/item/1410

반응형
Posted by blueasa
, |

 

 

 

화창한 가을 약채한정식으로 건강 챙기기

 

**위치**

경기도 성남시 분당구 운중동 55-4(하오개로 366-6)

한국학 중앙 연구원 입구

(주변 대중교통이 없으니 자동차로 이동)

**메뉴**

약정식(기본)13,000원

채정식(평일 점심) 11,000원

약채정식 16,000원

시골여행정식 23,000원

보리굴비정식 25,000원

**영업시간**

오전 11시~오후 9시30분/연중무휴

 

---------------------------------------------------------------------

 

날씨가 무지막지하게 좋았던 지난 한글날

집에 있기 아쉬워 야외로 고고~!

성남 판교 근처 외곽에 한적한 식당들이 몰려있고, 예쁜 빌라도 많다고 해서 바로 향했다.

 

우리가 찾은 곳은 <시골여행>이라는 한정식 집이다.

성남에선 꽤 이름난 곳인 듯

점심시간 지난 뒤에도 사람들이 북적였다.

 

 

[주변 살펴보기]

늦은 점심 도착한 주차장

외곽에 있어서 주차 걱정은 전~혀 할 필요가 없다.

 

 

 

 

입구를 들어서면 큰 호박덩어리들이 반겨준다.

 

저렇게 큰 호박은 매년 이맘 때 즈음 시골 할머니댁에서 종종 볼 수 있었는데

할머니께선 늙으셔서 밭에서 딸 힘이 없다고

매번 말씀하셨던게 생각이 났다.

 

 

 

 

문 앞엔 각종 약초(?) 같은 것들을 투명용기에 담아 보관해두었다.

조명아래 은은히 비치는게 운치있다. 

 

 

 

 

계산대 앞엔 각종 스타들의 사인들로 벽을 가득 채웠다.

 

 

 

 

이곳의 가장 큰 특징인 남자밥과 여자밥

대부분 밥보단 메인음식을 강조하는데 이곳은 밥에 중점을 둔거 같아 마음에 들었다.

차이점은 밥에 들어가는 육수였다.

 

 

 

 

실제로 문가엔 남자밥과 여자밥에 들어가는 물을 달이는 기구가 따로 있었다.

 

 

 

 

한켠에 마련된 셀프 포장

한정식은 반찬 가짓수가 많아서 남기는게 대부분인데

이곳은 남은 음식을 포장해 갈 수 있게 일회용 용기를 준비해둔게 마음에 들었다.

 

 

 

[주문 메뉴&본격 식사]

 

채정식은 평일 런치에만 된다고 해서 패스~

우리가 주문한건

약채정식

 

약채정식과 약정식의 차이는 낙지볶음과 코다리가 추가되는게 다르다.

2인에 6,000원 차이기도 하고

한정식 대부분은 기본만 먹어도 배부르기에

주문했는데

약정식만으로도 충분할거라는게 결론이다.

 

 

 

[기본반찬]

 

녹두죽

어떤 블로그에선 가장 기억에 남는 음식으로 꼽았던 메뉴.

녹두의 부드러운 식감과 달지 않은 담백함이 좋았다.

 

 

 

 

녹두죽을 다 먹으면 한상 차려진다.

식사를 하기전 거한 에피타이저라고 생각하면 된다.

 

 

 

 

보들보들 수육

 

차갑지 않았고, 기름기가 쪽 빠져서 고소고소했음

 

 

 

 

와사비 소스를 곁들인 생 팽이버섯 샐러드

 

별거 아닌거 같은데 먹어보니 톡 쏘는 맛에 중독됐다.

 

 

 

검은깨소스를 첨가한 양상추 샐러드

 

직접 만든 듯 소스가 엄청나게 고소했음

 

 

 

잡채

 

미리 만들어 놓을 수 밖에 없는 메뉴인지라 면이 불어있고, 겉에 윤기가 없었음

솔직히 이건 내가 만든게 더 맛있다고 생각됐다.

 

 

 

호박크림소스 새우

 

바로 튀긴 듯 바삭한 식감이 살아있었다.

한정식과는 어울리지 않는 메뉴지만 맛은 좋았음

 

 

 

참나물 무침과 곁들어 나온 숯불갈비

 

조금 짰지만 육질이 부드럽고 숯향이 배어 있었음

 

 

 

[약채정식 포함 추가 메뉴]

 

코다리 조림

 

두께가 2센티 정도 될법할 정도로 두툼한게 특징

한번 튀겨낸 다음 달콤짭짤하게 조려내었는데

전통방식으로 요리한 건 아닌 듯하다.

어른들보단 아이들에게 인기있을 메뉴

 

 

 

낙지볶음

 

살이 통통오른 낙지들이 많이 들어있었고, 알맞게 조리해서 부드러운 식감을 유지하고 있었다.

 기본 찬들이 양이 많아

나중에 보니 거의 건들이지 않았더라는 ㅠ.ㅠ

식구가 많지 않다면

그냥 기본메뉴 주문하길 다시 한번 추천한다.

 

 

 

낙지볶음, 코다리 조림, 숯불갈비는 식사를 위해 남겨두고 나머지는 올킬~!

 

 

[메인 식사]

 

참으로 궁금했던 여자밥

두둥~!

 

헷갈리지 않게 돌솥밥 뚜껑에 여자밥이라고 써 있음

뚜껑을 여니

대추, 은행, 호두, 호박씨 등이 들어있고, 밥이 갈색빛이 돌 만큼 어두웠다.

그렇다고 약초향이 강하진 않고

은은하면서 구수한 맛이 입안을 감돌았다.

확실히 일반 물로 밥을 지은 것과는 다른 풍미를 느낄 수 있었음

 

 

 

 

이건 남자밥

여자밥과 달리 색이 진하지 않다.

남자밥이라 맛은 따로 안봄 

 

 

 

 

밥을 빈 공기에 덜어낸 후에 돌솥그릇엔 보리차물을 넣으면 구수한 여자밥 숭늉이 만들어 진다.

 

 

 

[이제부턴 식사 밑찬들]

 

들깨버섯볶음

 

 

 

 

맛살마늘쫑볶음

 

 

 

 

 

말린 표고로 만든 표고버섯무침

 

 

 

 

이게 뭐였더라

말린시래기무침이었나

 

 

 

 

배추 겉절이

 

 

 

 

제일 별로였던 간장게장

게장의 핵심인 게가 신선하지 않아서 살이 으스러지고 비린맛도 강했다.

 

 

 

 

아 이게 시래기 무침이네

그럼 위에 있던건 뭐지ㅡ.ㅡa

 

 

 

 

달큰새콤했던 호박장아찌

 

 

 

 

식사 반찬 중 가장 맛있었던 조기구이

생선이 작아서

살이 있을랑가 싶었는데 속이 꽉차 있고 비린맛도 없어서 야무지게 다 멋었다.

게장보다 훨 맛있었음

 

 

 

 

도라지무침

 

 

 

 

 

멸치볶음

 

 

 

 

내용물이 푸짐했던 된장찌개

 

 

 

 

한상 거하게 먹은 뒤 배 두둘기고 있는데

호박식혜라며 후식을 주심

 

호박식혜는 처음 먹어봤는데, 호박맛이 많이 나서 신기했다.

식혜와 달리 걸리는 건더기가 없어서 바로 원샷~!

입구에 있었던 호박이

바로 이 호박식혜를 만든 호박인가?ㅎㅎ

 

 

 

 

식사를 하고 나오면 문 오른편 통로에 커피 마시면서 앉아있는 공간이 있다.

 

 

 

 

각 테이블엔 꽃잎을 띄운 항아리가 앙증맞게 자리잡고 있었다.

주인의 정성이 느껴지는 공간

 

***

이곳은 확실히 밥이 맛있는 집이었다.

가격도 비싼편이 아니라서 차가 있으면 한번씩 방문하면 좋을 듯

가족끼리 가면 건강도 챙기면서

이야기거리가 많아 재밌게 식사를 할 수 있는 곳일 듯 하다.

 

 

 

 


[출처] http://daldalspace.tistory.com/118

반응형

'나만의 맛집' 카테고리의 다른 글

[펌] 사천복요리 :쉐프 문경희는 위로 그 자체♥  (0) 2020.02.13
[신현리] 고향생각  (0) 2015.09.08
Posted by blueasa
, |

[링크] http://cafe.naver.com/jgplan

반응형
Posted by blueasa
, |
 

在Unity3D的Legacy动画系统中应用Root Motion

标签: unity3d动画Root Motion
 5591人阅读 评论(2) 收藏 举报
 分类:
 

目录(?)[+]

最近仔细比较了Unity3D目前版本中的两套动画系统:Legacy和Mecanim。Mecanim系统功能较之Legacy要强大很多,但是使用AnimatorController着实不方便(尽管使用AnimatorOverrideController可以避免重复编辑状态机),是因为游戏逻辑层面往往要用一个状态机或者类似的机制来控制角色的状态,而角色层面的状态逻辑和动画层面是无法一一对应的,两套复杂的状态机要配合起来。。。想想就觉得蛋疼啊!难怪很多朋友现在还在使用Legacy动画系统。Legacy动画系统其实功能也很全面了,包括Layer、过渡混合、上下身混合之类的功能完全能够胜任,而且控制起来就直接的多了。唯独Root Motion这个我很需要特性没有支持,本文就探讨一下如何在Legacy动画系统之上附加Root Motion功能,其实很简单大笑

何谓Root Motion

在不使用Root Motion的情况下,类似走、跑这样的位移控制是这样的:
  1. 请美术在导出动画时把位移去掉;
  2. 在程序代码里控制角色移动的速度,在播放动画的同时,计算其位移。
这种做法其实挺不科学的,程序控制的角色,只能当做一个质点来处理,并且大多数时候都是匀速运动,而动画中的角色的移动往往很难跟这个匹配。所以需要比较良好的计算和比较好的美术技巧才能避免角色“滑步”的现象。在“跑”这种快速移动这,滑步还比较好处理,如果是慢速移动。。。。再厉害的美术也爱莫能助了。这种情况下,最好还是使用Root Motion:
  1. 美术在导出动画的时候是附带位移的;
  2. 程序把动画的每一帧的位移是从动画中读取出来,再应用到角色上的,这样就能达到动画和位移的完美匹配了。

在Legacy中添加Root Motion功能

了解了Root Motion的概念之后,在Unity3D引擎中我们很简单就可以实现此功能了。Unity3D有一个统一的对象层次结构设计,这点非常赞,我们可以很简单找到角色的根骨骼,然后把其中的Transform变换读取出来,请见以下示例代码:
[csharp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. //-- 计算当前帧的Root Motion  
  2.             Vector3 rootPos = m_rootBone.localPosition;  
  3.             m_rootMotion = rootPos - m_lastRootPos;  
  4.             m_lastRootPos = rootPos;  
  5.             rootPos.x = 0;  
  6.             rootPos.z = 0;  
  7.             m_rootMotion.y = 0;  
  8.             m_rootBone.localPosition = rootPos;  
请注意,我们在后续的代码中要把m_rootMotion附加的角色对象上,所以m_rootBone的postion被reset了。
在读取了此帧的Root Motion,在可以把它应用到当前对象之上了:
[csharp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. //-- Apply Root Motion  
  2.     Vector3 nextPos = this.transform.position + m_rootMotion;  
  3.         this.transform.position = nextPos;  
另外,一个细节需要处理一下,在动画循环的那一帧,需要特殊处理一下。好的,看一下完整的源代码吧:
[csharp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. using UnityEngine;  
  2. using System.Collections;  
  3.   
  4. public class ApplyRootMotion : MonoBehaviour   
  5. {  
  6.     public Transform m_flagObject;  // 用来测试位置的一个对象  
  7.   
  8.     //-- Root Motion 控制变量  
  9.     Transform m_rootBone;  
  10.     Vector3 m_lastRootPos;  
  11.     Vector3 m_rootMotion;  
  12.     int m_lastAnimTime;  
  13.   
  14.     void Start ()   
  15.     {  
  16.         //-- 从SkinnedMeshRenderer中读取Root Bone  
  17.         SkinnedMeshRenderer skinMesh = this.gameObject.GetComponentInChildren<SkinnedMeshRenderer>();  
  18.         m_rootBone = skinMesh.rootBone;  
  19.   
  20.         //-- 变量初始化  
  21.         m_rootMotion = Vector3.zero;  
  22.         m_lastRootPos = m_rootBone.localPosition;  
  23.         m_lastAnimTime = 0;  
  24.     }  
  25.       
  26.     void Update ()   
  27.     {  
  28.         //-- Apply Root Motion  
  29.         Vector3 nextPos = this.transform.position + m_rootMotion;  
  30.         this.transform.position = nextPos;  
  31.   
  32.         //-- 测试代码:更新测试物体的位置  
  33.         Vector3 flagPos = m_flagObject.position;  
  34.         flagPos.x = nextPos.x;  
  35.         flagPos.z = nextPos.z;  
  36.         m_flagObject.position = flagPos;  
  37.   
  38.         //-- 测试代码:更新摄像机  
  39.         Camera.main.transform.LookAt(this.transform);  
  40.     }  
  41.   
  42.     void LateUpdate()  
  43.     {  
  44.         AnimationState animState = this.animation["walking"];  
  45.   
  46.         if ((int)animState.normalizedTime > m_lastAnimTime)  
  47.         {  
  48.             //-- 动画循环处理  
  49.             m_lastRootPos = m_rootBone.localPosition;  
  50.             m_rootMotion = Vector3.zero;  
  51.         }  
  52.         else  
  53.         {  
  54.             //-- 计算当前帧的Root Motion  
  55.             Vector3 rootPos = m_rootBone.localPosition;  
  56.             m_rootMotion = rootPos - m_lastRootPos;  
  57.             m_lastRootPos = rootPos;  
  58.             rootPos.x = 0;  
  59.             rootPos.z = 0;  
  60.             m_rootMotion.y = 0;  
  61.             m_rootBone.localPosition = rootPos;  
  62.         }  
  63.         m_lastAnimTime = (int)animState.normalizedTime;  
  64.     }  
  65. }  

最后是截图。。。好吧,静态图片看不出效果,可以下载完整Demo(请使用Unity 4.6版本打开),角色移动非常平滑,毫无滑步。

请移步百度网盘:http://pan.baidu.com/s/1o6kJsIe 密码:osoc






반응형
Posted by blueasa
, |

크런치

Etc / 2016. 3. 27. 15:45

크런치(crunch)는 crunch time이라는 숙어 ‘극도의 긴장이 필요한 때’에서 온 것으로 보이는데, 게임업계에서 개발 막바지 일정 기간 동안 야근과 철야를 밥먹듯이 해 개발을 완료해내는 그런 상태를 말한다. 그래서 “오늘부터 1개월간 크런치 모드입니다”라는 말은 “앞으로 한 달 동안은 죽었다고 생각하고 매일 야근 합시다”라는 뜻이다.

이 크런치에 대해서는 업계 내에서 오랫동안 다양한 의견이 있어왔다. 워렌 스펙터는

[…] “크런치는 창조적 매체에서 미지의 요인을 가지고 일하기 때문에 발생하는 결과입니다. 게임 개발은 항상 미지로 가득하기 때문에 퀄리티를 높이려 분투하는 개발사에서는 항상 크런치를 하게 돼요. […] 30년간 게임을 만들었는데 전 아직도 크런치 없이 인정할 만한 수준의 게임을 만드는 마법사는 본적이 없어요.”

라고 이야기를 했다고 하고, 스타독(Stardock)의 데릭 팩스턴은

“게임 업계의 기업들과 사람들은 크런치에 써버린 시간을 훈장처럼 자랑하는짓을 그만둬야 해요. 크런치는 망가진 업무 절차와 관리의 결과입니다. 크런치로 직원들이 희생됩니다. 전 왜 크런치가 다른 업계에서는 이슈가 되지 않는데 왜 게임회사들에만 이슈가 되는지 묻고 싶군요.”

라고 이야기를 했다고 하며, 개인적으로 만나본 여러 개발자들도 크런치의 필요성이나 효과에 대한 다양한 증언이 있었다.

그런데 얼마 전부터 가마수트라에 올라오기 시작한 게임 개발 성과 측정 프로젝트(한국어 번역)에서 분석한바에 따르면

우리의 결과물은 명백하게 크런치가 비범한 결과물을 낳지 않는다는 사실을 보여줍니다. 실제로는 크런치는 항상 게임을 덜 성공적으로 만듭니다. 프로젝트가 크런치를 이용해서 구덩이에서 빠져나오려고 발버둥 치면, 발밑의 구덩이가 더 깊어지는 결과를 낳을 뿐입니다.

이라고 확정적인 결론을 내렸다. 이 연구에서도 언급하지만,

한편, 경영학에서는 방대한 규모의 입증된 연구결과를 통해 연속된 초과근무는 건강생산성, 인간관계, 사기, 조직 몰입도,의사 결정 능력을 망치고 심지어 알콜 남용의 위험성을 높인다는 사실이 확인되어 있습니다.

대량의 입증된 경영학 연구에서는 근로자들의 총 생산성이 겨우 몇 주만 초과근무해도 음수로 변한다는 사실을 보여주고 있습니다. 근무시간을 일간 8시간에서 9시간으로 늘리면 총 생산성은 무려 16-20%가 감소합니다. 겨우 몇 주만 50시간을 근무해도 해당 기간 동안의 총 누적 생산량이 주당 40시간 근무했을 때보다 오히려 더 줄어듭니다. – 추가적인 주당 10시간의 근무는 실제로는 근로자들의 총 생산량을 감소시킵니다. 동시에 근로자들의 스트레스를 증가시키며, 인간관계를 목 조르고, 제품의 결함률을 높입니다.

다른 모든 산업에서는 초과 노동이 생산성에 전혀 기여하지 못하고, 오히려 부정적인 효과만 작용을 하는데, SW 업계에서는 이 크런치가 효과가 있다는 믿음을 여전히 가지고 있다는 것이다.

사실 이 문제는 게임 개발의 품질이나 생산성, 열정에 대한 문제 이전에 기본적인 개발자 삶의 질에 대한 이야기에서 출발해야 한다. 2013년 8월 게임 산업 종사자 실태조사에서 출퇴근 시간과 평균 야근 시간을 조사했던 것은 이런 맥락에서였다.

특히나 게임산업이 젊은 개발자들의 열정을 빨아먹는 산업이 아니라 다른 모든 산업들처럼 안정적이고 평범한 산업 환경이 되기 위해서는, 종사자들의 삶이 안정되어야 하고, 그러기 위해서는 일과 삶의 균형을 (개발자 스스로 맞추기에 앞서) 기업이 만들어줘야 하기 때문이다.

이미 그 열악하다던, 흥행 산업의 대명사인 영화 업계도 오랜 부당 노동(초과 근로와 최저 임금에도 못 미치던) 환경을 개선하는 노력으로 노조를 구성했으며, <국제시장>은 표준근로계약서를 도입해 그간 스탭들을 착취하던 관행을 깼다. 심지어는 만화계에서도 처우 개선에 대한 이야기가 시작됐다.

개발자가 게임업체를 다니면서 결혼하고, 자식을 낳고, 키우면서 가족과 행복하게 저녁을 먹는 그런 삶을 누리는 것이, 결국에는 개발자를 재충전하게 해주고 더 좋은 게임을 만들 수 있는 환경이 될 것이다. 그리고 그렇게 ‘안정적인 직업’이 될 때 젊은 지망생과 개발자들이 개발자로 업계에 입문할 꿈을 꾸고 살아가는 그런 환경이 될 것이라고 생각한다.

게임의 흥행 성공률이 낮다는 이유로 개발자들을 싸게 초과 근로를 인사 고과에 반영하면서 갈아 넣어서는, 단기적으로는 프로젝트를 끝낼 수 있겠지만 장기적으로는 경험 있고 실력 좋은 개발자를 업계에서 밖으로 밀어내는 결과를 낳는다.

당장 크런치를 없앨 수는 없을 수 있다. 하지만 적어도 우리가 이런 잘못된 개발 환경으로 인해서 무엇을 놓치고 있는지, 어떻게 개선하면 어떤 것을 얻을 수 있는지는 고민해볼 때가 됐다고 생각한다.

한국에서 게임 개발이 ‘산업’이 된지도 이제 20년이 되어 간다. 생각을 해볼 때다.


출처 : http://nairrti.works/2015/02/27/crunch/

반응형
Posted by blueasa
, |



소스 주소 : https://github.com/totuworld/unity/



출처 : https://www.youtube.com/watch?v=G9AdSd88BFg


반응형
Posted by blueasa
, |


팁들에 대해서

이 팁들은 모든 프로젝트에 적용 가능하지는 않습니다.
  • 이것들은 3~20명의 인원으로 구성된 작은팀의 프로젝트 경험을 기반으로 합니다.
  • 구조, 재사용성, 명확성, 기타 등등 의 비용은 팀 크기와 프로젝트의 크기에 따라 비용 지불 여부가 결정 됩니다.
  • 많은 팁들은 취향의 문제 입니다.
    (하지만 여기에 나온 여러 팁들은 우열을 가리기 어려울 정도로 좋은 기술들 입니다.)
  • 몇개의 팁은 공식 Unity 개발에 위배 되는 것들입니다. 예를 들면, 몇개의 특수한 인스턴스를 위한 프리팹의 사용은 매우 Unity 에서 싫어하는 방식으로, 비용 또한 상당히 높습니다(이렇게 라도 prefab 들을 사용하는게 나을 때도 있습니다).  그러나 나는 이런 팁들이 미친 짓 같아 보여도 가끔 좋은 결과를 가지고 오는 경우도 보곤 합니다.

Process


1. 자산을 여러 버전으로 나누지 말라. 항상 모든 자산은 오직 한 버전으로 있어야 한다. 만약 절대경로로 프리팹, 씬, 또는 메쉬 등을 분기 하여 만드는 경우 옳은 버전이다. "잘못된" 분기는 임시적 이름으로, 예를들면 밑줄 두개를 접두로 붙이는 경우다. __MainScene_Backup. 프리팹을 분기 하는 경우 분명하게 이름을 명시하는게 안전하게 생성하는 방법이다. (Prefabs 섹션을 참고)
원문
2. 버전 컨트롤을 사용할 경우 각 팀 구성원들은 테스트를 위해 체크 아웃한 프로젝트의 두번째 복사본이 있어야 한다. 변경 후, 두번째 복사본은 clean copy 및 update 를 하고 test 를 해야 한다. 모든 사람들은 clean copy 상태에서 변경을 해야 합니다.
원문
3. 레벨 수정을 위한 외부 레벨 툴을 사용하는걸 고려하라. Unity 는 완벽한 레벨 에디터가 아니다. 예로, 우리는 3D tile 기반의 게임을 위해 레벨 빌드를 TuDee 를 사용하여 만들었으며, 우리는 tile 친화적인 툴을 통해 도움을 받았다 (그리드 가 붙는, 그리고 90 도 로 여러번 회전, 2d 뷰, 빠른 선택). XML 파일을 통한 prefab 초기화는 간단합니다. Guerrilla Tool Development 에 자세한 아이디어가 있습니다.
원문
4. 레벨 저장을 scene 대신 XML 으로 하는걸 고려하라. 이건 훌륭한 기술이다:
  • 이렇게 만들면 각각의 scene 을 다시 만드는게 불필요해 집니다.
  • 이렇게 만들면 보다 빠르게 로딩 합니다(대부분의 객체가 scene 에서 공유되는 경우)
  • 이렇게 만들면 scene 을 합치는게 쉬워집니다.(Unity 의 새로운 text 기반 scene 은 너무 많은 데이터를 포함하고 있어 합치는게 비현실적으로 보입니다)
  • 이렇게 만들면 데이터 레벨 수준에서 관리를 할 수 있습니다.
당신은 여전히 Unity 를 레벨 에디터 로 사용 할 수 있습니다(필요성을 못 느낄경우). 네가 데이터 , 에디터와 레벨 로드 를 실시간으로 처리, 에디터로 부터 레벨 저장을 해야 할 경우 몇몇의 코드는 직렬화 와 역직렬화를 필요로 하게 된다. 너는 아마 또한 객체간 참조를 유지하기 위해 Unity 의 id system 을 모방 해야 할 것입니다.
원문
5. 일반적인 커스텀 inspector 코드를 작성하는 걸 고려하라. 커스텀 inspector 를 작성하는건 매우 간단하나, Unity 의 시스템은 많은 단점을 가지고 있다.
  • 상속을 활용한 방식을 지원하지 않습니다.
  • 당신이 정의 하는 inspector 컴포넌트는 필드 타입 수준에서 구성이 안되며, 오로지 클래스 타입 레벨만 지원합니다. 예 로, 만약 모든 게임 오브젝트가 SomeCoolType 필드타입 이고, inspector 에서 다르게 렌더링 하려 할 경우,당신은 모든 클래스들에 대해서 inspector 를 작성해야 합니다.

당신은 기본적으로 inspector 시스템을 다시 구현함으로써 이러한 문제를 해결 할 수 있습니다. reflection의 몇가지 기술을 사용하여 해결할 수 있으며, 자세한 방법은 문서의 끝에 나와 있습니다.

원문

Scene Organisation

6. scene 폴더를 빈 게임 오브젝트 의 이름으로 사용하라. 당신의 scene 에서 찾고자 하는 오브젝트를 쉽게 찾을수 있도록 구성 할 수 있습니다.
원문
7. 관리하는 prefab 과 폴더 (빈 게임 오브젝트) 를 0 0 0 에 넣습니다. 만약 위치를 명시하지 않은 채 오브젝트의 위치를 사용 하면, 아마 원점에 있을겁니다. 그 방법은, 실행 중 로컬과 월드 공간간의 문제를 줄여줄 수 있으며, 코드는 일반적으로 간단합니다.
원본
8. GUI 컴포넌트의 offset 사용을 최소화 하라. Offset 은 항상 자신의 부모 Component 에서 레이아웃 Component 를 사용해야 합니다. 그들은 조부모(부모의부모)의 위치에 의존해서는 안됩니다. Offset 을 올바르게 표시하기 위해서는 서로를 취소 할 수 없습니다 . 이런 일을 방지하기 위해 기본적으로:
부모 컨테이너가 임의의 위치로  (100, -50) 이고, 자식의 위치가  (10, 10) 이기를 희망 할 경우, 위치 정의를 (90, 60) [relative to parent] 으로 하면 된다.
이 에러는 컨테이너가 invisible 상태일때 공통으로 발생하며, 또는 시각적 표현이 없는 경우에 해당합니다.
원문
9. 당신의 월드 바닥을 y = 0 으로 하라. 이와 같이 하면 오브젝트를 바닥에 생성하는게 쉬워지며, 게임 로직, AI, 물리 등을 2D 공간(이게 적절할 경우)으로 월드에 적용할 수 있습니다.
원문
10. 모든 scene 을 게임 실행 가능하게 만듭니다. 이건 대폭적으로 테스트 시간을 줄일 수 있습니다. 모든 scene 을 실행 가능하게 만들려면 두가지 고려사항이 있습니다:
첫번째로, 이전에 로드해야 하는 scene 의 데이터가 필요로 하는데 사용 할 수 없는 경우, 임시 데이터 를 제공 할 수 있도록 해야 합니다.
두번째로, 생성한 오브젝트는 다음의 scene 과 scene 을 불러오는 사이에 계속 되어야 한다.
myObject = FindMyObjectInScene();
 
if (myObjet == null)
{
   myObject = SpawnMyObject();
}
원문


Art

11. 케릭터와 서있는 오브젝트는 pivot 기반이여 하며, center 중심이어선 안된다 . 이런식 으로 만들면 보다 쉽게 케릭터와 오브젝트를 바닥에 올려놓을 수 있습니다 . 이것은 또한 게임 로직, AI 와 물리 등이 필요로 하는 경우에 3D 환경에서 2D 같이 제작 할 경우 쉽게 만들 수 있습니다.
원문
12. 제작하는 모든 메시의 바라보는 방향을 일치 합니다(+Z 또는 -Z). 케릭터와 다른 오브젝트들의 메시는 바라보는 방향이 다르게 적용이 됩니다. 바라보는 방향을 통일 시키면 많은 알고리즘들을 쉽게 적용 할 수 있습니다.
원문
13. 시작시 스케일의 규모를 정하도록 한다. 만든 작업물을 임포트 할때 스케일 크기를 1로 하게 되면 그것의 트랜스폼 스케일은 1 1 1 이 된다. 스케일의 비교를 쉽게 하기 위해서 참조 오브젝트(Unity 상자)를 사용 합니다. 게임에 적합한 Unity 단위 비율을 선택하고, 정한것에 충실하도록 합니다.
원문
14. GUI 컴포넌트와 파티클을 수동으로 생성 해야 할 경우 2개의 삼각형으로 된 평면을 사용 합니다. 평면을 Z+ 방향으로 하는게 빌보드와 GUI 를 만드는데 쉽게 합니다.
원문
15. 테스트 작업물 만들기 및 사용
  • 스카이 박스들을 위한 사각형 표기.
  • 그리드(격자).
  • shader 테스트를 위한 다양한 평면 색상: 흰색, 검은색, 50% 회색, 적색, 녹색, 청색, 마젠타, 노랑, 청록색.
  • shader 테스트를 위한 그라데이션: 검은색 에서 흰색, 적색에서 녹색, 적색에서 청색, 녹색에서 청색.
  • 검은색과 흰색으로 된 체커보드(체커판).
  • 부드럽고 거친 노멀맵.
  • 빠른 테스트 scene 설정을 위한 라이팅 을 갖춤(프리팹).
원문

Prefabs

16. 모든 것에 프리팹을 사용하도록 한다.   당신의 scene 에 게임 오브젝트만 있는 경우에는 프리팹이 폴더에 있을 필요는 없습니다. 심지어 한번만 사용 하더라도 고유 객체를 프리팹으로 해야합니다. scene 을 바꾸지 않는 경우 만든것을 수정하는걸 편하게 작업할 수 있습니다. (또 다른 장점은 EZGUI 를 사용하는 경우 신뢰 할만한 스프라이트 아틀라스 를 구축할 수 있습니다.).
원문
17. 특수화된 프리팹은 사용해도 좋지만; 특수화 된 인스턴스는 사용하지 말라. 만약 다신이 두개의 적 타입을 가지고 있는 경우, 그리고 그것들이 프로퍼티만 다른 경우, 속성에 대해 별도의 프리팹을 만들고, 그것을 링크 합니다. 이렇게 하는게 가능한 경우는
  • 한 곳에서 각각의 타입을 변경 할 경우
  • scene 을 변경하지 않고 변경 할 경우
당신이 너무 많은 적 타입을 가지고 있을지라도, 특수화는 여전히 에디터의 인스턴스를 만들어서는 안됩니다. 한가지 대안책은 절차를 따르거나 또는 모든 적들을 기준의 파일 / 프리팹을 사용하는 겁니다. 적들을 구분하는걸 사용하여 하나의 적을 쓰러트리거나, 적의 위치 또는 플레이어 진행 기반의 알고리즘을 사용합니다.
원문
18. 프리팹과 프리팹의 링크는 되나; 인스턴스와 인스턴스 간의 링크는 안된다. scene 에 프리팹을 끌어놓을 때 프리팹의 연결이 유지 됩니다; 인스턴스에는 링크가 없습니다. 프리팹을 연결할 때마다 scene 의 설정을 줄여 주는게 가능해지며, scene 을 변경할 경우도 줄여줄 수 있습니다.
원문
19. 가능하다면, 인스턴스간의 연결 설정은 자동으로 하라. 만약 당신이 인스턴스간의 링크를 필요로 한다면, 프로그래밍적으로 링크를 설정 합니다. 예를 들어, Player 프리팹 자체를GameManager 을 시작함과 동시에 등록 할 수 있으며, 또는 GameManager 가 시작 할때 Player 프리팹 인스턴스를 찾을 수 있습니다.
만약 당신이 프리팹에 다른 스크립트를 추가 하길 원한다면 루트에 다른 메시를 넣어선 안됩니다. 당신이 메시로 부터 프리팹을 만들려고 한다면, 첫번째 메시의 부모는 비어있는 게임 오브젝트 이어야 하며, 그것은 루트 이어야 합니다. 스크립트를 루트에 넣게 되면, 메시 노드가 아니게 됩니다. 그 방법은 당신이 inspector 에서 설정한 모든 값을 잃어버릴일 없이 다른 메쉬와 메시를 대체하기 훨씬 쉬울 것 입니다.
하위 프리팹의 대안으로 연결된 프리팹을 사용합니다. Unity 는 하위 프리팹을 허용하지 않으며, 하위 프리팹 간의 관계가 명확하지 않기 때문에 팀에서 작업 할 때 기존의 타사 솔루션은 위험 할 수 있습니다.
원문
20. 분기 프리팹에 대해서 안전한 절차를 사용하도록 하라. 설명은 Player 프리팹 예제를 통해서 합니다.
프리팹을 바꾸는거에 대해 많은 문제에 대한 절차는:
  1. 복제된 Player 프리팹이 있습니다.
  2. 복제된 거에 대한 이름을 __Player_Backup 로 변경합니다.
  3. Player prefab 으로 생성한걸 바꿉니다.
  4. 작업이 진행 될 경우 __Player_Backup 를 지웁니다
복제후 Player_New 으로 이름을 바꺼선 안되며, 생성된걸 바꺼선 안됩니다!
일부는 상황이 더 복잡합니다. 예를 들어, 특정 변화는 두 사람을 포함 할 수 있으며, 두사람이 위의 과정을 따라하면서 작업을 완료하면 작업한 scene 을 망가질 수 있습니다. If it is quick enough, still follow the process above 그래도 여전히 필요로 하는경우, 위의 절차를 따르십시오. 바꾸는데 시간이 오래 걸릴경우, 다음의 절차를 따라야 할 수 있습니다:
  1. 사람 1:
    1. Player 프리팹을 복제 합니다.
    2. 이름을 __Player_WithNewFeature 나 __Player_ForPerson2 로 바꿉니다.
    3. 복제된 걸로 변경을 하고, 커밋을 하여 [사람 2] 에게 제공합니다.
  2. 사람 2:
    1. 새로운 프리팹으로 변경 생성 합니다.
    2. Player 프리팹을 복제하고, __Player_Backup 이라고 부릅니다. 
    3. __Player_WithNewFeature 인스턴스를 scene 으로 드래그 합니다.
    4. 오리지날 Player 프리팹을 인스턴스에 드래그 합니다.
    5. 작업이 진행 될 경우 __Player_Backup 와 __Player_WithNewFeature 를 지웁니다.
원문

Extensions and MonoBehaviourBase

21. 기본 mono behaviour 을 확장하고, 당신의 컴포넌트를 그것으로부터 모두 파생하라. 
이렇게 하면 몇가지 일반적인 기능을 구현 할 수 있으며, 이러한 유형의 호출에 안전하고 복잡한 호출이 가능해 집니다.(예로 무작위 호출)
원문
22. Invoke 를 위해 안전한 메서드를 정의 하며, StartCoroutine 과 Instantiate 가 있다.
Task delegate 를 정의하고, 문자열 이름에 의존하지 않는 방법을 정의 하는데 사용합니다.
예를 들면:
public void Invoke(Task task, float time)
{
   Invoke(task.Method.Name, time);
}
원문
23. 공유 인터페이스를 사용하여 컴포넌트를 함께 작동하도록 확장을 사용 합니다. 가끔 특정 인터페이스의 구현된 컴포넌트를 얻어오는데 편리하거나, 앞의 언급한 컴포넌트를 가지고 오브젝트를 찾는데 편합니다.
아래에 구현된 내용은 typeof 대신에 generic 버전의 함수들로 사용되고 있습니다. generic 버전은 인터페이스와 함께 작동하지 않지만, typeof 는 가능합니다. 아래의 방법은 generic 메소드를 이용하여 래핑합니다.
//Defined in the common base class for all mono behaviours
public I GetInterfaceComponent<I>() where I : class
{
   return GetComponent(typeof(I)) as I;
}
 
public static List<I> FindObjectsOfInterface<I>() where I : class
{
   MonoBehaviour[] monoBehaviours = FindObjectsOfType<MonoBehaviour>();
   List<I> list = new List<I>();
 
   foreach(MonoBehaviour behaviour in monoBehaviours)
   {
      I component = behaviour.GetComponent(typeof(I)) as I;
 
      if(component != null)
      {
         list.Add(component);
      }
   }
 
   return list;
}
원문
24. 구문보다 편리하게 사용하기 위해 확장을 사용합니다. 예를 들면:
public static class CSTransform 
{
   public static void SetX(this Transform transform, float x)
   {
      Vector3 newPosition = 
         new Vector3(x, transform.position.y, transform.position.z);
 
      transform.position = newPosition;
   }
   ...
}
원문
25. 방어적인 GetComponent 를 대안으로 사용 하라. Sometimes forcing component dependencies (through RequiredComponent) can be a pain. 때때로 강제적인 컴포넌트 의존(RequiredComponent 유발)은 고통이 될 수 있습니다. 예를들어, it makes it difficult to change components in the inspector 그것은 어려운 inspector 의 구성요소를 변경 할 수 있습니다(base type 이 같은 경우). As an alternative, the following extension of GameObject can be used when a component is required to print out an error message when it is not found. 대안으로, 확장된 GameObject 를 찾을때 컴포넌트가 발견되지 않은 경우 오류 메세지를 출력 하면 된다.
public static T GetSafeComponent<T>(this GameObject obj) where T : MonoBehaviour
{
   T component = obj.GetComponent<T>();
 
   if(component == null)
   {
      Debug.LogError("Expected to find component of type " 
         + typeof(T) + " but found none", obj);
   }
 
   return component;
}
원문

Idioms

26. 비슷한 일에 다른 해결책을 사용하지 마십시오. 대부분의 경우 일을 할때마다 하나 이상의 해결책이 있습니다. 이러한 경우, 프로젝트에 한가지 방법을 선택하고 진행 할 수 있습니다. 여기에 이유가 있습니다:
  • 몇가지 해결책이 함께 잘 작동하지 않습니다. 한가지 해결챌을 사용하면 다른 해결책에 적합하지 않으며 한 방향으로 디자인을 강요 합니다.
  • 몇가지 해결책을 사용하는게 어디로 가야 하는지 팀 멤버를 이해 시킵니다. 이것은 구조와 코드를 이해하기 쉽게 합니다. 실수를 하기 더 쉽도록 유도가 됩니다.
해결책 그룹들의 예시:
  • Coroutines vs. state machines.
  • 하위 프리팹들 vs. 연결된(링크) 프리팹들 vs. 전지 전능한 프리팹들.
  • 데이터 분리 전략.
  • 2D 게임에서 상태를 위해 스프라이트를 사용 하는 방법.
  • 프리팹 구조화.
  • Spawn 전략.
  • 개체를 찾을 수 있는 방법: 타입 vs. 이름 vs. 태그 vs. 레이어 vs. 레퍼런스(링크).
  • 그룹 개체들을 찾는 방법: 타입 vs. 이름 vs. 태그 vs. 레이어 vs. 레퍼런스(링크) 배열
  • 오브젝트의 그룹들 찾기 vs 자체 등록 (싱글톤 객체?)
  • 실행 순서 제어 (Unity 를 이용하여 실행 순서 제어 vs. yield 로직 vs. Awake / Start 와 Update / Late Update 의존 vs. 수동 메소드 vs. 모든 주문 아키텍쳐)
  • 개체 / 위치 / 타겟 의 선택과 인 게임의 마우스: 선택 관리자 vs 로컬 자체 관리
  • scene 변화와 데이터 간의 유지: PlayerPrefs 를 통해, 또는 새로운 scene 이 로드 될때 지워지지 않은 객체.
  • 애니메이션 결합 방법 (블렌딩, 더하거나 레이어링(계층관리)).
원문

Time

27. 쉽게 일시 중지 하기 위해 당신만의 Time class 를 유지 하라. Wrap Time.DeltaTime 와 Time.TimeSinceLevelLoad 은 계정 별 일시 중지와 시간 scale 이 가능합니다. 이것을 사용하는데는 훈련이 필요로 하지만, 많은 부분에 대해 쉽게 제작 가능하며, 특히 다른 Time 의 일을 실행 하기 편합니다. (예로 interface 애니메이션과 게임 애니메이션 등)
원문

Spawning Objects

28. 게임이 실행 될 때 생성되는 개체가 계층 구조를 더럽히게 하지 말라. Set their parents to a scene object to make it easier to find stuff when the game is running.
게임이 실행 중 일때 개체를 쉽게 찾을 수 있도록 scene 개체에 부모를 설정 합니다. 비어있는 게임 오브젝트를 사용하거나 behaviour 가 없는 싱글톤을 이용하여 만들면 코드에 접근하기 쉽습니다. 이 오브젝트를  DynamicObjects 라고 부릅니다.
원문

Class Design

29. 편의를 위해 싱글톤 을 사용하라. 다음 클래스는 자동으로 싱글톤 을 상속하는 클래스를 만들 것 입니다:
public class Singleton<T> : MonoBehaviour where T : MonoBehaviour
{
   protected static T instance;
 
   /**
      Returns the instance of this singleton.
   */
   public static T Instance
   {
      get
      {
         if(instance == null)
         {
            instance = (T) FindObjectOfType(typeof(T));
 
            if (instance == null)
            {
               Debug.LogError("An instance of " + typeof(T) + 
                  " is needed in the scene, but there is none.");
            }
         }
 
         return instance;
      }
   }
}
싱글톤은 관리자에 유용하며, ParticleManager 또는 AudioManager 또는  GUIManager 등이 있습니다.
  • 고유한 프리팹의 인스턴스를 위한 싱글톤은 사용하는걸 피하는고 관리하지 않는게 좋습니다(Player 같은). 이 원칙을 준수하지 않으면 상속 계층 구조를 복잡하게 하며, 그리고 특정 유형의 변경을 힘들게 합니다. 오히려 GameManager 에서 참조를 유지하는게 좋습니다.(또는 다른 전지 전능한 클래스 ;-))
  • 종종 클래스 외부에서 사용되는 public 변수 와 메소드 에 대한 static 속성과 메서드를 사용합니다. GameManager.Instance.player 대신에  GameManager.Player 으로 작성이 가능합니다.
원문
30. component 들을 들어, inspector 에 변화를 주지 말아야 할 변수는 공개 하지 않습니다. 그렇지 않으면 그것은 무엇을 하는지 알수 없게 되여 디자이너가 변경을 해버리게 됩니다. 이런 경우 두줄 이나 네줄의 언더스코어를 접두로 변수 이름에 붙이면 수정을 꺼리게 될 것이다.
(역자 : 이런 경우 [HideInspector] 를 변수 상위에 붙이면 아예 노출이 일어나지 않습니다)
public float __aVariable;
원문
31. 게임 로직에서 인터페이스를 분리하여 제공합니다. 이것은 기본적으로 MVC 패턴 입니다.
모든 입력 컨트롤러는 호출되여 알려 해당 컴포넌트에 명령을 제공해야 합니다. 컨트롤러 로직을 예로, 컨트롤러는 플레이어의 상태에 따라 제공할 명령을 결정 할 수 있습니다. 그러나 이것은 안좋습니다 (예로 컨트롤러를 더 추가하는 경우 중복된 로직으로 불릴 가능성이 있습니다). 대신에, 플레이어 객체는 앞으로 이동을 할 경우에 통지 해야만 하며,  현재 상태(예를 들어 느려졌거나 아니면 기절)에 따라 속도를 설정하고 Player 의 방향을 업데이트 합니다. 컨트롤러는 자신의 상태에 관한 일을 해야 합니다(컨트롤러는 플레이어가 상태를 바꾸려고 할때 상태가 바뀐 상태인지 알수가 없습니다; 따라서, 컨트롤러는 전혀 플레이어의 상태를 알수 없습니다). 또 다른 예는 무기의 변화 입니다. 할 수 있는 옳은 방법은 GUI 가 플레이어의 SwitchWeapon 을 호출 하는 방법입니다. GUI 는 transform 과 부모와 모든 요소에 대해서 조작해선 안됩니다.
모든 인터페이스 컴포넌트는 데이터를 유지해야하고 자신의 상태에 관한 처리를 합니다. 예로, 맵을 표시하거나, GUI 는 플레이어의 움직임에 따라 표시 할수 있도록 계산을 합니다. 그러나, 이것이 게임 상태 데이터 이면, GUI 에 속해서는 안됩니다. GUI 는 단지 게임 상태 데이터를 표시해야 하며, 데이터는 다른곳에 유지해야 합니다. map 데이터는 다른곳에서 유지해야 합니다(예를 들어, GameManager 에서).
게임플레이 오브젝트는 GUI 에 대해서 아는게 거의 없습니다. 한가지 예외는 일시 정지 행동으로, Time.timeScale 을 통해 전체적으로 제어 합니다(물론 이는 좋은 생각은 아닙니다.. 보기에는). 게임플레이 오브젝트는 게임이 멈춘 상태일 때 알아야 합니다. 하지만 그게 전부 입니다. 따라서 게임 플레이 오브젝트로 부터 GUI 컴포넌트를 연결할 필요는 없습니다.
일반적으로, 당신이 GUI 클래스들을 모두 지울 경우에도, 게임은 여전히 컴파일 되야 합니다.
당신은 또한 GUI 와 input 을 재 구현시 새로운 게임 로직을 작성할 필요가 없어야 합니다.
원문
32. 별도의 상태와 Bookkeeping. Bookkeeping 변수는 빠르게 또는 편리하게 사용하기 위함이며, 그리고 현재 상태로부터 복구하는걸 가능하게 해준다. 이렇게 분리하면, 당신은 더 쉽게 적용 가능하다.
  • 게임 상태 저장, 그리고
  • 게임 상태 디버그
한가지 방법은 각각의 게임 로직 클래스를 위해 SaveData 클래스를 정의 하는 것이다.
[Serializable]
PlayerSaveData
{
   public float health; //public for serialisation, not exposed in inspector
} 
 
Player
{
   //... bookkeeping variables
 
   //Don’t expose state in inspector. State is not tweakable.
   private PlayerSaveData playerSaveData; 
}
원문
33. 별도의 특성화 구성.
동일한 메시들로 두개의 적을 고려 하는데, 벨런스 값이 다르다.(예를 들어 다른 힘과 다른 스피드를 가지고 있는경우). 별도의 데이터에 대해 여러가지 방법이 있습니다. 이중에 하나는 내가 원하는 것일거라 생각하며, 오브젝트가 생성될 때나, 게임이 저장될 때 입니다.
(Tweakable 들은 상태 데이터가 아니나 구성 데이터로, 그래서 저장할 필요가 없습니다. 개체가 로드 되거나 생성되는경우, tweakable 들은 별도로 자동으로 로드 됩니다.)
  • 각각의 게임 클래스에 템플릿 클래스를 정의합니다. 예로, 적인 경우 우리는 또한 EnemyTemplate 라고 정의 합니다. 모든 차별화된 tweakable 들은 EnemyTemplate 에 저장합니다.
  • 게임 로직 클래스 에는, 변수나 템플릿 타입을 정의 합니다.
  • 적 프리팹을 만들고, 두개의 템플릿 프리팹 WeakEnemyTemplate, andStrongEnemyTemplate 을 만듭니다.
  • 로딩하거나 오브젝트를 생성하면, 정식 템플릿에 템플릿 변수를 설정 합니다.
이 방법은 매우 정교한 방법이 될 수 있습니다. (그리고 때로는 불필요하게 복잡하므로 주의!)
예를 들어, generic 다형성을 사용하기 위해, 우리는 아마 이런식으로 정의 하는게 좋을겁니다:
public class BaseTemplate
{
   ...
}
 
public class ActorTemplate : BaseTemplate
{
   ...
}
 
public class Entity<EntityTemplateType> where EntityTemplateType : BaseTemplate
{
   EntityTemplateType template;
   ...
}
 
public class Actor : Entity <ActorTemplate>
{
   ...
}
원문
34. 표시 텍스트 이외에 다른 용도로 문자열을 사용하지 마시오. 특히, 개체 나 프리팹의 식별을 위해서 문자열을 사용하지 않습니다. 한가지 어쩔수 없는 예외는 애니메이션 으로, 일반적으로 자신의 문자열 이름으로 접근 할수 있습니다.
원문
35. Avoid using public index-coupled arrays 공개된 인덱스 결합 배열을 사용하지 마시오. 예로 무기 배열, 총알 배열, 파티클 배열 을 정의 하지 않는 것으로, 이런 경우에 대해서 좋은 방법은 아래와 같습니다:
(역자 : 배열의 인덱스를 직접적으로 사용하는걸 줄이라는 의미 인거 같습니다)
public void SelectWeapon(int index)
{ 
   currentWeaponIndex = index;
   Player.SwitchWeapon(weapons[currentWeapon]);
}
 
public void Shoot()
{
   Fire(bullets[currentWeapon]);
   FireParticles(particles[currentWeapon]);   
}
이런 코드를 많이 사용 하지 않는 문제는, inspector 에 설정 할 때 실수를 할 가능성이 있기 때문 입니다.
오히려, 세 변수를 캡슐화 하는 클래스를 정의하고 그 배열을 확인 하십시오.
[Serializable]
public class Weapon
{
   public GameObject prefab;
   public ParticleSystem particles;
   public Bullet bullet;
}
이 코드는 보기에 단정해 보이나, 매우 중요하며, inspector 에 데이터를 셋팅할 때 실수하기 어렵습니다.
원문
36. 시퀀스 이외의 다른 구조에 대한 배열을 사용하지 마시오. 예로, 플레이어가 세개의 공격 타입을 가지고 있다고 칩시다. 각각 현재 무기를 사용하지만 서로 다른 포탄과 다른 동작을 생성합니다.
당신은 아마 배열을 사용해 세개의 총알을 발사 하려하며, 이러한 로직을 사용할 것입니다:
public void FireAttack()
{
   /// behaviour
   Fire(bullets[0]);
}
 
public void IceAttack()
{
   /// behaviour
   Fire(bullets[1]);
}
 
public void WindAttack()
{
   /// behaviour
   Fire(bullets[2]);
}
Enums 을 사용하면 좀 더 나은 코드가 될 수 있습니다...
public void WindAttack()
{
   /// behaviour
   Fire(bullets[WeaponType.Wind]);
}
그러나 inspector 에서는 안됩니다.
그것은 별도의 변수를 사용하는 것이 좋기 때문에 어떤 콘텐츠를 넣어도 이름으로 도움말이 될 수 있습니다. 타입이 단정 지어질때 클래스를 만듭니다.
[Serializable]
public class Bullets
{
   public Bullet FireBullet;
   public Bullet IceBullet;
   public Bullet WindBullet;
}
Fire 를 설정하면 Ice 와 Wind 데이터는 없다고 가정합니다.
원문
37. serializable 클래스의 그룹 데이터는 가까운거 끼리 모아 inspector 로 만들어야 한다 . 어떤 엔티티는 수십개의 조절할게 있을수 있습니다.  이 경우 inspector 에 연결될 변수를 찾는게 악몽이 될 수 있습니다. 더 쉽게 만들기 위해, 다음의 단계를 따르십시오: 
  • 변수의 그룹에 대한 별도의 클래스를 정의 합니다.
    그것들을 public 및 serializable 합니다.
  • 기본 클래스에서, 위와 같이 정의된 각각의 유형의 public 변수를 정의 합니다.
  • 변수를 Awake 나 Start 에서 초기화 하면 안되며; 그것들은 serializable 이기 때문에, Unity 에서 잘 처리 해줄 것입니다.
  • 당신은 정의된 변수가 연결되기 전에 초기화를 지정하는게 가능하다 
inspector 의 접을 수 있는 공간의 그룹 변수들은, 관리를 쉽게 합니다.
 
[Serializable]
public class MovementProperties //Not a MonoBehaviour!
{
   public float movementSpeed;
   public float turnSpeed = 1; //default provided
}
 
public class HealthProperties //Not a MonoBehaviour!
{
   public float maxHealth;
   public float regenerationRate;
}
 
public class Player : MonoBehaviour
{
   public MovementProperties movementProeprties;
   public HealthPorperties healthProeprties;
}
원문

Text

38. 스토리 텍스트가 많은 경우, 파일에 넣도록 합니다. inspector 의 에디팅 할수 있는 영역으로 두지 마십시오. 이것은 Unity 에디터를 열지 않고도 변경을 쉽게 해줄 것이며, 특히 scene 을 저장하지 않고도 가능할 것입니다.
원문
39. 당신이 로컬라이징을 할 계획이 있다면, 하나의 지역에 모든 문자열을 분리합니다. 
이 작업을 수행하는 방법에는 여러가지가 있습니다. 한가지 방법은 예를 들어, 기본값을 영어로 설정하고, 각 문자열에 대한 공개 문자열 필드에 텍스트 클래스를 정의 하는 것입니다. 다른 언어는 하위 클래스를 두고 언어에 맞추어 다시 초기화 하는 것입니다.
더 정교한 기법은 (텍스트의 길이가 길고 / 길거나 언어의 갯수가 많은 경우 적절합니다)sheet 로부터 읽어오고 선택된 언어로부터 기본이 되는 문자열을 선택해서 로직에 제공 하는 것입니다.
원문

Testing and Debugging

40. 물리, 애니메이션, 그리고 AI 를 디버깅하기 위해 그래픽 logger를 구현 합니다. 이건 상당히 빠르게 디버깅 할 수 있게 합니다. 여기에 자세한 정보가 있습니다 here.
원문
41. HTML logger를 정의 합니다.  몇가지 경우에 대해서, logging 은 매우 유용 합니다.
구문 분석(컬러 코드, 멀티 뷰, 스크린샷 기록)을 하게 되면 로그를 디버깅 하는게 훨씬 쾌적 할 수 있습니다. 여기에 자세한 정보가 있습니다 here.
원문
42. 당신의 FPS counter 를 구현합니다. 예. Unity 의 FBS counter 가 정말 제대로 측정할지도 모르나, 그것은 프레임 레이트가 아닙니다. 직관과 육안 검사와 일치 할 수 있도록 자신의 것을 구현 합니다.
원문
43. Implement shortcuts for taking screen shots 스크린샷을 이용해 바로가기를 구현 합니다.  많은 버그는 비쥬얼이며, 그리고 당신은 사진을 찍어두면 보고가 좀 더 쉬워집니다. 이상적인 시스템은 스크린샷을 덮어씌우지 않도록 PlayerPrefs 의 카운터를 유지 해야 합니다. 스크린샷은 실수로 사람들이 저장소에 커밋하는걸 방지하기 위해 프로젝트 폴더 외부에 저장해야 합니다.
원문
44. 플레이어의 월드 위치를 출력하기 위한 바로가기를 구현 합니다. 이것은 월드의 특정 장소에서 발생하는 버그의 위치를 리포트를 하기 쉬워질 것이며, 이는 번갈아 가면서도 쉽게 디버깅 할 수 있습니다.
원문
45. 테스트를 쉽게 만들기 위한 디버그 옵션을 구현합니다. 몇가지 예를 들면:
  • 모든 아이템 언락
  • 적 제거
  • GUI 끄기
  • 플레이어 무적
  • 모든 게임 플레이 불가.
원문
46. 충분히 작은 팀의 경우, 디버그 옵션을 사용하여 각 팀의 구성원을 위한 prefab 을 만듭니다. 파일에 사용자 ID 를 넣고: 게임이 실행 될 때 읽어집니다. 이 이유는:
  • 팀 구성원은 사고로 자신의 디버그 옵션을 커밋 하고 모두에게 영향을 미치기 않기 위함입니다.
  • 디버그 옵션을 바꾸는 것은 scene 을 바꾸는게 아니기 때문입니다.
원문
47. 모든 게임 플레이 요소와 scene 을 유지 합니다. 예로, 모든 적이 있는 scene, 당신이 상호 작용 할수 있는 모든 개체, 그외 등등. 너무 오래 게임을 하지 않고 기능을 테스트 할 수 있도록 합니다.
원문
48. 디버그 바로 가기 키를 위한 상수를 정의하고, 한 곳에서 보관 합니다. 디버그 키는 일반적으로 (또는 편리한) 게임 입력이 처리 되는 위치에서 같이 처리 되지는 않습니다. 바로 가기 키의 충돌을 방지 하기 위해 중앙 위치에 상수를 정의 합니다. 대안으로 디버그 함수인지 아닌지 상관없이 한 곳에 모든 키를 처리 하는 겁니다. (단점은 이 클래스는 이 개체에 대해서 참조를 추가로 해야 할 수도 있습니다)
원문

Documentation

49. 설치를 문서화 합니다. 대부분의 코드는 문서에 있어야 하며, 하지만 이러한 것은 코드 외부에서 문서화 해야합니다. 디자이너가 설정하기 위한 코드를 선별하여 만드는것은 시간 낭비 입니다. 문서 설정이 효율이 극대화 됩니다 (만약 문서가 최신이라면).
다음 사항을 문서화 합니다:
  • 레이어 사용 (충돌, 컬링, raycasting – essentially, 어떤 항목에 어떤걸 해야)
  • 태그 사용.
  • 레이어를 위한 GUI 깊이 (무엇을 통해서 표시해야)
  • scene 설치
  • Idiom 설정
  • 프리팹 구조
  • 애니메이션 레이어
원문

Naming Standard and Folder Structure

50. 문서화 규칙 및 폴더 구조를 따르시오. 일관성 규칙 및 폴더 구조는 쉽게 찾고자 하는걸 찾을 수 있으며, 일이 무엇인지 파악 할 수 있습니다.
당신은 아마도 자신의 규칙 및 폴더 구조를 생성 할 수 있습니다. 다음의 예는 한가지 입니다.
원문

Naming General Principles

  1. 무언가를 호출 합니다. bird 는 Bird 를 호출 합니다. 
  2. 이름을 선택 할 때는 확연하고 기억될 수 있는 이름으로 선택합니다. 만약 당신이 마야 게임을 만든다고 해서, 레벨 이름을 QuetzalcoatisReturn 로 해서는 안됩니다.
  3. 일관성을 유지 하십시오. 이름을 선택할때, 기준에 충실해야 합니다.
  4. Use Pascal case, like this: ComplicatedVerySpecificObject. Do not use spaces, underscores, or hyphens, with one exception (see Naming Different Aspects of the Same Thing).
    이와같이 파스칼 케이스를 사용합니다: ComplicatedVerySpecificObject. 공간( ), 언더바(_), 또는 하이푼(-), 한가지 예외와 함께 (같은 일에 다른 이름을 보게 될 경우)
  5. 버전 번호를 사용하지 말고, 또는 단어로 진행 상태를 표시 하지 마십시오 (WIPfinal).
  6. Do not use abbreviations: DVamp@W should be DarkVampire@Walk.
    약어를 사용하지 마십시오: DVamp@W 는 DarkVampire@Walk .으로 표기 합니다
  7. 디자인 문서의 용어를 사용합니다: 사망 을 문서상 호출 하려면 Die 애니메이션을 부릅니다. DarkVampire@Die, 를 부르며 DarkVampire@Death 는 안됩니다.
  8. 구체적인 설명은 왼쪽에 표기 합니다: DarkVampire 는 좋은 방법이며 VampireDark 는 안좋은 방법입니다. PauseButton는 좋은 방법이며 ButtonPaused는 안좋은 방법입니다. 이것을 예로 들면, 모든 버튼이 단어 버튼으로 시작하면 inspector 에서 일시 정지 버튼을 찾기 쉬워집니다. [많은 사람들이 다른 방법을 더 선호하며, 때문에 시각적으로 보다 더 확실히 그룹화를 하여 생성합니다. 이름이 있지만 그룹화를 위한게 아니며, 폴더를 위한것 입니다. 이름이 안정적으로 빠르게 자리 잡고 할 수 있도록 동일한 유형의 객체를 구분하는 것 입니다.]
  9. 일부 이름은 순서를 형성합니다. 이름에 숫자를 사용하며, 예로 PathNode0,PathNode1 가 있습니다. 항상 시작은 1이 아닌 0으로 시작합니다.
  10. 순서를 필요로 하지 않는 일에 번호를 사용하지 마십시오. 예로 Bird0Bird1,Bird2 는 FlamingoEagleSwallow 라고 표기합니다.
  11. 임시 객체는 이중으로 접두사에 언더바를 넣습니다 __Player_Backup.
원문

Naming Different Aspects of the Same Thing

언더바와 핵심 이름을 사용하여, 해당 상태에 "세부" 내용 설명합니다. 예로 들면:
  • GUI 버튼 상태는 EnterButton_Active, EnterButton_Inactive
  • 텍스쳐는 DarkVampire_Diffuse, DarkVampire_Normalmap
  • 스카이 박스는 JungleSky_Top, JungleSky_North
  • LOD 그룹은 DarkVampire_LOD0, DarkVampire_LOD1
단지 다른 유형의 항목 구분이 규칙을 사용하면 안되며, 예로  Rock_Small, Rock_Large 를 SmallRock, LargeRock 으로 사용 하는 경우 입니다.
원문

Structure

당신의 scene 의 구성, 프로젝트 폴더, 그리고 스크립트 폴더는 유사한 패턴을 가져야 합니다.
원문

Folder Structure

Materials
GUI
Effects
Meshes
   Actors
      DarkVampire
      LightVampire
      ...
   Structures
      Buildings
      ...
   Props
      Plants
      ...
   ...
Plugins
Prefabs
   Actors
   Items
   ...
Resources
   Actors
   Items
   ...
Scenes
   GUI
   Levels
   TestScenes
Scripts
Textures
GUI
Effects
...

Scene Structure

Cameras
Dynamic Objects
Gameplay
   Actors
   Items
   ...
GUI
   HUD
   PauseMenu
   ...
Management
Lights
World
   Ground
   Props
   Structure
   ...

Scripts Folder Structure

ThirdParty
   ...
MyGenericScripts
   Debug
   Extensions
   Framework
   Graphics
   IO
   Math
   ...
MyGameScripts
   Debug
   Gameplay
      Actors
      Items
      ...
   Framework
   Graphics
   GUI
   ...

How to Re-implement Inspector Drawing

1. 당신의 모든 에디터를 위한 기본 클래스를 정의

BaseEditor<T> : Editor 
where T : MonoBehaviour
{
   override public void OnInspectorGUI()
   {
      T data = (T) target;
 
      GUIContent label = new GUIContent();
      label.text = "Properties"; //
 
      DrawDefaultInspectors(label, data);
 
      if(GUI.changed)
      {         
         EditorUtility.SetDirty(target);
      }
   }
}

2. reflection 과 recursion 를 사용하여 draw 컴포넌트에 reflection 을 사용

public static void DrawDefaultInspectors<T>(GUIContent label, T target)
   where T : new()
{
   EditorGUILayout.Separator();
   Type type = typeof(T);      
   FieldInfo[] fields = type.GetFields();
   EditorGUI.indentLevel++;
 
   foreach(FieldInfo field in fields)
   {
      if(field.IsPublic)
      {
         if(field.FieldType == typeof(int))
         {
            field.SetValue(target, EditorGUILayout.IntField(
            MakeLabel(field), (int) field.GetValue(target)));
         }   
         else if(field.FieldType == typeof(float))
         {
            field.SetValue(target, EditorGUILayout.FloatField(
            MakeLabel(field), (float) field.GetValue(target)));
         }
 
         ///etc. for other primitive types
 
         else if(field.FieldType.IsClass)
         {
            Type[] parmTypes = new Type[]{ field.FieldType};
 
            string methodName = "DrawDefaultInspectors";
 
            MethodInfo drawMethod = 
               typeof(CSEditorGUILayout).GetMethod(methodName);
 
            if(drawMethod == null)
            {
               Debug.LogError("No method found: " + methodName);
            }
 
            bool foldOut = true;
 
            drawMethod.MakeGenericMethod(parmTypes).Invoke(null, 
               new object[]
               {
                  MakeLabel(field),
                  field.GetValue(target)
               });
         }      
         else
         {
            Debug.LogError(
               "DrawDefaultInspectors does not support fields of type " +
               field.FieldType);
         }
      }         
   }
 
   EditorGUI.indentLevel--;
}
위 method 는다음과 같은 헬퍼를 사용한다:
private static GUIContent MakeLabel(FieldInfo field)
{
   GUIContent guiContent = new GUIContent();      
   guiContent.text = field.Name.SplitCamelCase();      
   object[] descriptions = 
      field.GetCustomAttributes(typeof(DescriptionAttribute), true);
 
   if(descriptions.Length > 0)
   {
      //just use the first one.
      guiContent.tooltip = 
         (descriptions[0] as DescriptionAttribute).Description;
   }
 
   return guiContent;
}
이 inspector 에 툴팁을 생성하기 위해 클래스 코드에 주석을 사용합니다.

3. Define new Custom Editors

불행하게도, 당신은 여전히 각각의 MonoBehaviour 를 위한 클래스를 정의 해야 합니다. 다행히도, 이러한 정의는 비어 있을것이며; 모든 실제 작업은 기본 클래스에 의해 이루어 집니다.
[CustomEditor(typeof(MyClass))]
public class MyClassEditor : BaseEditor<MyClass>
{}
이론적으로 이 단계는 자동일 수 있지만, 난 아직 시도 해보지 않았습니다.

[출처] 유니티3D 50가지 팁|작성자 일리

출처 원문


[출처]

http://seungngil.tistory.com/entry/%EC%9C%A0%EB%8B%88%ED%8B%B0-%EC%9E%91%EC%97%85%EC%97%90-%EB%8C%80%ED%95%9C-50-%ED%8C%81-%EB%AA%A8%EB%B2%94-%EC%82%AC%EB%A1%80-50-Tips-for-Working-with-Unity-Best-Practices

반응형
Posted by blueasa
, |