Zenn
😷

【Roblox】UIのマスクについて

2025/03/28に公開

はじめに

RobloxでUIのマスクを行う際、GuiObjectのClipsDescendantsというプロパティとCanvasGroupというインスタンスの2種類が候補にあがります。

今回は、これらを用いたUIのマスクについてそれぞれご紹介します。

Robloxバージョン: 0.660.0.6600648

ClipsDescendants

ClipsDescendantsはGuiObjectのプロパティです。
trueにすることで四角形の境界の外側にレンダリングされる子孫 GUI 要素の部分をクリップ (非表示にする) することができます。

下記の白色のフレームがエクスプローラーのMaskFrame、星形のImageがStarImageになります。
MaskFrameのClipsDescendantsをtrueにすることで、MaskFrameより外に出たStarImageがマスクされます。

▼ ClipsDescendants false ▼ ClipsDescendants true

ClipsDescendantsの注意点

このClipsDescendantsをtrueにするだけでマスクは解決しそうですが、これにはいくつか注意点があります。

マスクできる範囲は四角形に限る

前述したとおり、ClipsDescendantsは「四角形の境界の外側にレンダリングされる子孫 GUI 要素の部分をクリップ」します。

つまり複雑な形のマスクはできません。
例えば、円のイメージをImageLabelに設定し、ClipsDescendantsをtrueします。
この時マスクは下記のように行われます。

このように、用意した円ではなくImageLableの境界である四角形がマスクの範囲として認識されます。

UICornerを使用しても適応されない

上記の「マスクできる範囲は四角形に限る」に絡みますが、ClipsDescendantsを適応するGuiObjectにUICornerを使用しても四角形の境界を基準にマスクされます。


MaskFrameに対してUICornerを使用した


マスクされる境界は外側の四角形になる...

回転したGuiObjectはマスクできない。

また、ClipsDescendantsをtrueにしたマスク用のフレームや子孫のGuiObjectが回転している場合はマスクできません。
これはプロパティのRotationが0以外のGuiObjectはマスクできないという意味です。
実際に試すと下記のようにマスクが実行されません。


エクスプローラーとプロパティ


90度回転した星はマスクされない!

CanvasGroup

CanvasGroupは、レンダリング結果に透明度やカラーを適用して子孫をグループとしてレンダリングすることができるインスタンスです。

下記のように、子孫がグループとしてレンダリングされます。

CanvasGroupの直下に配置したUICornerやUIGradientは、CanvasGroup内のすべての要素に適用されます。
また、子孫に対して常にClipsDescendantsがtrueになっています。

上記の性質により、前述のClipsDescendantsを用いたマスクと違い、UICornerを使用したマスクを行うことができます。


UICornerのCornerRadiusを大きくして、疑似的な円を作成

またClipsDescendantsと違い、CanvasGroup自身や子孫が回転した状態でも正しくマスクされます。

白のCanvasGroupと星のImageLableを回転させても、マスクが機能する

CanvasGroupの注意点

祖先のZIndexBehaviorがSiblingでないといけない

CanvasGroupを使う上で大切なのは祖先のZIndexBehaviorがSiblingでないといけないということです。

ZindexBehaviorはScreenGuiインスタンスやBillboardGuiインスタンスなど、UIをレンダリングする2DUIコンテナークラスにあるプロパティです。

これがEnum.ZIndexBehavior.Siblingに設定してある場合のみ、平坦化されたテクスチャとしてレンダリングされます。

Sibling以外が設定されている場合、ClipsDescendantsが機能しないので注意が必要が必要です。

CanvasGroupは余分なテクスチャメモリを消費する

CanvasGroupはGroupTransparency/ColorTint/Group Effects などの効果を実現するために余分なテクスチャメモリを使用します。

Robloxのテクスチャの品質と合計メモリ使用量は、クライアントのEnum.QualityLevelによって制限されています。このメモリ上限を超えると、CanvasGroupは空のテクスチャとしてレンダリングされます。
CanvasGroupを使用する場合はエクスペリエンスのメモリ状況に応じて注意しましょう。

また、CanvasGroupのサイズが動的に変化する場合は、内部的に新しいサイズのテクスチャを作成することになります。
こちらも余計な負荷がかかるので、できる限りCanvasGroupのサイズは静的なサイズで使用しましょう。

使用例

