拖了好久的第三篇终于开始了……
ps:由于Xcode7不支持iOS8.4,我最终换回了Yosemite和Xcode6.4,所以语法如果有所出入引起问题,可以谷歌解决或者在下面留言。
添加敌人
为了简便起见,我们将用相同的飞机作为敌人,但是方向和主角飞机相反,从上往下飞(这里解释一下,因为之前使用yScale翻转,结果导致碰撞检测时出现了很诡异的bug,暂时无解,所以我用系统内置的预览工具,将spaceship这张图片进行了上下翻转,然后另存为一个资源,具体如下图。如果有人使用scale并解决问题,可以告知一声,不胜感激)。
var enemyTime:NSTimeInterval = 1 var lastEnemy:NSTimeInterval = 0
在此之前呢,我们先把update函数清理一下。将创建子弹的代码放置到单独的函数中,同时添加创建敌人的函数:
func createBullet(){ let bullet = SKShapeNode(rectOfSize: CGSizeMake(10,10)) bullet.position = CGPointMake(plane.position.x,plane.position.y + 50) bullet.strokeColor = UIColor.clearColor() bullet.fillColor = UIColor.greenColor() addChild(bullet) bullet.runAction(SKAction.sequence([SKAction.moveByX(0,y: size.height,duration: 2),SKAction.removeFromParent()])) } func createEnemy(){ let enemy = SKSpriteNode(texture: enemyTexture) enemy.setScale(0.5) enemy.position = CGPointMake(plane.position.x,size.height) addChild(enemy) enemy.runAction(SKAction.sequence([SKAction.moveByX(0,y: -size.height,duration: 4),SKAction.removeFromParent()])) } update(){ //添加到update函数中 if currentTime >= lastBullet + bulletTime{ createBullet() lastBullet = currentTime } if currentTime >= lastEnemy + enemyTime{ createEnemy() lastEnemy = currentTime } }
简洁了很多吧~!
其实和创建子弹是一样的,只不过设定初始Y坐标为屏幕高度,位移方向从上往下,也就是负的屏幕高度值。运行一下,会发现有大量飞机出现在和你相同的竖直方向上。
优化敌人
首先,敌人出现的时间间隔随机一些:
if currentTime >= lastEnemy + enemyTime{ createEnemy() lastEnemy = currentTime enemyTime = NSTimeInterval(arc4random() % 20 + 5) / 10 //一个神奇的经过反复测试得出的还不错的随机时间间隔 }
下一步就是将初始位置随机一些,在createEnemy函数中进行修改:
func createEnemy(){ let enemy = SKSpriteNode(texture: enemyTexture) enemy.setScale(0.5) var randomX = CGFloat(arc4random()) % size.width enemy.position = CGPointMake(randomX,SKAction.removeFromParent()])) }
用随机数对宽度取模,即可轻松搞定~
现在敌人已经成了比较完美的靶标了,准备射击!
添加物理
首先添加三个不明觉厉的后面会用到的东西
let PlayerCategory:UInt32 = 1<<1 let BulletCategory:UInt32 = 1<<2 let EnemyCategory:UInt32 = 1<<3
这个其实是碰撞标记,这篇博客里简单提到了,其实就是把需要互相碰撞的物体进行一个归类,设定和检测彼此的碰撞,并且发送碰撞消息。目前用到的标记有三个:我方飞机、子弹、敌方飞机。
bullet.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(10,10)) bullet.physicsBody?.categoryBitMask = BulletCategory bullet.physicsBody?.collisionBitMask = 0 bullet.physicsBody?.contactTestBitMask = EnemyCategory
分别是创建物理盒,所属标记,碰撞标记,碰撞通知标记。碰撞消息的产生不受碰撞标记设置的影响,碰撞标记只关系到是否模拟碰撞效果。物理盒大小我们可以自己设定,如果使用了纹理,也可以用纹理的size属性进行设置,就比如接下来的敌人物理盒创建。
planeTexture下面添加let enemyTexture = SKTexture(imageNamed: "Enemy")
createEnemy函数中添加:
enemy.physicsBody = SKPhysicsBody(rectangleOfSize: enemyTexture.size()) enemy.physicsBody?.categoryBitMask = EnemyCategory enemy.physicsBody?.collisionBitMask = 0 enemy.physicsBody?.contactTestBitMask = BulletCategory
这五行添加到addChild之前即可。
接下来就是最重要的环节了,修改GameScene类定义的这一行,改为:class GameScene: SKScene,SKPhysicsContactDelegate
继承碰撞检测代理,然后在didMoveToView中添加:
physicsWorld.contactDelegate = self physicsWorld.gravity = CGVectorMake(0,0)
表示碰撞检测代理为当前世界盒,并且重力为0,保证任何物体不会向下掉落。
然后在类中任意地方添加一个函数:
func didBeginContact(contact: SKPhysicsContact) { if contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask == BulletCategory | EnemyCategory{ print("enemy die") contact.bodyA.node?.removeFromParent() contact.bodyB.node?.removeFromParent() } }
用来在碰撞发生时进行操作,首先进行判断,碰撞涉及的两个标记,是否是子弹和敌人标记。如果是,就杀死一个敌人,移除相关的子弹和击中的敌人。跑起来看看吧,已经比较完整了~
全部代码如下:
import SpriteKit let PlayerCategory:UInt32 = 1<<1 let BulletCategory:UInt32 = 1<<2 let EnemyCategory:UInt32 = 1<<3 class GameScene: SKScene,SKPhysicsContactDelegate { let planeTexture = SKTexture(imageNamed: "Spaceship") let enemyTexture = SKTexture(imageNamed: "Enemy") var plane:SKSpriteNode! var bulletTime:NSTimeInterval = 0.2//子弹发射间隔 var lastBullet:NSTimeInterval = 0//上次发射的时间点 var toucheGap = CGPoint(x: 0,y: 0) var enemyTime:NSTimeInterval = 1 var lastEnemy:NSTimeInterval = 0 override func didMoveToView(view: SKView) { /* Setup your scene here */ physicsWorld.contactDelegate = self physicsWorld.gravity = CGVectorMake(0,0) plane = SKSpriteNode(texture: planeTexture) plane.position = CGPointMake(size.width * 0.5,size.height * 0.5) plane.setScale(0.5) plane.name = "plane" addChild(plane) } override func touchesBegan(touches: Set<NSObject>,withEvent event: UIEvent) { /* Called when a touch begins */ let location:CGPoint! = (touches.first as! UITouch).locationInNode(self) toucheGap = CGPoint(x: location.x - plane.position.x,y: location.y - plane.position.y) } override func touchesMoved(touches: Set<NSObject>,withEvent event: UIEvent) { let location:CGPoint! = (touches.first as! UITouch).locationInNode(self) let xDir = clamp(location.x - toucheGap.x,min: 0,max: size.width) let yDir = clamp(location.y - toucheGap.y,max: size.height) plane.position = CGPoint(x: xDir,y: yDir) } override func touchesEnded(touches: Set<NSObject>,withEvent event: UIEvent) { } func didBeginContact(contact: SKPhysicsContact) { if contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask == BulletCategory | EnemyCategory{ print("enemy die") contact.bodyA.node?.removeFromParent() contact.bodyB.node?.removeFromParent() } } override func update(currentTime: CFTimeInterval) { /* Called before each frame is rendered */ if currentTime >= lastBullet + bulletTime{ createBullet() lastBullet = currentTime } if currentTime >= lastEnemy + enemyTime{ createEnemy() lastEnemy = currentTime enemyTime = NSTimeInterval(arc4random() % 20 + 5) / 10 } } func createBullet(){ let bullet = SKShapeNode(rectOfSize: CGSizeMake(10,10)) bullet.position = CGPointMake(plane.position.x,plane.position.y + 50) bullet.strokeColor = UIColor.clearColor() bullet.fillColor = UIColor.greenColor() bullet.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(10,10)) bullet.physicsBody?.categoryBitMask = BulletCategory bullet.physicsBody?.collisionBitMask = 0 bullet.physicsBody?.contactTestBitMask = EnemyCategory addChild(bullet) bullet.runAction(SKAction.sequence([SKAction.moveByX(0,SKAction.removeFromParent()])) } func createEnemy(){ let enemy = SKSpriteNode(texture: enemyTexture) enemy.setScale(0.5) var randomX = CGFloat(arc4random()) % size.width enemy.position = CGPointMake(randomX,size.height) enemy.physicsBody = SKPhysicsBody(rectangleOfSize: enemy.size) enemy.physicsBody?.categoryBitMask = EnemyCategory enemy.physicsBody?.collisionBitMask = 0 enemy.physicsBody?.contactTestBitMask = BulletCategory addChild(enemy) enemy.runAction(SKAction.sequence([SKAction.moveByX(0,SKAction.removeFromParent()])) } func clamp(x: CGFloat,min: CGFloat,max: CGFloat) -> CGFloat{ if x <= min{ return min }else if x >= max{ return max }else{ return x } } }