1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Godot4.0でListViewを作ります

Last updated at Posted at 2023-05-22

目的

 Godot4.0でListViewを作りたいと思います。名前+ハイスコアなどのリストアイテムをたくさん縦に並べて、表示しきれない場合は上下にスクロールできるコントロールです。
スクリーンショット (841).png

スマホで実行すると細いスクロールバーをつかむのが難しいです。

今回使用する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を左から右に並べてくれます。
スクリーンショット (787).png

 HBoxContainerの子要素はVBoxContainerとButtonを追加して横に並べます。
スクリーンショット (791).png
 VBoxContainerには子ノードとしてLabelを2つ追加して、名前とハイスコア文字列を表示します。子ノードは上から下に縦に並びます。
 スクリーンショット (792).png

 以上が基本の構成になりますが、デザイン的にいくつか問題がありそうです。
 基本の構成にいくつか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と同じ値を設定します。
スクリーンショット (796).png

リストアイテムの背景にテクスチャを設定する

 背景にテクスチャを設定するので、テクスチャ画像をダウンロードします。

 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に配置しましょう。
スクリーンショット (799).png

 シーン内のListItemを右クリックして、子ノードを追加からPanelContainerを追加しましょう。
 親のサイズと同じにしたいので、ビューメニューの右の⊕マークをクリックして、Rect全面をクリックします。サイズが親と同じサイズになります。また親のサイズを変更した場合も追従するようです。
スクリーンショット (797).png
 インスペクタのConttol/Theme Overrides/Styles/Panelの<空>をクリックして、新規StyleBoxTetureを選択します。
スクリーンショット (801).png
 追加したStyleBoxTextureをクリックして詳細設定を開きます。
 res://assets/texture/from3D_TEXTURE/Tiles_Wall_001_basecolor.jpgをtextureにドラッグアンドドロップします。
スクリーンショット (802).png

リストアイテムの背景を縁取りする

 リストアイテムの縁取りを設定しましょう。
 シーン内のPanelContainerを右クリックして子ノードを追加からPanelContainerを追加します。
 (テクスチャを設定したPanelContainerの子に、縁取り用のPanelContainerを追加します)
 インスペクタのControl/Theme Overrides/Styles/Panelの<空>をクリックして、新規StyleBoxFlatを選択します。
スクリーンショット (803).png
 追加したStyleBoxFlatをクリックして詳細設定を開きます。
 BG Colorをクリックするとカラーピッカーが表示されるのでA(アルファ)を0にして透明にします。
 BorderWidthを開いてLeft/Top/Right/Bottomをそれぞれ2pxにします。
 またCorner Radiusを開いて、各10pxにします。
 スクリーンショット (804).png
 こんな感じですが背景としてはちょっと明るいので暗くします。
スクリーンショット (805).png
 テクスチャを設定した方(親の方)のPanelContainerを選択して、インスペクタのControl/Theme Overrides/StylesのStyleBoxTexureの詳細情報のModulateを開いて、Colorをクリックするとカラーピッカーが表示されます。
 R=G=B=70にしてテクスチャを暗くします。
スクリーンショット (808).png

文字を追加する

 次に基本形で説明した、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と設定します。
 スクリーンショット (809).png

 ひとつめのMarginContainerを右クリックして子ノードとVBoxContainerを追加します。
 またVBoxContainerの子ノードとしてLabelを2つ追加します。
 それぞれLabelNameとLabelRecordと名称変更します。
 LabelNameを選択してインスペクタのLabel/textに仮で「Taro」と入力します。
 LabelRecordを選択してインスペクタのLabel/textに仮で「Score 10000」と入力します。
スクリーンショット (810).png
 文字を大きくしましょう。
 LabelNameを選択してインスペクタのLabel/LabelSettingsの右の<空>をクリックして、「新規LabelSettings」を選択します。
 スクリーンショット (813).png
 追加されたLabelSettingsをクリックして詳細設定を開き、Font/Sizeを30pxに変更します。
スクリーンショット (814).png
 LabelRecordも同様にFonts/Sizeを30pxにします。
スクリーンショット (815).png
 文字列の左がふちにくっついているのでマージンを設定しましょう。
 VBoxContainerの親のMarginContainerを選択して、インスペクタのControl/Theme Overrides/Constantsを開き、Margin Leftにチェックをいれて20pxと設定します。
スクリーンショット (816).png

ボタンを追加する

 MarginContainer2を右クリックして、子ノードを追加からPanelContainerを追加します。またPanelContainerを右クリックして、子ノードを追加からButtonを追加します。
 インスペクタのButton/Textに「Info」と入力します。
スクリーンショット (821).png
 ボタンを右端まで広げます。
 ボタンは親のサイズまで上下にサイズを広げます。ボタンの親は子のButtonの必要サイズを最小限確保しますとなっているので、ボタンの親のMarginContainer2のサイズを目いっぱい広げたいと思います。
 ボタンの親のMarginContainer2を選択してインスペクタのControl/Layout/Container Sizing/Hrizontalの「Expand」にチェックを入れます。ボタンが目いっぱい大きくなります。
スクリーンショット (824).png
 ボタンもマージンを設定します。
 MarginContainer2を選択して、Control/Theme Overrides/Constantsを開き、Margin Left/Top/Right/Bottomそれぞれにチェックをいれて10pxにします。
スクリーンショット (819).png

 ボタンが目立たないのでPanelContainerを編集します。
 Buttonの親のPanelContainerを選択して、インスペクタのControl/Theme Overrides/Styles/Panelの<空>をクリックして新規StyleBoxFlatを選択します。
スクリーンショット (827).png
 追加されたStyleBoxFlatをクリックして詳細設定を開き、BG ColorはR=B=G=60にしました。またBorder Widthを各1pxに設定しました。
スクリーンショット (828).png
 下記のようにボタンが縁取りされます。
スクリーンショット (830).png

リストアイテムのスクリプトを設定

 リストアイテムは名前をハイスコアの文字列を設定する必要があります。またボタンを押下した時に他のリストアイテムと区別するためにIDも設定したいと思います。

 ルートノードのListItemを右クリックして、スクリプトをアタッチします。
 res://list_item.gdに保存します。
スクリーンショット (832).png

 初期設定として下記のように実装します。

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をダブルクリックしてスクリプトに接続します。
 スクリーンショット (833).png
 ダイアログが表示されるので、受信側メソッドを確認してListItemを選択します。
スクリーンショット (834).png

 スクリプトに接続されました。

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にします。
スクリーンショット (837).png
 縦にスクロールしたいのでScrollContainerを子ノードとして追加します。
 リストアイテムは縦に並べていくので、さらに子ノードとしてVBoxContainerを配置して、VBoxContainerの子としてリストアイテムを追加するようにします。表示しきれないくらいたくさん追加するとスクロールバーが表示されます。

 ScrollContainerを選択して、ビューメニューの右の⊕の右をクリックして、「Rect全面」を選択すると、ScrollContainerが親のListViewのサイズ全面を使用するようになります。
スクリーンショット (839).png

ListViewのスクリプトの実装

 ListViewwを右クリックしてスクリプトをアタッチします。
 res://list_view.gdというファイル名で保存します。
スクリーンショット (840).png

 下記のように実装しましょう。
 また保存した後に、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しています。

 実行しましょう。
スクリーンショット (841).png

リストアイテムを大きく表示する

 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)が大きくなったのだと思います。

スクリーンショット (842).png

追記

 Windowsで実行するとマウスホイールで上下に動くので便利ですが、スマホで実行するととても細いスクロールバーをドラッグするしかないので使いにくいですね。

github

以上です。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?