Xây dựng một trò chơi JavaScript đơn giản – 1. Một công cụ trò chơi cơ bản – cách tạo javascript trò chơi

Từ và suy nghĩ về phát triển ứng dụng, phát triển trò chơi độc lập và nghệ thuật pixel.

Bạn đang xem : cách tạo javascript cho trò chơi

Xây dựng một trò chơi JavaScript đơn giản – 1. Công cụ trò chơi cơ bản

Chơi trò chơi điện tử rất thú vị. JavaScript rất thú vị. Vậy ….. Tại sao không phải là cả hai? Trong tập này, chúng tôi sẽ xây dựng một công cụ trò chơi cơ bản làm nền tảng để xây dựng.

Khi tôi bắt đầu tham gia phát triển web, mong muốn trở thành một nhà phát triển trò chơi của tôi đã bị loại bỏ trong khi tôi tiếp tục khám phá và xây dựng các kỹ năng của mình với tư cách là một nhà phát triển web. Khi kinh nghiệm của tôi phát triển và tôi bắt đầu hiểu các khái niệm cơ bản đằng sau điều gì khiến một trò chơi bắt đầu quay trở lại, nhưng thay vì sử dụng các ngôn ngữ truyền thống, tôi đã có rất nhiều điều thú vị khi thử nghiệm phát triển trò chơi điện tử bằng JavaScript.

Có rất nhiều thư viện và nền tảng hiện có để phát triển trò chơi JavaScript mà bạn có thể xây dựng ( Phaser , Unity , Kiwi , PlayCanvas ), sở thích cá nhân của tôi là Unity. Tuy nhiên, có một sự quyến rũ và hài lòng nhất định đến từ việc xây dựng một thứ gì đó từ đầu. Nếu bạn đang muốn bắt tay ngay vào việc xây dựng một gã khổng lồ đa nền tảng 3D điên rồ, tôi chắc chắn khuyên bạn nên kiểm tra một số khung / công cụ được đề cập ở trên, nhưng vì lợi ích của một bài tập giáo dục tuyệt vời, chúng tôi sẽ xây dựng một trò chơi nhỏ từ trên xuống từ đầu.

Trước khi chúng ta tiếp tục, tôi chỉ muốn đề cập rằng trong suốt quá trình viết và phát triển trò chơi đơn giản này, tôi sẽ nghe một buổi biểu diễn trực tiếp từ Truyền thuyết Zelda: Giao hưởng của các Nữ thần để thực sự tham gia vào tinh thần của dự án. Tôi thực sự khuyên bạn nên đốt nó lên và làm như vậy, điều đó thật tuyệt vời!

Tổng quan cấp cao

Chúng tôi sẽ tạo một trò chơi đơn giản từ trên xuống với các đặc điểm cơ bản của lối chơi cổ điển. Thành phẩm sẽ cho phép chúng ta dạo quanh một thị trấn nhỏ, đi vào các tòa nhà và chiến đấu với những kẻ xấu ngay bên ngoài thị trấn. Khi nhân vật di chuyển, máy ảnh sẽ vẫn đứng yên cho đến khi nó đi đến mép của khung nhìn hoặc di chuyển trong ô cửa. Khi hai sự kiện này xảy ra, camera sẽ di chuyển để hiển thị phần tiếp theo của thế giới hoặc người chơi sẽ được đưa vào một căn phòng mới (bất cứ thứ gì dẫn đến cánh cửa), tương ứng.

Chúng tôi sẽ sử dụng HTML5 Canvas API để xử lý việc hiển thị trò chơi của mình và chúng tôi sẽ phát triển JavaScript của mình với sự trợ giúp của Gulp , Browserify Đồng bộ hóa trình duyệt . Về mặt quản lý những gì đang diễn ra trong trò chơi, chúng tôi sẽ gắn các đối tượng trạng thái vào tất cả các thành phần khác nhau của chúng tôi để chúng tôi có thể quản lý trạng thái hiện tại của từng thành phần riêng lẻ, mà cá nhân tôi thấy là phương pháp giám sát và quản lý dữ liệu dễ dàng nhất .

Tôi đã hoàn thành bản phác thảo dự án cơ bản mà bạn có thể tải xuống và làm theo, có sẵn trên Github . Ngoài ra, bạn có thể truy cập kho lưu trữ để xem bản demo và chỉ cần thanh toán cam kết 3a4f588. Nếu bạn muốn có cái nhìn sâu hơn về cách thiết lập Gulp cho các dự án, bạn có thể xem bài đăng trước về việc xây dựng tệp gulp cho Jekyll khá kỹ lưỡng.

Bắt đầu

Đơn hàng kinh doanh đầu tiên của chúng tôi sẽ tạo ra một nền tảng tốt cho dự án. Chúng tôi có thể xây dựng toàn bộ thứ này trong một tệp tin quái dị duy nhất, nhưng vì lợi ích dễ đọc và khả năng bảo trì, tôi là người thích mô-đun hơn, đó là lý do tại sao tôi đã chọn xây dựng trò chơi và các thành phần của nó dưới dạng mô-đun sau đó kết hợp chúng lại với nhau thông qua Browserify . Hãy xem cấu trúc dự án được đề xuất của chúng tôi:

  index.html // Tải lên mô-đun trò chơi cho thời gian bên chơi trò chơi.
/ js / // Gốc JS chính.
- game.js // Mô-đun trò chơi chính.
- core / // Chứa các mô-đun liên quan đến "động cơ" cốt lõi.
- người chơi / // Chứa tất cả các mô-đun liên quan đến kẻ thù / nhân vật.
- utils / // Chứa các hằng số toàn cục / các hàm tiện ích.
- world / // Chứa các lớp thế giới chính và tất cả các cấp.
 

Đây là một cấu trúc khá đơn giản và khá dễ đoán. Tệp game.js chính của chúng tôi sẽ yêu cầu các mô-đun thích hợp từ player / , utils / , core / world / dưới dạng phần phụ thuộc, do đó sẽ yêu cầu bất kỳ phần phụ thuộc bổ sung nào cần thiết để tạo trò chơi của chúng tôi. Trong phần này, chúng ta có nhiều khả năng chỉ chạm vào một (có thể là hai) tệp trong mỗi thư mục.

Tạo khung nhìn bằng canvas

Điều đầu tiên, chúng tôi muốn tạo mô-đun trò chơi chính của mình để chúng tôi có thể bắt đầu làm việc. Sau đó, chúng tôi sẽ đưa nó vào trang html của chúng tôi và đặt nó động một canvas có dpi cao hoặc độ phân giải cao vào trang sau khi tính toán tỷ lệ pixel chính xác; bằng cách này, chúng tôi có thể đảm bảo rằng chúng tôi sẽ có kết xuất tinh thể rõ ràng ngay cả trên các thiết bị có mật độ điểm ảnh cao hơn.

