ゲームの作り方

最近、多くの人がゲーム開発について質問しているのに、そのトピックに関する記事があまりないことに気付きました。ゲーム開発の最初から最後までの一般的なプロセスに光を当てることにしました。これは主に概要であり、次の点に注意してください。 A. すべてのプロジェクトに逐語的に適用されるわけではありません。 B. 完成した結果への完全な段階的なガイドではありません。ゲームを行うには、自分でかなりのことを理解する必要があります。


ステップ 1:ゲーム ライブラリを選択する
すべての核心的なグラフィックス/サウンド プログラミング用に独自のライブラリを作成したくない場合は、おそらくゲーム ライブラリを取得することをお勧めします。多くのゲーム ライブラリがありますが、それらはすべて同じ基本機能を提供します。

ライブラリに追加してほしい機能:
  • 画像を読み込んでレンダリングする方法
  • 音声を読み込んで再生する方法
  • 基本的な画像操作 (回転など)
  • 基本的な描画機能 (円、線、長方形、点など)
  • テキストをレンダリングする能力
  • 時間を追跡して待つ能力
  • スレッドを作成および制御する能力 (あると便利ですが、必須ではありません)

一部のゲーム ライブラリには以下が含まれます:
  • Simple Fast Multi-Media Library (SFML):http://www.sfml-dev.org/
  • 単純な DirectMedia Layer (SDL):http://www.libsdl.org/
  • アレグロ:http://www.allegro.cc/
  • OpenGL (レンダリングのみ。ただし、AllegroGL などのラッパー ライブラリがあります):http://www.opengl.org/
  • DirectX (Windows のみ):http://msdn.microsoft.com/en-us/directx/
  • Irrlicht (3D ライブラリ):http://irrlicht.sourceforge.net/




ステップ 2:コンセプトを定義する
すべてのゲームはここから始まります。誰かの頭の中のアイデアにすぎません。
まず、ゲームのアイデアを思いつきます。簡単なアイデアが浮かんだら、それを拡張します。例えば、ボードゲームの場合、目的は何ですか/どうやって勝つのですか?ルールはどうなる?ゲームにキャラクターやストーリーがある場合は、それらを作成します。ゲームが完成したときにどうなるかについて、かなり明確に定義されたコンセプトを持っていることを確認してください。ゲームが複雑になればなるほど、コーディング中にゲーム自体について心配する必要がないように、最初に計画を立てる必要があります。ゲームは作成するにつれて進化することに注意してください。




ステップ 3:エンジンを計画する
ボード ゲームまたは基本的なアーケード ゲームを作成している場合は、これを完全にスキップして、ゲームをプログラムするだけでかまいません。ただし、より複雑なゲームの場合は、事前に作成されたエンジンを使用するか、独自の「エンジン」を作成することを検討することをお勧めします。あなたが尋ねるゲームエンジンとは何ですか?構造と全体的な機能は大きく異なりますが、ゲーム エンジンは、物理、リソース処理、ゲーム エンティティ管理などの高レベルの機能を提供する強力なライブラリと考えることができます。既存のエンジンを使用するか、独自のエンジンを作成するかは、いわばあなた次第であり、実際にどれだけのプログラミングを行いたいかによって異なります。既製のエンジンを使用すると、プログラマーとしてのゲームプレイ/イベントのスクリプト作成が他の何よりも簡単になります。

なぜ私は選択ではなく計画と言ったのですか?おそらく、次の Elder Scrolls を作成していない可能性が高いため、独自の「エンジン」を作成できます。次の Unreal Engine を作成するわけではないことに注意してください。また、(エンジンの要点と同様に) 再利用可能にするために作成したコードのほとんどは、最終的にゲーム ロジックと絡み合ってしまうため、簡単に再利用できます。これを念頭に置いて、「エンジン」の一部がゲーム固有のコードに依存していても心配しないでください。これは実際に起こることです。完全に再利用可能で非常に堅牢なフレームワークを作成することに集中する代わりに、コードが読みやすく、整理され、機能していることを確認することに集中してください。最初にゲームの作成に集中してから、移植可能なモジュールの作成を試みてください。有用で再利用可能なものを絶対に作成する必要がある場合は、リソース マネージャーやその他のさまざまなユーティリティ クラスを出発点として使用することをお勧めします。




