배포시 가장 큰 골치덩어리중 하나는 AndroidManifest.xml 파일 수정문제일 것이다. Android Plugin을 만들어서 넣자니 짜증나고... 그럴때 간단하게 AndroidManifest.xml 파일을 수정할 수 있는 방법을 공개한다.
프로젝트 Root폴더에 보면 "Temp" 폴더가 생성되어 있을텐데 거길 가만히 보면 "StagingArea"라는 폴더가 보인다. 여기로 들어가면 다음과 같이 폴더가 구성되어 있다.
빌드에서 사용될 각종 Resource 파일들이 보일텐데 이중에 필요한건 AndroidManifest.xml 파일과 res 폴더 두개이다. 이 2개를 선택해서 CTRL+C 해서 복사하고 유니티로 돌아와서 "Plugins" 폴더를 만든다음 다시 "Android"폴더를 만들고 거기에 복사해 넣자.
이제 복사한 AndroidManifest.xml 파일을 열어서 마음대로 주무르면 됨. 끝!
Some years ago I was required to build a simple Cover Flow Layout (think, iTunes Carousel). Originally I build the project in a Windows Forms application for a client we were working with. Sometime later we then needed a similar system in a project we were doing in Unity3D.
This is just a simple recreation of that work.
Its very old, and it wasn’t originally done in GitHub so I’ve just commited the whole project in one commit.
There are some simple layouts included to demonstrate the flexibility of the system,
The classic Cover Flow layout (iTunes Album Artwork style)
A Carousel Layout (Z-Depth carousel)
A “Messy Paper” Layout - Cells shift from 1 messy pile to another
Further Features
Cell reuse is supported using a simple Cell Pool with UICollectionCells registering Prefabs as “nibs” to be reused.
Data “binding” can be expanded upon with the cell reuse.
All layouts have various settings to tweak positions, speeds, snapping, wrapping and the like. These can also be updated at runtime in the editor to see results in real time.
Demos
Here’s a few GIFs showing the layouts in action in the editor (GIFs are only at 30fps and appear to have bad artifacts in them, running in the editor is obviously at full FPS with no rendering issues).
Cover Flow Layout
Carousel Layout
Messy Paper Layout
Layouts can have multiple configurable elements, here’s an example of the Cover Flow properties being edited at runtime…
It forces to sample current state of animations. "Sample animations" means: put character in the position defined by animation states. Sampling always happens between Update and LateUpdate automatically, but in some cases you might want to sample animations yourself. My most common case: I want to put character in pose of first frame some specific animation on Start, so I would do something like this:
using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TimeServer : MonoBehaviour {
[SerializeField]
private string _comment = "만료시킬 날짜를 적으세요 (한국시각 기준)";
public int _yyyy, _mm, _dd;
private DateTime _expireDateTime, _nowServerDateTime, _nowLocalDateTime;
private TimeSpan _duration;
// Use this for initialization
void Start () {
// 한국 시각
_duration = System.TimeSpan.FromHours(9);
_expireDateTime = new DateTime(Mathf.Clamp(_yyyy, 1900, 3000), Mathf.Clamp(_mm, 1,12), Mathf.Clamp(_dd, 1, 31));
_nowLocalDateTime = DateTime.Now;
_nowServerDateTime = GetNISTDate().Add(_duration);
if (Debug.isDebugBuild)
{
Debug.LogWarning("만료지정일 : " + _expireDateTime);
Debug.LogWarning("현재 로컬 시각 :" + _nowLocalDateTime);
Debug.LogWarning("현재 서버 시각 :" + _nowServerDateTime);
}
if (_nowLocalDateTime < _expireDateTime)
{
if (_nowServerDateTime < _expireDateTime)
{
// Debug.Log("실행");
}
else
{
// Debug.Log("서버 체크 결과 만료 됨");
}
}
else
{
// Debug.Log("로컬 체크 결과 만료 됨");
}
}
#region NTPTIME
//NTP time 을 NIST 에서 가져오기
// 4초 이내에 한번 이상 요청 하면, ip가 차단됩니다.
public static DateTime GetDummyDate()
{
return new DateTime(2017, 12, 24); //to check if we have an online date or not.
}
public static DateTime GetNISTDate()
{
System.Random ran = new System.Random(DateTime.Now.Millisecond);
DateTime date = GetDummyDate();
string serverResponse = string.Empty;
// NIST 서버 목록
string[] servers = new string[] {
"time.bora.net",
//"time.nuri.net",
//"ntp.kornet.net",
//"time.kriss.re.kr",
//"time.nist.gov",
//"maths.kaist.ac.kr",
"nist1-ny.ustiming.org",
"time-a.nist.gov",
"nist1-chi.ustiming.org",
"time.nist.gov",
"ntp-nist.ldsbc.edu",
"nist1-la.ustiming.org"
};
// 너무 많은 요청으로 인한 차단을 피하기 위해 한 서버씩 순환한다. 5번만 시도한다.
for (int i = 0; i < 5; i++)
{
try
{
// StreamReader(무작위 서버)
StreamReader reader = new StreamReader(new System.Net.Sockets.TcpClient(servers[ran.Next(0, servers.Length)], 13).GetStream());
serverResponse = reader.ReadToEnd();
reader.Close();
// 서버 리스폰스를 표시한다. (디버그 확인용)
if (Debug.isDebugBuild)
Debug.Log(serverResponse);
// 시그니처가 있는지 확인한다.
if (serverResponse.Length > 47 && serverResponse.Substring(38, 9).Equals("UTC(NIST)"))
{
// 날짜 파싱
int jd = int.Parse(serverResponse.Substring(1, 5));
int yr = int.Parse(serverResponse.Substring(7, 2));
int mo = int.Parse(serverResponse.Substring(10, 2));
int dy = int.Parse(serverResponse.Substring(13, 2));
int hr = int.Parse(serverResponse.Substring(16, 2));
int mm = int.Parse(serverResponse.Substring(19, 2));
int sc = int.Parse(serverResponse.Substring(22, 2));
if (jd > 51544)
yr += 2000;
else
yr += 1999;
date = new DateTime(yr, mo, dy, hr, mm, sc);
// Exit the loop
break;
}
}
catch (Exception e)
{
/* 아무것도 하지 않고 다음 서버를 시도한다. */
}
}
return date;
}
#endregion
}