Tạo Game Nông Trại Vui Vẻ trên Mobile bằng HTML5

Bài viết này là bài viết đầu tiên, mình viết về Game mà lại tạo bằng HTML5 nữa chứ. Vì vậy, khó có thể tránh khỏi sai sót. Mong các bạn soi mói sai sót và comment ở dưới cho mình. Okey, let it go ! 😀

[icon type=”fa fa-angellist”] Giới thiệu về HTML5 + Game :

HTML5 trong vòng vài năm trở lại đây đã trở nên phổ biến trong nhiều lĩnh vực như Web Design, Mobile first, SEO, và nổi trội nhất là lĩnh vực phát triển trên nhiều nền tảng để hiển thị tốt trên điện thoại và dĩ nhiên vẫn đáp ứng tốt trên Desktop.

Vì độ phổ biến và “hot girl” như thế nên mình chia sẻ bài viết như một lời mời hay lời giới thiệu cho các amateurs và newbie hoặc các pro nghía qua và tìm hiểu nó.

Để bắt đầu Tutorial này, bạn cần phải biết chút xíu về (HTML, CSS and Javascript) và 1 IDE  + Debug trên 1 Web Browser là Oke.

Thế nếu không biết thì sao ???

A. Khỏi đọc, tốn time.

B. Ghé vô trang codeSchool mình đã giới thiệu tại đây.

Trong tutorial này, mình sẽ giới thiệu về cách tạo 1 game “Nông trại vui vẻ” đơn giản nhất mô phỏng lại game trên Zing chẳng hạn :D.

Chúng ta sẽ sử dụng 1 Open-Source FrameWork cho HTML5 là LIMEJS.

Đây là kết quả chúng ta sẽ làm ra :

 

[notification type=”alert-warning” close=”true” ]

Yêu cầu trình độ :

  • Biết chút chút HTML + JAVASCRIPT thuần.
  • Biết  xíu xíu về OOP trong  javascript. Ko biết thì đọc bài này sẽ biết.
  • Một IDE để code, hiện tại mình sài Sublime.
  • Biết chút ít về câu lệnh trên DOS.
[/notification]

[icon type=”glyphicon glyphicon-question-sign”] Game “Nông trại vui vẻ” là gì ?

Trong Tutorial này, mình sẽ làm cái game “siêu đơn giản” luôn :D. Nó chỉ cho phép người dùng :

  • Mua hạt giống, trong hạt giống có hiển thị thông tin về thời gian thu hoạch, giá mua hạt giống, giá tiền thu về khi thu hoạch.
  • Hiển thị số tiền hiện tại khi trồng.
  • Cho phép 3 trạng thái : Xới đất, trồng hạt giống, lên cây và thu hoạch.

LimeJS !!!

LimeJS là một bộ Javascript game framework chuyên dùng phát triển game trên nền HTML5 và chạy mượt trên các nền tảng điện thoại hiện nay như iOS, Android, Windows Phone. Vì sự phát triển của HTML5 ngày nay mà Adobe Flash ngày càng giảm thiểu “sân chơi” và nhường lại cho các HTML5 Webgame này.

Bạn có thể làm quen và tìm hiểu Framework này tại đây. Theo sự hướng dẫn và tra cứu thư viện trong phần Getting Started Guide.

Bắt đầu làm game nào :

Các thành phần cần chuẩn bị trong game :

  • Land ( đất phẳng).
  • Plowing the land ( đất bị cày bừa lên).
  • A shop to get crops ( Shop mua hạt giống).
  • Planting crops ( trồng cây).
  • Plants growing ( Mọc cây).
  • Harvesting! ( Thu Hoạch).

Các hình ảnh ấy, bạn có thể tải về tại đây.

[icon type=”fa fa-bug”] Game World  !

(1 kiểu của hello world :v )

Sau khi các bạn đã tải LimeJS về và cài đặt theo sự hướng dẫn từ trang chủ hoặc trang github, ta cần tạo 1 thư mục cho Project là farming-game

