Lập trình game 2D trên HTML5 - Phần 2: Đồ họa và ảnh động

Tóm tắt Lập trình game 2D trên HTML5 - Phần 2: Đồ họa và ảnh động: ...c hiện Snail Bait. Với phần còn lại của loạt bài này, tôi sẽ tập trung vào các khía cạnh khác về phát triển trò chơi với HTML5, bắt đầu với hình ảnh động. Về đầu trang Hình ảnh động trong HTML5 Về cơ bản, việc thực hiện hình ảnh động rất đơn giản: Bạn vẽ nhiều lần một chuỗi các hình ảnh để...ame(). Các bản triển khai thực hiện có thêm tiền tố chỉ nhà cung cấp và sự hỗ trợ khác nhau cho việc thực hiện các tiêu chuẩn làm cho chức năng mới khó sử dụng, do đó, cộng đồng HTML5 đã phát minh ra một thứ được gọi là một polyfill. Các polyfill xác định mức hỗ trợ của trình duyệt cho một t...kê 9. Tính fps và cập nhật phần tử fps var lastAnimationFrameTime = 0, lastFpsUpdateTime = 0, fpsElement = document.getElementById('fps'); function calculateFps(now) { var fps = 1000 / (now - lastAnimationFrameTime); lastAnimationFrameTime = now; if (now - lastFpsUpdateTime ...

pdf18 trang | Chia sẻ: havih72 | Lượt xem: 188 | Lượt tải: 0download
Nội dung tài liệu Lập trình game 2D trên HTML5 - Phần 2: Đồ họa và ảnh động, để tải tài liệu về máy bạn click vào nút DOWNLOAD ở trên
đối tượng trong mảng platformData — để thiết lập kiểu tô đặc của bối 
cảnh và thuộc tính globalAlpha, thiết lập mức độ mờ của bất cứ thứ gì để sau đó bạn vẽ trong 
canvas. 
Lời gọi hàm context.translate() chuyển dịch hệ thống tọa độ của canvas — được mô tả 
trong Hình 2 — theo một số điểm ảnh (pixel) đã quy định theo chiều ngang. Việc chuyển dịch đó 
và các giá trị thiết lập thuộc tính chỉ là tạm thời vì chúng được thực hiện giữa các lời gọi đến 
context.save() và context.restore(). 
Hình 2. Hệ thống tọa độ Canvas mặc định 
Theo mặc định, gốc của hệ tọa độ là ở góc trên bên trái của canvas. Bạn có thể di chuyển gốc của 
hệ tọa độ bằng context.translate(). 
Tôi thảo luận về việc cuộn di chuyển background bằng context.translate() trong phần Cuộn 
di chuyển nền sau. Nhưng vào lúc này, bạn đã biết gần như mọi thứ cần biết về Canvas HTML5 
để thực hiện Snail Bait. Với phần còn lại của loạt bài này, tôi sẽ tập trung vào các khía cạnh khác 
về phát triển trò chơi với HTML5, bắt đầu với hình ảnh động. 
Về đầu trang 
Hình ảnh động trong HTML5 
Về cơ bản, việc thực hiện hình ảnh động rất đơn giản: Bạn vẽ nhiều lần một chuỗi các hình ảnh 
để làm cho nó xuất hiện như thể các đối tượng đang hoạt động theo một cách nào đó. Điều đó có 
nghĩa là bạn phải thực hiện một vòng lặp để vẽ một hình ảnh theo định kỳ. 
Theo truyền thống, các vòng lặp hình ảnh động đã được thực hiện trong JavaScript bằng 
setTimeout() hoặc như minh họa trong Liệt kê 4, bằng setInterval(): 
Liệt kê 4. Thực hiện các hình ảnh động bằng setInterval() 
setInterval( function (e) { // Don't do this for time-critical animations 
 animate(); // A function that draws the current animation 
frame 
}, 1000 / 60); // Approximately 60 frames/second (fps) 
Lời khuyên 
Đừng bao giờ sử dụng setTimeout() hay setInterval() cho các hình ảnh động hạn chế thời 
gian. 
Mã trong Liệt kê 4 chắc chắn sẽ tạo ra một hình ảnh động bằng cách liên tục gọi một hàm 
animate() để vẽ khung hình của hình ảnh động tiếp theo; tuy nhiên, bạn có thể không hài lòng 
với các kết quả đó, vì setInterval() và setTimeout() không biết gì về hình ảnh động. (Lưu ý: 
Bạn phải thực hiện hàm animate(); nó không phải là một phần của API Canvas). 
Trong Liệt kê 4, tôi thiết lập khoảng thời gian 1000/60 mili giây, tương đương với khoảng 60 
khung hình mỗi giây. Con số đó là ước tính tốt nhất của tôi về một tốc độ khung hình tối ưu và 
có thể nó không phải là một tốc độ rất tốt; tuy nhiên, vì setInterval() và setTimeout() không 
biết bất cứ điều gì về hình ảnh động, nên tôi phải quy định tốc độ khung hình. Sẽ là tốt hơn nếu 
thay vào đó trình duyệt quy định tốc độ khung hình, vì chắc chắn nó biết tốt hơn tôi khi nào cần 
vẽ khung hình ảnh động tiếp theo. 
Có một nhược điểm thậm chí còn nghiêm trọng hơn với việc sử dụng setTimeout và 
setInterval(). Mặc dù bạn chuyển cho các phương thức đó những khoảng thời gian tính bằng 
mili giây, nhưng các phương thức đó không làm chính xác đến milli giây. Trong thực tế, theo đặc 
tả của HTML, các phương thức này có thể kéo dài thoải mái thêm khoảng thời gian mà bạn quy 
định — vì nỗ lực để bảo tồn tài nguyên. 
Để tránh những nhược điểm này, bạn không nên sử dụng setTimeout() và setInterval() cho 
các hình ảnh động hạn chế thời gian; thay vào đó, bạn nên sử dụng phương thức 
requestAnimationFrame(). 
requestAnimationFrame() 
Trong đặc tả Kiểm soát định thời gian cho hình ảnh động dựa trên kịch bản lệnh (xem phần Tài 
nguyên), W3C định nghĩa một phương thức dựa trên đối tượng window có tên là 
requestAnimationFrame(). Không giống như setTimeout() hay setInterval(), phương 
thức requestAnimationFrame() đặc biệt được dành cho việc thực hiện hình ảnh động. Do đó, 
nó không bị một nhược điểm nào liên quan đến setTimeout() và setInterval(). Nó cũng dễ 
sử dụng, như Liệt kê 5 minh họa: 
Liệt kê 5. Thực hiện hình ảnh động bằng requestAnimationFrame() 
function animate(time) { // Animation loop 
 draw(time); // A function that draws the current 
animation frame 
 requestAnimationFrame(animate); // Keep the animation going 
}; 
requestAnimationFrame(animate); // Start the animation 
Bạn chuyển cho phương thức requestAnimationFrame() một tài liệu tham khảo đến một hàm 
gọi lại và khi trình duyệt đã sẵn sàng vẽ khung hình ảnh động tiếp theo, nó gọi hàm gọi lại này. 
Để duy trì hình ảnh động, hàm gọi lại cũng gọi phương thức requestAnimationFrame(). 
Như bạn có thể thấy trong Liệt kê 5, trình duyệt chuyển một tham số time tới hàm gọi lại của 
bạn. Bạn có thể tự hỏi rằng chính xác tham số time có nghĩa là gì. Đó có phải là thời điểm hiện 
tại không? Có phải là thời điểm mà lúc đó trình duyệt sẽ vẽ khung hình ảnh động tiếp theo 
không? 
Thật đáng ngạc nhiên, không có định nghĩa nào về tham số time đó. Điều duy nhất mà bạn có thể 
tin chắc là đối với bất kỳ trình duyệt đã cho nào, tham số time luôn luôn chỉ biểu diễn thời gian 
mà thôi; do đó, bạn có thể sử dụng nó để tính toán thời gian trôi qua giữa các khung hình, như tôi 
minh họa trong Tính toán tốc độ hình ảnh động theo số khung hình mỗi giây (fps). 
Một polyfill của requestAnimationFrame() 
Trên nhiều phương diện, HTML5 là một thiên đường viễn tưởng của lập trình viên. Thoát khỏi 
các API độc quyền, các nhà phát triển sử dụng HTML5 để thực hiện các ứng dụng chạy trên 
nhiều nền tảng trong các trình duyệt phổ biến. Các đặc tả tiến bộ nhanh chóng, liên tục kết hợp 
với công nghệ mới và tinh chỉnh chức năng hiện có. 
Các Polyfill: Lập trình cho tương lai 
Trong quá khứ, hầu hết các phần mềm nhiều nền tảng đã được thực hiện với mẫu thức chung nhỏ 
nhất. Các Polyfill thay đổi hoàn toàn khái niệm đó bằng cách cho phép bạn truy cập vào các tính 
năng nâng cao nếu chúng có sẵn và quay lại với bản thực hiện kém khả năng hơn khi cần. 
Tuy nhiên, công nghệ mới thường đi vào đặc tả kỹ thuật theo cách của mình, thông qua các chức 
năng đặc trưng của trình duyệt hiện có. Các nhà cung cấp trình duyệt thường thêm phần tiền tố 
vào các chức năng như vậy để nó không can nhiễu với bản thực hiện của trình duyệt khác; ví dụ; 
phương thức requestAnimationFrame(), ban đầu đã được Mozilla thực hiện như 
mozRequestAnimationFrame(). Sau đó, WebKit đã thực hiện nó với tên là hàm 
webkitRequestAnimationFrame(). Cuối cùng, W3C đã tiêu chuẩn hóa nó thành 
requestAnimationFrame(). 
Các bản triển khai thực hiện có thêm tiền tố chỉ nhà cung cấp và sự hỗ trợ khác nhau cho việc 
thực hiện các tiêu chuẩn làm cho chức năng mới khó sử dụng, do đó, cộng đồng HTML5 đã phát 
minh ra một thứ được gọi là một polyfill. Các polyfill xác định mức hỗ trợ của trình duyệt cho 
một tính năng cụ thể và cung cấp cho bạn quyền truy cập trực tiếp nếu trình duyệt thực hiện nó 
hoặc một bản thực hiện tạm thời để bắt chước chức năng tiêu chuẩn đến mức tốt nhất có thể 
được. 
Các polyfill sử dụng đơn giản, nhưng có thể phức tạp khi triển khai thực hiện. Liệt kê 6 cho thấy 
việc thực hiện một polyfill dành cho requestAnimationFrame(): 
Liệt kê 6. Polyfill của requestNextAnimationFrame() 
// Reprinted from Core HTML5 Canvas 
window.requestNextAnimationFrame = 
 (function () { 
 var originalWebkitRequestAnimationFrame = undefined, 
 wrapper = undefined, 
 callback = undefined, 
 geckoVersion = 0, 
 userAgent = navigator.userAgent, 
 index = 0, 
 self = this; 
 // Workaround for Chrome 10 bug where Chrome 
 // does not pass the time to the animation function 
 if (window.webkitRequestAnimationFrame) { 
 // Define the wrapper 
 wrapper = function (time) { 
 if (time === undefined) { 
 time = +new Date(); 
 } 
 self.callback(time); 
 }; 
 // Make the switch 
 originalWebkitRequestAnimationFrame = 
window.webkitRequestAnimationFrame; 
 window.webkitRequestAnimationFrame = function (callback, element) { 
 self.callback = callback; 
 // Browser calls the wrapper and wrapper calls the callback 
 originalWebkitRequestAnimationFrame(wrapper, element); 
 } 
 } 
 // Workaround for Gecko 2.0, which has a bug in 
 // mozRequestAnimationFrame() that restricts animations 
 // to 30-40 fps. 
 if (window.mozRequestAnimationFrame) { 
 // Check the Gecko version. Gecko is used by browsers 
 // other than Firefox. Gecko 2.0 corresponds to 
 // Firefox 4.0. 
 index = userAgent.indexOf('rv:'); 
 if (userAgent.indexOf('Gecko') != -1) { 
 geckoVersion = userAgent.substr(index + 3, 3); 
 if (geckoVersion === '2.0') { 
 // Forces the return statement to fall through 
 // to the setTimeout() function. 
 window.mozRequestAnimationFrame = undefined; 
 } 
 } 
 } 
 return window.requestAnimationFrame || 
 window.webkitRequestAnimationFrame || 
 window.mozRequestAnimationFrame || 
 window.oRequestAnimationFrame || 
 window.msRequestAnimationFrame || 
 function (callback, element) { 
 var start, 
 finish; 
 window.setTimeout( function () { 
 start = +new Date(); 
 callback(start); 
 finish = +new Date(); 
 self.timeout = 1000 / 60 - (finish - start); 
 }, self.timeout); 
 }; 
 } 
 ) 
(); 
Polyfill: Định nghĩa 
Từ polyfill là một từ ghép của hai từ polymorphism (đa hình) và backfill. Giống như đa hình, các 
polyfill chọn mã thích hợp trong thời gian chạy và chúng lấp lại chức năng còn thiếu. 
Polyfill được thực hiện trong Liệt kê 6 gắn một hàm có tên là requestNextAnimationFrame() 
vào đối tượng window. Việc đưa Next vào tên hàm là để phân biệt nó với hàm 
requestAnimationFrame() cơ bản. 
Hàm mà polyfill gán cho requestNextAnimationFrame() hoặc là requestAnimationFrame() 
nếu trình duyệt hỗ trợ nó hoặc là bản thực hiện có tên tiền tố chỉ nhà cung cấp. Nếu trình duyệt 
không hỗ trợ cả hai thứ đó, hàm này sẽ là một bản thực hiện tạm thời, sử dụng setTimeout() để 
bắt chước requestAnimationFrame() một cách tốt nhất có thể. 
Gần như tất cả tính phức tạp của polyfill đều liên quan đến việc giải quyết hai lỗi và tạo thành 
mã trước câu lệnh return. Lỗi đầu tiên liên quan đến Chrome 10, đó là nó chuyển một giá trị 
undefined (không xác định) cho tham số time. Lỗi thứ hai liên quan đến Firefox 4.0, đó là nó 
hạn chế tốc độ khung hình ở 35-40 khung hình mỗi giây. 
Mặc dù bản thực hiện polyfill của requestNextAnimationFrame() là thú vị, nhưng không cần 
thiết phải hiểu nó; thay vào đó, tất cả mọi thứ mà bạn cần biết là cách sử dụng nó, như tôi sẽ 
minh họa trong phần tiếp theo. 
Về đầu trang 
Vòng lặp trò chơi 
Bây giờ các điều tiên quyết về đồ họa và hình ảnh động đã xong, đây là lúc đưa Snail Bait vào 
chuyển động. Để bắt đầu, tôi đưa mã JavaScript dùng cho requestNextAnimationFrame() vào 
HTML của trò chơi, như thể hiện trong 7: 
Liệt kê 7. HTML 
 ... 
 ... 
Liệt kê 8 cho thấy vòng lặp hình ảnh động của trò chơi, thường được gọi là vòng lặp trò chơi: 
Liệt kê 8. Vòng lặp trò chơi 
var fps; 
function animate(now) { 
 fps = calculateFps(now); 
 draw(); 
 requestNextAnimationFrame(animate); 
} 
function startGame() { 
 requestNextAnimationFrame(animate); 
} 
Hàm startGame(), do trình xử lý sự kiện onload của hình ảnh nền sau gọi ra, khởi động trò 
chơi bằng cách gọi polyfill requestNextAnimationFrame(). Khi đến thời gian vẽ khung hình 
của hình ảnh động đầu tiên của trò chơi, trình duyệt gọi hàm animate(). 
Hàm animate() tính toán tốc độ khung hình của hình ảnh động, dựa vào giá trị hiện tại của tham 
số time. (Xem requestAnimationFrame() để biết thêm về giá trị time). Sau khi tính toán tốc 
độ khung hình, hàm animate() sẽ gọi một hàm draw() để vẽ khung hình của hình ảnh động tiếp 
theo. Sau đó, hàm animate() gọi requestNextAnimationFrame() để duy trì hình ảnh động. 
Tính toán tốc độ hình ảnh động theo số khung hình mỗi giây (fps) 
Liệt kê 9 cho thấy cách Snail Bait tính toán tốc độ khung hình của mình và cách nó cập nhật giá 
trị đọc tốc độ khung hình được thể hiện trong Hình 1: 
Liệt kê 9. Tính fps và cập nhật phần tử fps 
var lastAnimationFrameTime = 0, 
 lastFpsUpdateTime = 0, 
 fpsElement = document.getElementById('fps'); 
function calculateFps(now) { 
 var fps = 1000 
 / (now - 
 lastAnimationFrameTime); 
 lastAnimationFrameTime = now; 
 if (now - lastFpsUpdateTime > 1000) { 
 lastFpsUpdateTime = now; 
 fpsElement.innerHTML = fps.toFixed(0) + ' fps'; 
 } 
 return fps; 
} 
Tốc độ khung hình chỉ đơn giản là khoảng thời gian trôi qua tính từ khung hình của hình ảnh 
động cuối cùng, vì vậy bạn có thể lý luận rằng đó là khung hình mỗi giây thay vì nhiều khung 
hình mỗi giây và điều đó làm cho nó không có vẻ nhanh chút nào. Bạn có thể theo cách tiếp cận 
chặt chẽ hơn và duy trì một tốc độ khung hình trung bình qua vài khung hình, nhưng tôi đã thấy 
là không cần thiết; thực vậy, thời gian trôi qua kể từ khung hình của hình ảnh động cuối cùng 
chính xác là những gì tôi sẽ cần trong phần Chuyển động theo thời gian. 
Liệt kê 9 cũng minh họa một kỹ thuật hình ảnh động quan trọng: thực hiện một nhiệm vụ với 
một tốc độ khác so với tốc độ của hình ảnh động. Nếu tôi cập nhật giá trị đọc số khung hình/giây 
cho từng khung hình của hình ảnh động, thì sẽ không thể đọc được vì nó sẽ luôn thay đổi; thay 
vào đó, tôi cập nhật giá trị đọc đó mỗi giây một lần. 
Với vòng lặp trò chơi có sẵn và tốc độ khung hình trong tay, bây giờ tôi đã sẵn sàng để cuộn 
background. 
Về đầu trang 
Cuộn background 
Nền sau của Snail Bait, được thể hiện trong Hình 3, cuộn di chuyển chậm theo chiều ngang: 
Hình 3. Hình ảnh background 
Background sẽ cuộn liên tục vì các lề trái và lề phải của background giống hệt nhau, như minh 
họa ở Hình 4: 
Hình 4. Các lề giống hệt nhau làm cho các quá trình chuyển tiếp trơn tru (trái: lề phải; 
phải: lề trái) 
Snail Bait không ngừng cuộn backgroud bằng cách vẽ nó hai lần, như thể hiện trong Hình 5. Ban 
đầu, như thể hiện trong ảnh chụp màn hình ở trên cùng trong Hình 5, hình ảnh nền sau ở bên trái 
hoàn toàn nằm trong màn hình, trong khi một hình ảnh nền sau ở bên phải hoàn toàn ở ngoài 
màn hình. Khi thời gian trôi đi, nền sau cuộn di chuyển, như được minh họa bằng các ảnh chụp 
màn hình ở giữa và ở dưới cùng trong Hình 5: 
Hình 5. Cuộn từ phải sang trái: các vùng mờ biểu diễn các phần hình ảnh nằm ngoài màn 
hình 
Liệt kê 10 cho thấy mã tương quan với Hình 5. Hàm drawBackground() vẽ hình ảnh hai lần, 
luôn luôn ở cùng một vị trí. Việc cuộn di chuyển nhìn thấy được là kết quả của việc liên tục 
chuyển dịch hệ thống tọa độ của canvas sang trái, làm cho background có vẻ như được cuộn sang 
phải. 
(Đây là cách bạn có thể dung hòa vẻ mâu thuẫn bên ngoài của việc chuyển dịch sang trái nhưng 
lại là cuộn di chuyển hình sang phải: Hãy tưởng tượng canvas như một khung ảnh rỗng trên đỉnh 
của một tờ giấy dài. Tờ giấy này là hệ thống tọa độ và việc chuyển dịch nó sang trái giống như 
trượt nó sang bên trái bên dưới khung nền ảnh [canvas]. Do đó, canvas có vẻ như di chuyển sang 
bên phải). 
Liệt kê 10. Cuộn background 
var backgroundOffset; // This is set before calling drawBackground() 
function drawBackground() { 
 context.translate(-backgroundOffset, 
 0); 
 // Initially onscreen: 
 context.drawImage(background, 0, 0); 
 // Initially offscreen: 
 context.drawImage(background, background.width, 0); 
 context.translate(backgroundOffset, 
 0); 
} 
Hàm setBackground() chuyển dịch các điểm ảnh theo -backgroundOffset của bối cảnh 
canvas theo chiều ngang. Nếu backgroundOffset là số dương, nền sau sẽ di chuyển sang phải; 
nếu nó là số âm, nền sau sẽ di chuyển trái. 
Sau khi chuyển dịch nền sau, drawBackground() vẽ hình ảnh nền sau hai lần rồi chuyển dịch 
bối cảnh quay lại nơi nó đã có trước khi gọi drawBackground(). 
Vẫn còn một bước tính: tính backgroundOffset, để xác định sẽ chuyển dịch hệ thống tọa độ của 
canvas cho mỗi khung hình của hình ảnh động đi xa bao nhiêu. Mặc dù bản thân việc tính toán 
này có vẻ tầm thường, nhưng nó có ý nghĩa rất quan trọng, vì vậy tôi sẽ thảo luận nó tiếp theo. 
Về đầu trang 
Chuyển động theo thời gian 
Tốc độ khung hình của hình ảnh động của bạn sẽ thay đổi, nhưng bạn phải ngăn không để cho sự 
thay đổi tốc độ khung hình ảnh hưởng đến tốc độ tiến triển hình ảnh động của bạn. Ví dụ, Snail 
Bait cuộn background với tốc độ 42 điểm ảnh/giây bất kể tốc độ khung hình cơ bản của hình ảnh 
động. Các hình ảnh động phải dựa vào thời gian, có nghĩa là các vận tốc được quy định theo số 
điểm ảnh/giây và phải không phụ thuộc vào tốc độ khung hình. 
Việc sử dụng chuyển động dựa theo thời gian để tính toán số lượng các điểm ảnh cần phải di 
chuyển một đối tượng cho một khung hình đã cho rất đơn giản: Chia vận tốc cho tốc độ khung 
hình hiện tại. Vận tốc (số điểm ảnh/giây) chia cho tốc độ khung hình (số khung hình/giây) ra kết 
quả là số điểm ảnh/khung hình, có nghĩa là số lượng điểm ảnh mà bạn cần di chuyển một cái gì 
đó đối với khung hình hiện tại. 
Gợi ý 
Tốc độ hình ảnh động phải độc lập với tốc độ khung hình. 
Liệt kê 11 cho thấy cách Snail Bait sử dụng chuyển động dựa theo thời gian để tính toán độ dịch 
chuyển của background: 
Liệt kê 11. Thiết lập giá trị độ dịch chuyển của background 
var BACKGROUND_VELOCITY = 42, // pixels / second 
 bgVelocity = BACKGROUND_VELOCITY; 
function setBackgroundOffset() { 
 var offset = backgroundOffset + bgVelocity/fps; // Time-based motion 
 if (offset > 0 && offset < background.width) { 
 backgroundOffset = offset; 
 } 
 else { 
 backgroundOffset = 0; 
 } 
} 
Hàm setBackgroundOffset() tính toán số lượng điểm ảnh cần dịch chuyển background đối với 
khung hình hiện tại bằng cách chia vận tốc của background cho tốc độ khung hình hiện tại. Sau 
đó, nó cộng giá trị đó vào độ dịch chuyển background hiện có. 
Để liên tục cuộn background, hàm setBackgroundOffset() thiết lập lại độ dịch chuyển nền sau 
bằng 0 khi nó trở nên nhỏ hơn 0 hoặc lớn hơn độ rộng của nền sau. 
Về đầu trang 
Thị sai 
Nếu bạn đã từng ngồi ở ghế hành khách của một chiếc xe đang chạy và nhìn con dao nhíp trên 
tay bạn qua các cột điện thoại ở tốc độ cao, bạn biết rằng những thứ gần với bạn di chuyển nhanh 
hơn so với những thứ ở xa hơn. Điều đó được gọi là thị sai. 
Snail Bait là một trò chơi nhảy bậc thềm 2D (2D platformer), nhưng nó sử dụng một chút hiệu 
ứng thị sai để làm cho các bậc thềm có vẻ như ở gần bạn hơn so với background. Trò chơi này 
thực hiện thị sai đó bằng cách cuộn di chuyển các bậc thềm cần lưu ý nhanh hơn đáng kể so với 
background. 
Hình 6 minh họa cách Snail Bait thực hiện thị sai. Ảnh chụp màn hình trên cùng cho thấy 
background tại một thời điểm cụ thể, trong khi ảnh chụp màn hình phía dưới cùng cho thấy 
background tại một vài khung hình của hình ảnh động sau đó. Từ hai ảnh chụp màn hình đó, bạn 
có thể thấy rằng các bậc thềm đã di chuyển xa hơn background trong cùng một khoảng thời gian. 
Hình 6. Thị sai: Các bậc thềm (gần) di chuyển nhanh hơn so với background (xa) 
Liệt kê 12 cho thấy các hàm thiết lập các vận tốc và các độ dịch chuyển của bậc thềm: 
Liệt kê 12. Thiết lập các vận tốc và các độ dịch chuyển của bậc thềm 
var PLATFORM_VELOCITY_MULTIPLIER = 4.35; 
function setPlatformVelocity() { 
 // Platforms move 4.35 times as fast as the background 
 platformVelocity = bgVelocity * PLATFORM_VELOCITY_MULTIPLIER; 
} 
function setPlatformOffset() { 
 platformOffset += platformVelocity/fps; // Time-based motion 
} 
Xem lại Liệt kê 8 có liệt kê vòng lặp trò chơi Snail Bait. Vòng lặp đó gồm một hàm animate() 
để trình duyệt sẽ gọi khi đến thời gian vẽ khung hình của hình ảnh động tiếp theo của trò chơi. 
Đến lượt mình, hàm animate() đó gọi một hàm draw() để vẽ khung hình của hình ảnh động 
tiếp theo. Mã dùng cho hàm draw() ở giai đoạn phát triển này được hiển thị trong Liệt kê 13: 
Liệt kê 13. Hàm draw() 
function setOffsets() { 
 setBackgroundOffset(); 
 setPlatformOffset(); 
} 
function draw() { 
 setPlatformVelocity(); 
 setOffsets(); 
 drawBackground(); 
 drawRunner(); 
 drawPlatforms(); 
} 
Hàm draw() thiết lập vận tốc và các độ dịch chuyển cho cả hai, background và các bậc thềm. 
Sau đó, nó vẽ background, nhân vật đang chạy và các bậc thềm. 

File đính kèm:

  • pdflap_trinh_game_2d_tren_html5_phan_2_do_hoa_va_anh_dong.pdf