ステップ 4:エンジンを作成する (自作の場合)

これがあなたが選択したルートであれば、実際にエンジンの作成を開始する時が来ました。これは必ずしもゲーム自体を意味するわけではなく、コア レンダリング、物理演算、およびファイル処理を意味します。基本的に、ゲームの構築に使用される関数とクラスです。単純なゲームはフレームワークをあまり必要とせず、ゲーム ライブラリを直接使用してプログラムするだけで済みます。大規模なゲームで最も重要でありながら、最も無視されているコンポーネントの 1 つは、リソース マネージャーです。リソース マネージャーは (おそらく) リソース (グラフィックスとサウンドを考えてください) をロードし、リソースが一度だけロードされるようにし、リソースが不要になったときにアンロードするクラスです。 RAM は無限ではないため、ゲームが宇宙のすべての草に対して同じ画像の個別のコピーを読み込んでいると、うまくいきません。以下の Xander314 による優れたリソース マネージャーをご覧ください。 .


Xander314
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
/*
 ResourceManagerB.hpp - Generic template resource manager				
									
 (C) Alexander Thorne (SFML Coder) 2011	
 <a href="http://sfmlcoder.wordpress.com/">http://sfmlcoder.wordpress.com/</a>	
		
 Manages loading and unloading of a resource type specified by a
 template argument.

****************************************************************/

#include <map>
#include <string>
#include <exception>

typedef const std::string URI;

// exceptions
namespace Exceptions {

	// thrown if user requests a resource URI not present in the manager's list
	class URINotFound : public std::runtime_error 
	{ 
	public: 
		URINotFound(const std::string& Message = "The specified URI was not found in the resource index.")
			: runtime_error(Message) { } 
	};

	// thrown if a resource allocation fails
	class BadResourceAllocation : public std::runtime_error {
	public: 
		BadResourceAllocation(const std::string& Message = "Failed to allocate memory for resource.")
			: runtime_error(Message) {}
	};
}

template <class Resource> class ResourceManagerB {
	typedef std::pair<URI, Resource*> ResourcePair;
	typedef std::map<URI, Resource*> ResourceList;

	// the list of the manager's resources
	ResourceList Resources;
public:
	~ResourceManagerB() { UnloadAll(); }

	// Load a resource with the specified URI
	// the URI could represent, e.g, a filename
	URI& Load(URI& Uri);
	// unload a resource with the specified URI
	void Unload(URI& Uri);
	// unload all resources
	void UnloadAll();

	// get a pointer to a resource
	Resource* GetPtr(URI& Uri);
	// get a reference to a resource
	Resource& Get(URI& Uri);
};

template <class Resource>
URI& ResourceManagerB<Resource>::Load(URI& Uri)
{
	// check if resource URI is already in list
	// and if it is, we do no more
	if (Resources.find(Uri) == Resources.end())
	{
		// try to allocate the resource
		// NB: if the Resource template argument does not have a
		// constructor accepting a const std::std::string, then this
		// line will cause a compiler error
		Resource* temp = new (std::nothrow) Resource(Uri);
		// check if the resource failed to be allocated
		// std::nothrow means that if allocation failed
		// temp will be 0
		if (!temp)
			throw Exceptions::BadResourceAllocation();
		// add the resource and it's URI to the manager's list
		Resources.insert(ResourcePair(Uri, temp));
	}
	return Uri;
}

template <class Resource>
void ResourceManagerB<Resource>::Unload(URI& Uri)
{
	// try to find the specified URI in the list
	ResourceList::const_iterator itr = Resources.find(Uri);
	// if it is found...
	if (itr != Resources.end())
	{
		// ... deallocate it
		delete itr->second;
		// then remove it from the list
		Resources.erase(Uri);
	}
}

template <class Resource>
void ResourceManagerB<Resource>::UnloadAll()
{
	// iterate through every element of the resource list
	ResourceList::iterator itr;
	for (itr = Resources.begin(); itr != Resources.end(); itr++)
		// delete each resource
		delete itr->second;
	// finally, clear the list
	Resources.clear();
}

