目的
Godot4.0でListViewを作りたいと思います。名前+ハイスコアなどのリストアイテムをたくさん縦に並べて、表示しきれない場合は上下にスクロールできるコントロールです。
スマホで実行すると細いスクロールバーをつかむのが難しいです。
今回使用するControlについて
ControlはContainerを使ってうまく配置すると、表示デバイスのピクセルサイズが変わってもある程度うまく表示してくれます。
GODOT DOCS : Container types
https://docs.godotengine.org/en/latest/tutorials/ui/gui_containers.html#container-types
今回ListViewで使用するContainerを紹介します。
-
VBoxContainer
子要素を縦に並べます。
リストアイテムを縦に並べるときに便利ですね。 -
HBoxContainer
子要素を横に並べます。
リストアイテム内の配置に使用します。 -
Margin Container
子要素を縁から少し離して表示したいときに使用します。 -
PanelContainer
ControlやContainerは基本透明ですが、背景(単色やテクスチャなど)を設定したい場合はPanelContainerを親ノードにします。 -
ScrollContainer
子要素の縦横サイズが大きい場合スクロールバーを表示します。 -
label
文字を表示します。 -
Button
ボタンです
リストアイテムの作り方の基本 ※読むだけです
リストアイテムがLabelとかボタンだけではあまり実用的ではないので、リストアイテムをLabelとButtonを組み合わせた「シーン」として作成します。
リストアイテムは、左側に名前とハイスコア、右側に詳細表示ボタンを置くようなデザインにします。名前をハイスコアは縦に並べるとすると、リストアイテムの基本形は下記のようになると思います。
ルートノードはHBoxContainerです。子要素VBoxContainerとButtonを左から右に並べてくれます。
HBoxContainerの子要素はVBoxContainerとButtonを追加して横に並べます。
VBoxContainerには子ノードとしてLabelを2つ追加して、名前とハイスコア文字列を表示します。子ノードは上から下に縦に並びます。
以上が基本の構成になりますが、デザイン的にいくつか問題がありそうです。
基本の構成にいくつかContainerを追加してリストアイテムをつくりなおしましょう
(なので保存はしなくて良いです)
リストアイテムの作り方
新規シーンを作成します。ユーザインタフェースボタンをクリックしてControlを追加して名称をListItemに変更しましょう。
リストアイテムのサイズを設定する
ListItemノードはリストアイテムのサイズを保存する役割にします。
ListItemを選択した状態でインスペクタのControl/Layout/Transform/Sizeをx=400px、y=100pxに設定ましょう。2Dのエディタ上に反映されます。
またControl/Layout/Custom Minimum Sizeをx=400px、y=100pxにしましょう。
Custom Minimum Sizeはこのシーンをinstanciateしたときのサイズとして認識されるので、Transform/Sizeと同じ値を設定します。
リストアイテムの背景にテクスチャを設定する
背景にテクスチャを設定するので、テクスチャ画像をダウンロードします。
Googleで「3D TEXTURE tiles wall 001」と検索をして、下の方の「DOWNLOAD THE ALL THE MAPS」からダウンロードできます。
ファイル名は「Tiles_Wall_001_basecolor.jpg」です。
res://assets/texture/from3D_TEXTURE/Tiles_Wall_001_basecolor.jpgに配置しましょう。
シーン内のListItemを右クリックして、子ノードを追加からPanelContainerを追加しましょう。
親のサイズと同じにしたいので、ビューメニューの右の⊕マークをクリックして、Rect全面をクリックします。サイズが親と同じサイズになります。また親のサイズを変更した場合も追従するようです。
インスペクタのConttol/Theme Overrides/Styles/Panelの<空>をクリックして、新規StyleBoxTetureを選択します。
追加したStyleBoxTextureをクリックして詳細設定を開きます。
res://assets/texture/from3D_TEXTURE/Tiles_Wall_001_basecolor.jpgをtextureにドラッグアンドドロップします。
リストアイテムの背景を縁取りする
リストアイテムの縁取りを設定しましょう。
シーン内のPanelContainerを右クリックして子ノードを追加からPanelContainerを追加します。
(テクスチャを設定したPanelContainerの子に、縁取り用のPanelContainerを追加します)
インスペクタのControl/Theme Overrides/Styles/Panelの<空>をクリックして、新規StyleBoxFlatを選択します。
追加したStyleBoxFlatをクリックして詳細設定を開きます。
BG Colorをクリックするとカラーピッカーが表示されるのでA(アルファ)を0にして透明にします。
BorderWidthを開いてLeft/Top/Right/Bottomをそれぞれ2pxにします。
またCorner Radiusを開いて、各10pxにします。
こんな感じですが背景としてはちょっと明るいので暗くします。
テクスチャを設定した方(親の方)のPanelContainerを選択して、インスペクタのControl/Theme Overrides/StylesのStyleBoxTexureの詳細情報のModulateを開いて、Colorをクリックするとカラーピッカーが表示されます。
R=G=B=70にしてテクスチャを暗くします。
文字を追加する
次に基本形で説明した、HBoxContainerを追加します。
HBoxContainerの子はVBoxContainerとButtonでしたが、少し余白が欲しいので、HBoxContainerの子にMarginContainerを2つ追加します。一つ目のMarginContainerがリストアイテム左側に表示する文字情報用です。2つ目のMarginContainer2が右側のボタン用になります。
リストアイテムのサイズは400×100pxとしましたが、テキスト表示幅は300pxにします。
Controlのサイズは自動で設定されますが、最小のサイズをControl/Layout/Custom Minimum Sizeに設定することで指定可能です。
ひとつめのMarginContainerのControl/Layout/Custom Minimum SizeのX=300pxと設定します。
ひとつめのMarginContainerを右クリックして子ノードとVBoxContainerを追加します。
またVBoxContainerの子ノードとしてLabelを2つ追加します。
それぞれLabelNameとLabelRecordと名称変更します。
LabelNameを選択してインスペクタのLabel/textに仮で「Taro」と入力します。
LabelRecordを選択してインスペクタのLabel/textに仮で「Score 10000」と入力します。
文字を大きくしましょう。
LabelNameを選択してインスペクタのLabel/LabelSettingsの右の<空>をクリックして、「新規LabelSettings」を選択します。
追加されたLabelSettingsをクリックして詳細設定を開き、Font/Sizeを30pxに変更します。
LabelRecordも同様にFonts/Sizeを30pxにします。
文字列の左がふちにくっついているのでマージンを設定しましょう。
VBoxContainerの親のMarginContainerを選択して、インスペクタのControl/Theme Overrides/Constantsを開き、Margin Leftにチェックをいれて20pxと設定します。
ボタンを追加する
MarginContainer2を右クリックして、子ノードを追加からPanelContainerを追加します。またPanelContainerを右クリックして、子ノードを追加からButtonを追加します。
インスペクタのButton/Textに「Info」と入力します。
ボタンを右端まで広げます。
ボタンは親のサイズまで上下にサイズを広げます。ボタンの親は子のButtonの必要サイズを最小限確保しますとなっているので、ボタンの親のMarginContainer2のサイズを目いっぱい広げたいと思います。
ボタンの親のMarginContainer2を選択してインスペクタのControl/Layout/Container Sizing/Hrizontalの「Expand」にチェックを入れます。ボタンが目いっぱい大きくなります。
ボタンもマージンを設定します。
MarginContainer2を選択して、Control/Theme Overrides/Constantsを開き、Margin Left/Top/Right/Bottomそれぞれにチェックをいれて10pxにします。
ボタンが目立たないのでPanelContainerを編集します。
Buttonの親のPanelContainerを選択して、インスペクタのControl/Theme Overrides/Styles/Panelの<空>をクリックして新規StyleBoxFlatを選択します。
追加されたStyleBoxFlatをクリックして詳細設定を開き、BG ColorはR=B=G=60にしました。またBorder Widthを各1pxに設定しました。
下記のようにボタンが縁取りされます。
リストアイテムのスクリプトを設定
リストアイテムは名前をハイスコアの文字列を設定する必要があります。またボタンを押下した時に他のリストアイテムと区別するためにIDも設定したいと思います。
ルートノードのListItemを右クリックして、スクリプトをアタッチします。
res://list_item.gdに保存します。
初期設定として下記のように実装します。
extends Control
var m_id : int
func set_info(id, name, record):
m_id = id
$PanelContainer/PanelContainer/HBoxContainer/MarginContainer/VBoxContainer/LabelName.text = name
$PanelContainer/PanelContainer/HBoxContainer/MarginContainer/VBoxContainer/LabelRecord.text = record
またボタンを押下したときは、signalを発行してidを通知するようにします。
ボタンのシグナルを接続しましょう。
Buttonを選択して、インスペクタの右のノード/シグナルを選択します。
BaseButtonのpressedをダブルクリックしてスクリプトに接続します。
ダイアログが表示されるので、受信側メソッドを確認してListItemを選択します。
スクリプトに接続されました。
extends Control
var m_id : int
func set_info(id, name, record):
m_id = id
$PanelContainer/PanelContainer/HBoxContainer/MarginContainer/VBoxContainer/LabelName.text = name
$PanelContainer/PanelContainer/HBoxContainer/MarginContainer/VBoxContainer/LabelRecord.text = record
func _on_button_pressed():
pass # Replace with function body.
つづいて、ボタンを押下した場合、リストアイテムシーンからは「on_list_item_button_pressed(id)」というシグナルを送信するようにします。
extends Control
signal on_list_item_button_pressed(id)
var m_id : int
func set_info(id, name, record):
m_id = id
$PanelContainer/PanelContainer/HBoxContainer/MarginContainer/VBoxContainer/LabelName.text = name
$PanelContainer/PanelContainer/HBoxContainer/MarginContainer/VBoxContainer/LabelRecord.text = record
func _on_button_pressed():
on_list_item_button_pressed.emit(m_id)
ListViewのベースとなるシーンの作り方
次にリストアイテムを並べるシーンを作成します。
新規シーンを作成して、ユーザインタフェースボタンを押下します。
ルートノードとしてControlが追加されるので、ListViewに名称変更しましょう。
また表示サイズを設定します。
インスペクタのControl/Transform/Sizeをx=400px、y=600pxにします。
縦にスクロールしたいのでScrollContainerを子ノードとして追加します。
リストアイテムは縦に並べていくので、さらに子ノードとしてVBoxContainerを配置して、VBoxContainerの子としてリストアイテムを追加するようにします。表示しきれないくらいたくさん追加するとスクロールバーが表示されます。
ScrollContainerを選択して、ビューメニューの右の⊕の右をクリックして、「Rect全面」を選択すると、ScrollContainerが親のListViewのサイズ全面を使用するようになります。
ListViewのスクリプトの実装
ListViewwを右クリックしてスクリプトをアタッチします。
res://list_view.gdというファイル名で保存します。
下記のように実装しましょう。
また保存した後に、ListViewをクリックしてインスペクタに「M Scn List Item」が表示されるので、res://list_item.tscnをドラッグアンドドロップしてください。
extends Control
@export var m_scn_list_item : PackedScene
var m_test_list = [
["Taro", "Score 90000"],
["Jiro", "Score 80000"],
["Saburo", "Score 70000"],
["Shiro", "Score 60000"],
["Goro", "Score 50000"],
["Rokuro", "Score 40000"],
["Shichiro", "Score 30000"],
["Hachiro", "Score 20000"],
["Kuro", "Score 10000"],
["Juro", "Score 00000"],
]
func _ready():
var id=0
for info in m_test_list:
var ins = m_scn_list_item.instantiate()
ins.set_info(id, info[0], info[1])
$ScrollContainer/VBoxContainer.add_child(ins)
id = id + 1
# シグナルを接続する
ins.on_list_item_button_pressed.connect(on_list_item_button_press)
func on_list_item_button_press(id):
print(id)
-
ins.on_list_item_button_pressed.connect(on_list_item_button_press)
リストアイテム(=ins)が発行するシグナルを「on_list_item_button_press」メソッドに接続しています。 -
on_list_item_button_press
リストアイテムシーンを生成時にset_infoメソッドで設定したIDが通知されるので、printしています。
リストアイテムを大きく表示する
ListViewを表示するウインドウの都合で大きく表示したい場合です。
res://list_view.tscnを開いて、ルートノードのListViewを選択します。インスペクタのControl/Layout/Transform/SizeのX=600pxに拡張します。
次にres://list_view.gdを開いて、add_childする前に1行追加します。
ins.set_custom_minimum_size(Vector2(600, 200))
実行すると下記のようになります。
今回Buttonの親のMarginContainerのControl/Layout/Container SizingのHorizontalのExpandにチェックを入れたので、優先的にボタン(の親のMarginContainer)が大きくなったのだと思います。
追記
Windowsで実行するとマウスホイールで上下に動くので便利ですが、スマホで実行するととても細いスクロールバーをドラッグするしかないので使いにくいですね。
github
以上です。