godot

L003 コントロール tips


controlについての覚書です。
コントロールはユーザーインターフェースに使われます。

UI

シーンへの追加

UIのシーンを作成して、基準のシーンへ追加します。

基準のシーンとしてL012のEnemyA1を使用しています。

UIの追加
  • 1
    UIシーンの作成
    UIにあたるシーンを作成します。
  • 2
    追加したい基準のシーンへ追加
    シーンの追加

    追加コード

    #UIの設定
    var UI = preload("res://UI/hud.tscn")
    
    ----------------------------------------------------
    	var addUI = UI.instantiate()
    	add_child(addUI)

追加したシーンの操作

add_childで追加したHUDのシーン内にあるLabelのテキストをstageにします。
追加されたHUDシーンの子であるLabelを$$HUD/VBox/Labelで指定します。
プロパティであるtextを"stage"に書き換えます。

「リモート」を選択すると追加したシーンが確認できます。
$HUD/VBox/Label.text = "stage"

github

GitHub - WOCae/L012 at r5-1
Contribute to WOCae/L012 development by creating an account on GitHub.

Label

テキストを表示します。

RichTextLabel

BBCodeを用いて様々な表現が行えます。

RichTextLabelのBBCode
はじめに: Label nodes are great for displaying basic text, but they have limitations. If you want to change the color of the...

Godotのデモ rich_text_bbcode よりリッチテキストの例を確認します。
文字をゆらしたりできます。(Text effects)

デモ rich_text_bbcode より

bbcode_enabledにすると反映されます。

[pulse]Pulse[/pulse]   
[wave]Wave[/wave]
[tornado]Tornado[/tornado]  
[shake]Shake[/shake] 
[fade start=75 length=7]Fade[/fade]
[rainbow]Rainbow[/rainbow]

パラメーター付き

