【Gudot】謎解きゲームで使いそうな数値入力UIの作成方法

今回は謎解きゲームで使いそうな数値入力UIの作成方法を解説します。

数値入力UIの作成方法

素材データのダウンロード

今回は日本語テキストを使用したいので、以下のサイトなどから日本語フォントをプロジェクトに追加しておきます。

Themeリソースの作成

日本語フォントを使用するために Theme リソースを作成します。ファイルシステムを右クリックして「新規リソース...」を選びます。

Themeリソースを作成します。

リソースの名前を指定します。ここでは「default_theme.tres」としました。

作成した Themeリソースをダブルクリックして、インスペクターから「Default Font > [空]」をクリックして「新規 DynamicFont」を選びます。

作成した DynamicFont をクリックするとフォントの設定が表示されるので「Font > FontData > [空]」をクリックして「読み込み」を選びます。

プロジェクトに追加した日本語フォント "mplus-1c-regular.ttf" を開きます。

これで日本語フォントを設定できたので「保存」をクリックしてセーブしておきます。

数値入力のパーツのシーンを作成する

次に数値入力のパーツのシーンを作成します。

コンテナを使ってUI部品をまとめたいので「+その他のノード」をクリックします。

作成するUIノードは「VBoxContainer」となります。これは縦方向にUIをまとめるコンテナとなります。

UIノードを作成したらノード名を「NumberInputSimgle」とリネームしておきます。

ボタンと数値テキストを表示する

次に「Button」ノードを追加します。

作成したら名前を「ButtonUp」に変更します。

このボタンは上向きの矢印ボタンとするためです。

次に数値をテキストとして表示するために「Label」を追加します。ただいったんルートノードを選択しないと "ButtonUp" の下の階層に作られてしまうので、「NumberInputSimgle」を選択してから作成するとよいです。

Labelノードは特にリネームせずに「NumberInputSimgle」を選択して、下向きのボタンノード (Button) を作成します。

作成したボタン名は「ButtonDown」にリネームします。

Themeリソースを設定する

各UIで日本語を表示できるように Themeリソースを設定します。

このとき Shiftキーを押しながらノードを選択するとまとめて選択でき、まとめて Themeリソースを割り当てることができます。

選択したらインスペクターから「Theme > Theme > [空]」をクリックして、「読み込み」を選びます。

先ほど作成した Themeリソース "default_theme.tres" を開きます。

これで日本語フォントが各UIで使用できるようになったので、それぞれの「Text」に以下の文字を設定します。
  • ButtonUp: ▲ (上向きの三角)
  • Label: 0
  • ButtonDown: ▼ (下向きの三角)

これで数値入力UIっぽいものができたのですが、文字が左揃えになっているので、"Label"ノードを選択して、インスペクターから「Label > Align」の値を「Center」にして "中央揃え" に変更しておきます。

補足:見た目を良くしたい場合

今回は楽をするために "Button"ノードを使用し、日本語フォントで上下の三角ボタンを作成しましたが、「TextureButton」を使用すると Texture からボタンの見た目を画像に変更できます。

よりクオリティを上げたい場合はこちらのUIノードを使用するのが良いと思います。

上下ボタンの入力を処理する

次に上下ボタンの入力を処理します。

その前にまずは「Ctrl+S (Cmd+S)」でシーンを「NumberInputSimgle.tscn」として保存します。

保存できたら "NumberInputSimgle" ノード(ルートノード) にスクリプトをアタッチします。

extends VBoxContainer

export var min_value = 0 # 最小値
export var max_value = 9 # 最大値
export var value = 0 # 現在の値

onready var label = $Label

func _process(_delta: float) -> void:
    # ラベルに数値を反映する
    label.text = str(value)

ここで扱う場合には1桁の数値なので「0〜9」の数値を入力できるとします

次に "ButtonUp" を選び pressed() シグナルを追加します。シグナルのコードは以下のとおりです。

func _on_ButtonUp_pressed() -> void:
    # 上ボタンを押した場合
    # 値を減算する
    value -= 1
    if value < min_value:
        value = max_value   

上ボタンは値が増加しそうな気がするのですが、私の好みで値が減少するようにしました。違和感がある場合は増加でも良いと思います。

"ButtonDown" にも pressed() シグナルを追加します。シグナルのコードは以下のとおりです。

func _on_ButtonDown_pressed() -> void:
    # 下ボタンを押した場合
    # 値を加算する
    value += 1
    if value > max_value:
        value = min_value

では実行して動作を確認します。

念のため、NumberInputSimgle.gd スクリプトのコード全文をのせておきます。

extends VBoxContainer

export var min_value = 0 # 最小値
export var max_value = 9 # 最大値
export var value = 0 # 現在の値

onready var label = $Label

func _process(_delta: float) -> void:
    # ラベルに数値を反映する
    label.text = str(value)

func _on_ButtonUp_pressed() -> void:
    # 上ボタンを押した場合
    # 値を減算する
    value -= 1
    if value < min_value:
        value = max_value   

func _on_ButtonDown_pressed() -> void:
    # 下ボタンを押した場合
    # 値を加算する
    value += 1
    if value > max_value:
        value = min_value

