이 작업을 시작하게 된 썰


현재 만들고 있는 게임은 데이터들을 xml 형식으로 읽어온다.

그런데 전체 데이터 로드를 게임 진입부에서 한꺼번에 하기 때문에 (왜 그렇게 했을까... ㅠ_ㅠ)

데이터량이 많아지면 많아질수록 당연하게도 데이터 로딩시간은 점점 늘어날 수 밖에 없다.


전체적으로 데이터 로딩 구조를 바꾸기엔 손봐야할 부분이 너무 많기 때문에

일단 기존에 사용하던 기본 xml 파서를 가볍고 날렵한 놈으로 바꿔보기로 했다.


여기 저기 구글링을 통해 알아보니

TinyXml, Mono.xml, RapidXml 등 유니티에서 사용할 수 있는 몇몇 파서가 있었는데

그 중 RapidXml을 소개해보겠다.




먼저 기본 System.xml 파서와 Mono.xml, RapidXml 파서를 사용한 xml 데이터 로딩, 파싱에 소요되는 시간은 다음과 같다.

퍼포먼스 테스트 - 데이터 로딩 : xml 데이터 로드 10회 반복 


  • System.xml (유니티 기본) : 53.96 / ms


  • Mono.xml : 34.63 / ms

  • RapidXml : 0.39 / ms


  • 퍼포먼스 : RapidXml >>>>> Mono.xml > System.xml

0.39 !!!!????
잘못잰건가 싶어서 몇번을 다시 돌려봐도 결과는 비슷했다.
우앙 ㅋ 굿



그렇담 이번엔 파싱.


퍼포먼스 테스트 - 데이터 파싱 : xml 데이터 파싱 10회 반복 


  • System.xml (유니티 기본) : 293.79 / ms


  • Mono.xml : 242.64 / ms


  • RapidXml : 252.11 / ms

  • 퍼포먼스 : Mono.xml > RapidXml > System.xml



결론


파싱에서는 Mono.xml이 수치적으로 젤 퍼포먼스가 좋았지만 RapidXml과 근사치였다.
하지만 RapidXml의 어마무시하게 빠른 로딩속도까지 생각해보면

RapidXml의 압승!!!


사실 간단한 xml 로딩은 기본 파서를 사용해도 전혀 상관이 없다.

하지만 나와 같은 고민을 해본 개발자라면 Parser 교체를 해보는것도 좋은 대안이 될듯하다.

(현재 개발중인 게임도 RapidXml로 파서를 바꿔서 사용중인데 데이터 로딩 시간이 대폭 감소되었다. ^^;;)


교훈 : 데이터 구조 설계부터 잘 해라...   ㅜ_ㅜ





첨부된 파일 설명



첨부된 파일은 https://github.com/sczybt/UnityRapidXml의 샘플 코드를 기반으로 작성한 유니티 테스트 프로젝트이며
유니티 에디터 (Mac osx, Windows), Android 디바이스에서 사용 가능하도록 라이브러리를 추가해두었다.



- Plugins > Android > libs > 하위 파일들 : Android 빌드용 라이브러리


- Plugins > RapidXml.dll : PC용 라이브러리


- Plugins > RapidXml.bundle : Mac용 라이브러리


- Plugins > RapidXml > RapidXml.cs : RapidXml c# wrapper


- Assets > RapidXml > Scenes > Main.unity : 테스트 씬




Plugins 폴더 이외의 파일들은 테스트용이므로 실제 사용시에는 plugins폴더만 본인의 프로젝트로 임포트해서 사용하면 된다.












1. 3D Max의 System Unit을 미터(m)단위로 맞추고 Display Unit은 사용자 편의에 맞춰 설정


2. 모델링 export 시 Scale Factor를 1로 설정.


3. Unity 임포트 시 Inspector의 Scale Factor를 0.01 => 1로 변경 

(보통 max의 System Unit이 cm 단위로 되있어서 유니티 transform의 unit(m)와 맞춰주기 위해 디폴트로 0.01로 설정되있는듯)

Windows7 환경에서는 아무 문제없이 안드로이드 프로젝트 작업을 진행했었는데

얼마전 개발 환경을 imac으로 바꾼 후 Unity 셋팅을 하다 멘붕이 왔다.



이게 무어야;;;


No platforms found ???


분명히 sdk가 버전별로 설치되어 있음에도 불구하고 안드로이드 빌드 시 저놈의 팝업이 계속 뜨면서

빌드 실패...


구글 신에게 물어봤지만 뾰족한 해결방법을 찾지 못해 하루 반나절을 끙끙 대던 중 어찌저찌해서

해결방법을 알아냈다.


sdk 다운로드 시 최신이 아닌 이전 리비전을 다운 받은 후 다시 최신으로 업그레이드 하는 방법이다;;