bin/lime.py create farming-game

Dòng này sẽ tạo ra thư mục tên là farming-game bên trong thư mục LimeJS mà bạn đã giải nén từ trước. Nếu bạn cài đặt LimeJS không lỗi lầm gì thì mặc định bạn sẽ tìm thấy trong thư mục đó 1 file tên là farming-game.htmlMở file đó lên và thêm vào đoạn script như bên dưới.

<style>
        .lime-scene {display:block !important;}    
        body {background-color:black;}
   </style>

Chức năng là tạo background màu đen cho game còn phần class lime-scene thành block và hiển thị riêng. Bây giờ, mở file farming-game.js và replace bằng đoạn code bên dưới.

//set main namespace 
goog.provide('farming');   
//get requirements 
goog.require('lime.Director');
goog.require('lime.Scene');
goog.require('lime.Layer');   
 
//entrypoint 
farming.start = function(){     
 
    //game object
    var gameObj = {
        width: 320,
        height: 480,
    }
 
    var director = new lime.Director(document.body,gameObj.width,gameObj.height);     
    director.makeMobileWebAppCapable();     
    director.setDisplayFPS(false);        
 
    var gameScene = new lime.Scene().setRenderer(lime.Renderer.CANVAS);
    var landLayer = new lime.Layer().setAnchorPoint(0, 0);
    var controlsLayer = new lime.Layer().setAnchorPoint(0, 0);
 
    gameScene.appendChild(landLayer);
    gameScene.appendChild(controlsLayer);
 
    director.replaceScene(gameScene); //To show you see the Scene
}

 

Đây là một đoạn code mẫu cho bạn biết cách khai báo 1 namespace ( ở đây là farming) và những yêu cầu để bắt đầu với LimeJS . Trong file html có thẻ body khai báo hàm “farming.start()” để cho biết game sẽ chạy ở lần đầu load trang.

Chúng ta có thể khai báo các đối tượng bằng bất kỳ tên nào ta muốn. Như trong đoạn code này có gameObj dùng để chứa các level trong game. Chúng ta sẽ sử dụng các biến này trong game liên tục vậy nên ta phải làm sao để các biến này có thể truy cập bất kỳ lúc nào ở bất cứ đâu trong game.

Trong đoạn code trên, bạn có thể thấy đối tượng “director” chính là vai trò trong game. Mỗi vai trò sẽ có một kịch bản riêng gọi là Scene. Game của chúng ta sẽ có 2 layers, một là landLayer sẽ là layer chứa land ( đất) và cây cối thu hoạch, controlsLayer chứa các trạng thái và những lựa chọn của người chơi ( mua hạt giống, số tiền còn lại …).

[icon type=”glyphicon-adjust”] Khu Vực Quản Trị

Như trong kết quả ta thấy, phần quản trị có 1 nút tên là Shop dùng để mua các hạt giống. Vậy ta nên include 1 file mới quy định về nút này.

goog.require('lime.GlossyButton');

Chúng ta có thể add thêm nhiều thuộc tính trong đối tượng gameObj về kích thước quy định, đại loại như bên dưới để có thể quản lý được tốt hơn và có thể tận dụng lại sau này thay vì “hard-code”.

//game object
var gameObj = {
    width: 320,
    height: 480,
    tile_size: 64,
    num_tiles_x: 5,
    num_tiles_y: 6,
    landLayer_w: 64*5,
    landLayer_h: 64*6,
    controlsLayer_w: 64*5,
    controlsLayer_h: 64*1.5,
    costPlowing: 5,
 
    //shop
    shop_margin_x: 50,
    shop_margin_y: 20
}
[notification type=”alert-info” close=”true” ]Lưu ý : Các bạn thực hiện lại nhớ lưu ý dấu cách trong các câu lệnh, nếu không có thể sẽ lỗi app này.[/notification]

