iOS动作游戏打造任意物体可摧毁的物理场景

iOS动作游戏打造任意物体可摧毁的物理场景

首页休闲益智重建物体更新时间:2024-04-21

一个早期DOS下的双人游戏,类似于百战天虫类型。

有趣的是游戏中实现了可摧毁的物理场景,而且只用了很少的代码:

游戏实现起来十分巧妙和简单,利用了CoreGraphic中的clear混合模式,将香蕉炸弹以中心位置的纹理全部消除,从而实现“摧毁”效果。

游戏中为建筑物单独创建一个类,继承于SKSpriteNode,其中有一个currentImage用来存放当前楼体的纹理:

class BuildingNode: SKSpriteNode {

var currentImage:UIImage!

}

当香蕉炸弹触碰楼体时,我们根据实际接触的中心点制作出爆炸摧毁效果:

func hitAt(point:CGPoint){

let convertedPoint = CGPoint(x: point.x size.width/2.0, y: abs(point.y - (size.height/2.0)))

UIGraphicsBeginImageContextWithOptions(size, false, 0)

let ctx = UIGraphicsGetCurrentContext()

currentImage.draw(at: CGPoint(x: 0, y: 0))

ctx?.addEllipse(in: CGrect(x: convertedPoint.x - 32, y: convertedPoint.y - 32, width: 64, height: 64))

ctx?.setBlendMode(.clear)

ctx?.drawPath(using: .fill)

let img = UIGraphicsGetImageFromCurrentImageContext()!

UIGraphicsEndImageContext()

texture = SKTexture(image: img)

currentImage = img

configurePhysics()

}

最后一句代码configurePhysics用来重建楼体的物理像素精确物理外观,实现纹理和物理边界相符。

虽然作者的构思很棒,不过游戏有一个小小的不足,就是当香蕉炸弹触碰到多于一个楼体的时候,炸弹只会消除第一个碰到的楼体外观,这显得不够真实。

所以本猫在这里尝试做进一步的完善喽!

我们有两种思路,一是当香蕉碰到楼体时不立即将其销毁,而是给它一定耐久度,只有当耐久度为0时才把它销毁。

另外一种思路是遍历所有楼体查找香蕉炸弹爆炸时半径涉及的楼体,然后依次重绘。两种方法都很简单,我们依次来看看。

增加香蕉炸弹耐久度

在创建香蕉时加入如下代码:

banana = SKSpriteNode(imageNamed: "banana")

banana.name = "banana"

banana.physicsBody = SKPhysicsBody(circleOfRadius: banana.size.width/2)

banana.physicsBody!.categoryBitMask = CollisionTypes.banana.rawValue

banana.physicsBody!.collisionBitMask = CollisionTypes.building.rawValue|CollisionTypes.player.rawValue

banana.physicsBody!.contactTestBitMask = banana.physicsBody!.collisionBitMask

banana.physicsBody!.usesPreciseCollisionDetection = true

addChild(banana)

//新加如下代码

banana.userData = ["persistence":2]

在香蕉接触到楼体的方法中加上耐久度处理的代码:

var persistence = (banana.userData as! [String:Int])["persistence"]!

persistence -= 1

banana.userData = ["persistence":persistence]

if persistence == 0{

if timer != nil{

timer.invalidate()

timer = nil

}

banana.name = ""

banana.removeFromParent()

banana = nil

changePlayer()

}

运行App试试看,效果还不错,不过有个问题,当你大力出奇迹的甩出香蕉时可能啥楼体都碰不到,直接甩到天涯海角去了!

这时你就会傻傻的萌呆在那里,啥都做不了啊。

所以我们要再加一个超时判断:甩出香蕉后5秒若是无事发生就强制销毁炸弹并且切换用户控制,在GameScene中添加一个属性:

var timer:Timer!

在发射出香蕉的launch方法中加上相关逻辑:

timer = Timer(fire: Date().addingTimeInterval(5.0), interval: 5.0, repeats: false){[unowned self] _ in

DispatchQueue.main.async {

print("timer done!")

self.timer.invalidate()

self.timer = nil

self.banana.name = ""

self.banana.removeFromParent()

self.banana = nil

self.changePlayer()

}

}

运行App,将香蕉甩到天边看看!咦!怎么过了许久也没反应啊!原来当前消息环并不是空闲的,它会被SpriteKit引擎所占用,你的定时器会无限挂起,所以你必须告诉消息环,爷可不是好惹的:

RunLoop.current.add(timer, forMode: .commonModes)

遍历所有爆炸半径内的楼体

我们现在来看看第二种思路,首先我们需要确定炸弹爆炸半径,为了简单其实我取的是爆炸矩形而不是圆形:

let rect = CGRect(x: point.x - 32, y: point.y - 32, width: 64, height: 64)

因为SpriteKit默认Node位置在中心,所以你必须将其转换为UIKit的坐标。

我们新建一个checkHitBuildingAt方法,该方法依次取出场景中所有楼房,计算是否处在爆炸半径内,如果是则将其摧毁 ;)

func checkHitBuildingsAt(_ point:CGPoint){

let rect = CGRect(x: point.x - 32, y: point.y - 32, width: 64, height: 64)

for building in buildings{

if building.frame.intersects(rect){

print("Find hit building : \(building.frame)")

let buildingLocation = convert(point, to: building)

building.hitAt(point: buildingLocation)

}

}

}

修改香蕉炸弹与楼房触碰的代码如下:

func bananaHitBuilding(_ building:BuildingNode,at point:CGPoint){

checkHitBuildingsAt(point)

}

运行游戏看看,是不是很有成就感爆棚呢???

,
大家还看了
也许喜欢
更多游戏

Copyright © 2024 妖气游戏网 www.17u1u.com All Rights Reserved