[mac 용 sdk r21 down : http://dl.google.com/android/android-sdk_r21-macosx.zip]


1. 기존에 설치된 sdk 폴더 삭제.

2. 위의 링크에서 sdk를 받은 후 적절한 위치(왠지 간단한게 좋을 것 같아 Android/sdk/ 로 생성;)

3. 설치된폴더 > tools > android 를 실행 (Terminal에서 실행시킬 필요 없다. 그냥 클릭 ㄱ ㄱ)

4. sdk 4.0 (API 14)를 설치해본다.

5. unity에서 빌드 테스트

6. 성공


허무하게 해결..





윈도우 환경에서만 유니티 개발을 해오다가 이번에 맥 환경에서 개발을 하게되어

이것 저것 셋팅을 하다보니


아니 이게 뭐여;; 맥용 Monodevelope에선 한글 주석 입력이 더럽게 힘들구나!!!


라는걸 느끼고 바로 다른 에티터툴을 알아보기 시작했다.

그래서 찾아낸것이 바로  Sublime text 2


코딩에 많은 편의성을 주기 때문에 요즘 많이 사용하는 에디터라는데 오늘에야 처음 들어보다니.. 

일단 셋팅 ㄱ ㄱ 


1. Sublime text 2 를 다운 받는다.  http://www.sublimetext.com/2

    (라이선스 비용은 70$지만 라이선스 등록을 하지 않고 사용을 해도 가끔 등록 팝업 뜨는 것 말곤 기능상의 제약은 없다고 한다.)


2. Package install을 편하게 관리할 수 있는 Package control을 설치한다. 

   (http://wbond.net/sublime_packages/package_control/installation 참고)


- Package 파일 다운로드  [다운로드

  링크가 깨졌을 경우 http://wbond.net/sublime_packages/package_control/installation


Sublime text 2 > Preference > Browse Packages... > 




- 패키시 선택창이 뜨면 좀전에 다운받은 패키지 파일을 패키지 선택창의 Installed Packages에 옮긴 후 파일 선택


- Sublime text 2 재실행


3. Tools > Command Palette... > p 입력




4. Package Control: Install Package 선택 후 패키지 검색창에 unity 입력



위와 같이 유니티 관련 패키지들이 나오는데 설치하고자 하는 항목을 클릭하면 바로 설치가 된다.

(패키지 제거는 Package Contol: Remove Package선택.)


5. View > Syntax > 유니티 관련 항목이 있으면 정상 설치





Sublime가 Monodevelope의 대용으로 훌륭한 에디터 역할을 할 수 있을 진 좀 더 사용해봐야 알겠지만

대략적인 장점을 몇개 꼽자면


1. 모노디벨롭에 비해 엄청 가벼운 느낌 (그냥 메모장 여는 기분??;;)

2. 한글 주석 사용 가능 (한글 주석 사용이 가능하지만 UTF-8만 지원하기 때문에 이부분을 잘 고려해야함)

3. 에디터 내에서 빌드 가능

4. 유니티 API 확인이 가능하다. (에디터 내에서는 아니고 웹브라우저를 띄운다.)

5. 코드 하일라이터, 구문 자동 완성 기능 사용 가능


이정도??


모노디벨롭의 디버깅 기능과 St2의 장점을 활용해서 코딩을 하면 조금이나마 생산성 향상에 도움이 되지 않을까 싶다~!!




2D 게임을 만들기 위해 유니티에서 이미지를 임포트하면 웬지 모르게 이미지가 뭉게져 보이는데요. 이는 유니티가 3D 엔진이기 때문에, 임포트되는 텍스쳐에 대해서 자동으로 밉맵(Min Map)을 생성하기 때문입니다.

이를 해결하고 선명한 이미지를 만들기 얻기 위해서는 텍스쳐 타입을 "GUI" 로 설정하면 간단히 해결됩니다.

하지만, 만약 "Alpha from Grayscale" 을 사용해 이미지를 투명하게 처리해야할 필요가 있다면 텍스쳐 타입을 "Advanced" 로 선택 후 "Generate Mip Maps" 을 꺼 주시면 됩니다... ^^

마지막으로, 텍스처 "Max Size" 는 실제 게임에서 사용할 크기에 맞게 적절하게 설정하시고.. "Texture Format" 은 웬만하시면 Automatic Truecolor 을 선택하세요... 2D 게임의 경우, 압축 이미지를 사용하면 티가 많이 나더군요.. =ㅁ=

참... 쉽죠? ^^ㅋ;;


[출처] http://mobilism.tistory.com/entry/Unity3D-2D-%EA%B2%8C%EC%9E%84%EC%9D%84-%EC%9C%84%ED%95%9C-%ED%85%8D%EC%8A%A4%EC%B3%90-%EC%84%A4%EC%A0%95


와우(World of warcraft) 폐인이었던 나였기 때문에

이 동영상을 보자마자 와우가 떠올랐다;;




게임 UI중 score나 point 획득 시 아주 유용하게 쓰일듯하다!!



 WaitForSeconds라는 메소드가 있는데 C#에서 쓰러면 조금 까다롭게 형식에 맞춰서 작성해줘야 한다.

위 예제와 같이 Awake라는 메소드에서 시간을 두번 출력할때 첫번째 시간을 출력 한 뒤 5초뒤에 두번째 시간을 출력하고자 하는경우 또는 일정시간마다 반복 재귀호출 되는 메소드를 작성하고 싶을때에
'IEnumerator' 라는 인터페이스를 사용하도록 해당 메소드 앞에 적어줘야 한다.

IEnumerator delayTime()
{
yield return new WaitForSeconds(1);
Debug.Log("time = " + Time.time );
}


 위와 같이 delayTime이라는 메소드를 적어주면 메소드가 호출되면 1를 기다렸다가 1초뒤에 Debug 문장이 실행된다.

IEnumerator 인터페이스를 사용하는 메소드를 실행하기 위해서는 
특정 호출 메소드를 이용해야 하는데

StartCoroutine(delayTime);


다른 메서드들 처럼 그냥 메서드명() 으로 호출하면 실행되지 않고 위와 같이 StartCoroutine(메서드명) 으로 호출해야 한다.


일정시간 간격으로 계속 반복되어 실행되는 메서드를 만들러면

void Start () {
        StartCoroutine(countTime, 1);
 }
  
    IEnumerator countTime(float delayTime)
    {
        Debug.Log("Time = " + Time.time);
        yield return new WaitForSeconds(delayTime);
        StartCoroutine( countTime, 1 );
    }


위와 같이 적어주면 countTime이 waitForSeconds 명령에 의해 일정시간 만큼 딜레이를 한 뒤 자기자신을 재 호출하게되어 
1초 간격으로 계속 반복 호출하도록 할 수 있다.


출처 : http://clack.tistory.com/50

'개발 > Unity3d' 카테고리의 다른 글

2D 게임을 위한 텍스쳐 설정  (0) 2013.05.30
[NGUI] HUD Text  (0) 2013.05.30
Unity3d 50가지 팁  (0) 2013.05.30
[스크랩] 유니티 이미지 최적화  (2) 2013.05.29
유니티용 티스토어 플러그인  (0) 2013.05.29

이 팁에 대해서

이 팁이 모든 프로젝트에 적용되는 것은 아닙니다.

  • They are based on my experience with projects with small teams from 3 to 20 people.
  • There’s is a price for structure, re-usability, clarity, and so on — team size and project size determine whether that price should be paid.
  • Many tips are a matter of taste (there may be rivalling but equally good techniques for any tip listed here).
  • Some tips may fly in the face of conventional Unity development. For instance, using prefabs for specialisation instead of instances is very non-Unity-like, and the price is quite high (many times more prefabs than without it). Yet I have seen these tips pay off, even if they seem crazy.

Process

1. Avoid branching assets. There should always only ever be one version of any asset. If you absolutely have to branch a prefab, scene, or mesh, follow a process that makes it very clear which is the right version. The “wrong” branch should have a funky name, for example, use a double underscore prefix: __MainScene_Backup. Branching prefabs requires a specific process to make it safe (see under the section Prefabs).

2. Each team member should have a second copy of the project checked out for testing if you are using version control. After changes, this second copy, the clean copy, should be updated and tested. No-one should make any changes to their clean copies. This is especially useful to catch missing assets.

3. Consider using external level tools for level editing. Unity is not the perfect level editor. For example, we have used TuDee to build levels for a 3D tile-based game, where we could benefit from the tile-friendly tools (snapping to grid, and multiple-of-90-degrees rotation, 2D view, quick tile selection). Instantiating prefabs from an XML file is straightforward. SeeGuerrilla Tool Development for more ideas.

4. Consider saving levels in XML instead of in scenes. This is a wonderful technique:

  • It makes it unnecessary to re-setup each scene.
  • It makes loading much faster (if most objects are shared between scenes).
  • It makes it easier to merge scenes (even with Unity’s new text-based scenes there is so much data in there that merging is often impractical in any case).
  • It makes it easier to keep track of data across levels.

You can still use Unity as a level editor (although you need not). You will need to write some code to serialize and deserialize your data, and load a level both in the editor and at runtime, and save levels from the editor. You may also need to mimic Unity’s ID system for maintaining references between objects.

5. Consider writing generic custom inspector code. To write custom inspectors is fairly straightforward, but Unity’s system has many drawbacks:

  • It does not support taking advantage of inheritance.
  • It does not let you define inspector components on a field-type level, only a class-type level. For instance, if every game object has a field of type SomeCoolType, which you want rendered differently in the inspector, you have to write inspectors for all your classes.

You can address these issues by essentially re-implementing the inspector system. Using a few tricks of reflection, this is not as hard as it seems, details are provided at the end of the article.

Scene Organisation

6. Use named empty game objects as scene folders. Carefully organise your scenes to make it easy to find objects.

7. Put maintenance prefabs and folders (empty game objects) at 0 0 0. If a transform is not specifically used to position an object, it should be at the origin. That way, there is less danger of running into problems with local and world space, and code is generally simpler.

8. Minimise using offsets for GUI components. Offsets should always be used to layout components in their parent component only; they should not rely on the positioning of their grandparents. Offsets should not cancel each other out to display correctly. It is basically to prevent this kind of thing:

Parent container arbitrarily placed at (100, -50). Child, meant to be positioned at (10, 10), then placed at (90, 60) [relative to parent].

This error is common when the container is invisible, or does not have a visual representation at all.

9. Put your world floor at y = 0. This makes it easier to put objects on the floor, and treat the world as a 2D space (when appropriate) for game logic, AI, and physics.

10. Make the game runnable from every scene. This drastically reduces testing time. To make all scenes runnable you need to do two things:

First, provide a way to mock up any data that is required from previously loaded scenes if it is not available.

Second, spawn objects that must persist between scene loads with the following idiom:

myObject = FindMyObjectInScene();
 
if (myObjet == null)
{
   myObject = SpawnMyObject();
}

Art

11. Put character and standing object pivots at the base, not in the centre. This makes it easy to put characters and objects on the floor precisely. It also makes it easier to work with 3D as if it is 2D for game logic, AI, and even physics when appropriate.

12. Make all meshes face in the same direction (positive or negative z axis). This applies to meshes such as characters and other objects that have a concept of facing direction. Many algorithms are simplified if everything have the same facing direction.

13. Get the scale right from the beginning. Make art so that they can all be imported at a scale factor of 1, and that their transforms can be scaled 1, 1, 1. Use a reference object (a Unity cube) to make scale comparisons easy. Choose a world to Unity units ratio suitable for your game, and stick to it.

14. Make a two-poly plane to use for GUI components and manually created particles. Make the plane face the positive z-axis for easy billboarding and easy GUI building.

15. Make and use test art

  • Squares labelled for skyboxes.
  • A grid.
  • Various flat colours for shader testing: white, black, 50% grey, red, green, blue, magenta, yellow, cyan.
  • Gradients for shader testing: black to white, red to green, red to blue, green to blue.
  • Black and white checkerboard.
  • Smooth and rugged normal maps.
  • A lighting rig (as prefab) for quickly setting up test scenes.

Prefabs

16. Use prefabs for everything. The only game objects in your scene that should not be prefabs should be folders. Even unique objects that are used only once should be prefabs. This makes it easier to make changes that don’t require the scene to change. (An additional benefit is that it makes building sprite atlases reliable when using EZGUI).

17. Use separate prefabs for specialisation; do not specialise instances. If you have two enemy types, and they only differ by their properties, make separate prefabs for the properties, and link them in. This makes it possible to

  • make changes to each type in one place
  • make changes without having to change the scene.

If you have too many enemy types, specialisation should still not be made in instances in the editor. One alternative is to do it procedurally, or using a central file / prefab for all enemies. A single drop down could be used to differentiate enemies, or an algorithm based on enemy position or player progress.

18. Link prefabs to prefabs; do not link instances to instances. Links to prefabs are maintained when dropping a prefab into a scene; links to instances are not. Linking to prefabs whenever possible reduces scene setup, and reduce the need to change scenes.

19. As far as possible, establish links between instances automatically. If you need to link instances, establish the links programmatically. For example, the player prefab can register itself with the GameManager when it starts, or the GameManager can find the Player prefab instance when it starts.

Don’t put meshes at the roots of prefabs if you want to add other scripts. When you make the prefab from a mesh, first parent the mesh to an empty game object, and make that the root. Put scripts on the root, not on the mesh node. That way it is much easier to replace the mesh with another mesh without loosing any values that you set up in the inspector.

Use linked prefabs as an alternative to nested prefabs. Unity does not support nested prefabs, and existing third-party solutions can be dangerous when working in a team because the relationship between nested prefabs is not obvious.

20. Use safe processes to branch prefabs. The explanation use the Player prefab as an example.

Make a risky change to the Player prefab is as follows:

  1. Duplicate the Player prefab.
  2. Rename the duplicate to __Player_Backup.
  3. Make changes to the Player prefab.
  4. If everything works, delete __Player_Backup.

Do not name the duplicate Player_New, and make changes to it!

Some situations are more complicated. For example, a certain change may involve two people, and following the above process may break the working scene for everyone until person two finished. If it is quick enough, still follow the process above. For changes that take longer, the following process can be followed:

  1. Person 1:
    1. Duplicate the Player prefab.
    2. Rename it to __Player_WithNewFeature or __Player_ForPerson2.
    3. Make changes on the duplicate, and commit / give to Person 2.
  2. Person 2:
    1. Make changes to new prefab.
    2. Duplicate Player prefab, and call it __Player_Backup.
    3. Drag an instance of __Player_WithNewFeature into the scene.
    4. Drag the instance onto the original Player prefab.
    5. If everything works, delete __Player_Backup and __Player_WithNewFeature.

Extensions and MonoBehaviourBase

21. Extend your own base mono behaviour, and derive all your components from it.

This allows you to implement some general functionality, such as type safe Invoke, and more complicated Invokes (such as random, etc.).

22. Define safe methods for Invoke, StartCoroutine and Instantiate.

Define a delegate Task, and use it to define methods that don’t rely on string names. For example:

public void Invoke(Task task, float time)
{
   Invoke(task.Method.Name, time);
}

23. Use extensions to work with components that share an interface. It is sometimes convenient to get components that implement a certain interface, or find objects with such components.

The implementations below uses typeof instead of the generic versions of these functions. The generic versions don’t work with interfaces, but typeof does. The methods below wraps this neatly in generic methods.

//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. Use extensions to make syntax more convenient. For example:

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. Use a defensive GetComponent alternative. Sometimes forcing component dependencies (through RequiredComponent) can be a pain. For example, it makes it difficult to change components in the inspector (even if they have the same 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.

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. Avoid using different idioms to do the same thing. In many cases there are more than one idiomatic way to do things. In such cases, choose one to use throughout the project. Here is why:

  • Some idioms don’t work well together. Using one idiom well forces design in one direction that is not suitable for another idiom.
  • Using the same idiom throughout makes it easier for team members to understand what is going on. It makes structure and code easier to understand. It makes mistakes harder to make.

Examples of idiom groups:

  • Coroutines vs. state machines.
  • Nested prefabs vs. linked prefabs vs. God prefabs.
  • Data separation strategies.
  • Ways of using sprites for states in 2D games.
  • Prefab structure.
  • Spawning strategies.
  • Ways to locate objects: by type vs. name vs. tag vs. layer vs. reference (“links”).
  • Ways to group objects: by type vs. name vs. tag vs. layer vs. arrays of references (“links”).
  • Finding groups of objects versus self registration.
  • Controlling execution order (Using Unity’s execution order setup versus yield logic versus Awake / Start and Update / Late Update reliance versus manual methods versus any-order architecture).
  • Selecting objects / positions / targets with the mouse in-game: selection manager versus local self-management.
  • Keeping data between scene changes: through PlayerPrefs, or objects that are not Destroyed when a new scene is loaded.
  • Ways of combining (blending, adding and layering) animation.

Time

27. Maintain your own time class to make pausing easier. Wrap Time.DeltaTime andTime.TimeSinceLevelLoad to account for pausing and time scale. It requires discipline to use it, but will make things a lot easier, especially when running things of different clocks (such as interface animations and game animations).

Spawning Objects

28. Don’t let spawned objects clutter your hierarchy when the game runs. Set their parents to a scene object to make it easier to find stuff when the game is running. You could use a empty game object, or even a singleton with no behaviour to make it easier to access from code. Call this object DynamicObjects.

Class Design

29. Use singletons for convenience. The following class will make any class that inherits from it a singleton automatically:

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;
      }
   }
}

Singletons are useful for managers, such as ParticleManager or AudioManager or GUIManager.

  • Avoid using singletons for unique instances of prefabs that are not managers (such as the Player). Not adhering to this principle complicates inheritance hierarchies, and makes certain types of changes harder. Rather keep references to these in yourGameManager (or other suitable God class ;-) )
  • Define static properties and methods for public variables and methods that are used often from outside the class. This allows you to write GameManager.Player instead ofGameManager.Instance.player.