UIのマスクの使用例を紹介します。
ClipsDescendantsはどこでも使用できますが、進捗やHPを表示するバーなどで活躍します。

Robloxでは通常のHpバーなどは、進捗表示用のGuiObjectのサイズを変更することでバーが進行しているように見せる手法がとられると思います。


よくある進捗バー エクスプローラーの階層


実際の表示画面

参考コード
-- Hpバーの管理

local ScreenGui = script.Parent
local BaseFrame = ScreenGui:WaitForChild("BaseFrame")
local ProgressFrame = BaseFrame:WaitForChild("ProgressFrame")

local MaxHp = 1000
local Hp = MaxHp

-- 進捗を更新する
function UpdateProgressBar(progress: number)

	local oneOverProgress = math.clamp(progress, 0, 1)
	ProgressFrame.Size = UDim2.fromScale(oneOverProgress, ProgressFrame.Size.Y.Scale)
end


-- デバッグ用 Hp減少処理
while true do
	task.wait()
	
	local damage = 10
	Hp -= damage
	
	UpdateProgressBar(Hp / MaxHp)
end

この手法を利用する際に困る点として、進捗表示用のGuiがImageLableであった場合は画像が伸びてしまったり、UIGradientが適応されている場合はグラデーションごとサイズが変わってしまうなどが挙げられます。



グラデーションごとサイズが変わってしまう

こういった時に、進捗表示用のGuiObjectのサイズを変更するのではなく、マスク用のFrameのサイズを更新することで上記問題を解決できます。


MaskFrameを追加する


グラデーションのサイズは変わらずに進捗のみ変更している

参考コード
-- 進捗バーの管理

local ScreenGui = script.Parent
local BaseFrame = ScreenGui:WaitForChild("BaseFrame")
local MaskFrame = BaseFrame:WaitForChild("MaskFrame")		-- マスク用のフレーム追加
local ProgressFrame = MaskFrame:WaitForChild("ProgressFrame")

local MaxProgress = 1000
local CurrentProgress = MaxProgress

-- 進捗を更新する
function UpdateProgressBar(progress: number)

	local oneOverProgress = math.clamp(progress, 0, 1)

	MaskFrame.Size = UDim2.fromScale(progress, 1)
	ProgressFrame.Size = UDim2.fromScale(oneOverProgress, 1) 
end


-- デバッグ用 進捗更新処理
while true do
	task.wait()
	
	local complete = 3
	CurrentProgress -= complete
	
	UpdateProgressBar(CurrentProgress / MaxProgress)
end

まとめ

  • UIでマスクを行いたい場合は、ClipsDescendantsプロパティを使用する。
  • ClipsDescendantsを使用する際の注意点
    • 四角形の境界の外側にレンダリングされる子孫GUI要素の部分をマスクするため、複雑な形のマスクはできない。
    • ImageLabelやUICornerなどは適応されず、GuiObjectの四角形の境界の外側がマスクされる。
    • ClipsDescendantsをtrueにしたマスク用のフレームや子孫のGuiObjectが回転している場合はマスクできない。
  • また、UIでマスクを行う際はCanvasGroupも使用できる。
  • CanvasGroupはClipsDescendantsと違い、UICornerに対応しており、CanvasGroup自身や子孫が回転していてもマスクが機能する。
  • CanvasGroupを使用する際の注意点
    • 祖先のScreenGuiやBillboardGuiなどの2DUIコンテナーのZIndexBehaviorがSiblingでないと正しくレンダリングされない。
    • CanvasGroupを使用すると余計なテクスチャメモリを使用するので注意が必要。
    • CanvasGroupのサイズが動的に変化する場合は、内部的に新しいサイズのテクスチャを作成することになるので、静的なサイズで使用する。

UIのマスク表現はよく使用すると思います。
Robloxでは制約は多いですが、簡単なマスクであれば楽に実装できます。
CanvasGroupも注意点が多いですが、非常に便利なインスタンスです。
ぜひ利用して表現力豊かなゲームを制作しましょう!!

参考

https://create.roblox.com/docs/reference/engine/classes/GuiObject#ClipsDescendants
https://create.roblox.com/docs/reference/engine/classes/CanvasGroup
https://create.roblox.com/docs/reference/engine/enums/ZIndexBehavior#Sibling
https://create.roblox.com/docs/reference/engine/enums/QualityLevel

ランド・ホー Roblox開発チーム

Discussion

ログインするとコメントできます