레이블이 unity인 게시물을 표시합니다. 모든 게시물 표시
레이블이 unity인 게시물을 표시합니다. 모든 게시물 표시

2021년 3월 1일 월요일

[Unity] Firebase 6.15 를 사용한 프로젝트는 unity 2018 버젼을 사용해서 android export해야 되더라

 Firebase 6.15 를 사용한 프로젝트를 최신버젼에서 가져와서 돌려보려 했더니 안되더라, 

 unity 2018 버젼을 사용해서 android export해야 되더라


그리고 혹시모르니 external dependency manager 에서 android Resolver (먼제 Delete Resolved Libraries 하고) 해주면 좋음

2021년 2월 21일 일요일

[Unity] 실전 게임 만들기 강의 /한국/

좀비슈터, 미니RPG, 등 강좌 몇개 정리해놈. 

리듬게임(케이디)의 강좌도 정리해놈.

정리잘해논게 맘에듬

 https://ansohxxn.github.io/categories/unity-lesson-1

2021년 2월 20일 토요일

[unity] VisualGraph 3d

 https://m.blog.naver.com/PostView.nhn?blogId=hana100494&logNo=222223197400&targetKeyword=&targetRecommendationCode=1


잘정리되어 있다.



[Unity] 1300 자료./ 일본어 /알차다

 http://tsubakit1.hateblo.jp/

Unity

2021년 2월 17일 수요일

[Unity] GC Alloc를 발생시키지 않는 C# 코딩

 https://qiita.com/sapphire_al2o3/items/4f517523f50e0113af1f


Unity 코드를 쓸 때 GC Alloc가 발생하는 패턴을 조사했다. GC Alloc이 줄어들 패턴 good했지만, 코드가 알기 어려울 경우도 있으므로 반드시 개서를 할 필요는 없다.

Unity 2018.4에서 동작 확인

문자와 숫자의 연결

bad
int  i  =  123 ; 
string  s  =  "num_"  +  i ;
good
int  i  =  123 ; 
string  s  =  "num_"  +  i . ToString ();

string.Concat에 object로 전달되어 박스가되어 버리므로 문자열한다.

4 개의 문자열 연결

bad
string []  num  =  {  "0" ,  "1" ,  "2" ,  "3" ,  "4" ,  "5"  }; 
// 96byte 
s  =  num [ 0 ]; 
s  + =  num [ 1 ]; 
s  + =  num [ 2 ]; 
s  + =  num [ 3 ];
good
string []  num  =  {  "0" ,  "1" ,  "2" ,  "3" ,  "4" ,  "5"  }; 
// 34byte 
string  s  =  num [ 0 ]  +  num [ 1 ]  +  num [ 2 ]  +  num [ 3 ];

연결 할 때마다 새로운 문자열을 생성되는데, 4 개까지 연결라면 Concat (string, string, string, string)이 알려져 번에 연결 할 수있다.

루프에서 문자열 연결

good
var  sb  =  new  System . Text . StringBuilder (); 
for  ( int  i  =  0 ;  i  <  num . Length ;  i ++) 
{ 
    sb . Append ( num [ i ]); 
} 
string  s  =  sb . ToString ();

루프에서 많은 문자열 연결 할 수 있으면 한번씩 문자열이 생성되지 않도록 StringBuilder를 사용한다.

문자열 배열의 연결

good
string []  num  =  {  "0" ,  "1" ,  "2" ,  "3" ,  "4" ,  "5"  }; 
// 38byte 
string  s  =  string . Join ( "" ,  num );

문자열 배열을 연결하는 경우 Join을 사용한다.

string.Format

bad
string []  num  =  new  string [ 10 ]; 
for  ( int  i  =  0 ;  i  <  num . Length ;  i ++) 
{ 
    // 84byte 
    num [ i ]  =  string . Format ( "num_ {0}" ,  i ) ; 
}
good
string []  num  =  new  string [ 10 ]; 
for  ( int  i  =  0 ;  i  <  num . Length ;  i ++) 
{ 
    // 64byte 
    num [ i ]  =  $ "num_ { i . ToString ()} " ; 
}

string.Format보다 문자열 보간 ($ "")를 사용한다. 문자열 보간 쪽이 선명 인수가 문자열 만 있으면 간단한 문자열 연결됩니다 string.Format가 호출되지 않습니다.

StringBuilder에 숫자 추가

var  sb  =  new  System . Text . StringBuilder (); 
int  n  =  100 ; 
sb . Append ( n );

