2D와 3D ui는 혼용이 가능하다.
UICamera의 Depth 설정에 의해 먼저 보여질 것이 결정 된다.
3D UI를 한개 만든다. 의례적으로,
윈도우의 중심은 Add New Child로 빈 오브젝트를 만들고, UI 요소들을 자식으로 추가하면 관리하기 편하다.
Bitmap font generator로 비트맵 폰트를 만든다.
FontSetting | |
Bit Depth 를 8로 맞추면 폰트 외곽 부분이 제대로 처리 되지 않아서 글자가 이상하게 나오게 된다. |
|
이렇게 그리다 만듯한… |
|
Export Options | |
텍스쳐 크기를 1024×1024로 변경했는데, NGUI폰트 생성기가 한장만 인식하는 것 같아서 하나만으로 생성. 256×256이면 글자가 이상해지지 않을까? |
폰트 파일을 저장합니다. ( Save bitmap font as )
Assets으로 등록
NGUI로 폰트 만들기
= Event Receiver Mask : UI 입력을 받을 레이어를 설정한다. = : 이 레이어를 따로 하지 않으면, (기본은 Default) UI 요소를 선택했을때 화면에서도 UI 반응이 그대로 전달된다.
[RrGgBb]<텍스트>[-] 형식으로 UILabel의 텍스트에 색상을 줄 수 있다.
[ff0000]붉은 글씨[-]를 | <color red>붉은 글씨</color>를 |
좀 희한한 현상,
헐.. 왜 그럴까. 아무튼 Z 축을 조정하면 잘 나오니 문제 없지만.
이게..
NGUI를 사용해서 요런 기능을 추가한다.
동작 시나리오1
동작 시나리오2
유의사항
프로그래밍으로 동적 추가도 가능하고, Hierarchy에 NGUI Label을 추가해서 사용할 수도 있다.
순서
실행해보면 오브젝트를 따라다니는 라벨이 보인다. 결과 화면,
코드로 FloatingTextUp2D 생성할 때는,
++++ NotifyHandler.cs |
using UnityEngine; using System.Collections; public class NotifyHandler : MonoBehaviour { public Camera mainCamera; public Camera guiCamera; public GameObject prefab; public void fire( Vector3 clickedPos_ ) { GameObject _gameObj = Instantiate( prefab, clickedPos_, Quaternion.identity ) as GameObject; // No mean yet, just align as child SomeTools.AppendChildAndReset( gameObject, _gameObj ); // run floating script FloatingTextUp2D _floatTxtUp = _gameObj.GetComponent<FloatingTextUp2D>(); _floatTxtUp.initAndStartMoving( clickedPos_, mainCamera, guiCamera ); } }
++++
NotifyHandler 에 임의 메시지 보내기.
++++ UIInputReactor |
using UnityEngine; using System.Collections; public class UIInputReactor : MonoBehaviour { void Update() { if( Input.GetButtonDown("Fire1") ) { // 화면을 임의 클릭해서, 뭐든 있는 위치를 캐치해서, Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); RaycastHit hit; if( Physics.Raycast(ray, out hit, 100) ) { // NotifyHandler 오브젝트에 메시지 발송 (fire 실행) GameObject _obj = GameObject.Find("NotifyHandler"); _obj.SendMessage( "fire", hit.point ); } } } }
++++
결과화면,
오브젝트를 따라다니는 라벨
++++ FloatingText2D.cs |
using UnityEngine; using System.Collections; public class FloatingText2D : MonoBehaviour { private UILabel __label; private Vector3 __pos; private Transform __t; public bool followTarget = true; public Vector3 defaultSize = new Vector3(1,1,1); public GameObject targetObject; public Camera worldCamera; public Camera guiCamera; public TweenPosition tweenPos; public void init( string text_, GameObject gameObj_ ) { this.text = text_; this.target = gameObj_; } public void init( string text_, Color clr_, GameObject gameObj_ ) { this.color = clr_; this.text = text_; this.target = gameObj_; } public void spawnAt( GameObject targetObj_, Vector3 size_, Transform parent_ ) { target = targetObj_; __t.parent = parent_; defaultSize = size_; } public void followingObject() { __pos = worldCamera.WorldToViewportPoint( targetObject.transform.position ); __pos = guiCamera.ViewportToWorldPoint( __pos ); __pos.z = 0f; transform.position = __pos; } // Use this for initialization void Awake() { __t = transform; __label = GetComponent<UILabel>(); } void Start() { if( guiCamera == null ) guiCamera = NGUITools.FindCameraForLayer( gameObject.layer ); __label.transform.localScale = new Vector3( __t.transform.localScale.x * defaultSize.x, __t.transform.localScale.y * defaultSize.y, 1f ); } void LateUpdate() { if( followTarget ) followingObject(); } // property // public string text { get { return __label.text; } set { __label.text = value; } } public Color color { get { return __label.color; } set { __label.color = value; } } public Vector3 size { get { return __label.transform.localScale; } set { __label.transform.localScale = value; } } public TweenPosition tweenPosition { get { TweenPosition _tp = GetComponent<TweenPosition>(); if( _tp == null ) _tp = targetObject.AddComponent<TweenPosition>(); return _tp; } } public GameObject target { get { return targetObject; } set { targetObject = value; worldCamera = NGUITools.FindCameraForLayer( targetObject.layer ); } } }
++++
특정 위치에서 나타나서 위로 이동하는 라벨. 아래 부분에 업데이트 된 코드 있다.
++++ FloatingTextUp2D.cs |
using UnityEngine; using System.Collections; public class FloatingTextUp2D : MonoBehaviour { private UILabel __label; // from ScriptIncluded private Vector3 __pos; private Vector3 __initPos; public Camera guiCamera = null; public Camera worldCamera = null; public GameObject targetObject = null; public int tweenTime = 2; public int tweenDistance = 50; // class spec function // public void destroySelf() { Destroy( gameObject ); } public void initAndStartMoving( Vector3 initPos_, Camera mainCam_ = null, Camera guiCam_ = null ) { if( mainCam_ != null ) worldCamera = mainCam_; if( guiCam_ != null ) guiCamera = guiCam_; startMoveUp( initPos_, 2, 50 ); } public void startMoveUp( Vector3 initPos_, int tweenDuration_, int tweenEnd_ ) { __initPos = initPos_; calculatePosition( initPos_ ); TweenPosition _tp = this.tweenPosition; _tp.duration = tweenDuration_; _tp.from = transform.localPosition; _tp.to = _tp.from + Vector3.up * tweenEnd_; _tp.eventReceiver = gameObject; _tp.callWhenFinished = "destroySelf"; } public void calculatePosition( Vector3 pos_ ) { __pos = worldCamera.WorldToViewportPoint( pos_ ); __pos = guiCamera.ViewportToWorldPoint( __pos ); __pos.z = 0f; transform.position = __pos; } // Unity3d reaction function // public void Awake() { __label = GetComponent<UILabel>(); } void Start() { if( guiCamera == null ) guiCamera = NGUITools.FindCameraForLayer( gameObject.layer ); if( worldCamera == null ) worldCamera = Camera.mainCamera; __label.MakePixelPerfect(); // why bigger??? if( targetObject != null ) { startMoveUp( targetObject.transform.position, tweenTime, tweenDistance ); } } // Property helper // public TweenPosition tweenPosition { get { TweenPosition _tp = GetComponent<TweenPosition>(); if( _tp == null ) _tp = gameObject.AddComponent<TweenPosition>(); return _tp; } } }
++++
UILabel을 화면 어딘가( 프로그램 지정 )에서 위쪽으로 흘려 보내는 스크립트.
추가 수정 사항
수정해야할 것
UILabel 크기 조정
폰트 픽셀 크기를 얻어와서, UILabel의 전체 크기를 구하는 함수. 출력 위치를 화면 안쪽으로 옮길때 사용하므로, 크기 조정된 rect를 리턴할 수 있도록 수정
// 지정폰트 픽셀크기를 구해서(보통 1) float pixelSize = (mLabel.font.atlas != null) ? mLabel.font.atlas.pixelSize : 1f; // 스케일 비율에 맞춰 너비를 계산 Vector3 scale = mLabel.cachedTransform.localScale; // 자동 스케일 기능이 켜져 있으면, if( scaleByScreen != 0f ) // (화면크기 / 글자수) * (확대축소비율) = 실제로 출력 되어야 할 너비 scale.x = ((guiCamera.pixelWidth / mLabel.relativeSize.x) * scaleByScreen); else // 그게 아니면 원래 사용하던 방식으로 계산. (예상, 폰트크기가 그대로 scale.x에 반영) scale.x = mLabel.font.size * pixelSize; scale.y = scale.x; scale.z = 1f; return (mLabel.relativeSize * scale.x); // 글자수 * 글자당스케일
UILabel::MakePixelPerfact()함수를 가져와서, 스케일 조정하는 부분의 코드에서 scaleByScreen 에 희망 확대 비율이 적혀 있으면 그 크기만큼 조정한다.
public void adjustScale() { // 화면 확대 설정이 없으면, 원래 크기대로 설정한다. if( scaleByScreen == 0f ) { mLabel.MakePixelPerfect(); return; } else if( mLabel.font != null ) { float pixelSize = (mLabel.font.atlas != null) ? mLabel.font.atlas.pixelSize : 1f; Vector3 scale = mLabel.cachedTransform.localScale; //scale.x = mLabel.font.size * pixelSize; // 원래코드 // 화면 확대 비율만큼 너비를 계산한다. scale.x = ((guiCamera.pixelWidth / mLabel.relativeSize.x) * scaleByScreen); // //... 나머지 부분은 UILabel.MakePixelPerfact()와 같다. } }
++++ FloatingTextUp2D.cs |
using UnityEngine; using System.Collections; [RequireComponent( typeof( TweenPosition ) )] public class FloatingTextUp2D : MonoBehaviour { public enum Side { Left, Right, Center, NoOutOfScreen, NoTouch } private UILabel mLabel; // from ScriptIncluded private Vector3 mObjPos; public Camera guiCamera = null; public Camera worldCamera = null; public GameObject targetObject = null; public float tweenDuration = 2.5f; public float tweenDistance = 350f; public Side startSide = Side.NoOutOfScreen; /// <summary> /// move up by Percent from -1 ~ 1. 0 is middle of scrren. If this is false, tweenDistance used to end of tween moving. /// </summary> public bool moveUpByPercent = false; /// <summary> /// how much move up? this value from -1 to 1. 0 is middle of screen. /// </summary> [Range(-1, 1)] public float moveUpPercent = 0f; /// <summary> /// Auto Scale by screen width. if zero, use original size. if 1, full width. /// </summary> [Range( 0, 1 )] public float scaleByScreen = 0f; /// <summary> /// If scaleByScreen not setted. use this. /// </summary> //public Vector2 defaultSize = new Vector2( 1, 1 ); public void destroySelf() { Destroy( gameObject ); } // get UILabel width, height roughly. code from UILabel.MakePixelPerfect() public Vector2 getUILabelSize() { float pixelSize = (mLabel.font.atlas != null) ? mLabel.font.atlas.pixelSize : 1f; Vector3 scale = mLabel.cachedTransform.localScale; if( scaleByScreen != 0f ) scale.x = ((guiCamera.pixelWidth / mLabel.relativeSize.x) * scaleByScreen); else scale.x = mLabel.font.size * pixelSize; scale.y = scale.x; scale.z = 1f; return (mLabel.relativeSize * scale.x); } public Vector3 fixPosition( Vector3 value_ ) { Rect rect = new Rect(); rect = guiCamera.pixelRect; if( startSide != Side.NoTouch ) { Vector3 _tmpPos = guiCamera.WorldToScreenPoint( value_ ); Vector2 _labelSize = getUILabelSize(); float _middleX = Mathf.Abs( _labelSize.x * mLabel.pivotOffset.x ); if( startSide == Side.Center ) { _tmpPos.x = (rect.xMin + rect.xMax) * 0.5f; } else if( startSide == Side.Left ) { _tmpPos.x = (rect.xMin + _middleX); } else if( startSide == Side.Right ) { _tmpPos.x = (rect.xMax - _middleX); } else if( startSide == Side.NoOutOfScreen ) { if( (_tmpPos.x - _middleX) < rect.xMin ) _tmpPos.x = rect.xMin + _middleX; if( (_tmpPos.x + _middleX) > rect.xMax ) _tmpPos.x = rect.xMax - _middleX; } value_ = guiCamera.ScreenToWorldPoint( _tmpPos ); } return value_; } public void adjustPosition( Vector3 pos_ ) { mObjPos = worldCamera.WorldToViewportPoint( pos_ ); mObjPos = guiCamera.ViewportToWorldPoint( mObjPos ); mObjPos = fixPosition( mObjPos ); mObjPos.z = 0f; transform.position = mObjPos; } public void adjustScale() { if( scaleByScreen == 0f ) { mLabel.MakePixelPerfect(); // why bigger??? return; } else if( mLabel.font != null ) { float pixelSize = (mLabel.font.atlas != null) ? mLabel.font.atlas.pixelSize : 1f; //pixelSize = 4; Vector3 scale = mLabel.cachedTransform.localScale; //scale.x = mLabel.font.size * pixelSize; scale.x = ((guiCamera.pixelWidth / mLabel.relativeSize.x) * scaleByScreen); scale.y = scale.x; scale.z = 1f; Vector2 actualSize = mLabel.relativeSize * scale.x; int x = Mathf.RoundToInt( actualSize.x / pixelSize ); int y = Mathf.RoundToInt( actualSize.y / pixelSize ); Vector3 pos = mLabel.cachedTransform.localPosition; pos.x = Mathf.FloorToInt( pos.x / pixelSize ); pos.y = Mathf.CeilToInt( pos.y / pixelSize ); pos.z = Mathf.RoundToInt( pos.z ); if( mLabel.cachedTransform.localRotation == Quaternion.identity ) { if( (x % 2 == 1) && (mLabel.pivot == UILabel.Pivot.Top || mLabel.pivot == UILabel.Pivot.Center || mLabel.pivot == UILabel.Pivot.Bottom) ) pos.x += 0.5f; if( (y % 2 == 1) && (mLabel.pivot == UILabel.Pivot.Left || mLabel.pivot == UILabel.Pivot.Center || mLabel.pivot == UILabel.Pivot.Right) ) pos.y -= 0.5f; } pos.x *= pixelSize; pos.y *= pixelSize; mLabel.cachedTransform.localPosition = pos; mLabel.cachedTransform.localScale = scale; } } public void startMoveUp( Vector3 initPos_, float tweenDuration_, float tweenEnd_ ) { adjustScale(); adjustPosition( initPos_ ); TweenPosition _tp = this.tweenPosition; _tp.eventReceiver = gameObject; _tp.callWhenFinished = "destroySelf"; _tp.from = transform.localPosition; if( moveUpByPercent ) { _tp.to = Vector3.up * (Screen.height * 0.5f) * moveUpPercent; _tp.to.x = _tp.from.x; } else { _tp.to = _tp.from + Vector3.up * tweenEnd_; } if( tweenDuration_ != 0 ) { _tp.duration = tweenDuration_; } } public void initAndStartMoving( Vector3 initPos_, Camera mainCam_ = null, Camera guiCam_ = null ) { if( mainCam_ != null ) worldCamera = mainCam_; if( guiCam_ != null ) guiCamera = guiCam_; startMoveUp( initPos_, tweenDuration, tweenDistance ); } // Unity3d reaction function public void Awake() { mLabel = GetComponent<UILabel>(); } void Start() { if( guiCamera == null ) { guiCamera = NGUITools.FindCameraForLayer( gameObject.layer ); } if( worldCamera == null ) { worldCamera = Camera.mainCamera; } if( targetObject != null ) { startMoveUp( targetObject.transform.position, tweenDuration, tweenDistance ); } } public TweenPosition tweenPosition { get { TweenPosition _tp = GetComponent<TweenPosition>(); if( _tp == null ) _tp = gameObject.AddComponent<TweenPosition>(); return _tp; } } }
++++
GameObject에 TweenAlpha를 컴포넌트로 추가한 다음, 필요할때마다 사용하고 싶을때
// debugMenu가 show_ 플래그에 따라 화면에 보이거나 사라지거나 하는 동작이 있을때, // true 면 alpha 값이 0 에서 1로 (점점 보여지도록), false면 그 반대로 // 애니메이션 시작되면서 알파 값이 적용 되도록한다. // 느릴려나..? public void debugMenuOn( bool show_ ) { AnimationState _animState = debugWindow.animation["menuMoveInside"]; TweenAlpha _tweenAlpha = debugWindow.GetComponent<TweenAlpha>(); _tweenAlpha.Reset(); if( show_ ) { _animState.speed = 1; _tweenAlpha.from = 0; _tweenAlpha.to = 1; } else { _animState.speed = -1; _animState.time = _animState.length; _tweenAlpha.from = 1; _tweenAlpha.to = 0; } _tweenAlpha.enabled = true; debugWindow.animation.Play( "menuMoveInside" ); }
GameObject에 TweenAlpha를 컴포넌트로 추가한 다음, 필요할때마다 사용하고 싶을때 2
다른 케이스.
스크립트 내에서 컴포넌트로 추가된 TweenAlpha를 불러와서 코드로 시작하게 하는
float _from = 1f; float _to = 0f; float _duration = 0.2f; TweenAlpha _ta = mBackground.GetComponent<TweenAlpha>(); _ta.Reset(); _ta.eventReceiver = gameObject; // Tween이 종료되면, destroySelf() 를 불러, 오브젝트를 삭제한다. _ta.callWhenFinished = "destroySelf"; _ta.from = _from; _ta.to = _to; _ta.duration = _duration * 2f ; _ta.Play( true ); // play! 파라미터가 false면 반대로 플레이 된다고 하는데 확인 하진 않음.
컴포넌트로 추가되어 있지 않아도 자동 추가하며, UIWidget을 계열 오브젝트면 바로 적용 된다.
//- TweenAlpha : quick example TweenAlpha _ta = mBackground.GetComponent<TweenAlpha>(); _ta.duration = 0.5f; _ta.from = 1; _ta.to = 0;