Bây giờ, chúng ta tiếp tục tạo object cho player dùng để chứa tiền và số lần thu hoạch.

//player object
var playerObj = {
    money: 300,
    currentCrop: 0             
}

Tiếp theo, bạn add đoạn code bên dưới vào dưới đoạn này : “gameScene.appendChild(controlsLayer);

//controls area
var controlArea = new lime.Sprite().setAnchorPoint(0,0)
    .setPosition(0, gameObj.height-gameObj.controlsLayer_h)
    .setSize(gameObj.controlsLayer_w, gameObj.controlsLayer_h)
    .setFill('#0D0D0D')
controlsLayer.appendChild(controlArea);
 
//shop button
var shopButton = new lime.GlossyButton().setColor('#133242').setText('Shop')
    .setPosition(60, gameObj.height-gameObj.controlsLayer_h/2)
    .setSize(80, 40);
controlsLayer.appendChild(shopButton);
 
//money
var moneyLabel = new lime.Label().setText('$'+playerObj.money).setFontColor('#E8FC08')
    .setPosition(gameObj.controlsLayer_w-50, gameObj.height-gameObj.controlsLayer_h/2);
controlsLayer.appendChild(moneyLabel);

Nãy giờ là ta vẽ được hình bên dưới bằng cách sử dụng canvas trong bộ Framework LimeJS này !!!

[notification type=”alert-info” close=”true” ]Lưu ý : Nếu không ra kết quả như bên dưới, bạn nên kiểm tra lại lỗi chính tả hoặc dấu chấm phẩy hoặc thứ tự trong đoạn code của bạn. Cách để tìm lỗi tốt nhất là nhấn F12 để xem console báo lỗi bạn nhé.[/notification]

Land ( Đất )

Để dễ xử lý và bảo trì sau này về các chức năng liên quan đến đất, ta sẽ tạo thêm 1 file tên là “land.js”  và có đoạn code như bên dưới.

goog.provide('farming.Land');
goog.require('lime.Sprite');
 
/**
 * Land elements
 * 
 * @param {} gameObj
 */
farming.Land = function(gameObj, playerObj) {
    goog.base(this);
    this.setAnchorPoint(0, 0);
    this.setSize(gameObj.tile_size,gameObj.tile_size);
    this.setFill('images/bare_land.png');
}
 
goog.inherits(farming.Land,lime.Sprite);

Đoạn goog.inherits cho ta biết gì đó về sự kế thừa ở đây. Khi chúng ta tạo thêm 1 kiểu đối tượng trong LimeJS, đối tượng này sẽ kế thừa từ lime.Sprite object. Từ đó, ta có thể lấy các thuộc tính từ đối tượng gameObj playerObj để sử dụng. Kiểu như file farming-game.js chứa các hàm khởi tạo (constructor).

[notification type=”alert-info” close=”true” ]Lưu ý : Khi các bạn tạo file mới, nên khai báo lệnh dưới để cập nhật lại trong LimeJS.[/notification]
bin/lime.py update

Trong file “farming-game.js” bạn chèn thêm đoạn này trên đầu :

goog.require('farming.Land');

 

Bây giờ, chúng ta sẽ tạo nhiều land layer bằng cách sử dụng vòng lặp for như bên dưới và nhớ là add đoạn code này bên dưới phần Money nhé ( dươí đoạn controlsLayer.appendChild(moneyLabel); )

//create land elements
    for(var i=0; i<gameObj.num_tiles_x; i++) {
        for(var j=0; j<gameObj.num_tiles_y; j++) {
            var landElement = new farming.Land(gameObj, playerObj).setPosition(i*gameObj.tile_size, j*gameObj.tile_size);
            landLayer.appendChild(landElement);
        }
    }

Ta reload farming.html và sẽ thấy kết quả như sau :

Vòng

 

Land states ( Trạng Thái Đất )

