几日前,笔者碰到一个NGUI的屏幕尺寸的适配问题。情况是这样子的:Unity3d编译了一个iPhone版的Xcode工程,非Universal版,进入真机测试的时候,发现当该iPhone版的工程在iPad2真机上NGUI出现了问题,如下图:
由图片可分析出,NGUI的位置都是正确的,但是大小不正确;显然这是由于屏幕分辨率导致的问题,因为NGUI用的是高清图,并且iPad屏幕是非retina屏幕,NGUI的底层代码根据这个屏幕分辨率来缩放,如果屏幕是全屏的,基本上没啥问题;进一步可以锁定问题出在编译的Iphone版工程运行在iPad上面,由于Unity3d的摄像头视野大小是iPhone的屏幕大小,但是实际的屏幕分辨率却是非retina,而图片却是retina导致在非retina屏幕上显示出了retina图片的真实像素尺寸(本来应该缩小一倍)。找到问题的原因后,就可以着手解决了。
由于查看Unity3d的Screen这个类提供的API没有查询屏幕的分辨率是否是retina屏幕的,只能通过Objective-c的API查询。在这里,笔者的思路是,通过在Unity3d层的NGUI的底层NGRoot.cs(NGUI负责屏幕分辨率适配的类)这个类的C#脚本中向Xcode层的Objective-c语言发送消息查询当前的屏幕是非retina屏,是否iPad硬件,再加上一个根据当前Xcode的版本的开关变量(比如是iPhone版就打开,如果是iPad版或者Universal版就关闭)一起判断,决定是否要将当前屏幕的分辨率乘以2(NGUI底层是用某个常量除以当前屏幕的分辨率来适配屏幕的)再来计算缩放。这涉及到Uinty3d层与Xcode层的通信问题。
幸好,C#给我们提供了多平台适应的API,使用System.Runtime.InteropServices,可以定义一个接口,在c#项目外实现。这里可以在C#脚本里定义在需要在Xcode里实现的接口,在接口前加上[DllImport(
"__Internal"
)]即可。代码实例如下:
在Unity3d层的代码:
//----------------------------------------------
// NGUI: Next-Gen UI kit
// Copyright © 2011-2012 Tasharen Entertainment
//----------------------------------------------
using UnityEngine;
using System.Runtime.InteropServices;
/// <summary>
/// This is a script used to keep the game object scaled to 2/(Screen.height).
/// If you use it, be sure to NOT use UIOrthoCamera at the same time.
/// </summary>
[ExecuteInEditMode]
[AddComponentMenu("NGUI/UI/Root")]
public class UIRoot : MonoBehaviour
{
public bool automatic = true;
public int manualHeight = 800;
[DllImport ("__Internal")]
public static extern bool isDoubleScreen ();
bool isIPhonePlatform = false;
Transform mTrans;
void Start ()
{
mTrans = transform;
UIOrthoCamera oc = GetComponentInChildren<UIOrthoCamera> ();
if (oc != null) {
Debug.LogWarning ("UIRoot should not be active at the same time as UIOrthoCamera. Disabling UIOrthoCamera.", oc);
Camera cam = oc.gameObject.GetComponent<Camera> ();
oc.enabled = false;
if (cam != null)
cam.orthographicSize = 1f;
}
if (Application.platform == RuntimePlatform.IPhonePlayer) {
isIPhonePlatform = true;
}
}
void Update ()
{
manualHeight = Mathf.Max (2, automatic ? Screen.height : manualHeight);
if (isIPhonePlatform) {
if (UIRoot.isDoubleScreen()) {
manualHeight *= 2;
}
}
float size = 2f / manualHeight;
Vector3 ls = mTrans.localScale;
if (!Mathf.Approximately (ls.x, size) ||
!Mathf.Approximately (ls.y, size) ||
!Mathf.Approximately (ls.z, size)) {
mTrans.localScale = new Vector3 (size, size, size);
}
}
}
在Xcode层的代码:
#pragma mark - logic
#define isRetina ([UIScreen mainScreen].currentMode.size.width / [UIScreen mainScreen].bounds.size.width == 2 ? YES : NO)
//定义高清图片的开关 ,1打开,0关闭
#define isRetinaPicture 1
//是否是iPhone程序运行在iPad上
#define isPhoneAppOnPad (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone && [[[UIDevice currentDevice] model] hasPrefix:@"iPad"])
bool isDoubleScreen () {
if (!isRetina && isPhoneAppOnPad && isRetinaPicture) {
return true;
}
return false;
}
解决后的NGUI在iPad屏幕上的显示如下图:
好了,上边介绍了如何在Unity3d层向Xcode层发送查询消息解决了NGUI屏幕分辨率适配的问题。接下来再介绍如何在Xcode层向Unity3d层发送消息,在这里,Unity3d提供了一个API来实现,即
void UnitySendMessage(const char* obj, const char* method, const char* msg);
这个API有三个参数,第一个参数是Unity3d里接收消息的物体的名称,第二个参数是Unity3d绑定的物体的脚本里实现的方法名称,第三个参数是是第二个参数的实现方法里需要接收传递的数据参数。代码实例如下:
Xcode层的代码:
NSString *param = @"param";
UnitySendMessage("XcodeAdapter", "methodFromXcode", [param cStringUsingEncoding:NSUTF8StringEncoding]);
Unity3d层的代码:
using UnityEngine;
using System.Collections;
using System.Runtime.InteropServices;
//该脚本绑定在Unity3d场景里一个叫XcodeAdapter的GameObject上
public class XcodeAdapter : MonoBehaviour {
public void methodFromXcode (string param)
{
Debug.Log(param + "~~~~!!!!");
}
}
本文结束~~~~