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

카테고리

분류 전체보기 (2858)
Unity3D (897)
Programming (479)
Server (33)
Unreal (4)
Gamebryo (56)
Tip & Tech (192)
협업 (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

Edit -> Project Settings -> Player 들어가서 


Resolution and Presentation 에서 

Display Resolution dialog 에서 

[Disabled] = 실행시 설정창 비활성화 
[Enabled] = 실행시 설정창 활성화 
[Hidden By Default] = 평소에는 비활성화 Shift 누르면서 프로그램 실행시키면 설정창 활성화 입니다. 

참고 
http://openwiki.kr/unity/class-playersettings




반응형
Posted by blueasa
, |


[링크] http://blog.naver.com/PostList.nhn?blogId=dunkydonk&from=postList&categoryNo=18

반응형
Posted by blueasa
, |

ios는 상관없지만.. android의 경우 시간을 사용자 임의로 변경할 수 있기 때문에, 시간과 관련하여 컨텐츠의 변경이 필요할 경우 정확한 시간의 정보가 필요함.


이럴때, 미국 국립표준 연구소의 시간을 받아와서 사용하는 방법이 있다.


출처는 - http://stackoverflow.com/questions/6435099/how-to-get-datetime-from-the-internet 요기

실제 수정해서 사용한 코드는 다음과 같음.

TcpClient tcpClient = new TcpClient("time.nist.gov", 13);

StreamReader sr = new StreamReader(tcpClient.GetStream());

        

// 형태 57486 16-04-08 08:53:18 50 0 0 737.0 UTC(NIST) * 

string readData = sr.ReadToEnd();

// 형태 16-04-08 08:57:07

string _time = readData.Substring(readData.IndexOf(" ") + 1, 17);


// 대한민국은 UTC 기준 +9시간.

Datetime currentTime = Convert.ToDateTime(_time).AddHours(9);


// Debug.Log("현재 시간 : " + currentTime.ToString("yyyy-MM-dd HH:mm:ss"));




출처 : http://redccoma.tistory.com/128

반응형

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

[펌] OnApplicationFocus 와 OnApplicationPause 차이  (0) 2016.09.22
[펌] CUSTOM COROUTINES(Unity 5.3)  (0) 2016.07.21
[펌] 4. Coroutine 구현에 대해서 공부  (0) 2016.05.11
[펌] Root Motion in Legacy Animation  (0) 2016.03.28
BigNumber  (0) 2016.02.04
Posted by blueasa
, |

가장 처음 만들었던 게임을 만들고 난 후에 가장 아쉬운 부분은 보안적인 처리가 취약했다는거.

(취약이라기 보다는 전무...)

이번 프로젝트를 진행하면서 내 자신이 그래도 어느정도는 만족할 만한 보안처리(클라이언트 기준)를 하고싶었고,
그래서 나온 결과물이

- PlayerPrefs 암호화 (사용자의 임의적인 값 변경을 해도 영향을 받지 않도록)
- 변수 암호화 (메모리 주소 스캔이 어렵도록)
- 클라이언트와 서버간 통신중 주고받는 데이터의 암호화

이중 2개는 처리했고 남은 이슈는 3번째 이슈인데 이것도 내일 아마 처리될듯 하다.

첫번째 암호화에 대해서는 이전 포스팅으로 기록을 남겼고,
이번 포스팅에서는 기본 변수를 어떻게 저장해야 메모리 스캔을 어렵도록 할수 있을까에 대한 포스팅이다.


아는 게 너무 없어서 이것저것 서칭해본 결과 아래의 클래스를 프로젝트에 만들어 두었다.

using System;

using System.Collections;

using System.Security.Cryptography;

using System.Text;


public static class SecureConverter

{

    private static int xorCode = (UnityEngine.Random.Range(0, 10000) + UnityEngine.Random.Range(0, 10000) + UnityEngine.Random.Range(0, 10000)).GetHashCode();

        

    public static int SecureInt(int data)

    {

        return  data ^ xorCode;

    }


    public static string SecureString(string data)

    {

        string newText = "";


        for (int i = 0; i < data.Length; i++)

        {

            int charValue = Convert.ToInt32(data[i]); //get the ASCII value of the character

            charValue ^= xorCode; //xor the value


            newText += char.ConvertFromUtf32(charValue); //convert back to string

        }


        return newText;

    }

}


사용은 아래처럼 저장할 변수와 프로퍼티를 관리하는 스크립트에 만들어 두고 프로퍼티로 접근하면 자동으로 암호화된 값이 저장되고, 불러올때는 복호화해서 불러오기 때문에 원래의 데이터 그대로를 사용할 수 있게 해두었다.

private int _abc;
public int abc { get { return SecureConvert.SecureInt(_abc); } set { _abc = SecureConvert.SecureInt(value); } }

어디서든 abc = 300; 과 같은 형태로 저장하면 실제 암호화된 형태로 저장되기 때문에 원시적인 데이터 검색만으로는 실제 메모리 주소를 찾기가 어렵게 된다.


추신. MD5 나 SHA1 같은 것으로 대체하면 더욱 좋을듯. 이는 추후 포스팅~



출처 : http://redccoma.tistory.com/114

반응형
Posted by blueasa
, |

아무런 처리 없이 PlayerPrefs를 사용할 경우 값이 그대로 노출되면서 접근이 가능하고, 프로그램 밖에서 값을 수정할 경우 수정된 값 그대로 프로그램에 적용되는 문제점이 있음.


그런고로 1차적으로 어느정도는 암호화해야 하는데..구글링 한 결과 아주 만족스럽지는 않지만 적어도 원하지 않는 값으로 프로그램에 적용되는 것을 막아주도록 작성된 내용을 유니티포럼에서 찾아 포스팅한다..

(출처 - http://forum.unity3d.com/threads/playerprefs-encryption.26437/)


아래 코드의 포인트는 테스트해본 결과 실제 저장되는 PlayerPrefs의 값은 원본 그대로 저장된다. 노출자체는 된다는 얘기지. 다만, 직접 해당 유저가 해당 값을 수정하면 CheckEncryption함수를 통과하지 못해 DEFAULT 값으로 처리되면서, 기존에 저장되어 있던 PlayerPrefs의 해당 키(3개)는 모두 삭제처리된다.


using UnityEngine;

using System.Collections;

using System.Security.Cryptography;

using System.Text;

 

public class EncryptedPlayerPrefs  {

 

    // Encrypted PlayerPrefs

    // Written by Sven Magnus

    // MD5 code by Matthew Wegner (from [url]http://www.unifycommunity.com/wiki/index.php?title=MD5[/url])

    

    

    // Modify this key in this file :

    private static string privateKey="9ETrEsWaFRach3gexaDr";

    

    // Add some values to this array before using EncryptedPlayerPrefs

    public static string[] keys;

    

    

    public static string Md5(string strToEncrypt) {

        UTF8Encoding ue = new UTF8Encoding();

        byte[] bytes = ue.GetBytes(strToEncrypt);

 

        MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();

        byte[] hashBytes = md5.ComputeHash(bytes);

 

        string hashString = "";

 

        for (int i = 0; i < hashBytes.Length; i++) {

            hashString += System.Convert.ToString(hashBytes[i], 16).PadLeft(2, '0');

        }

 

        return hashString.PadLeft(32, '0');

    }

    

    public static void SaveEncryption(string key, string type, string value) {

        int keyIndex = (int)Mathf.Floor(Random.value * keys.Length);

        string secretKey = keys[keyIndex];

        string check = Md5(type + "_" + privateKey + "_" + secretKey + "_" + value);

        PlayerPrefs.SetString(key + "_encryption_check", check);

        PlayerPrefs.SetInt(key + "_used_key", keyIndex);

    }

    

    public static bool CheckEncryption(string key, string type, string value) {

        int keyIndex = PlayerPrefs.GetInt(key + "_used_key");

        string secretKey = keys[keyIndex];

        string check = Md5(type + "_" + privateKey + "_" + secretKey + "_" + value);

        if(!PlayerPrefs.HasKey(key + "_encryption_check")) return false;

        string storedCheck = PlayerPrefs.GetString(key + "_encryption_check");

        return storedCheck == check;

    }

    

    public static void SetInt(string key, int value) {

        PlayerPrefs.SetInt(key, value);

        SaveEncryption(key, "int", value.ToString());

    }

    

    public static void SetFloat(string key, float value) {

        PlayerPrefs.SetFloat(key, value);

        SaveEncryption(key, "float", Mathf.Floor(value*1000).ToString());

    }

    

    public static void SetString(string key, string value) {

        PlayerPrefs.SetString(key, value);

        SaveEncryption(key, "string", value);

    }

    

    public static int GetInt(string key) {

        return GetInt(key, 0);

    }

    

    public static float GetFloat(string key) {

        return GetFloat(key, 0f);

    }

    

    public static string GetString(string key) {

        return GetString(key, "");

    }

    

    public static int GetInt(string key,int defaultValue) {

        int value = PlayerPrefs.GetInt(key);

        if(!CheckEncryption(key, "int", value.ToString())) return defaultValue;

        return value;

    }

    

    public static float GetFloat(string key, float defaultValue) {

        float value = PlayerPrefs.GetFloat(key);

        if(!CheckEncryption(key, "float", Mathf.Floor(value*1000).ToString())) return defaultValue;

        return value;

    }

    

    public static string GetString(string key, string defaultValue) {

        string value = PlayerPrefs.GetString(key);

        if(!CheckEncryption(key, "string", value)) return defaultValue;

        return value;

    }

    

    public static bool HasKey(string key) {

        return PlayerPrefs.HasKey(key);

    }

    

    public static void DeleteKey(string key) {

        PlayerPrefs.DeleteKey(key);

        PlayerPrefs.DeleteKey(key + "_encryption_check");

        PlayerPrefs.DeleteKey(key + "_used_key");

    }

    

}


사용법은 PlayerPrefs의 사용법과 동일하다. (아래의 암호화를 위한 키를 추가한 후 사용하면 된다.) 

  EncryptedPlayerPrefs.keys=new string[5];


        EncryptedPlayerPrefs.keys[0]="23Wrudre";

        EncryptedPlayerPrefs.keys[1]="SP9DupHa";

        EncryptedPlayerPrefs.keys[2]="frA5rAS3";

        EncryptedPlayerPrefs.keys[3]="tHat2epr";

        EncryptedPlayerPrefs.keys[4]="jaw3eDAs";

사용시 PlayerPrefs 클래스 대신 EncryptedPlayerPrefs을 사용하면 된다. 키값 전부 지워주는 것은 기존대로 PlayerPrefs꺼 가져다가 쓰면 됨. 추후 빌드시 난독화 과정을 거치면 어느정도 보안처리를 했다고 볼수 있겠지. 


---- 10월 27일 추가.


위의 클래스를 사용하면 값이 그대로 보이기때문에, 이러한 문제를 해결하기위해서는 PlayerPrefs를 약간 가공해서 쓰는것도 한가지 방법이다.

특정 시크릿키를 사용해 Int일 경우 String으로 변환해서 변환된 문자열을 암호화하여 PlayerPrefs.SetString으로 저장하고 불러올때는 복호화 후 Convert 등을 이용해 다시 int 로 바꾸어 호출해 사용하는 방법이 좋아보인다.


이와 관련하여 암/복호화를 특정키를 사용하여 쉽게 할 수 있도록 하는 클래스는 검색하면 어느정도 나오니 그것을 이용해 구현하는 것이 적합.


---- 2015.06.17

http://ikpil.com/1342



출처 : http://redccoma.tistory.com/113

반응형
Posted by blueasa
, |

Android 나 PC 의 유니티에서 WWW 를 통해 파일을 받을 경우 IOS 는 해당 컨텐츠 헤더에 있는 값으로 자동으로 캐싱을 진행해 준다.

너무나 완벽한 OS 라서 문제가 생긴다..


아래 두가지 경우의 파일이 있다.



< Cache-Control 이 설정되어 있는 경우 >



< Cache-Control 이 없는 경우 >



Cache-Control 이 있는경우 IOS 에서는 해당 파일 캐싱을 진짜로 구현해 준다. ( 원래 이게 당연한 거지만.. )

따라서 저 만료 날자가 되기 전에 다시 같은 주소로 요청을 하면 새로 받아오지 않고 이전 캐싱된 데이터를 준다.


이 경우 가장 간단하게 해결하는 방법은 주소뒤에 의미없는 값을 추가하여 캐싱되지 않는 것처럼 꾸미는 것이다.

WWW( "http://www.naver.com/file.json?t=213948712" );

그러나 이 경우 클라이언트나 CDN 등에 쓸데없는 캐싱이 생길 수 있으며, 깔끔하지 않다.


가능한 Request 에서 요청 헤더에 다음과 같이 캐싱을 하지 않겠다고 추가하는것이 좋다.

WWWForm form = new WWWForm();

form.AddField( "Cache-Control", "no-cache" );




출처 : http://flystone.tistory.com/195

반응형
Posted by blueasa
, |


[사진 : 서브웨이 서퍼 플레이 스크린샷]



출처 : http://answers.unity3d.com/questions/288835/how-to-make-plane-look-curved.html

http://devkorea.co.kr/bbs/board.php?bo_table=m03_lecture&wr_id=3315&page=0&sca=&sfl=&stx=&sst=&sod=&spt=0&page=0&currentId=42


위의 사진처럼 땅이 평평하지 않고 휘어지는 듯한 효과를 연출하기 위함이다. 

땅 자체를 곡선으로 만들수도 있겠지만 

카메라가 보는 부분의 렌더링을 수정함으로써 효과를 낼 수 있다.



BendTheWorldShader.unitypackage


위의 파일은 예제 파일이며, 

https://dl.dropboxusercontent.com/u/7761356/UnityAnswers/Web/BendTheWorld/WebPlayer.html

웹에서도 실행을 해볼 수 있다.



적용 방법 : 


위의 예제파일이나, 웹에서 보듯이 shader프로그래밍을 하면 된다.

shader 파일을 생성한다.



shader 파일에 아래와 같이 코드를 입력한다.


접기


Shader "Custom/Curved" {

Properties {

_MainTex ("Base (RGB)"2D) = "white" {}

_QOffset ("Offset"Vector) = (0,0,0,0)

_Dist ("Distance"Float) = 100.0

}

SubShader {

Tags { "RenderType"="Opaque" }

Pass

{

CGPROGRAM

#pragma vertex vert

#pragma fragment frag

#include "UnityCG.cginc"


            sampler2D _MainTex;

float4 _QOffset;

float _Dist;

struct v2f {

    float4 pos : SV_POSITION;

    float4 uv : TEXCOORD0;

} ;

v2f vert (appdata_base v)

{

    v2f o;

    // The vertex position in view-space (camera space)

    float4 vPos = mul (UNITY_MATRIX_MV, v.vertex);

    

    // Get distance from camera and scale it down with the global _Dist parameter

    float zOff = vPos.z/_Dist;

    // Add the offset with a quadratic curve to the vertex position

    vPos += _QOffset*zOff*zOff;

    

    o.pos = mul (UNITY_MATRIX_P, vPos);

    o.uv = mul( UNITY_MATRIX_TEXTURE0, v.texcoord );

    return o;

}


half4 frag (v2f i) : COLOR

{

    half4 col = tex2D(_MainTex, i.uv.xy);

    return col;

}

ENDCG

}

}

FallBack "Diffuse"

}






작성한 shader 파일을 texture의 shader로 적용한다.





그러면 카메라의 위치에서 멀리 있는 객체일수록 z축의 아래로 쳐져 있는 듯한 효과를 나타낼 수 있다. 




이제, 휘어지게 보여질 객체에 스크립트에서 처리해야 한다



mOffset =  new Vector2(0.0f,-1.8f)

        mModelsRenderer = mModel.GetComponentsInChildren<Renderer>();

mModelsMeshFilter = mModel.GetComponentsInChildren<MeshFilter>();

foreachRenderer render in mModelsRenderer )

render.material.SetVector("_QOffset", mOffset );

foreachMeshFilter meshfilter in mModelsMeshFilter )

