失蹤人口迴歸
本篇教程要基於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