30. For components, never make variables public that should not be tweaked in the inspector. Otherwise it will be tweaked by a designer, especially if it is not clear what it does. In some rare cases it is unavoidable. In that case use a two or even four underscores to prefix the variable name to scare away tweakers:

public float __aVariable;

31. Separate interface from game logic. This is essentially the MVC pattern.

Any input controller should only give commands to the appropriate components to let them know the controller has been invoked. For example in controller logic, the controller could decide which commands to give based on the player state. But this is bad (for example, it will lead to duplicate logic if more controllers are added). Instead, the Player object should be notified of the intent of moving forward, and then based on the current state (slowed or stunned, for example) set the speed and update the player facing direction. Controllers should only do things that relate to their own state (the controller does not change state if the player changes state; therefore, the controller should not know of the player state at all). Another example is the changing of weapons. The right way to do it is with a method on PlayerSwitchWeapon(Weapon newWeapon), which the GUI can call. The GUI should not manipulate transforms and parents and all that stuff.

Any interface component should only maintain data and do processing related to it’s own state. For example, do display a map, the GUI could compute what to display based on the player’s movements. However, this is game state data, and does not belong in the GUI. The GUI should merely display game state data, which should be maintained elsewhere. The map data should be maintained elsewhere (in the GameManager, for example).

