using System;
|
using System.Collections.Generic;
|
using UnityEngine.UI;
|
using UnityEngine;
|
|
|
namespace Poco
|
{
|
public class UnityNode : AbstractNode
|
{
|
public static Dictionary<string, string> TypeNames = new Dictionary<string, string>() {
|
{ "Text", "Text" },
|
{ "Gradient Text", "Gradient.Text" },
|
{ "Image", "Image" },
|
{ "RawImage", "Raw.Image" },
|
{ "Mask", "Mask" },
|
{ "2DRectMask", "2D-Rect.Mask" },
|
{ "Button", "Button" },
|
{ "InputField", "InputField" },
|
{ "Toggle", "Toggle" },
|
{ "Toggle Group", "ToggleGroup" },
|
{ "Slider", "Slider" },
|
{ "ScrollBar", "ScrollBar" },
|
{ "DropDown", "DropDown" },
|
{ "ScrollRect", "ScrollRect" },
|
{ "Selectable", "Selectable" },
|
{ "Camera", "Camera" },
|
{ "RectTransform", "Node" },
|
};
|
public static string DefaultTypeName = "GameObject";
|
private GameObject gameObject;
|
private Renderer renderer;
|
private RectTransform rectTransform;
|
private Rect rect;
|
private Vector2 objectPos;
|
private List<string> components;
|
private Camera camera;
|
|
|
public UnityNode(GameObject obj)
|
{
|
gameObject = obj;
|
camera = Camera.main;
|
foreach (var cam in Camera.allCameras)
|
{
|
// skip the main camera
|
// we want to use specified camera first then fallback to main camera if no other cameras
|
// for further advanced cases, we could test whether the game object is visible within the camera
|
if (cam == Camera.main)
|
{
|
continue;
|
}
|
if ((cam.cullingMask & (1 << gameObject.layer)) != 0)
|
{
|
camera = cam;
|
}
|
}
|
|
renderer = gameObject.GetComponent<Renderer>();
|
rectTransform = gameObject.GetComponent<RectTransform>();
|
rect = GameObjectRect(renderer, rectTransform);
|
objectPos = renderer ? WorldToGUIPoint(camera, renderer.bounds.center) : Vector2.zero;
|
components = GameObjectAllComponents();
|
}
|
|
public override AbstractNode getParent()
|
{
|
GameObject parentObj = gameObject.transform.parent.gameObject;
|
return new UnityNode(parentObj);
|
}
|
|
public override List<AbstractNode> getChildren()
|
{
|
List<AbstractNode> children = new List<AbstractNode>();
|
foreach (Transform child in gameObject.transform)
|
{
|
children.Add(new UnityNode(child.gameObject));
|
}
|
return children;
|
}
|
|
public override object getAttr(string attrName)
|
{
|
switch (attrName)
|
{
|
case "name":
|
return gameObject.name;
|
case "type":
|
return GuessObjectTypeFromComponentNames(components);
|
case "visible":
|
return GameObjectVisible(renderer, components);
|
case "pos":
|
return GameObjectPosInScreen(objectPos, renderer, rectTransform, rect);
|
case "size":
|
return GameObjectSizeInScreen(rect, rectTransform);
|
case "scale":
|
return new List<float>() { 1.0f, 1.0f };
|
case "anchorPoint":
|
return GameObjectAnchorInScreen(renderer, rect, objectPos);
|
case "zOrders":
|
return GameObjectzOrders();
|
case "clickable":
|
return GameObjectClickable(components);
|
case "text":
|
return GameObjectText();
|
case "components":
|
return components;
|
case "texture":
|
return GetImageSourceTexture();
|
case "tag":
|
return GameObjectTag();
|
case "layer":
|
return GameObjectLayerName();
|
case "_ilayer":
|
return GameObjectLayer();
|
case "_instanceId":
|
return gameObject.GetInstanceID();
|
default:
|
return null;
|
}
|
}
|
|
|
public override Dictionary<string, object> enumerateAttrs()
|
{
|
Dictionary<string, object> payload = GetPayload();
|
Dictionary<string, object> ret = new Dictionary<string, object>();
|
foreach (KeyValuePair<string, object> p in payload)
|
{
|
if (p.Value != null)
|
{
|
ret.Add(p.Key, p.Value);
|
}
|
}
|
return ret;
|
}
|
|
|
private Dictionary<string, object> GetPayload()
|
{
|
Dictionary<string, object> payload = new Dictionary<string, object>() {
|
{ "name", gameObject.name },
|
{ "type", GuessObjectTypeFromComponentNames (components) },
|
{ "visible", GameObjectVisible (renderer, components) },
|
{ "pos", GameObjectPosInScreen (objectPos, renderer, rectTransform, rect) },
|
{ "size", GameObjectSizeInScreen (rect, rectTransform) },
|
{ "scale", new List<float> (){ 1.0f, 1.0f } },
|
{ "anchorPoint", GameObjectAnchorInScreen (renderer, rect, objectPos) },
|
{ "zOrders", GameObjectzOrders () },
|
{ "clickable", GameObjectClickable (components) },
|
{ "text", GameObjectText () },
|
{ "components", components },
|
{ "texture", GetImageSourceTexture () },
|
{ "tag", GameObjectTag () },
|
{ "_ilayer", GameObjectLayer() },
|
{ "layer", GameObjectLayerName() },
|
{ "_instanceId", gameObject.GetInstanceID () },
|
};
|
return payload;
|
}
|
|
private string GuessObjectTypeFromComponentNames(List<string> components)
|
{
|
List<string> cns = new List<string>(components);
|
cns.Reverse();
|
foreach (string name in cns)
|
{
|
if (TypeNames.ContainsKey(name))
|
{
|
return TypeNames[name];
|
}
|
}
|
return DefaultTypeName;
|
}
|
|
private bool GameObjectVisible(Renderer renderer, List<string> components)
|
{
|
if (gameObject.activeInHierarchy)
|
{
|
bool light = components.Contains("Light");
|
// bool mesh = components.Contains ("MeshRenderer") && components.Contains ("MeshFilter");
|
bool particle = components.Contains("ParticleSystem") && components.Contains("ParticleSystemRenderer");
|
if (light || particle)
|
{
|
return false;
|
}
|
else
|
{
|
return renderer ? renderer.isVisible : true;
|
}
|
}
|
else
|
{
|
return false;
|
}
|
}
|
|
private int GameObjectLayer()
|
{
|
return gameObject.layer;
|
}
|
private string GameObjectLayerName()
|
{
|
return LayerMask.LayerToName(gameObject.layer);
|
}
|
|
private bool GameObjectClickable(List<string> components)
|
{
|
Button button = gameObject.GetComponent<Button>();
|
return button ? button.isActiveAndEnabled : false;
|
}
|
|
private string GameObjectText()
|
{
|
Text text = gameObject.GetComponent<Text>();
|
return text ? text.text : null;
|
}
|
|
private string GameObjectTag()
|
{
|
string tag;
|
try
|
{
|
tag = !gameObject.CompareTag("Untagged") ? gameObject.tag : null;
|
}
|
catch (UnityException)
|
{
|
tag = null;
|
}
|
return tag;
|
}
|
|
private List<string> GameObjectAllComponents()
|
{
|
List<string> components = new List<string>();
|
Component[] allComponents = gameObject.GetComponents<Component>();
|
if (allComponents != null)
|
{
|
foreach (Component ac in allComponents)
|
{
|
if (ac != null)
|
{
|
components.Add(ac.GetType().Name);
|
}
|
}
|
}
|
return components;
|
}
|
|
private Dictionary<string, float> GameObjectzOrders()
|
{
|
float CameraViewportPoint = 0;
|
if (camera != null)
|
{
|
CameraViewportPoint = Math.Abs(camera.WorldToViewportPoint(gameObject.transform.position).z);
|
}
|
Dictionary<string, float> zOrders = new Dictionary<string, float>() {
|
{ "global", 0f },
|
{ "local", -1 * CameraViewportPoint }
|
};
|
return zOrders;
|
}
|
|
private Rect GameObjectRect(Renderer renderer, RectTransform rectTransform)
|
{
|
Rect rect = new Rect(0, 0, 0, 0);
|
if (renderer)
|
{
|
rect = RendererToScreenSpace(camera, renderer);
|
}
|
else if (rectTransform)
|
{
|
rect = RectTransformToScreenSpace(rectTransform);
|
}
|
return rect;
|
}
|
|
private float[] GameObjectPosInScreen(Vector3 objectPos, Renderer renderer, RectTransform rectTransform, Rect rect)
|
{
|
float[] pos = { 0f, 0f };
|
|
if (renderer)
|
{
|
// 3d object
|
pos[0] = objectPos.x / (float)Screen.width;
|
pos[1] = objectPos.y / (float)Screen.height;
|
}
|
else if (rectTransform)
|
{
|
// ui object (rendered on screen space, other render modes may be different)
|
// use center pos for now
|
Canvas rootCanvas = GetRootCanvas(gameObject);
|
RenderMode renderMode = rootCanvas != null ? rootCanvas.renderMode : new RenderMode();
|
switch (renderMode)
|
{
|
case RenderMode.ScreenSpaceCamera:
|
//上一个方案经过实际测试发现还有两个问题存在
|
//1.在有Canvas Scaler修改了RootCanvas的Scale的情况下坐标的抓取仍然不对,影响到了ScreenSpaceCameram模式在不同分辨率和屏幕比例下识别的兼容性。
|
//2.RectTransformUtility转的 rectTransform.transform.position本质上得到的是RectTransform.pivot中心轴在屏幕上的坐标,如果pivot不等于(0.5,0.5),
|
//那么获取到的position就不等于图形的中心点。
|
//试了一晚上,找到了解决办法。
|
|
//用MainCanvas转一次屏幕坐标
|
Vector2 position = RectTransformUtility.WorldToScreenPoint(rootCanvas.worldCamera, rectTransform.transform.position);
|
//注意: 这里的position其实是Pivot点在Screen上的坐标,并不是图形意义上的中心点,在经过下列玄学公式换算才是真的图形中心在屏幕的位置。
|
//公式内算上了rootCanvas.scaleFactor 缩放因子,经测试至少在Canvas Scaler.Expand模式下,什么分辨率和屏幕比都抓的很准,兼容性很强,其他的有待测试。
|
//由于得出来的坐标是左下角为原点,触控输入是左上角为原点,所以要上下反转一下Poco才能用,所以y坐标用Screen.height减去。
|
position.Set(
|
position.x - rectTransform.rect.width * rootCanvas.scaleFactor * (rectTransform.pivot.x - 0.5f),
|
Screen.height - (position.y - rectTransform.rect.height * rootCanvas.scaleFactor * (rectTransform.pivot.y - 0.5f))
|
);
|
pos[0] = position.x / Screen.width;
|
pos[1] = position.y / Screen.height;
|
break;
|
case RenderMode.WorldSpace:
|
Vector2 _pos = RectTransformUtility.WorldToScreenPoint(rootCanvas.worldCamera, rectTransform.transform.position);
|
pos[0] = _pos.x / Screen.width;
|
pos[1] = (Screen.height - _pos.y) / Screen.height;
|
break;
|
default:
|
pos[0] = rect.center.x / (float)Screen.width;
|
pos[1] = rect.center.y / (float)Screen.height;
|
break;
|
}
|
}
|
return pos;
|
}
|
|
private Canvas GetRootCanvas(GameObject gameObject)
|
{
|
Canvas canvas = gameObject.GetComponentInParent<Canvas>();
|
// 如果unity版本小于unity5.5,就用递归的方式取吧,没法直接取rootCanvas
|
// 如果有用到4.6以下版本的话就自己手动在这里添加条件吧
|
#if UNITY_4_6 || UNITY_4_7 || UNITY_4_8 || UNITY_4_9 || UNITY_5_0 || UNITY_5_1 || UNITY_5_2 || UNITY_5_3 || UNITY_5_4
|
if (canvas && canvas.isRootCanvas) {
|
return canvas;
|
} else {
|
if (gameObject.transform.parent.gameObject != null) {
|
return GetRootCanvas(gameObject.transform.parent.gameObject);
|
} else {
|
return null;
|
}
|
}
|
#else
|
if (canvas && canvas.isRootCanvas)
|
{
|
return canvas;
|
}
|
else if (canvas)
|
{
|
return canvas.rootCanvas;
|
}
|
else
|
{
|
return null;
|
}
|
#endif
|
}
|
|
private float[] GameObjectSizeInScreen(Rect rect, RectTransform rectTransform)
|
{
|
float[] size = { 0f, 0f };
|
if (rectTransform)
|
{
|
Canvas rootCanvas = GetRootCanvas(gameObject);
|
RenderMode renderMode = rootCanvas != null ? rootCanvas.renderMode : new RenderMode();
|
switch (renderMode)
|
{
|
case RenderMode.ScreenSpaceCamera:
|
Rect _rect = RectTransformUtility.PixelAdjustRect(rectTransform, rootCanvas);
|
size = new float[] {
|
_rect.width * rootCanvas.scaleFactor / (float)Screen.width,
|
_rect.height * rootCanvas.scaleFactor / (float)Screen.height
|
};
|
break;
|
case RenderMode.WorldSpace:
|
Rect rect_ = rectTransform.rect;
|
RectTransform canvasTransform = rootCanvas.GetComponent<RectTransform>();
|
size = new float[] { rect_.width / canvasTransform.rect.width, rect_.height / canvasTransform.rect.height };
|
break;
|
default:
|
size = new float[] { rect.width / (float)Screen.width, rect.height / (float)Screen.height };
|
break;
|
}
|
}
|
else
|
{
|
size = new float[] { rect.width / (float)Screen.width, rect.height / (float)Screen.height };
|
}
|
return size;
|
}
|
|
private float[] GameObjectAnchorInScreen(Renderer renderer, Rect rect, Vector3 objectPos)
|
{
|
float[] defaultValue = { 0.5f, 0.5f };
|
if (rectTransform)
|
{
|
Vector2 data = rectTransform.pivot;
|
defaultValue[0] = data[0];
|
defaultValue[1] = 1 - data[1];
|
return defaultValue;
|
}
|
if (!renderer)
|
{
|
//<Modified> some object do not have renderer
|
return defaultValue;
|
}
|
float[] anchor = { (objectPos.x - rect.xMin) / rect.width, (objectPos.y - rect.yMin) / rect.height };
|
if (Double.IsNaN(anchor[0]) || Double.IsNaN(anchor[1]))
|
{
|
return defaultValue;
|
}
|
else if (Double.IsPositiveInfinity(anchor[0]) || Double.IsPositiveInfinity(anchor[1]))
|
{
|
return defaultValue;
|
}
|
else if (Double.IsNegativeInfinity(anchor[0]) || Double.IsNegativeInfinity(anchor[1]))
|
{
|
return defaultValue;
|
}
|
else
|
{
|
return anchor;
|
}
|
}
|
|
private string GetImageSourceTexture()
|
{
|
Image image = gameObject.GetComponent<Image>();
|
if (image != null && image.sprite != null)
|
{
|
return image.sprite.name;
|
}
|
|
RawImage rawImage = gameObject.GetComponent<RawImage>();
|
if (rawImage != null && rawImage.texture != null)
|
{
|
return rawImage.texture.name;
|
}
|
|
SpriteRenderer spriteRenderer = gameObject.GetComponent<SpriteRenderer>();
|
if (spriteRenderer != null && spriteRenderer.sprite != null)
|
{
|
return spriteRenderer.sprite.name;
|
}
|
|
Renderer render = gameObject.GetComponent<Renderer>();
|
if (renderer != null && renderer.material != null)
|
{
|
return renderer.material.color.ToString();
|
}
|
|
return null;
|
}
|
|
protected static Vector2 WorldToGUIPoint(Camera camera, Vector3 world)
|
{
|
Vector2 screenPoint = Vector2.zero;
|
if (camera != null)
|
{
|
screenPoint = camera.WorldToScreenPoint(world);
|
screenPoint.y = (float)Screen.height - screenPoint.y;
|
}
|
return screenPoint;
|
}
|
|
protected static Rect RendererToScreenSpace(Camera camera, Renderer renderer)
|
{
|
Vector3 cen = renderer.bounds.center;
|
Vector3 ext = renderer.bounds.extents;
|
Vector2[] extentPoints = new Vector2[8] {
|
WorldToGUIPoint (camera, new Vector3 (cen.x - ext.x, cen.y - ext.y, cen.z - ext.z)),
|
WorldToGUIPoint (camera, new Vector3 (cen.x + ext.x, cen.y - ext.y, cen.z - ext.z)),
|
WorldToGUIPoint (camera, new Vector3 (cen.x - ext.x, cen.y - ext.y, cen.z + ext.z)),
|
WorldToGUIPoint (camera, new Vector3 (cen.x + ext.x, cen.y - ext.y, cen.z + ext.z)),
|
WorldToGUIPoint (camera, new Vector3 (cen.x - ext.x, cen.y + ext.y, cen.z - ext.z)),
|
WorldToGUIPoint (camera, new Vector3 (cen.x + ext.x, cen.y + ext.y, cen.z - ext.z)),
|
WorldToGUIPoint (camera, new Vector3 (cen.x - ext.x, cen.y + ext.y, cen.z + ext.z)),
|
WorldToGUIPoint (camera, new Vector3 (cen.x + ext.x, cen.y + ext.y, cen.z + ext.z))
|
};
|
Vector2 min = extentPoints[0];
|
Vector2 max = extentPoints[0];
|
foreach (Vector2 v in extentPoints)
|
{
|
min = Vector2.Min(min, v);
|
max = Vector2.Max(max, v);
|
}
|
return new Rect(min.x, min.y, max.x - min.x, max.y - min.y);
|
}
|
|
protected static Rect RectTransformToScreenSpace(RectTransform rectTransform)
|
{
|
Vector2 size = Vector2.Scale(rectTransform.rect.size, rectTransform.lossyScale);
|
Rect rect = new Rect(rectTransform.position.x, Screen.height - rectTransform.position.y, size.x, size.y);
|
rect.x -= (rectTransform.pivot.x * size.x);
|
rect.y -= ((1.0f - rectTransform.pivot.y) * size.y);
|
return rect;
|
}
|
|
public static bool SetText(GameObject go, string textVal)
|
{
|
if (go != null)
|
{
|
var inputField = go.GetComponent<InputField>();
|
if (inputField != null)
|
{
|
inputField.text = textVal;
|
return true;
|
}
|
}
|
return false;
|
}
|
}
|
}
|