用Swift做个游戏Lecture05 —— 真实的物理世界

系列:用Swift作个游戏
作者:pmst(1345614869)
微博:PPPPPPMST

友情提示:为了方便大家快速上手项目,我上传了课时的教程至github,请找到Code文件夹中->L05文件夹->FlappyBird-Start下载。

倘若你觉得文章还不错,请关注我并点击喜欢,这是对我写文章最大的鼓励。

游戏的雏形已经基本实现,呈现了背景,地面持续滚动,Player上下跳窜以及源源不断的仙人掌。不过细心的你也应当发现有以下几个不足:

  1. Player可以通过不断点击升高到屏幕外。
  2. 仙人掌表示不服:你丫想穿越我就穿越,当我是透明吗?

因此本节的任务是设置场景精灵的物理体,当课时完毕,Player一旦触碰到仙人掌就会下落,不能继续游戏。

01.设置场景内精灵的物体形状

暂且对游戏内容按下不表,先谈谈咱们真实的世界,重力加速度9.8g,非透明的物体之间碰撞会发生形变。而在Sprite Kit中的物理世界,首先我需要引出Physics Shapes —— 物体形状,就拿人来说,倘若我粗略地来形容一个人的物理体,我就会给出一个x*y*z(长宽高计算体积)的长方体,一旦外物触碰轮廓表面,我就说两者发生了接触;不过若已精确角度来说,形容人的物理体以其皮肤表面为轮廓勾勒出一个体积,显然这比先前的立方体来的精确太多了;当然有时候嫌麻烦,指定头部(姑且就当成一个球体吧)作为人的物理体,因此除头部外的身体都相当于是透明的,外物接触了手、腿等都不算发生接触,只有与头部接触才算。

讲了那么多,现在回到游戏,开始塑造真实的物理世界,首先找到didMoveToView()方法,在最上方添加一行代码设置场景物理世界的重力为(0,0),原因是我们打算使用自定义设置的参数:

override func didMoveToView(view: SKView) {
    physicsWorld.gravity = CGVector(dx: 0, dy: 0)
    //... 以下为早前内容
}

接着咱们要说physicsBody,译为物理体。我们可以设置每个节点的物理体,那样它就可以和其他同样设置了物理体的节点发生碰撞、检测接触等,它有三个属性,值均为UInt32类型:

  • categoryBitMask: 表明当前body属于哪个类别。
  • collisionBitMask: 当前物体可以与哪些类别发生碰撞。
  • contactTestBitMask:用于告知当前物体与哪些类别物理发生接触时。

游戏中类似这种,我们往往用二进制数来表示物体,譬如0b1表明是Player,0b10表明障碍物,0b100表明地面。想必编程男都不陌生吧。OK,请在enum Layer:CGFLoat{}下方新增一个结构体用于表明分类,注意里面均为类型属性:

struct PhysicsCategory {
  static let None: UInt32 = 0
  static let Player: UInt32 =     0b1 // 1
  static let Obstacle: UInt32 =  0b10 // 2
  static let Ground: UInt32 =   0b100 // 4
}

对于类型属性,调用方法形如:PhysicsCategory.None,更多关于类型属性,请参看官方文档Type properties一节。

接下来我们主要添加以下物理体到场景中:

  1. Player,这里我们将借助一个勾勒工具来绘制其物理体。
  2. 障碍物,同上。
  3. 地面,其实就是一条水平线。

为啥要设置以上三个物理体呢?因为设置完物理体后,我们才能知道谁和谁发生了接触contact,如此进行下一步计算。至于collision咱们是不关心的,不需要设置。

设置地面的物理体

找到setupBackground()方法 在方法最下方添加如下内容:

func setupBackground(){
    //...
    //===以上为早前内容===
    //===以下为新增内容===
   let lowerLeft = CGPoint(x: 0, y: playableStart)//地板表面的最左侧一点
   let lowerRight = CGPoint(x: size.width, y: playableStart) //地板表面的最右侧一点
   // 1
   self.physicsBody = SKPhysicsBody(edgeFromPoint: lowerLeft, toPoint: lowerRight)
   self.physicsBody?.categoryBitMask = PhysicsCategory.Ground
   self.physicsBody?.collisionBitMask = 0
   self.physicsBody?.contactTestBitMask = PhysicsCategory.Player
}

