2018년 6월 28일 목요일

Unity Assert on Device


using System.Collections.Generic;

private readonly Dictionary<TYPE, IValue> _values = new Dictionary<TYPE, IValue>();

private void SetTable(TYPE type, IValue origin)
{
    Assert.IsFalse(_values.ContainsKey(type), type.ToString());
    _values.Add(type, origin);
}

public ITable GetTable(TYPE type)
{
    IValue value = null;
    Assert.IsTrue(_values.TryGetValue(type, out value));
    return value;
}



유니티에서 위의 코드를 사용하고 있었는데 지속적으로 핸드폰에서만 문제가 발생했다.

에디터에서는 재현이 되지 않아 로그를 추가하여 APK를 빌드하고 기기에 설치하여 Logcat 으로 로그를 확인해 보았다.

_values 에는 분명 값이 있고 _values.TryGetValue 를 사용하면 정상적인 값을 얻을 수 있었지만,  GetTable 함수를 사용하면 null 만 반환되는 문제였다.

몇 줄 되지 않는 코드라 대체 어떤것이 문제인지 도통 감을 잡을수가 없었다.

마지막이라는 심정으로 GetTable 함수의 Assert 를 제거하고 다시 APK를 빌드하여 핸드폰에서 실행하자 거짓말처럼 GetTable 함수가 정상적인 값을 반환했다.

대체 이해가 불가하여 옆자리 동료에게 물어보니 이유를 들을 수 있었다.

Assert 함수들의 소스 코드를 보면

[Conditional("UNITY_ASSERTIONS")]
public static void IsTrue(bool condition)
{
    Assert.IsTrue(condition, (string) null);
}

[Conditional("UNITY_ASSERTIONS")] 가 붙어있음을 확인 할 수 있다.

에디터에서 Assert 함수들이 잘 동작하는 이유는 유니티가 C# 프로젝트 파일을 만들때

속성에 위 스크린샷 처럼 UNITY_ASSERTIONS 속성을 강제로 넣어주기 때문이다.

APK에서는 UNITY_ASSERTIONS 속성이 정의되어 있지 않고, Assert 함수들은 동작하지 않게 된다.

게다가 GetTable 함수의 Assert 코드 위에 IValue value = null; 로 초기화까지 해 버렸다.(C++ 쓰던 습관으로)
아마 IValue value; 로 했었으면 APK 빌드 도중 컴파일 에러가 나서 더 빨리 문제를 찾을수 있었을 지도.

APK에서도 Assert 함수들을 사용하고 싶으면 UNITY_ASSERTIONS 디파인을
Player Settings -> Other Settings -> Scripting Define Symbols 에 추가하면 된다.

2018년 6월 26일 화요일

Android Studio Logcat 은 어디 있을까

안드로이드 스튜디오가 버전 업 되면서 최신 버전을 깔면 Logcat 이 나오질 않는다.
구글링을 해봐도 온통 Alt + 6 을 누르라는 글 뿐.

최신 스튜디오에서는 로그캣+Android Device Monitor 가 빠지고 Sdk 의 tool 로 옮겨 갔다 .
위치는

C:\Users\사용자이름\AppData\Local\Android\Sdk\tools\monitor.bat

위의 배치 파일을 실행하면 Logcat 이 포함되어 있는 Android Device Monitor 가 실행된다.

2018년 6월 22일 금요일