[pulse freq=1.0 color=#ffffff40 ease=-2.0]{text}[/pulse]
[wave amp=50.0 freq=5.0 connected=1]{text}[/wave]
[tornado radius=10.0 freq=1.0 connected=1]{text}[/tornado]
[shake rate=20.0 level=5 connected=1]{text}[/shake]
[fade start=4 length=14]{text}[/fade]
[rainbow freq=1.0 sat=0.8 val=0.8]{text}[/rainbow]

tree

ツリー状のリストを作成します。

Godotのデモ「ControlGallery」を参照します。

@tool
extends Tree


# Called when the node enters the scene tree for the first time.
func _ready():
	var root: TreeItem = create_item()
	root.set_text(0, "Tree - Root")
	var child1: TreeItem = create_item(root)
	child1.set_text(0, "Tree - Child 1")
	var child2: TreeItem = create_item(root)
	child2.set_text(0, "Tree - Child 2")
	var subchild1: TreeItem = create_item(child1)
	subchild1.set_text(0, "Tree - Subchild 1")

サンプル

実際に使用する処理を追加しました。

使用フォント:MPLUSRounded1c-Bold

右上のtextEditに項目を入力してボタンを押すと、ツリーに項目を追加(layerAdd)、または、要素を追加・削除(add・sub)します。ツリーの項目を選択してボタン(layersub)を押すと、項目が削除されます。

extends Control

@onready var tree = $VBoxContainer2/ScrollContainer/Tree
@onready var scroll_container = $VBoxContainer2/ScrollContainer
@onready var display_text_edit = $VBoxContainer/VSplitContainer/DisplayTextEdit  # 表示専用
@onready var input_text_edit = $VBoxContainer/VSplitContainer/InputTextEdit  # 書き込み用
@onready var add_button = $VBoxContainer2/HBoxContainer/add
@onready var sub_button = $VBoxContainer2/HBoxContainer/sub

@onready var layerAdd_button = $VBoxContainer2/HBoxContainer2/layerAdd
@onready var layersub_button = $VBoxContainer2/HBoxContainer2/layersub


var resizing: bool = false
var last_mouse_x: int = 0

var layers_data = {
	"グループA": {
		"レイヤー1": ["りんご", "みかん", "ばなな"],
		"レイヤー2": ["ひまわり", "チューリップ"]
	},
	"グループB": {
		"レイヤー3": ["パイナップル", "ぶどう"],
		"レイヤー4": ["桜", "バラ"]
	}
}

func _ready():
	scroll_container.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_AUTO
	scroll_container.vertical_scroll_mode = ScrollContainer.SCROLL_MODE_AUTO
	
	setup_tree()
	tree.allow_rmb_select = true  # 右クリック選択を有効化
	tree.connect("item_selected", Callable(self, "_on_item_selected"))
	tree.connect("item_activated", Callable(self, "_on_item_activated"))  # ダブルクリック時のイベント
	tree.connect("item_edited", Callable(self, "_on_item_edited"))
	add_button.pressed.connect(_on_add_button_pressed)
	sub_button.pressed.connect(_on_sub_button_pressed)
	#separator.gui_input.connect(_on_separator_gui_input)  # 追加: スプリッターのドラッグ処理

	# 表示専用にする
	display_text_edit.editable = false

	#tree.allow_rmb_select = true  # 右クリック選択を有効化
	#tree.connect("gui_input", Callable(self, "_on_tree_gui_input"))  # 右クリックイベント
	layerAdd_button.pressed.connect(_on_layer_add_button_pressed) # レイヤー追加ボタンの接続
	tree.connect("item_selected", Callable(self, "_on_item_selected"))  # 選択時のシグナルを追加
	layersub_button.pressed.connect(_on_layer_sub_button_pressed) # layersubボタンの接続
		
func setup_tree():
	tree.clear()
	var root = tree.create_item()

	for group_name in layers_data.keys():
		var group_item = tree.create_item(root)
		group_item.set_text(0, group_name)
		group_item.set_editable(0, false)  # 初期状態では編集不可

		for layer_name in layers_data[group_name].keys():
			var layer_item = tree.create_item(group_item)
			layer_item.set_text(0, layer_name)
			layer_item.set_metadata(0, layers_data[group_name][layer_name])  # 配列を保存
			layer_item.set_editable(0, false)  # 初期状態では編集不可
	
	root.set_text(0, "Root Name")


func _on_item_activated():
	var selected_item = tree.get_selected()
	if selected_item:
		selected_item.set_editable(0, true)  # ダブルクリックで編集可能にする
		tree.edit_selected()  # 編集モードに入る

func _on_item_edited():
	var selected_item = tree.get_edited()
	if selected_item:
		var new_name = selected_item.get_text(0)
		var parent_item = selected_item.get_parent()

		if parent_item == null:
			return  # ルートアイテムは変更しない

		for group_name in layers_data.keys():
			if layers_data[group_name].has(selected_item.get_text(0)):
				var old_name = selected_item.get_text(0)
				var value = layers_data[group_name][old_name]
				layers_data[group_name].erase(old_name)  # 削除
				layers_data[group_name][new_name] = value  # 新しいキーで追加
				return
			for layer_name in layers_data[group_name].keys():
				if layers_data[group_name][layer_name] == selected_item.get_metadata(0):
					var value = layers_data[group_name][layer_name]
					layers_data[group_name].erase(layer_name)  # 削除
					layers_data[group_name][new_name] = value  # 新しいキーで追加
					return
		
		selected_item.set_editable(0, false)  # 編集後は再び編集不可にする

func _on_add_button_pressed():
	var selected_item = tree.get_selected()
	if selected_item == null:
		print("レイヤーが選択されていません")
		return

	var new_text = input_text_edit.text.strip_edges()  # 前後の空白を除去
	if new_text == "":
		print("追加するテキストがありません")
		return

	var new_items = new_text.split("\n", false)  # 改行で分割
	var layer_data = selected_item.get_metadata(0)

	if layer_data is Array:
		layer_data.append_array(new_items)  # 複数行をリストに追加
		selected_item.set_metadata(0, layer_data)
		display_text_edit.text = "\n".join(layer_data)
		print("追加後のデータ:", layer_data)

	input_text_edit.clear()  # 入力欄をクリア

func _on_sub_button_pressed():
	var selected_item = tree.get_selected()
	if selected_item == null:
		print("レイヤーが選択されていません")
		return

	var remove_text = input_text_edit.text.strip_edges()  # 前後の空白を除去
	if remove_text == "":
		print("削除するテキストがありません")
		return

	var remove_items = remove_text.split("\n", false)  # 改行で分割
	var layer_data = selected_item.get_metadata(0)

	if layer_data is Array:
		for item in remove_items:
			if item in layer_data:
				layer_data.erase(item)  # 指定された項目を削除
		selected_item.set_metadata(0, layer_data)
		display_text_edit.text = "\n".join(layer_data)
		print("削除後のデータ:", layer_data)

	input_text_edit.clear()  # 入力欄をクリア

func _on_separator_gui_input(event):
	if event is InputEventMouseButton:
		if event.button_index == MOUSE_BUTTON_LEFT:
			resizing = event.pressed
			last_mouse_x = event.position.x
	elif event is InputEventMouseMotion and resizing:
		var delta_x = event.position.x - last_mouse_x
		tree.custom_minimum_size.x += delta_x
		last_mouse_x = event.position.x

func _on_layer_add_button_pressed():
	var selected_item = tree.get_selected()
	if selected_item == null:
		print("グループまたはレイヤーが選択されていません")
		return

	var parent_item = selected_item.get_parent()

	var new_layer_name = input_text_edit.text.strip_edges()
	if new_layer_name == "":
		print("グループ名またはレイヤー名を入力してください")
		return

	if parent_item == null:  # ルートノードが選択されている場合
		layers_data[new_layer_name] = {}  # 新しいグループを追加
		setup_tree()
		input_text_edit.clear()
		print("グループを追加:", new_layer_name)
	else: #グループまたはレイヤーが選択されている場合
		var group_name = ""

		if parent_item.get_parent() == null: #グループが選択されている場合
			group_name = selected_item.get_text(0)
		else: #レイヤーが選択されている場合
			group_name = parent_item.get_text(0)

		if layers_data.has(group_name):
			if layers_data[group_name].has(new_layer_name):
				print("すでにレイヤーが存在します")
				return

			layers_data[group_name][new_layer_name] = []
			setup_tree()
			input_text_edit.clear()

			print("レイヤーを追加:", new_layer_name)
		else:
			print("グループが存在しません")

func _on_item_selected():
	var selected_item = tree.get_selected()
	if selected_item:
		print("選択されたアイテム:", selected_item.get_text(0))  # 選択されたアイテムの名前をprint出力
		var layer_data = selected_item.get_metadata(0)
		if layer_data is Array:
			display_text_edit.text = "\n".join(layer_data)
		else:
			display_text_edit.text = ""
			
func _on_layer_sub_button_pressed():
	var selected_item = tree.get_selected()
	if selected_item == null:
		print("グループまたはレイヤーが選択されていません")
		return

	var parent_item = selected_item.get_parent()

	if parent_item == null: #ルートノードが選択されている場合
		var group_name = selected_item.get_text(0)
		layers_data.erase(group_name)
		print("グループを削除:", group_name)

	elif parent_item.get_parent() == null: #グループが選択されている場合
		var group_name = selected_item.get_text(0)
		layers_data.erase(group_name)
		print("グループを削除:", group_name)
	else: #レイヤーが選択されている場合
		var group_name = parent_item.get_text(0)
		var layer_name = selected_item.get_text(0)
		layers_data[group_name].erase(layer_name)
		print("レイヤーを削除:", layer_name)
		if layers_data[group_name].is_empty(): #グループが空になったらグループごと削除
			layers_data.erase(group_name)
			print("グループを削除:", group_name)

	setup_tree()
	input_text_edit.clear()
GitHub - WOCae/l003_tree
Contribute to WOCae/l003_tree development by creating an account on GitHub.

TextureProgressBar

ゲージを表示します。

画像をゲージに設定します。
barはunderとProgressに設定します。
枠のような形状はOverに設定します。

画像 bar

画像 bar w

(画像はPixeloramaで作成しました。)

時間経過で減少します。
マウスの左クリックでゲージが増加、
右クリックで減少します。

作業

下記のスクリプトとmainにセットします。時間経過でゲージが減少するようにしています。

extends Node2D

var hp:float = 100
var maxhp:float = 100 # 最大HP 

func _ready():

	$"sample".max_value =maxhp
	$"sample/Label".text = "Hp:" + str(hp)
	
	
func _process(delta):
	#時間経過でhpを減らす
	if hp >= 0:
		hp -= delta
	
	# HPバー更新
	var hpbar:TextureProgressBar = $"sample"
	var hpPer = _hpPer()
	hpbar.value = maxhp * hpPer
	
	# HPバー更新
	hpbar.tint_progress = lerp(Color.YELLOW, Color.YELLOW, hpPer)
	hpbar.tint_under = lerp(Color.RED, Color.RED, hpPer)
	#hpbar.tint_over = lerp(Color.WHITE, Color.WHITE, hpPer)
	$"sample/Label".text = "Hp:" +"%4.0f" %  hp
	if hp <= 0:
		$"sample/Label".text = "Hp:0"
	
func _hpPer():
	return 1.0 * hp / maxhp;

ゲームの進行で最大値が変化する場合には、$"sample".max_value =maxhpを更新する必要があります。

Godot 4 の lerp() 関数について

Lerp() 関数は、2 つの値の間を線形補間する関数です。つまり、2 つの値と補間率 (0 から 1 の範囲) を指定すると、その補間率に応じて 2 つの値の間の値を返します。

Godot 4 では、lerp() 関数は以下の 2 種類が用意されています。

  • lerp(): 2 つの数値間の補間を行います。
  • Vector2.lerp(): 2 つの Vector2 型の値間の補間を行います。
  • Vector3.lerp(): 2 つの Vector3 型の値間の補間を行います。

lerp() 関数の使い方

lerp() 関数の使い方は以下の通りです。

GDScript

var value = lerp(start_value, end_value, amount)

value: 補間された値

  • start_value: 開始値
  • end_value: 終了値
  • amount: 補間率

github

GitHub - WOCae/L003
Contribute to WOCae/L003 development by creating an account on GitHub.

ボタン

Buttonのカラー設定

インスペクターからtheme_override_styles/normalにStyleBoxFlatを設定して変更。

コメント