【Swift】【iOS】子供が自動販売機が好きなようなのでアプリ作った。
2015/12/06
そういえば、一週間前に申請していたNeedleBalloonPopというアプリが認められて、App Storeに並んでいました。
これで個人で作ったiPhoneアプリが33個iTunesに並んだことになるようです。
さて、1歳半を過ぎた子供が自動販売機に興味があり、自販機を見るたびに指をさして、「バイジャバイジャ」(自販機を意味しているらしい。より詳細に言えば、自販機のおつりを出すためのレバーを意味しているらしい。)と言うようになりました。
そういう時は、自販機に息子を連れて行って、お釣りのでてくるレバーを1回以上いじらせないと、不機嫌になったりします。
逆に、自販機のレバーをいじらせると、「バイジャバイジャー」と言いながら上機嫌に喜んで笑う。
あとは「ボタン」と言いながらボタンを押したり、「あな」と言いながらお金を入れるところをいじったり、「ネジ」と言いながらネジをいじったりします。
というわけで自販機が最近アツいようなので、自販機っぽいアプリを自作してみようと思いました。xcodeで。
とりあえず自販機っぽい画像をFireAlpacaで作成。赤い長方形から消しゴムで長方形を2つくり抜く。その部分は透過。
とりあえず5本の飲み物画像を用意。
Pixabay.comから画像をいただくと捗ります。
あとはそれらをxcodeでスクリーンに貼り付けて、ボトルの下にそれぞれ対応する水色丸をボタンとして用意して、ボタンを押すとジュースが落ちてくるとかいう仕様です。
とりあえずお金とかお釣りの部分とかは作ってない。
ボタン押すといくらでもペットボトルとか缶ジュースが落ちてきます。
いろいろ落ちてくる。

