プレイヤーのコーディング

このパートでは、playerの動きやアニメーション、衝突判定を設定します。

そのために、組み込みノードからは得られない機能を追加する必要があるので、スクリプトを追加します。 Player ノードをクリックし、「スクリプトをアタッチ」ボタンをクリックします:

../../_images/add_script_button.png

スクリプト設定ウィンドウ内の設定はそのままで構いません。「作成」をクリックしてください:

注釈

C#スクリプトまたはその他の言語を作成する場合は、作成を実行する前に [言語] ドロップダウン メニューから言語を選択します。

../../_images/attach_node_window.png

注釈

はじめてGDScriptを扱う場合は、 スクリプト言語 を読んでから先に進んでください。

まず、このオブジェクトに必要なメンバー変数を宣言します:

extends Area2D

export var speed = 400 # How fast the player will move (pixels/sec).
var screen_size # Size of the game window.

最初の変数 speedexport キーワードを使用すると、インスペクタでその値を設定できるようになります。これは、値をノードの組み込みプロパティのように調整できるようにする場合に便利です。 Player ノードをクリックすると、インスペクタの "Script Variables" セクションにプロパティが表示されます。ここで値を変更すると、スクリプトに記述された値よりも優先されることに注意してください。

警告

C#を使用している場合、新しいエクスポート変数またはシグナルを表示する場合は、プロジェクトアセンブリを(再)ビルドする必要があります。このビルドは、エディタウィンドウの下部にある「Mono」をクリックしてMonoパネルを表示し、「Build Project」ボタンをクリックして手動でトリガーできます。

../../_images/export_variable.png

_ready() 関数は、ノードがシーンツリーに入ると呼び出されます。これは、ゲームウィンドウのサイズを調べる良いタイミングです:

func _ready():
    screen_size = get_viewport_rect().size

これで _process() 関数を使用して、プレイヤーが何をするかを定義できます。 _process() はフレームごとに呼び出されるため、これを使用してゲームの要素を更新しますが、これは頻繁に変更されることが予想されます。プレイヤーの場合、次のことを行う必要があります:

  • 入力をチェックします。

  • 指定した方向に移動します。

  • 適切なアニメーションを再生します。

まず、入力をチェックする必要があります - プレイヤーはキーを押しているでしょうか?このゲームでは、4方向の入力チェックをする必要があります。入力アクションは、プロジェクト設定の「インプットマップ」で定義されます。ここで、カスタムイベントを定義し、異なるキー、マウスイベント、またはその他の入力を割り当てることができます。このデモでは、キーボードの矢印キーに割り当てられているデフォルトのイベントを使用します。

「プロジェクト」→「プロジェクト設定」の順にクリックしてプロジェクト設定を開き、上部の「インプットマップ」タブをクリックします。一番上のバーに"move_right"と入力し、「追加」をクリックして move_right アクションを追加します。

../../_images/input-mapping-add-action.png

キー入力をこのアクションに設定する必要があります。右の"+"アイコンをクリックし、出てきたドロップダウンメニューの中から"Key"オプションを選択します。どのキーを利用したいか聞かれますので、キーボードの右矢印キーをクリックし、"Ok"を選択してください。

../../_images/input-mapping-add-key.png

以上の手順を残り3方向分設定します。

  1. move_left を左矢印キーに設定します。

  2. move_up を上矢印キーに設定します。

  3. そして``move_down`` を下矢印キーに設定します。

インプットマップは次のようになります:

../../_images/input-mapping-completed.png

「閉じる」ボタンを押してプロジェクト設定を終了します。

注釈

今回は一つのキーのみを各アクションに設定しましたが、複数のキーやジョイスティックボタン、マウスボタンを同じアクションに設定することができます。

キーが押されているかどうかを Input.is_action_pressed() を使用して検出できます。これは、押された場合は true 、押されていない場合は false を返します。

func _process(delta):
    var velocity = Vector2.ZERO # The player's movement vector.
    if Input.is_action_pressed("move_right"):
        velocity.x += 1
    if Input.is_action_pressed("move_left"):
        velocity.x -= 1
    if Input.is_action_pressed("move_down"):
        velocity.y += 1
    if Input.is_action_pressed("move_up"):
        velocity.y -= 1

    if velocity.length() > 0:
        velocity = velocity.normalized() * speed
        $AnimatedSprite.play()
    else:
        $AnimatedSprite.stop()

まず velocity(0, 0) に設定することから始めます - デフォルトでは、プレイヤーは動いてはいけません。次に、各入力をチェックし、 velocity から加算/減算して全体の方向を取得します。たとえば、 を同時に押した場合、結果の velocity ベクトルは (1, 1) になります。この場合、水平方向と垂直方向の動きを追加しているため、プレイヤーは水平方向に移動した場合よりも、斜め方向に速く移動します。

加速度を正規化すれば、これを防ぐことができます。つまり、速度の長さ1 に設定し、それから希望の速度を乗算します。これは、これ以上速い対角移動がないことを意味します。

ちなみに

以前にベクトル演算を使用したことがない場合、あるいは忘れてしまった場合は、ベクトル演算でGodotのベクトル使用の説明をみることができます。これは知っておくと良いですが、このチュートリアルの残りの部分では必要ないでしょう。