Gameplay objects should know virtually nothing of the GUI. The one exception is the pause behaviour, which is may be controlled globally through Time.timeScale (which is not a good idea as well… see ). Gameplay objects should know if the game is paused. But that is all. Therefore, no links to GUI components from gameplay objects.

In general, if you delete all the GUI classes, the game should still compile.

You should also be able to re-implement the GUI and input without needing to write any new game logic.

32. Separate state and bookkeeping. Bookkeeping variables are used for speed or convenience, and can be recovered from the state. By separating these, you make it easier to

  • save the game state, and
  • debug the game state.

One way to do it is to define a SaveData class for each game logic class. The

[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. Separate specialisation configuration.

Consider two enemies with identical meshes, but different tweakables (for instance different strengths and different speeds). There are different ways to separate data. The one here is what I prefer, especially when objects are spawned, or the game is saved. (Tweakables are not state data, but configuration data, so it need not be saved. When objects are loaded or spawned, the tweakables are automatically loaded in separately)

  • Define a template class for each game logic class. For instance, for Enemy, we also define EnemyTemplate. All the differentiating tweakables are stored in EnemyTemplate
  • In the game logic class, define a variable of the template type.
  • Make an Enemy prefab, and two template prefabs WeakEnemyTemplate andStrongEnemyTemplate.
  • When loading or spawning objects, set the template variable to the right template.

This method can become quite sophisticated (and sometimes, needlessly complicated, so beware!).

For example, to better make use of generic polymorphism, we may define our classes like this:

public class BaseTemplate
{
   ...
}
 
public class ActorTemplate : BaseTemplate
{
   ...
}
 
public class Entity<EntityTemplateType> where EntityTemplateType : BaseTemplate
{
   EntityTemplateType template;
   ...
}
 
public class Actor : Entity <ActorTemplate>
{
   ...
}

34. Don’t use strings for anything other than displayed text. In particular, do not use strings for identifying objects or prefabs etc. One unfortunate exception is animations, which generally are accessed with their string names.

35. Avoid using public index-coupled arrays. For instance, do not define an array of weapons, an array of bullets, and an array of particles , so that your code looks like this:

public void SelectWeapon(int index)
{ 
   currentWeaponIndex = index;
   Player.SwitchWeapon(weapons[currentWeapon]);
}
 
public void Shoot()
{
   Fire(bullets[currentWeapon]);
   FireParticles(particles[currentWeapon]);   
}

The problem for this is not so much in the code, but rather setting it up in the inspector without making mistakes.

Rather, define a class that encapsulates the three variables, and make an array of that:

[Serializable]
public class Weapon
{
   public GameObject prefab;
   public ParticleSystem particles;
   public Bullet bullet;
}

The code looks neater, but most importantly, it is harder to make mistakes in setting up the data in the inspector.

36. Avoid using arrays for structure other than sequences. For example, a player may have three types of attacks. Each uses the current weapon, but generates different bullets and different behaviour.

You may be tempted to dump the three bullets in an array, and then use this kind of logic:

public void FireAttack()
{
   /// behaviour
   Fire(bullets[0]);
}
 
public void IceAttack()
{
   /// behaviour
   Fire(bullets[1]);
}
 
public void WindAttack()
{
   /// behaviour
   Fire(bullets[2]);
}

Enums can make things look better in code…

public void WindAttack()
{
   /// behaviour
   Fire(bullets[WeaponType.Wind]);
}

…but not in the inspector.

It’s better to use separate variables so that the names help show which content to put in. Use a class to make it neat.

[Serializable]
public class Bullets
{
   public Bullet FireBullet;
   public Bullet IceBullet;
   public Bullet WindBullet;
}

This assumes there is no other Fire, Ice and Wind data.

37. Group data in serializable classes to make things neater in the inspector. Some entities may have dozens of tweakables. It can become a nightmare to find the right variable in the inspector. To make things easier, follow these steps:

  • Define separate classes for groups of variables. Make them public and serializable.
  • In the primary class, define public variables of each type defined as above.
  • Do not initialize these variables in Awake or Start; since they are serializable, Unity will take care of that.
  • You can specify defaults as before by assigning values in the definition;

This will group variables in collapsible units in the inspector, which is easier to manage.

[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. If you have a lot of story text, put it in a file. Don’t put it in fields for editing in the inspector. Make it easy to change without having to open the Unity editor, and especially without having to save the scene.

39. If you plan to localise, separate all your strings to one location. There are many ways to do this. One way is to define a Text class with a public string field for each string, with defaults set to English, for example. Other languages subclass this and re-initialize the fields with the language equivalents.

More sophisticated techniques (appropriate when the body of text is large and / or the number of languages is high) will read in a spread sheet and provide logic for selecting the right string based on the chosen language.

Testing and Debugging

40. Implement a graphical logger to debug physics, animation, and AI. This can make debugging considerably faster. See here.

41. Implement a HTML logger. In some cases, logging can still be useful. Having logs that are easier to parse (are colour coded, has multiple views, records screenshots) can make log-debugging much more pleasant. See here.

42. Implement your own FPS counter. Yup. No one knows what Unity’s FPS counter really measures, but it is not frame rate. Implement your own so that the number can correspond with intuition and visual inspection.

43. Implement shortcuts for taking screen shots. Many bugs are visual, and are much easier to report when you can take a picture. The ideal system should maintain a counter inPlayerPrefs so that successive screenshots are not overwritten. The screenshots should be saved outside the project folder to avoid people from accidentally committing them to the repository.

44. Implement shortcuts for printing the player’s world position. This makes it easy to report the position of bugs that occur in specific places in the world, which in turns makes it easier to debug.

45. Implement debug options for making testing easier. Some examples:

  • Unlock all items.
  • Disable enemies.
  • Disable GUI.
  • Make player invincible.
  • Disable all gameplay.

46. For teams that are small enough, make a prefab for each team member with debug options. Put a user identifier in a file that is not committed, and is read when the game is run. This why:

  • Team members do not commit their debug options by accident and affect everyone.
  • Changing debug options don’t change the scene.

47. Maintain a scene with all gameplay elements. For instance, a scene with all enemies, all objects you can interact with, etc. This makes it easy to test functionality without having to play too long.

48. Define constants for debug shortcut keys, and keep them in one place. Debug keys are not normally (or conveniently) processed in a single location like the rest of the game input. To avoid shortcut key collisions, define constants in a central place. An alternative is to process all keys in one place regardless of whether it is a debug function or not. (The downside is that this class may need extra references to objects just for this).

Documentation

49. Document your setup. Most documentation should be in the code, but certain things should be documented outside code. Making designers sift through code for setup is time-wasting. Documented setups improved efficiency (if the documents are current).

Document the following:

  • Layer uses (for collision, culling, and raycasting – essentially, what should be in what layer).
  • Tag uses.
  • GUI depths for layers (what should display over what).
  • Scene setup.
  • Idiom preferences.
  • Prefab structure.
  • Animation layers.

Naming Standard and Folder Structure

50. Follow a documented naming convention and folder structure. Consistent naming and folder structure makes it easier to find things, and to figure out what things are.

You will most probably want to create your own naming convention and folder structure. Here is one as an example.

Naming General Principles

  1. Call a thing what it is. A bird should be called Bird.
  2. Choose names that can be pronounced and remembered. If you make a Mayan game, do not name your level QuetzalcoatisReturn.
  3. Be consistent. When you choose a name, stick to it.
  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).
  5. Do not use version numbers, or words to indicate their progress (WIPfinal).
  6. Do not use abbreviations: DVamp@W should be DarkVampire@Walk.
  7. Use the terminology in the design document: if the document calls the die animationDie, then use DarkVampire@Die, not DarkVampire@Death.
  8. Keep the most specific descriptor on the left: DarkVampire, not VampireDark;PauseButton, not ButtonPaused. It is, for instance, easier to find the pause button in the inspector if not all buttons start with the word Button. [Many people prefer it the other way around, because that makes grouping more obvious visually. Names are not for grouping though, folders are. Names are to distinguish objects of the same type so that they can be located reliably and fast.]
  9. Some names form a sequence. Use numbers in these names, for example, PathNode0,PathNode1. Always start with 0, not 1.
  10. Do not use numbers for things that don’t form a sequence. For example, Bird0Bird1,Bird2 should be FlamingoEagleSwallow.
  11. Prefix temporary objects with a double underscore __Player_Backup.

Naming Different Aspects of the Same Thing

Use underscores between the core name, and the thing that describes the “aspect”. For instance:

  • GUI buttons states EnterButton_Active, EnterButton_Inactive
  • Textures DarkVampire_Diffuse, DarkVampire_Normalmap
  • Skybox JungleSky_Top, JungleSky_North
  • LOD Groups DarkVampire_LOD0, DarkVampire_LOD1

Do not use this convention just to distinguish between different types of items, for instance Rock_Small, Rock_Large should be SmallRock, LargeRock.

Structure

The organisation of your scenes, project folder, and script folder should follow a similar pattern.

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. Define a base class for all your editors

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. Use reflection and recursion to do draw components

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--;
}

