拖了好久的第三篇终于开始了……
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, size.height)
addChild(enemy)
enemy.runAction(SKAction.sequence([SKAction.moveByX(0, y: -size.height, duration: 4), 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, min: 0, 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, y: size.height, duration: 2), 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, y: -size.height, duration: 4), 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
}
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。