반응형

간혹 빌드가 끝나고나서 특정 이미지를 교체해야 하거나 영상을 바꿔야 되면 해당 비디오 파일 또는 이미지 파일을 교체 후 새롭게 빌드를 시켜야 하는 번거러운 작업을 해야할 경우가 생긴다. 

교체해야할 상황이 빈번하다고 판단되는 프로젝트일 경우 특정 폴더에 교체 할 소스 파일을 넣어두고 해당 소스파일만 바꾸면 새로 빌드할 필요가 없게 미리 처리해 두면 될 것이다. 

using System.Collections.Generic;
using System.IO;
using UnityEngine;

public class FileItemHandler : MonoBehaviour {
    public string ParentFolderName;
    public string TargetFolderName;
    public List<string> FilePathList;
    public List<string> CoverPathList;
    public List<Texture2D> CoverSpriteList;
    string filepath;
    void Awake () { GetPathList (); }

    void GetPathList () {
        string _path = "";

        //타켓 폴더 패스 설정
        if (Application.platform == RuntimePlatform.Android) {
            //android일 경우 //
            _path = AndroidRootPath () + "Download/FH/" + ParentFolderName + "/" + TargetFolderName;
        } else {
            //unity일 경우 //
            _path = System.Environment.GetFolderPath (System.Environment.SpecialFolder.Personal) + "/Desktop/FH/" + ParentFolderName + "/" + TargetFolderName;
        }

        DirectoryInfo Folder = new DirectoryInfo (_path);

        //각 비디오의 패스(URL) 리스트 만들기
        foreach (var file in Folder.GetFiles ()) {
            if (file.Extension != ".meta" && file.Extension != ".DS_Store") { //비디오 이외의 확장자를 가진 파일 제외시키기
                filepath = _path + "/" + file.Name;
                if (!filepath.Contains ("._")) { //파일명 에러 수정해주기
                    // filepath = filepath.Replace ("._", "");
                    if (filepath.Contains (".mp4")) //비디오 파일 add 리스트
                        FilePathList.Add (filepath);
                    else if (filepath.Contains (".jpg")) { //커버이미지 파일 add 리스트
                        CoverPathList.Add (filepath);
                        Texture2D tex = null;
                        byte[] filedata;
                        if (File.Exists (filepath)) {
                            filedata = File.ReadAllBytes (filepath);
                            tex = new Texture2D (2, 2);
                            tex.LoadImage (filedata);
                            // Sprite sp = SpriteFromTexture2D (tex);
                            CoverSpriteList.Add (tex);
                        }
                    }
                }
            }
        }
        Debug.Log(ParentFolderName + "/" + TargetFolderName + ", FileCount : " + FilePathList.Count+ ",, SpriteCount : " + CoverSpriteList.Count);
    }

    string AndroidRootPath () {
        string[] temp = (Application.persistentDataPath.Replace ("Android", "")).Split (new string[] { "//" }, System.StringSplitOptions.None);
        return (temp[0] + "/");
    }

    Sprite SpriteFromTexture2D (Texture2D texture) {
        return Sprite.Create (texture, new Rect (0.0f, 0.0f, texture.width, texture.height), new Vector2 (0.5f, 0.5f), 100.0f);
    }
}

 

 

반응형
반응형

아무 게임오브젝트에 해당 스크립트를 붙이면 Debug가 출력 될 때 화면에 똑같이 출력해준다.

에디터에서는 필요없겠지만 기기에서 테스트할 때 편리하다.

 

using System.Collections;
using UnityEngine;

public class DebugToScreen : MonoBehaviour {
 	string myLog;
 	Queue myLogQueue = new Queue ();

 	void OnEnable () {
 		Application.logMessageReceived += HandleLog;
 	}

 	void OnDisable () {
 		Application.logMessageReceived -= HandleLog;
 	}

