我相信,很多人应该都接触过这款游戏,在我的记忆中,这是那个文曲星和步步高还流行的时代里,步步高上最受欢迎的游戏了。
前面做过了连连看这种最简单级别的游戏,这次我们来尝试一下稍微进阶一点,做一个,有地图,有属性,有主角,有战斗的,各种模块都有那么一点点的小游戏。
首先要做的是架构分析。
这款游戏主要存在这么几个要素:
1、地图,作为主角移动的空间和限制。
2、道具,包括各种血瓶,钥匙,宝物,可以给予主角数值加成或者某些能力。
3、敌人,用于发生战斗。
4、剧情,这部分非常少甚至可以忽略不计,因此放在最后。
今天我们就先来搭建起一个舞台,也就是地图。
常规版本的魔塔是0-16层一共17层,你当然可以考虑加载17个SKScene来对17层地图进行保存和渲染,但是这样会造成严重的资源浪费,主角在上下楼之间的跳转也会造成不必要的性能开销。
又因为你需要一直保存着地图上的情况,不可能每次到达该楼层才创建对象(你不能下楼上楼一次刚刚吃的血瓶就又刷新出来了吧?),所以我的对策是,将所有的楼层全部拼接到一个SKSpriteNode上,然后通过SKCropNode遮罩节点进行渲染裁剪,来达成对应的效果。
粗略示意如下
----------- ---------- //渲染窗口
aa bb a c d s wf //交互层(道具和怪物)
---------- ----------- ----------- //单层地图
------------------------------------------------------//地图依附背景节点
通过移动依附背景节点的坐标,我们可以更换在渲染窗口中进行显示的地图,从而达成楼层切换的目的,又因为所有的地图资源文件一经创建后不再销毁,其上依附的相关信息自然就不会因为销毁-创建过程而重新建立,保存地图信息的目的也就达到了。
这里插入一下裁剪节点SKCropNode的使用方法。
首先,新建一个工程,去掉多余的代码,在Scene的didMoveToView方法中写入如下代码:
override func didMove(to view: SKView) {
let backnode = SKSpriteNode(texture: SKTexture(imageNamed: "9779.jpg"), color: SKColor.clear, size: self.frame.size)
backnode.anchorPoint = CGPoint.zero
backnode.position = CGPoint(x: 0, y: 0)
addChild(backnode)
}
图片你们自己随意找,现在你得到的效果应该是这个样子:
添加被裁剪节点看,现在经过设置,这个节点正常被添加到scene的话是会占满整个显示屏幕的。现在让我们来添加SKCropNode来制造遮罩裁剪效果。
现在我们把代码改成这样。
override func didMove(to view: SKView) {
let backnode = SKSpriteNode(texture: SKTexture(imageNamed: "9779.jpg"), color: SKColor.clear, size: self.frame.size)
backnode.anchorPoint = CGPoint.zero
backnode.position = CGPoint(x: 0, y: 0)
let crop = SKCropNode.init()
let window = SKSpriteNode(color: SKColor.black, size: CGSize(width: 200, height: 200))
window.anchorPoint = CGPoint.zero
window.position = CGPoint(x: 0, y: 0)
crop.maskNode = window
crop.addChild(backnode)
addChild(crop)
}
说明:SKCropNode节点本身没有锚点,位置点,大小等属性,你可以把他理解为一个完全遮住屏幕的蒙版,因为是完全遮住,所以大小之类的也就不需要设置了。
你能够调整的真正对象实际上是SKCropNode.maskNode,虽然直译上这个叫做遮罩节点,但是实际上该节点起到的作用反而是在“完全遮住的cropNode上开启的窗口”这样的角色,maskNode拥有anchorPoint属性和Position属性还有size属性,可以用来调解可视窗口区域。
而被遮罩裁剪的节点必须作为子节点添加到CropNode上,而不能直接添加到Scene上,因为遮罩效果只能对CropNode的子节点生效。
代码改变为这样以后你看到的效果应该是这样:
跟上图对比后你会发现,妹子的图片的位置和大小等情况并没有改变,但是只有maskNode所在的范围被渲染了也就是可见,这也就是CropNode的作用了。
另外,很重要的一点,maskNode的担当节点必须本身存在可被渲染的颜色!!!!如果你在上文中将window节点的Color改为SKColor.clear的话,你会发现全屏黑色,实际上无任何渲染!重复!maskNode本身必须可以被渲染否则窗口无效。
现在你可以愉快的通过更改maskNode和backNode的位置来改变渲染区域了,举个栗子,代码和效果如下:
class GameScene: SKScene {
var windowNode:SKSpriteNode? = nil
var back:SKSpriteNode? = nil
var touchCount = 0
override func didMove(to view: SKView) {
let backnode = SKSpriteNode(texture: SKTexture(imageNamed: "9779.jpg"), color: SKColor.clear, size: self.frame.size)
backnode.anchorPoint = CGPoint.zero
backnode.position = CGPoint(x: 0, y: 0)
let crop = SKCropNode.init()
let window = SKSpriteNode(color: SKColor.black, size: CGSize(width: 200, height: 200))
window.anchorPoint = CGPoint.zero
window.position = CGPoint(x: 0, y: 0)
crop.maskNode = window
crop.addChild(backnode)
addChild(crop)
self.windowNode = window
self.back = backnode
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if self.touchCount % 4 == 0 {
self.back?.position = CGPoint(x: (self.back?.position.x)!, y: (self.back?.position.y)! - 200)
}else if self.touchCount % 4 == 1 {
self.windowNode?.position = CGPoint(x: (self.windowNode?.position.x)!, y: (self.windowNode?.position.y)! + 200)
}else if self.touchCount % 4 == 2 {
self.back?.position = CGPoint(x: (self.back?.position.x)!, y: (self.back?.position.y)! + 200)
}else if self.touchCount % 4 == 3 {
self.windowNode?.position = CGPoint(x: (self.windowNode?.position.x)!, y: (self.windowNode?.position.y)! - 200)
}
self.touchCount += 1
}
}
crop效果示意.gif
了解了CropNode的使用方法,现在可以根据上面我给出的层次图来构造地图了。基本原理就是给cropNode添加一个能够容纳17层地图平铺(0-16层,每层大小一样,个人推荐1X17排列,不然始终存在空间浪费而且移动坐标方便计算。)
class GameScene: SKScene {
var mapback:SKSpriteNode? = nil
override func didMove(to view: SKView) {
//创建背景
self.backgroundColor = SKColor.black
let backnode = SKSpriteNode(color: SKColor.white, size: CGSize(width: 414 * 17, height: 414))
backnode.anchorPoint = CGPoint.zero
backnode.position = CGPoint(x: 0, y: 736 - 414)
self.mapback = backnode
for i in 0...16 {
let color = UIColor.init(displayP3Red: CGFloat(arc4random()%255) / 255.0, green: CGFloat(arc4random()%255) / 255.0, blue: CGFloat(arc4random()%255) / 255.0, alpha: 1.0)
let map = SKSpriteNode(color: color, size: CGSize(width: 414, height: 414))
map.anchorPoint = CGPoint.zero
map.position = CGPoint(x: 414 * i, y: 0)
let name = "mapFloor\(i)" //重要!因为层级跳转时需要能够顺利拼接地图名来获取新地图
map.name = name
backnode.addChild(map)
}
let crop = SKCropNode.init()
let window = SKSpriteNode(color: SKColor.black, size: CGSize(width: 414, height: 414))
window.anchorPoint = CGPoint.zero
window.position = CGPoint(x: 0, y: 736-414)
crop.maskNode = window
crop.addChild(backnode)
addChild(crop)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if (self.mapback?.position.x)! <= -414 * 16 {
self.mapback?.position = CGPoint(x: 0, y: 736 - 414)
}else{
self.mapback?.position = CGPoint(x: (self.mapback?.position.x)! - 414, y: 736 - 414)
}
}
}
这样你就初步的建立起了一个可以自由切换的地图层级系统,之后我们就可以向上面添加各种元素了,今天的内容先到这里,下期见。
网友评论