はい!みなさんご注目!
ついにiPhone向けゲームアプリ「たぬらん!!」がApp Storeからリリースされました!!!
https://itunes.apple.com/us/app/tanu-run/id1436120521?ls=1&mt=8
狸っぽいアライグマが大樹の枝をジャンプで登っていくシンプルなゲームだけど、今後はゲームステージを増やしつつGameCenterでプレイヤー同士が競えるような新機能も追加していく予定。
iPhoneをお持ちの読者の方は無料アプリなんでぜひ遊んでね(=^・・^=)♬
さて。「たぬらん!!」はブログ仲間琴音さんとのコラボ作品で、彼女がこしらえたキャラクタや音楽を私ししもんがプログラミングで動かした感じ。製作期間はざっくり3週間。統合開発環境Xcodeの使い方とSwift言語を学ぶところから始めて、Googleが提供するモバイルアプリ広告「AdMob」で収益化。最後の数日でバグを潰してAppleの審査を一発で通したわけで、iPhoneアプリの処女作として我らは存分に健闘したと思っている。
でもその道程はラクではなかった。
プログラミングをするには数学や論理的思考に長けている必要がありそうだけど、実際はGoogle検索能力と英語力が一番モノをいう。と言うのも意味不明なことが発生したとき(ぶっちゃけメッチャ発生する)、問題があまりにも複雑過ぎて素人アタマで考えてもまず答えが出ないんだよね。アホの考え休むに似たり。
そんなときは先人の知恵を拝借した方が格段に解決が速いんだ。
ところが技術の世界の公用語は英語であり、そもそもAppleの開発環境は英語版しか提供されてなかったりする。さらにその道の玄人が日本人とは限らないわけで、英語で書かれた質問掲示板を読み込んで玉石混交の情報の山から的確な答えをほじくり返すのが「プログラミング」という作業の大半を占める。
英語力。これは今後プログラミングをするなら必須だ。
とはいえ日本語で活発に情報発信しているエンジニアの方々もたくさんいらっしゃる。しかも日本にはiPhoneアプリの開発者がたくさんいる関係で、もはやSwiftで簡単な2Dアクションゲームを作るくらいなら、日本語オンリーでマルっと全てなんとかなっちゃうほど。
いわば英語の大海原に日本語の島をこしらえて、ワサワサ領土を広げている感じ。
かっこいい!!
僕も先人の後に続きたい!!
そこで今日は、僕がゲームアプリ開発の初期に困りに困った「下からは通過できる床」の作り方を書く。マリオみたいなアクションゲームにありがちな、下からジャンプした場合スルー出来るけど、上に乗ることが出来る一方通行の床のことだ。
それっぽいことを語っている人は見つけたんだけど、Swift4でこれを実現するズバリの答えは日本語では多分まだ存在していないと思う。
だから結局僕は英語サイトでヒントを貰い、最終的には自分で工夫して実現できた。
今日はこのノウハウを日本語でネットに撒いておく。
下からは通過できる床
とりあえず今から作りたいのはこんな感じのやつ。下からは通過できるけど、上から落ちてくると乗っかることが出来る。
そして「たぬらん!!」の主人公タヌ氏。琴音さんのオリジナルキャラクタである「アライグマのたぬ」はLINEのスタンプにもなっているのでカワイイと思ったらぜひご購入くださいね。
https://store.line.me/stickershop/product/1331021/ja
あ、便乗してししもんのスタンプも是非!むしろこっちを買ってw だけど「シンガポールのししもん」LINEスタンプはおそらくレインボーフラッグと豚のキャラクタのせいで、イスラム教徒が多く同性愛に不寛容な国での販売が禁止されちゃっている。「シンガポールのししもん」なのにシンガポールで発売されてないという…。
NEVERはマジでクソ(=^・・^#=)
バルス!!!
まぁそんなことはどうでもいいとして。
今日は「下からは通過できる床」についてである。
と、思ったんだけど。
これを実現するにはあまりにも語ることが多すぎる。iOS標準の2Dゲームエンジン「SpriteKit」の使い方、物理空間の定義、当たり判定とイベント処理、そしてそもそもApp Storeで自作アプリを公開する方法…。
なのでこのiPhoneアプリ開発ノウハウはシリーズ化して、それぞれについて気合を入れてちゃんと解説することにする。ここまで2000字読んできた読者の皆様ごめんなさい。
でも大丈夫だから!!
とりあえず既にXcodeでSwiftをスラスラ書けるなら、下記のソースコードをコピペするだけで「下からは通過できる床」を実現できちゃう(=^・・^=)♬ 我がブログはなんて有益なんだ。
まず、プレイヤであるタヌ氏TANUと、本題である「下からは通過できる床」に当たる枝EDAをSKSpriteNodeで定義する。こんなノリ。
var TANU = SKSpriteNode(imageNamed: "TANU.png") var EDA = SKSpriteNode(imageNamed: "EDA.png")
こうしておくと後でヘマしてもNodeが画面に表示だけはされるので、宣言の段階でテクスチャ画像を設定しておくのがししもん流。間違ってたらゴメンけど、とりあえずこれで動いている。
そしてアプリがiOSからサンドボックスごとメモリに展開されて実際に動き出したら呼ばれるdidMove関数で、タヌと枝に表示サイズや位置、物理空間での振る舞い、そしてこれから重要になる当たり判定を初期化する。
override func didMove(to view: SKView) { EDA.name = "EDA" EDA.size = CGSize(width: 300, height: 80) EDA.position = CGPoint(x: view.frame.midX, y: view.frame.midY) EDA.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: 300, height: 30)) EDA.physicsBody?.isDynamic = false EDA.physicsBody?.affectedByGravity = false EDA.physicsBody?.categoryBitMask = mask.EDA EDA.physicsBody?.collisionBitMask = mask.TANU EDA.physicsBody?.contactTestBitMask = mask.TANU addChild(EDA) TANU.name = "TANU" TANU.size = CGSize(width: 150, height: 150) TANU.position = CGPoint(x: view.frame.midX, y: view.frame.midY - 150) TANU.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: 110, height: 110)) TANU.physicsBody?.isDynamic = true TANU.physicsBody?.affectedByGravity = true TANU.physicsBody?.allowsRotation = false TANU.physicsBody?.categoryBitMask = mask.TANU TANU.physicsBody?.collisionBitMask = mask.EDA TANU.physicsBody?.contactTestBitMask = mask.EDA addChild(TANU) }
ああああ、説明が下手過ぎて自分が嫌になる。今後それぞれの項目について詳説する記事を書くので許して!お願い(=^> <^;=)
もう、いい。開き直った。ガシガシ次に行くぞ。
お次はスマホの画面をタップしたイベントに連動して呼ばれるtouchesBegan関数。ここでタヌと枝の当たり判定を無効化し、TANUとEDAが何に衝突しても通過出来るように設定する。
override func touchesBegan(_ touches: Set, with event: UIEvent?) { EDA.physicsBody?.collisionBitMask = 0 TANU.physicsBody?.collisionBitMask = 0 TANU.physicsBody?.applyImpulse(CGVector(dx: 0, dy: 100)) }
physicsBody配下のapplyImpulseはノードに加速度を与える関数だ。これを設定することでタヌがジャンプするアクションを起こしている。なお縦方向の位置変化率dyに引数で「100」を設定しているけど、この数値を調節することでジャンプの強さを定義することができる。だからここを変数にすれば状況に応じてキャラの反応を変えることも可能だ。
でも注目すべきはその前にタヌが何に衝突しても通過出来るように設定しているところ。
EDA.physicsBody?.collisionBitMask = 0 TANU.physicsBody?.collisionBitMask = 0
実際のゲームではジャンプ中のこの状態でもアイテムにぶつかったら判定しなくてはいけないのだけど、とりあえず今回のサンプルではわかりやすさ優先で簡略化している。
は?それでもわかり難い?
バルス(=^・・^#=)!
次に、上方から衝突したらちゃんと当たり判定されて、枝の上に乗っかるようにしたい。これを実現しているのが下記のコード。実装しているのは画面が更新されるフレーム毎に超高速で呼ばれているupdate関数だ。
override func update(_ currentTime: TimeInterval) { if TANU.position.y - TANU.size.height / 2 > EDA.position.y { EDA.physicsBody?.collisionBitMask = mask.TANU TANU.physicsBody?.collisionBitMask = mask.EDA } }
ここでタヌが枝の上まで上昇したことをif文で判定し、真なら再びタヌと枝の当たり判定を復活させている。そうすれば物理空間physicsBodyで設定した重力に従ってタヌが落下したとき、もし枝にあたったらキチッと判定さえて上に乗っかることができる。
でも本来は、毎秒数十回も実行されるupdate関数に無駄なコードを書くことはCPUの負荷を上げるので望ましくない。「たぬらん!!」でも最終的にこの処理は別のタイミングで呼ぶようにしたけど、でも最初のうちはそんなことは気にせず「思い通りに動けばなんでもいい」の方針でコーディングした。
Done is better than perfect、である。
以上がXcode9・Swift4・SpriteKitでアクションゲームに必須の「下から通過できる床」を作る方法であります。
いかがだったでしょう。意味わかんなかったっすね(=^・・^;=)
マジすんまそん…。
サンプルコードと今後のコンテンツ
この記事で紹介したサンプルコードの全文とXcode9のプロジェクトファイルは、下記のGitHubで公開してるんで必要なら使って下さい。
https://github.com/shishimong/onewaygothrough
なお「アライグマのタヌ」の画像については琴音さんが全ての著作権を持っています。無断転載や改変はその一切を禁止します。
あとXcode9・Swift4・SpriteKitの環境で作るiPhoneアプリについて、今後同じコードをネタに下記についても書いていく予定。乞うご期待。
SpriteKit事始め
iOSの2Dゲームエンジンである「SpriteKit」を使って簡単なアプリを作る方法について書く。とりあえずViewにScene、SceneにNodeを追加してiPhone実機の画面にイラストを表示するところまで。
physicsBodyの設定
ゲームエンジンって結局は物理環境のシミュレータなんだよね。質量をぶつけて積んである物体を崩す「アングリーバード」みたいな。そこでiOSが提供してくれる物理空間でSpriteNodeにphysicsBodyを設定して、物体を重力や加速度に従って動かすやり方を説明する。
当たり判定
今回の「下からは通過できる床」で説明しきれなかったcategoryBitMask、collisionBitMask、contactTestBitMaskの違いと使い方について書く。特にこの設定をミスるとSpriteが衝突してもdidBegin関数が呼ばれず、何も知らない僕は結構ハマった。この辺の事情について詳説する予定。
FirebaseとAdmobでアプリを収益化
僕は無職だから1円でも惜しい。せっかくiPhoneアプリを作るならこのブログのように広告を出して収益化したいものだ。さらに欲を言えばどういう人がいつどこでアプリを使ったのかも調べてマーケティングに役立てたい。
それをマルっと全部実現してくれるのがFirebaseとAdmob。
そうGoogleならね。
FirebaseとAdmobを使ってiPhoneアプリに広告を乗せて収益化する方法について書く。
App Storeの審査について
App Storeでアプリを公開するにはApple公式による審査を突破しなくてはいけない。今回僕は予めバグを潰して万全を期して望んだけど、実は審査項目はアプリ本体のバグだけではない。
いっかい審査に落ちただけで開発期間を何日も無駄にすることになる。この回ではApp Storeの審査の注意点と、一発で通すノウハウについて書く。
まぁそんな感じで…。
2017年1月から発達障害の生き難さについて書いてきたこのブログ。ところがシンガポールで定職を失い好きな場所で好きなことだけしている生活になったら、皮肉なことにその方面で書くネタが無くなっちゃった。
だって毎日単純に楽しいんだもん。
無職はいいぞぉ(=^・・^=)♬
だから今後このブログ「無職は恥だが癖になる」は、発達障害があって企業社会で上手く泳げなくても、手に職をつけてユルい南国で無理やり生きていく具体的な技術指南となります。
最近無職なのになぜかクソ忙しくて更新頻度が落ちているけど、今後とも勢力的に情報発信していく所存。
乞うご期待(=^・・^=)♬