 	void HandleLog (string logString, string stackTrace, LogType type) {
 		myLog = logString;
 		string newString = "\n [" + type + "] : " + myLog;
 		myLogQueue.Enqueue (newString);
 		if (type == LogType.Exception) {
 			newString = "\n" + stackTrace;
 			myLogQueue.Enqueue (newString);
 		}
 		myLog = string.Empty;
 		foreach (string mylog in myLogQueue) {
 			myLog += mylog;
 		}
 	}

 	void OnGUI () {
 		GUILayout.Label (myLog);
	}
}

 

반응형
반응형

핸드폰에서는 자이로센서를 사용하면 되겠지만...

고정되어있는 디바이스에서 화면에 공간적인 느낌을 낼 수 있는 방법중 하나가 카메라같은 센서를 사용하여 사람을 인식 한 후 위치를 가져와 화면을 움직을 수 있을 것이다. 대표적인 센서로는 키넥트나 인텔에서 나온 리얼센스카메라를 사용하면 되겠으나 그렇게 되면 배보다 배꼽이 더 커지는게 아닐까 싶다. 소프트웨어로 처리할 수 있는 기술도 있는데 대표적인게 Open CV라는 녀석인듯 하다. 근데 이놈도 유니티에서 사용하려니 95달러나... 다른 경로를 통해 검색해 보니 좀 낮은 버전을 얻을 수 있었다.

 

하지만 테스트 해본 결과 얼굴인식이 그다지 잘 되지는 않는것 같다. 간혹 얼굴을 측면으로 돌린다거나 멀어지면 인식이 되지 않는 경우가 많다. 그래서 고민하다 화면 전체의 이전 프레임과 현재프레임의 픽셀을 비교하고 달라지는 픽셀을 검출하는 방식으로 달라지는 픽셀들의 위치값의 평균을 가져오면 얼굴인식처럼 사람위치를 트래킹할 수 있겠다는 생각을 해보았다.

 

픽셀의 위치값을 가져오기 위해 해당 픽섹의 위치에 GameObject를 만들어야 하는데 해상도 만큼 만들게 되면 기기가 뻗을 수도 있다. 웹캠 해상도가 1280x720이면 921,600개를 만들어야 하고 매프레임마다 변화값을 체크해야하니 무리가 될 수 밖에 없겠다.

그래서 적당히 픽셀을 건너뛰고 오브젝트를 만들었다. 아래 코드는 20픽셀씩 스킵하였다. 기기가 조금 뜨거워 지는듯 하지만 적당히 잘 돌아간다.

 

아래코드는  웹캠을 통해 화면을 받아오고 픽셀을 검출해서 비교하기 위한 코드이다.

using System.Collections;
using System.Collections.Generic;
using DG.Tweening;
using UnityEngine;
using UnityEngine.UI;

public class WebCamTracker : MonoBehaviour {
	WebCamTexture webCamTexture;
	WebCamDevice webCamDevice;
	public RawImage rawImage;
	int width = 1280, height = 720;

	[SerializeField]
	bool isCamOn;
	[SerializeField]
	GameObject Pref;

	static List<Color> NowColor;
	List<PixelHandler> ListGameObj, ListOn;
	public RectTransform RT_Pos;
	public Transform T_Text, T_Head;
	public TextMesh[] ArrText;
	public Slider Slider;
	public float Value;
	public Text Sensitivity;

	int skip = 20;
	Vector2 LastPos;
	float CutOffMaxPixelCount = 1000;

