美文网首页ios小游戏程序员iOS Developer
SpriteKit学习笔记22-做个台球游戏吧

SpriteKit学习笔记22-做个台球游戏吧

作者: 叶流月 | 来源:发表于2017-09-04 16:12 被阅读124次
    台球效果示意.gif

    主要利用的就是物理模拟系统,调整球体的重量、摩擦力、线性阻力和弹性系数来让表现尽量拟合台球的实际情况,另外一个重点就是操作方式的设定。

    如果你看过我关于物理世界的前序文章那么本文应该很容易理解,下面开始coding。

        override func didMove(to view: SKView) {
            //建立物理世界
            self.backgroundColor = SKColor.white
            self.physicsBody = SKPhysicsBody(edgeLoopFrom: CGRect(x: 15, y: 15, width: self.view!.frame.size.width - 30, height: self.view!.frame.size.height - 30))
            let backnode = SKSpriteNode.init(color: SKColor.init(colorLiteralRed: 153.0/255.0, green: 204.0/255.0, blue: 51.0/255.0, alpha: 1.0), size: CGSize(width: self.view!.frame.size.width - 30, height: self.view!.frame.size.height - 30))
            backnode.anchorPoint = CGPoint.zero
            backnode.position = CGPoint(x: 15, y: 15)
            addChild(backnode)
            self.physicsWorld.gravity = CGVector(dx: 0.0, dy: 0.0)
            self.physicsWorld.contactDelegate = self
     
        }
    

    这里把重力设定为零,然后做出一个背景为绿色,距离各边都是15的举行作为物理世界主体(球桌),并将其摆好。

    第二步,放台球。我使用了阿里妈妈图标库里的球素材变换颜色做了这十个球,你有更合适的素材可以自行更换。主要设定为这样:

        func buildBall(textureName:String, position:CGPoint){
            let ball1 = SKSpriteNode(texture: SKTexture(imageNamed: textureName))
            ball1.position = position
            ball1.anchorPoint = CGPoint(x: 0.5, y: 0.5)
            ball1.physicsBody = SKPhysicsBody(circleOfRadius: 16, center: CGPoint(x: 0.5, y: 0.5))
            ball1.physicsBody?.isDynamic = true
            ball1.physicsBody?.restitution = 0.85
            ball1.physicsBody?.linearDamping = 0.90
            ball1.physicsBody?.friction = 0.20
            ball1.physicsBody?.angularDamping = 0.15
            ball1.physicsBody?.mass = 7.5
            ball1.physicsBody?.contactTestBitMask = 3
            ball1.physicsBody?.categoryBitMask = 1
            ball1.name = textureName
            self.BallsArr.add(ball1)
            let info = BallInfo.setBallInfo(Position: ball1.position, Name: textureName)
            self.LastPositionArr.add(info)
            if textureName == "球球 (0).png" {
                self.ball = ball1  //白球
            }
            addChild(ball1)
        }
    

    BallInfo是一个自建的Model,包含两个信息:对象的名字和对象的位置属性。BallsArr是一个NSMutableArray,用来保存所有的球类SKSpriteNode对象。原本是想直接保存CGPoint类型的位置属性的(Objective-C不能在array保存非对象成员但是swift可以),可是实现过程中感觉查序列很麻烦,干脆连名字一起封成一个数组反而省心一点。

    然后是设置球袋,这里我选在SKShapeNode来用贝塞尔曲线勾画,也可以用贴图,相应的球袋的物理体就根据贝塞尔曲线或者纹理来设置,代码如下:

            let path6 = UIBezierPath.init()
            path6.move(to: CGPoint(x: self.view!.frame.size.width - 15, y: self.view!.frame.size.height / 2 - 15))
            path6.addLine(to: CGPoint(x: self.view!.frame.size.width - 15, y: self.view!.frame.size.height / 2 + 15))
            path6.addArc(withCenter: CGPoint(x: self.view!.frame.size.width - 15, y: self.view!.frame.size.height / 2), radius: 15, startAngle:1.5, endAngle: -1.5, clockwise: true)
            
            
            let pocket6 = SKShapeNode(path: path6.cgPath)
            pocket6.physicsBody = SKPhysicsBody(polygonFrom: path6.cgPath)
            pocket6.fillColor = SKColor.black
            pocket6.strokeColor = SKColor.black
            pocket6.physicsBody?.isDynamic = false
            pocket6.physicsBody?.categoryBitMask = 3
            pocket6.physicsBody?.contactTestBitMask = 1
            addChild(pocket6)
    

    只放出一个,因为基本大同小异。
    注意这里将球的categoryBitMask设置为1,球袋的设置为3,碰撞比特码请看前序教程。

    针对碰撞代理,通过bitMask来判断碰撞体类型进而做出判断,代码如下:

        func didBegin(_ contact: SKPhysicsContact) {
            print("发生了碰撞")
            let bodyA = contact.bodyA
            let bodyB = contact.bodyB
            if ((bodyA.categoryBitMask == 1 && bodyB.categoryBitMask == 3) || (bodyA.categoryBitMask == 3 && bodyB.categoryBitMask == 1)) {
                print("碰撞了球袋")
                var disappearBall:SKPhysicsBody? = nil
                if bodyA.categoryBitMask == 1 {
                    bodyA.node?.removeFromParent()
                    disappearBall = bodyA
                }else{
                    bodyB.node?.removeFromParent()
                    disappearBall = bodyB
                }
                if disappearBall?.node?.name == "球球 (0).png" {
                    disappearBall?.node?.position = CGPoint(x: kwidth / 2, y: 100)
                    addChild(disappearBall!.node!)
                }else{
                    //可以考虑剔除已经进了的球的信息,to do list
                }
            }
        }
    

    最后是操控部分:

        override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
            if self.GameState == 0 {  //角度
                let touch = touches.first
                let ballPosition = self.ball?.position
                let touchPosition = touch!.location(in: (self.ball?.parent)!)
                let touchpoint = CGPoint(x: (ballPosition?.x)! - touchPosition.x, y:(ballPosition?.y)! - touchPosition.y)
                let ballPoint = self.ball!.position
                let arc = -touchpoint.x / touchpoint.y
                self.cue?.zRotation = atan(arc)
                let a4 = sqrt(1 / (touchpoint.x * touchpoint.x + touchpoint.y * touchpoint.y))
                let PointBaseBall = CGPoint(x: 150 * touchpoint.x * a4, y: 150 * touchpoint.y * a4)
                let truePoint = CGPoint(x: PointBaseBall.x + ballPoint.x, y: PointBaseBall.y + ballPoint.y)
                self.cue?.position = truePoint
                self.AnglePoint = CGPoint(x: 115 * touchpoint.x * a4, y: 115 * touchpoint.y * a4)
            } else if self.GameState == 1 {   //力度
                let touch = touches.first
                let touchpoint = touch!.location(in: self.ball!)
                self.HitPower = sqrt(touchpoint.x * touchpoint.x + touchpoint.y * touchpoint.y) //击球力度
                let ballPoint = self.ball!.position
                let cuePosition = CGPoint(x: ballPoint.x + self.AnglePoint.x / 115 * (HitPower + 100), y: ballPoint.y + self.AnglePoint.y / 115 * (HitPower + 100))
                self.cue?.position = cuePosition
            }
            
        }
        override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
            if self.GameState == 0 {
                self.GameState = 1
            }else if self.GameState == 1 {
                self.GameState = 2
                let cueAction = SKAction.move(to: CGPoint(x: (self.ball?.position.x)! + self.AnglePoint.x, y: (self.ball?.position.y)! + self.AnglePoint.y), duration: 0.15)
                self.cue?.run(cueAction, completion: {
                    self.cue?.isHidden = true
                    self.ball?.physicsBody?.applyImpulse(CGVector(dx: -self.AnglePoint.x * self.HitPower, dy: -self.AnglePoint.y * self.HitPower))
                })
            }
        }
    

    基本就是这样。如果你对物理系统理解比较透彻那么本文对你应该没什么难度,如果很多不明白的地方请关注本系列教程的前序文章,本文的代码可以点击我的github查看,本文到此结束。

    相关文章

      网友评论

        本文标题:SpriteKit学习笔记22-做个台球游戏吧

        本文链接:https://www.haomeiwen.com/subject/frejjxtx.html