Hãy bắt đầu với phần đầu tiên đó bằng cách tạo mô-đun trò chơi đơn giản bên trong /js/game.js .

 

var

$ container =

document

.getElementById (

'container' < / p>);

function

Trò chơi

(

)

{

this

.viewport =

document

.createElement (

'canvas' < / p>);

this

.context = viewport.getContext (

'2d'

);

this

.viewport.width =

800

;

this

.viewport.height =

600

; $ containerner.insertBefore (

this

.viewport, $ containerner.firstChild);

this

.context.font =

'32px Arial'

;

this

.context.fillText (

'Sẽ rất nguy hiểm nếu đi một mình trên tuyến đường này.'

,

5

,

50

,

800

);

return

this

; }

window

.game =

new

Game ();

module

.exports = game;

Chúng tôi không làm bất cứ điều gì lạ mắt ở đây, chỉ tạo một canvas (6 – 10), ném nó vào vùng chứa của chúng tôi (13), sau đó hiển thị một số văn bản (16 – 17). Nhiều khả năng nếu bạn xem đoạn mã trên trên màn hình có mật độ điểm ảnh cao, nó sẽ xuất ra văn bản nhưng nó sẽ siêu mờ và thô. Để giải quyết vấn đề này, chúng tôi sẽ tạo một tiện ích sẽ tính toán tỷ lệ pixel chính xác sau đó giảm tỷ lệ canvas để có chất lượng hiển thị tối ưu. Chúng tôi sẽ dựa trên các tiện ích của mình từ một bài đăng trên blog HTML5 Rocks tuyệt vời về chủ đề này và đưa chúng vào thư mục utils / tiện dụng của chúng tôi dưới tên tệp utils.canvas.js .

Chỉ cần lưu ý, nhưng các tệp tiện ích của chúng tôi sẽ là các tệp xuất cực kỳ đơn giản của các ký tự đối tượng được chứa với các phương thức, vì vậy bạn có thể mong đợi chúng tuân theo một cấu trúc như vậy:

 

mô-đun

.exports = {

methodOne

:

function

(

)

{},

methodThai

:

function

(

)

{} };

Trước tiên, chúng tôi sẽ cần một cách để có được tỷ lệ pixel chính xác. Để làm điều này, chúng tôi sẽ cần một bối cảnh canvas, chúng tôi sẽ sử dụng ngữ cảnh này để lấy tỷ lệ thiết bị hỗ trợ từ cửa hàng hỗ trợ thiết bị hỗ trợ. Sau đó, chúng tôi sẽ chia tỷ lệ pixel từ cửa sổ theo tỷ lệ hỗ trợ đó, cho chúng tôi tỷ lệ pixel của chúng tôi.

 

 
getPixelRatio: 

function

getPixelRatio

(

context

)

{

console

.log (

'Xác định tỷ lệ pixel.'

);

var

backingStores = [

'webkitBackingStorePixelRatio'

,

'mozBackingStorePixelRatio'

,

'msBackingStorePixelRatio'

,

'oBackingStorePixelRatio'

,

'backingStorePixelRatio'

];

var

deviceRatio =

window

.devicePixelRatio;

var

backingRatio = backingStores.reduce (

function

(

trước, uốn

)

{

return

(context.hasOwnProperty (curr)? context [curr]:

1

); });

return

deviceRatio / backingRatio; },

Các nhận xét khá dài dòng, nhưng một bản tóm tắt nhanh thứ hai về những gì đang diễn ra:

  1. Chúng tôi tạo một loạt các đạo cụ cửa hàng sao lưu có tiền tố trình duyệt thích hợp. (10 – 16)
  2. Chúng tôi giảm mảng của mình xuống một tỷ lệ cửa hàng dự phòng duy nhất, đặt mặc định là 1 nếu không tồn tại. (21 – 23)
  3. Chúng tôi chia tỷ lệ pixel của thiết bị (từ cửa sổ) cho tỷ lệ hỗ trợ, cho chúng tôi tỷ lệ pixel của chúng tôi. (26)

Tiếp theo, chúng tôi cần áp dụng tỷ lệ đã tính toán cho canvas của mình, sau đó giảm tỷ lệ toàn bộ canvas thông qua CSS và chuyển đổi, mang lại cho chúng tôi kết xuất chất lượng cao có thể dự đoán được với các kích thước có thể dự đoán được. Chúng tôi sẽ ném tất cả những thứ này vào bên trong một tiện ích canvas khác, sau đó trả canvas về đối tượng trò chơi chính của chúng tôi để tạo và chèn dom.

 

 
createCanvas: 

function

createCanvas

(

w, h

)

{

console

.log (

'Tạo canvas.'

);

var

canvas =

document

.createElement (

'canvas' ), context = canvas.getContext (

'2d'

);

var

ratio =

this

.getPixelRatio (context); canvas.width =

Math

.round (w * ratio); canvas.height =

Math

.round (h * ratio); canvas.style.width = w +

'px'

; canvas.style.height = h +

'px'

; context.setTransform (ratio,

0

,

0

, ratio,

0

,

0

);

return

canvas; }

Một lần nữa, khá đơn giản. Bạn sẽ nhận thấy rằng sau khi tạo canvas và lấy ngữ cảnh, chúng tôi đang chuyển ngữ cảnh đó tới phương thức getPixelRatio () , sau đó nhân chiều rộng và chiều cao canvas với số trả về, sau đó đặt chiều rộng và các kiểu chiều cao với chiều rộng và chiều cao ban đầu; đây là chúng tôi giảm tỷ lệ khung. Nó giống như nếu bạn xem một video 1080p trên YouTube với chiều rộng 400px. Sau đó, quá trình biến đổi của chúng tôi tương đối mở rộng canvas trở lại bằng số lượng chúng tôi đã giảm tỷ lệ nó, theo cách đó chúng tôi có được kích thước pixel chính xác. Hãy thử xóa setTransform và xem các hiệu ứng, nó rất thú vị.

Giờ đây, cả hai phương thức tiện ích của chúng tôi để tạo canvas đều đã được tạo, chúng tôi có thể viết lại mô-đun trò chơi chính của mình để tạo một canvas có kích thước thích hợp động và sau đó gắn nó vào vùng chứa của chúng tôi, cung cấp cho chúng tôi khung nhìn.

  

var

cUtils =

request

(

'./ utils / utils.canvas.js'

), $ container =

document

.getElementById (

'container'

);

function

Trò chơi

(

w, h

)

