Xcode5 + ObjC版を作成し、以下記事を投稿しました。
iOS SpriteKitによるAngry Birdsのようなゲームを作る。
今回は、Xcode6 + swift版です。
##横画面対応
http://qiita.com/kitanoow/items/4be69cf705c6aef3a6c6
こちらを参考にしてください。
##nodeの配置
今回は、力を加える(飛ばす)ボールと、障害物を用意します。
ボールについては、はじめから表示させておき、
障害物については、画面外に配置させ、あえてスクロールが必要な状態にしたいと
思います。
また、今回はボールに対して、センタリング処理を実現する予定ですので、
以下のようにworld/cameraの配置も行います。
world/camera については以下に記述しています。
override func didMoveToView(view: SKView) {
/* Setup your scene here */
var size:CGSize = view.frame.size
self.backgroundColor = UIColor(red:0.15,green:0.15,blue:0.3,alpha:1.0)
self.anchorPoint = CGPointMake(0.5, 0.5)
myWorld.name = "world"
self.addChild(myWorld)
var camera:SKNode = SKNode()
camera.name = "camera";
myWorld.addChild(camera)
override func didSimulatePhysics(){
var camera:SKNode = self.childNodeWithName("//camera")!
self.centerOnNode(camera)
}
func centerOnNode(node:SKNode) {
if let scene:SKScene = node.scene
{
var cameraPositionInScene:CGPoint = scene.convertPoint(node.position, fromNode: node.parent!)
node.parent!.position = CGPointMake(node.parent!.position.x - cameraPositionInScene.x, node.parent!.position.y - cameraPositionInScene.y);
}
}
##ボール・地面・障害物の配置
それぞれdidMoveToViewにて、
//地面
var ground:SKSpriteNode = SKSpriteNode(color: SKColor.brownColor(),
size:CGSizeMake(size.width*10,size.height))
ground.position = CGPointMake(0, -ground.size.height + 30);
ground.physicsBody = SKPhysicsBody(rectangleOfSize: ground.size)
ground.physicsBody!.dynamic = false;
myWorld.addChild(ground)
//ボール
ball = SKSpriteNode(imageNamed:"ball.png")
ball.position = CGPointMake(0, -20);
ball.name = "ball";
ball.physicsBody = SKPhysicsBody(circleOfRadius:ball.size.width/2)
ball.physicsBody!.dynamic = false;
myWorld.addChild(ball)
ついでといってはアレですが、今回利用するメンバ変数やenum値については
先に書いておきます。
enum GameStatus:Int{
case kDragNone=0, //初期値
kDragStart, //Drag開始
kDragEnd //Drag終了
}
class GameScene: SKScene,SKPhysicsContactDelegate {
var ball:SKSpriteNode!;
var target:SKSpriteNode!;
var gameStatus:Int = 0;
var startPos:CGPoint!;
var sprtical_flg:Bool = false;
var myWorld:SKNode = SKNode()
障害物についてはdidMoveToViewにて以下を追記します。
//障害物
var start_x:Int = 500;
var sprite:SKSpriteNode = SKSpriteNode(color: SKColor.greenColor(), size: CGSizeMake(15,80))
sprite.physicsBody = SKPhysicsBody(rectangleOfSize: sprite.size)
sprite.physicsBody!.categoryBitMask = 0x1 << 1;
sprite.position = CGPointMake(CGFloat(start_x + 100),-90);
myWorld.addChild(sprite)
sprite = SKSpriteNode(color: SKColor.greenColor(), size: CGSizeMake(15,80))
sprite.physicsBody = SKPhysicsBody(rectangleOfSize: sprite.size)
sprite.physicsBody!.categoryBitMask = 0x1 << 1;
sprite.position = CGPointMake(CGFloat(start_x + 200),-90);
myWorld.addChild(sprite)
sprite = SKSpriteNode(color: SKColor.greenColor(), size: CGSizeMake(150,15))
sprite.physicsBody = SKPhysicsBody(rectangleOfSize: sprite.size)
sprite.physicsBody!.categoryBitMask = 0x1 << 1;
sprite.position = CGPointMake(CGFloat(start_x + 150),-50);
myWorld.addChild(sprite)
sprite = SKSpriteNode(color: SKColor.greenColor(), size: CGSizeMake(15,50))
sprite.physicsBody = SKPhysicsBody(rectangleOfSize: sprite.size)
sprite.physicsBody!.categoryBitMask = 0x1 << 0;
sprite.position = CGPointMake(CGFloat(start_x + 120),-20);
myWorld.addChild(sprite)
sprite = SKSpriteNode(color: SKColor.greenColor(), size: CGSizeMake(15,50))
sprite.physicsBody = SKPhysicsBody(rectangleOfSize: sprite.size)
sprite.physicsBody!.categoryBitMask = 0x1 << 0;
sprite.position = CGPointMake(CGFloat(start_x + 180),-20);
myWorld.addChild(sprite)
sprite = SKSpriteNode(color: SKColor.greenColor(), size: CGSizeMake(100,15))
sprite.physicsBody = SKPhysicsBody(rectangleOfSize: sprite.size)
sprite.position = CGPointMake(CGFloat(start_x + 150),0);
sprite.physicsBody!.categoryBitMask = 0x1 << 0;
myWorld.addChild(sprite)
target = SKSpriteNode(color: SKColor.redColor(), size: CGSizeMake(15,15))
target.physicsBody = SKPhysicsBody(rectangleOfSize: target.size)
target.physicsBody!.contactTestBitMask = 0x1 << 0;
target.position = CGPointMake(CGFloat(start_x + 150),-35);
myWorld.addChild(target)
また、衝突を検出するために、didMoveToViewに
self.physicsWorld.contactDelegate = self;
も追記します。
障害物については以下のような形を考えています。位置については数値決め打ちで申し訳ないですが。。
赤色が衝突対象物です。
また、今回の衝突については
上記のようなピンクで囲った箇所が赤色のNodeにぶつかったら
その箇所にパーティクルを表示させたいと思いますので、
ピンクで囲ったNodeとにtargetのNode(赤色)ついては、
categoryBitMaskを 1(0x1<<0)を
それ以外については
contactTestBitMaskを 2(0x1<<1)を指定しています。
衝突とmask値の関係については以下にまとめています。ざっくりですが。
【Xcode5】Xcodeの使い方 SpriteKit 編 Vol11 〜 ノード(剛体)の衝突とBitMaskについて〜
##タッチを検出し、nodeを移動させる
行うこととしましては
- タッチ開始時に、そこに、ballノードがあれば、その座標の保持とステータス変更。
- タッチ移動時には、ballの位置をそのタッチの位置に合わせて変更。
- タッチ終了時には、力を加えるということと、ズームアウトなどのアニメーションを実行します。
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
/* Called when a touch begins */
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
var node:SKNode! = self.nodeAtPoint(location);
if(node != nil){
if(node.name=="ball"){
gameStatus = GameStatus.kDragStart.rawValue;
startPos = location;
}
}
}
}
override func touchesMoved(touches: NSSet, withEvent event: UIEvent) {
if(gameStatus == GameStatus.kDragStart.rawValue ){
var touch:UITouch = touches.anyObject() as UITouch;
var touchPos:CGPoint = touch.locationInNode(self) ;
ball.position = touchPos;
}
}
override func touchesEnded(touches: NSSet, withEvent event: UIEvent) {
if(gameStatus == GameStatus.kDragStart.rawValue ){
gameStatus = GameStatus.kDragEnd.rawValue ;
var touch:UITouch = touches.anyObject() as UITouch;
var endPos:CGPoint = touch.locationInNode(self) ;
//x,yの移動距離を算出
var diff:CGPoint = CGPointMake(startPos.x - endPos.x, startPos.y - endPos.y);
ball.physicsBody!.dynamic = true;
//yを少し大きく
ball.physicsBody!.applyForce(CGVectorMake(diff.x * 20 , diff.y * 50))
var scaleOut:SKAction = SKAction.scaleTo(0.5,duration:0.2);
var moveUp:SKAction = SKAction.moveByX(0,y:-100,duration:0.2);
var scale1:SKAction = SKAction.group([scaleOut,moveUp]);
var delay:SKAction = SKAction.waitForDuration(1.0);
var scaleIn:SKAction = SKAction.scaleTo(1,duration:1.0);
var moveDown:SKAction = SKAction.moveByX(0,y:100,duration:1.0);
var scale2:SKAction = SKAction.group([scaleIn,moveDown])
var moveSequence:SKAction = SKAction.sequence([scale1, delay,scale2]);
myWorld.runAction(moveSequence)
}
}
より上空にあがるようにするために、y向きに少し力を大きめにしています。
また、アニメーションについては
- ズームアウトと上への移動
- 待機
- ズームインと下への移動
をsequenceで実行しています。
SKActionの、groupとsequenceの違いについては
こちらにまとめています。
【Xcode5】Xcodeの使い方 SpriteKit 編 Vol3 〜 SKActionの使い方その1 〜
ただ、このままではボールが画面外に出た場合に追跡できないため、
ballが中心より右側に移動した場合は、カメラの位置も、ballの位置になるように
変更したいと思います。ただし今回は、ballのxだけを引きづくようにします。
override func didSimulatePhysics(){
var camera:SKNode = self.childNodeWithName("//camera")!
if gameStatus == GameStatus.kDragEnd.rawValue && ball.position.x > 0 {
camera.position = CGPointMake(ball.position.x, camera.position.y);
}
self.centerOnNode(camera)
}
##パーティクルの実行について
衝突が合った場合は
//衝突開始時
func didBeginContact(contact:SKPhysicsContact){
if !sprtical_flg {
sprtical_flg = true;
var path:String = NSBundle.mainBundle().pathForResource("MyParticle", ofType: "sks")!
var spark:SKEmitterNode = NSKeyedUnarchiver.unarchiveObjectWithFile(path) as SKEmitterNode!
println(contact.contactPoint)
spark.numParticlesToEmit = 50;
spark.particlePosition = target.position;
myWorld.addChild(spark)
ball.removeFromParent();
target.removeFromParent();
}
}
衝突が合った箇所にパーティクルを表示させ
赤色のNode(target)を非表示にしています。
パーティクルの利用については
【Xcode5】Xcodeの使い方 SpriteKit 編 Vol3 〜 SKActionの使い方その1 〜
ひとまず以上です。
細かい部分は微妙な所や決め打ちな所があって恐縮ですが、
何かの参考になれば幸いでございます。
あと以下にソースをアップしておりますので、よろしければ
AngryBirdsLikeGameBySwift