The above method uses the following helper:

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;
}

Note that it uses an annotation in your class code to generate a tooltip in the inspector.

3. Define new Custom Editors

Unfortunately, you will still need to define a class for each MonoBehaviour. Fortunately, these definitions can be empty; all the actual work is done by the base class.

[CustomEditor(typeof(MyClass))]
public class MyClassEditor : BaseEditor<MyClass>
{}

In theory this step can be automated, but I have not tried it.

[출처] http://devmag.org.za/2012/07/12/50-tips-for-working-with-unity-best-practices/

Unity3D v.3.5.5f3 Pro / iOS Pro (with 2D ToolKit) 기준.


1. 이미지 자체 최적화


png 등의 이미지를 import 하면 압축된 용량이 아닌 원래 용량으로 인입됨.

아이폰의 경우 pvrtc로 압축하여 용량 줄일 수 있음.


256 x 256 size 이미지 크기 비교

 - RGBA 32bit : 256 kb

 - RGBA 16bit : 128 kb

 - RGBA Compressed PVRTC4 : 32 kb

 - RGBA Compressed PVRTC2 : 16 kb


texture type 설정

 - GUI/HUD 용도로 사용하는 이미지는 GUI로 설정 (이미지 품질이 가장 좋음)

 - 그 외는 Texture 혹은 Advanced 선택

   - Advanced는 Texture 설정에서 사용자가 옵션 선택 가능

   - Advanced에서 "Generate Mip maps" 설정을 해제하면 용량을 줄일 수 있다. 

    (2D에선 불필요. 게임 화면과 카메라와의 거리를 가깝게 잡은 경우 화질 개선에도 도움이 됨)


이미지 비율 조정

 - Unity3d에서는 한 변의 길이가 2의 배수인 정사각형으로 이미지를 사용하게 됨. (2, 4, 8, 16 …)

 - 압축할 때 이를 2의 배수인 정사각형으로 늘려주므로 정사각형 안에 들어가도록 사용할 것.

   - 9 x 9 이미지는 16 x 16 으로 늘어나게 됨.

   - 툴을 통해 이미지 병합시 만약 8 x 16 처럼 비율이 맞지 않을 경우 동일하게 16 x 16으로 늘어나므로

     8 x 8 두장 혹은 다른 이미지를 더 합쳐 16 x 16으로 만드는 것이 용량면에서는 좋음 (성능과 반비례)

   - 실제 1024 x 1024 altas를 PVRTC4로 압축시 용량은 0.5 mb정도 (2048 x 2048 : 2mb)


2. 이미지 화질과의 상관관계


개별 이미지를 atlas로 합치는 것이 성능 면에서는 도움이 됨.

 - but atlas size가 커질 경우 화질이 떨어질 수 있음. (정확한 원인은 파악 못함)

 - 개인적인 관점에서 1024정도가 적당한 듯. (2048 이상일 경우 화질 저하 정도가 꽤 큼)

 - 4096 사이즈의 경우는 iphone에서 이미지가 보이지 않음

    - Edit / Graphic Emulation 설정을 No emulation으로 설정할 경우 보인다는 글이 있었으나 적용해도 보이지 않아 실패함.


