もっと便利に
少し前に書いたこちらで、Canvas設定について書いたのでこれで十分なのですが・・・
もう少し便利になるように、Canvas用のコンポーネントを作りました。
右往左往した結果、~3とかになってしまっていますけど、気にしてはいけません。
作ったばかりなのでバグもあったり、意図通り動かなかったりするかもしれませんが、気にしてはいけません。
動作させるとこんな感じ


パラメーターとか
Target Area | キャンバス直下に作ったゲームオブジェクトを登録して、そのオブジェクトの下位にUIを配置していくことを想定しています。 |
Reference Resolution | "Canvas Scaler"と同じです。 |
Safe Rect | 広告枠の確保とかで使えます。720x1150にして130のSafe Rectを設定するとかです。 |
残りは・・・ | 上の動画をみて興味があったら実際に使ってみてください! リクエストがあれば、また改めて詳しく説明したり? |
ソースコード
※ファイルは2つあります。
using System.Collections; | |
using System.Collections.Generic; | |
using UnityEngine; | |
public static class SafeAreaHelper | |
{ | |
/// <summary> | |
/// アンカー | |
/// </summary> | |
public enum Anchor | |
{ | |
/// <summary> | |
/// 左側 | |
/// </summary> | |
Left, | |
/// <summary> | |
/// 右側 | |
/// </summary> | |
Right, | |
/// <summary> | |
/// 上側 | |
/// </summary> | |
Top, | |
/// <summary> | |
/// 下側 | |
/// </summary> | |
Bottom, | |
} | |
/// <summary> | |
/// RectOffsetの簡易Float版 | |
/// </summary> | |
public class RectFloatOffset | |
{ | |
/// <summary> | |
/// 左側 | |
/// </summary> | |
public float left; | |
/// <summary> | |
/// 右側 | |
/// </summary> | |
public float right; | |
/// <summary> | |
/// 上側 | |
/// </summary> | |
public float top; | |
/// <summary> | |
/// 下側 | |
/// </summary> | |
public float bottom; | |
/// <summary> | |
/// 水平 | |
/// </summary> | |
public float Horizontal { get { return left + right; } } | |
/// <summary> | |
/// 垂直 | |
/// </summary> | |
public float Vertical { get { return top + bottom; } } | |
/// <summary> | |
/// コンストラクター | |
/// </summary> | |
public RectFloatOffset() | |
{ | |
left = 0; | |
right = 0; | |
top = 0; | |
bottom = 0; | |
} | |
/// <summary> | |
/// コンストラクター | |
/// </summary> | |
/// <param name="targetLeft">左側</param> | |
/// <param name="targetRight">右側</param> | |
/// <param name="targetTop">上側</param> | |
/// <param name="targetBottom">下側</param> | |
public RectFloatOffset(float targetLeft, float targetRight, float targetTop, float targetBottom) | |
{ | |
left = targetLeft; | |
right = targetRight; | |
top = targetTop; | |
bottom = targetBottom; | |
} | |
} | |
public static Rect RectTransformToScreen(Canvas canvas, RectTransform targetRectTransform,float scaledDensity) | |
{ | |
var canvasRect = canvas.GetComponent<RectTransform>(); | |
var scaleX = Screen.width / canvasRect.sizeDelta.x; | |
var scaleY = Screen.height / canvasRect.sizeDelta.y; | |
var worldCorners = new Vector3[4]; | |
targetRectTransform.GetWorldCorners(worldCorners); | |
worldCorners[0] = canvas.transform.InverseTransformPoint(worldCorners[0]); | |
worldCorners[1] = canvas.transform.InverseTransformPoint(worldCorners[1]); | |
worldCorners[2] = canvas.transform.InverseTransformPoint(worldCorners[2]); | |
worldCorners[3] = canvas.transform.InverseTransformPoint(worldCorners[3]); | |
float x = worldCorners[0].x * scaleX; | |
float y = worldCorners[1].y * scaleY; | |
float width = (worldCorners[3].x - worldCorners[0].x) * scaleX; | |
float height = (worldCorners[1].y - worldCorners[0].y) * scaleY; | |
x += Screen.width * 0.5f; | |
y += Screen.height * 0.5f; | |
y = Screen.height - y; | |
x /= scaledDensity; | |
y /= scaledDensity; | |
width /= scaledDensity; | |
height /= scaledDensity; | |
return new Rect(x, y, width, height); | |
} | |
public static Rect TransformToScreen(Camera targetCamera, SpriteRenderer targetSprite,float scaledDensity) | |
{ | |
var worldCenter = targetSprite.transform.position; | |
var worldSize = targetSprite.transform.lossyScale; | |
worldSize.x *= targetSprite.sprite.textureRect.width; | |
worldSize.y *= targetSprite.sprite.textureRect.height; | |
worldSize.x /= targetSprite.sprite.pixelsPerUnit; | |
worldSize.y /= targetSprite.sprite.pixelsPerUnit; | |
var worldCorners = new Vector3[4]; | |
worldCorners[0] = worldCenter; | |
worldCorners[0].x -= worldSize.x * 0.5f; | |
worldCorners[0].y -= worldSize.y * 0.5f; | |
worldCorners[1] = worldCenter; | |
worldCorners[1].x -= worldSize.x * 0.5f; | |
worldCorners[1].y += worldSize.y * 0.5f; | |
worldCorners[2] = worldCenter; | |
worldCorners[2].x += worldSize.x * 0.5f; | |
worldCorners[2].y += worldSize.y * 0.5f; | |
worldCorners[3] = worldCenter; | |
worldCorners[3].x += worldSize.x * 0.5f; | |
worldCorners[3].y -= worldSize.y * 0.5f; | |
worldCorners[0] = RectTransformUtility.WorldToScreenPoint(targetCamera, worldCorners[0]); | |
worldCorners[1] = RectTransformUtility.WorldToScreenPoint(targetCamera, worldCorners[1]); | |
worldCorners[2] = RectTransformUtility.WorldToScreenPoint(targetCamera, worldCorners[2]); | |
worldCorners[3] = RectTransformUtility.WorldToScreenPoint(targetCamera, worldCorners[3]); | |
float x = worldCorners[0].x; | |
float y = worldCorners[1].y; | |
float width = (worldCorners[3].x - worldCorners[0].x); | |
float height = (worldCorners[1].y - worldCorners[0].y); | |
y = Screen.height - y; | |
x /= scaledDensity; | |
y /= scaledDensity; | |
width /= scaledDensity; | |
height /= scaledDensity; | |
return new Rect(x, y, width, height); | |
} | |
} |