Việc đầu tiên khi chơi game này là bạn nhảy vô cày đất, cấy giống, đợi cây lớn và thu hoạch. Vậy thì ta có tổng cộng 4 trạng thái trong game tạm gọi như sau (empty, plowed, growing and ripe). Và bây giờ ta bắt đầu khai báo ( dưới dòng goog.inherits(farming.Land,lime.Sprite) ).

//states
farming.Land.prototype.EMPTY = 0;
farming.Land.prototype.PLOWED = 1;
farming.Land.prototype.GROWING = 2;
farming.Land.prototype.READY = 3;

Và sau trạng thái READY xong, ta phải reset lại trạng thái ban đầu. Bạn thêm đoạn code dưới vào trong hàm farming.Land()

this.state = this.EMPTY;

Plowing ( Cấy Đất )

Cứ mỗi khi người chơi click vào một miếng đất, thì ta cần phải làm cho nó giống như đã được cày. Ta sẽ bắt đầu thêm sự kiện vào hàm farming.Land.

var land = this;
    goog.events.listen(this,['mousedown', 'touchstart'], function(e) {
        e.event.stopPropagation();        
        if(land.state == land.EMPTY && playerObj.money >= gameObj.costPlowing) {
            //plow land
            land.setFill('images/plowed.png')
            land.state = land.PLOWED;
 
            //update player money
            playerObj.money -= gameObj.costPlowing;
            gameObj.updateMoney();
        }
    });

Đoạn code trên xử lý sự kiện ‘mousedown’ trên desktop và ‘touchstart’ trên thiết bị cảm ứng. Nếu đất của bạn là đất phẳng và tiền của bạn lớn hơn tiền để cày 1 miếng đất thì mảnh đất ấy sẽ được đổi hình nền sang hình plowed.png và đánh trạng thái là đã cày. Xong, người chơi sẽ được cập nhật lại số tiền nhưng hiện tại hàm này chỉ xử lý trong hệ thống mà chưa hiện ra trên màn hình người chơi ( scene) vậy nên ta quay lại file “farming-game.js” để thông báo lên màn hình.

//updating money indicator
gameObj.updateMoney = function() {
    moneyLabel.setText('$'+playerObj.money);
};
[notification type=”alert-info” close=”true” ]Lưu ý: hàm director.replaceScene(gameScene);  luôn nằm dưới cùng vì nó sẽ update lại màn hình chơi game của các bạn.[/notification]

Và đây là kết quả :

Kết

 

Shop

Okey, việc cày đất có vẻ vui khi các bạn được kết quả như trên, nhưng vui hơn sẽ là phần tạo và cấy giống cho mảnh đất của bạn. Ta sẽ bắt đầu tạo ra vài mùa vụ theo từng loại cây mà bạn sẽ cấy vào. Mở file “farming-game.js” và chèn đoạn code phía dưới đối tượng playerObj.

gameObj.crops = [
    {
        name: 'tomato',
        cost: 10,
        revenue: 18,
        time_to_ripe: 10, //secods
        time_to_death: 30, //second from when it's ripe
        image: 'tomato.png'
    },
    {
        name: 'artichoke',
        cost: 20,
        revenue: 38,
        time_to_ripe: 60,
        time_to_death: 60,
        image: 'artichoke.png'
    },
    {
        name: 'lettuce',
        cost: 15,
        revenue: 26,
        time_to_ripe: 30,
        time_to_death: 60,
        image: 'lettuce.png'
    },
    {
        name: 'eggplant',
        cost: 30,
        revenue: 78,
        time_to_ripe: 120,
        time_to_death: 120,
        image: 'eggplant.png'
    },
    {
        name: 'peppers',
        cost: 40,
        revenue: 82,
        time_to_ripe: 180,
        time_to_death: 180,
        image: 'peppers.png'
    }
];

