您的位置:1010cc时时彩经典版 > 操作系统 > 1010cc时时彩经典版游戏中小球与反弹棒的碰撞,

1010cc时时彩经典版游戏中小球与反弹棒的碰撞,

发布时间:2019-08-14 12:58编辑:操作系统浏览(152)

    小球与反弹棒结束碰撞

    接下来是碰撞结束的处理:

    -(BOOL)ccPhysicsCollisionPreSolve:(CCPhysicsCollisionPair *)pair ball:(CCNode *)ball stick:(CCNode *)stick{
        CCPhysicsBody *phyBall = ball.physicsBody;
    
        CGPoint velocity = phyBall.velocity;
    
        NSInteger neg = arc4random_uniform(2);
        NSInteger rnd = arc4random_uniform(500);
    
        //当小球碰到反弹棒时,给小球一个随机冲力
        if (neg == 0) {
            velocity.x  = rnd;
        }else
            velocity.x -= rnd;
    
        [phyBall applyImpulse:ccpMult(velocity, 1.3)];
        return YES;
    }
    

    方法代码不长,但涉及到一些物理对象的操作,有必要详细说一下:

    首先取得小球的物理对象,然后随机生成小球的一个冲力,然后将冲力应用到小球上.That’s all!

    小球和砖块的碰撞:碰撞结束

    我们希望在小球与砖块碰撞时增加它的反弹力,所以我们要调整小球的力矩,而调整物理对象的力矩不能再CollisionBegin中完成,否则物理引擎会抱怨了 ;[ ,我们自然将其放到碰撞结束部分中:

    -(BOOL)ccPhysicsCollisionPreSolve:(CCPhysicsCollisionPair *)pair ball:(CCNode *)ball
                                brick:(CCNode *)brick{
        CGPoint velocity = ball.physicsBody.velocity;
        //略微增加小球的力矩
        [ball.physicsBody applyImpulse:ccpMult(velocity, 1.00005)];
        return YES;
    }
    

    注意我增加了一个特别微小的力矩值,因为力矩会在多次碰撞后快速累积,很快就会变得非常大,毕竟你也不想看到小球经过几次反弹后超过光速吧 ;)

    现在回到GameScene.m中,我们所有的碰撞处理就放在该类中.我们首先要搞清楚什么会和...

    变长的反弹棒

    我们想实现如下功能:在掉落变长星之后,如果反弹棒接到它,则使反弹棒的长度变为原先的2倍.

    看似很简单,但实际有一个问题.你不能仅仅延长反弹棒精灵纹理的长度,因为你在这个游戏中使用的是物理引擎,反弹棒的物理对象的大小是不可以在游戏运行中随意变化的.

    所以我们需要想办法延长反弹棒的物理大小,当然同时也要延长其精灵帧的大小,这样才能相互配合达到逼真的延长效果.

    这里本猫使用偷梁换柱的方法,用Ai制作一个延长后的反弹棒,并调整它的物理对象适应新的长度,然后在反弹棒需要变长时,用新长棒代替原来的短棒.

    首先用Ai制作一个长的反弹棒.然后在SpriteBuilder中创建一个StickLonger.ccb文件,设置好其物理对象的边界:

    1010cc时时彩经典版 1

    小球 Ball

    class Ball {
      constructor (_main) {
        let b = {
          x: _main.ball_x,                      // x 轴坐标
          y: _main.ball_y,                      // y 轴坐标
          w: 18,                                // 图片宽度
          h: 18,                                // 图片高度
          speedX: 1,                            // x 轴速度
          speedY: 5,                            // y 轴速度
          image: imageFromPath(allImg.ball),    // 图片对象
          fired: false,                         // 是否运动,默认静止不动
        }
        Object.assign(this, b)
      }
      move (game) {
        ...
      }
    }
    

    小球类:其大部分属性与挡板类似,主要通过 move 方法控制小球运动轨迹

    小球 Ball

    (NO.00004)iOS实现打砖块游戏(九):游戏中小球与反弹棒的碰撞

     


    前一篇博文介绍了物理对象中小球与砖块的碰撞处理,在这一篇中我们再来看一下小球与反弹棒碰撞发生的那点事 ;)

    小球和砖块的碰撞:碰撞开始

    我们知道一般物理引擎对于碰撞的处理不是一蹴而就的,它分为多个阶段.在Chipmunk中将碰撞按照时间的前后分为开始和后续两个部分;不是在所有的碰撞处理中都要关注这2个方法,一般只要关注开始碰撞的部分就可以了.但是在小球和砖块碰撞的处理中,我们还要关注后续的部分,因为有些代码不可以放在碰撞的开始部分.下面我们先来看看碰撞开始回调代码是如何写的:

    -(BOOL)ccPhysicsCollisionBegin:(CCPhysicsCollisionPair *)pair ball:(CCNode *)ball brick:(CCNode *)brick{
    
        [_userInterface updateHitsLabel:[NSString stringWithFormat:@Hits:%d,  _hits]];
    
        _score  = ((Brick*)brick).value * _scoreRatio;
        if (_scoreRatio > 1) {
            [self updateMsgLabel:[NSString stringWithFormat:@Score X %d,_scoreRatio]];
        }
        //小球没连续碰撞一次砖块,得分比率都会翻倍
        _scoreRatio *= 2;
    
        [_userInterface updateScoreLabel:[NSString stringWithFormat:@得分:%d,_score]];
    
        [brick removeFromParent];
    
        @synchronized(self){
            [_level removeFromBricks:brick];
        }
    
        return YES;
    }
    

    代码不长,首先更新了游戏场景中的撞击计数,然后按不同砖块的分值更新玩家的得分,然后将砖块从场景中删除.注意,最后调用了我们之前在Level类中实现的removeFromBricks方法,因为我们必须要更新关卡的数组.

    (NO.00004)iOS实现打砖块游戏(十二):伸缩自如,我是如意金箍棒(上)!

    大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处.
    如果觉得写的不好请告诉我,如果觉得不错请多多支持点赞.谢谢! hopy ;)


    通用的星星类已经完成了,下面我们来实现具体的变长和缩短道具.

    计分板 Score

    class Score {
      constructor (_main) {
        let s = {
          x: _main.score_x,                               // x 轴坐标
          y: _main.score_y,                               // y 轴坐标
          text: '分数:',                                 // 文本分数
          textLv: '关卡:',                               // 关卡文本
          score: 200,                                     // 每个砖块对应分数
          allScore: 0,                                    // 总分
          blockList: _main.blockList,                     // 砖块对象集合
          blockListLen: _main.blockList.length,           // 砖块总数量
          lv: _main.LV,                                   // 当前关卡
        }
        Object.assign(this, s)
      }
      // 计算总分
      computeScore () {
        ...
      }
    }
    

    分数类:会记录当前分数、关卡数,其中 computeScore 方法会在小球碰撞砖块且砖块血量为0时调用,并累加总分

    小球类:其大部分属性与挡板类似,主要通过 move 方法控制小球运动轨迹

    小球与反弹棒开始碰撞

    同样我们在碰撞中也要调整小球的力矩,所以也要分开处理,首先是碰撞开始时的处理:

    -(BOOL)ccPhysicsCollisionBegin:(CCPhysicsCollisionPair *)pair ball:(CCNode *)ball
                                                                    stick:(CCNode *)stick{
        //一旦小球碰到反弹棒恢复得分比率
        _scoreRatio = 1;
        return YES;
    }
    

    前面我们实现了当小球多次连续碰撞砖块时会增加得分比率,所以这里我们在小球碰撞到反弹棒时,要将得分比率重新复位.这就是他要干的全部工作.其实可以将其放到结束碰撞中去,然后完全去掉开始碰撞的处理.

    (NO.00004)iOS实现打砖块游戏(八):游戏中小球与砖块的碰撞

     


    现在回到GameScene.m中,我们所有的碰撞处理就放在该类中.我们首先要搞清楚什么会和什么碰撞.就目前来说,我们先来实现2中碰撞:小球和砖块的碰撞,以及小球和反弹棒的碰撞.

    扩展Star类

    最后我们在Star类中添加doStickLongerWork方法:

     (void)doStickLongerWork:(Stick *)stick{
    
        GameScene *gameScene = [GameScene sharedGameScene];
        CCPhysicsNode *physicsWorld = (CCPhysicsNode*)stick.parent;
    
        @synchronized(gameScene){
            if ([stick.name isEqualToString:@stickLonger]) {
                return;
            }
    
            if ([stick.name isEqualToString:@stickShorter]) {
                Stick *stickNormal = [Stick stickNormal];
                stickNormal.position = stick.position;
                [stick removeFromParent];
                //[physicsWorld removeChild:stick cleanup:YES];
                [physicsWorld addChild:stickNormal];
                gameScene.stickInGameScene = stickNormal;
                return;
            }
        }
    
        CGPoint position = stick.position;
    
        __block Stick *stickLonger;
    
        @synchronized(gameScene){
            stickLonger = [Stick stickLonger];
            //[physicsWorld removeChild:stick cleanup:YES];
            [stick removeFromParent];
            stickLonger.position = position;
            [physicsWorld addChild:stickLonger];
            stickLonger.visible = NO;
            gameScene.stickInGameScene = stickLonger;
    
    
            CCSprite *stickNode = (CCSprite*)[CCBReader load:@Elements/StickNode];
            stickNode.position = stickLonger.position;
            [gameScene addChild:stickNode z:50];
    
            CCActionScaleTo *longerAction = [CCActionScaleTo actionWithDuration:0.4f scaleX:2.0f scaleY:1.0f];
            CCActionCallBlock *blk = [CCActionCallBlock actionWithBlock:^{
                [stickNode removeFromParent];
                stickLonger.visible = YES;
            }];
            CCActionSequence *seq = [CCActionSequence actions:longerAction,blk,nil];
            [stickNode runAction:seq];
        }
    
        [stickLonger scheduleBlock:^(CCTimer *timer){
            @synchronized(gameScene){
                Stick *stickNormal = [Stick stickNormal];
                stickNormal.position = stickLonger.position;
                [stickLonger removeFromParent];
                [physicsWorld addChild:stickNormal];
                gameScene.stickInGameScene = stickNormal;
            }
        } delay:10];
    }
    

    你看到以上方法的第一感觉估计是:好长啊!其实内容很好理解,基本上它做了如下几件事:

    如果反弹棒已经变长了,则啥也不做返回 如果反弹棒处在变短状态,则恢复其原本大小.这里考虑到了其他可能改变反弹棒的叠加效果.在反弹棒变短的代码逻辑中,我们同样会考虑到这一点. 创建一个变长的反弹棒对象,但暂时将其隐藏,因为我们还想实现一个反弹棒由短变长的动画效果.因为这个动画很短,所以不会影响用户的操控.如果你希望更完美的实现用户操控的连续性,你可以将反弹棒的触摸处理临时”嫁接”到这个临时对象上,当然是有点麻烦,篇幅原因,这里只是点到为止. 在10秒之后将反弹棒恢复为原来大小

    变长道具制作完毕,我们现在编译运行app,看一下效果:

    1010cc时时彩经典版 2

     

    大熊猫猪侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好...

    入口函数 _main

    let _main = {
      LV: 1,                               // 初始关卡
      MAXLV: 3,                            // 最终关卡
      scene: null,                         // 场景对象
      blockList: null,                     // 所有砖块对象集合
      ball: null,                          // 小球对象
      paddle: null,                        // 挡板对象
      score: null,                         // 计分板对象
      ball_x: 491,                         // 小球默认 x 轴坐标
      ball_y: 432,                         // 小球默认 y 轴坐标
      paddle_x: 449,                       // 挡板默认 x 轴坐标
      paddle_y: 450,                       // 挡板默认 y 轴坐标
      score_x: 10,                         // 计分板默认 x 轴坐标
      score_y: 30,                         // 计分板默认 y 轴坐标
      fps: 60,                             // 游戏运行帧数
      game: null,                          // 游戏主要逻辑对象
      start: function () {
        let self = this
        /**
         * 生成场景(根据游戏难度级别不同,生成不同关卡)
         */
        self.scene = new Scene(self.LV)
        // 实例化所有砖块对象集合
        self.blockList = self.scene.initBlockList()
        /**
         * 小球
         */
        self.ball = new Ball(self)
        /**
         * 挡板
         */
        self.paddle = new Paddle(self)
        /**
         * 计分板
         */
        self.score = new Score(self)
        /**
         * 游戏主要逻辑
         */
        self.game = new Game(self.fps)
        /**
         * 游戏初始化
         */
        self.game.init(self)
      }
    }
    

    入口函数:实现了游戏中需要的所有类的实例化,并进行游戏的初始化

    作者: 弦云孤赫 原文链接:
    本文可以转载,但务必注明原作者和原出处。
    ps:博主在本次项目中使用了大量 es6 语法,故对于 es6 语法不太熟悉的小伙伴最好能先了解一些基本的原理再继续阅读。

    .
    ├─ index.html // 首页html
    │ 
    ├─ css // css样式资源文件
    ├─ images // 图片资源文件 
    └─ js
     ├─ common.js // 公共js方法
     ├─ game.js // 游戏主要运行逻辑
     └─ scene.js // 游戏场景相关类
    

    反弹棒的移动

    我们现在要实现反弹棒的移动功能,在GameScene.m中,添加移动触摸方法:

    -(void)touchBegan:(CCTouch *)touch withEvent:(CCTouchEvent *)event{
        //CCLOG(@touch began from GameScene);
    }
    
    
    -(void)touchMoved:(CCTouch *)touch withEvent:(CCTouchEvent *)event{
        CGPoint location = [[CCDirector sharedDirector] convertTouchToGL:touch];
    
        @synchronized(self){
            [self.stickInGameScene moveStickTo:location];
        }
    }
    

    编译运行app,现在游戏效果如下:

    1010cc时时彩经典版 3

    好了游戏初步有了一定可玩性,下一篇中我们回到关卡中让它不断生成更多的砖块. 下篇见 ;)

    前一篇博文介绍了物理对象中小球与砖块的碰撞处理,在这一篇中我们再来看一下...

    实现变长星道具

    回到Xcode中,在Stick类的接口文件中添加一个新属性:

     (instancetype)stickLonger;
    

    在Stick.m中实现该类方法:

     (instancetype)stickLonger{
        Stick *stick = (Stick*)[CCBReader load:@Elements/StickLonger];
        stick.name = @stickLonger;
        return stick;
    }
    

    可以看到除了stick对象发生了变化,其名称也和普通的stick有所区别.

    回到GameScene.m中在小球与砖块的碰撞处理中添加以下一句:

    [Star spawnStar:(Brick*)brick];
    

    接着我们要处理星星和反弹棒接触时的事件:

    -(BOOL)ccPhysicsCollisionBegin:(CCPhysicsCollisionPair *)pair star:(CCNode *)star
                             stick:(CCNode *)stick{
        //star形状是五角星,可能会在短时间内多次发生碰撞;但在star已经由第一次碰撞时删除掉从而导致star为nil
        //所以这里要确保star不为nil.
        //star删除也不能保证其一定为nil,所以增加判断其parent是否为nil.(2015-11-09 2006)
        StarType starType;
        @synchronized(self){
            if (!star || !star.parent) {
                return YES;
            }
            starType = ((Star*)star).starType;
            [star removeFromParentAndCleanup:YES];
        }
    
        switch (starType) {
            case starTypeStickLonger:
                @synchronized(self){
                    [self scheduleBlock:^(CCTimer *timer){
                        [Star doStickLongerWork:self.stickInGameScene];
                    } delay:0];
                }
                break;
            case starTypeUnknown:
            case starTypeMax:
                NSAssert(NO, @error star type!);
                break;
            default:
                break;
        }
    
        return YES;
    }
    

    为什么其中使用了同步伪指令呢?因为其中的代码会改变GameScene中反弹棒的状态,而该状态在GameScene中也可能同时被改变,所以我们需要将其做同步处理.

    砖块 Block

    class Block {
      constructor (x, y, life = 1) {
        let bk = {
          x: x,                                                                               // x 轴坐标
          y: y,                                                                               // y 轴坐标
          w: 50,                                                                              // 图片宽度
          h: 20,                                                                              // 图片高度
          image: life == 1 ? imageFromPath(allImg.block1) : imageFromPath(allImg.block2),     // 图片对象
          life: life,                                                                         // 生命值
          alive: true,                                                                        // 是否存活
        }
        Object.assign(this, bk)
      }
      // 消除砖块
      kill () {
        ...
      }
      // 小球、砖块碰撞检测
      collide (ball) {
        ...
      }
      // 计算小球、砖块碰撞后x轴速度方向
      collideBlockHorn (ball) {
        ...
      }
    }
    

    砖块类:会有两个属性不同,分别是 life 和 是否存活。然后,在小球和砖块撞击时,调用 kill 方法扣除当前砖块血量,当血量为0时,清除砖块。collide 方法检测小球与砖块是否碰撞,并返回布尔值

    class Score {
     constructor (_main) {
     let s = {
     x: _main.score_x,  // x 轴坐标
     y: _main.score_y,  // y 轴坐标
     text: '分数:',   // 文本分数
     textLv: '关卡:',  // 关卡文本
     score: 200,   // 每个砖块对应分数
     allScore: 0,   // 总分
     blockList: _main.blockList,  // 砖块对象集合
     blockListLen: _main.blockList.length, // 砖块总数量
     lv: _main.LV,   // 当前关卡
     }
     Object.assign(this, s)
     }
     // 计算总分
     computeScore () {
     ...
     }
    }
    

    游戏主逻辑 Game

    class Game {
      constructor (fps = 60) {
        let g = {
          actions: {},                                                  // 记录按键动作
          keydowns: {},                                                 // 记录按键 keycode
          state: 1,                                                     // 游戏状态值,初始默认为1
          state_START: 1,                                               // 开始游戏
          state_RUNNING: 2,                                             // 游戏开始运行
          state_STOP: 3,                                                // 暂停游戏
          state_GAMEOVER: 4,                                            // 游戏结束
          state_UPDATE: 5,                                              // 游戏通关
          canvas: document.getElementById("canvas"),                    // canvas 元素
          context: document.getElementById("canvas").getContext("2d"),  // canvas 画布
          timer: null,                                                  // 轮询定时器
          fps: fps,                                                     // 动画帧数,默认60
        }
        Object.assign(this, g)
      }
      ...
    }
    

    游戏核心类:这里包括游戏主要运行逻辑,包括但不限于以下几点

    • 绘制游戏整个场景
    • 调用定时器逐帧绘制动画
    • 游戏通关及游戏结束判定
    • 绑定按钮事件
    • 边界检测处理函数
    • 碰撞检测处理函数

    这里对游戏中需要绘制的挡板、小球、砖块、计分板都进行了实例化,并将游戏主要运行逻辑单独进行实例化

    挡板 Paddle

    class Paddle {
      constructor (_main) {
        let p = {
          x: _main.paddle_x,                                   // x 轴坐标
          y: _main.paddle_y,                                   // y 轴坐标
          w: 102,                                              // 图片宽度
          h: 22,                                               // 图片高度
          speed: 10,                                           // x轴移动速度
          ballSpeedMax: 8,                                     // 小球反弹速度最大值
          image: imageFromPath(allImg.paddle),                 // 引入图片对象
          isLeftMove: true,                                    // 能否左移
          isRightMove: true,                                   // 能否右移
        }
        Object.assign(this, p)
      }
      // 向左移动
      moveLeft () {
        ...
      }
      // 向右移动
      moveRight () {
        ...
      }
      // 小球、挡板碰撞检测
      collide (ball) {
        ...
      }
      // 计算小球、挡板碰撞后x轴速度值
      collideRange (ball) {
        ...
      }
    }
    

    挡板类:主要会定义其坐标位置、图片大小、x 轴位移速度、对小球反弹速度的控制等,再根据不同按键响应 moveLeft 和 moveRight 移动事件,collide 方法检测小球与挡板是否碰撞,并返回布尔值

    class Ball {
     constructor (_main) {
     let b = {
     x: _main.ball_x, // x 轴坐标
     y: _main.ball_y, // y 轴坐标
     w: 18, // 图片宽度
     h: 18, // 图片高度
     speedX: 1, // x 轴速度
     speedY: 5, // y 轴速度
     image: imageFromPath(allImg.ball), // 图片对象
     fired: false, // 是否运动,默认静止不动
     }
     Object.assign(this, b)
     }
     move (game) {
     ...
     }
    }
    

    前言

    首先,先说明一下做这个系列的目的:其实主要源于博主希望熟练使用 canvas 的相关 api ,同时对小游戏的实现逻辑比较感兴趣,所以希望通过这一系列的小游戏来提升自身编程能力;关于 es6 语法,个人认为以后 es6 语法会越来越普及,所以算是提前熟悉语法使用技巧。小游戏的实现逻辑上可能并不完善,也许会有一些 bug ,但是毕竟只是为了提升编程能力与技巧,希望大家不要太较真

    作为第一次分享,我选择打砖块这个逻辑不算太复杂的小游戏。同时,为了接近真实游戏效果,在游戏中也添加了关卡,砖块血量,以及物理碰撞模型的简略实现。其实关注游戏实现逻辑就好了

    线上演示地址:
    github地址:
    ps:github地址有代码演示,以及源码可供参考,线上演示地址可供预览

    先上一个游戏完成后的截图
    1010cc时时彩经典版 4

    游戏工程目录如下

    .
    ├─ index.html                   // 首页html
    │  
    ├─ css                          // css样式资源文件
    ├─ images                       // 图片资源文件  
    └─ js
       ├─ common.js                 // 公共js方法
       ├─ game.js                   // 游戏主要运行逻辑
       └─ scene.js                  // 游戏场景相关类
    

    PS:本次项目中使用了大量 es6 语法,故对于 es6 语法不太熟悉的小伙伴最好能先了解一些基本的原理再继续阅读。

    游戏实现逻辑

    这里对游戏中需要绘制的挡板、小球、砖块、计分板都进行了实例化,并将游戏主要运行逻辑单独进行实例化

    • 绘制游戏整个场景
    • 调用定时器逐帧绘制动画
    • 游戏通关及游戏结束判定
    • 绑定按钮事件
    • 边界检测处理函数
    • 碰撞检测处理函数

    本文由1010cc时时彩经典版发布于操作系统,转载请注明出处:1010cc时时彩经典版游戏中小球与反弹棒的碰撞,

    关键词:

上一篇:UIWebView的基本用法

下一篇:没有了