template <class Resource>
Resource* ResourceManagerB<Resource>::GetPtr(URI& Uri)
{
	// find the specified URI in the list
	ResourceList::const_iterator itr;
	// if it is there...
	if ((itr = Resources.find(Uri)) != Resources.end())
		// ... return a pointer to the corresponding resource
		return itr->second;
	// ... else return 0
	return 0;
}

template <class Resource>
Resource& ResourceManagerB<Resource>::Get(URI& Uri)
{
	// get a pointer to the resource
	Resource* temp = GetPtr(Uri);
	// if the resource was found...
	if (temp)
		// ... dereference the pointer to return a reference
		// to the resource
		return *temp;
	else
		// ... else throw an exception to notify the caller that
		// the resource was not found
		throw Exceptions::URINotFound();
}



エンジン/フレームワークのもう 1 つの重要な側面は、インターフェイスです。ゲーム自体のロジックを書いているときは、何百もの更新関数を検索して実際に必要なものを見つけようとするので、メインのゲーム ループを書くのに 4 時間もかからないはずです。シンプルで簡潔にしてください。 1 つまたは 2 つの関数呼び出しですべてのゲーム ロジックを更新し、さらに 1 つまたは 2 つの関数呼び出しでシーンをレンダリングできる場合は、正しい方向に進んでいます。継承や純粋な仮想基底クラスなどのオブジェクト指向の原則を利用する (インターフェースを考えてください) ) は、健全な構造を持つフレームワークを作成する優れた方法です。

たとえば、すべてのゲーム オブジェクトの基本クラスは次のように定義できます。
1
2
3
4
5
6
7
8
9
10
11
class GameObject
{
public:
    virtual ~GameObject()=0;

    virtual Vector2f getPosition();

    virtual bool interact(Object* o);

    virtual void draw(); //highly library dependent
};


すべてのサブクラスがこのインターフェイスに保持されるようになったため、オブジェクトが実際に何であるかに関係なく、定義したすべてのオブジェクトを簡単に格納および管理できる 1 つの保持エンティティを持つことができます。より多くのことを学び、プログラミングするにつれて、選択した言語のさまざまな機能を有利に使用する方法が見つかります。




ステップ 5:メディア (オーディオとグラフィック)
ここまでで、少なくともゲームを実際にどのように見せたいかについて考えたことがあると思います。また、使用するメディアのセットが既にあるかもしれません。しかし、あなたが私のような人なら、思いついた「美しいデザイン」に興奮して夢中になり、テストの時点までに単一の画像を持っていない.画面上で踊るクリエーション。今こそ、必要なリソースの取得を開始する絶好の機会です。あなたが芸術的に傾倒しているなら、それは素晴らしいことです。そうでなくても心配はいりません。希望は失われていません。 Google で検索するだけで、膨大な量の無料のグラフィックスと効果音を入手できます。 Audacity と GIMP は、取得または作成したものを編集するための不可欠なツールです。




ステップ 6:ゲームを作成する
使用するエンジンまたは独自のフレームワークを選択したら、実際にゲーム ロジック自体を作成することができます。理想的には、この記事全体を少なくとも 1 回は読んでから、数え切れないほどの時間を費やして、その役割を超えて事実上使用できなくなりますが、独立して機能するには不十分な「エンジン」を作成することはできません。フレームワークは、オブジェクトの相互作用を構造化するベースを提供し (ただし、必ずしもそれを定義する必要はありません)、すべてのレンダリングと物理などのその他の低レベルの詳細を処理する必要があります。ゲーム ロジック自体は、オブジェクトの相互作用 (たとえば、GameObject のサブクラスを定義することによって)、ゲーム ルール (勝敗を構成する要素など)、ゲームの初期状態 (最初に読み込まれるマップ、開始するアイテム、など)、メイン ゲーム ループが含まれます .

メインのゲームループは一体何?簡単に言えば、これはループ、つまりメイン ループです。ゲームをプレイしている間に繰り返されることを考えてみてください。それがこの不思議なループに含まれています。たとえば、ゲームの反復ごとに、すべてのオブジェクトを更新してから、それらすべてを画面に描画する必要があります。更新と描画以外に、メイン ループもおそらくタイミングを担当します。アップデートが多すぎるゲームは、ユーザーにとって信じられないほど高速に見え、おそらく難しすぎるでしょう。光の速さでピンポンを考えてみてください。理想的には、このループは以前に作成したフレームワークを使用し、それ自体は非常に単純です。以下の例を参照してください。