コンポーネントのほうです。
using System.Collections; | |
using UnityEngine; | |
using UnityEngine.UI; | |
namespace Chigusa | |
{ | |
/// <summary> | |
/// セーフエリアを含んだキャンバス | |
/// </summary> | |
[ExecuteInEditMode] | |
[DisallowMultipleComponent] | |
[RequireComponent(typeof(RectTransform))] | |
[RequireComponent(typeof(Canvas))] | |
[RequireComponent(typeof(CanvasScaler))] | |
public class CanvasInSafeArea3 : MonoBehaviour | |
{ | |
[Tooltip("対象のパネル")] | |
public RectTransform targetArea; | |
[Tooltip("基準解像度")] | |
public Vector2 referenceResolution = new Vector2(720, 1280); | |
[Tooltip("セーフエリア")] | |
public RectOffset safeRect; | |
[Tooltip("セーフオフセット")] | |
public Vector2 safeOffset; | |
[Tooltip("セーフハーフオフセット")] | |
public bool safeHalfOffset; | |
[Tooltip("アンカー")] | |
public TextAnchor anchor = TextAnchor.MiddleCenter; | |
[Tooltip("横への自動ストレッチ")] | |
public bool stretchWidth = false; | |
[Tooltip("縦への自動ストレッチ")] | |
public bool stretchHeight = false; | |
protected Canvas TargetCanvas { get; set; } | |
protected RectTransform TargetCanvasRect { get; set; } | |
protected CanvasScaler TargetCanvasScaler { get; set; } | |
/// <summary> | |
/// Awake | |
/// </summary> | |
private void Awake() | |
{ | |
TargetCanvas = GetComponentInParent<Canvas>(); | |
TargetCanvasRect = TargetCanvas.GetComponent<RectTransform>(); | |
TargetCanvasScaler = GetComponentInParent<CanvasScaler>(); | |
} | |
/// <summary> | |
/// Start、初期数フレームは安定しないのである程度まわす | |
/// </summary> | |
/// <returns>IEnumerator</returns> | |
IEnumerator Start() | |
{ | |
for (int index = 0; index < 5; index++) | |
{ | |
Setting(); | |
yield return 0; | |
} | |
} | |
#if UNITY_EDITOR | |
/// <summary> | |
/// Editor用 | |
/// </summary> | |
void LateUpdate() | |
{ | |
TargetCanvas = GetComponentInParent<Canvas>(); | |
TargetCanvasRect = TargetCanvas.GetComponent<RectTransform>(); | |
TargetCanvasScaler = GetComponentInParent<CanvasScaler>(); | |
if (!UnityEditor.EditorApplication.isPlaying) | |
Setting(); | |
} | |
#endif | |
/// <summary> | |
/// 設定 | |
/// </summary> | |
void Setting() | |
{ | |
if (!targetArea) | |
return; | |
TargetCanvasScaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize; | |
TargetCanvasScaler.referenceResolution = referenceResolution; | |
TargetCanvasScaler.screenMatchMode = CanvasScaler.ScreenMatchMode.Expand; | |
var tempRect = GetSafeRectOffset(true); | |
var tempRectOffset = GetSafeRectOffset2(true); | |
var tempOffsetArea = GetOffsetArea(); | |
var positionX = tempRectOffset.left - tempRectOffset.right; | |
var positionY = tempRectOffset.top - tempRectOffset.bottom; | |
var scale = GetSafeScale(true); | |
// ストレッチ | |
var tempVector = referenceResolution; | |
if (stretchWidth) | |
tempVector.x = Mathf.Max(tempVector.x, (TargetCanvasRect.sizeDelta.x - tempRect.Horizontal) / scale); | |
if (stretchHeight) | |
tempVector.y = Mathf.Max(tempVector.y, (TargetCanvasRect.sizeDelta.y - tempRect.Vertical) / scale); | |
// 配置 | |
targetArea.sizeDelta = tempVector; | |
var tempPivot = new Vector2(0.5f, 0.5f); | |
targetArea.localScale = new Vector3(scale, scale, 1); | |
{ | |
var tempOffsetArea2 = tempOffsetArea; | |
if (safeHalfOffset) | |
{ | |
tempOffsetArea2.left *= 0.5f; | |
tempOffsetArea2.right *= 0.5f; | |
tempOffsetArea2.top *= 0.5f; | |
tempOffsetArea2.bottom *= 0.5f; | |
} | |
if (anchor == TextAnchor.MiddleCenter) | |
{ | |
positionX += Mathf.Clamp(safeOffset.x, -tempOffsetArea2.left, tempOffsetArea2.right); | |
positionY += Mathf.Clamp(-safeOffset.y, -tempOffsetArea2.top, tempOffsetArea2.bottom); | |
} | |
switch (anchor) | |
{ | |
case TextAnchor.LowerCenter: | |
case TextAnchor.LowerLeft: | |
case TextAnchor.LowerRight: | |
tempPivot.y = 0; | |
positionY = -tempRect.bottom; | |
positionY += Mathf.Clamp(-safeOffset.y, -(tempOffsetArea2.top + tempOffsetArea2.bottom), 0); | |
break; | |
case TextAnchor.UpperCenter: | |
case TextAnchor.UpperLeft: | |
case TextAnchor.UpperRight: | |
tempPivot.y = 1; | |
positionY = tempRect.top; | |
positionY += Mathf.Clamp(-safeOffset.y, 0, tempOffsetArea2.top + tempOffsetArea2.bottom); | |
break; | |
} | |
switch (anchor) | |
{ | |
case TextAnchor.LowerLeft: | |
case TextAnchor.MiddleLeft: | |
case TextAnchor.UpperLeft: | |
tempPivot.x = 0; | |
positionX = tempRect.left; | |
positionX += Mathf.Clamp(safeOffset.x, 0, tempOffsetArea2.left + tempOffsetArea2.right); | |
break; | |
case TextAnchor.LowerRight: | |
case TextAnchor.MiddleRight: | |
case TextAnchor.UpperRight: | |
tempPivot.x = 1; | |
positionX = -tempRect.right; | |
positionX += Mathf.Clamp(safeOffset.x, -(tempOffsetArea2.left + tempOffsetArea2.right), 0); | |
break; | |
} | |
} | |
targetArea.pivot = tempPivot; | |
targetArea.anchorMin = tempPivot; | |
targetArea.anchorMax = tempPivot; | |
targetArea.anchoredPosition3D = new Vector3(positionX, -positionY, 0); | |
} | |
/// <summary> | |
/// 範囲の取得 | |
/// </summary> | |
/// <param name="useSafe">useSafe</param> | |
/// <returns>範囲</returns> | |
public SafeAreaHelper.RectFloatOffset GetSafeRectOffset(bool addDeviceSafeArea) | |
{ | |
var result = new SafeAreaHelper.RectFloatOffset(0, 0, 0, 0); | |
if (safeRect != null) | |
{ | |
result.left += safeRect.left; | |
result.right += safeRect.right; | |
result.top += safeRect.top; | |
result.bottom += safeRect.bottom; | |
} | |
if (addDeviceSafeArea) | |
{ | |
var addRect = AddDeviceSafeArea(); | |
result.left += addRect.left; | |
result.right += addRect.right; | |
result.top += addRect.top; | |
result.bottom += addRect.bottom; | |
} | |
return result; | |
} | |
/// <summary> | |
/// SafeScaleの取得 | |
/// </summary> | |
/// <returns>値</returns> | |
public float GetSafeScale(bool addDeviceSafeArea) | |
{ | |
var tempRect = GetSafeRectOffset(addDeviceSafeArea); | |
var scaleX = (TargetCanvasRect.sizeDelta.x - tempRect.Horizontal) / referenceResolution.x; | |
var scaleY = (TargetCanvasRect.sizeDelta.y - tempRect.Vertical) / referenceResolution.y; | |
var scale = Mathf.Min(scaleX, scaleY); | |
scale = Mathf.Min(1, scale); | |
return scale; | |
} | |
/// <summary> | |
/// 範囲の取得 | |
/// </summary> | |
/// <param name="useSafe">useSafe</param> | |
/// <returns>範囲</returns> | |
public SafeAreaHelper.RectFloatOffset GetSafeRectOffset2(bool addDeviceSafeArea) | |
{ | |
var tempRect = GetSafeRectOffset(addDeviceSafeArea); | |
var offsetMinX = Mathf.Max(0, tempRect.left - tempRect.Horizontal * 0.5f); | |
var offsetMinY = Mathf.Max(0, tempRect.top - tempRect.Vertical * 0.5f); | |
var offsetMaxX = Mathf.Max(0, tempRect.right - tempRect.Horizontal * 0.5f); | |
var offsetMaxY = Mathf.Max(0, tempRect.bottom - tempRect.Vertical * 0.5f); | |
return new SafeAreaHelper.RectFloatOffset(offsetMinX, offsetMaxX, offsetMinY, offsetMaxY); | |
} | |
/// <summary> | |
/// デバイスセーフエリアの追加分を取得する | |
/// </summary> | |
/// <returns>エリア</returns> | |
SafeAreaHelper.RectFloatOffset AddDeviceSafeArea() | |
{ | |
var scaleX = TargetCanvasRect.sizeDelta.x / Screen.width; | |
var scaleY = TargetCanvasRect.sizeDelta.y / Screen.height; | |
var result = new SafeAreaHelper.RectFloatOffset() | |
{ | |
left = (int)(Screen.safeArea.xMin * scaleX), | |
right = (int)((Screen.width - Screen.safeArea.xMax) * scaleX), | |
top = (int)((Screen.height - Screen.safeArea.yMax) * scaleY), | |
bottom = (int)(Screen.safeArea.yMin * scaleY), | |
}; | |
Vector2 sizeDelta = Vector2.zero; | |
GetOutRect(ref sizeDelta, SafeAreaHelper.Anchor.Left); | |
result.left = Mathf.Max(0, result.left - sizeDelta.x); | |
GetOutRect(ref sizeDelta, SafeAreaHelper.Anchor.Right); | |
result.right = Mathf.Max(0, result.right - sizeDelta.x); | |
GetOutRect(ref sizeDelta, SafeAreaHelper.Anchor.Top); | |
result.top = Mathf.Max(0, result.top - sizeDelta.y); | |
GetOutRect(ref sizeDelta, SafeAreaHelper.Anchor.Bottom); | |
result.bottom = Mathf.Max(0, result.bottom - sizeDelta.y); | |
return result; | |
} | |
/// <summary> | |
/// 余白を取得する | |
/// </summary> | |
/// <returns>余白</returns> | |
public SafeAreaHelper.RectFloatOffset GetOffsetArea() | |
{ | |
var scaleX = TargetCanvasRect.sizeDelta.x / Screen.width; | |
var scaleY = TargetCanvasRect.sizeDelta.y / Screen.height; | |
var deviceAreaRect = new SafeAreaHelper.RectFloatOffset() | |
{ | |
left = (int)(Screen.safeArea.xMin * scaleX), | |
right = (int)((Screen.width - Screen.safeArea.xMax) * scaleX), | |
top = (int)((Screen.height - Screen.safeArea.yMax) * scaleY), | |
bottom = (int)(Screen.safeArea.yMin * scaleY), | |
}; | |
var result = new SafeAreaHelper.RectFloatOffset(); | |
Vector2 sizeDelta = Vector2.zero; | |
GetOutRect(ref sizeDelta, SafeAreaHelper.Anchor.Left); | |
result.left = Mathf.Max(0, sizeDelta.x - deviceAreaRect.left - safeRect.left); | |
GetOutRect(ref sizeDelta, SafeAreaHelper.Anchor.Right); | |
result.right = Mathf.Max(0, sizeDelta.x - deviceAreaRect.right - safeRect.right); | |
GetOutRect(ref sizeDelta, SafeAreaHelper.Anchor.Top); | |
result.top = Mathf.Max(0, sizeDelta.y - deviceAreaRect.top - safeRect.top); | |
GetOutRect(ref sizeDelta, SafeAreaHelper.Anchor.Bottom); | |
result.bottom = Mathf.Max(0, sizeDelta.y - deviceAreaRect.bottom - safeRect.bottom); | |
return result; | |
} | |
/// <summary> | |
/// 範囲外の取得 | |
/// </summary> | |
/// <param name="sizeDelta">出力されるサイズ</param> | |
/// <param name="targetAnchor">anchor</param> | |
/// <param name="targetUseSafe">useSafe</param> | |
/// <param name="targetUseDeviceSafe">useDeviceSafe</param> | |
/// <param name="targetInSafeRect">inSafeRect</param> | |
/// <param name="safeArea">safeArea</param> | |
/// <param name="addDeviceSafeArea">addDeviceSafeArea</param> | |
public void GetOutRect(ref Vector2 sizeDelta, SafeAreaHelper.Anchor targetAnchor) | |
{ | |
var tempRectOffset = GetSafeRectOffset2(false); | |
var positionX = tempRectOffset.left - tempRectOffset.right; | |
var positionY = tempRectOffset.top - tempRectOffset.bottom; | |
var scale = GetSafeScale(false); | |
sizeDelta.x = 0; | |
sizeDelta.y = 0; | |
switch (targetAnchor) | |
{ | |
case SafeAreaHelper.Anchor.Left: | |
sizeDelta.x = TargetCanvasRect.sizeDelta.x * 0.5f + positionX - referenceResolution.x * scale * 0.5f; | |
sizeDelta.y = TargetCanvasRect.sizeDelta.y; | |
break; | |
case SafeAreaHelper.Anchor.Right: | |
sizeDelta.x = TargetCanvasRect.sizeDelta.x * 0.5f - positionX - referenceResolution.x * scale * 0.5f; | |
sizeDelta.y = TargetCanvasRect.sizeDelta.y; | |
break; | |
case SafeAreaHelper.Anchor.Top: | |
sizeDelta.y = TargetCanvasRect.sizeDelta.y * 0.5f + positionY - referenceResolution.y * scale * 0.5f; | |
sizeDelta.x = TargetCanvasRect.sizeDelta.x; | |
break; | |
case SafeAreaHelper.Anchor.Bottom: | |
sizeDelta.y = TargetCanvasRect.sizeDelta.y * 0.5f - positionY - referenceResolution.y * scale * 0.5f; | |
sizeDelta.x = TargetCanvasRect.sizeDelta.x; | |
break; | |
} | |
} | |
} | |
} |


0 件のコメント :
コメントを投稿