meshfilter.mesh.bounds = new BoundsVector3.zero, Vector3.one * 3000 );



render.material.SetVector("_QOffset", mOffset )

 는 휘어지는 방향과 크기를 정하며


meshfilter.mesh.bounds = new BoundsVector3.zero, Vector3.one * 3000 );

는 mesh범위를 키워 카메라에서 멀리 있어도 휘어지는 효과 그대로 화면에 보이도록 함이다.




출처 : http://jenemia.tistory.com/258

반응형
Posted by blueasa
, |

유니티 5.3.1 f1 로 버전업 했더니, NGUI 에서 범상치 않은 워닝을 내뱉었다.


1
2
3
Assets/NGUI/Examples/Scripts/Other/LoadLevelOnClick.cs(15,37): 
warning CS0618: `UnityEngine.Application.LoadLevel(string)' is obsolete: 
`Use SceneManager.LoadScene'

cs



Application.LoadLevel 로 씬 이동 하던 것을 SceneManager.LoadScene 으로 사용하란다.


그래서 유니티의 바람대로 SceneManager.LoadScene 으로 고쳐썼더니 네임스페이스가 없다고 한다....


검색해보니 UnityEngine.SceneManagement 라는 네임 스페이스가 추가된 듯.. 



1
using UnityEngine.SceneManagement;
cs