3. 용량 & 성능


이미지를 import하고 이를 atlas로 병합하는 과정을 거칠 경우 원본 이미지가 프로젝트에 포함되어 있을 수 있음.

 - 실제 build시 unity에서 사용하지 않는 이미지는 포함시키지 않게 되어 있으나

   Asset 폴더 내에 Resource 폴더가 있을 경우 Resource 폴더 내의 모든 내용은 강제로 프로젝트에 포함됨. (동적 로딩용)

 - 원본 이미지를 별도로 압축하지 않은 경우 앱 용량이 매우 커질 수 있음. =_=


iOS build 시 설정

 - Target Device : iPhone / iPad보다 iPhone 혹은 iPad 단독으로 설정

 - Target Platform : armv6 < armv7 < armv6 & 7

 - Api Compatibility Level : .NET 2.0 Subset < .NET 2.0

 - Stripping Level : Use micro mscorlib < Strip ByteCode < Strip Assemblies < Disabled

 - Script Call Optimization : Fast but no Exceptions < Slow and Safe


[출처] http://rnblues.blog.me/90151016867


작년 중순쯤에 인형뽑기 프로젝트 때문에 만들었던 티스토어 플러그인이다.

첨에는 이클립스 결합방식으로 하다가 뭔가 과정도 복잡하고 이래저래 손이 많이 가길래