Như bạn thấy, mỗi giống cây đều có tên, giá giống, doanh thu, thời gian trồng và chết (tính bằng giây) khá thú vị. Bây giờ, chúng ta sẽ tạo một màn hình game mới ( new scence ) chứa các giống cây khi ta bấm vào nút Shop.

//shop
var shopScene = new lime.Scene().setRenderer(lime.Renderer.CANVAS);
var shopLayer = new lime.Layer().setAnchorPoint(0, 0);
 
var shopBackground = new lime.Sprite().setAnchorPoint(0,0).setPosition(0,0)
    .setSize(gameObj.width, gameObj.height).setFill('#0D0D0D');
shopLayer.appendChild(shopBackground);
shopScene.appendChild(shopLayer);
 
//close button
var closeButton = new lime.GlossyButton().setColor('#133242').setText('Back')
    .setPosition(gameObj.width/2, gameObj.height-25)
    .setSize(80, 40);
shopLayer.appendChild(closeButton);
 
//launch shop event
goog.events.listen(shopButton,['mousedown', 'touchstart'], function(e) {
    director.replaceScene(shopScene);
});
 
//close shop event
goog.events.listen(closeButton,['mousedown', 'touchstart'], function(e) {
    director.replaceScene(gameScene);
});

Hiện tại, màn hình Shop của chúng ta chưa có gì cả và việc của chúng ta là sẽ thêm một số giống cây như ban đầu đã nói vô màn hình này. Các bạn bỏ đoạn code phía dưới vào file “farming-game.js”

//shop items
for(var i=0; i<gameObj.crops.length; i++) {
    var item = new lime.Sprite().setAnchorPoint(0,0).setPosition(gameObj.shop_margin_x, gameObj.shop_margin_y + (gameObj.shop_margin_y + gameObj.tile_size)*i)
        .setFill('images/'+gameObj.crops[i].image).setSize(gameObj.tile_size, gameObj.tile_size);
    shopLayer.appendChild(item);

    var timeLabel = new lime.Label().setText(gameObj.crops[i].name+' ('+gameObj.crops[i].time_to_ripe+' days)').setFontColor('#E8FC08')
    .setPosition(gameObj.shop_margin_x+150, gameObj.shop_margin_y*1.5 + (gameObj.shop_margin_y + gameObj.tile_size)*i);
    shopLayer.appendChild(timeLabel);
    var costLabel = new lime.Label().setText('cost: $'+gameObj.crops[i].cost).setFontColor('#E8FC08')
    .setPosition(gameObj.shop_margin_x+150, gameObj.shop_margin_y*2.5 + (gameObj.shop_margin_y + gameObj.tile_size)*i);
    shopLayer.appendChild(costLabel);
    var label = new lime.Label().setText('revenue: $'+gameObj.crops[i].revenue).setFontColor('#E8FC08')
    .setPosition(gameObj.shop_margin_x+150, gameObj.shop_margin_y*3.4 + (gameObj.shop_margin_y + gameObj.tile_size)*i);
    shopLayer.appendChild(label);

    //pick crop
    (function(item, i) {
        goog.events.listen(item,['mousedown', 'touchstart'], function(e) {
            playerObj.currentCrop = i;
            director.replaceScene(gameScene);
        });
    })(item, i);
}

