【前編】iPadでプログラミングにチャレンジ(ゲーム編)

iPadでプログラミングにチャレンジしてゲームを作ってみましょう。プログラミングにはパソコンは不要です。iPadでもiPad AirでもiPad ProでもiPad miniでもできてしまいます。

Pythonistaをダウンロード

今回も利用するツールはPythonistaです。iPadでプログラミングをするには必須とも言えるツールです。
今回、Pythonistaに付属する多数のサンプルのうち、ゲームチュートリアルをベースにして説明します。PythonistaのExampleに「Game Tutorial」フォルダがあり、その中に収録されているものを分かりやすく説明します。

今回の出来上がりイメージ

二回に分けてゲームの作り方を説明していきます。今回は下記のイメージのように、キャラクターを画面に表示して、iPadを傾けて左右に動かせるところまでやってみましょう。
このゲームを作るにあたってキャラクターなどの画像を用意する必要はありません。Pythonistaが最初から用意してくれている素材を利用していきますのでご安心を。
説明ではPythonの文法やプログラミングの基本的なところは説明しません。分かりやすい表現を使うようにしていますが、Pythonを少し学習したことがある、というくらいの人を対象に説明しています。

ソースファイルを作る

Pythonistaを起動したら下記の画像を参考にファイルを作ります。
まず画面左下の+をタップしてEmpty Scriptを選択します。あとはファイル名と保存先を決めて「Create」を押せば作成できます。

+をタップ
Empty Scriptをタップ
ファイル名と保存先を決めてCreate

プログラミングの心得

今回作るのは最終的にはシューティングゲームになります。いきなり仕上げていくのではなく、動くものを少しづつ積み重ねて作り上げていくイメージになっています。ゲーム以外のプログラミングでも同じで、いきなり完璧なものを作らず、小さなところから少しずつ作っていきます。今回のプログラミングを通じて、ゲームの作り方だけでなく、プログラミングの進め方も実感してもらえればと思います。

概要の説明

最終的にはプレーヤー(今回はエイリアン)をiPadを左右に傾けてコントロールし、上から降ってくる隕石を避けながらコインを集めていくゲームになります。隕石はレーザーで撃ち落とせるようにもします。

今回、「Scene」と「Node」と呼ばれる部品を使って作成していきます。これらはソースコードの中にも実際に出てきます。

SceneはiPadの画面そのもののイメージで、画像を表示したり動かしたりする「場所」になります。

Nodeは表示する画像や文字などを表すものです。表示する位置や大きさを指定したり、回転させたりできます。

詳細はソースコードと一緒に説明するので、少しずつ慣れていってくださいね。

プログラミングの心得

今回作るのは最終的にはシューティングゲームになります。いきなり仕上げていくのではなく、動くものを少しづつ積み重ねて作り上げていくイメージになっています。ゲーム以外のプログラミングでも同じで、いきなり完璧なものを作らず、小さなところから少しずつ作っていきます。今回のプログラミングを通じて、ゲームの作り方だけでなく、プログラミングの進め方も実感してもらえればと思います。

ステージを作ってプレーヤーを配置する

最初のステップは下記のようになります。画面右上の▷を押して実行すると、ステージにエイリアンがポツンと立っているのが表示されると思います。

# coding: utf-8
from scene import *

class Game (Scene):
	def setup(self):
		self.background_color = '#004f82'
		ground = Node(parent=self)
		x = 0
		while x <= self.size.w + 64:
			tile = SpriteNode('plf:Ground_PlanetHalf_mid', position=(x, 0))
			ground.add_child(tile)
			x += 64
		self.player = SpriteNode('plf:AlienGreen_front')
		self.player.anchor_point = (0.5, 0)
		self.player.position = (self.size.w/2, 32)
		self.add_child(self.player)

if __name__ == '__main__':
	run(Game(), PORTRAIT, show_fps=True)

ポイントの説明

それでは重要なところを中心に説明します。

class Game(Scene):

冒頭で説明した「Scene」が記載されていますね。これはSceneを継承してGameというクラスを作成しています。
Gameクラスの中で基本的な表示やプレーヤーのコントロールを記載していきます。

def setup(self):

このsetupaはゲームが起動した直後、画面が表示される前に一度だけ呼び出されるメソッドです。Sceneクラスを継承することで呼び出されるようになっています。

ground = Node(parent=self)

「Node」が出てきましたね。画面に表示する部品を定義しています。"parent=self"となっているのは、parent(つまり親)にself(つまり自分)を設定するという意味です。
親は部品を置く場所を指しているため、self(自分)とすることで、Gameという画面の中にNodeを表示するという意味になります。

 while x <= self.size.w + 64:
	tile = SpriteNode('plf:Ground_PlanetHalf_mid', position=(x, 0))
	ground.add_child(tile)
	x += 64 

