【Cocos Creator实战教程(8)】——打砖块(物理引擎)

失踪人口回归


这里写图片描述
本篇教程要基于Cocos Creator1.5的物理引擎,编写一个简单的打砖块游戏,尽可能多讲一点,但现在已经快11点了,我12点要睡觉啊,好像又讲不了多少,这个世界啊,本来就是一个矛盾体。

这里写图片描述

新建一个工程,取名叫做brick-breaker,brick是什么意思呢,就是砖块的意思,每次给工程起名字,我都能学会一个新单词。

目录结构如下:

这里写图片描述

game场景,设置Canvas
这里写图片描述

先搭一个游戏背景
这里写图片描述
(因为我已经做完了,我就不从头做一遍了,所以暂时用不到的节点我就把active关掉)

再建一个物理层,用来装游戏里的带有物理属性的东西,设置锚点为左下角
这里写图片描述

又到了学单词的时间,请拿出你们的小本本:

wall:墙//小球碰到就会反弹的那种墙
ground:地面//球碰到地面,这局游戏就结束了
brick_layout:砖块布局//这个单词我们之前讲过了就不讲了
ball:球//就是球
paddle:桨//这里特指那个可以控制移动的白色长方形

这个wall肯定是要有碰撞属性的,在属性面板,添加一个物理组件
这里写图片描述

因为我们的墙有上,左,右三面,所以再添加三个碰撞组件(一个节点可以有多个碰撞组件)。
这里写图片描述

编辑一下
这里写图片描述

地面同理,小球同理,托盘同理
这里写图片描述
(这里把地面和墙分开是为了后面墙和地面可能有不同的逻辑)

现在已经编辑了几个物理节点的碰撞包围盒,但还没有编辑他们的物理属性(cc.RigidBody)

先从小球开始,点击ball节点,在属性检查器可以看到

这里写图片描述

把第一个参数勾选,代表启用碰撞回调,可以在脚本里写回调函数

Bullet:高速运动的物体开启,避免穿透,这里不用勾选

type选择Dynamic,

static:不会受到力的影响,不会受到速度影响,指的是物理引擎,我们依然可以通过移动节点来改变位置
kinematic:不受力的影响,会受到速度影响
dynamic:受力影响,受速度影响
animated:据说和动画结合使用,我还没弄懂。。。

为什么不选kinematic呢?留个作业。

Gravity Scale设置为0(标准是1,数值代表比例),也就是没有重力。

设置线速度(1000,1000)

这里写图片描述
在下面的碰撞组件里,设置Friction (摩擦系数)等于0(没有摩擦力),Restitution(弹性系数)等于1(没有动量损耗)

因为小球是我们的主角,左右的碰撞都是对球来说的,所以碰撞属性都在小球这一方设置就可以了。

另外要设置wall,ground,paddle,brick的type为static
brick的tag为1,
ground的tag为2,
paddle的tag为3,
wall的tag位4

下面来看脚本

BrickLayout.js

cc.Class({
    extends: cc.Component,

    properties: {
        padding: 0,
        spacing: 0,
        cols: 0,
        brickPrefab: cc.Prefab,
        bricksNumber: 0,
    },

    init(bricksNumber) {
        this.node.removeAllChildren();
        this.bricksNumber = bricksNumber;
        for (let i = 0; i < this.bricksNumber; i++) {
            let brickNode = cc.instantiate(this.brickPrefab);
            brickNode.parent = this.node;
            brickNode.x = this.padding + (i % this.cols) * (brickNode.width + this.spacing) + brickNode.width / 2;
            brickNode.y = -this.padding - Math.floor(i / this.cols) * (brickNode.height + this.spacing) - brickNode.height / 2;
        }
    }
});

自己写了一个动态添加砖块的布局脚本,传入需要添加的砖块数量就可以动态加入的布局节点中。

BrickPrefab长这样,我就默认你会做prefab了
这里写图片描述

OverPanel.js

cc.Class({
    extends: cc.Component,

    properties: {
        resultLabel:cc.Label,
        scoreLabel:cc.Label,
    },

    // use this for initialization
    onLoad: function () {

    },

    init(gameCtl){
        this.gameCtl = gameCtl;
        this.node.active = false;
    },

    show(score,isWin){
        this.node.active = true;
        if(isWin){
            this.resultLabel.string = 'YOU WIN!';
        }else{
            this.resultLabel.string = 'YOU LOSE!';
        }
        this.scoreLabel.string = score+'';
    },

    onBtnRestart(){
        this.gameCtl.startGame();
    }
});

结束界面
这里写图片描述

Paddle.js