위와 같이 네임스페이스를 추가하니 정상적으로 작동한다.




알고보니 유니티 5.3에서 멀티 씬 편집(Multi-Scene Editing) 을 지원한다고 한다.


아래와 같이 프로젝트 뷰 에서 오른쪽 클릭을 한 후, Open Scene Additive 를 클릭하면...



▼ 이렇게 하이라키 뷰에 여러 씬이 열린다.





또한 SceneManager 추가되었고, 덕분에 자주쓰는 메소드들도 거의 다 구식이 되어버렸다.


변경점은 다음과 같다. 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
// old
Application.LoadLevel(0);                                       // 로드. 
Application.LoadLevel("SceneName");
AsyncOperation ao = Application.LoadLevelAsync(0);              // 로드. (비동기)
AsyncOperation ao = Application.LoadLevelAsync("SceneName");
Application.LoadLevelAdditive(0);                               // 씬 병합 추가. 
Application.LoadLevelAdditive("SceneName");
Application.LoadLevelAdditiveAsync(0);                          // 씬 병합 추가. (비동기)
Application.LoadLevelAdditiveAsync("SceneName");
Application.UnloadLevel(0);                                     // 언로드. 
Application.UnloadLevel("SceneName");
Application.levelCount;                                // BuildSetting 에 등록 된 씬 개수. 
Application.loadedLevel;                                        // 현재 씬 인덱스. 
Application.loadedLevelName;                                    // 현재 씬 이름. 
 