{

this

.viewport = cUtils.generateCanvas (w, h);

this

.viewport.id =

"gameViewport"

;

this

.context =

this

.viewport.getContext (

'2ngày '

); $ containerner.insertBefore (

this

.viewport, $ containerner.firstChild);

this

.context.font =

'32px Arial'

;

this

.context.fillStyle =

'# fff'

;

this

.context.fillText (

'Sẽ rất nguy hiểm nếu đi một mình trên tuyến đường này.'

,

5

,

50

);

return

this

; }

window

.game =

new

Trò chơi (

800

,

600

);

module

.exports = game;

Mô-đun Trò chơi của chúng tôi phần lớn vẫn giữ nguyên, ngoại trừ việc chúng tôi sử dụng phương thức createCanvas () từ mô-đun tiện ích canvas của chúng tôi để tạo chế độ xem thay vì tạo nó theo cách thủ công. Mặt khác, bây giờ chúng ta nên có một canvas có độ phân giải cao được tạo động để chơi cùng, vì vậy, bây giờ chúng ta phải tìm ra cách sử dụng nó để chơi trò chơi.

Tạo vòng lặp trò chơi

Vì vậy, chúng tôi đã có một canvas và một số thư mục khiến chúng tôi trông như thể chúng tôi thực sự biết mình đang làm gì. Các bước tiếp theo hợp lý liên quan đến việc tạo một số loại công cụ kết xuất và logic để xử lý việc cập nhật trạng thái trò chơi và kết xuất lại canvas của chúng tôi để phản ánh trạng thái đã cập nhật. May mắn thay, chúng tôi có thể giải quyết cả hai điều này trong một lần rơi bằng cách tạo vòng lặp trò chơi .

Khi nói đến tạo hoạt ảnh và quản lý canvas, có một vài vòng lặp nữa để chuyển qua so với khi bạn đang tạo hoạt ảnh cho một nút dom điển hình, chủ yếu là vì bạn không thể thực sự tham chiếu đến nội dung canvas đã tạo sau khi bạn đã hiển thị chúng . Để giải quyết vấn đề này, chúng tôi sẽ kết thúc việc lưu trữ mọi thứ ở trạng thái trung tâm; với các thực thể riêng lẻ (người chơi, kẻ thù, v.v.) lưu trữ vị trí của chúng và các dữ liệu thích hợp khác trong trạng thái của riêng chúng. Sau đó, khi đến lúc kết xuất lại, chúng tôi sẽ gọi mỗi phương thức render () thực thể sẽ sử dụng dữ liệu trong các trạng thái của chúng để hiển thị thực thể một cách chính xác.

Vì với canvas, chúng ta cần xóa các đối tượng trước khi hiển thị lại chúng ở một vị trí khác, chúng tôi sẽ hiển thị lại toàn bộ canvas mọi khung hình. Để thực hiện điều này, chúng ta sẽ cần một vòng lặp, là JavaScript có hàng triệu cách để làm điều đó. Hãy xem xét các tùy chọn chính của chúng tôi:

  1. setTimeout hoặc setInterval được đặt thành khoảng thời gian sẽ tắt với tốc độ bằng với mục tiêu của chúng tôi FPS.
  2. Sử dụng window.requestAnimationFrame , đây là phương pháp được khuyến nghị, để cho trình duyệt biết chúng tôi muốn tạo hoạt ảnh và kích hoạt phương thức kết xuất của chúng tôi trước lần sơn lại tiếp theo .

Tùy chọn đầu tiên của chúng tôi, sử dụng setTimeout hoặc setInterval sẽ dễ dàng hơn nhiều (và có thể đọc được đối với hầu hết các phần), nhưng nó cực kỳ kém hiệu quả. Tùy chọn thứ hai, mặc dù phức tạp hơn nhưng lại hiệu quả hơn rất nhiều do trình duyệt có thể tối ưu hóa hoạt ảnh trong các chu kỳ sơn lại.

Dàn dựng vòng lặp

Hãy bắt đầu bằng cách tìm ra các mô-đun mà chúng ta sẽ cần cho vòng lặp chính của chúng ta. Vòng lặp chính sẽ có ba phần:

  /js/core/game.loop.js // Vòng lặp thực tế
/js/core/game.update.js // Phương thức cập nhật
/js/core/game.render.js // Phương thức kết xuất
 

Các mô-đun này sẽ cần truy cập vào một số hằng số mà chúng tôi sẽ thiết lập trong mô-đun trò chơi toàn cầu của mình, vì vậy chúng tôi sẽ tạo một thuộc tính constants mới cùng với một trạng thái trống trong mô-đun trò chơi chính của chúng tôi trước tiên.

  

function

Trò chơi (

w, h, targetFps, showFps

)

{

cái này

.constants = {

width

: w,

height

: h,

targetFps

: targetFps,

showFps

: showFps };

this

.state = {}; . . . }

window

.game =

new

Trò chơi (

800

,

600

,

60

,

true

);

Ngoài các đạo cụ ban đầu của chúng tôi cho chiều rộng và chiều cao, chúng tôi cũng đã thêm giá đỡ tốc độ khung hình mục tiêu và cờ cho biết có hiển thị FPS hiện tại hay không; cái sau là hoàn toàn tùy chọn cả trong việc triển khai và sử dụng. Bây giờ chúng tôi đang lưu trữ tất cả những thứ này bên trong một vật thể tiếp xúc đẹp đẽ tiện dụng, chúng tôi có thể bắt đầu trên các mô-đun mới của mình.

Vì tất cả những thứ này đều không thể thiếu đối với hoạt động của trò chơi của chúng tôi, chúng tôi có thể tính các mô-đun này là mô-đun cốt lõi mà chúng tôi sẽ khởi tạo từ mô-đun trò chơi chính của mình. Vì phương pháp cập nhật và hiển thị khá đơn giản và dễ hiểu (hiện tại), nên hãy bắt đầu với những phương pháp đó.

Vì chúng tôi chỉ tạo ra vòng lặp của mình và hiện không có chức năng thực tế, phương pháp cập nhật của chúng tôi sẽ thực sự nhàm chán. Cuối cùng, nó sẽ lấy đối tượng trạng thái trò chơi, xác định các thay đổi trong, cập nhật nó, sau đó trả lại nó. Tuy nhiên, chúng tôi sẽ giả vờ như bây giờ chúng tôi đang làm những thứ yêu thích.

 

 

function

gameUpdate

(

phạm vi

)

{

return

function

cập nhật

(

tFrame

)

{

var

state = scope.state || {};

if

(state.hasOwnProperty (

'entity'

)) {

var

entity = state.entities;

cho

(

var

entity

in

entity) { các thực thể [entity] .update (); } }

return

trạng thái; } }

module

.exports = gameUpdate;

Như tôi đã nói, không có gì siêu điên rồ xảy ra. Chúng tôi đang chèn phạm vi toàn cục (8), sau đó tạo một đối tượng mới dựa trên trạng thái toàn cục (10), lặp qua các thực thể trong trạng thái toàn cục (13), kích hoạt phương thức update () cho mỗi thực thể hoạt động (16 – 22), sau đó trả lại nó (15). Dễ dàng.

