godot

L010 ゲーム構成


Godot4の覚書です。
簡単な構成で、一通り確認することを目的としています。

構成

だいたいのゲームは以下のような構成になっていると思います。

スタート画面ゲーム画面ゲームオーバー画面設定画面(ない場合も)
。。。途中

ゲームの遷移状態は、大きく分けて以下の4種類に分類できます。

1. ゲームシーン

ゲーム全体を構成する大きな区切りの状態です。タイトル画面、ゲームプレイ画面、ゲームオーバー画面などがあります。

2. プレイヤーの状態

プレイヤーキャラクターの動作や状況を表す状態です。移動、攻撃、ジャンプ、しゃがみ、待機などがあります。

3. オブジェクトの状態

ゲーム内に出現するオブジェクトの動作や状況を表す状態です。表示、非表示、アクティブ、非アクティブ、破壊などがあります。

4. その他の状態

上記以外にも、ゲームによっては以下のような状態が存在します。

  • アイテムの状態 (所持、使用、消費)
  • イベントの状態 (発生、進行、完了)
  • BGMの状態 (再生、停止、音量調整)
  • セーブデータの状態 (ロード、セーブ)

これらの状態は、ゲームエンジンやプログラミング言語によって、さまざまな方法で管理されます。

遷移状態の例

以下は、ゲームの遷移状態の例です。

  • プレイヤーが敵に攻撃をヒットさせると、敵の「ダメージ」状態になり、体力が減少します。
  • プレイヤーがアイテムを獲得すると、アイテムの「所持」状態になり、インベントリに追加されます。
  • プレイヤーがゲームクリアすると、「ゲームクリア」状態になり、エンディング画面が表示されます。

遷移状態を設計する際のポイント

ゲームの遷移状態を設計する際には、以下の点に注意する必要があります。

  • プレイヤーにとってわかりやすい状態であること
  • 必要な状態のみを定義すること
  • 状態間の遷移がスムーズであること
  • エラー状態を考慮すること

まとめ

ゲームの遷移状態は、ゲームを構成する重要な要素です。適切に設計することで、プレイヤーにとってわかりやすく、スムーズなゲームプレイを実現することができます。

ゲーム内の状態を管理するには、さまざまな方法があります。状況に応じて適切な方法を選択することが重要です。

代表的な方法

  • ステートマシン

状態を列挙し、各状態間での遷移を矢印で表した有限ステートマシン (FSM) を用いる方法です。シンプルなゲームや、状態間の遷移が比較的単純な場合に適しています。

  • フラグ

特定の状態を表すフラグ変数を用意し、その値を操作することで状態を管理する方法です。シンプルなゲームや、状態管理が比較的単純な場合に適しています。

  • コンポーネント

各オブジェクトに状態を管理するコンポーネントをアタッチする方法です。オブジェクトごとに異なる状態を管理する必要がある場合や、複雑な状態遷移を表現する必要がある場合に適しています。

  • エンティティ-コンポーネントシステム (ECS)

エンティティとコンポーネントを用いてゲームオブジェクトを表現し、状態を管理する方法です。大規模なゲームや、複雑な状態管理が必要な場合に適しています。

その他

上記以外にも、ゲームエンジンやフレームワークによっては、独自のステート管理機能を提供している場合があります。

状態管理のポイント

  • ゲームの状態を明確に定義する
  • 適切な方法で状態を管理する
  • 状態間の遷移を明確にする
  • エラー状態を考慮する

まとめ

ゲーム内の状態を管理するには、さまざまな方法があります。状況に応じて適切な方法を選択し、ゲームの状態を明確に定義し、適切な方法で管理することが重要です。

設定

画面サイズ

ゲーム画面のサイズを設定しておきます。
幅:800、 高さ:1380、 モード:viewport

スタート画面

スタート画面を作ります。
startが明滅するようにしています。

extends Control


# Called when the node enters the scene tree for the first time.
func _ready():
	pass # Replace with function body.


# Called every frame. 'delta' is the elapsed time since the previous frame.
var counti:int
func _process(delta):
	
	counti +=1
	$start.show()
	if counti > 50:
		$start.hide()
		
		
	if counti > 100:
		counti = 0
	#print("ループ"+str(counti))
	

シーンの切り替え

シーン遷移

スタート画面からゲーム本編へ移動します。または設定画面へ移動する場合もあります。
本編や設定画面を作成したら、シーンを呼び出して移動します。
ほとんどの場合において、なんらかのキーを押すことで移動しますので、キー設定も行う必要があります。startするためのボタンは、キーボードなら何でもよいようにします。

以下のコードを追加します。キーボードのキーが押された場合、mianシーンに変えます。

#シーンの切り替え
func _input(event: InputEvent) -> void:
    # キーボードのキーが押された場合の処理
	if event is InputEventKey:
		get_tree().change_scene_to_file("res://Scene/main.tscn")

