1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Godot 4.6】初心者が1時間で作る!スマホ向け2Dフルーツキャッチゲーム開発記

1
Posted at

Godot Engineで2Dゲームを制作するための備忘録です。

この記事はGeminiを活用して作成しました。

0. はじめに

Godot Engine 4.6を使用し、落ちてくるフルーツを駕籠(かご)でキャッチするシンプルな2Dアクションゲームを作成しました。
開発時間約1時間で、スプライトシートによるマルチ表示やスマホのタッチ操作にも対応しています。

1. ゲームの基本仕様

  • ジャンル: 落ちもの2Dアクションゲーム
  • ターゲット端末: Android(縦画面 / 解像度 1080 × 1920)
  • 操作方法: 画面タッチ・ドラッグによる左右移動(PCキーボード操作にも対応)
  • ルール:
    • 上から落ちてくるフルーツを駕籠でキャッチすると +10点
    • 障害物(台所用品)をキャッチすると -10点
    • フルーツはスプライトシート画像からランダムで1種が描画される

2. プロジェクト設定

  • 画面サイズ: Viewport Width 1080 / Viewport Height 1920
  • Stretch: Mode canvas_items / Aspect keep
  • Orientation: Portrait(縦向き)

3. 各シーンのノード構造と設定 

① かご(プレイヤー)シーン:basket.tscn

  • ルートノード: Area2D (ルート名は Basket に変更)
    • 子ノード1: Sprite2D(駕籠のイラスト画像を割り当て)
    • 子ノード2: CollisionShape2D(Shape: RectangleShape2D

② フルーツ(ターゲット)シーン:fruit.tscn

  • ルートノード: Area2D (ルート名は Fruit に変更する)
    • グループ設定: fruit を追加(※ヒット判定に使用)
    • 子ノード1: Sprite2D
      • Texture: 8種のフルーツが入ったスプライトシート
      • Animation -> Hframes: 8 / Vframes: 1(画像に合わせて設定)
    • 子ノード2: CollisionShape2D(Shape: CircleShape2D

③ 台所用品(障害物)シーン:kitchen_item.tscn

  • ルートノード: Area2D (ルート名は KitchenItem に変更する)
    • 子ノード1: Sprite2D(台所用品のイラスト画像)
    • 子ノード2: CollisionShape2D(Shape: CircleShape2D

④ メインシーン:main.tscn

  • ルートノード: Node2D (ルート名は Main に変更する)
    • 子ノード1: 駕籠シーンのインスタンス (Basket) (位置: X 540, Y 1700 付近)
    • 子ノード2: Timer (SpawnTimer) (Wait Time: 0.8 / Autostart: On
    • 子ノード3: CanvasLayer
      • 子ノード: Label (ScoreLabel) (Text: Score: 0

4. スクリプト一覧

かごシーンのBasketに添付

# basket.gd
extends Area2D

# --- 変数定義 ---
# キーボード操作時の移動スピード(float型)
@export var speed: float = 800.0

# タッチ操作時の追従スムーズさ(float型)
@export var follow_speed: float = 15.0

# 指で画面をタッチ中かどうかを管理するフラグ(bool型)
var is_touching: bool = false

# タッチされている目標のX座標(float型)
var target_x: float = 0.0


# --- 関数定義 ---
# ゲーム開始時の初期化処理
func _ready() -> void:
	target_x = position.x


# 画面へのタッチやドラッグなどの入力イベントを検知する関数
func _unhandled_input(event: InputEvent) -> void:
	if event is InputEventScreenTouch:
		if event.pressed:
			is_touching = true
			target_x = event.position.x
		else:
			is_touching = false
			
	elif event is InputEventScreenDrag:
		if is_touching:
			target_x = event.position.x


# 毎フレーム実行される移動計算処理
func _process(delta: float) -> void:
	if is_touching:
		# 【タッチ操作時】指のX位置に向かってスムーズに移動
		position.x = lerp(position.x, target_x, follow_speed * delta)
	else:
		# 【キーボード操作時】従来の矢印キー(左右)入力による移動
		var direction: float = Input.get_axis("ui_left", "ui_right")
		position.x += direction * speed * delta
	
	# 移動範囲を画面内に制限(左端80px〜右端1000px)
	position.x = clamp(position.x, 80.0, 1000.0)

フルーツシーンのFruitに添付

# fruit.gd
extends Area2D

# --- 変数定義 ---
# フルーツの落下スピード(float型)
@export var speed: float = 300.0

# 表示するフルーツのイラスト番号(int型)
# -1 のときはランダムに決定し、0〜7 を直接指定するとそのフルーツを表示します
@export var fruit_number: int = -1


# --- 関数定義 ---
# 初期化関数(スプライトシートから表示するコマを選択)
func _ready() -> void:
    # スクリプトから自分自身(Fruitノード)を "fruit" グループに追加する
	add_to_group("fruit")

    # フルーツ番号が指定されていない(-1)場合、0〜7の中からランダムに番号を選
	if fruit_number == -1:
		fruit_number = randi_range(0, 7)
	
	$Sprite2D.frame = fruit_number


# 毎フレーム実行される落下処理
func _process(delta: float) -> void:
	position.y += speed * delta
	
	# 画面外に落ちたらノードを削除
	if position.y > 2000.0:
		queue_free()

台所用品シーンのKitchenItemに添付

# kitchen_item.gd
extends Area2D

# --- 変数定義 ---
# 台所用品の落下スピード(float型)
@export var speed: float = 350.0

# 表示する台所用品のイラスト番号(int型:整数型)
# -1 のときはランダムに決定し、0〜7 を直接指定するとその台所用品を表示します
@export var kitchen_item_number: int = -1

# --- 関数定義 ---
# 台所用品が画面に生み出された瞬間に1度だけ実行される初期化関数
func _ready() -> void:
	# 台所用品の番号が指定されていない(-1)場合、0〜7の中からランダムに番号を選ぶ
	if kitchen_item_number == -1:
		kitchen_item_number = randi_range(0, 7)
	
	# 子ノードのSprite2Dの表示フレーム(コマ)を、決定した台所用品の番号に変更する
	$Sprite2D.frame = kitchen_item_number


# 毎フレーム実行される落下と削除の判定関数
func _process(delta: float) -> void:
	# 下方向(Y軸のプラス方向)へ移動させる
	position.y += speed * delta
	
	# 画面の一番下(2000px)を通り過ぎたらノードを削除して消去する
	if position.y > 2000.0:
		queue_free()


メインシーンのMainに添付

# main.gd
extends Node2D

# --- 変数定義 ---
# 画面に出現させるフルーツのシーン(PackedScene型)
@export var fruit_scene: PackedScene

# 画面に出現させる台所用品のシーン(PackedScene型)
@export var kitchen_scene: PackedScene

# 現在獲得しているスコア(int型)
var score: int = 0

# スコア表示ラベル(Label型)
@onready var score_label: Label = $CanvasLayer/ScoreLabel


# --- 関数定義 ---
# 初期化とシグナル接続
func _ready() -> void:
	$Basket.area_entered.connect(_on_basket_area_entered)$SpawnTimer.timeout.connect(_on_spawn_timer_timeout)


# タイマーによるアイテムのランダムスポーン処理
func _on_spawn_timer_timeout() -> void:
	var item: Area2D
	
	# 70%の確率でフルーツ、30%の確率で台所用品を生成
	if randf() < 0.7:
		item = fruit_scene.instantiate()
	else:
		item = kitchen_scene.instantiate()
	
	# 画面上のランダムな横位置に配置
	item.position = Vector2(randf_range(100, 980), -100)
	add_child(item)


# かごとの当たり判定およびスコア計算
func _on_basket_area_entered(area: Area2D) -> void:
	if area.is_in_group("fruit"):
		score += 10
	else:
		score -= 10
	
	score_label.text = "Score: " + str(score)
	area.queue_free()

5. おわりに

Godot 4.6の Area2D や InputEvent 機能を活用することで、初心者でも短時間でタッチ操作対応の2Dゲームを作成できました。
今後は制限時間機能や難易度調整などを追加して、よりゲーム性を高めていきたいと思います!

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?