유니티에서 플러그인 형식으로 사용할 수 있도록 만들었다.

(물론 이거 만드느라 개고생을 ㅠㅠ)


데브코리아 노하우란에 올려두었더니 여러 개발사에서 사용하는것으로 보아 

그럭저럭 동작은 잘 되는 듯 하다.


[사용 시 주의]

항상 티스토어 개발자 센터에서 최신 유니티용 라이브러리를 다운받아

assets 및 libs를 최신으로 유지해야한다.


===============================================================================


Tstore_Plugin.zip


Tstore_Plugin.zip 파일 내용 >

 
1) lib : 티스토어 결재 모듈 라이브러리
     - IAPLibD(Unity3D) : 개발버전 (테스트용)
     - IAPLibR(Unity3D).jar : 상용버전 (실제 서비스용)
2) assets : 티스토어 결재 모듈 리소스
2) eclipse : 안드로이드 플러그인 용 이클립스 프로젝트
3) unity : 유니티 테스트 프로젝트

사용방법 >
 
== 이클립스 프로젝트 셋팅
1) 이클립스 프로젝트를 열어 CustomUnityPlayerActivity.java의 패키지명을 자신의 유니티 패키지명과 동일하게 변경
2) 이클립스 프로젝트 설정을 열어 Java Build Path > 라이브러리 > Jar 추가 >
             - 테스트용이면 IAPLibD(Unity3D).jar 추가
             - 실제 서비스용이면IAPLibR(Unity3D).jar 추가
3) 프로젝트 > 내보내기 > Jar파일 > 내보낼 자원선택 (해당 프로젝트 체크 ), 생성된 클래스 파일 및 자원 보내기 체크 후 완료
 
== 유니티 프로젝트 셋팅
4) 유니티 프로젝트의 Plugins > Android >
             - 생성한 Java lib(jar) 복사
             - 이클립스 프로젝트의 AndroidManifest.xml 복사
             - 첨부된 파일 중 assets 폴더 복사
             - 첨부된 파일 중 lib > 개발 버전에 맞는 jar 복사



+ Recent posts