3104号室

本のレビューや音楽のことや日々思っていることを書くところ。つまり雑記。

第2回 2D/3Dゲームエンジン Godot Engine 入門 「2Dキャラクタを動かしてみる」

f:id:masashi-yamada0110:20180916192530p:plain

前回はGodotを使って"Hello World!"という文字列を画面に表示させてみました。

jet-city.hatenablog.com

今回は一気にステップアップしてキャラクタを動かしてみましょう。

作るもの

オリジナルのキャラクタをカーソルキーを使って動かしてみます。

動いている間はキャラクタの画像をアニメーションさせます。

f:id:masashi-yamada0110:20180916153058g:plain

準備

使用する画像を以下に置いておきました。

character.zip - Google ドライブ

character.zipを展開すると以下の3つの画像ファイルが入っています。

展開して任意のフォルダに保存して置いてください。

f:id:masashi-yamada0110:20180916154256p:plain green_man.png

f:id:masashi-yamada0110:20180916154334p:plain green_man_01.png

f:id:masashi-yamada0110:20180916154350p:plain green_man_02.png

プロジェクト作成

プロジェクトの作成方法は前回と変わりません。 任意の名前(下の画像の場合は"move")をつけて「作成と編集」ボタンを押しましょう。

f:id:masashi-yamada0110:20180916160259p:plain

プロジェクトに画像を追加する

画面の左側に「ファイルシステム」というタブがあるので、その下のフォルダアイコンと「res://」という文字が書かれている箇所(下記画像参照)をクリックしてください。

その後、先ほどダウンロードして保存した3つの画像ファイルを下の画像の箇所にドラッグ&ドロップしてください。

f:id:masashi-yamada0110:20180916160923p:plain

ドラッグ&ドロップするとドロップした箇所に、下記のように画像が追加されます。

f:id:masashi-yamada0110:20180916161542p:plain

ノード作成と設定

画面右側の「Scene」タブのすぐ下にある「+」をクリックしてノードを追加します。

f:id:masashi-yamada0110:20180909232406p:plain

「Create New Node」というダイアログが開くので、 ツリーの中から「Area2D」というノードを選択し、「作成」ボタンをクリックします。

f:id:masashi-yamada0110:20180916162025p:plain

そうすると、「Scene」タブ内に「Area2D」というノードが追加されます。

これがプレイヤーを表すノードとなるので、「Area2D」と書かれた箇所を2回クリックして、名前を「Player」に変更しておきましょう。

f:id:masashi-yamada0110:20180916162734p:plain

「Player」ノードを選択した状態で、もういちど「+」ボタンをクリックします。

先ほどと同様の手順で、今度は「AnimatedSprite」ノードを追加します。

すると「Player」ノードの下に「AnimatedSprite」ノードが作成されます。

「AnimatedSprite」を選択した状態で、画面右下の「インスペクター」タブから「Frames」プロパティの「」と書かれた項目をクリックします。

すると「New SpriteFrames」というメニュー項目が表示されるのでクリックしてください。

f:id:masashi-yamada0110:20180916163723p:plain

その後、先ほどまで「」と表示されていた箇所に「」と表示されるので、その項目をクリックします。

f:id:masashi-yamada0110:20180916164219p:plain

クリックすると画面下部に「アニメーション」エリアが現れるので、 画面左側の「res://」から「green_man.png」→「green_man_01.png」→「green_man.png」→「green_man_02.png」の順番になるように画像をドラッグ&ドロップしてください。

f:id:masashi-yamada0110:20180916164643p:plain

このままでは表示される画像の大きさが小さいので、「AnimatedSprite」ノードを洗濯した状態で、 画面右下の「インスペクター」からTransform->Scaleプロパティを選択し「x」、「y」の両方に「2」を入力して表示サイズを2倍にします。

f:id:masashi-yamada0110:20180916190905p:plain

これで必要なノードの作成と設定は終了です。

続いてスクリプトを記述してみましょう。

スクリプトを記述してキャラクタを動かす

画面右上の「Scene」タブから「Player」ノードを選択し、f:id:masashi-yamada0110:20180916165358p:plainアイコンをクリックしてください。

すると以下のようなダイアログが表示されるので、何も変更を加えずに「作成」ボタンをクリックします。

f:id:masashi-yamada0110:20180916165856p:plain

画面中央に下図のようなエリアが表示されます。このエリアでスクリプトを編集します。

f:id:masashi-yamada0110:20180916170328p:plain

スクリプトはGDScriptと言うGodot Engine独自のスクリプトを使用しています。

スクリプトの仕様の詳細に関しては公式のドキュメントを参照してください。

python と似た構文なので、pythonを使った経験があれば難なく使えると思います。