	void Start () {
		if (Application.platform == RuntimePlatform.Android)
			webCamDevice = WebCamTexture.devices[1];
		else
			webCamDevice = WebCamTexture.devices[0];
		webCamTexture = new WebCamTexture (webCamDevice.name, width, height);
		rawImage.texture = webCamTexture;
		webCamTexture.Play ();
		rawImage.GetComponent<RectTransform> ().SetSizeWithCurrentAnchors (RectTransform.Axis.Horizontal, width);
		rawImage.GetComponent<RectTransform> ().SetSizeWithCurrentAnchors (RectTransform.Axis.Vertical, height);
		NowColor = new List<Color> ();
		makePixels ();
		Slider.onValueChanged.AddListener (ValueChange);
		ValueChange (Slider.value);
	}
	void ValueChange (float value) {
		Value = (value * CutOffMaxPixelCount);
		Value = Mathf.Round (Value);
		Sensitivity.text = "Cut Off Pixel : " + Value.ToString ();
	}
	void makePixels () {
		ListGameObj = new List<PixelHandler> ();
		ListOn = new List<PixelHandler> ();
		for (int y = 0; y < height / skip; y++) {
			for (int x = 0; x < width / skip; x++) {
				GameObject go = Instantiate (Pref);
				go.SetActive (true);
				go.transform.SetParent (Pref.transform.parent);
				go.transform.localScale = new Vector3 (1f, 1f, 1f);
				go.transform.localPosition = new Vector2 (x * skip - width / 2 + skip - 10, y * skip - height / 2 + 30);
				go.GetComponent<PixelHandler> ().CG.alpha = 0.5f;
				ListGameObj.Add (go.GetComponent<PixelHandler> ());
			}
		}
		Debug.Log ("make ListGameObj");
	}

	static float posX, posY;

	IEnumerator ComparePixel () {
		yield return new WaitForSeconds (0.1f);

		NowColor.Clear ();
		for (int y = 0; y < webCamTexture.height / skip; y++) {
			for (int x = 0; x < webCamTexture.width / skip; x++) {
				NowColor.Add (webCamTexture.GetPixel (x * skip, y * skip));
			}
		}

		ListOn.Clear ();
		for (int i = 0; i < ListGameObj.Count; i++) {
			ListGameObj[i].Img.color = NowColor[i];

			if (ListGameObj[i].isOn)
				ListOn.Add (ListGameObj[i]);

			if (ListGameObj[i].isOn) {
				ListGameObj[i].RT.DOScale (1f, 0.2f).SetEase (Ease.Linear);
				ListGameObj[i].RT.DOLocalRotate (new Vector3 (0, 0, 45), 0.2f).SetEase (Ease.Linear);
			} else {
				ListGameObj[i].RT.DOScale (0f, 0.2f).SetEase (Ease.Linear);
				ListGameObj[i].RT.DOLocalRotate (new Vector3 (0, 0, 0), 0.2f).SetEase (Ease.Linear);
			}
		}
		for (int i = 0; i < ArrText.Length; i++) {
			ArrText[i].text = "True Pixel Count : " + ListOn.Count;
		}
		if (ListOn.Count > Value) {
			for (int i = 0; i < ListOn.Count; i++) {
				posX = posX + ListOn[i].RT.anchoredPosition.x;
				posY = posY + ListOn[i].RT.anchoredPosition.y;
			}
			posX = Mathf.Clamp (posX / ListOn.Count, -width / 2, width / 2);
			posY = Mathf.Clamp (posY / ListOn.Count, -width / 2, width / 2);
			if (!float.IsNaN (posX) && !float.IsNaN (posY))
				try {
					RT_Pos.DOLocalMove (new Vector3 (-posX, posY * 0.4f, 24), 0.4f).SetEase (Ease.Linear);
				} catch { }
			LastPos = new Vector2 (-posX, posY);
		} else {
			RT_Pos.DOLocalMove (LastPos, 0.4f).SetEase (Ease.Linear);
		}
		posX = posY = 0;

		StartCoroutine ("ComparePixel");

	}
	void Update () {

		if (webCamTexture.width > 16) {
			if (!isCamOn) {
				width = webCamTexture.width;
				height = webCamTexture.height;
				isCamOn = true;
				StartCoroutine ("ComparePixel");
				// for (int i = 0; i < ArrText.Length; i++) {
				// 	ArrText[i].text = width + "X" + height;
				// }
			}
			T_Head.DOLocalRotate (new Vector3 (RT_Pos.anchoredPosition.x * 0.08f, 0, 30 + RT_Pos.anchoredPosition.y * 0.2f), 0.3f).SetEase (Ease.Linear);
			T_Text.DOLocalRotate (new Vector3 (7 + RT_Pos.anchoredPosition.y * 0.1f, -RT_Pos.anchoredPosition.x * 0.1f, 0), 0.3f).SetEase (Ease.Linear);
		}
	}
}

 