終了画面

作りはスタート画面とほぼ同じです。
ゲームオーバーの条件を満たした場合に遷移する設定を、ゲームmainに設定します。
画面の作成と、キーによる操作の設定をします。

インプットマップでキーボードのqキーにQuitを設定します。

func _input(event):
	if event.is_action_released("Quit"):
		get_tree().quit() #ゲーム終了
	elif event.is_action_released("ui_accept"):
		get_tree().change_scene_to_file("res://Scene/start_game.tscn")

"ui_accept"は組み込みアクションになります。スペースキーやエンターキーが該当します。

ゲーム本編

ひとまず、簡単な条件を設定します。
・プレイヤーだけが登場
・5秒停止しているとゲームオーバー

画面

mainのシーンを作成してゲーム画面とします。

画面の背景を黒にします。

プレイヤー

プレイヤーのシーンを作成してmainシーンに配置します。
プロジェクト設定のインプットマップにキー操作を設定します。(動画だとすでにゲームパッドの設定が入っています)

このスクリプトは位置を指定して動作します。(下記に速度でのコードがあります)

func _process(delta):
	
	if Input.is_action_pressed(&"move_right"):
		position.x += 1
	if Input.is_action_pressed(&"move_left"):
		position.x -= 1
	if Input.is_action_pressed(&"move_down"):
		position.y += 1
	if Input.is_action_pressed(&"move_up"):
		position.y -= 1
キーボードの↑→↓←を追加

velocity

チュートリアルにあるサンプルは速度変更して動作します。
正規化することで斜めの移動速度も上下左右と同じにしています。
これを追加して同時に動かしています。

最初の2Dゲーム
このステップバイステップチュートリアルシリーズの中では、初めての2Dゲームを作成します。このシリーズの最後には、以下のようなシンプルですが完成されたあなた自身によるゲームが完成します。 image0 このチュートリアルではエディタの使い方、...
四方向と斜め移動で差があります。
var screen_size # Size of the game window.

@export var speed = 80

func _ready():
	screen_size = get_viewport_rect().size
	
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
		
	position += velocity * delta
	position = position.clamp(Vector2.ZERO, screen_size)

ルール

ここで、mainシーンにゲームルールのスクリプトを記述しておきます。

var lastPosi_X:float
var lastPosi_Y:float
var timeCount:float

func _process(delta):
	#print(delta)
	#位置を調べる
	if $Player.position.x== lastPosi_X && $Player.position.y== lastPosi_Y:
		timeCount += delta

	else:
		timeCount = 0

	lastPosi_X = $Player.position.x
	lastPosi_Y = $Player.position.y

	#カウント5以上で画面遷移
	if timeCount >= 5:
		get_tree().change_scene_to_file("res://Scene/end_game.tscn")

 プレイヤーの位置が変化ない場合、deltaをカウントして5以上になると、ゲームオーバー画面に変わります。

HUD

経過時間を表示するHead-Up Display(HUD)を設定します。

	$HUD/time.text = str(ceil(timeCount * 10) / 10) #ceil関数:切り捨て整数表示

フォーマットで指定する場合

	$HUD/time.text = "%7.1f" % timeCount #7は全文字数(.を含む)1は小数点以下数

Pause画面

ゲーム中に一旦停止する画面です。
ここでは別のシーンを呼び出さず、ノードを表示する方法で行います。
Pause画面で大事なこととして、Pauseした場合に再度操作するにあたって、
Process設定をAlwaysにしておくことです。設定しておかないと、操作を受け付けてくれません。

マップ

マップではなく障害物のような扱いですが、地形を描いてみます。
プレイヤーが接触するとゲームオーバーになります。
マップのシーンを作成して、mainシーンに配置します。
mainのスクリプトで、mapからのシグナルを受信して処理を行います。

	if area.name == "Player":
		# ゲームオーバーになる
		get_tree().change_scene_to_file("res://Scene/end_game.tscn")
		

接触する場合

接触物として当たる場合には、CharacterBody2Dノードでプレイヤーを作成して、マップをStaticBody2Dノードで作成します。

var screen_size # Size of the game window.

@export var speed = 80

func _ready():
	screen_size = get_viewport_rect().size
	
func _physics_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
		
	position += velocity * delta
	position = position.clamp(Vector2.ZERO, screen_size)
	
	move_and_slide()
	

エネミー

エネミー(敵)というよりは、シーンの組付けに関することです。

構成

ゲームではそれぞれの構成が関係しあいます。また、デザインであったり個々でも深く作り込まれます。
いくつかのケースを確認します。

マップ追加
。。。途中

サンプルモデル

githubに置いています。
コリジョンの形状表示をonにする必要があります。
メニュー>デバッグ>コリジョン形状を表示 にしてください。

GitHub - WOCae/L010
Contribute to WOCae/L010 development by creating an account on GitHub.

L010-main.zip

コメント