今回は入門ということなので、網羅的な説明よりも、必要な内容をその都度説明していこうとおもいます。

まず、最初に以下のようなスクリプトが入力されます。

extends Area2D

# class member variables go here, for example:
# var a = 2
# var b = "textvar"

func _ready():
    # Called when the node is added to the scene for the first time.
    # Initialization here
    pass

#func _process(delta):
#  # Called every frame. Delta is time since last frame.
#  # Update game logic here.
#  pass

まず、最初の行のpython extends Area2Dですが、これは今編集中の"Player"クラスが"Area2D"クラスを継承していることを表します。

ここで他の言語を学習した経験のある人はクラスの宣言がないことを不思議に思うかもしれません。

じつは"GDScript"では「ファイルはクラス」と考えます。

1つのファイルが必ず1つのクラスを表すので、他の言語でみられるpython class Player:のような宣言が必要ありません。

メンバ変数を宣言する場合は、コメントの例にあるようにインデントなしで以下のように宣言します。

var a

今回はスクリーンの大きさをメンバ変数に持ちたいので以下のように宣言します。

var screen size

次にpython func _ready():についてですが、 この行はクラスの生成時に最初に呼び出されるメソッドで、基本的にはクラスの初期化処理を記述します。

func _ready():
    #ここに初期化処理を書く

以下のように記述して先ほど宣言したscreensize変数にスクリーンのサイズを代入しましょう。 また、キャラクタの初期位置(position)を画面の真ん中にしたいので、その処理を記述します。

func _read():
    screensize = get_viewport().size # スクリーンサイズを代入
    position.x = screensize.x / 2
    position.y = screensize.y / 2

その下にpython #func _process(delta): というコメントアウトされた行がありますが、先頭の#を削除してコメントアウトを外してください。

このpython _process(delta)はフレーム毎に呼ばれるメソッドです。クラスのフレーム毎の振る舞いはここに記述します。

func _process(delta):
    # ここにフレーム毎の振る舞いを記述する

キーボードの入力にたいして、キャラクタを動かすのでpython _process(delta)メソッドには 以下のコードを記述します。各コードの意味はコード内にコメントで記述しておきましたので参考にしてください。

func _process(delta):
    var velocity = Vector2() # Playerが動くベクトルを宣言
    if Input.is_action_pressed("ui_right"): # 右カーソルが押されていたら
        velocity.x += 1                               # ベクトルのx要素に1足す
    if Input.is_action_pressed("ui_left"):   # 左カーソルが押されていたら
        velocity.x -= 1                                # ベクトルのx要素から1引く
    if Input.is_action_pressed("ui_down"): #上カーソルが押されていたら
        velocity.y += 1                                # ベクトルのy要素に1足す
    if Input.is_action_pressed("ui_up"):     # 上カーソルが押されていたら
        velocity.y -= 1                                 #  ベクトルのy要素から1引く
 
    if velocity.length() > 0: # もしベクトルの長さが0より大きいつまりキーが押されている時
        velocity = velocity.normalized() * 100 # 実際に動かすのは100ドットなので100掛ける
        $AnimatedSprite.play()  # アニメーションをスタート
    else: 
        $AnimatedSprite.stop() # それ以外ではアニメーションを停止
    
     # position を変更することでPlayerの座標を移動する
    position += velocity * delta # ベクトル * delta

    # 動ける範囲を画面内に限定する
    position.x = clamp(position.x, 0, screensize.x) 
    position.y = clamp(position.y, 0, screensize.y)

最終的なコードは以下です。

extends Area2D

var screensize

func _ready():
    screensize = get_viewport().size
    position.x = screensize.x / 2
    position.y = screensize.y / 2

func _process(delta):
    var velocity = Vector2()
    if Input.is_action_pressed("ui_right"):
        velocity.x += 1
    if Input.is_action_pressed("ui_left"):
        velocity.x -= 1
    if Input.is_action_pressed("ui_down"):
        velocity.y += 1
    if Input.is_action_pressed("ui_up"):
        velocity.y -= 1
    if velocity.length() > 0:
        velocity = velocity.normalized() * 100
        $AnimatedSprite.play()
    else:
        $AnimatedSprite.stop()
        
    position += velocity * delta
    position.x = clamp(position.x, 0, screensize.x)
    position.y = clamp(position.y, 0, screensize.y)

動かしてみる

実際に動かしてみましょう。画面右上のPlayボタンをクリックしてください。

f:id:masashi-yamada0110:20180909235858p:plain

いくつか確認のダイアログが開くと思いますが、特に何も変更せず「OK」または「保存」ボタンをクリックして先に進めてください。

ウインドウが開くのでカーソルキーを押すとキャラクターが動くことを確認できます。

f:id:masashi-yamada0110:20180916153058g:plain

参考文献