cc.Class({
    extends: cc.Component,

    onLoad: function () {
        this.node.parent.on("touchmove", (event) => {
            //将世界坐标转化为本地坐标
            let touchPoint = this.node.parent.convertToNodeSpace(event.getLocation());
            this.node.x = touchPoint.x;
        });
    },

    init(){
        this.node.x = 360;
    }

});

托盘随着手指移动

Ball.js

cc.Class({
    extends: cc.Component,

    properties: {

    },

    init(gameCtl) {
        this.gameCtl = gameCtl;
        this.node.position = cc.v2(360,270);//初始化位置
        this.getComponent(cc.RigidBody).linearVelocity = cc.v2(800,800);//初始化速度
    },

    onBeginContact(contact, self, other) {
        switch (other.tag) {
            case 1://球碰到砖块
                this.gameCtl.onBallContactBrick(self.node, other.node);
                break;
            case 2://球碰到地面
                this.gameCtl.onBallContactGround(self.node, other.node);
                break;
            case 3://球碰到托盘
                this.gameCtl.onBallContactPaddle(self.node, other.node);
                break;
            case 4://球碰到墙
                this.gameCtl.onBallContactWall(self.node, other.node);
                break;
        }
    },
});

球碰到其他物体,让gameCtl处理

GameCtl.js

const GameModel = require('GameModel');
cc.Class({
    extends: cc.Component,

    properties: {
        gameView: require('GameView'),
        ball: require('Ball'),
        paddle: require('Paddle'),
        brickLayout: require('BrickLayout'),
        overPanel: require('OverPanel'),
    },

    // use this for initialization
    onLoad: function () {
        //安卓返回键退出
        cc.systemEvent.on(cc.SystemEvent.EventType.KEY_DOWN, (event) => {
            if (event.keyCode === cc.KEY.back) {
                cc.director.end();
            }
        });
        this.physicsManager = cc.director.getPhysicsManager();
        this.gameModel = new GameModel();
        this.startGame();

    },

    //this.physicsManager.debugDrawFlags =0;
    // cc.PhysicsManager.DrawBits.e_aabbBit |
    // cc.PhysicsManager.DrawBits.e_pairBit |
    // cc.PhysicsManager.DrawBits.e_centerOfMassBit |
    // cc.PhysicsManager.DrawBits.e_jointBit |
    // cc.PhysicsManager.DrawBits.e_shapeBit
    // ; 

    init() {
        this.physicsManager.enabled = true;
        this.gameModel.init();

        this.gameView.init(this);
        this.ball.init(this);
        this.paddle.init();
        this.brickLayout.init(this.gameModel.bricksNumber);
        this.overPanel.init(this);

    },

    startGame() {
        this.init();
    },

    pauseGame() {
        this.physicsManager.enabled = false;
    },

    resumeGame() {
        this.physicsManager.enabled = true;
    },

    stopGame() {
        this.physicsManager.enabled = false;
        this.overPanel.show(this.gameModel.score, this.gameModel.bricksNumber === 0);
    },

    onBallContactBrick(ballNode, brickNode) {
        brickNode.parent = null;
        this.gameModel.addScore(1);
        this.gameModel.minusBrick(1);
        this.gameView.updateScore(this.gameModel.score);
        if (this.gameModel.bricksNumber <= 0) {
            this.stopGame();
        }
    },

    onBallContactGround(ballNode, groundNode) {
        this.stopGame();
    },

    onBallContactPaddle(ballNode, paddleNode) {

    },

    onBallContactWall(ballNode, brickNode) {

    },

    onDestroy() {
        this.physicsManager.enabled = false;
    }

});

GameCtl挂在Canvas上,保证第一个执行,将对应的组件拖入
这里写图片描述

GameView.js

cc.Class({
    extends: cc.Component,

    properties: {
        scoreLabel:cc.Label,
    },

    init(gameCtl){
        this.gameCtl = gameCtl;
        this.scoreLabel.string = '0';
    },

    updateScore(score){
        this.scoreLabel.string = score;
    }
});

GameModel.js

cc.Class({
    extends: cc.Component,

    properties: {
        score:0,
        bricksNumber:0,
    },

    init(){
        this.score = 0;
        this.bricksNumber = 50;
    },

    addScore(score){
        this.score += score;
    },

    minusBrick(n){
        this.bricksNumber -= n;
    },

});

尝试着写的mvc,并不规范,简单的理解就是,model和view分离,沟通都通过control。

逻辑清楚的代码是不需要过多讲解的,对吧,对的。

源码在此:https://github.com/potato47/brick-breaker-master

游戏试玩:http://119.29.40.244/brick-breaker/

关爱失踪人口:

这里写图片描述

《毕业前的程序员》系列正在更新。。。如果你不关注,你就会错过一个天才的成长历程。。。233


这里写图片描述

评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值