
AI Assisted
この記事は筆者の実装経験をもとに実装コードをベースで執筆し、AIによる校閲・推敲を経て公開しています。
2日目でステージ(舞台)が完成しました。 今日はその舞台で踊る**「テトリミノ(ブロック)」**を作成し、プレイヤーが操作できるようにします。
今日のゴールはこれです!
コードを書く前に、キーボードの設定を行います。
Web開発でいう addEventListener のキーバインド設定に近い作業です。
Godotでは「プロジェクト設定」→「インプットマップ」で、キー操作に名前をつけることができます。

これで、コードの中で「左キーが押されたら」ではなく「move_left が起きたら」と書けるようになります。
テトリスのブロックは、ゲーム中に何度も生成されたり消えたりします。 こういう部品こそ、別シーン(コンポーネント) として独立させるのがGodot流です。
Node2D にする(名前は Piece)。TileMapLayer を追加する。
scenes/piece.tscn として保存。
ここからが本番です。 テトリスには「I, O, T, S, Z, J, L」という7種類の形があります。これをデータとして定義し、操作を受け付けるスクリプトをAIに書いてもらいます。
AIへの指示:
Godot 4 (GDScript) でテトリスのブロックを制御する piece.gd を書いて。 Node2Dを継承。
- テトリミノの形状: 7種類の形をVector2の配列の辞書として定義して(例: T型なら(0,0), (-1,0), (1,0), (0,1) みたいに)。
- 初期化:
_readyでランダムな形を選んで、子ノードのTileMapLayerにその形を描画して。- 移動:
inputsアクション (move_left, move_right, move_down) を検知して、自分自身(position)を32px単位で移動させて。- 自動落下: Timerノードを使わず、
_processで1秒ごとに1マス下に落ちるロジックを入れて。
生成されたコードを Piece シーンにアタッチします。
// piece.gd
extends Node2D
# タイルサイズ(ピクセル)
const TILE_SIZE := 32
# 自動落下の間隔(秒)
const GRAVITY_INTERVAL := 1.0
# テトリミノ7種類の形状定義
const TETROMINO_SHAPES := {
"I": [Vector2i(0, 0), Vector2i(1, 0), Vector2i(2, 0), Vector2i(3, 0)],
"O": [Vector2i(0, 0), Vector2i(1, 0), Vector2i(0, 1), Vector2i(1, 1)],
"T": [Vector2i(0, 0), Vector2i(1, 0), Vector2i(2, 0), Vector2i(1, 1)],
"S": [Vector2i(1, 0), Vector2i(2, 0), Vector2i(0, 1), Vector2i(1, 1)],
"Z": [Vector2i(0, 0), Vector2i(1, 0), Vector2i(1, 1), Vector2i(2, 1)],
"J": [Vector2i(0, 0), Vector2i(0, 1), Vector2i(1, 1), Vector2i(2, 1)],
"L": [Vector2i(2, 0), Vector2i(0, 1), Vector2i(1, 1), Vector2i(2, 1)],
}
@onready var tile_map_layer: TileMapLayer = $TileMapLayer
var current_shape_key: String
var gravity_timer: float = 0.0
# ★変更点1:現在のブロックの形状データを保持する変数(回転で中身が変わるため)
var current_cells: Array = []
func _ready() -> void:
var shape_keys := TETROMINO_SHAPES.keys()
current_shape_key = shape_keys[randi() % shape_keys.size()]
# ★変更点2:定数からコピーして、変形可能なデータとして持つ
current_cells = TETROMINO_SHAPES[current_shape_key].duplicate()
_draw_shape()
func _draw_shape() -> void:
tile_map_layer.clear()
var atlas_coords := Vector2i(0, 0)
# ★変更点3:current_cells(現在の状態)を見て描画
for cell_pos: Vector2i in current_cells:
tile_map_layer.set_cell(cell_pos, 0, atlas_coords)
func _process(delta: float) -> void:
_handle_input()
_handle_gravity(delta)
func _handle_input() -> void:
if Input.is_action_just_pressed("move_left"):
_move(-1, 0)
if Input.is_action_just_pressed("move_right"):
_move(1, 0)
if Input.is_action_just_pressed("move_down"):
_move(0, 1)
# ★変更点4:回転入力の検知
if Input.is_action_just_pressed("rotate"):
_rotate_shape()
func _handle_gravity(delta: float) -> void:
gravity_timer += delta
if gravity_timer >= GRAVITY_INTERVAL:
gravity_timer = 0.0
_move(0, 1)
func _move(dx: int, dy: int) -> void:
position.x += dx * TILE_SIZE
position.y += dy * TILE_SIZE
作った「役者」を舞台に上げます。
Main シーン(親)のスクリプトで、Piece シーン(子)を生成(インスタンス化)する処理を追加します。
AIへの指示(Main側):
main.gd を作成して Mainノードにアタッチして。
preloadでpiece.tscnを読み込んで。_readyで Piece のインスタンスを作成し、画面の上部中央(グリッド座標の (4, 0) あたり)に配置してadd_childして。
// main.gd
# main.gd
extends Node2D
# 作成したPieceシーンを読み込んでおく
var piece_scene = preload("res://scenes/piece.tscn")
func _ready() -> void:
spawn_piece()
func spawn_piece() -> void:
# Pieceのインスタンス(実体)を作成
var piece = piece_scene.instantiate()
# 出現位置を決める
# x: 4列目 (4 * 32px), y: 0列目
piece.position = Vector2(4 * 32, 0)
# Mainシーンの子として追加(これで画面に出る)
add_child(piece)
ここで一度実行してみたところ、ブロックは落ちてきましたが、「回転」が動きませんでした。 原因はシンプルで、AIへの指示に「回転処理」を入れ忘れていたからです(プログラミングあるあるですね)。
そこで、追加で回転ロジックを実装しました。
# 回転ロジック(90度回転)
func _rotate_shape() -> void:
var new_cells: Array = []
for cell in current_cells:
# 数学の公式: (x, y) -> (-y, x) で90度回転させる
var new_pos = Vector2i(-cell.y, cell.x)
new_cells.append(new_pos)
current_cells = new_cells
_draw_shape()
この修正を入れたことで、スペースキーでクルクル回るようになりました!
まだ壁にめり込んだり、床を突き抜けたりしますが、「自分の作ったゲームが動いている」 という感動はひとしおです。
Hello World用のファイルは削除し、テトリス用にフォルダ構成を整理しました。
godot-tetris-practice/
├── .godot/ # 内部キャッシュ(Git除外)
├── assets/
│ └── block.png # 32x32 白ブロック画像
├── scenes/
│ ├── main.tscn # メインシーン
│ └── piece.tscn # 追加:ブロックの部品
├── script/
│ ├── main.gd # 追加:ゲーム進行管理
│ ├── board.gd # 盤面データ
│ └── piece.gd # 追加:ブロックの操作と回転
├── project.godot # プロジェクト設定
└── icon.svg # アプリアイコン
次回は、いよいよゲーム開発の最難関、突き抜けてしまう壁や床への**「衝突判定(Collision)」**を実装し、ゲームとしてのルールを作っていきます!
スポーツ×ITの会社でバックエンドエンジニア兼マネージャーとして勤務。インテル関連の情報を中心に、AI・IT技術やサイト運用ノウハウも発信しています。
最終更新: 2026年1月13日
© 2025 nero15.dev. All rights reserved.