「Godot」0からゲーム作ろう! (実践1: キャラ操作 ー2Dゲーム基礎ー)

記事2へようこそ! Coding課のAugustです.


本シリーズは, 完全に0から, あらゆる2Dゲームにも使われるエンジン機能や, ゲーム制作に欠かせない

  • 信号(Signal, ~=UntiyのEvent System)
  • キャラ操作
  • Tweenの作り方
  • User Interface

などを
Godotエンジンを使って解説するものです.

Unity達人でも, Godotエンジンでの制作流れをみて, 感じられるものがいるかもしれません.



本記事は,

  • 初期設定
  • キーボード・ゲームパッドでのキャラ操作の実装

という順で進みます.

(今回の進展を示す動画)



なお, もし 「このゲーム・この機能, どうやって作れる?」に対して興味を持つ方は, (複雑過ぎない機能かつ現段階2D限定)なら, Discordでメッセージくれたら次回の記事で作りたいと思います.

では早速始めましょう.

Godot Engineのdownloadは, https://godotengine.org/ または Steam上でできます.
私は普段先行リリースのVer 4.5を使っています. Godot4である以上, 支障はないと考えます.

下準備

プロジェクトを開いたら

3D画面から2Dに移行して, その他のノードからを選び, 一番上の “Node”型ノードを制作し, 名前を”Main”(適当) に変え, Ctrl+Sで保存しよう.

次に, 画面の右上にある playボタン, あるいはF5を押して, 実行してみよう

「現在のものを選択」を押したら, “Main” Sceneは, ゲームを開いて最初に表示されるシーンにあった. これは通常のゲームでは, メインメニューなどに該当する.


さて, これから正式に制作を始めよう!

キーボード・ゲームパッドでのキャラ操作の実装

Ctrl + N (新しいSceneを作る) を押して, 2Dシーンを作る.

ノードを”Player”(名前自由) をし, Ctrl+A, あるいはこの「+」を押して, 子ノードを追加して, Sprite2Dを選ぶ

次に, プロジェクトフォルダに以下の画像(あるいは自分の画像)を入れよう.

さて, この画像をSprite2DのTextureとして適用しよう.

これで, 動かしたいキャラクターは定義された.




次は, 移動に使うキーボードボタンを定義しよう.



さて, コードを書き始めよう!

ここで, GDScriptの基本を教えよう:

extends Node2D (Node2Dというクラスを継承する) (Node2D<-CanvasItem<-Node<-Object)


GDScript はPythonのような動的型付け言語と同時に, 静的型付けもできる.
var speed:int = 200(新しい変数speedを定義し, speedはintで, デフォルト値は200)

または

var speed:= 200 (新しい変数speedを定義し, speedのデフォルト値は200, 型は「200」と同じ)
は静的型付けの例で,
int speed = 200;
を 意味する.

静的型付けた変数は, 違う型につけられるとエラーが出る.
動的型付けにしたい場合:
var speed = 200

と書けばいい.

func _ready() は, Unityのstart()と同様で, ノードがツリーに入る同時に実行する.
func _process()は, UnityのUpdate()と同様に, フレームごと実行される.

さて, この行

func _process(_delta: float) -> void:
    if Input.is_action_pressed("right"):
        self.position += speed * Vector2.RIGHT * _delta

float型変数_deltaとは, フレーム毎の時間差である. これをspeedにかけることで, どのPCのFPSでも安定な200/sの速度が保証される.
if Input.is_action_pressed("right") 英語が意味する通り, “right”と定義されたボタンが押されたら発動する



さて, そろそろ長くなってきたので, スピードアップしていきましょう!

var direction:Vector2 = Vector2.ZERO
func _process(_delta:float) -> void:
	direction = get_movement_vector()
	self.position += direction*speed*_delta

func get_movement_vector() -> Vector2:
	var x_movement = Input.get_action_strength("right")-Input.get_action_strength("left")
	var y_movement = Input.get_action_strength("down") - Input.get_action_strength("up")
	return Vector2(x_movement,y_movement).normalized()

簡単にいうと, get_movement_vectorという二次元ベクトルを戻り値にする関数を定義し, それを_process()で毎フレーム呼ぶことで, 上下左右のキャラ操作を実現する.
x軸から右は正の値で, 右-左で, 同時に押されても0になるだけ
Godot2Dのy軸は逆で, 0,0は画面の左上にある. つまり, 下方向の方は正の値で, “down” – “up”

最後に, .normalized()を使ってベクトルを正規化する理由は, 斜め移動のノルム(長さ)が sqrt(1^2+1^2) = sqrt(2) ~= 1.414になるため, 移動速度が1.4倍になることを防ぐためである.

さて, このキャラクターをMain Sceneに召喚して, 実行してみよう!

さて, ここから, キャラクターの移動方向に従い, 素材を変えよう!

@onready var sprite:Sprite2D = $Sprite2D
@onready var front_img:Texture2D = preload("res://front.png")
@onready var side_img:Texture2D = preload("res://side.png")
@onready var back_img:Texture2D = preload("res://back.png")

@onready とは, func _ready() の中に入ると同意味で, とはいえ, 実際の_ready()関数より前に実行される. (今回は無関係)
$の印は, 子ノードを意味する. $Player/Sprite2D は, 子ノードのプレイヤーの子ノードである Sprite2Dを指す.
preload(“素材へのパス”)は, ゲームが実行する前(pre)に, コンパイル時素材を用意するということ (ので, load()も存在する, 使い分けるは確実決定.)

func _process(_delta:float) -> void:
	direction = get_movement_vector()
	self.position += direction*speed*_delta
	
	if direction.y < 0:
		sprite.texture = back_img
	elif direction.y > 0:
		sprite.texture = front_img
	elif direction.x < 0:
		sprite.texture = side_img
		sprite.flip_h = false
	elif direction.x > 0:
		sprite.texture = side_img
		sprite.flip_h = true

次に_processに, これを入れる.
簡単にいうと, 純粋な上下運動時, 正面・背面の素材を使う, 左右方向が含まれる場合, 側面の素材を左右反転しながら使う. 今回支給した側面素材は, 左向きなので, 右に移動するときは,

sprite.flip_h = true
をする. (注:この実装法はかなり雑です. 信号(Signal)まだ紹介してないため使用していない.)


今回のコード:

extends Node2D
class_name Player

@export var speed:int = 200

@onready var sprite:Sprite2D = $Sprite2D
@onready var front_img:Texture2D = preload("res://front.png")
@onready var side_img:Texture2D = preload("res://side.png")
@onready var back_img:Texture2D = preload("res://back.png")

func _ready() -> void:
	pass
	
var direction:Vector2 = Vector2.ZERO
func _process(_delta:float) -> void:
	direction = get_movement_vector()
	self.position += direction*speed*_delta
	if direction.y < 0:
		sprite.texture = back_img
	elif direction.y > 0:
		sprite.texture = front_img
	elif direction.x < 0:
		sprite.texture = side_img
		sprite.flip_h = false
	elif direction.x > 0:
		sprite.texture = side_img
		sprite.flip_h = true
		
func get_movement_vector() -> Vector2:
	var x_movement = Input.get_action_strength("right")-Input.get_action_strength("left")
	var y_movement = Input.get_action_strength("down") - Input.get_action_strength("up")
	return Vector2(x_movement,y_movement).normalized()




コメントを残す

CAPTCHA