Một điều thú vị mà bạn có thể nhận thấy là tôi đang mong đợi một tham số scope được chuyển vào trước, sau đó tôi trả về một hàm khác thực sự đang thực hiện tất cả công việc. Bạn cũng sẽ thấy điều này trong phương thức kết xuất, và đó là sở thích cá nhân hơn bất cứ thứ gì. Một giải pháp thay thế sẽ là xóa hàm được trả về, chuyển nội dung của nó vào hàm gameUpdate chính và thay vì đưa phạm vi vào, chúng tôi sẽ nhấn vào đối tượng window.game chung . Vì tôi muốn tránh phải viết mã khó cho các tham chiếu này, nên tôi chỉ đưa phạm vi vào làm tham số.

Mô-đun kết xuất trò chơi

Mô-đun kết xuất bận hơn một chút so với mô-đun cập nhật của chúng tôi, nhưng nó không có gì quá điên rồ. Chúng tôi sẽ chuyển văn bản giả từ mô-đun trò chơi chính vào mô-đun này, thêm logic kết xuất FPS của chúng tôi, sau đó viết một vòng lặp nhỏ nhanh chóng để lặp lại qua các thực thể đang hoạt động tiềm năng.

 

 

function

gameRender

(

phạm vi

)

{

var

w = scope.constants.width, h = scope.constants.height;

return

function

kết xuất

(

)

{ scope.context.clearRect (

0

,

0

, w, h); scope.context.font =

'32px Arial'

; scope.context.fillStyle =

'# fff'

; scope.context.fillText (

'Đi theo tuyến đường này một mình rất nguy hiểm.'

,

5

,

50

);

if

(scope.constants.showFps) { scope.context.fillStyle =

'# ff0'

; scope.context.fillText (

'FPS'

, w -

100

,

50

); }

if

(scope.state.hasOwnProperty (

'entity'

)) {

var

entity = scope.state.entities;

cho

(

var

entity

in

entity) { các thực thể [entity] .render (); } } } }

module

.exports = gameRender;

Giống như mô-đun cập nhật, chúng tôi đang chèn bối cảnh trò chơi (10), sau đó chúng tôi đang xóa canvas (17), hiển thị nội dung giả của chúng tôi (19-22) và thiết lập nó để hiển thị FPS ở trên cùng bên phải góc của canvas nếu cờ showFps được đặt (25-28).

Sự kỳ lạ duy nhất xảy ra ở đây là giữa các dòng 31 và 38, trong đó chúng tôi đang lặp lại tất cả các thực thể đang hoạt động bằng một vòng lặp for ... in đơn giản (nếu toàn cầu trạng thái có bất kỳ thực thể nào được đặt, theo dòng 31), sau đó kích hoạt phương thức render () của từng thực thể đang hoạt động (36).

Chúng tôi sẽ quay lại mô-đun này trong giây lát để thay đổi văn bản chúng tôi đang viết thay cho tốc độ khung hình thực tế, nhưng hiện tại nó đã hoàn thành.

Vòng lặp của sự diệt vong

Bây giờ đã đến lúc chúng ta chuyển sang vòng lặp thần thoại của mình, cho đến nay đây sẽ là phần mã phức tạp nhất mà chúng ta đã viết. Như tôi đã đề cập trước đó, chúng tôi sẽ sử dụng requestAnimationFrame để xử lý vòng lặp của chúng tôi, theo khuyến nghị của MDN và dựa trên thông tin từ Paul Irish , cùng với một số điều chỉnh tốc độ khung hình dựa trên một số câu trả lời tuyệt vời về tràn ngăn xếp .

Tôi biết rằng có rất nhiều thứ để nuốt, vì vậy chúng tôi sẽ giải quyết vấn đề này từng cái một. Hãy bắt đầu với chính vòng lặp, sau đó chúng ta sẽ chuyển sang điều chỉnh tốc độ khung hình.

 

 

function

gameLoop

(

phạm vi

)

{

var

loop =

this

; loop.main =

function

mainLoop

(

tframe

)

{ loop.stopLoop =

cửa sổ

.requestAnimationFrame (loop.main); scope.state = scope.update (bây giờ); scope.render (); }; loop.main ();

return

vòng lặp; }

module

.exports = gameLoop;

Bản thân vòng lặp không quá khủng khiếp và nếu bạn đã xem qua tài liệu MDN về giải phẫu của một trò chơi trên web thì nó sẽ rất quen thuộc; chúng tôi chỉ tạo một cuộc gọi lại và chuyển nó vào window.requestAnimationFrame , sau đó kích hoạt các phương thức cập nhật và kết xuất của chúng tôi, được khởi tạo trong ngữ cảnh chung (đã được đưa vào). Tôi nhận thấy đây không phải là cách thanh lịch nhất để kích hoạt các phương thức cập nhật và kết xuất, nhưng lý do của tôi là để các mô-đun cập nhật và kết xuất được khởi tạo và hiển thị trên toàn cầu nếu vì bất kỳ lý do gì, chúng cần được kích hoạt theo cách thủ công. Một giải pháp thay thế cho điều này là yêu cầu các mô-đun trong mô-đun vòng lặp trò chơi và khởi tạo chúng với ngữ cảnh được chèn.

Tiếp theo, chúng ta sẽ bắt đầu với việc điều chỉnh tốc độ khung hình. Có khá nhiều thứ về điều này, vì vậy tôi sẽ cố gắng chia nhỏ nó tốt nhất có thể. Trước tiên, chúng ta cần thiết lập các biến để tính toán tốc độ khung hình. Chúng tôi sẽ yêu cầu dấu thời gian đánh dấu hoạt ảnh hiện tại, dấu thời gian đánh dấu hoạt ảnh trước đó cũng như sự khác biệt giữa hai loại; FPS mục tiêu của chúng tôi và khoảng thời gian mục tiêu của chúng tôi giữa các lần đánh dấu hoạt ảnh (1000 / fps).

 

     
    

var

fps = scope.constants.targetFps, fpsInterval =

1000

/ fps, before =

window

.performance.now (), chu kỳ = {

mới

: {

frameCount

:

0

, startTime: trước đây, kể từ Bắt đầu:

0

},

: {

frameCount

:

0

,

startTime

: trước đây,

sineStart

:

0

} }, resetInterval =

5

, resetState =

'mới'

; loop.fps =

0

; loop.main =

function

mainLoop

(

tframe

)

{. . . }