We also check whether the player is moving so we can call play() or stop() on the AnimatedSprite.

ちなみに

$ is shorthand for get_node(). So in the code above, $AnimatedSprite.play() is the same as get_node("AnimatedSprite").play().

In GDScript, $ returns the node at the relative path from the current node, or returns null if the node is not found. Since AnimatedSprite is a child of the current node, we can use $AnimatedSprite.

移動方向がわかったので、プレイヤーの位置を更新します。clamp() を使用して、プレイヤーが画面を離れないようにします。Clampingの意味は長さに制限をかける事です。_process 関数の下部に追加して下さい (else までインデントしないように注意):

position += velocity * delta
position.x = clamp(position.x, 0, screen_size.x)
position.y = clamp(position.y, 0, screen_size.y)

ちなみに

_process() 関数の delta パラメータは フレームの長さ - 前のフレームが完了するまでに要した時間を参照します。この値を使うことで、動きの処理はフレームレートの変動の影響を受けなくなります。

Click "Play Scene" (F6, Cmd + R on macOS) and confirm you can move the player around the screen in all directions.

警告

「デバッガ」パネルで以下のようなエラーが発生した場合

null インスタンス上の ベース 'null インスタンス' の関数 'play' を呼び出そうとしています。

this likely means you spelled the name of the AnimatedSprite node wrong. Node names are case-sensitive and $NodeName must match the name you see in the scene tree.

アニメーションの選択

Now that the player can move, we need to change which animation the AnimatedSprite is playing based on its direction. We have the "walk" animation, which shows the player walking to the right. This animation should be flipped horizontally using the flip_h property for left movement. We also have the "up" animation, which should be flipped vertically with flip_v for downward movement. Let's place this code at the end of the _process() function:

if velocity.x != 0:
    $AnimatedSprite.animation = "walk"
    $AnimatedSprite.flip_v = false
    # See the note below about boolean assignment.
    $AnimatedSprite.flip_h = velocity.x < 0
elif velocity.y != 0:
    $AnimatedSprite.animation = "up"
    $AnimatedSprite.flip_v = velocity.y > 0

注釈

上記のコードにおけるブール値の代入は、プログラマーがよく使う略式記法です。比較テスト (ブール値) とブール値の代入を行っていますが、両方とも同時に行うことができます。次のコードと、上記の1行でのブール値代入とを比べてみましょう。

if velocity.x < 0:
    $AnimatedSprite.flip_h = true
else:
    $AnimatedSprite.flip_h = false

もう一度シーンを再生して、それぞれの方向のアニメーションが正しいことを確認してください。

ちなみに

ここでよくある間違いは、アニメーションの名前を間違って入力してしまうことです。SpriteFramesパネルに表示されるアニメーションの名前は、コードの中で入力したものと一致していなければなりません。もしアニメーションの名前を "Walk" とした場合、コード中では大文字の "W" も使わなければなりません。

動きが正しく機能していることを確認したら、次の行を _ready() に追加して、ゲームの開始時にプレイヤーが非表示になるようにします:

hide()

コリジョン(衝突/当り判定)の準備

Player には敵に攻撃されたことを検知してもらいたいのですが、まだ敵を作っていません!Godotのシグナル機能を使って動作させるので、大丈夫です。

スクリプトの先頭で extends Area2d の後に、次の行を追加します:

signal hit

これは、プレイヤーが敵と衝突したときにプレイヤーが発信する(送り出す)"hit"と呼ばれるカスタムシグナルを定義します。衝突を検出するために Area2D を使用します。 Player ノードを選択し、インスペクタタブの横にある「ノード」タブをクリックすると、プレイヤーが発信するシグナルのリストが表示されます:

../../_images/player_signals.png

カスタムの「hit」シグナルもありますね! 敵は RigidBody2D ノードになるため、 body_entered(body: Node) シグナルが必要です。これは、ボディがプレイヤーに接触したときに発信されます。「接続」をクリックすると、「シグナルの接続」ウィンドウが現れます。これらの設定を変更する必要はないので、再度「接続」をクリックしてください。Godotはプレイヤーのスクリプトに自動的に関数を作成します。

../../_images/player_signal_connection.png

シグナルがこの関数に接続されていることを示す緑色のアイコンに注意してください。次のコードを関数に追加します:

func _on_Player_body_entered(body):
    hide() # Player disappears after being hit.
    emit_signal("hit")
    # Must be deferred as we can't change physics properties on a physics callback.
    $CollisionShape2D.set_deferred("disabled", true)

敵がプレイヤーに当たるたびに、シグナルが発せられます。プレイヤーの衝突を無効にして、 hit シグナルを複数回トリガーしないようにする必要があります。

注釈

エリアのコリジョン形状を無効にすると、それがエンジンの衝突処理の途中だったときにエラーが発生する可能性があります。 set_deferred() を使用すると、安全にシェイプを無効にできるようになるまでGodotを待機させることができます。

最後のピースとなるのは、新しいゲームの開始時にPlayerをリセットするため、呼び出す関数を追加することです。

func start(pos):
    position = pos
    show()
    $CollisionShape2D.disabled = false

playerが設定できたことを確認したら、次のパートで敵を設定しましょう。