How to make a simply game with cocos2d-js
Giới thiệu Bài viết này tôi sẽ hướng dẫn các bạn thích làm game cách làm 1 game đơn giản nhưng khá thú vị bằng cocos2d-js. Với framework này bạn có thể chạy game đã làm trên cả HTML5, iOS và Android. Khi hoàn thành game bạn làm sẽ có giao diện như sau: Cài đặt Để cài đặt bạn truy cập vào ...
Giới thiệu
Bài viết này tôi sẽ hướng dẫn các bạn thích làm game cách làm 1 game đơn giản nhưng khá thú vị bằng cocos2d-js. Với framework này bạn có thể chạy game đã làm trên cả HTML5, iOS và Android. Khi hoàn thành game bạn làm sẽ có giao diện như sau:
Cài đặt
Để cài đặt bạn truy cập vào địa chỉ: http://www.cocos2d-x.org/download tại đây bạn có thể cài đặt cả framework, IDE cũng như các editor hỗ trợ khác. Chi tiết: http://cocos2d-x.org/docs/tutorial/framework/html5/parkour-game-with-javascript-v3.0/chapter1/en
Tạo project
Sau khi cài đặt xong framework bạn có thể tạo project mới bằng command sau: cocos new SlideAndSurvive -l js Lệnh này sẽ tạo 1 project tên là: SlideAndSurvive trong thư mục: SlideAndSurvive.
Resources
- Bạn có thể tự tạo các file resource(ảnh, font, âm thanh) nếu ko có thể tại về tại đây: https://github.com/aron-bordin/Slide-and-Survive/tree/master/res bạn copy các file này vào tư mục res trong project của bạn
- Tiếp theo bạn cần khai báo các resource này trong file src/resource.js
var res = { Square_png : 'res/bloco.png', Enemy_png : 'res/inimigo.png', TitleFont: 'res/fonts/Marker Felt.ttf', Music: 'res/fundo.mp3' }; var g_resources = [ res.Square_png, res.Enemy_png, res.TitleFont, res.Music ];
Main
Giờ thì bạn mở file main.js và gõ đoạn code sau vào:
cc.game.onStart = function(){ cc.view.setDesignResolutionSize(800, 450, cc.ResolutionPolicy.SHOW_ALL); cc.view.resizeWithBrowserSize(true); //load resources cc.LoaderScene.preload(g_resources, function () { cc.director.runScene(new GameScene());//run the GameScene }, this); }; cc.game.run();
Đoạn code trên đầu tiên set độ phân giải của game là: 800×450, nhưng bạn đừng bận tâm vì cocos2d sẽ tự động resize tùy theo thiết bị. Tiếp theo đó là set giá trị cho phép trình duyệt resize game theo kích cỡ thích hợp. Tiếp nữa ta load các resource đã khai báo ở trên và cuối cùng là chạy Scene chính của game: GameScene.
Game Scene
Đầu tiên bạn gõ đoạn code sau vào file src/app.js
var Objs = { //global objects EnemiesDirection: [], Enemies: [], Square: null, Title: null, gameTime: null, gameTimeInfo: null, gameTimeTotal: null, gameBestInfo: null, gameBestValue: null, gameInfo1: null, gameInfo2: null, soundInfo: null } var timePlayed = 0; //game time var isAlive = false; //is the game is running function moveSquare(destination){ //move the green square to destination } function gameStart(){//game start, hide texts } function gameOver(){//game over, check score } var GameLayer = cc.Layer.extend({//main scene ctor:function () { }, update: function(dt){//update callback, run every frame }, checkCollision: function(){ }, onTouchBegan: function(touch, event){//touchbegan callback }, onTouchMoved: function(touch, event){//touchmoved callback }, addTexts: function(){//add the texts to the screen }, SoundClicked: function(){ }, addSquares: function(){//add the squares to the scene }, generateDirection: function(){//generate a random direction } }); var GameScene = cc.Scene.extend({//create the scene and start the game onEnter:function () { this._super(); var layer = new GameLayer(); this.addChild(layer); } });
Đoạn code này chủ yếu định nghĩa 1 số hàm và biến, chi tiết hơn các hàm và biến này chúng ta sẽ cùng tìm hiểu.
- Constructor: Hàm tạo chủ yếu đăng ký event: touch, move và gọi các hàm khác để tạo text, các hình vuông và tạo random các hướng di chuyển của các hình vuông này
// 1. super init first this._super(); var eventListener = cc.EventListener.create({//event listener event: cc.EventListener.TOUCH_ONE_BY_ONE, //one click swallowTouches: true, //is onTouch return true, stop event propagation onTouchBegan: this.onTouchBegan, //callbacks onTouchMoved: this.onTouchMoved}); this.addSquares();//add enemies square this.addTexts(); //add texts cc.eventManager.addListener(eventListener, Objs.Square);//start the event listener for(var i = 0; i < 4; i++) Objs.EnemiesDirection[i] = this.generateDirection();//generate a random movement direction this.scheduleUpdate();//runs update() every frame }
- function addSquares Phần này load các file ảnh của các hình vuông từ resources ra và set vị trí ban đầu cho chúng
addSquares: function(){//add the squares to the scene Objs.Square = cc.Sprite.create(res.Square_png); Objs.Square.setPosition(cc.p(400,225)); Objs.Square.setTag(1); this.addChild(Objs.Square); var En = Objs.Enemies; for(var i = 0; i < 4; i++) { En[i] = cc.Sprite.create(res.Enemy_png); this.addChild(En[i]); } En[0].setPosition(cc.p(100, 100)); En[1].setPosition(cc.p(700, 100)); En[1].setScaleX(1.7); En[1].setScaleY(0.4); En[2].setPosition(cc.p(700, 350)); En[2].setScaleX(0.4); En[2].setScaleY(1.5); En[3].setPosition(cc.p(100, 350)); En[3].setScale(0.8); Objs.Enemies = En; }
- function addTexts Thêm các đoạn text và menu lên màn hình
addTexts: function(){//add the texts to the screen var bestTime = localStorage.getItem("bestTime");//load the best time from localStorage Objs.Title = cc.LabelTTF.create("Slide & Survive", res.TitleFont, 40); Objs.Title.setPosition(cc.p(400, 350)); this.addChild(Objs.Title); Objs.gameTime = cc.LabelTTF.create("0.000", res.TitleFont, 20); Objs.gameTime.setPosition(cc.p(50, 10)); this.addChild(Objs.gameTime); Objs.gameTimeInfo = cc.LabelTTF.create("Time: ", res.TitleFont, 26); Objs.gameTimeInfo.setPosition(cc.p(200, 225)); this.addChild(Objs.gameTimeInfo); Objs.gameTimeTotal = cc.LabelTTF.create("0.000", res.TitleFont, 26); Objs.gameTimeTotal.setPosition(cc.p(300, 225)); this.addChild(Objs.gameTimeTotal); Objs.gameBestInfo = cc.LabelTTF.create("Best time: ", res.TitleFont, 26); Objs.gameBestInfo.setPosition(cc.p(540, 225)); this.addChild(Objs.gameBestInfo); //check if there is a bestTime, if not set the default as 0 Objs.gameBestValue = cc.LabelTTF.create(bestTime ? parseFloat(bestTime).toFixed(3) : "0.000", res.TitleFont, 26); Objs.gameBestValue.setPosition(cc.p(650, 225)); this.addChild(Objs.gameBestValue); Objs.gameInfo1 = cc.LabelTTF.create("Move the green square avoiding contact with the red ones!", res.TitleFont, 20); Objs.gameInfo1.setPosition(cc.p(400, 310)); this.addChild(Objs.gameInfo1); Objs.gameInfo2 = cc.LabelTTF.create("Are you able to do it???", res.TitleFont, 20); Objs.gameInfo2.setPosition(cc.p(440, 290)); this.addChild(Objs.gameInfo2); var useSound = localStorage.getItem("Sound"); if(useSound == 1) { cc.audioEngine.playMusic(res.Music, true); } else { if(useSound != 0) { localStorage.setItem("Sound", 1); useSound = 1; } } Objs.soundInfo = cc.MenuItemFont.create(useSound == 1 ? "Disable sound" : "Enable Sound", this.SoundClicked, this); Objs.soundInfo.setFontSize(20); var Menu = cc.Menu.create(Objs.soundInfo); Menu.setPosition(680, 20); this.addChild(Menu); },
- function generateDirection tạo ra các hướng bay ngẫu nhiên cho các khối hình
generateDirection: function(){//generate a random direction var i = Math.floor((Math.random() * 3)); var v = 7; switch (i){ case 0: return cc.p(v, v); case 1: return cc.p(-v, v); case 2: return cc.p(-v, -v); case 3: return cc.p(v, -v); } return cc.p(0, 0); }
- function update Đây là hàm chính được gọi mỗi lần update frame vì vậy đây sẽ là nơi xử lý các logic chính của game:
update: function(dt){//update callback, run every frame if(!isAlive)//if is not running, stop return; timePlayed += dt; //add dt to game time Objs.gameTime.setString(timePlayed.toFixed(3));//update game time label var size = cc.director.getWinSize();//get win size for(var i = 0; i < 4; i++){//move the enemies var pos = Objs.Enemies[i].getPosition(); if((pos.x <= 0) || (pos.x >= size.awidth))//the enemy position will be relative with his direction rect Objs.EnemiesDirection[i] = cc.p(Objs.EnemiesDirection[i].x * -1, Objs.EnemiesDirection[i].y) if((pos.y <= 0) || (pos.y >= size.height)) Objs.EnemiesDirection[i] = cc.p(Objs.EnemiesDirection[i].x, Objs.EnemiesDirection[i].y * -1) Objs.Enemies[i].setPosition(cc.pAdd(Objs.EnemiesDirection[i], pos)); } this.checkCollision();//check collisionss },
- function checkCollision để check va chạm và xử lý game over
checkCollision: function(){ //create a rect to represent our green square var rectHero = cc.rect(Objs.Square.getPositionX() - Objs.Square.getContentSize().awidth/2*Objs.Square.getScaleX(), Objs.Square.getPositionY() - Objs.Square.getContentSize().height/2*Objs.Square.getScaleY(), Objs.Square.getContentSize().awidth*Objs.Square.getScaleX(), Objs.Square.getContentSize().height*Objs.Square.getScaleY()); for(var i =0; i < 4; i++){ //create a rect for each enemy var rectEnemy = cc.rect(Objs.Enemies[i].getPositionX() - Objs.Enemies[i].getContentSize().awidth/2*Objs.Enemies[i].getScaleX(), Objs.Enemies[i].getPositionY() - Objs.Enemies[i].getContentSize().height/2*Objs.Enemies[i].getScaleY(), Objs.Enemies[i].getContentSize().awidth*Objs.Enemies[i].getScaleX(), Objs.Enemies[i].getContentSize().height*Objs.Enemies[i].getScaleY()); if(cc.rectIntersectsRect(rectHero, rectEnemy)) {//check collision gameOver();//if ok, gameover return; } } },
- function SoundClicked hàm này để chạy file âm thanh khi click menu
SoundClicked: function(){ var enabled = localStorage.getItem('Sound'); if(enabled == 1){ cc.audioEngine.stopMusic(true); localStorage.setItem('Sound', 0); Objs.soundInfo.setString('Enable sound'); } else { cc.audioEngine.playMusic(res.Music, true); localStorage.setItem('Sound', 1); Objs.soundInfo.setString('Disable sound'); } },
- event onTouchBegan Event này xử lý khi click lần đầu, ta sẽ cho start game
onTouchBegan: function(touch, event){//touchbegan callback var target = event.getCurrentTarget(); var PosInScreen = target.convertToNodeSpace(touch.getLocation()); var Size = target.getContentSize(); var rect = cc.rect(0, 0, Size.awidth, Size.height); if(cc.rectContainsPoint(rect, PosInScreen)){ //check if i'm clicking in the green square switch(target.getTag()){ case 1: if(!isAlive){//if the game is not running gameStart();//start it moveSquare(cc.p(400, 225));//make sure to start the game at the center of the screen } return true; } } return false; },
- event onTouchMoved Event này xảy ra khi ta di chuột, ta sẽ di chuyển khối hình chính theo vị trí di
onTouchMoved: function(touch, event){//touchmoved callback var target = event.getCurrentTarget(); var PosInScreen = target.convertToNodeSpace(touch.getLocation()); var Size = target.getContentSize(); var rect = cc.rect(0, 0, Size.awidth, Size.height); if(!isAlive)//if is not running, go away return; if(cc.rectContainsPoint(rect, PosInScreen)){//check if clicked in the green square switch(target.getTag()){ case 1: moveSquare(touch._point);//move the square return true; } } return false; },
- function moveSquare Hàm này sẽ thực hiện việc di chuyển khối hình chính
function moveSquare(destination){ //move the green square to destination var size = cc.director.getWinSize(); if((destination.x > 0 ) && (destination.x < size.awidth)) //check if square is inside the screen if((destination.y > 0) && (destination.y < size.height)) Objs.Square.setPosition(destination); //if ok, move it }
- function gameStart Hàm này xử lý start game, bắt đầu cho di chuyển các khối hình và bắt đầu tính thời gian
function gameStart(){//game start, hide texts isAlive = true; timePlayed = 0; Objs.Title.setVisible(false); Objs.gameTimeInfo.setVisible(false); Objs.gameTimeTotal.setVisible(false); Objs.gameBestInfo.setVisible(false); Objs.gameBestValue.setVisible(false); Objs.gameInfo1.setVisible(false); Objs.gameInfo2.setVisible(false); }
- function gameOver Hàm cuối cùng này xử lý khi có va chạm và gameOver, ta sẽ hiện thông báo và dừng game.
function gameOver(){//game over, check score isAlive = false; Objs.Title.setVisible(true); Objs.gameTimeInfo.setVisible(true); Objs.gameTimeTotal.setVisible(true); Objs.gameBestInfo.setVisible(true); Objs.gameBestValue.setVisible(true); Objs.gameInfo1.setVisible(true); Objs.gameInfo2.setVisible(true); Objs.gameTimeTotal.setString(timePlayed.toFixed(3)); Objs.Square.setPosition(cc.p(400, 225));//move enemies to default location Objs.Enemies[0].setPosition(cc.p(100, 100)); Objs.Enemies[1].setPosition(cc.p(700, 100)); Objs.Enemies[2].setPosition(cc.p(700, 350)); Objs.Enemies[3].setPosition(cc.p(100, 350)); var bestTime = parseFloat(Objs.gameBestValue.getString()); //get best time if(timePlayed > bestTime){ //check the game time localStorage.setItem('bestTime', timePlayed); //if is a new best time, save it Objs.gameBestValue.setString(timePlayed.toFixed(3)); //and show it } }
Kết
Vậy là chúng ta đã hoàn thành game, chúc các bạn thành công