Chà, có rất nhiều biến số. Hãy ghi nhớ các chú thích mã, có một điều tôi muốn giải thích đó là đối tượng cycle . Nếu bạn nhìn vào câu trả lời tràn ngăn xếp mà tôi đã đề cập trước đó, có rất nhiều ví dụ về một số phép tính để xác định và điều chỉnh tốc độ khung hình, nhưng chúng sử dụng một chu kỳ tính toán duy nhất mà trong khi thử nghiệm, tôi đã nhận thấy một số vấn đề.

  1. Sau một thời gian, hiệu suất sẽ giảm do sự gia tăng liên tục của số khung hình đơn và các biến thời gian đã trôi qua.
  2. Cũng sau một thời gian, độ chính xác sẽ giảm đáng kể. Vì các phép tính phụ thuộc vào thời gian hiện tại và số lượng khung hình so với thời gian gốc, bởi vì khoảng cách quá lớn nên giá trị kết quả sẽ đại diện sai cho tốc độ khung hình thực tế và sẽ hoạt động giống như một giá trị “trung bình trên -time-you-had-this-page-open “.

Vấn đề thứ hai có thể là phần trình bày về vấn đề tốc độ khung hình mà tôi thừa nhận, nhưng như một giải pháp mà tôi đã thực hiện, vì vậy có hai mức tăng diễn ra song song và khi chúng đạt đến khoảng thời gian xác định trước, resetInterval , chu kỳ hoạt động sẽ được đặt lại về 0 và chu kỳ thay thế sẽ diễn ra như chu kỳ hoạt động. Điều này giúp duy trì tốc độ khung hình được tính toán chính xác dựa trên các mẫu trong một khoảng thời gian ngắn, thay vì một khoảng thời gian ngày càng lớn.

Bây giờ, quay lại vòng lặp của chúng tôi.

 

loop.main = 

function

mainLoop

(

tframe

)

{ loop.stopLoop =

window

.requestAnimationFrame (loop.main);

var

now = tframe, trôi qua = bây giờ - trước đây, activeCycle, targetResetInterval;

if

(trôi qua & gt; fpsInterval) { before = now - (trôi qua% fpsInterval); scope.update (bây giờ); scope.render (); } };

Điều chỉnh vòng lặp đến khung hình / giây lý tưởng của chúng tôi thực sự tương đối đơn giản, như bạn có thể thấy ở trên. Đây là các tiêu đề:

  • Dòng 8 & amp; 9: vào bộ nhớ cache của dấu thời gian hiện tại, sau đó trừ dấu thời gian trước đó khỏi nó để có thời gian đã trôi qua.
  • Dòng 13: Nếu thời gian trôi qua từ dòng 9 lớn hơn khoảng thời gian mục tiêu của chúng tôi, hãy tiếp tục kết xuất lại.
  • Dòng 14 – 22: Đặt biến giữ dấu thời gian trước đó thành dấu thời gian hiện tại, sau đó kích hoạt cập nhật và hiển thị các mô-đun.

Khá đơn giản, vì vậy bây giờ hãy tính toán tốc độ khung hình hiện tại và hiển thị nó cho các mô-đun khác (cụ thể là mô-đun kết xuất).

 

before = now - (trôi qua% fpsInterval);

 

cho

(

var

calc

trong

chu kỳ) { ++ chu kỳ [calc] .frameCount; cycle [calc] .sinceStart = now - cycle [calc] .startTime; } activeCycle = cycle [resetState]; loop.fps =

Math

.round (

1000

/ (activeCycle.sinceStart / activeCycle.frameCount) *

100

) /

100

; targetResetInterval = (cycle.new.frameCount === cycle.old.frameCount ? resetInterval * fps : (resetInterval *

2

) * fps);

if

(activeCycle.frameCount & gt; targetResetInterval) { chu kỳ [resetState] .frameCount =

0

; cycle [resetState] .startTime = now; chu kỳ [resetState] .sinceStart =

0

; resetState = (resetState ===

'new'

?

'old'

:

'mới'

); }

Đây có lẽ là phần lớn nhất mà chúng tôi đã trải qua cho đến nay, vì vậy, hãy xem xét từng khối một.

  • Dòng 4 – 7: Đầu tiên, chúng tôi sẽ lặp lại qua cả hai chu kỳ, tăng số lượng khung hình và cập nhật thời gian đã trôi qua kể từ khi bắt đầu chu kỳ.
  • Dòng 10 & amp; 11: Đặt chu kỳ hoạt động, sau đó sử dụng một số phép toán để xác định và đặt thuộc tính hiển thị của chúng tôi thành tốc độ khung hình hiện tại.
  • Dòng 12 – 15: Đặt khoảng thời gian đặt lại mục tiêu của chúng tôi. Khi các chu kỳ bắt đầu xen kẽ, khi chu kỳ hoạt động đạt đến mục tiêu của nó, chu kỳ không hoạt động sẽ bằng một nửa mục tiêu vì nó chạy song song. Do đó, thay vì đặt lại vào khoảng thời gian của chúng tôi, chúng tôi muốn đặt lại ở mức gấp đôi khoảng thời gian của mình.
  • Dòng 21 – 27: Nếu số khung chu kỳ hiện hoạt lớn hơn khoảng mục tiêu của nó, hãy đặt lại chu kỳ hiện hoạt thành 0 và chuyển sang chu kỳ không hoạt động.

Bây giờ, việc đó đã hoàn tất, chúng tôi có thể chạy lại mô-đun kết xuất của mình ( /js/core/game.render.js ) và ném giá trị loop.fps bị lộ thay cho văn bản ‘FPS’ giả của chúng tôi:

  scope.context.fillText (scope.loop.fps, w - 100, 50);
 

Kết hợp tất cả lại với nhau

Vì chúng tôi có tất cả các mô-đun của mình được xây dựng và hiển thị dưới dạng mô-đun có thể xuất, giờ đây chúng tôi có thể chỉ cần khởi tạo chúng trong mô-đun trò chơi chính và trò chơi của chúng tôi sẽ tự động chạy vòng lặp chính của chúng tôi, hiển thị tốc độ khung hình ở góc trên cùng bên phải nếu bạn ‘ đã đặt thông số showFps của bạn thành true.

 . . .
$ containerner.insertBefore (

this

.viewport, $ containerner.firstChild);

this

.update = gameUpdate (

this

);

this

.render = gameRender (

this

);

this

.loop = gameLoop (

this

);

return

this

;

Huzzah! Chúng tôi đã xây dựng (mặc dù là cơ bản) công cụ trò chơi thô sơ của riêng mình! Nó tự động tạo phần tử canvas có độ phân giải cao và đưa nó vào HTML của chúng tôi, tự cập nhật ở tốc độ khung hình có thể định cấu hình và hiển thị tốc độ khung hình đã nói bằng cách sử dụng chiến lược chu kỳ tính toán xen kẽ (bạn có thể xem liệu bạn có tung nhật ký tốc độ khung hình của cả hai chu kỳ vào vòng lặp của bạn).

