
Cocos2d-X là thư viện phát triển Game trên mobile đa nền tảng: Game có thể chạy trên các hệ điều hành Android, iOS, Windows Phone. Nắm vững cocos2d-x giúp bạn tự tin sáng tạo và phát triển nhiều thể loại game với hiệu ứng đẹp và bắt mắt. Khóa học lập trình game Cocos2d-X được thiết kế nhằm cung cấp cho học viên kiến thức đầy đủ và toàn diện về lập trình game mobile
Thực tế thì mình cũng chả phải là một lập trình viên chuyên game hay gì cả, chỉ là lần này mình có được nhận làm một project game ngắn hạn từ anh sếp và được recomend sử dụng Cocos Creator. Sau một thời gian không quá dài tiếp xúc với nó thì mình thấy nó cũng khá hay nên muốn chia sẻ cho mọi người về engine này
Theo như mình có được biết thì trước đây khi chưa có Cocos Creator thì mọi người vẫn sử dụng Cocos2d-x code bằng C++. Nhược điểm của thằng này là không có giao diện người lập trình phải tự tưởng tượng code và rồi chạy lên mới thấy được kết quả. Để khắc phục điều này, làm tăng hiệu suất và bắt kịp được với Unity, thì Cocos Creator đã được ra đời. Hiện tại thì thằng này chỉ hỗ trợ trên MAC và Window nên anh em nào dùng Ubuntu thì chắc sẽ hơi buồn.
Vậy Cocos2d-x là gì thì Cocos2d-x là 1 Engine hỗ trợ lập trình Game đa nền tảng : Mobile ( IOS, ANDROID, Blackberry, TIZEN, WP) Window, MacOS, HTML5,.. đại loại là đủ cả. Theo wikipedia thì tác giả của Cocos2d-x là một người trung quốc có tên là Zhe Wang.


2012, at Zynga. With Rolando Abarca and Zhe Wang discussing Cocos2d-x’s and cocos2d-iphone’s roadmaps
Hiện tại thì nó vẫn được những bạn láng giếng phát triển khá là mạnh và không ngừng hoàn thiện để trở thành Unity phiên bản free, nhưng chặng đường chắc vẫn còn khá dài.
Mình thì không thích dài dòng mà mình thấy các tốt nhất để học một cái gì là cứ cho vào làm luôn nó mới nhanh vỡ ra được, chứ cứ lý thuyết suông thì rất khó hình dung.

Đây là trang chủ để tải cocos creator : https://www.cocos.com/en/creator
Sau khi tải về và cài đặt (cài cái này khá là lâu nha) thì giao diện chính của creator sẽ như thế này

Giờ chúng ta sẽ đi luôn vào làm game đầu tiên như trong docs của cocos hướng dẫn. Dưới đây là nguồn assets làm game và game đã hoàn thành
Sau khi tải Assets ban đầu về chúng ta sẽ import nó vào creator, rất đơn giản chỉ cần kéo thả vào thôi

Phần Assets sẽ là nơi chứ các nguồn dữ liệu để ta dụng để tạo hình
Sau khi đã có Assets ta sẽ tạo các Scene. Scene là một bản bồ 2D và có thể mở chế độ 3D, giúp ta kéo tả, thay đổi tạo ra UI. Mỗi Scene sẽ là một màn hình kiểu như là trên web chúng ta sẽ có trang home, trang detail, trang about thì scene chính là các view đó. Ta có thể tạo nhiều scene và chuyển đổi giữa chúng trong game.

Thêm khác thực thể vào Scene

Ở đây ta có thể tùy kéo kéo chỉnh, thay đổi vị trí của các thực thể sao cho mình thấy ổn nhất. Có nhiều chế độ kéo thả



Ở trên scene ta chỉ cần nắm thế thôi là có thể kéo thả, thay đổi kích thước và dic chuyển khắp bản đồ
Thì ngoài việc kéo thả ở scene chúng ta cũng có thể thay đổi vị trí, thay đổi độ trong suốt, thêm các script, thêm animation và rất nhiều thức khác ở trong phần này

Đây khi khu vực để ta xác định được đâu là thành phần cha con của nhau hoặc thằng nào ở lớp trên, thằng nào ở lớp dưới.