Các bạn lưu ý là chỗ (function(item, i)… ta truyền được biến i vào trong hàm nhé vì ta  vẫn đang ở trong vòng lặp. Cái này là một Javascript Pattern, bạn có thể tìm hiểu thêm trên mạng.

 

shop1

 

 

Planting ( Trồng )

Sau khi chọn giống, ta bắt đầu gieo lên đất vì thể ta quay lại file “land.js” và thêm vài đoạn code vào vòng if trong sự kiện ‘mousedown’ ‘touchstart’

else if(land.state == land.PLOWED && playerObj.money >= gameObj.crops[playerObj.currentCrop].cost) {
        //plant
        land.setFill('images/growing.png');
        land.state = land.GROWING;

        //store crop and left time for it to be ready and to die
        land.crop = playerObj.currentCrop;
        land.ripeTime = gameObj.crops[playerObj.currentCrop].time_to_ripe * 1000;
        land.deathTime = gameObj.crops[playerObj.currentCrop].time_to_death * 1000;

        //update player money
        playerObj.money -= gameObj.crops[playerObj.currentCrop].cost;
        gameObj.updateMoney();
    }

 

Như bạn thấy, ta thêm điều kiện là nếu đất đã được cày và số tiền của ta vẫn đủ để gieo trồng thì ta tiến hành trồng. Trồng xong trên một miếng đất, ta lại cập nhật lại số tiền cho người chơi lại. Xong, bạn có thể F5 lại và gieo giống thử.

Plants growth and death ( Cây Lớn Lên và chết)

Khi ta gieo giống xong, cây sẽ phát triển, sau khi lớn lên thì ra trái và ta sẽ thu hoạch, thu hoạch xong hoặc nếu bạn không thu hoạch thì cây sẽ chết và đất sẽ trở lại trạng thái ban đầu. Để biểu diễn những tiến trình phát triển như thế, ta sẽ thay các ảnh thích hợp cho mỗi trạng thái.

Trong LimeJS có thể tạo ra 1 chức năng gọi là lên lịch kiểm tra (Schedule Helper) theo thời gian ta định sẵn. Đoạn code bên dưới, ta sẽ xử lý việc phát triển và thu hoạch trái. ( chèn vào bên dưới hàm bắt sự kiện trong file “land.js” bạn nhé )

//growing plants
dt = 1000;
lime.scheduleManager.scheduleWithDelay(function() {
    if(this.state == land.GROWING) {            
        if(this.ripeTime <= 0) {
            this.state = land.READY;
            this.setFill('images/'+gameObj.crops[this.crop].image);
        }
        else {
            this.ripeTime -= dt;
        }
    }
    else if(this.state == land.READY) {
        if(this.deathTime <= 0) {
            this.state = land.EMPTY;
            this.setFill('images/bare_land.png');
        }
        else {
            this.deathTime -= dt;
        }
    }
}, this, dt);
[notification type=”alert-info” close=”true” ]Ghi chú : ‘dt’ ở đây bạn hiểu là deadtime nhé.[/notification]

Bây giờ bạn có thể trồng và xem nó phát triển, xem quả chín nhưng chưa thể thu hoạch được.

Harvest ( Thu Hoạch )

Để thu hoạch được, ta thêm 1 điều kiện kiểm tra trạng thái của đất là READY thì ta đổi lại hình nền đất phẳng ban đầu. Sau đó, cập nhật lại số tiền của người chơi là xong. ( Thêm đoạn code này vào vòng if của sự kiện ‘touchstart’ bạn nhé ).

else if(land.state == land.READY ) {
        //harvest
        land.setFill('images/bare_land.png');
        land.state = land.EMPTY;

        //update player money
        playerObj.money += gameObj.crops[land.crop].revenue;
        gameObj.updateMoney();
    }

The complete game

The

Okey, tới đây là đã xong cái game này rồi. Bạn có thể download trọn bộ source code game ở nút bấm bên dưới.

Bài viết này lấy nguồn từ trang binpress.comtrong lúc mình đang là newbie khám phá về limeJS. Nếu có thắc mắc gì, các bạn cứ comment phía dưới, khi nào rãnh mình sẽ trả lời bạn.

[button style=”btn-success btn-lg” icon=”glyphicon glyphicon-circle-arrow-down” align=”left” type=”link” target=”false” title=”Source Farming Game” link=”https://anhthienad.com/farming-game-html5-source”]

0 0 votes
Article Rating
Subscribe
Notify of
guest
1 Comment
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Mag-sign up sa Binance

Can you be more specific about the content of your article? After reading it, I still have some doubts. Hope you can help me.

1
0
Would love your thoughts, please comment.x
()
x