Phần thưởng: Tạo người chơi có thể di chuyển

Tôi có thể kết thúc ở đó, nhưng tôi thường thực sự khó chịu bất cứ khi nào tôi thấy một hướng dẫn nhiều phần khiến tôi hoàn toàn bị treo cổ trước khi phép thuật xảy ra, vì vậy tôi không muốn trở thành “gã đó”. Đối với một số người, việc hoàn thành vòng lặp là một điều kỳ diệu và có lẽ họ đang rất say mê vào lúc này; Tôi cũng vậy nhưng tôi nghĩ một điểm dừng kỳ diệu hơn nữa sẽ là khả năng di chuyển một chiếc hộp nhỏ xung quanh khung nhìn. Không có gì điên rồ, chỉ đủ để tương tác và thực sự có được cảm hứng tuôn trào khi bạn nhận được câu “Eureka!” ngay khi bạn di chuyển hộp quanh khung nhìn một cách không mục đích.

Hãy bắt đầu bẻ khóa.

Tạo mô-đun trình phát của chúng tôi

Trước đó, tôi đã tiếp tục đề cập đến các thực thể, về cơ bản là những gì chúng tôi sẽ tạo. Nếu bạn đã từng chơi Unity trước đây, thì thuật ngữ này sẽ quen thuộc với bạn vì các trò chơi Unity được xây dựng từ các thực thể. Thực thể về cơ bản là các thành phần tương tác như NPC, kẻ thù, đường đạn, v.v. Cuối cùng, thực thể của chúng ta sẽ có rất nhiều phương pháp và thuộc tính khác nhau như sức khỏe, sát thương, tấn công và các thứ thuộc loại trò chơi khác; hiện tại, tất cả những gì chúng ta cần là trạng thái chứa vị trí của chúng ta trong khung nhìn, phương thức hiển thị và phương thức cập nhật.

Đối với các tiện ích, các tiện ích cần thiết có thể nhìn thấy trước bao gồm một cách để ràng buộc số của chúng tôi với một ranh giới cụ thể (chiều rộng của khung nhìn và 0 của chúng tôi) và một cách xác định xem có phím đang được nhấn vào bất kỳ lúc nào hay không. Hãy bắt đầu bằng cách tạo ra thực thể trình phát của chúng tôi, sau đó chúng tôi có thể tạo và cắm các tiện ích đó nếu cần.

 

 

function

Trình phát

(

phạm vi, x, y

)

{

var

player =

this

; player.state = {

vị trí

: {

x

: x,

y

: y },

moveSpeed ​​

:

1,5

};

var

height =

23

, width =

16

; player.render =

function

playerRender

(

)

{ scope.context.fillStyle =

'# 40d870'

; scope.context.fillRect ( player.state.position.x, player.state.position.y, chiều rộng, chiều cao ); }; player.update =

function

playerUpdate

(

)

{ };

return

trình phát; }

module

.exports = Player;

Như bạn có thể thấy, cấu trúc thực thể của chúng tôi cực kỳ giống với tất cả các mô-đun khác của chúng tôi; chúng ta chỉ có một khai báo hàm được xuất với một số thuộc tính và phương thức được hiển thị. Điều quan trọng cần lưu ý ở đây là các phương thức render update , lần lượt là các dòng 23 – 30 và 34 – 37. Bạn sẽ nhớ rằng trong cả mô-đun cập nhật và kết xuất toàn cầu của chúng tôi, chúng tôi đang lặp lại tất cả các thực thể đang hoạt động và loại bỏ các phương thức này, vì vậy việc đưa chúng vào thực thể của chúng tôi là rất quan trọng.

Một lần nữa, chúng tôi đang đưa phạm vi vào mô-đun nhưng điều này không cần thiết 100%; chúng tôi cũng có thể đưa nó vào bằng cách bỏ tham số scope và sử dụng window.game global để lấy ngữ cảnh. Vì chúng tôi đã tiêm nó trước đó, tôi sẽ gắn bó với nó ở đây. Nếu sau này có vấn đề làm phiền tôi, tôi chắc chắn rằng tôi sẽ thay đổi nó trong bài viết tiếp theo.

Về mặt kỹ thuật, miễn là chúng tôi khởi tạo mô-đun trình phát, trò chơi của chúng tôi sẽ hoạt động và hiển thị trình phát của chúng tôi trong khung nhìn của chúng tôi. Điều này nhiều khả năng sẽ xảy ra trên cơ sở mỗi cấp độ (hoặc thế giới), nhưng hiện tại chúng tôi sẽ chỉ đưa điều đó vào mô-đun chính của mình.

 

that = 

this

;

var

createPlayer =

function

createPlayer

(

)

{ that.state.entities = that.state.entities || {}; that.state.entities.player =

new

playerEnt ( cái đó, that.constants.width /

2

, that.constants.height -

100

); } ();

Yêu cầu người chơi của chúng tôi di chuyển

Bây giờ chúng ta đã có một thực thể đang hoạt động, đã đến lúc chúng ta điền vào phương thức cập nhật đối tượng để nó thực sự thực hiện một điều gì đó. Đơn hàng kinh doanh đầu tiên của chúng tôi là yêu cầu người chơi của chúng tôi di chuyển xung quanh bất cứ khi nào chúng tôi nhấn các phím mũi tên xuống. Vì chúng tôi không sử dụng jQuery hoặc bất kỳ thư viện nào nên chúng tôi sẽ phải tìm ra cách để theo dõi dom một cách duyên dáng cho các sự kiện khóa xuống, sau đó lưu trữ xem khóa có bị hỏng ở bất kỳ điểm nào đã cho dưới dạng boolean hay không. Có hai cách chúng ta có thể làm điều này, chúng ta có thể trả về một số hàm sẽ hoạt động như getters và truy xuất bất cứ thứ gì biến khóa được đặt thành tại thời điểm đó ( keys.isLeftPressed () ) hoặc chúng ta có thể sử dụng Object.defineProperty để thực sự tạo một số getters cho các biến ( keys.isPressed.left ). Điều này rõ ràng là hoàn toàn để lại cho sở thích cá nhân và tôi sẽ sử dụng Object.defineProperty vì tôi thích cú pháp mà nó để lại cho chúng tôi.



 

function

keyDown

(

)