아래코드는 해당 픽셀에 붙는 코드이다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class PixelHandler : MonoBehaviour {
	public Image Img;
	public RectTransform RT;
	public CanvasGroup CG;
	public bool isOn;
	Color MyTempColor1;
	// public Color FistFrame;
	float min = 0.6f, max = 1.4f;

	void Start () {
		StartCoroutine (OnCheck ());
	}
	IEnumerator OnCheck () {

		MyTempColor1 = Img.color;
		yield return new WaitForSeconds (0.1f);
		if (
			MyTempColor1.r * max > Img.color.r && MyTempColor1.r * min < Img.color.r ||
			MyTempColor1.g * max > Img.color.g && MyTempColor1.r * min < Img.color.g ||
			MyTempColor1.b * max > Img.color.b && MyTempColor1.r * min < Img.color.b
		)
			isOn = false;
		else
			isOn = true;

		StartCoroutine (OnCheck ());
	}
}

 

 

반응형
반응형
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using DG.Tweening;
using UnityEngine.SceneManagement;

public delegate void ProgressDelegate(float progress);

public class LoadSceneAsyncProgress : MonoBehaviour {
	public Image ProgressBar;
	public CanvasGroup CG_Loading;

	void Start() {
		CG_Loading.DOFade (0.5f, 0.5f).SetEase (Ease.Linear).SetLoops (-1, LoopType.Yoyo);
		StartCoroutine(LoadSceneAsyncByName("FamilyHomeVI_SR", OnLoadLevelProgressUpdate));

	}
	
	public static IEnumerator LoadSceneAsyncByName(string nextLevel, ProgressDelegate progressDelegate) {
		AsyncOperation async = SceneManager.LoadSceneAsync(nextLevel);
	
		while (!async.isDone) {
			progressDelegate (async.progress);
			async.allowSceneActivation = async.progress > 0.8;
			yield return null;
		}
	}

	private void OnLoadLevelProgressUpdate(float progress) {
		ProgressBar.fillAmount = progress;
		Debug.Log ("async.progress: " + progress);
	}
}

참조 : https://gist.github.com/crowjdh/26272392e425063cbd69586cd542a46d

 

Getting progress while loading scene in Unity using SceneManager.LoadSceneAsync.

Getting progress while loading scene in Unity using SceneManager.LoadSceneAsync. - UnityLoadSceneAsyncProgress.cs

gist.github.com

참조 : https://docs.unity3d.com/ScriptReference/AsyncOperation-progress.html

 

Unity - Scripting API: AsyncOperation.progress

Return an operation's progress. (Read Only) This returns how close the operation is to finishing. The operation is finished when the progress float reaches 1.0 and isDone is called. If you set allowSceneActivation to false, progress is halted at 0.9 until

docs.unity3d.com

 

반응형
반응형

GPS좌표간 거리계산
특정 매장근처에 가면 노티가 뜨는 시나리오를 구현하였다.
구글맵을 이용하면 경위도 좌표를 쉽게 구할 수 있고
Unity 안드로이드의 현재 위치값을 가져오는 Input.Location을 사용하였다.

출처 : http://fruitdev.tistory.com/189

반응형
반응형

 

1. 하이어라키에 빈게임오프젝트를 만들고 적당히 이름을 변경해준다.

 

2. 빈게임오브젝트에 SpriteRenderer를 추가해준다. (참고로 UI Image, RawImage도 된다)

 

3. 생성한 게임오브젝트를 선택하고 Animation창을 열어 Create 버튼을 눌러 애니메이션을 생성한다.

 

4. 준비해 둔 시퀀스파일을 모두 선택 후 생성한 Animation에 드래그해서 집어 넣는다.

 

 

 

 

5. 애니메이션의 프레임을 움직여보면 시퀀스가 적용된 것을 볼 수 있다.

반복재생, 속도 조절 등등....은 Animator사용법을 숙지하고 사용하면 끝.

 

 

 

 

 