다운받은 AssetBundle 검증하기


  1. 아카마이에 올린 어셋 번들을 다운 받았을 때 cr and fl 문제가 발생해서 용량도 달라지고, 번들을 로딩하면 멈추거나 크래쉬가 발생함.
  2. 패치 서버로 부터 각 어셋번들 파일의 Hash와 CRC 정보를 제공받고 있으므로, 다운로드 후 .Net 코드를 이용하여 MD5해쉬와 CRC32 를 추출하여 패치서버의 AssetBundleManifest의 Hash, CRC와 비교했는데 같은 파일이라도 동일하지 않았음.
  3. 단순 용량 비교는 가능하지만 파일 변조를 검증할 수 없어서 검사하지 않음.
  4. 번들 파일만으로는 검증할 방법이 없어 Unity 의 AssetBundle.LoadFromFile 의 파라메터로 들어가는 Crc를 이용하기로 결정
  5. 패치 서버로 부터 받은 Crc 를 파라메터로 추가해서 LoadFromFile 함수를 호출하고, 바로 Unload(true) 를 호출 하도록 작업
  6. 변조된 번들 파일을 LoadFromFile 로 호출 해 보니(Crc 를 추가하여) 정확하게 에러로그와 함께 null을 뱉어 줌(Exception 이 아님).
  7. 번들파일 경로가 담긴 구조체를 List 에 넣어 Linq.Each 를 사용해서 LoadFromFile 을 호출 했을 때, 에러가 발생하면 Each 가 더 이상 동작하지 않아 for( 로 수정함

2018년 6월 21일 목요일

서버에서 DateTime을 문자열로 받는 경우


  1. 점검 시간 표시를 위해 서버에서 UTC 시간을 string 으로 받아오고 있었음.
  2. 서버에서 받은 string을 Local 로 변환하기 위해서 DateTime.Parse(문자열).ToLocalTime(); 을 사용하고 있었음.
  3. 서버 머신의 OS가 영문에서 한글로 바뀜
  4. 서버에서 주는 string의 내용이 6/21/2018 6:59:22 AM 에서 6/21/2018 오전 6:59:22 으로 변경됨
  5. DateTime.Parse 예외 발생


해결
  • 서버에서 ToString("yyyy-MM-dd HH:mm:ss") 으로 변경함.

폴더만 생성해도 IOException : Disk full 이 발생할 수 있다.


  1. 파이어베이스에서 IOException이 발생함
  2. 코드는 Directory.CreateDirectory인데 해당 기기에 40메가 정도 용량이 남은 상태
  3. try catch 로 Disk_Full 처리 함.
  4. https://stackoverflow.com/questions/9293227/how-to-check-if-ioexception-is-not-enough-disk-space-exception-type

TortoiseSVN - “is locked in another working copy” error

On Windows, I fixed the problem by the following steps:
1. Launch Repository Browser by clicking "Repo-browser" in the menu.
2. Locate the locked file.
3. Right click your mouse on the locked file.
4. Click "break lock".

https://stackoverflow.com/questions/21862134/svn-frequently-says-file-is-locked-by-me-in-another-working-copy

ADB로 메모리 확인

adb shell pidof com.xxx.xxx Pid 확인

adb shell dumpsys meminfo com.xxx.xxx 메모리 확인



PC에 기기가 두대 이상이면 안됨.

Unity Certificate 문제


  • 내부 번들 서버가 주소는 https 지만 인증서 기간이 만료되어 있던 상태에서 UnityWebRequest 를 사용하여 번들 패치 시스템을 만들어야 했음
  • www.google.co.kr은 접속이 되도 내부 번들 주소에 접근이 되지 않아서 삽질 중에 ServicePointManager.ServerCertificateValidationCallback = delegate { return true; }; 를 발견함
  • 비슷한 형식을 UnityWebRequest 에서도 발견함. public CertificateHandler certificateHandler
  • 유니티의 CertificateHandler 을 상속받은 클래스를 UnityWebRequest 의 certificateHandler에 넣어주면 끝
  • 에디터에서는 동작하나 APK에서는 동작하지 않음
  • 결국 주소를 https에서 http 로 변경함

Nox 디버깅


  1. Root 켜기
  2. 개발자 옵션, USB 디버깅 켜기
  3. 녹스가 켜진 상태에서 
C:\Program Files (x86)\Nox\bin>nox_adb.exe connect 127.0.0.1:62001 already connected to 127.0.0.1:62001 C:\Program Files (x86)\Nox\bin>nox_adb.exe devices List of devices attached 127.0.0.1:62001 device

NGUI Particle Clipping

중국 애들이 만든걸 주워옴

Shader "ParticlesAdditiveAreaClip"
{
Properties
    {
        _TintColor ("Tint Color", Color) = (0.5,0.5,0.5,0.5)
        _MainTex ("Particle Texture", 2D) = "white" {}
        _Area ("Area", Vector) = (0,0,1,1)
    }

    Category
    {
        Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" }
        Blend SrcAlpha One
        AlphaTest Greater .01
        ColorMask RGB
        Cull Off
        Lighting Off
        ZWrite Off
        Fog { Color (0,0,0,0) }

        SubShader
        {
            Pass
            {
                CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag
                #pragma multi_compile_particles

                #include "UnityCG.cginc"

                sampler2D _MainTex;
                fixed4 _TintColor;
                float4 _Area;

                struct appdata_t
                {
                    float4 vertex : POSITION;
                    fixed4 color : COLOR;
                    float2 texcoord : TEXCOORD0;
                };

                struct v2f
                {
                    float4 vertex : SV_POSITION;
                    fixed4 color : COLOR;
                    float2 texcoord : TEXCOORD0;
                    float2 worldPos : TEXCOORD1;
                };

                float4 _MainTex_ST;

                v2f vert (appdata_t v)
                {
                    v2f o;
                    o.vertex = UnityObjectToClipPos(v.vertex);
                    o.texcoord = TRANSFORM_TEX(v.texcoord,_MainTex);
                    o.color = v.color;
                    o.worldPos = mul(unity_ObjectToWorld, v.vertex).xy;
                    return o;
                }

                fixed4 frag (v2f i) : SV_Target
                {
                    bool inArea = i.worldPos.x >= _Area.x && i.worldPos.x <= _Area.z && i.worldPos.y >= _Area.y && i.worldPos.y <= _Area.w;
                    return inArea? 2.0f * i.color * _TintColor * tex2D(_MainTex, i.texcoord) : fixed4(0,0,0,0);
                }
                ENDCG
            }
        }
    }

}

using System;
using UnityEngine;

[RequireComponent(typeof(UIPanel))]
public class CustomUIClipper : MonoBehaviour
{
    private const string ShaderName = "ParticlesAdditiveAreaClip";
    private const float ClipInterval = 0.5f;

    private UIPanel m_targetPanel;
    private Shader m_shader;

    private void Start()
    {
        // find panel
        m_targetPanel = GetComponent<uipanel>();

        if (m_targetPanel == null)
            throw new ArgumentNullException("Cann't find the right UIPanel");
        if (m_targetPanel.clipping != UIDrawCall.Clipping.SoftClip)
            throw new InvalidOperationException("Don't need to clip");

        m_shader = Shader.Find(ShaderName);

        //if (!IsInvoking("Clip"))
        //    InvokeRepeating("Clip", 0, ClipInterval);

        Clip();
    }

    private Vector4 CalcClipArea()
    {
        var clipRegion = m_targetPanel.finalClipRegion;
        var nguiArea = new Vector4()
        {
            x = clipRegion.x - clipRegion.z / 2,
            y = clipRegion.y - clipRegion.w / 2,
            z = clipRegion.x + clipRegion.z / 2,
            w = clipRegion.y + clipRegion.w / 2
        };

        var uiRoot = m_targetPanel.root;
        var pos = m_targetPanel.transform.position;
        var rate1 = (float)Screen.width / (float)Screen.height;
        var rate2 = (float)uiRoot.manualWidth / (float)uiRoot.manualHeight;
        const float h = 2f;
        var w = h * rate1;
        var tempH = h / uiRoot.manualHeight;
        var tempW = w / uiRoot.manualWidth;
        var tempRate = Mathf.Max(tempW, tempH);
        if (rate1 < rate2)
        {
            tempRate = Mathf.Min(tempW, tempH);
        }

        var result =  new Vector4()
        {
            x = pos.x + nguiArea.x * tempRate,
            y = pos.y + nguiArea.y * tempRate,
            z = pos.x + nguiArea.z * tempRate,
            w = pos.y + nguiArea.w * tempRate
        };

        return result;
    }

    private void Clip()
    {
        var clipArea = CalcClipArea();
        var renderers = GetComponentsInChildren<renderer>();
        for (var i = 0; i < renderers.Length; ++i)
        {
            var mat = renderers[i].material;

            if (mat.shader.name != ShaderName)
                mat.shader = m_shader;

            mat.SetVector("_Area", clipArea);
        }
    }
}
네이버 블로그에서 좋은걸 발견함 http://naver.me/5ink6yfx

msb3482 에러

VS 2015와 VS 2017이 동시에 설치되어 있어서 2015를 삭제하자 발생한 문제.

체크가 꺼져 있었음

Visual Studio 업데이트가 느릴 때

제어판 -> 네트워크 연결 -> 우클릭 -> 속성 -> IPV6 Off -> 재부팅

https://stackoverflow.com/questions/44563536/visual-studio-2017-installer-extremely-slow?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa