📖 Godot Engine 4.2の日本語のドキュメント / スクリプト言語
「 👆 Godot にあるものは全て ノード(Node)で、
GDScript というのは そのノードを操作するものみたいだな」
「 GDScript は Python に似ているが 全然別物ということだぜ。
新しく覚え直せだぜ」
「 また、GDScript の実行速度は遅いらしいぜ。
GDScript は C++ 言語で書かれたプログラムを呼び出すから、内部的な処理は速いらしいぜ」
「 👇 Godot には ノードと シーンという用語が出てくるんだけど、
計算機科学の わたしたちから見ると 造語のクセがあるわよ」
📖 ノードとシーン
.
「 👆 グラフセオリー(Graph Theory;グラフ理論)の一題材の ツリー・ストラクチャー(Tree Structure;木構造)を
わたしたちは 知っているが」
「 👆 Godot は ツリーのことを シーン(Scene)と言い換えているのかだぜ?」
「 👆 さらに 計算機科学の わたしたちは 木の中に含まれるサブツリー(Subtree;部分木)を知っているぜ」
「 👆 Godot では、 シーンもまた ノードになる、という 言い方 をしている」
「 シーン、つまり サブツリーのファイルの拡張子は .tscn
のようね」
「 なんて発音するか分からん嫌な拡張子だ…… ティーシーン?」
「 Godot は、 .tscn
を再生するプレイヤーなのよ」
「 👇 GDScript のリファレンスがあるそうだぜ。リンクをメモしておこう」
「 プログラムのレッスンを進めていくんで、なにか 小さな画像素材を 1つ用意してくれだぜ」
「 👆 最初に選ぶのが その他のノード って どうかしてるよな?」
「 👆 その中から Sprite2D を選ぶなんて、直観的に無理だぜ」
「 👆 これが Sprite2D をルート(Root;根)に持つ シーン(※つまりサブツリー)を作成したところだぜ」
「 👆 テンプレートを Object:Empty にしとけだそうだぜ」
「 👆 なんか コード・エディターが出てくるな。ここに GDScript を書けばいいのだろう」
extends Sprite2D
func _init():
print("Hello, world!")
「 👆 Hello, world! と出力ビューに表示されたな」
「 func _init():
メソッドは コンストラクタなんだ」
extends Sprite2D
var speed = 400
var angular_speed = PI
func _init():
print("Hello, world!")
func _process(delta):
rotation += angular_speed * delta
「 func _process(delta):
メソッドは 時間 delta 分の処理なんだ」
「 👆 Ctrl
キーを押しながら コードをクリックすると 説明が出てきたり、定義に飛んだりするようだぜ」
extends Sprite2D
var speed = 400
var angular_speed = PI
func _init():
print("Hello, world!")
func _process(delta):
# その場で ねずみ花火のように くるくる回る
rotation += angular_speed * delta
# 洗濯機の中の衣類のように 周る
var velocity = Vector2.UP.rotated(rotation) * speed
position += velocity * delta
📺 動画
「 入力のインプットを受け取る方法は 2種類あって、
Input
シングルトンを使う方法と、 _unhandled_input()
コールバック関数を使う方法があるようだぜ」
extends Sprite2D
var speed = 400
var angular_speed = PI
func _init():
print("Hello, world!")
func _process(delta):
# その場で ねずみ花火のように くるくる回る
rotation += angular_speed * delta
# 洗濯機の中の衣類のように 周る
var velocity = Vector2.UP.rotated(rotation) * speed
var movement = velocity * delta
# 何も押さなければその場で回転
var direction = 0
# 左キー押下で頭上の方へ進む
if Input.is_action_pressed("ui_left"):
direction = -1
# 右キー押下で足下の方へ進む
if Input.is_action_pressed("ui_right"):
direction = 1
movement *= direction
# 移動ベクトルを足す
position += movement
📺 動画
「 プロジェクト設定 の インプットマップ タブを見れば Input.is_action_pressed()
メソッドの引数に何書いたらいいか
自分で調べられるそうだぜ」
extends Sprite2D
var speed = 400
var angular_speed = PI
func _init():
print("Hello, world!")
func _process(delta):
var velocity = Vector2.ZERO
# 上キーを押していなければ進まない仕組み
if Input.is_action_pressed("ui_up"):
velocity = Vector2.UP.rotated(rotation) * speed
# その場で ねずみ花火のように くるくる回る
rotation += angular_speed * delta
# 洗濯機の中の衣類のように 周る
var movement = velocity * delta
# 何も押さなければその場で回転
var direction = 0
# 左キー押下で頭上の方へ進む
if Input.is_action_pressed("ui_left"):
direction = -1
# 右キー押下で足下の方へ進む
if Input.is_action_pressed("ui_right"):
direction = 1
movement *= direction
# 移動ベクトルを足す
position += movement
「 👆 上キーを押していなければ 進まないという仕組みも追加したぜ」
📖 シグナルの使用
「 イベントハンドラーじゃないのかだぜ? Linux みたいだな」
「 👆 新しいシーンを作れとのことだぜ。 シーンって何なんだぜ?」
「 サブツリーのルートノード(Root Node;根)なんじゃないか?
「 👆 シーンは サブツリーなのよ。 サブツリーのルートをさらに選ぶのよ」
「 👆 なんで 2D シーン
を選んで、出てくるのが Node2D
なんだぜ? 技術的に ぐちゃぐちゃだな」
「 👆 子ノードとして Button
を追加しろとのことだぜ」
Node/CanvasItem/Control/BaseButton/Button
「 👆 なんか クラス階層図みたいなツリー構造だな。 BaseButton
の下に Button
が出てくるの カッコ悪いよな」
「 Godot は ノードを主張してるくせに Button
のサブ・ノードが見えないじゃない」
「 👆 F6
キーを打鍵すると サブツリーを 動作テストできるそうだぜ」
「 👆 ノード
タブをクリックすると シグナルの一覧が出てくるぜ」
「 お前は インスペクター なんじゃないの?
なんで インスペクターの隣の ノード タブをクリックしたんだぜ?
ノード タブは、インスペクターじゃないってのかだぜ?」
「 Godot の国語は ぐちゃぐちゃ だな。技術的にクソだ」
「 👆 BaseButton
の下に pressed()
メソッドがあるから ダブル・クリックしろだぜ」
「 pressed()
は メソッドなの? シグナルなの?」
「 pressed()
メソッドは pressed
メッセージが送られてきたときに実行されるイベントハンドラーなんじゃないか?」
「 👆 きふわらべが 別のサブツリーに居て アクセスできなかったので、
カット&ペーストで 連れてきたぜ」
「 👆 エンプティセット(Empty Set;空集合,くうしゅうごう)だぜ」
「 👆 センダー(Sender;送信者)である BaseButton の pressed()
メソッドが呼び出されたとき、
さらに レシーバー(Receiver;受信者)である Sprite2D の _on_button_pressed
メソッドが呼び出される」
「 Godot の開発者たちは 国語のセンスがないことが分かった。わたしが 言い換えてやるぜ」
「 👆 わたしのコードに イベントハンドラーが 勝手に追加されたぜ」
「 じゃあ そこに ボタンが押されたときに やりたい処理を書けばいいのよ」
「 👆 イベントハンドラーのシグネチャーの左横に 緑色の矢印が表示されていて、クリックできるそうだぜ」
func _on_button_pressed():
# 働いてたら休む。
# 休んでたら働く。
set_process(not is_processing())
「 👆 ボタンを押下したときのコードを サンプルに従って書いたぜ。
メソッド名が悪いよな。意味が分からん。まあ Godot は、国語がウリではないから 仕方ない」
「 エンプティセットを保存してないから、古いサブツリーを実行しているのでは?」
「 実行ボタンをクリックしたときに実行される サブツリー は、どこで変更できる?」
「 エディターに戻って Ctrl + F1
キーを押してみろだぜ」
「 👆 上図のボタンをクリックすると スクリプトのページが開くそうだぜ」
extends Sprite2D
var speed = 400
var angular_speed = PI
func _init():
print("Hello, world!")
func _process(delta):
var velocity = Vector2.ZERO
# 上キーを押していなければ進まない仕組み
if Input.is_action_pressed("ui_up"):
velocity = Vector2.UP.rotated(rotation) * speed
# その場で ねずみ花火のように くるくる回る
rotation += angular_speed * delta
# 洗濯機の中の衣類のように 周る
var movement = velocity * delta
# 何も押さなければその場で回転
var direction = 0
# 左キー押下で頭上の方へ進む
if Input.is_action_pressed("ui_left"):
direction = -1
# 右キー押下で足下の方へ進む
if Input.is_action_pressed("ui_right"):
direction = 1
movement *= direction
# 移動ベクトルを足す
position += movement
func _on_button_pressed():
# 働いてたら休む。
# 休んでたら働く。
set_process(not is_processing())
# サブツリーが全てインスタンス化されたときに呼び出される
func _ready():
# タイマーノード取得
var timer = get_node("Timer")
# timer ソースの timeout シグナルに _on_timer_timerout メソッドを接続
timer.timeout.connect(_on_timer_timeout)
func _on_timer_timeout():
# 可視性を反転
visible = not visible
📺 動画
📖 カスタムシグナル
「 このあとも チュートリアルは続くが、洋ゲーなんで 興味無いんで スキップする!」
「 👇 書き方がいくつか有るようだぜ。これを読んで分かるか?」
📖 Enum
enum State {STATE_IDLE, STATE_JUMP = 5, STATE_SHOOT}
func _ready():
# Access values with Name.KEY, prints '5'
print(State.STATE_JUMP)
# Use constant dictionary functions
# prints '["STATE_IDLE", "STATE_JUMP", "STATE_SHOOT"]'
print(State.keys())
「 別ファイルで定義した列挙型をインポートするには どうやったらいいんだぜ?」
「 👇 書き方がいくつか有るようだぜ。これを読んで分かるか?」
📖 How to export an enum and import to another script
# Author: samsfacee
# state_machine.gd
extends Node2D
class_name StateMachine
enum State { STATE_STANDING, STATE_JUMPING, STATE_DUCKING, STATE_DIVING}
# player.gd
var state = StateMachine.State.STATE_STANDING
「 クラスに名前を付けて グローバルに公開するのか。それ以外の方法は?」
📖 How to declare a global named enum?
# Author: Zylann
const MyNamedEnum = preload("path/to/MyNamedEnum.gd")
func _ready():
print(MyNamedEnum.TYPE1)
「 別ファイルで定義されたクラスを生成するには どうやったらいいんだぜ?」
📖 class_nameを使用した新しいオブジェクトのインスタンス化は、preload()関数やload()関数とは異なりますか?
Question: Robotex
Answer: klaas
あるファイルでクラスを定義:
extends Object
class_name MyClass
生成方法1:
# 名前付きスクリプトは起動時にグローバルに登録されます。
var obj = MyClass.new()
生成方法2:
# プリロードされたスクリプトは、コンパイル時にクラスの var または const にロードされます。
# これらはグローバルにアクセス可能ではありません。
var MyClass = preload("MyClass.gd")
var obj = MyClass.new()
生成方法3:
# ロードされたスクリプトは、実行時にクラス変数にロードされます。
# これらはグローバルにアクセス可能ではありません。
var MyClass = load("MyClass.gd")
var obj = MyClass.new()
.
Crieitは誰でも投稿できるサービスです。 是非記事の投稿をお願いします。どんな軽い内容でも投稿できます。
また、「こんな記事が読みたいけど見つからない!」という方は是非記事投稿リクエストボードへ!
こじんまりと作業ログやメモ、進捗を書き残しておきたい方はボード機能をご利用ください。
ボードとは?
コメント