// 5.3
SceneManager.LoadScene(0);                                      // 로드. 
SceneManager.LoadScene("SceneName");
AsyncOperation ao = SceneManager.LoadSceneAsync(0);             // 로드. (비동기)
AsyncOperation ao = SceneManager.LoadSceneAsync("SceneName");
SceneManager.LoadScene(0, LoadSceneMode.Additive);              // 씬 병합 추가. 
SceneManager.LoadScene("SceneName", LoadSceneMode.Additive);
SceneManager.LoadSceneAsync(0, LoadSceneMode.Additive);         // 씬 병합 추가. (비동기)
SceneManager.LoadSceneAsync("SceneName", LoadSceneMode.Additive);
SceneManager.UnloadScene(0);                                    // 언로드. 
SceneManager.UnloadScene("SceneName");
SceneManager.sceneCount;                                        // 현재 로드 된 씬 개수. 
SceneManager.sceneCountInBuildSettings;                // BuildSetting 에 등록 된 씬 개수. 
SceneManager.GetActiveScene().buildIndex;                       // 현재 씬 인덱스. 
SceneManager.GetActiveScene().name;                             // 현재 씬 이름. 
 
// 씬 정보 조회. 
Scene activeScene = SceneManager.GetActiveScene();
Scene scene1 = SceneManager.GetSceneAt(0);
Scene scene2 = SceneManager.GetSceneByName("SceneName");
Scene scene3 = SceneManager.GetSceneByPath("Assets/SceneName.unity");
Scene[] loadedScenes = SceneManager.GetAllScenes();
 