余談: 文字入力を可能にしたい場合

今回は解説しませんが、数値入力ではなく、文字入力(文字選択)を可能にしたい場合は「選択可能な文字をリストにする」方法で実装できます。

以下実装例です。

# 文字を選ばせる入力UIの実装例

extends VBoxContainer

export var value = 0 # 現在の値
var table = ["あ", "い", "う", "え", "お"]

onready var label = $Label

func _process(_delta: float) -> void:
    # ラベルにvalueに対応するテーブルの文字を反映する
    label.text = table[ value ]

4桁の数値入力を行うUIを作成する

1桁の数値入力であればこれで良いのですが、たいていはある程度の桁数の数値入力が必要になると思います。

そこで4桁の数値入力をできるUIを作成してみます。

ルートノード(Mainシーン)の作成

コントロールノードを新規作成します。

ノード名は「Main」に変更して、シーンとして保存(Main.tscn)しておきます。

そしてスクリプト (Maingd) をアタッチしておきます。

extends Control

まだ何も実装しないので中身は型の定義のみで良いです。

HBoxContainerを作成する

複数の桁数を管理するために "HBoxContainer" ノードを作成します。

これは UIを横並びに整列するUIコンテナノードです。

そして作成した HBoxContainer に "NumberInputSimgle.tscn" をドラッグ&ドロップで追加していきます。

うまく追加できない (HBoxContainerの直接の子ノードにならない) 場合は、作成したノードをドラッグ&ドラップで調整をします。

入力判定確認用のラベルを作成する

入力判定確認用に2つのラベルを追加します。

それぞれ日本語フォントのテーマ(default_theme.tres) を設定しておいてください。

そして Label ノードの Text には「99かける99は?」と入力し、Label2ノードの Textには「入力した値」と設定して良い感じの位置に調整しておきます。

入力判定を行うボタンを追加する

次に「Button」ノードを追加します。

Themeには "default_theme.tres" を設定して日本語フォントを使えるようにしておき、Textには「決定」と設定してわかりやすい場所に移動させます。

そしてButtonの pressed() シグナルを追加し、Main.gd スクリプトを以下のように実装します。

extends Control

onready var label = $Label2
onready var numbers = $HBoxContainer

func _ready() -> void:
    pass # Replace with function body.

func _on_Button_pressed() -> void:
    var num = 0
    
    # 逆順で合計値を求める
    var digit = numbers.get_child_count() - 1
    
    for child in numbers.get_children():
        var v = child.value
        num += v * pow(10, digit)
        digit -= 1
    
    label.text = "入力した値: %d"%num
    
    if num == 9801:
        label.text = "正解!"

HBoxContainerのget_child() で上から順に数値入力(NumberInputSingle)が取得できるので、逆順(千の位→百の位→十の位→一の位)で合計値を計算します。

pow() を使うと10の指数(桁数)からその桁に対応する値を求めることができるので便利です。

では実行して動作を確認します。

99x99 は 9801 なので、"9801" を入力すると正解となります。

完成プロジェクト

今回作成したプロジェクトファイルを添付しておきます。

 

【Godot】多言語テキストに対応する方法

この記事では、Godot Engineで標準実装されている多言語(各言語に対応する翻訳)テキストに対応する方法を説明します。

多言語対応の方法

翻訳テキストを用意する

Godot Engineが標準で対応している翻訳データは「CSV」形式のファイルとなります。

例えば以下のような「カンマ」区切りのテキストファイルです。

keys,en,ja
GREET,"Hello, friend",こんにちは
ASK,"How are you?",元気?
BYE,Goodbye,さようなら

1行目には最初に「keys」と記述し、言語コードを指定します。使用可能な言語コードは公式ドキュメントの「ロケール」に記載されています。ここでは一般的に使われそうな言語コードを列挙しておきます。

先ほどのファイルは "en" と "ja" のみ定義しているので対応言語は 英語と日本語のみとなります。