例えばある程度の上限量まで落ちてきたらジュースを排除するみたいな処理をかけばいいのですが、何もしてないので上から溢れてきます。
溢れてくるのもそれはそれで面白いか。
コードとしてポイントになるのは、ボタンを押すと自販機画像の後ろからジュースを出現させてそれに重力をかけて下に落としているのですが、何もしないと画面下まで落ちてしまうので、ジュースの取り口の長方形の下水平線とほぼ重なるように「under」という水平方向に長い見えない長方形を作成し、それに重力効果を付与しておいて、ジュースがぶつかると止まるようにしています。
あと、横からはみ出るとまずいので、同様に、重力効果を付与した縦長の見えない長方形を両脇に作成、これらをleftとrightとしています。
これが壁になって、自販機画像の横からはみ出ることはないです。
あと、ボタンに左から0~4というnameを振っておいて、ジュース画像の名前も0〜4に対応付けておく。
ボタンが押されるとnameを取得して、そのnameに対応した画像を読み込んで表示させてます。
その際、新たに読み込む画像がレイヤの最前列に来てしまうので、
sprite.zPosition = -1
としておいて自販機画像の奥に表示される形にしています。
あと、途中で使わなくなったこれ。
//let bottle = self.childNodeWithName("\(i!)"+"bottle")
childNodeWithName(ノードに付与したname)で、viewに貼り付けたノードの名前に応じてそのノードを取得できるそうな。
これは便利そう。
最初はディスプレイされてるジュースがそのまま落ちてくるようにしていて、ディスプレイされてるジュースそのものにnameをつけていたので、
1 最初にそれぞれのボトルに「sprite.name = "\(Int(i))"+"bottle"」とかで名前を付与しておく(いちばん左のは「0bottle」という名前になる)
2 ボタンを押すとボタンの番号が取れる(0とか)
3 ボタンが押された時の処理を書くメソッドに、「let bottle = self.childNodeWithName("\(i!)"+"bottle")」と書いておいて、名前に応じたノードをbottleとして作成(0bottleという名前のノードをbottleというノードとして作成)
4 以下のコードで、そのbottleに重力がかかるようにする。(いちばん下のボトルが落ちる)
//let physicsBody = SKPhysicsBody(rectangleOfSize: bottle!.frame.size)
//physicsBody.dynamic = true
//physicsBody.contactTestBitMask = 1
//bottle!.physicsBody = physicsBody
という風にやっていたのですが、途中から、普通の自販機みたいに、ディスプレイされてるのが落ちるわけじゃないようにしたので上記の方法は使いませんでした。
あと、各画像は縦長に立った状態で用意しているところ、普通の自販機だと横に倒れた状態で出てくるので、それっぽくしようとしたら、重力の部分だけ縦長の状態のままになって、ノードの画像と重力がかかる部分とがずれてしまった。
とりあえずノードを横に倒す方法は
sprite.zRotation = CGFloat(M_PI*0.5)
で行けるのだけど、それを
sprite.physicsBody = physicsBody
self.addChild(sprite)
で、画像を横倒しにせずにそのまま重力を付与して画像表示した後に、書かないと、おかしなことになるみたいですなあ。
とりあえず色々思ったことはあるけどまあ後で書くとしてコード全文。
今回も、特にアプリ申請するつもりはないので、iphone6シミュレータでしか正常表示されないと思います。
import Foundation
import SpriteKit
class GameScene: SKScene,SKPhysicsContactDelegate {
override func didMoveToView(view: SKView) {
//重力を設定
self.physicsWorld.gravity = CGVector(dx: 0, dy: -10)
self.physicsWorld.contactDelegate = self
//背景を白に
self.backgroundColor = UIColor.whiteColor()
//ジュースの画像を並べる
hyojiBottle("0.png", i: 0)
hyojiBottle("1.png", i: 1)
hyojiBottle("2.png", i: 2)
hyojiBottle("3.png", i: 3)
hyojiBottle("4.png", i: 4)
//画像
let under = SKSpriteNode(color: UIColor.whiteColor(), size: CGSize(width: self.frame.width, height: 10))
under.position = CGPoint(x: self.size.width*0.5, y: 150)
let physicsBody = SKPhysicsBody(rectangleOfSize: under.frame.size)
physicsBody.dynamic = false
physicsBody.contactTestBitMask = 1
under.physicsBody = physicsBody
self.addChild(under)
let left = SKSpriteNode(color: UIColor.whiteColor(), size: CGSize(width: 10, height: self.frame.height))
left.position = CGPoint(x: self.size.width*0.1, y: self.frame.height*0.5)
let physicsBody2 = SKPhysicsBody(rectangleOfSize: left.frame.size)
physicsBody2.dynamic = false
physicsBody2.contactTestBitMask = 1
left.physicsBody = physicsBody2
self.addChild(left)
let right = SKSpriteNode(color: UIColor.whiteColor(), size: CGSize(width: 10, height: self.frame.height))
right.position = CGPoint(x: self.size.width*0.9, y: self.frame.height*0.5)
let physicsBody3 = SKPhysicsBody(rectangleOfSize: right.frame.size)
physicsBody3.dynamic = false
physicsBody3.contactTestBitMask = 1
right.physicsBody = physicsBody3
self.addChild(right)
hyojiBottle("machine.png", i: 5)
hyojiButton(0)
hyojiButton(1)
hyojiButton(2)
hyojiButton(3)
hyojiButton(4)
}
func hyojiBottle(fileName: String, i: CGFloat) {
let texture = SKTexture(imageNamed: fileName)
let sprite = SKSpriteNode(texture: texture)
if i == 5 {
sprite.position = CGPointMake(self.size.width*0.5, self.size.height*0.5)
sprite.size = CGSize(width: texture.size().width+40, height: texture.size().height)
}else{
sprite.position = CGPointMake(self.size.width*0.14*(i+0.5)+60, self.size.height*0.75)
sprite.size = CGSize(width: texture.size().width*0.7, height: texture.size().height*0.7)
sprite.name = "\(Int(i))"+"bottle"
}
self.addChild(sprite)
}
func hyojiButton(i: Int) {
let button = SKShapeNode(circleOfRadius: 15)
button.fillColor = UIColor.cyanColor()
button.position = CGPoint(x: 55*(i+1)+30, y: 400)
button.name = "\(i)"
print(button.name!)
self.addChild(button)
}
func hyojiBottle2(fileName: String, i: CGFloat) {
let texture = SKTexture(imageNamed: fileName)
let sprite = SKSpriteNode(texture: texture)
sprite.position = CGPointMake(self.size.width*0.14*(i+0.5)+60, self.size.height*0.5)
sprite.size = CGSize(width: texture.size().width*0.7, height: texture.size().height*0.7)
sprite.zPosition = -1
let physicsBody = SKPhysicsBody(rectangleOfSize: sprite.frame.size)
physicsBody.dynamic = true
physicsBody.contactTestBitMask = 1
sprite.physicsBody = physicsBody
self.addChild(sprite)
sprite.zRotation = CGFloat(M_PI*0.5)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
if let touch: AnyObject = touches.first{
let location = touch.locationInNode(self)
let node:SKNode = self.nodeAtPoint(location)
if let _ = node.name {
let i = Int(node.name!)
hyojiBottle2("\(i!)", i: CGFloat(i!))
//let bottle = self.childNodeWithName("\(i!)"+"bottle")
//let physicsBody = SKPhysicsBody(rectangleOfSize: bottle!.frame.size)
//physicsBody.dynamic = true
//physicsBody.contactTestBitMask = 1
//bottle!.physicsBody = physicsBody
}
}
}
}