// Scene 구조체. 
int buildIndex;
string name;
string path;
bool isLoaded;
bool isDirty;       // 씬을 변경(수정)했는지 여부. 
int rootCount;      // 씬의 Root에 있는 GameObject 개수. 
bool IsValid();     // 유효한 씬인지 여부. 
 
// 기타. 
Scene scene = gameObject.scene;                 // 게임오브젝트가 속해있는 씬을 가져오기. 
GameObject go = new GameObject("New Object");   // 게임오브젝트를 생성하면 현재 씬에 추가 됨. 
SceneManager.MoveGameObjectToScene(go, scene);      // 게임오브젝트를 다른 씬으로 이동. 
SceneManager.MergeScenes(sourceScene, destinationScene);    // 씬을 병합. 
 
// SceneManager.Get~() 으로 가져올 수 있는 것은 로드가 끝난 씬만 가능. 
Scene scene = SceneManager.GetSceneByName("SceneName");
bool isValid = scene.IsValid();     // false 가 리턴 됨.
cs



정리하다보니 꽤 많네;;;


아, 그리고 DontDestroyOnLoad 메소드 비스므리 한 것은 아예 없었는데,


Tips 를 참조해 보면, DontDestroyOnLoad 의 사용은 피하는 것이 좋고, Manager 씬을 만들어서 병합하는 것이 좋다고 한다.


앞으로 개발 시 참고해야 할 사항인 것 같다.



PS. Unity 5.3 에서는 과금 플러그인을 이용할 필요가 없을 수도 있겠다.


Unity Service Tab 에서 여러가지를(Unity Ads, Analytics, Cloud Build, In-App Purchasing, Multiplayer) 지원함..






출처 : http://worksp.tistory.com/10

반응형
Posted by blueasa
, |

[파일]


Albatross-Programming-Unity-Unity로 다수의 인원이 개발할 때 알아두면 좋은 9가지.pdf


반응형
Posted by blueasa
, |


[파일]


인게임_광고의_올바른_방법(UnityAds).pdf


반응형
Posted by blueasa
, |