そして、それぞれの先頭の行にテキストのキーを指定し、各言語のテキストをカンマ区切りで記述していきます。なおテキストの中でカンマ記号 (,) や改行文字を使用するテキストの場合は、ダブルクォーテーション(") で囲ます

翻訳データをプロジェクトに追加する

先ほどのテキストを "greeting.csv" というファイル名にしてプロジェクトに追加します。

すると以下のファイルが生成されます。

  • greeting.en.translation: "en" の翻訳インポート設定
  • greeting.ja.translation: "ja" の翻訳インポート設定

それぞれの翻訳データを使用するにはこれらのファイルをプロジェクト設定に追加する必要があります

翻訳データをプロジェクトに設定する

エディタから「プロジェクト > プロジェクト設定」を選びます。

プロジェクト設定から「ローカライズ」タブを選択し「翻訳」カテゴリとなっていることを確認します。

そうしたら「追加」ボタンをクリックして「greeting.en.translation」「greeting.ja.translation」を追加します。

翻訳テキストを使用する方法

tr() 関数を使うことで翻訳テキストを使用できます。

extends Node2D

func _ready() -> void:

    $LabelGreet.text = tr("GREET") # キー"GREET"の文字をラベルに設定する
    $LabelAsk.text = tr("ASK") # キー"ASK"の文字をラベルに設定する
    $LabelBye.text = tr("BYE") # キー"BYE"の文字をラベルに設定する

OSの言語設定が「日本語」になっている場合、このように日本語が表示されます。

言語を切り替える方法

表示言語を英語に変更したい場合は「TranslationServer」の set_locale() を使用します。

extends Node2D

func _ready() -> void:

    # 英語に切り替える場合    
    TranslationServer.set_locale("en")
    
    $LabelGreet.text = tr("GREET")
    $LabelAsk.text = tr("ASK")
    $LabelBye.text = tr("BYE")

プロジェクトファイル

今回作成したプロジェクトファイルを添付しておきます。

http://syun777.sakura.ne.jp/tmp/godot-data/TestLocal.zip

参考

今回の記事では以下のドキュメントを参考にしました

【Godot】AnimationPlayerでのアニメーションを実装する基本

この記事では AnimationPlayerを使用したアニメーションの実装方法について紹介します

点滅アニメーションの作成

まずは頂点カラーを変えるだけの簡単なアニメーションを実装してみます。

ルートノードの作成

Node2Dを作成して名前を「Player」に変更しておきます。

Godotくんをスプライトとして配置

次に「icon.png」をキャンバスにドラッグ&ドロップしてSpriteを作成します。

位置は原点を中心にしておきます。

AnimationPlayerノードを追加

AnimationPlayerノードを追加します。

ノードの階層はこのようになりました。

アニメーションビューの表示

"AnimationPlayer"ノードを選択した状態で、エディタの一番下にある「アニメーション」をクリックして、アニメーションビューを表示します。

アニメーションの作成

アニメーションを作成するには「アニメーション」をクリックして、「新規」を選ぶと新規アニメーション作成のダイアログが表示されるので、任意の名前を入力します。

アニメーションの名前は、点滅アニメーションなので「blink」という名前にしました。

アニメーション・トラックの作成

アニメーションはトラックという単位で管理されます。トラックを作成するので「トラックを追加」をクリックし、「プロパティトラック」を選びます。

プロパティトラックはノードの変数に直接アクセスするアニメーション・トラックです。

するとやたらデカいダイアログが表示されますが、気にせずに「Icon」ノードを選び「OK」をクリックします。

次にプロパティの選択です。今回は色を変更したいので「modulate」を選びます。

すると「Icon > modulate」のトラックが作成されました。

キーフレームの追加

続けてキーフレームの追加です。トラックの上で右クリックをして「キーを挿入」を選びます。

するとキーが追加されました。

このキーをクリックするとインスペクターにキーの情報が表示されます。

このキーの値(Value)が白色であることがわかります。

では同じ手順を繰り返して「1秒」「2秒」の位置にもキーを打ちます。

ここで縦に伸びている青い線をドラッグしてみます。この線はアニメーションの現在の位置を示す線なのですが、この線は「0.0〜1.0」の間でしか動かすことができません。

これはアニメーションの最大フレーム数(時間)が「1秒」に設定されているためです。

なのでここの値を「2」にすることで、アニメーションの有効範囲が「0.0〜2.0秒」まで広がります。

これでキーをクリックすることでそれぞれの値を編集できるようになります。

キーの位置がズレていたらインスペクターから修正しておきます。

今回は赤点滅にするので「1秒のキー」の色を赤色にしました。

では再生ボタンをクリックして再生します。

再生するとわかるのですが、2つ問題点があります。

  1. 赤くなったら白くならない
  2. アニメーションの終端で停止してしまう

1については、Godotではアニメーションを2秒とした場合、2秒にあるキーは含まないためとなります。ですので、2秒のアニメーションであれば例えば「1.99秒」などに最後のキーを配置する必要があります。

 

2つめの問題は「ループ再生」が有効になっていないためです。

ループ設定はアニメーション時間のとなりにあるアイコンをクリックすると有効になります。

これで点滅が有効になりました。

なおGodot Engineの仕様上、ループ再生時は 0秒(先頭)のキーと補完してくれるみたいなので、最終フレームのキーは存在しなくてもうまく動作するようです。

アニメーション再生と停止に便利なショートカットキー

アニメーションの確認には以下のショートカットキーを使いこなすと便利です。

ショートカットキー 説明
D 再生
SHIFT+D 先頭から再生を開始する
S 停止
A 逆再生
SHIFT+D 終端から逆再生を開始する

キーボードの「ASD」キーが割り当てられていると覚えておくと良いと思います。

移動アニメーションを作成する

アニメーションは色の変化だけではなく、移動にも使うことができます。

jumpアニメーションの作成

「アニメーション」をクリックして「新規」を選び、新規アニメーションを作成します。名前は「jump」にしておきます。

プロパティトラックの追加

「トラックを追加 > プロパティトラック」 を選びます。

Iconノードを選び、"position" プロパティを選びます。

これで "position" プロパティを操作するアニメーショントラックが作成されました。

キーの作成

0秒、1秒、2秒のところにキーを作成します。

ですが、2秒のキーをクリックしてもインスペクターに何も表示されないのは、アニメーションの最大秒数が足りないためです。

なのでここに「2」を設定し、最大秒数を「2秒」に変更します。ついでにループ再生も有効にしておきます。

次に「1秒」にあるキーを選択し、インスペクターから 「Value > y」の値を「-30」にします。

再生すると上下移動を繰り返すアニメーションが確認できます。

ただこれだと動きが今ひとつなので、「への字」のようなアイコンをクリックして、「キュービック」を選びます。

これはキーフレームの補完を曲線にするためのオプションです。これにより滑らかな上下運動となります。

ジャンプというよりは、ふわふわ浮かぶような動きですがひとまずこれで完成とします。

スクリプトからのアニメーション制御

スクリプトからアニメーションを制御します。

"Player" (ルートノード) を選択して以下のスクリプトをアタッチします。

extends Node2D


onready var anim = $AnimationPlayer

func _ready() -> void:
    # (x, y) = (300, 400) に移動しておく
    position = Vector2(300, 400)

func _process(delta: float) -> void:
    if Input.is_action_just_pressed("ui_accept"):
        print("stop")
        anim.stop()
    
    if Input.is_action_just_pressed("ui_up"):
        print("play blink")
        anim.play("blink")
    if Input.is_action_just_pressed("ui_down"):
        print("play jump")
        anim.play("jump")
        

 

Animationノードの play() で再生を行い、stop() で停止を行います。なおアニメーションは同時に1つのみ再生が可能なので、複数のアニメーションを同時再生したい場合には、別々の AnimationPlayer を作成する必要がありそうです。

完成プロジェクト

今回作成したプロジェクトファイルを添付しておきます。

【Godot】残像エフェクトの作り方

今回は残像エフェクトの作り方を解説します。

残像エフェクトの作り方

残像エフェクトの仕組み

今回紹介する残像エフェクトは、プレイヤーの移動した位置にそのフレームでの画像を残す方法となります。

そして時間経過で少しずつ透過しながら消えるようにします。

このエフェクトは、アクションゲームや格闘ゲームなどリアルタイム性のあるゲームで使うと効果的な演出になるかと思います。

残像エフェクトのプロジェクトファイル

今回は実装サンプルコードを解説する形となります。

残像エフェクトのコード

仕組みとしては、一定時間で消えるSpriteで実装できます。

以下サンプルコードです。

extends Sprite

var _timer:float

# ゴーストエフェクト開始
func start(_position:Vector2, _scale:Vector2, _frame:int, _flip_h:bool):
    position = _position
    scale = _scale
    frame = _frame
    flip_h = _flip_h
    _timer = 1.0

func _process(delta: float) -> void:
    _timer -= delta
    if _timer <= 0:
        # タイマー終了で消える
        queue_free()

    var alpha = _timer * 0.5
    modulate.a = alpha

残像エフェクトのもととなる情報を "GhostEffect" に渡します。今回は以下の情報を渡すようにしました

  • スプライトのフレーム番号: frame
  • 座標: position
  • スケール値: scale
  • 左右反転フラグ: flip_h

元のキャラクターのアニメーションで使用している情報を渡せば問題ないと思います。

以下、残像エフェクトを出現させる側のコードです。

# 残像エフェクトの処理  
func _proc_ghost_effect() -> void:
    if state == eState.IDLE:
        ghost_cnt = 0
        return # 停止中は何もしない
        
    # 残像エフェクトを生成判定
    ghost_cnt += 1
    if ghost_cnt%6== 2:
        # 残像エフェクト生成
        var eft = GHOST_EFFECT.instance()
        eft.start(position, scale, frame, flip_h)
        _ghost_effects.add_child(eft)

スケール値をなぜ使用しているのか

残像エフェクトとは直接関係ありませんが、最近の2Dジャンプアクションゲームの流行として、ジャンプ開始時に縦にスケールを伸ばし、着地時に横方向にスケール値を伸ばす(縦方向に縮める)という演出がよく使われます。

これを入れるとジャンプが気持ちよくなるので、2Dジャンプアクションゲームを作るときは知っておいて損はないテクニックではないかと思います。

参考

今回の解説は以下の記事を参考にしました

【Godot】モザイクシェーダーの作り方

今回はモザイクシェーダーの作り方について解説します。

モザイクシェーダーの作り方

画像素材の追加

まずは以下の画像をプロジェクトに追加します。

なお上記データには「white.png」というダミーデータも含まれています。このファイルも使用するのでプロジェクトに追加しておきます。

ルートシーンを作成し、"bg.png" を Spriteとして登録しておきます。

スプライトに直接モザイクシェーダーを記述する

まずはこのスプライトに直接シェーダーを適用してみます。

"Bg" ノードを選択して、インスペクタから「Canvas Item > Material > Material > [空]」をクリックして、「新規 Shader Material」を作成します。

続けて作成した Material をクリックして、「Shader > [空]」を選び「新規 Shader」を作成します。

作成したシェーダーをクリックすると、シェーダーエディタが開きます。

シェーダーコード

シェーダーには以下のコードを記述します。

shader_type canvas_item;

// モザイクを正方形にするための調整比率.
const float RATIO = 1024.0 / 600.0;

void fragment() {
    float size = 0.0001 + 0.05 * abs(sin(TIME));
    float size2 = size * RATIO;
    
    vec2 uv = UV;
    uv -= mod(uv, vec2(size, size2));
    
    // ブロックの位置を補正する
    uv.x += mod(1.0, size) * 0.5;
    uv.y += mod(1.0, size2) * 0.5;

    COLOR.rgb = texture(TEXTURE, uv, 0.0).rgb;
}

実行するとスプライトにモザイクが適用されます。

画面の一部分にモザイクを適用する

画面の特定の部分のみに適用するようにしてみます。

bg スプライトのシェーダーはいったんクリアしておきます。

次に "Main" ノードの直下に「TextureRect」を追加します。

TextureRectのインスペクターの「Texture」に "white.png" を設定しておきます。そして 「Rect > Scale」でサイズを調整しておきます。今回は横2倍のサイズとしておきました。

そして TextureRect のシェーダーを作成し、以下のように記述します。

shader_type canvas_item;

// モザイクを正方形にするための比率
const float ASPECT = 2.0;

// モザイク1ブロックあたりの大きさ。
// 0.01 あたりが良さそう
uniform float size : hint_range(0.001, 0.1);

// 疑似乱数
vec2 random(vec2 uv){
    uv = vec2( dot(uv, vec2(127.1,311.7) ),
               dot(uv, vec2(269.5,183.3) ) );
    return -1.0 + 2.0 * fract(sin(uv) * 43758.5453123);
}

void fragment() {
    float size_y = size * ASPECT;
    
    vec2 uv = SCREEN_UV;
    uv -= mod(uv, vec2(size, size_y));
    
    // 位置をランダムに少しだけずらす
    float t = TIME * 2.0; // 動く速度
    vec2 ofs = vec2(cos(t), sin(t));
    uv += 0.005 * ofs;

    // 右上に移動してしまうので補正する
    uv.x += mod(1.0, size);
    uv.y += mod(1.0, size_y);
    
    COLOR.rgb = textureLod(SCREEN_TEXTURE, uv, 0.0).rgb;
}

すると TextureRect の領域のみモザイクが適用されます。

完成プロジェクトファイル

今回作成したプロジェクトを添付しておきます。

http://syun777.sakura.ne.jp/tmp/godot-data/TestMosaic.zip

【Godot】オブジェクトを一時停止する方法

この記事ではオブジェクトの動きを一時停止する方法について解説します。

get_tree().paused で停止する方法

Godotには "get_tree().paused" というフラグに "true" を設定することで、ゲーム全体を停止させることができます。

実装方法については以下の記事で書きました。

 この設定方法はゲーム全体を止めてしまいますのでポーズ機能の実装には良いです。ですが、例えばアクションゲームや格闘ゲームでのヒットストップ(攻撃が当たった時に数フレームだけ停止する)を特定のオブジェクトにのみ適用する……といったことには向いていません

その場合には、"set_process()" を使用すると良いと思います。

set_process() を使用した一時停止

方法は簡単で停止したいオブジェクト(ノード)の "set_process()" 引数に "false" を指定して呼び出します。

var obj; // 停止したいオブジェクト

// これにより _process() が呼び出されなくなる
obj.set_process(false);

// 一時停止を解除する場合
obj.set_process(true);

// 一時停止しているかどうかを判定する
if obj.is_processing():
  // 一時停止している
  pass
else:
  // 一時停止していない
  pass

停止する仕組みとしては、"_process()" が呼び出されなくなるだけです。ですので例えば物理エンジンによる挙動を一時停止したい場合は "set_physics_process()" を使用します。

その他の関数については、公式ドキュメントの "Node" についての資料を確認します。

一時停止のサンプルコード

例えば以下のように "CanvasLayer" に "Godot"というオブジェクトがぶら下がっているとします。

これらをそれぞれ一時停止するコードです。

extends Node2D

onready var layer1 = $Layer1
onready var layer2 = $Layer2

# Buttonをクリックした
func _on_Button_pressed():
  # $Layer1以下のオブジェクトを操作する
    for godot in layer1.get_children():
        if godot.is_processing():
            # 動いていたら停止する
            godot.set_process(false)
        else:
            # 停止していたら動かす
            godot.set_process(true)

# Button2をクリックした
func _on_Button2_pressed():
  # $Layer2以下のオブジェクトを操作する
    for godot in layer2.get_children():
        if godot.is_processing():
            # 動いていたら停止する
            godot.set_process(false)
        else:
            # 停止していたら動かす
            godot.set_process(true)

なお、"set_process()" はそのノードのみ有効です。子となるノードに対しては適用されないので、停止したいオブジェクトすべてに対して関数呼び出しが必要となります。

サンプルプロジェクトファイル

今回の記事の内容を実装したプロジェクトファイルを添付しておきます。

【Godot】ツールチップの実装方法

今回はマウスでオブジェクトを選択したときに表示されるツールチップの実装方法について紹介します

ツールチップの実装方法

素材データ

今回使用する素材データです。

ToolTipシーンの作成

"Control" ノードをシーンとして作成し、名前を "ToolTip" に変更します。そしてその下に "Label" ノードを追加します。

フォントを差し替える

デフォルトのフォントは小さいのでフォントを変更します

https://www.dafont.com/xolonium.font

 

ここからフォントデータ「Xolonium」をダウンロードします。そして「Xolonium-Regular.ttf」をプロジェクトに追加します。

Labelノードに戻って、"Control > Theme" を選び、新規Themeを作成します。

作成した Theme をクリックしてテーマを表示し、"Theme > Default Font" から 新規DynamicFont を選びます。

そして、"Font Data" から 「読み込み」を選んで Xolonium-Regular.ttf を読み込みます。

さらに "Settings" からフォントサイズとアウトラインとその色を設定します。

  • Settings > Size: 32
  • Settings > Outline Size: 3
  • Outline Color: 黒 (0, 0, 0, 255)

ツールチップスクリプトを追加する

"ToolTip" ノードを選んでスクリプトをアタッチします。

スクリプトを以下のように記述します。

extends Control

# ツールチップで表示するテキスト
export var text := "Tool Tip"
# 親ノードのパス
export var owner_path:NodePath

onready var _label = $Label
onready var _owner_node = get_node(owner_path)

func _ready() -> void:
    # 初期状態は非表示にする
    visible = false
    
    # シグナルを接続する
    _owner_node.connect("mouse_entered", self, "_mouse_entered")
    _owner_node.connect("mouse_exited", self, "_mouse_exited")
    
func _process(delta: float) -> void:
    var base_pos = _get_screen_pos()
    base_pos.y -= 32 # 上に少しずらす
    rect_position = base_pos
    _label.text = text

# 描画位置を計算する
func _get_screen_pos() -> Vector2:
    var ofs = Vector2()
    if _owner_node is Node2D:
        # Node2Dを継承している場合
        ofs = _owner_node.position
    
    return get_viewport().get_mouse_position() - ofs

# 表示開始
func _mouse_entered():
    visible = true

# 表示終了
func _mouse_exited():
    visible = false

少し特殊なのが以下の記述です。

# 親ノードのパス
export var owner_path:NodePath

...

onready var _owner_node = get_node(owner_path)

親となるノードのパスを "NodePath" で指定して、そのノードを "get_node()" で取得しています。

Mainシーンの作成

これでツールチップが実装できたので、Mainシーンを作成します。

素材をプロジェクトに追加

次にイスの画像 "Chair.png" をプロジェクトに追加します。

この画像をマウスオーバーしたときに、先程作成した ToolTipを表示できるようにします。

イスオブジェクトを作成する

つづけて "Area2D" を追加します。

当たり判定を行うためには「Area2D」「KinematicBody2D」などが必要となるためです。ここではひとまず「Area2D」を使用します。

名前は「Chair」に変更しておきます。

"Chair.png" をキャンバスにドラッグ&ドロップして、Spriteとして追加します。

当たり判定の作成

マウスとの当たり判定を行うために、"CollisionPolygon2D" を追加します。

ノードの階層はこのようになっていれば問題ありません。

次にキャンバスをクリックして当たり判定のポリゴンを作成します。

ツールチップをChairノードにぶら下げる

最後に ToolTipシーンを Chairノードにぶら下げます。

ノード構成はこのようになっていればOKです。

そうしたらインスペクターの "ToolTip > Script Variables" から "Text" を 「Chair」に変更し、"Owner Path" に 「Chair (Area2D)」を割り当てます。

では実行して動作を確認します。

イスにマウスオーバーしたときのみ、ツールチップが表示されるようになります。

完成プロジェクト

今回作成したプロジェクトファイルを添付しておきます。

参考

今回は以下の動画を参考にしました。

この動画では、3Dでの判定や、マウスが画面端に移動したときの回り込みなどを解説しているので、より詳しい設定が必要な場合は参考になると思います。

【Godot】YSortを使った描画順の制御

今回はYSortを使用した描画順の制御について解説します。

📌YSortノードはGodot4.xでは削除されました

Godot4.xで YSort を行いたい場合、CanvasItem にある "y_sort_enabled" を true にすることでYSortが有効となります。

bool y_sort_enabled =false

trueの場合、最も低いY位置を持つ子ノードは、より高いY位置を持つ子ノードの前に描画されます。falseの場合、Yソートは無効になります。Yソートは、CanvasItemを継承する子にのみ影響します。

Yソートでノードをネストできます。子Yソートノードは、親Yソートと同じスペースでソートされます。この機能を使用すると、シーンツリーを変更することなく、シーンをより適切に整理したり、複数のシーンに分割したりできます。

GODOT DOCS > CanvasItem

Y座標による描画の違和感

例えばオブジェクト同士で重なりがある場合、立体感のある画像を重ねると違和感が発生します。

これは極端な例ですが、いくつか重なりに違和感があります。なぜこのようなことが起きるのかというと、Y座標で描画物が制御されていないためです。

YSortで描画順を制御する

これをプログラムで制御すると少し厄介なのですが、なんとGodot EngineにはY座標でオブジェクトの描画順を制御するノードが用意されています。

この「YSort」に描画順を制御したいノードをぶら下げると、違和感のない描画順になります。

サイズの異なるオブジェクトを使う場合

YSortを使う場合の注意点として、サイズの異なるオブジェクトを配置する場合です。

例えば木の画像が「128px」、キャラクターを「64px」とした場合、

そのまま YSort を使うとこのような違和感が発生します。

これは描画の基準(ピボット)が画像の中心となっているためです。

ですので描画の基準を足元に統一します。

すると違和感の解消されたYSortで描画が行われるようになります。

今回作成したプロジェクト

今回作成したプロジェクトファイルを添付しておきます。

  • [YSortを使う必要はないのでプロジェクトは削除しました]

なお木の画像については「ぴぽや倉庫」様よりお借りました。

【Godot】Godot Engineをソースコードからビルドする方法

この記事では、Godot Engine をソースコードからビルドする方法する方法を解説します。

ソースコードからのビルドする理由は主に以下の3つになると思います

  • 実行ファイル化されていない最新のビルドを取得したい場合
  • エンジンの挙動をカスタマイズ、不具合をローカルで修正する場合
  • アドオン化されていないプラグインを自分でビルドする場合

例えば、SpineのプラグインDragonbonesのプラグイン はアドオン化されていないので、自分でビルドする必要があります。

ビルドシステムの説明

以下のドキュメントに記載されている方法でビルド可能です。

ここを参考に、今回は Windows環境でのビルドを方法を説明します。

Godot Engineのソースコードの取得する

まずは Githubからソースコードを取得します。

Windows用のコンパイル

Windows環境のビルド方法は、以下のドキュメントに記載されています。

https://docs.godotengine.org/ja/stable/development/compiling/compiling_for_windows.html#doc-compiling-for-windows

■必要条件
Windowsコンパイルするには、次のことが必要です:

* Studio caveats" below or you will have to run/download the installer again.

* MinGW-w64 with GCC can be used as an alternative to Visual Studio. Be sure to install/configure it to use the posix thread model.

* Python 3.5 以降

* SCons 3.0 build system. If using Visual Studio 2019, you need at least SCons 3.1.1.

* Visual Studio Community, version 2017 or later. VS 2019 is recommended. Make sure to read "Installing Visual

Windows用のコンパイル > 必要条件

ここでは、"Visual Studio 2017" 以降がインストール済みであるとして説明をしていきます。

Scopeをインストールする

Godot Engineは SCon というビルドツールを使用しています。
そのために "Scoop" をインストールします。

https://scoop.sh/

こちらにページによると、PowerShell でインストールするのが良いようです。
ここでは PowerShell でインストールしていきます。

まずは、PowerShell を起動して以下のコマンドを実行します。

Invoke-Expression (New-Object System.Net.WebClient).DownloadString('https://get.scoop.sh')

問題なくインストールできれば良いですが、以下のメッセージが表示されてインストールできない場合があります。

PowerShell requires an execution policy in [Unrestricted, RemoteSigned, ByPass] to run Scoop.
For example, to set the execution policy to 'RemoteSigned' please run :
'Set-ExecutionPolicy RemoteSigned -scope CurrentUser'

これは、実行ポリシーが未設定であるためです。

この場合は、以下のコマンドで Scope の実行ポリシーを有効にします。

Set-ExecutionPolicy RemoteSigned -scope CurrentUser

すると以下のメッセージが表示されるので、「Yキー」を押して変更を行います。

実行ポリシーの変更
実行ポリシーは、信頼されていないスクリプトからの保護に役立ちます。実行ポリシーを変更すると、about_Execution_Policies
のヘルプ トピック (https://go.microsoft.com/fwlink/?LinkID=135170)
で説明されているセキュリティ上の危険にさらされる可能性があります。実行ポリシーを変更しますか?
[Y] はい(Y) [A] すべて続行(A) [N] いいえ(N) [L] すべて無視(L) [S] 中断(S) [?] ヘルプ (既定値は "N"): y

インストールが完了したら、以下のコマンドで正常にインストールされているかどうかを確認をします

scoop help

各種ツールのインストール

次に各種ツールをインストールします。
以下のコマンドを実行するとまとめてインストールしてくれます。

scoop install gcc python scons make

Pythonがインストールされていない場合は別途インストールする必要があるみたいですが、私の環境は問題なくこれでビルド環境は作れました。

ビルドの実行

ビルドの実行は、Godot Engineのソースコードのルートフォルダに移動して以下のコマンドを実行します。

scons platform=windows

ビルド完了までは結構時間がかかります。私の環境では 1時間半ほどかかりました。

ビルド完了

以下のメッセージが表示されればビルド完了です。

[Initial build] Linking Static Library ==> core\core.windows.tools.64.lib
[Initial build] Linking Program ==> bin\godot.windows.tools.64.exe
ライブラリ bin\godot.windows.tools.64.lib とオブジェクト bin\godot.windows.tools.64.exp を作成中
[Initial build] Building compilation database compile_commands.json
[Initial build] Building GLES3 GLSL header: "drivers\gles3\shaders\cube_to_dp.glsl.gen.h"
[Initial build] Building GLES3 GLSL header: "drivers\gles3\shaders\effect_blur.glsl.gen.h"
[Initial build] Building GLES3 GLSL header: "drivers\gles3\shaders\tonemap.glsl.gen.h"
[Initial build] progress_finish(["progress_finish"], [])
[Initial build] Building RD_GLSL header: "servers\rendering\renderer_rd\shaders\giprobe_write.glsl.gen.h"
[Initial build] scons: done building targets.
[Time elapsed: 00:40:02.976]
PS C:\Users\syun77\Downloads\godot-master>

exeファイルはルートディレクトリの「bin」フォルダに作られ、実行可能となります。

【Godot】多重スクロールの実装方法

この記事では、複数の背景を異なる速度で移動させることで、擬似的に奥行きを表現する「多重スクロール」の実装方法について紹介します。

多重スクロールの実装方法

素材データ

今回使用する素材データです

このデータには以下の2つのファイルが含まれています。

bg_back.png (背面に表示する画像)

bg_sky.png (空・前面に表示する)

プロジェクトを作成し、この2つの画像ファイルをプロジェクトに追加します。

ルートシーンの作成

2Dシーン(Node2D)を作成し、名前を「Main」に変更しておきます。

ParallaxBackground / ParallaxLayerを作成する

次に「ParallaxBackground」を作成します。

これは多重スクロールの背景を管理するためのノードです。このノードの下に配置された「ParallaxLayer」を制御するためのノードです。

ということで「ParallaxLayer」を作成します。

今回は背景を2枚使用するので、2つの「ParallaxLayer」を用意します。

背景スプライトを作成する

次に「bg_back.png」を「ParallaxLayer」ノードの下に配置します。

画面の左上に合わせたいので、スプライトのインスペクターから以下のようにします。

  • Offset > Centerd: チェックを外して左上基準にする
  • Transform > Positon: (x, y) = (0, 0) にする

bg_sky.png を "ParallaxLayer2" の下に "BgSky" として同じように配置し、BgSkyスプライトの位置に合わせます。

プレイヤーの作成

シーンノードの作成

2Dシーン(Node2D) を新規作成し、名前を "Player" に変更します。

Iconスプライトの作成

次に "icon.png" を原点に配置して、"Icon"スプライトを作成します。

Camera2Dノードの作成

さらに "Camera2D" ノードを追加します。

Camera2D のインスペクターから「Current」にチェックを入れ、カメラを有効にします。

プレイヤースクリプトの作成

Playerノードにスクリプトをアタッチして、以下のように記述します。

extends Node2D

# 移動速度
const SPEED := 400.0

func _process(delta: float) -> void:
    
    # 移動量を求める
    var velocity = Vector2()
    if Input.is_action_pressed("ui_left"):
        velocity.x = -1
    elif Input.is_action_pressed("ui_right"):
        velocity.x = 1
    if Input.is_action_pressed("ui_up"):
        velocity.y = -1
    elif Input.is_action_pressed("ui_down"):
        velocity.y = 1

    # 移動する
    position += velocity * SPEED * delta    

押したキーの方向に合わせて移動するスクリプトとなります。

これでプレイヤーは完成です。

プレイヤーを配置して動かす

Mainシーンに戻り、"Player" シーンを配置します。

では、実行して動作を確認します。

ですが、特に背景は移動しません。これは背景スクロールの設定をしていないためです。

ParallaxLayerの設定をする

「ParallaxLayer」ノードを選択して 「ParallaxLayer > Motion > Mirroring」から "x" の値を "800" にします。

これは "ParallaxLayer" 以下の描画を X方向に繰り返す値です。"bg_back.png" の横幅は 800px なので、ここに "800" を指定しています。

同様に 「ParallaxLayer2」ノードの値も設定します。

"bg_sky.png" の横幅も 800px なので、こちらも 800 となります。

では実行して動作を確認します。

スクロールはできているのですが、時々背景が表示されなくなります。これは背景画像が 800px で、画面サイズが 800px であることを想定しているためです。

ということで、メニューの「プロジェクト > プロジェクト設定」を選びます

 

「Display > Window」を選択し、Widthを「800」、Heightを「480」に変更します。

設定できたら実行して動作を確認します。

左右移動で、無事スクロールできるようになりました。

視差表現を行う

ただこれだけだと単にスクロールしているだけです。

プレイヤーを中心に、前面に街並み(bg_back.png)、背面に空(bg_sky.png) が存在していると想定して、視差表現を行うようにします。

前面の画像に最背面の画像を含めてしまったため、多少わかりにくいですが、イメージとしては以下のようになります。

前面にあるものほど移動速度を速くし、後ろにあるものほど速度が遅くすると、奥行きがあるように感じられます。

具体的なパラメータとしては、"ParallaxLayer" (前面の街並み) の 「Motion > Scale」のX値を「2」にします。

次に、"ParallaxLayer2" (後ろの雲) の「Motion > Scale」のX値を「0.5」にします。

実行すると、奥行きのある多重スクロールとなります。

完成プロジェクトファイル

今回作成したプロジェクトファイルを添付しておきます。

http://syun777.sakura.ne.jp/tmp/godot-data/TestParallax.zip

参考

この記事は以下の動画を参考に作成しました。

www.youtube.com