.NET Framework에서는 정수는 ToString되어 추가된다. ToString하지 않으 문자 단위로 추가 할 필요가있다.
https://gist.github.com/sapphire-al2o3/ba7d6a80836a2e5ee117abb4c3d75132

Enum 캐스트

bad
// 40byte 
EnumType  e  =  ( EnumType ) System . Enum . ToObject ( typeof ( EnumType ),  i ); 
// 40byte 
i  =  System . Convert . ToInt32 ( e );
good
// 0byte 
EnumType  e  =  ( EnumType ) i ; 
// 0byte 
i  =  ( int ) e ;

Enum을 Dictionary의 Key하지 (.NET 3.5)

bad
var  d  =  new  Dictionary < EnumType ,  int > ();
good
var  d  =  new  Dictionary < int ,  int > ();

.NET 4라면 박스하지 않기 때문에 Enum을 Key하고 접근해서 GC Alloc는 발생하지 않지만, 저속이므로 int를 Key로하는 것이 좋다.

코루틴 반환

bad
// 20byte 
yield  return  0 ;
good
// 0byte 
yield  return  null ;

0을 반환하는 경우, 박스 화하여 GC Alloc가 발생한다.

코루찐에서 foreach

bad
IEnumrator  Sum ( int []  array ) 
{ 
    int  s  =  0 ; 
    foreach  ( var  a  in  array ) 
    { 
        s  + =  a ; 
        yield  return  null ; 
    } 
}

int []  array  =  {  1 ,  2 ,  3 ,  4 ,  5  }; 
StartCoroutine ( Sum ());
good
IEnumrator  Sum ( int []  array ) 
{ 
    int  s  =  0 ; 
    for  ( int  i  =  0 ;  i  <  array . Length ;  i ++) 
    { 
        s  + =  a [ i ]; 
        yield  return  null ; 
    } 
}

int []  array  =  {  1 ,  2 ,  3 ,  4 ,  5  }; 
StartCoroutine ( Sum ());

foreach보다 for 쪽이 임시 변수가 적기 때문에 코루찐의 크기가 작아진다.

List의 크기 지정을

bad
List < int >  list  =  new  List < int > (); 
// 8.3Kbyte 
for  ( int  i  =  0 ;  i  <  1000 ;  i ++) 
{ 
    list . Add ( i ); 
}
good
List < int >  list  =  new  List < int > ( 1000 ); 
// 4.0Kbyte 
for  ( int  i  =  0 ;  i  <  1000 ;  i ++) 
{ 
    list . Add ( i ); 
}

List와 Dictionary 미리 추가하는 크기를 알고있는 경우 생성 할 때 Capacity를 지정한다.

배열 정렬

bad
int []  array  =  new  int [ 100 ]; 
// 10.9Kbyte 
for  ( int  i  =  0 ;  i  <  100 ;  i ++) 
{ 
    Array . Sort ( array ); 
}
good
int []  array  =  new  int [ 100 ]; 
// 112byte 
for  ( int  i  =  0 ;  i  <  100 ;  i ++) 
{ 
    Array . Sort ( array ,  ( x ,  y )  =>  x  -  y ); 
}

비록 int의 배열에도 람다 식을 지정하지 않으면 불필요한 캐스트가 발생 해 버린다. (.NET Core이라고 괜찮아)

Delegate

bad
void  Log ( int  value )  {  Debug . Log ( value );  }

void  Squared ( int  value ,  Action < void >  callback ) 
{ 
    int  v  =  value  *  2 ; 
    callback ( v ); 
}

int []  num  =  {  0 ,  1 ,  2 ,  3 ,  4  }; 
foreach  ( var  n  in  num ) 
{ 
    Squared ( n ,  Log ); 
}
good
void  Log ( int  value )  {  Debug . Log ( value );  }

void  Squared ( int  value ,  Action < void >  callback ) 
{ 
    int  v  =  value  *  2 ; 
    callback ( v ); 
}

int []  num  =  {  0 ,  1 ,  2 ,  3 ,  4  }; 
var  log  =  Log ; 
foreach  ( var  n  in  num ) 
{ 
    Squared ( n ,  log ); 
}

여러 번 호출 함수는 캐시한다.

람다 식의 범위