{

this

.isPressed = {};

var

trái, phải, lên, xuống;

document

.onkeydown =

function

(

ev

)

{

if

(ev.keyCode ===

39

) {right =

đúng

; }

if

(ev.keyCode ===

37

) {left =

đúng

; }

if

(ev.keyCode ===

38

) {up =

đúng

; }

if

(ev.keyCode ===

40

) {down =

đúng

; } };

document

.onkeyup =

function

(

ev

)

{

if

(ev.keyCode ===

39

) {right =

sai

; }

if

(ev.keyCode ===

37

) {left =

sai

; }

if

(ev.keyCode ===

38

) {up =

sai

; }

if

(ev.keyCode ===

40

) {down =

sai

; } };

Object

.defineProperty (

this

.isPressed,

'left' < / p>, {

get

:

function

(

)

{

return

left; },

có thể định cấu hình

:

true

,

liệt kê được

:

true

});

Object

.defineProperty (

this

.isPressed,

'right' < / p>, {

get

:

function

(

)

{

trả về

đúng; },

có thể định cấu hình

:

true

,

liệt kê được

:

true

});

Object

.defineProperty (

this

.isPressed,

'up' < / p>, {

get

:

function

(

)

{

quay lại

lên; },

có thể định cấu hình

:

true

,

liệt kê được

:

true

});

Object

.defineProperty (

this

.isPressed,

'down' < / p>, {

get

:

function

(

)

{

quay lại

xuống; },

có thể định cấu hình

:

true

,

liệt kê được

:

true

});

return

this

; }

module

.exports = keyDown ();

Bây giờ, tôi hiểu rằng điều này có thể trông duyên dáng như một con gấu trượt băng với tất cả các khối if của nó, nhưng nó chắc chắn tốt hơn rất nhiều so với việc làm ô nhiễm thực thể của chúng ta với tất cả những thứ mumbo jumbo này (tin tôi đi, tôi đã nó trong mô-đun khi tôi viết lần đầu tiên và những giọt nước mắt là có thật). Vậy, chuyện gì đang xảy ra ở đây?

  • Chúng tôi chuẩn bị thuộc tính isPressed của mình bằng cách đặt nó thành một đối tượng trống và chúng tôi lưu vào bộ nhớ cache tất cả các biến của mình (10, 11).
  • Thiết lập trình xử lý sự kiện khi một phím được nhấn xuống, sau đó chúng tôi xác định phím nào được nhấn và đặt các biến cho phù hợp (14 – 19).
  • Tương tự, chúng tôi thiết lập trình xử lý sự kiện khi khóa được phát hành và đặt lại các biến cho phù hợp. Điều này rất quan trọng vì nó sẽ nói với trò chơi của chúng ta rằng “Này, chúng ta không di chuyển sang trái nữa” (22 – 27).
  • Cuối cùng, chúng tôi xác định một số getters cho mỗi khóa của chúng tôi. Chúng không phải là getters phức tạp nhất trên thế giới, nhưng chúng rất tuyệt vời vì chúng cung cấp cho chúng ta cú pháp của một khai báo biến, nhưng nó sẽ trả về bất kỳ giá trị nào đang hoạt động tại thời điểm đó (33 – 55).

Sau khi yêu cầu mô-đun này vào mô-đun trình phát của chúng tôi, chúng tôi có quyền truy cập vào khả năng yêu cầu xem khóa có hoạt động hay không tại bất kỳ thời điểm nào bằng cách gọi thuộc tính khóa đó trong đối tượng isPressed (< chẳng hạn như code> keys.isPressed.left ).

 

var

key =

request

(

'../ utils / utils .keysDown.js '

);

function

Trình phát

(

phạm vi, x, y

)

{ . . . player.update =

function

playerUpdate

(

)

{

nếu

(key.isPressed.left) { player.state.position.x - = player.state.moveSpeed; }

nếu

(key.isPressed.right) { player.state.position.x + = player.state.moveSpeed; }

if

(key.isPressed.up) { player.state.position.y - = player.state.moveSpeed; }

if

(key.isPressed.down) { player.state.position.y + = player.state.moveSpeed; } }; . . . }

module

.exports = Player;

Dễ hơn mong đợi, phải không? Và vì chúng tôi đã xác định moveSpeed ​​của mình là một biến, sau này chúng tôi có thể thay đổi tốc độ di chuyển động để thực hiện những thứ như bề mặt nhẵn, tăng sức mạnh, chạy nước rút, v.v. Nhược điểm duy nhất mà chúng tôi còn lại là nếu chúng tôi tiếp tục chẳng hạn như bên trái, chúng tôi có thể dễ dàng bị lạc vào sâu trong vùng hoang dã ngoài khung nhìn; Không bao giờ trở lại. Hãy khắc phục điều đó.

Ràng buộc vị trí của chúng tôi với một ranh giới

Khi tôi nói rằng chúng ta sẽ ràng buộc vị trí của mình với một ranh giới, đó chỉ là một cách nói hoa mỹ rằng chúng ta sẽ buộc tọa độ của mình không vượt quá giá trị tối thiểu hoặc tối đa. Đó là một thủ thuật thực sự đơn giản và mặc dù không nhất thiết phải có mô-đun riêng, nhưng việc sử dụng nó trong mô-đun trình phát của chúng tôi trở nên gọn gàng hơn rất nhiều. Chưa kể chúng tôi luôn có thể mở rộng mô-đun để chứa các hàm trợ giúp khác liên quan đến toán học.

 

 

var

Boundary =

function

numberBoundary

(

min, max

)

{

return

Math

.min (

Math

.max (

this

, min), max); };

Số

.prototype.boundary = Ranh giới;

module

.exports = Boundary;

Tất cả những gì chúng tôi đang làm ở đây là lấy ranh giới tối thiểu và tối đa và áp dụng vị trí hiện tại của chúng tôi để nó không vượt quá hai điểm ranh giới đó. Một số “Eureka!” Bao gồm điều đó vì chúng tôi đang mở rộng nguyên mẫu của đối tượng Number, số của chúng tôi có sẵn cho chúng tôi trong phương thức thông qua this và tôi chỉ xuất nó dưới dạng mô-đun sang hãy duyệt qua để kết hợp tệp với phần còn lại của dự án của chúng tôi.

Giờ đây, chúng tôi có thể quay trở lại mô-đun trình phát của mình và thay đổi phương pháp cập nhật để ràng buộc vị trí của chúng tôi với các ranh giới mà chúng tôi đã đặt ra.

 

var

key =

request

(

'../ utils / utils .keysDown.js '

), mathHelpers =

request

(

'../ utils / utils.math.js'

);

function

Trình phát

(

phạm vi, x, y

)

{ . . . player.update =

function

playerUpdate

(

)

{ . . . player.state.position.x = player.state.position.x.boundary (

0

, (scope.constants.width - width)); player.state.position.y = player.state.position.y.boundary (

0

, (scope.constants.height - height)); }; . . . }

module

.exports = Player;

