先修复一下,错误的场景 删除不必要的钓场资产 修复into场景 update:更新meta文件,修复报错 update:修复资源 修复第一章节建造 修复建造第三章 update:删除多余内容 update:更新README.md update:还原图标 update:更新README update:更新配置
1386 lines
45 KiB
C#
1386 lines
45 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using UnityEngine.UI;
|
|
using UnityEngine.U2D;
|
|
using UnityEngine.EventSystems;
|
|
using UnityEngine.AddressableAssets;
|
|
using System.Threading.Tasks;
|
|
using asap.core;
|
|
using System.Linq;
|
|
|
|
namespace GameCore
|
|
{
|
|
public class GetUIAsyncEvent
|
|
{
|
|
public string uIType;
|
|
public bool show = true;
|
|
}
|
|
public enum ScreenType
|
|
{
|
|
INVALID,
|
|
LANDSCAPE,
|
|
PORTRAIT,
|
|
}
|
|
|
|
public class UIType
|
|
{
|
|
public string Path { get; private set; }
|
|
|
|
public string Name { get; private set; }
|
|
public int UIOffset { get; private set; }
|
|
|
|
public UIType(string path)
|
|
{
|
|
Path = path;
|
|
Name = path.Substring(path.LastIndexOf('/') + 1);
|
|
UITypes.UITypesDic[Name] = this;
|
|
}
|
|
|
|
public UIType(string path, int uiOffset)
|
|
{
|
|
Path = path;
|
|
Name = path.Substring(path.LastIndexOf('/') + 1);
|
|
UIOffset = uiOffset;
|
|
UITypes.UITypesDic[Name] = this;
|
|
}
|
|
public void SetType(string path)
|
|
{
|
|
Path = path;
|
|
Name = path.Substring(path.LastIndexOf('/') + 1);
|
|
UITypes.UITypesDic[Name] = this;
|
|
}
|
|
public UIType SetName(string name)
|
|
{
|
|
Name = name;
|
|
UITypes.UITypesDic[Name] = this;
|
|
return this;
|
|
}
|
|
public override string ToString()
|
|
{
|
|
return string.Format("UIType {0}({1})", Name, Path);
|
|
}
|
|
|
|
public override bool Equals(object obj)
|
|
{
|
|
if (obj is UIType otherType)
|
|
{
|
|
if (this.Path == otherType.Path)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return base.Equals(obj);
|
|
}
|
|
|
|
public override int GetHashCode()
|
|
{
|
|
return Path.GetHashCode();
|
|
}
|
|
}
|
|
|
|
class SpriteAtlasCache
|
|
{
|
|
public SpriteAtlas atlas;
|
|
public Dictionary<string, Sprite> spriteDict;
|
|
|
|
public SpriteAtlasCache()
|
|
{
|
|
atlas = null;
|
|
spriteDict = new Dictionary<string, Sprite>();
|
|
}
|
|
}
|
|
|
|
|
|
public class UIManager : MonoBehaviour
|
|
{
|
|
public const int TIME_OUT_MS = 10000;
|
|
public const int BLOCK_DELAY_MS = 1500; // in ms
|
|
public const float TRANSITION_TIMER = 0.3f;
|
|
|
|
public RectTransform WaitingPanel;
|
|
public Image TransitionPanel;
|
|
public Action WaitingTimeOutDefaultCallback;
|
|
private RectTransform alertContainer;
|
|
private UIType[] alertUIList;
|
|
Dictionary<string, GameObject> _UIDict = new Dictionary<string, GameObject>();
|
|
Dictionary<string, GameObject> _UIDictABB = new Dictionary<string, GameObject>();
|
|
//非自动填充,具体业务需求填充
|
|
public Stack<UIType> panelStack = new Stack<UIType>();
|
|
|
|
Dictionary<string, SpriteAtlasCache> _atlasDict = new Dictionary<string, SpriteAtlasCache>();
|
|
Dictionary<string, Dictionary<string, Texture>> _textureDict = new Dictionary<string, Dictionary<string, Texture>>();
|
|
Dictionary<string, Dictionary<string, Sprite>> _spriteDict = new Dictionary<string, Dictionary<string, Sprite>>();
|
|
Transform _topSibling = null; // Assuming there's only one top sibling needed, make it a list otherwise
|
|
int _waitingAnimShowingTimer = int.MaxValue; // in milliseconds
|
|
int _waitingBlockTimeout = 0; // in milliseconds
|
|
float _waitingBlockStartTime;
|
|
WaitingUI waitingUI;
|
|
CanvasScaler canvasScaler;
|
|
public CanvasScaler CanvasScaler => canvasScaler;
|
|
|
|
ScreenOrientation
|
|
prevLandscape; // Save previous Landscape orientation, OS tend to restore this when auto-rotation is on and change screen from portrait to landscape
|
|
|
|
Dictionary<string, Action> _waitingBlockDict = new Dictionary<string, Action>();
|
|
|
|
static public UIManager Instance { get; private set; }
|
|
|
|
// public Camera UICamera { get; private set; }
|
|
|
|
public Canvas UICanvas { get; private set; }
|
|
GraphicRaycaster graphicRaycaster { get; set; }
|
|
|
|
public RectTransform RectTrans { get; private set; }
|
|
|
|
public ScreenType screenType { get; private set; }
|
|
|
|
Rect safeArea;
|
|
|
|
public Camera UI3DCamera { get; private set; }
|
|
|
|
private float anchorMax;
|
|
|
|
/*
|
|
* public static void Init(UIType ui_type, ScreenType screen_type, Action default_waiting_timeout_callback = null)
|
|
* {
|
|
* if (Instance != null || ui_type == null)
|
|
* {
|
|
* return;
|
|
* }
|
|
*
|
|
* GameObject prefab = AssetManager.LoadAsset<GameObject>(ui_type.Path);
|
|
*
|
|
* if (prefab != null)
|
|
* {
|
|
* GameObject main_ui = GameObject.Instantiate(prefab) as GameObject;
|
|
* DontDestroyOnLoad(main_ui);
|
|
*
|
|
* if (main_ui != null)
|
|
* {
|
|
* main_ui.name = ui_type.Name;
|
|
* }
|
|
*
|
|
* Instance = main_ui.AddComponent<UIManager>();
|
|
* Instance.screenType = screen_type;
|
|
* Instance.canvasScaler = main_ui.GetComponent<CanvasScaler>();
|
|
* Instance.WaitingTimeOutDefaultCallback = default_waiting_timeout_callback;
|
|
*
|
|
* var waiting_obj = main_ui.FindChildGameObject("WaitingBlock");
|
|
*
|
|
* if (waiting_obj != null)
|
|
* {
|
|
* var trans = waiting_obj.GetComponent<RectTransform>();
|
|
* Instance.InitWaitingPanel(trans);
|
|
* waiting_obj.SetActiveAsNeed(false);
|
|
* }
|
|
* else
|
|
* {
|
|
* Debug.LogError("[UIManager]Init: Fail to find WaitingBlock GameObject");
|
|
* }
|
|
* }
|
|
* }
|
|
*/
|
|
|
|
public static async Task Init(UIType ui_type, ScreenType screen_type, Action default_waiting_timeout_callback = null)
|
|
{
|
|
if (Instance != null || ui_type == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var main_ui = await Addressables.InstantiateAsync(ui_type.Path).Task;
|
|
|
|
if (main_ui != null)
|
|
{
|
|
DontDestroyOnLoad(main_ui);
|
|
main_ui.name = ui_type.Name;
|
|
|
|
Instance = main_ui.AddComponent<UIManager>();
|
|
Instance.screenType = screen_type;
|
|
Instance.canvasScaler = main_ui.GetComponent<CanvasScaler>();
|
|
Instance.WaitingTimeOutDefaultCallback = default_waiting_timeout_callback;
|
|
|
|
var waiting_obj = main_ui.FindChildGameObject("WaitingBlock");
|
|
|
|
if (waiting_obj != null)
|
|
{
|
|
var trans = waiting_obj.GetComponent<RectTransform>();
|
|
Instance.InitWaitingPanel(trans);
|
|
waiting_obj.SetActiveAsNeed(false);
|
|
}
|
|
else
|
|
{
|
|
Debug.LogError("[UIManager]Init: Fail to find WaitingBlock GameObject");
|
|
}
|
|
|
|
var alertContainer = main_ui.FindChildGameObject("Alert");
|
|
if (alertContainer != null)
|
|
{
|
|
Instance.alertContainer = alertContainer.GetComponent<RectTransform>();
|
|
Instance.alertUIList = new UIType[] { UITypes.NoticeConfirmPopupPanel, UITypes.AccountAbnormalityPopupPanel };
|
|
}
|
|
}
|
|
}
|
|
|
|
public static void SetUICamera(Camera camera, Camera ui3d)
|
|
{
|
|
if (Instance != null)
|
|
{
|
|
Instance.UI3DCamera = ui3d;
|
|
Instance.UI3DCamera.gameObject.SetActive(false);
|
|
Instance.UICanvas.worldCamera = camera;
|
|
camera.orthographicSize = Screen.height * 0.5f;
|
|
}
|
|
}
|
|
|
|
|
|
public static void Destroy()
|
|
{
|
|
Debug.Log("[UIManager]Destroy");
|
|
|
|
if (Instance != null)
|
|
{
|
|
List<string> keys = new List<string>(Instance._UIDict.Keys);
|
|
foreach (var kv in keys)
|
|
{
|
|
Instance.DestroyUI(kv);
|
|
//Addressables.ReleaseInstance(kv.Value);
|
|
}
|
|
|
|
GameObject.DestroyImmediate(Instance.gameObject);
|
|
Instance = null;
|
|
}
|
|
}
|
|
|
|
static EventSystem _eventSystem;
|
|
static float _inputBlockTimer = -1f;
|
|
static Action _onInputBlockTimeOut = null;
|
|
|
|
public static void BlockInput(float block_timer = -1f, Action on_time_out = null)
|
|
{
|
|
if (EventSystem.current != null && EventSystem.current.gameObject != null)
|
|
{
|
|
Debug.Log("[UIManager]BlockInput");
|
|
_eventSystem = EventSystem.current;
|
|
_eventSystem.enabled = false;
|
|
_inputBlockTimer = block_timer;
|
|
_onInputBlockTimeOut = on_time_out;
|
|
}
|
|
}
|
|
|
|
public static void UnblockInput()
|
|
{
|
|
if (_eventSystem != null)
|
|
{
|
|
Debug.Log("[UIManager]UnblockInput");
|
|
_eventSystem.enabled = true;
|
|
_eventSystem = null;
|
|
_onInputBlockTimeOut = null;
|
|
_inputBlockTimer = -1f;
|
|
}
|
|
}
|
|
|
|
public static bool isInputBlocked()
|
|
{
|
|
return _eventSystem != null;
|
|
}
|
|
|
|
public static void RestoreInputBlock()
|
|
{
|
|
if (Instance._waitingBlockDict.Count > 0)
|
|
{
|
|
BlockInput();
|
|
}
|
|
}
|
|
|
|
public static bool IsShowingWaitingBlock(string message_id)
|
|
{
|
|
bool is_showing = false;
|
|
|
|
if (Instance != null)
|
|
{
|
|
is_showing = Instance._IsShowingWaitingBlock(message_id);
|
|
}
|
|
|
|
return is_showing;
|
|
}
|
|
|
|
public static void ClearWaitingBlock()
|
|
{
|
|
if (Instance != null)
|
|
{
|
|
UnblockInput();
|
|
Debug.Log("[UIManager]ClearWaitingBlock");
|
|
Instance._ClearWaitingBlock();
|
|
}
|
|
}
|
|
|
|
public static IEnumerator ChangeScreenOrientation(ScreenType screen_type)
|
|
{
|
|
Debug.Log($"[UIManager]ChangeScreenOrientation: {screen_type}");
|
|
|
|
if (Instance == null || Instance.canvasScaler == null)
|
|
{
|
|
Debug.LogError($"[UIManager]ChangeScreenOrientation: Not found canvasScaler!");
|
|
yield break;
|
|
}
|
|
|
|
var current_width = Instance.RectTrans.rect.width;
|
|
var current_height = Instance.RectTrans.rect.height;
|
|
var target_width = current_height;
|
|
var target_height = current_width;
|
|
var ref_res = Instance.canvasScaler.referenceResolution;
|
|
var target_ref_res = new Vector2(ref_res.y, ref_res.x);
|
|
|
|
if (screen_type == ScreenType.PORTRAIT)
|
|
{
|
|
// Already in the right orientation, just ignore this
|
|
if (!Application.isEditor)
|
|
{
|
|
if (Instance.screenType == ScreenType.PORTRAIT ||
|
|
Screen.orientation == ScreenOrientation.Portrait)
|
|
{
|
|
Debug.LogError(
|
|
$"[UIManager]ChangeScreenOrientation: Already in the screenType = {Instance.screenType}, or Orientation = {Screen.orientation}!");
|
|
yield break;
|
|
}
|
|
}
|
|
|
|
Instance.prevLandscape = Screen.orientation;
|
|
|
|
var target_orientation = ScreenOrientation.Portrait;
|
|
|
|
Screen.orientation = target_orientation;
|
|
Instance.screenType = screen_type;
|
|
|
|
if (!Application.isEditor)
|
|
{
|
|
yield return null;
|
|
|
|
Instance.canvasScaler.referenceResolution = target_ref_res;
|
|
|
|
yield return new WaitUntil(() =>
|
|
{
|
|
return Instance.RectTrans.rect.width == target_width &&
|
|
Instance.RectTrans.rect.height == target_height;
|
|
});
|
|
}
|
|
|
|
Instance.canvasScaler.referenceResolution = target_ref_res;
|
|
|
|
if (Instance.canvasScaler.screenMatchMode == CanvasScaler.ScreenMatchMode.MatchWidthOrHeight)
|
|
{
|
|
Instance.canvasScaler.matchWidthOrHeight = 0;
|
|
}
|
|
}
|
|
else if (screen_type == ScreenType.LANDSCAPE)
|
|
{
|
|
// Already in the right orientation, just ignore this
|
|
if (!Application.isEditor)
|
|
{
|
|
if (Instance.screenType == ScreenType.LANDSCAPE ||
|
|
Screen.orientation == ScreenOrientation.LandscapeLeft ||
|
|
Screen.orientation == ScreenOrientation.LandscapeRight)
|
|
{
|
|
Debug.LogError(
|
|
$"[UIManager]ChangeScreenOrientation: Already in the screenType = {Instance.screenType}, or Orientation = {Screen.orientation}!");
|
|
yield break;
|
|
}
|
|
}
|
|
|
|
var target_orientation = ScreenOrientation.LandscapeLeft;
|
|
|
|
if (Instance.prevLandscape == ScreenOrientation.LandscapeLeft ||
|
|
Instance.prevLandscape == ScreenOrientation.LandscapeRight)
|
|
{
|
|
target_orientation = Instance.prevLandscape;
|
|
}
|
|
|
|
if (Input.deviceOrientation == DeviceOrientation.LandscapeLeft)
|
|
{
|
|
target_orientation = ScreenOrientation.LandscapeLeft;
|
|
}
|
|
else if (Input.deviceOrientation == DeviceOrientation.LandscapeRight)
|
|
{
|
|
target_orientation = ScreenOrientation.LandscapeRight;
|
|
}
|
|
|
|
Screen.orientation = target_orientation;
|
|
Instance.screenType = screen_type;
|
|
|
|
if (!Application.isEditor)
|
|
{
|
|
yield return null;
|
|
|
|
Instance.canvasScaler.referenceResolution = target_ref_res;
|
|
|
|
yield return new WaitUntil(() =>
|
|
{
|
|
return Instance.RectTrans.rect.width == target_width &&
|
|
Instance.RectTrans.rect.height == target_height;
|
|
});
|
|
}
|
|
|
|
Instance.canvasScaler.referenceResolution = target_ref_res;
|
|
|
|
if (Instance.canvasScaler.screenMatchMode == CanvasScaler.ScreenMatchMode.MatchWidthOrHeight)
|
|
{
|
|
Instance.canvasScaler.matchWidthOrHeight = 1;
|
|
}
|
|
|
|
Screen.autorotateToLandscapeLeft = true;
|
|
Screen.autorotateToLandscapeRight = true;
|
|
Screen.autorotateToPortrait = false;
|
|
Screen.autorotateToPortraitUpsideDown = false;
|
|
Screen.orientation = ScreenOrientation.AutoRotation;
|
|
}
|
|
else
|
|
{
|
|
Debug.LogError($"[UIManager]ChangeScreenOrientation: Not handle ScreenType {screen_type}");
|
|
}
|
|
}
|
|
|
|
bool _IsShowingWaitingBlock(string message_id)
|
|
{
|
|
bool is_found = false;
|
|
|
|
if (Instance != null)
|
|
{
|
|
if (_waitingBlockDict.ContainsKey(message_id))
|
|
{
|
|
is_found = true;
|
|
}
|
|
}
|
|
|
|
return is_found;
|
|
}
|
|
|
|
public static void ShowWaitingBlock(string message_id, Action time_out_callback = null,
|
|
int delay_time_ms = BLOCK_DELAY_MS, int timeout_ms = TIME_OUT_MS)
|
|
{
|
|
if (IsShowingWaitingBlock(message_id))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (Instance != null)
|
|
{
|
|
Instance._ShowWaitingBlock(message_id, time_out_callback, delay_time_ms, timeout_ms);
|
|
}
|
|
}
|
|
|
|
private static void ShowForceWaitingBlock()
|
|
{
|
|
if (Instance != null)
|
|
{
|
|
Instance.ShowWaitingBlock(-1);
|
|
}
|
|
}
|
|
|
|
private static void HideForceWaitingBlock()
|
|
{
|
|
if (Instance != null)
|
|
{
|
|
Instance.HideWaitingBlock();
|
|
}
|
|
}
|
|
|
|
public static void HideWaitingBlock(string message_id)
|
|
{
|
|
if (!IsShowingWaitingBlock(message_id))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (Instance != null)
|
|
{
|
|
Instance._HideWaitingBlock(message_id);
|
|
}
|
|
}
|
|
|
|
void _ShowWaitingBlock(string message_id, Action time_out_callback = null, int delay_time_ms = BLOCK_DELAY_MS,
|
|
int timeout_ms = TIME_OUT_MS)
|
|
{
|
|
if (Instance != null)
|
|
{
|
|
if (delay_time_ms > timeout_ms)
|
|
{
|
|
Debug.LogError(
|
|
$"[UIManager]_ShowWaitingBlock: Delay timer {delay_time_ms} shouldn't bigger than timeout timer {timeout_ms}, will set it to timeout timer instead.");
|
|
delay_time_ms = timeout_ms;
|
|
}
|
|
|
|
if (delay_time_ms < _waitingAnimShowingTimer)
|
|
{
|
|
_waitingAnimShowingTimer = delay_time_ms;
|
|
}
|
|
|
|
if (timeout_ms > _waitingBlockTimeout)
|
|
{
|
|
_waitingBlockTimeout = timeout_ms;
|
|
}
|
|
|
|
if (_waitingBlockDict.Count == 0)
|
|
{
|
|
_waitingBlockStartTime = Time.realtimeSinceStartup;
|
|
}
|
|
|
|
if (_waitingBlockDict.ContainsKey(message_id))
|
|
{
|
|
_waitingBlockDict[message_id] = time_out_callback;
|
|
|
|
return;
|
|
}
|
|
|
|
BlockInput();
|
|
|
|
//Debug.LogWithTrace("[UIManager]_ShowWaitingBlock: {0}, delay time {1}", message_id, delay_time_ms);
|
|
Debug.LogFormat("[UIManager]_ShowWaitingBlock: {0}, delay time {1}", message_id, delay_time_ms);
|
|
_waitingBlockDict.Add(message_id, time_out_callback);
|
|
}
|
|
}
|
|
|
|
void _HideWaitingBlock(string message_id)
|
|
{
|
|
Debug.Log($"[UIManager]_HideWaitingBlock: {message_id}");
|
|
|
|
if (_waitingBlockDict.Count == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (_waitingBlockDict.ContainsKey(message_id))
|
|
{
|
|
_waitingBlockDict.Remove(message_id);
|
|
}
|
|
|
|
if (_waitingBlockDict.Count == 0)
|
|
{
|
|
if (WaitingPanel != null)
|
|
{
|
|
WaitingPanel.gameObject.SetActiveAsNeed(false);
|
|
WaitingPanel.GetComponent<CanvasGroup>().alpha = 1;
|
|
}
|
|
|
|
_waitingAnimShowingTimer = int.MaxValue;
|
|
_waitingBlockTimeout = 0;
|
|
_waitingBlockStartTime = 0;
|
|
UnblockInput();
|
|
// Debug.Log("[UIManager]_HideWaitingBlock: No block any more!");
|
|
}
|
|
else
|
|
{
|
|
if (Application.isEditor)
|
|
{
|
|
var etor = _waitingBlockDict.GetEnumerator();
|
|
|
|
while (etor.MoveNext())
|
|
{
|
|
var item_key = etor.Current.Key;
|
|
Debug.Log($"[UIManager]_HideWaitingBlock: {item_key} still waiting block");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool isWaitingBlockShowing()
|
|
{
|
|
return WaitingPanel != null && WaitingPanel.gameObject.activeSelf;
|
|
}
|
|
|
|
// timeout_ms < 0 means keep showing and no time out trigger
|
|
void ShowWaitingBlock(int timeout_ms)
|
|
{
|
|
if (WaitingPanel != null && !WaitingPanel.gameObject.activeSelf)
|
|
{
|
|
WaitingPanel.SetAsLastSibling();
|
|
WaitingPanel.gameObject.SetActiveAsNeed(true);
|
|
}
|
|
|
|
if (waitingUI != null)
|
|
{
|
|
waitingUI.Show(timeout_ms);
|
|
}
|
|
|
|
BlockInput();
|
|
}
|
|
|
|
// Note: Only use this to hide force waiting block
|
|
void HideWaitingBlock()
|
|
{
|
|
|
|
if (WaitingPanel != null)
|
|
{
|
|
WaitingPanel.gameObject.SetActiveAsNeed(false);
|
|
WaitingPanel.GetComponent<CanvasGroup>().alpha = 1;
|
|
}
|
|
|
|
UnblockInput();
|
|
}
|
|
|
|
void _ClearWaitingBlock()
|
|
{
|
|
if (WaitingPanel != null)
|
|
{
|
|
WaitingPanel.gameObject.SetActiveAsNeed(false);
|
|
WaitingPanel.GetComponent<CanvasGroup>().alpha = 1;
|
|
}
|
|
|
|
_waitingBlockDict.Clear();
|
|
}
|
|
|
|
void OnWaitingTimeOut()
|
|
{
|
|
Debug.LogError("[UIManager]OnWaitingTimeOut");
|
|
|
|
bool is_handled = false;
|
|
var etor = _waitingBlockDict.GetEnumerator();
|
|
|
|
while (etor.MoveNext())
|
|
{
|
|
Debug.LogError($"[UIManager]OnWaitingTimeOut: Handling waiting time out of {etor.Current.Key}");
|
|
|
|
var callback = etor.Current.Value;
|
|
|
|
if (callback != null)
|
|
{
|
|
callback();
|
|
is_handled = true;
|
|
}
|
|
|
|
if (_waitingBlockDict.Count <= 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
Debug.Log("[UIManager]OnWaitingTimeOut: No waiting lock any more.");
|
|
UnblockInput();
|
|
_ClearWaitingBlock();
|
|
|
|
if (!is_handled)
|
|
{
|
|
if (WaitingTimeOutDefaultCallback != null)
|
|
{
|
|
WaitingTimeOutDefaultCallback.Invoke();
|
|
}
|
|
}
|
|
}
|
|
|
|
IEnumerator CrossFadeAlpha(Image image, float alpha, float duration, Action on_complete = null)
|
|
{
|
|
image.CrossFadeAlpha(alpha, duration, true);
|
|
|
|
yield return new WaitForSeconds(duration);
|
|
|
|
on_complete?.Invoke();
|
|
}
|
|
|
|
public void ShowTransitionPanel(Action on_complete = null)
|
|
{
|
|
if (TransitionPanel == null)
|
|
{
|
|
Debug.LogError("[UIManager]ShowTransitionPanel: TransitionPanel not found under UIRoot");
|
|
return;
|
|
}
|
|
|
|
Debug.Log("[UIManager]ShowTransitionPanel");
|
|
TransitionPanel.gameObject.SetActiveAsNeed(true);
|
|
StartCoroutine(CrossFadeAlpha(TransitionPanel, 1f, TRANSITION_TIMER, on_complete));
|
|
BlockInput();
|
|
}
|
|
|
|
public void HideTransitionPanel(Action on_complete = null)
|
|
{
|
|
if (TransitionPanel == null)
|
|
{
|
|
Debug.LogError("[UIManager]HideTransitionPanel: TransitionPanel not found under UIRoot");
|
|
return;
|
|
}
|
|
|
|
if (!TransitionPanel.gameObject.activeSelf)
|
|
{
|
|
Debug.Log("[UIManager]HideTransitionPanel: TransitionPanel already hidden");
|
|
return;
|
|
}
|
|
|
|
Debug.Log("[UIManager]HideTransitionPanel");
|
|
UnblockInput();
|
|
|
|
StartCoroutine(CrossFadeAlpha(TransitionPanel, 0f, TRANSITION_TIMER, () =>
|
|
{
|
|
TransitionPanel.gameObject.SetActiveAsNeed(false);
|
|
on_complete?.Invoke();
|
|
}));
|
|
}
|
|
|
|
void Awake()
|
|
{
|
|
Instance = this;
|
|
UICanvas = GetComponent<Canvas>();
|
|
//UICamera = UICanvas.renderMode == RenderMode.ScreenSpaceOverlay ? Camera.main : GetComponentInChildren<Camera>();
|
|
graphicRaycaster = GetComponent<GraphicRaycaster>();
|
|
RectTrans = GetComponent<RectTransform>();
|
|
safeArea = Screen.safeArea;
|
|
Vector2 sizeDelta = RectTrans.sizeDelta;
|
|
float resolutionRatioHeight = sizeDelta.y;
|
|
float heightRatio = resolutionRatioHeight / Screen.height;
|
|
|
|
anchorMax = (Screen.height - safeArea.size.y - safeArea.position.y) * heightRatio;
|
|
|
|
var panel = gameObject.FindChildGameObject("TransitionPanel");
|
|
|
|
if (panel != null)
|
|
{
|
|
TransitionPanel = panel.GetComponent<Image>();
|
|
}
|
|
|
|
if (WaitingPanel != null)
|
|
{
|
|
InitWaitingPanel();
|
|
}
|
|
}
|
|
|
|
void Start()
|
|
{
|
|
// Fix issues about pressing buttons. Sometime very small movements in finger touch can be interpreted as scrolling rather than a simple touch.
|
|
if (EventSystem.current != null)
|
|
{
|
|
int defaultValue = EventSystem.current.pixelDragThreshold;
|
|
EventSystem.current.pixelDragThreshold =
|
|
Mathf.Max(defaultValue, (int)(defaultValue * Screen.dpi / 160f));
|
|
}
|
|
}
|
|
|
|
void InitWaitingPanel(RectTransform trans = null)
|
|
{
|
|
if (trans != null)
|
|
{
|
|
WaitingPanel = trans;
|
|
}
|
|
|
|
if (WaitingPanel != null)
|
|
{
|
|
waitingUI = WaitingPanel.gameObject.GetComponentInChildren<WaitingUI>();
|
|
|
|
if (waitingUI != null)
|
|
{
|
|
waitingUI.OnTimeOut = OnWaitingTimeOut;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Update()
|
|
{
|
|
if (_waitingBlockDict.Count > 0 && !isWaitingBlockShowing())
|
|
{
|
|
float current_time = Time.realtimeSinceStartup;
|
|
var delta_time = current_time - _waitingBlockStartTime;
|
|
|
|
if (delta_time > _waitingAnimShowingTimer * 0.001f)
|
|
{
|
|
ShowWaitingBlock(_waitingBlockTimeout);
|
|
}
|
|
}
|
|
|
|
if (_eventSystem != null && _inputBlockTimer > 0)
|
|
{
|
|
_inputBlockTimer -= Time.unscaledDeltaTime;
|
|
|
|
if (_inputBlockTimer < 0f)
|
|
{
|
|
if (_onInputBlockTimeOut != null)
|
|
{
|
|
_onInputBlockTimeOut();
|
|
_onInputBlockTimeOut = null;
|
|
}
|
|
|
|
UnblockInput();
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool IsShowingUI(UIType ui_type)
|
|
{
|
|
string ui_type_name = ui_type.Name;
|
|
if (_UIDict.ContainsKey(ui_type_name) && null != _UIDict[ui_type_name] && _UIDict[ui_type_name].activeSelf)
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public bool ContainsUI(UIType ui_type)
|
|
{
|
|
string ui_type_name = ui_type.Name;
|
|
return ContainsUI(ui_type_name);
|
|
}
|
|
public bool ContainsUI(string ui_type_name)
|
|
{
|
|
if (_UIDict.ContainsKey(ui_type_name) && null != _UIDict[ui_type_name])
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public GameObject GetContainedUI(UIType ui_type)
|
|
{
|
|
GameObject ui_obj = null;
|
|
string ui_type_name = ui_type.Name;
|
|
if (_UIDict.ContainsKey(ui_type_name))
|
|
{
|
|
ui_obj = _UIDict[ui_type_name];
|
|
|
|
if (ui_obj == null)
|
|
{
|
|
_UIDict.Remove(ui_type_name);
|
|
}
|
|
}
|
|
|
|
return ui_obj;
|
|
}
|
|
|
|
public async void PreloadUI(UIType ui_type, Transform parent = null)
|
|
{
|
|
var obj = await GetUIAsync(ui_type, parent);
|
|
|
|
if (obj != null)
|
|
{
|
|
obj.SetActiveAsNeed(false);
|
|
}
|
|
}
|
|
|
|
public async Task<GameObject> GetUIAsync(UIType ui_type, Transform parent = null)
|
|
{
|
|
GameObject ui_obj = null;
|
|
string ui_type_name = ui_type.Name;
|
|
|
|
if (_UIDict.ContainsKey(ui_type_name))
|
|
{
|
|
ui_obj = _UIDict[ui_type_name];
|
|
|
|
if (ui_obj == null)
|
|
{
|
|
_UIDict.Remove(ui_type_name);
|
|
}
|
|
}
|
|
|
|
if (ui_obj == null && !_UIDict.ContainsKey(ui_type_name))
|
|
{
|
|
graphicRaycaster.enabled = false;
|
|
ui_obj = await LoadUIAssetAsync(ui_type);
|
|
graphicRaycaster.enabled = true;
|
|
}
|
|
|
|
if (ui_type != UITypes.common_tips)
|
|
{
|
|
GetUIAsyncEvent(ui_type_name);
|
|
}
|
|
return ui_obj;
|
|
}
|
|
async void GetUIAsyncEvent(string ui_type_name, bool isShow = true)
|
|
{
|
|
|
|
if (ui_type_name.Contains("_tips") || ui_type_name == UITypes.GuidancePanel.Name || ui_type_name == UITypes.ToastPanel.Name)
|
|
{
|
|
return;
|
|
}
|
|
await Awaiters.NextFrame;
|
|
GContext.Publish(new GetUIAsyncEvent() { uIType = ui_type_name, show = isShow });
|
|
}
|
|
|
|
public void DestroyUI(UIType ui_type, int delay_ms = 0)
|
|
{
|
|
string ui_type_name = ui_type.Name;
|
|
DestroyUI(ui_type_name, delay_ms);
|
|
}
|
|
public async void DestroyUI(string ui_type, int delay_ms = 0)
|
|
{
|
|
async void Release(GameObject obj, int ms)
|
|
{
|
|
await Task.Delay(ms);
|
|
Addressables.Release(obj);
|
|
}
|
|
|
|
if (!_UIDict.ContainsKey(ui_type))
|
|
{
|
|
//JJ.Debug.Log("[UIManager]_UIDict doesn't contain UIType : {0}", uiType);
|
|
return;
|
|
}
|
|
|
|
if (_UIDict[ui_type] != null)
|
|
{
|
|
GameObject ui_obj = _UIDict[ui_type];
|
|
GameObject ui_obj_aab = _UIDictABB[ui_type];
|
|
|
|
if (ui_obj.transform == _topSibling)
|
|
{
|
|
_topSibling = null;
|
|
}
|
|
|
|
if (delay_ms > 0)
|
|
{
|
|
GameObject.Destroy(ui_obj, delay_ms * 0.001f);
|
|
Release(ui_obj_aab, delay_ms);
|
|
}
|
|
else
|
|
{
|
|
GameObject.Destroy(ui_obj);
|
|
Addressables.Release(ui_obj_aab);
|
|
}
|
|
}
|
|
|
|
_UIDict.Remove(ui_type);
|
|
_UIDictABB.Remove(ui_type);
|
|
await Awaiters.NextFrame;
|
|
GetUIAsyncEvent(ui_type, false);
|
|
}
|
|
|
|
public async Task<GameObject> RestartShowUI(UIType ui_type)
|
|
{
|
|
GameObject ui_obj;
|
|
string ui_type_name = ui_type.Name;
|
|
if (_UIDictABB.ContainsKey(ui_type_name))
|
|
{
|
|
if (_UIDict.ContainsKey(ui_type_name))
|
|
{
|
|
GameObject _ui_obj = _UIDict[ui_type_name];
|
|
GameObject.Destroy(_ui_obj);
|
|
_UIDict.Remove(ui_type_name);
|
|
}
|
|
await Awaiters.NextFrame;
|
|
GameObject _ui_obj_abb = _UIDictABB[ui_type_name];
|
|
ui_obj = Instantiate(_ui_obj_abb, transform);
|
|
ui_obj.name = ui_type.Name;
|
|
SetRectTransform(ui_obj.GetComponent<RectTransform>(), ui_type.UIOffset);
|
|
// Make sure top sibling always on top.
|
|
if (_topSibling != null)
|
|
{
|
|
_topSibling.SetAsLastSibling();
|
|
}
|
|
_UIDict[ui_type_name] = ui_obj;
|
|
}
|
|
else
|
|
{
|
|
ui_obj = await GetUIAsync(ui_type);
|
|
}
|
|
|
|
if (ui_obj != null && !ui_obj.activeSelf)
|
|
{
|
|
ui_obj.SetActive(true);
|
|
}
|
|
return ui_obj;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 已经确定提前下好 或不需要下载的Panel 走这个接口
|
|
/// 不显示Loading条
|
|
/// </summary>
|
|
/// <param name="ui_type"></param>
|
|
/// <returns></returns>
|
|
public async Task<GameObject> ShowUINotLoading(UIType ui_type)
|
|
{
|
|
GameObject ui_obj = await GetUIAsync(ui_type);
|
|
if (ui_obj != null && !ui_obj.activeSelf)
|
|
{
|
|
ui_obj.SetActive(true);
|
|
}
|
|
await Awaiters.NextFrame;
|
|
return ui_obj;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 如果需要下载 并且不下载 就返回 Null
|
|
/// </summary>
|
|
/// <param name="ui_type"></param>
|
|
/// <returns></returns>
|
|
public async Task<GameObject> ShowUIPack(UIType ui_type)
|
|
{
|
|
GameObject ui_obj = null;
|
|
string ui_type_name = ui_type.Name;
|
|
if (_UIDict.ContainsKey(ui_type_name))
|
|
{
|
|
ui_obj = _UIDict[ui_type_name];
|
|
if (ui_obj == null)
|
|
{
|
|
_UIDict.Remove(ui_type_name);
|
|
}
|
|
}
|
|
|
|
if (ui_obj == null)
|
|
{
|
|
graphicRaycaster.enabled = false;
|
|
ILoadResourceService loadResourceService = GContext.container.Resolve<ILoadResourceService>();
|
|
bool isCanEnter = await loadResourceService.CheckResource(ui_type.Path);
|
|
if (!isCanEnter)
|
|
{
|
|
//TaskCompletionSource<bool> task = new TaskCompletionSource<bool>();
|
|
//var panel = await ShowUINotLoading(UITypes.DownLoadPopupPanel);
|
|
//panel.GetComponent<DownLoadPopupPanel>().SetBtn(() => { task.SetResult(true); }, () => { task.SetResult(false); });
|
|
//graphicRaycaster.enabled = true;
|
|
//bool result = await task.Task;
|
|
//if (!result)
|
|
//{
|
|
// return null;
|
|
//}
|
|
//else
|
|
//{
|
|
// graphicRaycaster.enabled = false;
|
|
//}
|
|
graphicRaycaster.enabled = true;
|
|
return null;
|
|
}
|
|
ui_obj = await LoadUIAssetAsync(ui_type);
|
|
graphicRaycaster.enabled = true;
|
|
}
|
|
|
|
if (ui_type != UITypes.common_tips)
|
|
{
|
|
GetUIAsyncEvent(ui_type.Name);
|
|
}
|
|
|
|
if (ui_obj != null && !ui_obj.activeSelf)
|
|
{
|
|
ui_obj.SetActive(true);
|
|
}
|
|
|
|
await Awaiters.NextFrame;
|
|
return ui_obj;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 不确定是否下载走这个接口 弹loading动画
|
|
/// </summary>
|
|
/// <param name="ui_type"></param>
|
|
/// <returns></returns>
|
|
|
|
public async Task<GameObject> ShowUILoad(UIType ui_type)
|
|
{
|
|
GameObject ui_obj = null;
|
|
string ui_type_name = ui_type.Name;
|
|
if (_UIDict.ContainsKey(ui_type_name))
|
|
{
|
|
ui_obj = _UIDict[ui_type_name];
|
|
if (ui_obj == null)
|
|
{
|
|
_UIDict.Remove(ui_type_name);
|
|
}
|
|
}
|
|
|
|
if (ui_obj == null)
|
|
{
|
|
graphicRaycaster.enabled = false;
|
|
ILoadResourceService loadResourceService = GContext.container.Resolve<ILoadResourceService>();
|
|
bool isCanEnter = await loadResourceService.Load(ui_type.Path);
|
|
if (!isCanEnter)
|
|
{
|
|
TaskCompletionSource<bool> task = new TaskCompletionSource<bool>();
|
|
var panel = await ShowUINotLoading(UITypes.CloudTransitionPanel);
|
|
panel.GetComponent<CloudTransitionPanel>().SetBtn(false, () => { task.SetResult(true); }, () => { task.SetResult(false); });
|
|
graphicRaycaster.enabled = true;
|
|
bool result = await task.Task;
|
|
if (!result)
|
|
{
|
|
return null;
|
|
}
|
|
else
|
|
{
|
|
graphicRaycaster.enabled = false;
|
|
}
|
|
}
|
|
ui_obj = await LoadUIAssetAsync(ui_type);
|
|
graphicRaycaster.enabled = true;
|
|
}
|
|
|
|
if (ui_type != UITypes.common_tips)
|
|
{
|
|
GetUIAsyncEvent(ui_type_name);
|
|
}
|
|
|
|
if (ui_obj != null && !ui_obj.activeSelf)
|
|
{
|
|
ui_obj.SetActive(true);
|
|
}
|
|
|
|
await Awaiters.NextFrame;
|
|
return ui_obj;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 请使用 ShowUILoad 并做好判空处理
|
|
/// 如果确定已经下载好或者不需要下载 请使用 ShowUINotLoading
|
|
/// </summary>
|
|
/// <param name="ui_type"></param>
|
|
/// <returns></returns>
|
|
public async Task<GameObject> ShowUI(UIType ui_type)
|
|
{
|
|
GameObject ui_obj = null;
|
|
string ui_type_name = ui_type.Name;
|
|
if (_UIDict.ContainsKey(ui_type_name))
|
|
{
|
|
ui_obj = _UIDict[ui_type_name];
|
|
if (ui_obj == null)
|
|
{
|
|
_UIDict.Remove(ui_type_name);
|
|
}
|
|
}
|
|
|
|
if (ui_obj == null)
|
|
{
|
|
graphicRaycaster.enabled = false;
|
|
ui_obj = await LoadUIAssetAsync(ui_type);
|
|
graphicRaycaster.enabled = true;
|
|
}
|
|
|
|
if (ui_type != UITypes.common_tips)
|
|
{
|
|
GetUIAsyncEvent(ui_type_name);
|
|
}
|
|
|
|
if (ui_obj != null)
|
|
{
|
|
if (!ui_obj.activeSelf)
|
|
ui_obj.SetActive(true);
|
|
ui_obj.transform.SetAsLastSibling();
|
|
}
|
|
|
|
await Awaiters.NextFrame;
|
|
return ui_obj;
|
|
}
|
|
|
|
void SetRectTransform(RectTransform rect, float UIOffset)
|
|
{
|
|
if (UIOffset > -1)
|
|
{
|
|
float min = 1.777778f;
|
|
float aspect = (float)Screen.height / Screen.width;
|
|
float offset = (Math.Clamp(aspect, min, 2.16f) - min) / (2.16f - min) * UIOffset;
|
|
rect.offsetMin = new Vector2(0, offset + 20);
|
|
float anchorMaxY = anchorMax > offset ? anchorMax : offset;
|
|
rect.offsetMax = new Vector2(0, -anchorMaxY);
|
|
}
|
|
}
|
|
|
|
async Task<GameObject> LoadUIAssetAsync(UIType ui_type)
|
|
{
|
|
if(string.IsNullOrEmpty(ui_type.Path))
|
|
{
|
|
Debug.LogError($"[UIManager]LoadUIAssetAsync: UIType path is null or empty: {ui_type.Name}");
|
|
return null;
|
|
}
|
|
var add = await Addressables.LoadAssetAsync<GameObject>(ui_type.Path).Task;
|
|
GameObject ui_obj = null;
|
|
string ui_type_name = ui_type.Name;
|
|
if (add != null)
|
|
{
|
|
if (!_UIDict.ContainsKey(ui_type_name))
|
|
{
|
|
var par = alertUIList.Contains(ui_type) ? alertContainer : transform;
|
|
ui_obj = Instantiate(add, par);
|
|
ui_obj.name = ui_type.Name;
|
|
SetRectTransform(ui_obj.GetComponent<RectTransform>(), ui_type.UIOffset);
|
|
// Make sure top sibling always on top.
|
|
if (_topSibling != null)
|
|
{
|
|
_topSibling.SetAsLastSibling();
|
|
}
|
|
_UIDictABB[ui_type_name] = add;
|
|
_UIDict.Add(ui_type_name, ui_obj);
|
|
}
|
|
else
|
|
{
|
|
ui_obj = _UIDict[ui_type_name];
|
|
Addressables.Release(add);
|
|
}
|
|
}
|
|
return ui_obj;
|
|
}
|
|
|
|
|
|
public async void HideUI(UIType ui_type)
|
|
{
|
|
string ui_type_name = ui_type.Name;
|
|
if (_UIDict.ContainsKey(ui_type_name) == false || _UIDict[ui_type_name] == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
GameObject ui_obj = _UIDict[ui_type_name];
|
|
|
|
if (ui_obj.transform == _topSibling)
|
|
{
|
|
_topSibling = null;
|
|
}
|
|
|
|
if (ui_obj.activeSelf)
|
|
{
|
|
ui_obj.SetActive(false);
|
|
await Awaiters.NextFrame;
|
|
GetUIAsyncEvent(ui_type_name, false);
|
|
}
|
|
}
|
|
|
|
public void SetAlwaysTopSibling(Transform top_transform)
|
|
{
|
|
_topSibling = top_transform;
|
|
}
|
|
|
|
// public bool IsUIEnable()
|
|
// {
|
|
// return UICamera.enabled;
|
|
// }
|
|
//
|
|
// public void ShowUI()
|
|
// {
|
|
// UICamera.enabled = true;
|
|
// }
|
|
//
|
|
// public void HideUI()
|
|
// {
|
|
// UICamera.enabled = false;
|
|
// }
|
|
|
|
// Put the given object transform on top of all the UI
|
|
public void PutOnTop(Transform ui_trans)
|
|
{
|
|
ui_trans.SetParent(RectTrans);
|
|
ui_trans.SetAsLastSibling();
|
|
|
|
// Make sure top sibling always on top.
|
|
if (_topSibling != null)
|
|
{
|
|
_topSibling.SetAsLastSibling();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Clone the first Sprite in this atlas that matches the name packed in this atlas and return it.
|
|
/// Note: Using AssetBundle.GetAsset<Sprite> will not clone, and will be better.
|
|
/// </summary>
|
|
/// <param name="atlas_name"></param>
|
|
/// <param name="sprite_name"></param>
|
|
/// <returns></returns>
|
|
public Sprite GetSprite(string atlas_name, string sprite_name)
|
|
{
|
|
Sprite sprite_ret = null;
|
|
|
|
if (_atlasDict.TryGetValue(atlas_name, out var atlas_cache))
|
|
{
|
|
if (atlas_cache.spriteDict.ContainsKey(sprite_name))
|
|
{
|
|
sprite_ret = atlas_cache.spriteDict[sprite_name];
|
|
}
|
|
else
|
|
{
|
|
sprite_ret = atlas_cache.atlas.GetSprite(sprite_name);
|
|
atlas_cache.spriteDict[sprite_name] = sprite_ret;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//var atlas = AssetManager.LoadAsset<SpriteAtlas>(atlas_name);
|
|
var atlas = Addressables.LoadAssetAsync<SpriteAtlas>(atlas_name).WaitForCompletion();
|
|
|
|
if (atlas != null)
|
|
{
|
|
var new_atlas_cache = new SpriteAtlasCache
|
|
{
|
|
atlas = atlas
|
|
};
|
|
sprite_ret = atlas.GetSprite(sprite_name);
|
|
new_atlas_cache.spriteDict[sprite_name] = sprite_ret;
|
|
}
|
|
}
|
|
|
|
if (sprite_ret == null)
|
|
{
|
|
Debug.LogError($"[UIManager]GetSprite: Not found sprite {sprite_name} in Atlas {atlas_name}");
|
|
}
|
|
|
|
return sprite_ret;
|
|
}
|
|
|
|
public void ClearAllUIAtlasRef()
|
|
{
|
|
_atlasDict.Clear();
|
|
}
|
|
|
|
public void ReleaseSpriteDic(string dicName)
|
|
{
|
|
if (!string.IsNullOrEmpty(dicName) && _spriteDict.TryGetValue(dicName, out var sprites))
|
|
{
|
|
foreach (var item in sprites)
|
|
{
|
|
Addressables.Release(item.Value);
|
|
}
|
|
_spriteDict.Remove(dicName);
|
|
}
|
|
}
|
|
public async Task SetTexture(RawImage rawImage, string textureName, string dicName = "dicName")
|
|
{
|
|
Texture texture;
|
|
if (_textureDict.TryGetValue(dicName, out var textures))
|
|
{
|
|
if (textures.TryGetValue(textureName, out texture))
|
|
{
|
|
rawImage.texture = texture;
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
textures = new Dictionary<string, Texture>();
|
|
_textureDict.Add(dicName, textures);
|
|
}
|
|
|
|
texture = await Addressables.LoadAssetAsync<Texture>(textureName).Task;
|
|
if (texture != null)
|
|
{
|
|
if (textures.ContainsKey(textureName))
|
|
{
|
|
Addressables.Release(texture);
|
|
rawImage.texture = textures[textureName];
|
|
}
|
|
else
|
|
{
|
|
rawImage.texture = texture;
|
|
textures[textureName] = texture;
|
|
}
|
|
}
|
|
}
|
|
public void ReleaseTextureDic(string dicName)
|
|
{
|
|
if (!string.IsNullOrEmpty(dicName) && _textureDict.TryGetValue(dicName, out var textures))
|
|
{
|
|
foreach (var item in textures)
|
|
{
|
|
Addressables.Release(item.Value);
|
|
}
|
|
_textureDict.Remove(dicName);
|
|
}
|
|
}
|
|
|
|
public Vector2 WorldToScreen(Vector3 worldPos)
|
|
{
|
|
Camera camera;
|
|
if (UICanvas != null && UICanvas.worldCamera != null)
|
|
camera = UICanvas.worldCamera;
|
|
else
|
|
camera = Camera.main;
|
|
var screenPoint = RectTransformUtility.WorldToScreenPoint(Camera.main, worldPos);
|
|
var res = camera.ScreenToWorldPoint(screenPoint);
|
|
return res;
|
|
}
|
|
}
|
|
}
|