bad
List < int >  RemoveList ( List < int >  list ,  int  v ) 
{ 
    if  ( list  ! =  null  &&  list . Count  >  0 ) 
    { 
        return  list . FindAll ( x  =>  x  ! =  v ); 
    } 
    return  null ; 
}
good
List < int >  RemoveList ( List < int >  list ,  int  v ) 
{ 
    if  ( list  ! =  null  &&  list . Count  >  0 ) 
    { 
        int  t  =  v ; 
        return  list . FindAll ( x  =>  x  ! =  t ); 
    } 
    return  null ; 
}

list가 null의 경우는 아무것도하지 않지만 bad 쪽은 함수를 호출하는 것만으로 GC Alloc가 발생한다.
함수에 들어갈 때 인수를 캡처 한 람다 표현식이 생성되어 버리기 때문에 인수를 내부의 범위에 캐싱 캡처하면 범위에 들어 가지 않는 경우는 람다 식을 생성한다.

IList 루프

bad
IList  list  =  new  string [ 10 ]; 
foreach  ( var  e  in  list )  {}
good
IList  list  =  new  string [ 10 ]; 
for  ( int  i  =  0 ;  i  <  list . Count ;  i ++)  {}

인터페이스를 통해 foreach를 사용하면 박스가 발생하기 때문에 for를 사용한다.

멤버의 순서

bad
// 80byte 
class  C 
{ 
    byte  a ; 
    long  b ; 
    byte  c ; 
    long  d ; 
    byte  e ; 
    long  f ; 
    byte  g ; 
    long  h ; 
}
good
// 56byte 
class  C 
{ 
    long  b ; 
    long  d ; 
    long  f ; 
    long  h ; 
    byte  a ; 
    byte  c ; 
    byte  e ; 
    byte  g ; 
}

Unity는 class에도 자동으로 적절한 메모리 레이아웃이되지 않도록 때문에 불필요한 패딩이 들어 가지 않도록 멤버 정의의 순서를 고려한다.

인코딩

bad
string  s  =  "hoge" ; 
int  n  =  System . Text . Encoding . GetEncoding ( "UTF-8" ). GetByteCount ( s );
good
string  s  =  "hoge" ; 
int  n  =  System . Text . Encoding . UTF8 . GetByteCount ( s );

GetEncoding을 호출하면 매번 클래스가 생성되기 때문에 캐시되는 UTF8을 사용한다.

Unity의 API

Object.name

bad
for  ( int  i  =  0 ;  i  <  4 ;  i ++) 
{ 
    Debug . Log ( go . name ); 
}

name 속성에 액세스 할 때마다 GC Alloc가 발생하기 때문에 캐시한다.

Application *** Path

  • Application.persistentDataPath
  • Application.temporaryCachePath

등도 방문 할 때마다 문자열이 생성되어 버리므로 캐시한다.

Renderer.materials

bad
for  ( int  i  =  0 ;  i  <  renderer . materials . length ;  i ++) 
{ 
}
good
foreach  ( var  m  in  renderer . materials ) 
{ 
}

Renderer.materials 속성에 배열을 생성 해 돌려 있기 때문에 루프에서 매번 사용하지 않도록한다.

Input.touches

bad
// 80byte 
foreach  ( var  touch  in  Input . touches ) 
{ 
}
good
// 0byte 
for  ( int  i  =  0 ;  i  <  Input . touchCount ;  i ++) 
{ 
    var  touch  =  Input . GetTouch ( i ); 
}

이쪽도 배열을 생성 해 돌려 주므로 Input.GetTouch을 사용한다.

Animator.GetParameter

bad
for  ( int  i  =  0 ;  i  <  animator . parameterCount ;  i ++) 
{ 
    // animators.parameters [i] .name과 거의 같은 
    Debug . Log ( animator . GetParameter ( i ). name ); 
}
good
foreach  ( var  paramter  in  animator . parameters ) 
{ 
    Debug . Log ( paremter . name ); 
}

Animator.parameters배열을 반환 API이므로 캐시한다.
Animator.GetParameter치아 Animator.parameters를 내부에서 호출있을 뿐이므로 사용하지 않는다.

결과를 배열로 반환 API

good
void  GetComponentsInChildren  ( List < T >  results ) 
void  GetComponentsInChildren  ( List < T >  results ) 
void  GetComponentsInParent  ( bool  includeInactive ,  List < T >  results ) 
int  Physics . RaycastNonAlloc  (...  ,  RaycastHit []  results ,  ...) 
Animator . GetCurrentAnimatorClipInfo  ( int layerIndex ,  List < AnimatorClipInfo >  clips )

몇번이나 호출하는 경우 인수 배열이나 목록을 전달하고 결과를받는 함수를 사용한다.