반응형
반응형

아래 한줄 호출해주면 됨.

하이어라키에 존재하지 않는 리소스는 깨끗하게 날려주심.

 

단점이 있다면 한번 로딩했던 에셋을 다시 사용(Instantiate) 경우 다시 로딩시간이 필요하므로 상황에 맞춰서 사용해야 .

 

 

Resources.UnloadUnusedAssets(); 

 

반응형
반응형

 

Application.CaptureScreenshot(myScreenshotLocation);
요 함수로 간단하게 될 줄 알았던 스크린샷이 문제가 생겼다.
파일까지 저장이 잘 되지만 갤러리나 사진첩에서 해당 이미지가 업데이트 되지 않는 문제가 생긴다. 강제로 업데이트를 해줘야하는데 위 코드 하단에 보이는 것 처럼 해주면 된다.

참고 : http://answers.unity3d.com/questions/200173/android-how-to-refresh-the-gallery-.html

반응형
반응형

에셋스토어에 Bitmap Drawing 이라고 검색하면 무료로 올라와있다.
텍스쳐에 실시간으로 원하는 그림을(?) 그릴 수 있게 도와주는 API이다.
사용법은 에셋을 Import시키고 Example파일을 참고하면된다.

 

반응형
반응형

리스트를 사용하다 보면 원하는 요소의 값대로 정렬하고자 할 때가 있다.
그럴 때 적당히 수정해서 사용하면된다.

void swap(int _i, int _j) 	{ 		
    ItemHandler tempItem =  MainList[_i]; 		
    MainList[_i] = MainList[_j]; 		
    MainList[_j] = tempItem; 	
}   	

IEnumerator sorting() 	{ 		
    yield return new WaitForSeconds (0.5f);  		
    // 승점 정렬 		
    for(int i = 0 ; i < MainList.Count; i++ ) { 			
        for(int j = i + 1 ; j < MainList.Count; j++) { 				
            if(MainList[i].Point < MainList[j].Point) { 					
                swap(i, j); 				
            } 				
            else if(MainList[i].Point == MainList[j].Point  				        
            && MainList[i].Gold < MainList[j].Gold) {	// 동률일 경우, gold,  					
                swap(i, j); 				
            } 				
            else if(MainList[i].Point == MainList[j].Point  				        
            && MainList[i].Gold == MainList[j].Gold  				        
            && MainList[i].Silver < MainList[j].Silver){ // 동률에 gold까지 같으면 silver. 					
                swap(i,j); 				
            } 				
            else if(MainList[i].Point == MainList[j].Point  				        
            && MainList[i].Gold == MainList[j].Gold  				        
            && MainList[i].Silver == MainList[j].Silver  				        
            && MainList[i].Bronze < MainList[j].Bronze){ // 동률에 gold, silver까지 같으면 bronze. 					
                swap(i,j); 				
            } 				
            else if(MainList[i].Point == MainList[j].Point  				        
            && MainList[i].Gold == MainList[j].Gold  				        
            && MainList[i].Silver == MainList[j].Silver  				        
            && MainList[i].Bronze == MainList[j].Bronze  				        
            && MainList[i].Garak < MainList[j].Garak){ // 동률에 gold, silver, bronze까지 같으면 garak. 					
                swap(i,j); 				
            } 				
            else if(MainList[i].Point == MainList[j].Point  				        
            && MainList[i].Gold == MainList[j].Gold  				        
            && MainList[i].Silver == MainList[j].Silver  				        
            && MainList[i].Bronze == MainList[j].Bronze  				        
            && MainList[i].Garak == MainList[j].Garak  				        
            && MainList[i].Bbak > MainList[j].Bbak){ // 동률에 gold, silver, bronze, garak까지 같으면 bbak. 					
                swap(i,j); 				
            } 			
        } 		
    }   		
    
    for(int t = 0 ; t < MainList.Count; t++ ) { 			
        MainList[t].gameObject.transform.SetSiblingIndex(t+1); 		
    }   	
}
반응형

+ Recent posts