「SpriteNode」というのが出てきました。これはNodeクラスのサブクラスとなっています。つまり、画面を表示する部品の一つですね。主に画像を扱うNodeとなります。
SpriteNodeの横に「'plf:Ground_PlanetHalf_mid'」と記載されています。これがPythonistaが用意する素材の名前です。この画像は地面を表したものになっていて、「ground.add_child(tile)」となっている通り、groundに追加しています。
また、whileで画面の左から右端までループで地面画像を並べるようにしています。ここまで動かせばiPadの画面に地面が表示された状態になっています。

self.player = SpriteNode('plf:AlienGreen_front')
self.player.anchor_point = (0.5, 0)
self.player.position = (self.size.w/2, 32)
self.add_child(self.player)

次にプレーヤーを配置しています。プレーヤーも「SpriteNode」を使っていますね。さらに「'plf:AlienGreen_front'」というPythonistaが用意している素材を指定しています。最後に「self.add_child()」を呼び出してプレーヤーをGame画面に追加しています。

「self.player.anchor_point = (0.5, 0)」というのは少し分かりづらいですが、今は気にしなくて大丈夫です。表示する画像の座標の原点をどこにするかといった設定になりますが、慣れて余裕が出てくれば徐々に分かってくると思います。

ここまでで実行すると、画面のステージの上にエイリアンがポツンと立っているのが表示されます。

動きをつけて見よう

今のままでは静止画のようにしかなっておらず、何も動きません。
このステップではiPadの傾きを検知してプレーヤーを左右に動かしてみます。プログラミングをやっていて、面白さを感じることができる最初のステップだと思います。

updateメソッドの実装

最初のステップでは「setup()」を実装しました。これは最初に一度呼び出されるだけですが、今回実装するのは「update()」です。このメソッドは通常1秒間に60回程度呼び出されます。update()の中で時間のかかる処理をするとアニメーションがカクカクとしたものになってしまうので、なるべくシンプルになるように心がけるようにしましょう。

最初のステップに追記していく形で説明します。追記したものは下記です。「ここから〜」「〜ここまで」とコメントしているところが追記したところです。

from scene import *

class Game (Scene):
	def setup(self):
		self.background_color = '#004f82'
		ground = Node(parent=self)
		x = 0
		while x <= self.size.w + 64:
			tile = SpriteNode('plf:Ground_PlanetHalf_mid', position=(x, 0))
			ground.add_child(tile)
			x += 64
		self.player = SpriteNode('plf:AlienGreen_front')
		self.player.anchor_point = (0.5, 0)
		self.player.position = (self.size.w/2, 32)
		self.add_child(self.player)
	# 今回追加したところ。ここから〜
	def update(self):
		g = gravity()
		if abs(g.x) > 0.05:
			x = self.player.position.x
			max_speed = 40
			x = max(0, min(self.size.w, x + g.x * max_speed))
			self.player.position = (x, 32)
	# 〜ここまで。

if __name__ == '__main__':
	run(Game(), PORTRAIT, show_fps=True)

ポイントの説明

それではポイントを説明していきます。

def update(self):

これが一番のポイントです。update()メソッドは前述の通り毎秒60回呼び出されることを想定して処理を書くようにしましょう。

g = gravity()

gravity()メソッドでiPadの傾きを取得します。これを呼び出すだけで、x方向、y方向、z方向の傾きを取得でき、傾きの範囲は-1.0〜1.0となります。数字が大きいほど傾きが大きいことを表しています。今回はx方向の傾きのみを扱います。

if abs(g.x) > 0.05:

abs()というのは絶対値を取るためのメソッドです。g.xがマイナスになることもあるので、絶対値を取って0.05よりも傾きが大きくなったらプレーヤーを動かすようにしています。

x = self.player.position.x
max_speed = 40
x = max(0, min(self.size.w, x + g.x * max_speed))
self.player.position = (x, 32)

この処理がプレーヤーを左右に動かしています。傾きが大きくなった場合、最大速度を40として、画面の外にはみ出ないようにしています。

「max(0, min(self.size.w, x + g.x * max_speed))」のところが少しトリッキーに見えてますね。
max()関数は2つの引数のうち、大きい方を取得します。そして、min()関数は2つの関数の小さい方を取得します。
max()によって、x座標が最大0になるように制御しています。画面の外はマイナス座標になるので、外に飛び出さないようになっていると理解できれば大丈夫です。
また、min()関数で画面サイズよりも外に出ないように制御しています。x座標が画面サイズより大きくなった時、min()で強制的に画面サイズちょうどに収まるようにしているんですね。1行でパッとみた感じ分かりづらいですが、効率の良い書き方になっていますね。

ここまでで実行すると、iPadを固めるとプレーヤーが左右に滑るように動くようになっているのが分かります。

歩くアニメーションを追加しよう

ここまで動かすと、プレーヤーが一点を見つめ、足を動かすことなく左右に動くという、なんとも気持ちの悪い動きをします。エイリアンなので、そういうエイリアンもひょっといるのかも・・・しれませんが、足が動いていた方が自然ですよね。
今回、移動に合わせて歩いているようなアニメーションを追加し、音も鳴らすようにしています。いよいよゲームっぽさが出てきますね。