Tin tưởng tôi, đây là một công cụ gọn gàng hơn RẤT NHIỀU so với việc ném tất cả những thứ tối thiểu đó vào mô-đun. Như tôi đã đề cập trước đây, vì chúng tôi đã mở rộng nguyên mẫu đối tượng Number với Number.prototype.boundary , phương thức đó hiện có sẵn cho tất cả các Numbers trong dự án của chúng tôi. Vì x và y là số chứ không phải là chuỗi, nên chúng có thể sử dụng ranh giới để trả về vị trí thích hợp.

Kết thúc

Vì vậy, hãy xem lại những gì chúng tôi đã làm trong tập này:

  • Đã xây dựng một cấu trúc dự án rad khá dễ đoán và dễ làm việc.
  • Đã tạo một canvas có độ phân giải cao được tạo động và đưa nó vào dom làm chế độ xem.
  • Đã tạo ra một công cụ kết xuất và logic thô sơ bằng cách sử dụng requestAnimationFrame , điều chỉnh động nó đến tốc độ khung hình có thể định cấu hình
  • Đã tạo một máy tính tỷ lệ khung hình xen kẽ hai chu kỳ để theo dõi tốc độ khung hình trong quá trình phát triển
  • Đã tạo thực thể tương tác đầu tiên của chúng tôi, trình phát và thêm logic để di chuyển anh ta xung quanh màn hình
  • Có cảm nhận về cách bổ sung vào nền tảng dự án của chúng tôi

Không quá tồi cho một lần vượt qua đầu tiên. Tôi biết chúng tôi chủ yếu làm những thứ đằng sau hậu trường (kiểu diễn ra mãi mãi, sau đó bạn hào hứng đưa nó cho khách hàng xem và họ nói “uhhh. Đó là nó? Đó là tất cả những gì bạn đã làm? Tôi đang nhìn cái gì vậy?” ) và sự lặp lại hiện tại không phải là điều thú vị nhất trên thế giới, nhưng đó là một nền tảng vững chắc để chúng tôi bắt đầu. Trong một vài bài đăng tiếp theo, chúng ta sẽ bắt đầu đi vào và thực hiện những công việc thú vị như tạo sprite cho các thực thể của chúng ta, tạo cấp độ, viết trong phát hiện va chạm và có thể trích xuất thêm lớp cập nhật logic của chúng tôi để hiệu quả hơn.

Nếu bạn muốn xem toàn bộ mã cho bài đăng này, bạn có thể xem mã trên github repo (cụ thể là thẻ 0.2.0-p1) hoặc bạn có thể < span> tải nó xuống dưới dạng tệp zip hoặc tar.

Chuyến đi an toàn cho đến vòng tiếp theo!

Đề cương / phần cuộc phiêu lưu

  • Phần 1: Công cụ trò chơi cơ bản
  • Phần 2: Tạo thế giới trò chơi của chúng tôi – Sắp ra mắt
  • Phần 3: Sprites, kẻ thù và nguy hiểm – Sắp ra mắt
  • Phần 4: Triển khai hệ thống sự kiện – Sắp có
  • Phần ~: Nhiều hơn sẽ được bổ sung khi chúng được viết!


Xem thêm những thông tin liên quan đến chủ đề cách tạo javascript trò chơi

How To Code The Snake Game In Javascript

  • Tác giả: Web Dev Simplified
  • Ngày đăng: 2020-06-02
  • Đánh giá: 4 ⭐ ( 5881 lượt đánh giá )
  • Khớp với kết quả tìm kiếm: 🚨 IMPORTANT:

    Land A Job With Remote.work: https://remote.work/?utm_source=Web%20Dev%20Simplified&utm_medium=youtube&utm_campaign=june

    In this video I show you how to build the classic snake game using pure JavaScript. That means we will use no libraries or frameworks at all. We won’t even need to use canvas in this tutorial since CSS grid makes working with snake incredibly easy.

    📚 Materials/References:

    GitHub Code: https://github.com/WebDevSimplified/Javascript-Snake-Game

    🧠 Concepts Covered:

    – How to create a game loop
    – How to position elements in a CSS grid
    – Best practices for game development
    – Best practices for breaking up large files

    🌎 Find Me Here:

    My Blog: https://blog.webdevsimplified.com
    My Courses: https://courses.webdevsimplified.com
    Patreon: https://www.patreon.com/WebDevSimplified
    Twitter: https://twitter.com/DevSimplified
    Discord: https://discord.gg/7StTjnR
    GitHub: https://github.com/WebDevSimplified
    CodePen: https://codepen.io/WebDevSimplified

    SnakeGame WDS JavaScript

Sử dụng javascript làm game lật hình up giống nhau

  • Tác giả: codefly.vn
  • Đánh giá: 4 ⭐ ( 5943 lượt đánh giá )
  • Khớp với kết quả tìm kiếm:

Tạo mini game 15 puzzle bằng HTMl/CSS/Javascript

  • Tác giả: blog.vietnamlab.vn
  • Đánh giá: 3 ⭐ ( 5244 lượt đánh giá )
  • Khớp với kết quả tìm kiếm:

Làm Game đơn giản với HTML và Javascript (P.1)

  • Tác giả: viblo.asia
  • Đánh giá: 5 ⭐ ( 9450 lượt đánh giá )
  • Khớp với kết quả tìm kiếm: Khi nói đến phát triển các ứng dụng Game, chúng ta đều nghĩ ngay tới Android. Nhưng có khi nào bạn nghĩ rằng chỉ với HTML và JavaScript bạn cũng có thể phát triển ra những game đơn giản hấp dẫn? Trước…

Hướng dẫn làm game Easy Math bằng JavaScript

  • Tác giả: hocjavascript.net
  • Đánh giá: 4 ⭐ ( 4145 lượt đánh giá )
  • Khớp với kết quả tìm kiếm: Hướng dẫn mọi người làm một game tương tự như vậy đơn giản trong khoảng 30 phút với chỉ JavaScript (làm game Easy Math bằng JavaScript).

Làm Game Rắn Săn Mồi Bằng Javascript

  • Tác giả: codelearn.io
  • Đánh giá: 5 ⭐ ( 9350 lượt đánh giá )
  • Khớp với kết quả tìm kiếm: Sau khi tự lập trình game, bạn sẽ nắm vững hơn một số khái niệm trong Javascript cũng như biết cách dùng p5.js để làm đồ họa, animation trên web.

Trò chơi được lập trình bằng JavaScript

  • Tác giả: www.javascriptbank.com
  • Đánh giá: 3 ⭐ ( 3246 lượt đánh giá )
  • Khớp với kết quả tìm kiếm: Các trò chơi được viết và chơi bằng JavaScript. Nhỏ nhưng mà hay. – Trang 1 – Ngôn ngữ: tiếng Việt

Xem thêm các bài viết khác thuộc chuyên mục: Kiến thức lập trình

Xem Thêm  Node.js - Hệ thống tệp - nút js tệp mới