thằng nào càng ở dưới tức là nó đang trên lớp cao nhất. Kiểu như ở trong css có thuộc tính z-index khi ta sử dụng position vậy. Càng ở dưới thì z-index càng cao và có thể che lớp thằng kia. Giống như cái ground đang che đi cả thằng PurpleMonster.
phần này dùng để tạo ra các animation đơn giảm chị cần thay đổi góc nghiêng hay thay đổi vị trí của thực thể. Đây là ví dụ về 1 animation nhảy


Bây giờ sau khi đã hiểu hết các phần và chức năng của nó chúng ta sẽ đi vào làm game đầu tiên nha
Đầu tiên chúng ta cần kéo các thành phần của game vào để có một cái UI tổng quát
Sau khi sử dụng hết các kiến thức ở trên để kéo thả ra một UI như trong hình, thì bây giờ công việc cần làm là add thêm các script để bắt sự kiện trong game. Tạo một folder scripts --> và new một file Player.js.
Tạo folder

Tạo file js
Player ta sẽ thấy một khung js có sẵn // Player.js cc.Class({ extends: cc.Component, properties: { // foo: { // // ATTRIBUTES: // default: null, // The default value will be used only when the component attaching // // to a node for the first time // type: cc.SpriteFrame, // optional, default is typeof default // serializable: true, // optional, default is true // }, // bar: { // get () { // return this._bar; // }, // set (value) { // this._bar = value; // } // }, }, // LIFE-CYCLE CALLBACKS: // onLoad () {}, start () { }, // update (dt) {}, }); // Player.js //... properties: { // main character's jump height jumpHeight: 0, // main character's jump duration jumpDuration: 0, // maximal movement speed maxMoveSpeed: 0, // acceleration accel: 0, }, //...PurpleMonster để lát nữa sẽ xử lý sự kiện trong file js này. Quay về màn hình vào phần Node Tree nãy mình đã nói, chọn thực thể PurpleMonster sau đó để ý sang phần properties của nó. Ta sẽ xuống button Add Component chọn Custom component và chọn Player
Player ở trên và ta sẽ điền các thông số vào đây.
// Player.js properties: { //... }, setJumpAction: function () { // jump up var jumpUp = cc.moveBy(this.jumpDuration, cc.v2(0, this.jumpHeight)).easing(cc.easeCubicActionOut()); // jump down var jumpDown = cc.moveBy(this.jumpDuration, cc.v2(0, -this.jumpHeight)).easing(cc.easeCubicActionIn()); // repeat return cc.repeatForever(cc.sequence(jumpUp, jumpDown)); },onLoad(). Điều này có tác dụng là nó sẽ chạy ngay sau khi thực thể lại được load ra// Player.js onLoad: function () { // initialize jump action this.jumpAction = this.setJumpAction(); this.node.runAction(this.jumpAction); },
cc.repeatForever sẽ giúp hành động xảy ra lặp lại mãi mãi. cc.sequence thực hiện hành động tuần tự. jumpUp, jumpDown là nhảy lên và nhảy xuống --> cứ như vậy cc.repeatForever(cc.sequence(jumpUp, jumpDown)) sẽ làm PurpleMonster nhảy mãi mãi và hàm được để ở onLoad() nên hàm sẽ được chạy ngay sau khi khi thực thể được load.
// Player.js setJumpAction: function () { //... }, onKeyDown (event) { // set a flag when key pressed switch(event.keyCode) { case cc.macro.KEY.a: this.accLeft = true; break; case cc.macro.KEY.d: this.accRight = true; break; } }, onKeyUp (event) { // unset a flag when key released switch(event.keyCode) { case cc.macro.KEY.a: this.accLeft = false; break; case cc.macro.KEY.d: this.accRight = false; break; } },onKeyDown là khi ta nhấm phím, cònonKeyUp là khi ta nhả phím ra. Sẽ có 1 switch ở trong để kiểm tra xem ta bấm phím nào ở keydown và nhả phím nào ở keyup. Sau đó set hướng cho nó ở keydown cho hướng đấy thành true và khi nhả phím thì set lại thành false chỉ đơn giản vậy thôi.
// Player.js onLoad: function () { // Initialize the jump action this.jumpAction = this.setJumpAction(); this.node.runAction(this.jumpAction); // Acceleration direction switch this.accLeft = false; this.accRight = false; // The main character's current horizontal velocity this.xSpeed = 0; // Initialize the keyboard input listening cc.systemEvent.on(cc.SystemEvent.EventType.KEY_DOWN, this.onKeyDown, this); cc.systemEvent.on(cc.SystemEvent.EventType.KEY_UP, this.onKeyUp, this); }, onDestroy () { // Cancel keyboard input monitoring cc.systemEvent.off(cc.SystemEvent.EventType.KEY_DOWN, this.onKeyDown, this); cc.systemEvent.off(cc.SystemEvent.EventType.KEY_UP, this.onKeyUp, this); },Khởi tạo lắng nghe bàn phím khi load và hủy lắng nghe khi thực thể đó bị phá hủy
// Player.js update: function (dt) { // update speed of each frame according to the current acceleration direction if (this.accLeft) { this.xSpeed -= this.accel * dt; } else if (this.accRight) { this.xSpeed += this.accel * dt; } // restrict the movement speed of the main character to the maximum movement speed if ( Math.abs(this.xSpeed) > this.maxMoveSpeed ) { // if speed reach limit, use max speed with current direction this.xSpeed = this.maxMoveSpeed * this.xSpeed / Math.abs(this.xSpeed); } // update the position of the main character according to the current speed this.node.x += this.xSpeed * dt; },Mình đã thử test thì thấy thằng update() này cứ 0,01 giây nó sẽ gọi một lần
update(dt) { console.log("Run in function update() time : ", dt); }
Tức là nó sẽ gọi hàm gần như liên tục
if (this.accLeft) { this.xSpeed -= this.accel * dt; } else if (this.accRight) { this.xSpeed += this.accel * dt; }Phần này sẽ kiểm tra xem người dùng đang bắt nhân vật chạy về hướng nào. Sau đó sẽ thay đổi vị trí nhân vật theo hướng đó bằng cách trừ hay công tọa đọ X với một tốc độ tăng theo thời gian. Nhưng để phòng trường hợp tăng tốc độ quá nhanh thì một phần giới hạn tốc độ được thiết lập ở ngay phía dưới.
if ( Math.abs(this.xSpeed) > this.maxMoveSpeed ) { // if speed reach limit, use max speed with current direction this.xSpeed = this.maxMoveSpeed * this.xSpeed / Math.abs(this.xSpeed); }Phần cuối của hàm l à set vị trí cho nhân vật
this.node.x += this.xSpeed * dt;Sau khi đã thực hiện xong cho nhân vật tiếp đến chúng ta sẽ thực hiện cho ngôi sao. Do ngôi sao sẽ xuất hiện và mất đi khi nhân vật chạm tới nó. Tức là nó sẽ được tạo ra và phá hủy đi liên lục do vậy ở đây ta sẽ động chạm đến một khái niệm Prefab. Prefab là những node những thực thể ta đã tạo ra nhưng cần sử dụng đi sử dụng lại nhiều lần, nó giống như khái niệm component ở trong các framework React hay Vue vậy.

// Star.js properties: { // When the distance between the star and main character is less than this value, collection of the point will be completed pickRadius: 0, },
Rồi sau khi đã add script cho nó ta biến nó thành 1 một Prefab bằng cách. Kéo nó từ Scene xuống ô Assets vậy là xong


Game.js// Game.js properties: { // this property quotes the PreFab resource of stars starPrefab: { default: null, type: cc.Prefab }, // the random scale of disappearing time for stars maxStarDuration: 0, minStarDuration: 0, // ground node for confirming the height of the generated star's position ground: { default: null, type: cc.Node }, // player node for obtaining the jump height of the main character and controlling the movement switch of the main character player: { default: null, type: cc.Node } },starPrefab ta để type là Prefab. bây giờ add script này cho thằng Canvas kéo lên phần Node Tree --> chọn Canvas --> rồi Properties chọn Add Component và chọn file script Game
// Game.js onLoad: function () { // obtain the anchor point of ground level on the y axis this.groundY = this.ground.y + this.ground.height/2; // this.ground.top may also work // generate a new star this.spawnNewStar(); }, spawnNewStar: function() { // generate a new node in the scene with a preset template var newStar = cc.instantiate(this.starPrefab); // put the newly added node under the Canvas node this.node.addChild(newStar); // set up a random position for the star newStar.setPosition(this.getNewStarPosition()); }, getNewStarPosition: function () { var randX = 0; // According to the position of the ground level and the main character's jump height, randomly obtain an anchor point of the star on the y axis var randY = this.groundY + Math.random() * this.player.getComponent('Player').jumpHeight + 50; // according to the width of the screen, randomly obtain an anchor point of star on the x axis var maxX = this.node.width/2; randX = (Math.random() - 0.5) * 2 * maxX; // return to the anchor point of the star return cc.v2(randX, randY); },Test nào

// Game.js spawnNewStar: function() { // ... // Staging a reference of Game object on a star component newStar.getComponent('Star').game = this; },// Star.js getPlayerDistance: function () { // judge the distance according to the position of the player node var playerPos = this.game.player.getPosition(); // calculate the distance between two nodes according to their positions var dist = this.node.position.sub(playerPos).mag(); return dist; }, onPicked: function() { // When the stars are being collected, invoke the interface in the Game script to generate a new star this.game.spawnNewStar(); // then destroy the current star's node this.node.destroy(); }, update: function (dt) { // judge if the distance between the star and main character is less than the collecting distance for each frame if (this.getPlayerDistance() < this.pickRadius) { // invoke collecting behavior this.onPicked(); return; } },Điểm sẽ bắt đầu từ 0 khi trò chơi bắt đầu. 1 điểm sẽ được thêm khi 1 sao được thu thập. Để hiển thị điểm số, trước tiên chúng ta nên tạo một node Label. Chọn Canvas trong Node Tree , nhấp chuột phải và chọn Create -> Create Renderer Nodes -> Node With Label. Một node label mới sẽ được tạo dưới node Canvas. Tiếp theo, chúng tôi sẽ sử dụng các bước sau để thiết lập node label này:
scorescore và thiết lập position thành (0, 180)String thành Score: 0Font Size là 50assets/mikado_outline_shadow(chú ý! Biểu tượng làFont của Label
// Game.js properties: { // ... // reference of score label scoreDisplay: { default: null, type: cc.Label } },Tiếp đến là khởi tạo điểm trong onLoad()
// Game.js onLoad: function () { // ... // initialize scoring this.score = 0; },Sau đó thêm một phương thức mới được đặt tên gainScore :
// Game.js gainScore: function () { this.score += 1; // update the words of the scoreDisplay Label this.scoreDisplay.string = 'Score: ' + this.score; },// Star.js onPicked: function() { // when the stars are being collected, invoke the interface in the Game script to generate a new star this.game.spawnNewStar(); // invoke the scoring method of the Game script this.game.gainScore(); // then destroy the current star's node this.node.destroy(); },Chọn Canvas nhìn sang Properties và để ý phần Game sẽ có các trường nhu sau

Đây chính là các properties ta đã định nghĩa trong file Game.js. Tiếp theo ta cần kéo các thành phần cần thiết vào đây.

Sau khi đã kéo các thành phần vào properties thì ta cần xóa node star trên Node Tree đi. Vì về sau ta sẽ generate các star bằng Prefab nên thằng node này sẽ không cần thiết nữa. Nếu không xóa đi nó sẽ bị hiện tượng lúc nào cũng có 2 ngôi sao và một ngôi sao không bao giờ biến mất như thế này. À tiện thể test xem kết quả luôn nhá

Xóa node star đi

Kết quả đã ok
Ta sẽ thiết lập thời gian xuất hiện của ngôi sao để làm sao từ khi ngôi sao được xuất hiện mà 60s sau chẳng hạn, mà nó không được thu thập thì sẽ game over. Vây đầu tiên cần thêm biến đếm thời gian
// Game.js onLoad: function () { // ... // initialize timer this.timer = 0; this.starDuration = 0; // generate a new star this.spawnNewStar(); // initialize scoring this.score = 0; },Sau đó thêm logic đặt lại bộ định thời vào cuối spawnNewStar phương thức, trong đó this.minStarDuration và this.maxStarDuration là các thuộc tính của Game được khai báo ở đầu. Chúng được sử dụng để quy định tỷ lệ ngẫu nhiên của thời gian sao:
// Game.js spawnNewStar: function() { // ... // reset timer, randomly choose a value according the scale of star duration this.starDuration = this.minStarDuration + Math.random() * (this.maxStarDuration - this.minStarDuration); this.timer = 0; },Thêm logic cập nhật bộ đếm thời gian và phán đoán vượt quá thời lượng cho phương thức update:
// Game.js update: function (dt) { // update timer for each frame, when a new star is not generated after exceeding duration // invoke the logic of game failure if (this.timer > this.starDuration) { this.gameOver(); return; } this.timer += dt; },Cuối cùng, thêm phương thức gameOver
// Game.js gameOver: function () { this.player.stopAllActions(); //stop the jumping action of the player node cc.director.loadScene('game'); }Để người chơi biết rằng ngồi sao sắp biến mất ta cần có cho nó một hiệu ứng bằng cách giảm opacity của nó xuống làm nó mờ dần đi.
// Star.js update: function() { // ... // update the transparency of the star according to the timer in the Game script var opacityRatio = 1 - this.game.timer/this.game.starDuration; var minOpacity = 50; this.node.opacity = minOpacity + Math.floor(opacityRatio * (255 - minOpacity)); }Game mà im lặng thì rất khô khan, bây giờ chúng ta sẽ thêm cho nó hiệu ứng âm thanh khi nó thực hiện các hành động.
Player.js và thêm thuộc tính tham jumpAudio// Player.js properties: { // ... // jumping sound effect resource jumpAudio: { default: null, type: cc.AudioClip }, },Sau đó viết lại phương thức setJumpAction chèn cuộc gọi lại để phát hiệu ứng âm thanh và phát âm thanh bằng cách thêm phương thức playJumpSound
// Player.js setJumpAction: function () { // jump up var jumpUp = cc.moveBy(this.jumpDuration, cc.v2(0, this.jumpHeight)).easing(cc.easeCubicActionOut()); // jump down var jumpDown = cc.moveBy(this.jumpDuration, cc.v2(0, -this.jumpHeight)).easing(cc.easeCubicActionIn()); // add a callback function to invoke other defined methods after the action is finished var callback = cc.callFunc(this.playJumpSound, this); // repeat unceasingly, and invoke callback to play sound after landing each time return cc.repeatForever(cc.sequence(jumpUp, jumpDown, callback)); }, playJumpSound: function () { // invoke sound engine to play the sound cc.audioEngine.playEffect(this.jumpAudio, false); },Thêm thuộc tính âm thanh ghi điểm vào Game.js
// Game.js properties: { // ... // scoring sound effect resource scoreAudio: { default: null, type: cc.AudioClip } },Sau đó chèn vào phương thức gainScore
// Game.js gainScore: function () { this.score += 1; // update the words of the scoreDisplay Label this.scoreDisplay.string = 'Score: ' + this.score.toString(); // play the scoring sound effect cc.audioEngine.playEffect(this.scoreAudio, false); },Tiếp đến làm tương tự như kéo cá thành phần, giờ ta sẽ kéo các file audio vào ô thuộc tính tương ứng.



Đã hoàn tất giờ chúng ta thử trả nghiệm thôi
Phần Build của nó rất đơn giản. Chọn Project -> chọn Build

Tiếp đén chọn Build và chờ thanh Build... hiện conpleted là được


Vậy là đã build xong giờ có thể test kết quả build bằng cách chọn Play
Để đơn giản hóa việc deploy chúng ta sẽ sử dụng surge: https://surge.sh/

npm install --global surgesau khi đã cài đặt ta sẽ vào thư mục mà vừa nãy ta build ra và mở terminal
Chạy lệnh surge

Sau đó thiết lập tên domain là xong

Và đây là kết quả : http://gamestar.surge.sh/
Đến đây chắc hẳn mọi người đã biết cách làm sao để làm những game đơn giản với Cocos Creator rồi. Còn nếu muốn tạo những game phức tạp hơn thì cần phải đọc docs và tham khảo từ những sản phẩm đi trước rất nhiều nữa. Cảm ơn các bạn đã quan tâm đến bàì viết, rất vui và hẹn gặp lại ở những bài viết tiếp theo.
Courses you may like