このステップでも上述のソースコードに追記する形で説明します。少し複雑なので、追加した場所に[1]〜[4]の番号をコメントで追加していますので、番号に合わせて説明します。

from scene import *
import sound

def cmp(a, b):
	return ((a > b) - (a < b))

# ---[1]
standing_texture = Texture('plf:AlienGreen_front')
walk_textures = [Texture('plf:AlienGreen_walk1'), Texture('plf:AlienGreen_walk2')]

class Game (Scene):
	def setup(self):
		self.background_color = '#004f82'
		ground = Node(parent=self)
		x = 0
		while x <= self.size.w + 64:
			tile = SpriteNode('plf:Ground_PlanetHalf_mid', position=(x, 0))
			ground.add_child(tile)
			x += 64
		self.player = SpriteNode(standing_texture)
		self.player.anchor_point = (0.5, 0)
		self.player.position = (self.size.w/2, 32)
		self.add_child(self.player)
		# ---[2]
		self.walk_step = -1
	
	def update(self):
		g = gravity()
		if abs(g.x) > 0.05:
			#---[3]
			self.player.x_scale = cmp(g.x, 0)
			x = self.player.position.x
			max_speed = 40
			x = max(0, min(self.size.w, x + g.x * max_speed))
			self.player.position = x, 32
			# ---[4]
			step = int(self.player.position.x / 40) % 2
			if step != self.walk_step:
				self.player.texture = walk_textures[step]
				sound.play_effect('rpg:Footstep00', 0.05, 1.0 + 0.5 * step)
				self.walk_step = step
		else:
			self.player.texture = standing_texture
			self.walk_step = -1

if __name__ == '__main__':
	run(Game(), PORTRAIT, show_fps=True)

ポイントの説明

いよいよ動きのあるゲームに仕上げていきます。早速説明です。

# ---[1]
standing_texture = Texture('plf:AlienGreen_front')
walk_textures = [Texture('plf:AlienGreen_walk1'), Texture('plf:AlienGreen_walk2')]

ポイントは2点です。1つはじっと立っている場合の画像として「'plf:AlienGreen_front'」を使うこと。もう1つは歩いている画像を2枚用意すること。2枚は「'plf:AlienGreen_walk1'」と「'plf:AlienGreen_walk2'」で指定されて配列になっています。この後、standing_textureとwalk_texturesの2つを使って動きをつけていきます。

# ---[2]
self.walk_step = -1

walk_stepで停止中、歩行中の2つの状態を表せるようにします。また、歩行中は2パターンの画像を切り替える必要があるので、次のように表現します。

-1の時、「停止中」を表す。
0の時、「歩行中」を表し、1枚目の画像を使う。
1の時、「歩行中」を表し、2枚目の画像を使う。

#---[3]
self.player.x_scale = cmp(g.x, 0)

cmp()関数はソースの冒頭に定義しているものを呼び出していますが、ここでは0より大きければ1となり、0より小さければ-1となるように計算しています。
そして、x_scaleというのは画像の向きが右向きか、左向きにするかを変更するために指定しています。1の場合は右向き、-1の場合は左向きになるようにしているのがこの1行ですね。

# ---[4]
step = int(self.player.position.x / 40) % 2
if step != self.walk_step:
self.player.texture = walk_textures[step]
sound.play_effect('rpg:Footstep00', 0.05, 1.0 + 0.5 * step)
self.walk_step = step

「int(self.player.position.x / 40) % 2」というのは、x座標が40ポイントごとに歩いている画像を切り替えるための計算を行っています。この計算結果が移動に合わせて0と1が交互に切り替わるイメージです。
この計算結果がstep変数に格納されているので、「walk_textures[step]」を呼び出した時に画像が切り替わるという仕組みです。
そして、画像が切り替わった時に「sound.play_effect('rpg:Footstep00', 0.05, 1.0 + 0.5 * step)」を呼び出すことで音声を再生しているので、一歩ごとに歩いている感じが演出されています。

else:
	self.player.texture = standing_texture
	self.walk_step = -1

iPadの傾きがない場合、この部分が呼び出されるので止まっている画像に切り替わるという仕組みです。シンプルですが、よくできていますね。

最後に

第一回目としてソースコードを追加しながら、追加した部分を丁寧に見てきました。ステップごとに動かしてみることで、ゲームとしては不完全でもプログラミングとしては確実に前進していることを確かめながら進めていることを実感していただけたのではないでしょうか。もし、途中で思ったものと違う結果が表示された場合、どこを直せば良いかは追加したところだけを見れば良いので修正が楽になるというメリットもあります。こういったやり方に慣れてくると、プログラミングがどんどん楽しくなってきます。

ゲームを作りながらプログラミングの楽しさを学べるなんて楽しすぎますね!それでは、また。

Twitterでフォローしよう

Pickup
おすすめの記事