对于1中,我们用一条平行线来实例化物理体,然后是三部曲,分别设置了其分类为Ground;不予其他任何物理发生碰撞(因为设置了0);设置了能与其发生接触的物体有Player

设置Player的物理体

找到setupPlayer()方法 同样新增以下内容到方法最后:

func setupPlayer(){
   player.position = CGPointMake(size.width * 0.2, playableHeight * 0.4 + playableStart)
   player.zPosition = Layer.Player.rawValue
    // 注意我们将worldNode.addChild(player)移到了最下方。
    
    //=========以下为新增内容===========
   let offsetX = player.size.width * player.anchorPoint.x
   let offsetY = player.size.height * player.anchorPoint.y
   
   let path = CGPathCreateMutable()
   
   CGPathMoveToPoint(path, nil, 17 - offsetX, 23 - offsetY)
   CGPathAddLineToPoint(path, nil, 39 - offsetX, 22 - offsetY)
   CGPathAddLineToPoint(path, nil, 38 - offsetX, 10 - offsetY)
   CGPathAddLineToPoint(path, nil, 21 - offsetX, 0 - offsetY)
   CGPathAddLineToPoint(path, nil, 4 - offsetX, 1 - offsetY)
   CGPathAddLineToPoint(path, nil, 3 - offsetX, 15 - offsetY)
   
   CGPathCloseSubpath(path)
   
   player.physicsBody = SKPhysicsBody(polygonFromPath: path)
   player.physicsBody?.categoryBitMask = PhysicsCategory.Player
   player.physicsBody?.collisionBitMask = 0
   player.physicsBody?.contactTestBitMask = PhysicsCategory.Obstacle | PhysicsCategory.Ground
   
   worldNode.addChild(player)// hey 我现在在这里!!!!
}

我们通过绘制路径来勾勒出Player的自定义物理体,别吃惊,我只不过借助了某些工具,地址在这里,ps:可能需要翻墙。

设置仙人掌的物理体

同理我们只需要在产生仙人掌的实例方法中添加其物理体即可,请定位到createObstacle()->SKSpriteNode方法:

func createObstacle() -> SKSpriteNode {
  let sprite = SKSpriteNode(imageNamed: "Cactus")
  sprite.zPosition = Layer.Obstacle.rawValue
  
  //========以下为新增内容=========
  let offsetX = sprite.size.width * sprite.anchorPoint.x
  let offsetY = sprite.size.height * sprite.anchorPoint.y

  let path = CGPathCreateMutable()

  CGPathMoveToPoint(path, nil, 3 - offsetX, 0 - offsetY)
  CGPathAddLineToPoint(path, nil, 5 - offsetX, 309 - offsetY)
  CGPathAddLineToPoint(path, nil, 16 - offsetX, 315 - offsetY)
  CGPathAddLineToPoint(path, nil, 39 - offsetX, 315 - offsetY)
  CGPathAddLineToPoint(path, nil, 51 - offsetX, 306 - offsetY)
  CGPathAddLineToPoint(path, nil, 49 - offsetX, 1 - offsetY)

  CGPathCloseSubpath(path)
  
  sprite.physicsBody = SKPhysicsBody(polygonFromPath: path)
  sprite.physicsBody?.categoryBitMask = PhysicsCategory.Obstacle
  sprite.physicsBody?.collisionBitMask = 0
  sprite.physicsBody?.contactTestBitMask = PhysicsCategory.Player
  
  return sprite
}

注意到不管是哪种方式设置物理体,我们都需要设置其分类,碰撞掩码以及测试接触掩码,不过这里我们并不需要碰撞,所以全部设为0,即None。

最后请点击运行,你会发现场景中的Player仙人掌以及地面表层都有一层轮廓。没错!这就是其各自的物理体。我们在GameViewCOntroller中通过设置了skView.showsPhysics = true来显示的。

下文我将更新如何处理物体与物体之间的接触事件。 倘若觉得文章不错,点击喜欢或者关注我吧。.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容