ゲームループ:
1
2
3
4
5
6
7
8
9
10
while (!Game.playerLost())
{
    world.update(); //assume this world object owns all of the GameObjects and updates them as well

    screen.clear();
    world.draw(screen);
    screen.display();

    ensureProperFPS(); //just a placeholder, put the actual timing logic right here in the loop
}





ステップ 7:そこから何かを取り出す
ゲーム ロジックとは別にフレームワークを作成することについて話している主な理由は、再利用可能なコードの書き方を学ぶためです。それから、実際に再利用可能にすることを心配するのではなく、ゲームを作ることに専念するように言いました。初心者がプロジェクトをあきらめる主な理由の 1 つは、ゲームの「エンジンを作成する」ために膨大な時間と労力を費やしていることですが、優れたエンジンに何が含まれているかはまだよくわかっていません。または実際に機能する構造/インターフェース。そのすべての時間を無駄にした後、彼らは何も示すことができず、結果として落胆して辞めます。最初にゲームを作成し、次に再利用可能なコードを作成することに集中することで、理想的には目に見えるものに仕上がります。あなたの努力に対する具体的な報酬であり、さらに努力を続ける理由です。

これで満足のいくプレイ可能なゲームができたので、ゲーム コードから移植可能なモジュールを試してみることができます。キーボード入力を処理するための優れたリソース マネージャーまたは優れたクラスを作成しましたか?ソースファイルをコピーして、他のプロジェクトで「すぐに」使用できるように、それらを完全に移植できるようにしてください。次のプロジェクトで完全に新しく始めたい場合でも、それは問題ありません。プロジェクトから何かを取得するために、文字通りプロジェクトからコードを取得する必要はありません。その過程で何かを学んだ限り、それはすべて価値がありました.



ステップ 8:パッケージ化して配布する
すべての作業が終わったら、おそらく人々にあなたのゲームを実際にプレイしてもらいたいと思うでしょう!必要なすべてのファイルを zip ファイル、圧縮アーカイブ、または実行可能インストーラーにパッケージ化して、全員に送信します。




ヒント:
私はゲームを作ることについて多くのことを学びましたが、難しいこともあります。あなたがしなければならないことがいくつかあります:
  • まず、整理整頓をしましょう!あなたは、あらゆるものに対して優れた組織システムを持っているべきです。コード、メディア、ドキュメントなど。フォルダには理由があります。使用してください!

  • また、コードを簡潔で読みやすいものに保つようにしてください。関数に意味のある名前を付け、すべてをできるだけシンプルに保ちます

  • ドキュメント!この記事では実際にそれについて話したことはありませんが、すべてを文書化してください!すべてのデータ ファイルの形式を文書化し、すべての関数とクラスの機能を文書化します。実行するまで、これによってどれだけの時間が節約され、どれだけ多くの頭痛が回避されるかわかりません

  • 小さく始めましょう。最初に次のポケモン ゲームを作成しようとしないでください。小規模で扱いやすいプロジェクトから始めて、スキルに応じて目標を拡大してください。頭を悩ませてプロジェクトに取り組もうとしても、落胆するだけです

  • 賞品に注目!私のプロジェクトが多すぎることの最大の欠点の 1 つは、全体像を無視して、細部にこだわってしまうことでした。はい、私の雲、雨と雷、足跡と霧の効果はすべて美しいものでしたが、ゲームを完成させることはできませんでした。かなり後で作成できます。最初にゲームを作成してください!

  • 楽しんでください!ゲームの本質は楽しむことであり、それを作ることもまた楽しいものです。作業を楽しんでいると、タスクを達成するのが常に簡単になります。もちろんイライラすることもありますが、怒りすぎたら休憩しましょう!散歩をしながら別のことを考えることは、多くの場合、問題に対する最善の解決策です。戻ってきたときには新たなスタートを切ることができます。これにより、以前の一連の思考では思いつかなかった解決策を見つけることができます。

chrisname からのいくつかの開始ヒント :