Lập trình game 2D trên HTML5, Phần 1: Giới thiệu Snail Bait

Bắt đầu với trò chơi đi cảnh đầu tiên của bạn

Trong loạt bài này, chuyên gia HTML5 David Geary sẽ từng bước một giới thiệu cho bạn cách thực hiện một trò chơi video 2D trên HTML5. Bài đầu tiên này giới thiệu cho bạn trò chơi đã hoàn thành và sau đó giúp bạn bắt đầu thực hiện nó ngay từ đầu. Nếu bạn đã từng muốn tạo ra một trò chơi HTML5 nhưng không có thời gian để nắm vững mọi chi tiết, thì đây là loạt bài dành cho bạn.

David Geary, Tác giả và diễn giả, Clarity Training, Inc.

Ảnh của David GearyDavid Geary, tác giả của quyển sách Core HTML5 Canvas, cũng là đồng sáng lập của Nhóm người dùng HTML5 Denver và là tác giả của 8 cuốn sách Java, bao gồm cả những cuốn sách bán chạy nhất về Swing và JavaServer Faces. David là một diễn giả thường xuyên tại các hội nghị, bao gồm JavaOne, Devoxx, Loop Strange, NDC và OSCON và ông đã ba lần đạt danh hiệu JavaOne Rock Star (diễn giả hàng đầu tại hội nghị JavaOne). Ông đã viết loạt bài JSF 2 fuGWT fu cho developerWorks. Bạn có thể theo dõi David trên Twitter tại @davidgeary.



16 04 2013

Điều tuyệt vời khi phát triển phần mềm là bạn có thể đưa bất cứ điều gì sống động mà bạn có thể tưởng tượng ra lên màn hình, miễn là chúng có lý. Do không bị vướng vào những hạn chế vật lý đã cản trở các kỹ sư trong các ngành khác, nên các nhà phát triển phần mềm từ lâu đã sử dụng các bộ công cụ API đồ họa và giao diện người dùng (UI) để thực hiện các ứng dụng sáng tạo và hấp dẫn. Người ta có thể cho rằng dạng phát triển phần mềm sáng tạo nhất là lập trình trò chơi; một vài nỗ lực đáng làm theo quan điểm sáng tạo còn hơn cả việc biến cái nhìn của bạn về trò chơi trở thành hiện thực.

Các trò chơi đi cảnh (Platform Games - PG)

Donkey Kong, Mario Bros., Sonic the HedgehogBraid tất cả đều là các trò chơi nổi tiếng, bán chạy nhất và tất cả chúng đều là những trò chơi đi cảnh. Có lúc các trò chơi đi cảnh đã chiếm 1/3 doanh thu của tất cả trò chơi video. Hiện nay, thị phần của chúng đã thấp hơn đáng kể, nhưng vẫn còn có nhiều trò chơi đi cảnh thành công.

Tuy nhiên, điều đáng làm không có nghĩa là dễ làm; thực ra, thường là ngược lại. Việc thực hiện các trò chơi, đặc biệt là các trò chơi video, đòi hỏi phải có một sự hiểu biết tốt về lập trình, hiểu biết tốt về các hình ảnh đồ họa và ảnh động và rất nhiều kiến thức toán học pha trộn với liều lượng đáng kể về tính nghệ thuật và tính sáng tạo. Và đó chỉ là sự khởi đầu. Các nhà phát triển trò chơi thành công dành rất nhiều thời gian để trau chuốt trò chơi của mình bằng cách tinh lọc cách chơi trò chơi và các hình ảnh đồ họa, ngoài việc thực hiện nhiều khía cạnh của trò chơi không liên quan gì đến cách chơi — chẳng hạn như các bảng ghi điểm, các hướng dẫn, các hình ảnh động giữa các vòng đời và các mức chơi và các trình tự kết thúc trò chơi.

Mục đích của loạt bài này là giới thiệu cho bạn cách thực hiện một trò chơi video HTML5 để bạn có thể bắt đầu làm việc với một trò chơi riêng của mình.

Hãy xem nội dung video "Giới thiệu về loạt bài này" ở đây.

Trò chơi: Snail Bait

Trong loạt bài này, tôi sẽ hướng dẫn bạn cách thực hiện một trò chơi đi cảnh đầu tiên bằng HTML5 Canvas API (API Khung nền ảnh HTML5). Trò chơi đó là Snail Bait, được hiển thị trong Hình 1. Bạn có thể chơi trò chơi này trực tuyến, xem phần Tài nguyên để có một liên kết đến trò chơi này. Hãy bảo đảm rằng trình duyệt của bạn có tăng tốc phần cứng cho Canvas (chỉ vừa mới được triển khai thực hiện trong hầu hết các trình duyệt, trong đó có Chrome kể từ phiên bản 18); nếu không, hiệu năng của Snail Bait sẽ vô cùng kém. (Xem ô bên Hiệu năng của HTML5 Canvas để biết thêm thông tin).

Hình 1. Snail Bait chạy trong Chrome
Ảnh chụp màn hình của Snail Bait chạy trong Chrome

Các công nghệ HTML5 được dùng trong Snail Bait

  • Canvas (2D API)
  • Điều khiển thời gian cho các hình ảnh động dựa trên kịch bản lệnh
  • Âm thanh
  • CSS3 (các quá trình chuyển tiếp và các truy vấn phương tiện)

Snail Bait là một trò chơi đi cảnh cổ điển. Nhân vật chính, mà tôi sẽ đơn giản gọi là cô bé đang chạy, chạy dọc theo các bậc thềm và nhảy qua lại giữa các bậc thềm trôi nổi di chuyển theo chiều ngang. Mục tiêu cuối cùng của nhân vật là đến được một bậc thềm rung động có một nút nhấn vàng ở cuối mức chơi. Tất cả nhân vật, bậc thềm đung đưa và nút nhấn vàng đều được hiển thị trong Hình 1.

Người chơi điều khiển nhân vật bằng bàn phím: phím d để di chuyển nhân vật sang trái, phím k di chuyển nhân vật sang phải, phím j hay f tạo bước nhảy của nhân vật và phím p để tạm dừng trò chơi.

Khi trò chơi bắt đầu, nhân vật của bạn có ba 'mạng'. Các biểu tượng nhân vật biểu thị số 'mạng' còn lại được hiển thị ở phía trên bên trái của khung nền ảnh của trò chơi, như bạn có thể thấy trong Hình 1. Trong hình trình của nhân vật, để đi đến cuối màn chơi, nhân vật phải tránh những kẻ xấu — những con ong, con dơi và ốc sên — đồng thời cố gắng thu thập các thứ có giá trị như đồng tiền, những viên hồng ngọc và ngọc bích. Nếu nhân vật va chạm vào những kẻ xấu, nhân vật sẽ bị nổ tung, bạn sẽ mất một mạng và phải quay lại màn chơi từ đầu. Khi nhân vật chạm vào những thứ tốt, số điểm của bạn sẽ tăng lên và phát ra một hiệu ứng âm thanh.

Có dùng các phím WASD không?

Theo quy ước, các trò chơi máy tính thường sử dụng các phím w, a, s và d để điều khiển trò chơi. Quy ước đó được phát triển chủ yếu là vì nó cho phép những người chơi thuận tay phải sử dụng chuột và bàn phím đồng thời. Nó cũng để cho tay phải tự do để nhấn phím spacebar hoặc các phím bổ trợ chẳng hạn như phím Ctrl hoặc ALT. Snail Bait không sử dụng các phím WASD vì nó không nhận đầu vào từ chuột hoặc các phím bổ trợ. Nhưng bạn có thể dễ dàng sửa đổi mã của trò chơi để sử dụng bất kỳ tổ hợp các phím nào.

'Những kẻ xấu' thường được treo lơ lửng đâu đó để trực chờ nhân vật của chúng ta chạm vào chúng. Tuy nhiên, ốc sên thì lâu lâu lại bắn các bom ốc sên (quả bóng bạc được hiển thị gần giữa Hình 1). Các quả bom, cũng giống như những kẻ xấu khác, làm nổ tung nhân vật nếu như vô tình chạm vào chúng.

Trò chơi kết thúc theo một trong hai cách: bạn mất tất cả ba mạng hoặc bạn đưa nhân vật đến bậc thềm đung đưa (bạn sẽ được điểm thưởng khi đáp xuống nút nhấn màu vàng). Dù bằng cách nào đi nữa, trò chơi sẽ kết thúc bằng danh sách những người đã thực hiện trò chơi được thể hiện trong Hình 2:

Hình 2. Danh sách những người đã thực hiện trò chơi
Ảnh chụp màn hình của chuỗi kết thúc trò chơi của Snail Bait

Điều bạn không thể nhìn thấy trong Hình 1 là mọi thứ — trừ nhân vật do bạn đang điều khiển di chuyển — một cách liên tục. Chuyển động cuộn này tiếp tục phân loại Snail Bait như là một trò chơi đi cảnh cuộn sang phải. Tuy nhiên, đó không phải là chuyển động duy nhất trong trò chơi này để dẫn tôi đến các hình ảnh 2D (hai chiều) và các hành vi của chúng.


Các hình ảnh 2D: Phân vai các nhân vật

Hiệu năng của HTML5 Canvas

Gần đây, hầu hết các trình duyệt đều thực hiện tăng tốc phần cứng cho các quá trình chuyển tiếp CSS nhưng vẫn chưa làm được như vậy với Canvas. Canvas đã luôn tương đối nhanh, đặc biệt là so với các hệ thống đồ họa khác như Scalable Vector Graphics (SVG - Đồ họa Vectơ co giãn được), nhưng Canvas không có sự tăng tốc phần cứng là không khớp với bất cứ thứ gì có tăng tốc phần cứng.

Bây giờ, tất cả các trình duyệt hiện đại đều tăng tốc phần cứng cho các phần tử Canvas. iOS 5 cũng vậy, có nghĩa là các trò chơi video dựa trên Canvas với hình ảnh động mượt mà bây giờ có thể chạy không chỉ trên máy tính để bàn, mà cả trên các thiết bị di động của Apple.

Ngoại trừ nền sau, mọi thứ trong Snail Bait đều là một hình ảnh 2D (sprite). Một hình ảnh 2D là một đối tượng mà bạn có thể vẽ trên khung nền ảnh của trò chơi. Các hình ảnh 2D không phải là một phần của API Canvas, nhưng chúng đơn giản và dễ thực hiện. Các hình ảnh 2D của trò chơi là:

  • Các bậc thềm (các vật vô tri vô giác)
  • Cô bé đang chạy (nhân vật chính)
  • Các con ong và các con dơi (xấu)
  • Các nút nhấn (tốt)
  • Các viên đá hồng ngọc và bích ngọc (tốt)
  • Tiền xu (tốt)
  • Các con ốc sên (xấu)
  • Bom của ốc sên (xấu)

Bên cạnh việc cuộn từ phải sang trái, hầu như tất cả các hình ảnh 2D của trò chơi có chuyển động độc lập riêng của mình. Ví dụ, những viên đá hồng ngọc và bích ngọc nhấp nhô ở các tốc độ khác nhau và các nút nhấn và ốc sên đi tới đi lui dọc theo chiều dài của bậc thềm mà chúng ở trên đó.

Replica Island

Ý tưởng cho các hành vi của hình ảnh 2D — là một ví dụ về các mẫu thiết kế Chiến lược — đến từ Replica Island, một trò chơi đi cảnh nguồn mở phổ biến trên Android. Hầu hết các hình ảnh đồ họa của Snail Bait lấy từ Replica Island (được sử dụng theo giấy phép). Xem phần Tài nguyên để có các liên kết đến mẫu thiết kế Chiến lược trên Wikipedia và trang chủ của Replica Island .

Sự chuyển động độc lập này là một trong nhiều hành vi của hình ảnh 2D. Các hình ảnh 2D có thể có những hành vi khác không liên quan gì đến chuyển động cả; ví dụ, bên cạnh chuyển động nhấp nhô, những viên đá hồng ngọc và ngọc bích còn sáng lấp lánh.

Mỗi hình ảnh 2D có một mảng các hành vi. Một hành vi chỉ đơn giản là một đối tượng có một phương thức execute(). Trong mỗi khung hình của hình ảnh động, trò chơi gọi từng phương thức execute() của hành vi. Trong phương thức đó, các hành vi xử lý các hình ảnh 2D gắn với chúng theo một cách nào đó tùy thuộc vào các điều kiện của trò chơi. Ví dụ, khi bạn nhấn phím k để di chuyển nhân vật sang bên phải, hành vi di chuyển sang bên của nhân vật sau đó sẽ di chuyển nhân vật về bên phải trong mỗi khung hình của hình ảnh động cho đến khi bạn thay đổi hướng của nhân vật. Một hành vi khác, chạy tại chỗ, định kỳ thay đổi hình ảnh của nhân vật để nó xuất hiện như là nhân vật đang chạy tại chỗ. Cả hai hành vi đó kết hợp lại để làm cho hình ảnh đó xuất hiện như là nhân vật đang chạy hoặc sang trái hoặc sang phải.

Bảng 1 liệt kê các hình ảnh 2D của trò chơi và các hành vi tương ứng của chúng:

Bảng 1. Các hình ảnh 2D và các hành vi trong Snail Bait
Hình ảnh 2DCác hành vi
Bậc thềm
  • Di chuyển theo chiều ngang (tất cả các hình ảnh 2D ngoại trừ cô bé đang chạy và bom ốc sên di chuyển hòa hợp với các bậc thềm)
Nhân vật
  • Chạy tại chỗ
  • Di chuyển sang bên
  • Nhảy
  • Rơi xuống
  • Va chạm vào kẻ xấu và nổ tung
  • Va chạm vào những thứ tốt và nhận được điểm
Ong và dơi
  • Bay lượn
  • Vỗ cánh
Nút nhấn
  • Đi tới đi lui
  • Thu gọn lại
  • Thay đổi: Làm cho kẻ xấu nổ tung hoặc Kết thúc mức chơi
Đồng tiền, viên hồng ngọc và bích ngọc
  • Sáng lấp lánh
  • Nhấp nhô
  • Đi tới đi lui
Ốc sên
  • Đi tới đi lui
  • Ném bom
Bom ốc sên
  • Di chuyển từ phải sang trái (nhanh hơn các bậc thềm)
  • Va chạm phải cô bé đang chạy rồi biến mất

Các bài tiếp theo trong loạt bài này sẽ cung cấp cho bạn một cái nhìn sâu hơn về các hình ảnh 2D và hành vi của chúng. Bây giờ, để cung cấp cho bạn một tổng quan mức cao, Liệt kê 1 cho thấy trò chơi này tạo ra hình ảnh 2D của nhân vật runner như thế nào:

Liệt kê 1. Tạo các hình ảnh 2D
var runInPlace = {  // Just an object with an execute method
   execute: function (sprite, time, fps) {
      // Update the sprite's attributes based on the time and frame rate
   }
};

var runner = new Sprite('runner', // name
                        runnerPainter, // painter
                        [ runInPlace,... ]); // behaviors

Một đối tượng runInPlace được định nghĩa và được chuyển cho hàm tạo hình ảnh 2D của nhân vật trong cùng một mảng với các hành vi khác. Khi trò chơi đang chạy, nó gọi phương thức execute() của đối tượng runInPlace cho mỗi khung hình của hình ảnh động.


Các hướng dẫn tốt nhất về phát triển trò chơi với HTML5

Tài sản có sẵn miễn phí

Hầu hết các nhà phát triển trò chơi cần sự trợ giúp nào đó với các hình ảnh đồ họa, các hiệu ứng âm thanh và âm nhạc của mình. May mắn thay, có nhiều tài nguyên có sẵn miễn phí theo các thỏa thuận cấp phép khác nhau.

Snail Bait sử dụng:

  • Các hiệu ứng âm thanh từ freesound.org.
  • Các đoạn ghi âm thanh từ soundclick.com.
  • Hình ảnh 2D cô bé đang chạy từ panelmonkey.org (trang web này đã bị tấn công).
  • Tất cả các hình ảnh đồ họa khác từ Replica Island.

Tôi sẽ thảo luận các hướng dẫn thực hành tốt nhất về phát triển trò chơi trong suốt loạt bài này, bắt đầu từ đây với năm phần cụ thể cho HTML5:

Tôi sẽ xem xét năm phần trên một cách chi tiết trong các bài tiếp theo của loạt bài này; bây giờ tôi sẽ giới thiệu qua từng phần.

1. Tạm dừng trò chơi khi cửa sổ mất tiêu điểm

Nếu một trò chơi HTML5 đang chạy trong một trình duyệt và bạn thay đổi tiêu điểm sang tab khác trong cửa sổ hoặc một cửa sổ trình duyệt khác, thì hầu hết các trình duyệt đều hãm tốc độ khung hình trong hình ảnh động của trò chơi để tiết kiệm tài nguyên như CPU và nguồn pin. Ngoài ra, việc hãm tốc độ khung hình hầu như luôn làm rối loạn các thuật toán phát hiện va chạm, vì chúng giả định trò chơi chạy ở một tốc độ khung hình tối thiểu. Để tránh các hạn chế tốc độ khung hình gây ra sự đổ vỡ của thuật toán phát hiện va chạm sau đó, bạn nên tự động tạm dừng trò chơi khi cửa sổ bị mất tiêu điểm.

2. Thực hiện đếm ngược khi cửa sổ lấy lại tiêu điểm

Khi cửa sổ trò chơi của bạn lấy lại tiêu điểm, một ý tưởng hay là cung cấp cho người dùng một vài giây để chuẩn bị cho trò chơi tiếp tục lại. Snail Bait sử dụng đếm ngược ba giây khi cửa sổ lấy lại tiêu điểm, như thể hiện trong Hình 3:

Hình 3. Tự động tạm dừng của Snail Bait
Ảnh chụp màn hình về tự động tạm dừng của Snail Baite: một số 3 lớn được đặt trên nền sau

3. Sử dụng các quá trình chuyển tiếp CSS3

Hình 4 là một ảnh chụp màn hình được thực hiện sau khi trò chơi nạp:

Hình 4. Các hiệu ứng CSS3
Ảnh chụp màn hình của các hiệu ứng CSS3 trong Snail Bait

Có hai điều cần lưu ý trong Hình 4. Đầu tiên là một thông báo chào mừng — một thông báo được hiển thị rất nhanh cho người chơi — có thể nhìn thấy đó là Good luck! (Chúc may mắn!). Thông báo chào mừng đó mờ dần khi trò chơi nạp lên và sau năm giây, nó mất hẳn. Thứ hai, lưu ý các vùng lựa chọn (với âm thanh và âm nhạc) và các hướng dẫn (cho biết nhấn các phím nào thực hiện các chức năng nào) dưới khung nền ảnh của trò chơi. Khi trò chơi khởi động, các hộp chọn và các hướng dẫn hoàn toàn mờ đục, như chúng đang xuất hiện trong Hình 4; sau khi bắt đầu chơi, những phần tử đó từ từ mờ dần cho đến khi chúng hầu như không thể nhìn thấy được (như trong Hình 3), để cho chúng không làm mất tập trung của người chơi..

Snail Bait làm mờ các phần tử và làm cho thông báo chúc mừng hiện rõ rồi mờ dần đi bằng các quá trình chuyển tiếp của CSS3.

4. Phát hiện ra và phản ứng với các trò chơi chạy chậm

Không giống như các trò chơi có bàn điều khiển, chạy trong một môi trường có điều khiển chặt chẽ, các trò chơi HTML5 chạy trong một môi trường rất hay thay đổi, không thể đoán trước được và hỗn loạn. Trò chơi của bạn thường hay chạy chậm không thể chấp nhận được khi những người chơi đang chạy các video của YouTube trong ô cửa sổ khác hoặc nếu không thì họ đang sử dụng quá nhiều CPU hay GPU. Và luôn có khả năng là những người chơi của bạn sẽ sử dụng một trình duyệt không thể hỗ trợ theo kịp trò chơi được.

Là một nhà phát triển trò chơi, bạn phải lường trước được chỗ rẽ không may của các sự kiện và phản ứng cho phù hợp. Snail Bait liên tục giám sát tốc độ khung hình và khi nó phát hiện rằng tốc độ khung hình đã hạ xuống dưới một ngưỡng cụ thể rất nhiều lần trong nhiều giây, nó hiển thị thông báo chạy chậm trong Hình 5:

Hình 5. Phát hiện ra các khung hình chậm mỗi giây
Ảnh chụp màn hình về một cảnh báo rằng Snail Bait đang chạy quá chậm, đề nghị người dùng nâng cấp trình duyệt có HTML5 Canvas được tăng tốc phần cứng và cho người dùng tùy chọn để vô hiệu hóa các sự xuất hiện tiếp theo của cảnh báo đó

5. Kết hợp các tính năng xã hội

Hầu như tất cả các trò chơi thành công đều kết hợp với các khía cạnh xã hội, chẳng hạn như gửi số điểm lên Twitter hoặc Facebook. Khi một người chơi Snail Bait nhấn chuột lên liên kết Tweet my score (Điểm số của tôi trên Tweet) thường xuất hiện ở cuối trò chơi (xem Hình 2), Snail Bait chuyển đến một ô cửa sổ riêng của Twitter và tự động tạo ra một thông báo tweet về số điểm, giống như ô cửa sổ trong Hình 7:

Hình 7. Văn bản thông báo của Tweet
Ảnh chụp màn hình của văn bản tweet mà Snail Bait tự động soạn ra

Bây giờ bạn có một sự hiểu biết ở mức cao về trò chơi này, đây là lúc để xem xét viết mã.


Mã HTML và CSS của Snail Bait

Số liệu thống kê mã của Snail Bait

Các dòng mã:

  • HTML: 276
  • CSS: 410
  • JavaScript: 3,898

Snail Bait được thực hiện với mã HTML, CSS và JavaScript; tuy nhiên, như bạn có thể thấy trong ô bên về Số liệu thống kê mã của Snail Bait, hầu hết mã là JavaScript. Trong thực tế, phần còn lại của loạt bài này chủ yếu liên quan đến mã JavaScript, chỉ thỉnh thoảng xen vào mã HTML và CSS3.

Hình 8 cho thấy các phần tử HTML và CSS tương ứng của chúng cho chính trò chơi này, bỏ qua HTML và CSS với các phần tử khác như các thông báo và danh sách những người đã thực hiện trò chơi:

Hình 8. HTML và CSS của trò chơi (bỏ qua các khai báo bóng mờ của hộp)
Ảnh chụp màn hình có chú thích hiển thị mã HTML và CSS cho Snail Bait (không có các thông báo và các phần thưởng)

Nhấp vào để xem ảnh lớn

Hình 8. HTML và CSS của trò chơi (bỏ qua các khai báo bóng mờ của hộp)

Ảnh chụp màn hình có chú thích hiển thị mã HTML và CSS cho Snail Bait (không có các thông báo và các phần thưởng)

CSS hầu như không đáng kể, ngoại trừ một vài thuộc tính đáng quan tâm mà tôi đã nêu bật trong Hình 8. Trước tiên, tôi đã thiết lập thuộc tính margin của phần tử wrapper0 auto, có nghĩa là wrapper và mọi thứ trong nó, được đặt vào chính giữa theo chiều ngang cửa sổ. Thứ hai, các phần tử lives (mạng) và sound-and-music (âm thanh và âm nhạc) có một vị trí absolute (tuyệt đối). Nếu chúng ở bên trái so với vị trí mặc định, đó là vị trí relative (tương đối), các DIV sẽ mở rộng hết chiều rộng của khung nền ảnh và các phần tử lân cận của chúng (tương ứng là điểm số và các hướng dẫn) sẽ dịch chuyển bên dưới chúng. Cuối cùng, các lớp CSS keys (các phím) và explanation (giải thích) có một thuộc tính display (hiển thị) nhận giá trị inline (nội dòng) để đặt các phần tử kèm theo trên cùng một hàng.

Liệt kê 2 cho thấy CSS từ Hình 8:

Liệt kê 2. Tệp game.css (đoạn trích)
#arena {
   text-align: center;
   padding: 5px;
   width: 805px;
   height: 445px;
}

#copyright {
   margin-top: -35px;
   float: right;
   margin-right: 12px;
   padding: 2px;
   color: blue;
   text-shadow: 1px 1px 1px rgba(255,255,255,0.7);
   font-size: 0.8em;
}

.explanation {
   color: #ff0;
   text-shadow: 1px 1px 1px rgba(0,0,0,1.0);
   display: inline;
   margin-top: 5px;
   padding-right: 5px;
   padding-left: 5px;
   padding-bottom: 2px;
}
         
#game-canvas {
   border: 2px inset rgba(0,0,80,0.62);
   -webkit-box-shadow: rgba(0,0,0,0.5) 8px 8px 16px;
   -moz-box-shadow: rgba(0,0,0,0.5) 8px 8px 16px;
   -o-box-shadow: rgba(0,0,0,0.5) 8px 8px 16px;
   box-shadow: rgba(0,0,0,0.5) 8px 8px 16px;
}

#instructions {
   height: 30px;
   margin-right: 8px;
   padding-top: 6px;
   padding-left: 25px;

   -webkit-transition: opacity 2s;
   -moz-transition: opacity 2s;
   -o-transition: opacity 2s;
   transition: opacity 2s;

   color: #ff0;
   font-size: 1.05em;
   opacity: 1.0;
}

.keys {
   color: blue;
   text-shadow: 1px 1px 1px rgba(255,255,0,1.0);
   background: rgba(0,0,0,0.1);
   border: thin solid rgba(0,0,0,0.20);
   border-radius: 5px;
   margin-left: 10px;
   padding-right: 10px;
   padding-left: 10px;
   padding-bottom: 5px;
   display: inline;
}

#sound-and-music {
   position: absolute;
   top: 495px;
   margin-left: 10px;
   color: #ff0;
   text-shadow: 1px 1px 1px rgba(0,0,0,0.5);
   background: rgba(0,0,0,0.1);
   border-radius: 5px;
   border: thin solid rgba(0,0,0,0.20);
   padding-top: 2px;
   padding-bottom: 2px;
   z-index: 1;
}

#wrapper {
   margin: 0 auto;
   margin-top: 20px;
   padding: 5px;
   width: 817px;
   height: 520px;
}

Như bạn có thể thấy trong Liệt kê 3, có liệt kê mã HTML như trong Hình 8, mã HTML của trò chơi là một loạt các DIV và một khung nền ảnh, có một vài hình ảnh và một số hộp chọn:

Liệt kê 3. Tệp game.html (đoạn trích)
<!DOCTYPE html>
<html>
   <!-- Head........................................................-->

   <head>
     <title>Snail Bait</title>
   </head>

   <!-- Body........................................................-->

   <body>
      <!-- Wrapper..................................................-->

      <div id='wrapper'>
         <!-- Header.................................................-->

         <div id='header'>
            <div id='lives'>
               <img id='life-icon-left'   src='images/runner-small.png'/>
               <img id='life-icon-middle' src='images/runner-small.png'/>
               <img id='life-icon-right'  src='images/runner-small.png'/>
            </div>

            <div id='score'>0</div>
            <div id='fps'></div>
         </div>

         <!-- Arena..................................................-->

         <div id='arena'>
            <!-- The game canvas.....................................-->

            <canvas
               id='game-canvas' width='800' height='400'> 
                  Your browser does not support HTML5 Canvas.
           </canvas>

            <!-- Sound and music.....................................-->

            <div id='sound-and-music'>
               <div class='checkbox-div'>
                  Sound <input id='sound-checkbox'
                                  type='checkbox' checked/>
               </div>
   
               <div class='checkbox-div'>
                  Music <input id='music-checkbox' 
                                  type='checkbox' checked/>
               </div>
            </div>

            <!-- Instructions........................................-->

            <div id='instructions'>
               <div class='keys'>
                  d / k

                  <div class='explanation'>
                     move left/right
                  </div>
               </div>

               <div class='keys'>
                  f / j

                  <div class='explanation'>
                     jump
                  </div>
               </div>

               <div class='keys'>
                  p

                  <div class='explanation'>
                     pause

                  </div>
               </div>
            </div>

            <!-- Copyright...........................................-->

            <div id='copyright'> ©2012 David Geary</div>
         </div>
      </div>

      <!-- JavaScript................................................-->

      <script src='js/stopwatch.js'></script>
      <script src='js/animationTimer.js'></script>
      <script src='js/sprites.js'></script>
      <script src='js/requestNextAnimationFrame.js'></script>
      <script src='js/behaviors/bounce.js'></script>
      <script src='js/behaviors/cycle.js'></script>
      <script src='js/behaviors/pulse.js'></script>
      <script src='game.js'></script>
  </body>
</html>

Phần tử canvas (khung nền ảnh) là nơi tất cả hành động diễn ra. Phần tử canvas đó đi kèm với một bối cảnh 2D với một API mạnh mẽ để thực hiện các trò chơi 2D và nhiều thứ khác nữa. Văn bản bên trong phần tử canvas là văn bản dự phòng mà trình duyệt sẽ hiển thị chỉ khi nó không hỗ trợ Canvas của HTML5.

Một lưu ý cuối cùng về mã HTML và CSS của trò chơi: Lưu ý rằng chiều rộng và chiều cao của khung nền ảnh được chỉ rõ trong các thuộc tính width (chiều rộng) và height của phần tử canvas. Những thuộc tính đó liên quan đến kích thước của phần tử canvas kích thước của bề mặt vẽ chứa trong phần tử đó.

Mặt khác, việc sử dụng CSS để thiết lập chiều rộng và chiều cao của phần tử canvaschỉ thiết lập kích thước của phần tử canvas thôi. Bề mặt vẽ vẫn duy trì ở chiều rộng và chiều cao mặc định của nó tương ứng là 300 và 150 điểm ảnh. Điều đó có nghĩa là rất có thể bạn có một sự không khớp giữa kích thước phần tử canvas và kích thước của bề mặt vẽ của nó và khi điều đó xảy ra trình duyệt sẽ co giãn bề mặt vẽ cho phù hợp với phần tử đó. Trong hầu hết trường hợp, đó là một hiệu ứng không mong muốn, nên một ý tưởng tốt là không bao giờ thiết lập kích thước của phần tử canvas bằng CSS.

Có nên vẽ vào một khung nền ảnh nhỏ và để cho CSS co giãn nó không?

Một số trò chơi cố ý vẽ vào một khung nền ảnh nhỏ và sử dụng CSS để giãn rộng khung nền ảnh đến một kích thước có thể chơi được. Bằng cách đó, bạn không phải xử lý nhiều điểm ảnh của khung nền ảnh để tăng hiệu năng. Thông thường, việc giãn rộng một khung nền ảnh bằng CSS được tăng tốc phần cứng, do đó chi phí của việc giãn rộng này có thể là nhỏ nhất. Tuy nhiên, hiện nay — vì hầu như tất cả các phiên bản mới nhất của các trình duyệt hiện đại đều được trang bị với khung nền ảnh có tăng tốc phần cứng — trong hầu hết trường hợp, vẽ vào một khung nền ảnh có đủ kích thước cũng vẫn nhanh như thế.

Cũng giống với các bộ phim hay như Pulp Fiction, bạn đã thấy đoạn kết của câu chuyện. Bây giờ tôi sẽ quay trở lại từ đầu.


Khởi đầu khiêm tốn của Snail Bait

Hình 9 cho thấy điểm khởi đầu của trò chơi, đơn giản là vẽ nền sau, các bậc thềm và nhân vật. Để bắt đầu, các bậc thềm và nhân vật không phải là các hình ảnh 2D; thay vào đó, trò chơi vẽ chúng trực tiếp. Xem phần Tải về để lấy mã tạo nền sau và nhân vật.

Hình 9. Vẽ nền sau và cô bé đang chạy
Bắt đầu Snail Bait

Liệt kê 3 liệt kê điểm khởi đầu của mã HTML của trò chơi, đó chỉ là một phiên bản thu nhỏ của mã HTML trong Liệt kê 2:

Liệt kê 3. Tệp game.html (phiên bản khởi đầu)
<!DOCTYPE html>
<html>
   <!-- Head.........................................................-->

   <head>
      <title>Snail Bait</title>
      <link rel='stylesheet' href='game.css'/>
   </head>

   <!-- Body.........................................................-->

   <body>
      <!-- Wrapper...................................................-->

      <div id='wrapper'>
         <!-- Header.................................................-->

         <div id='header'>
            <div id='score'>0</div>
         </div>

         <!-- Arena..................................................-->

         <div id='arena'>
            <!-- The game canvas.....................................-->

            <canvas id='game-canvas' width='800' height='400'>
               Your browser does not support HTML5 Canvas.
            </canvas>
         </div>
      </div>

      <!-- JavaScript................................................-->

      <script src='game.js'></script>
  </body>
</html>

Liệt kê 4 cho thấy mã JavaScript:

Liệt kê 4. Tệp game.js (phiên bản khởi đầu)
// --------------------------- DECLARATIONS ----------------------------

var canvas = document.getElementById('game-canvas'),
    context = canvas.getContext('2d'),

   // Constants............................................................

   PLATFORM_HEIGHT = 8,  
   PLATFORM_STROKE_WIDTH = 2,
   PLATFORM_STROKE_STYLE = 'rgb(0,0,0)',

   STARTING_RUNNER_LEFT = 50,
   STARTING_RUNNER_TRACK = 1,

   // Track baselines
   //
   // Platforms move along tracks. The constants that follow define
   // the Y coordinate (from the top of the canvas) for each track.

   TRACK_1_BASELINE = 323,
   TRACK_2_BASELINE = 223,
   TRACK_3_BASELINE = 123,

   // Images

   background  = new Image(),
   runnerImage = new Image(),

   // Platforms
   //
   // Each platform has its own fill style, but the stroke style is
   // the same for each platform.

   platformData = [  // One screen for now
      // Screen 1.......................................................
      {
         left:      10,
         width:     230,
         height:    PLATFORM_HEIGHT,
         fillStyle: 'rgb(255,255,0)',
         opacity:   0.5,
         track:     1,
         pulsate:   false,
      },

      {  left:      250,
         width:     100,
         height:    PLATFORM_HEIGHT,
         fillStyle: 'rgb(150,190,255)',
         opacity:   1.0,
         track:     2,
         pulsate:   false,
      },

      {  left:      400,
         width:     125,
         height:    PLATFORM_HEIGHT,
         fillStyle: 'rgb(250,0,0)',
         opacity:   1.0,
         track:     3,
         pulsate:   false
      },

      {  left:      633,
         width:     100,
         height:    PLATFORM_HEIGHT,
         fillStyle: 'rgb(255,255,0)',
         opacity:   1.0,
         track:     1,
         pulsate:   false,
      },
   ];

// ------------------------- INITIALIZATION ----------------------------

function initializeImages() {
   background.src = 'images/background_level_one_dark_red.png';
   runnerImage.src = 'images/runner.png';

   background.onload = function (e) {
      startGame();
   };
}

function drawBackground() {
   context.drawImage(background, 0, 0);
}

function calculatePlatformTop(track) {
   var top;

   if      (track === 1) { top = TRACK_1_BASELINE; }
   else if (track === 2) { top = TRACK_2_BASELINE; }
   else if (track === 3) { top = TRACK_3_BASELINE; }

   return top;
}

function drawPlatforms() {
   var pd, top;

   context.save(); // Save context attributes on a stack
   
   for (var i=0; i < platformData.length; ++i) {
      pd = platformData[i];
      top = calculatePlatformTop(pd.track);

      context.lineWidth = PLATFORM_STROKE_WIDTH;
      context.strokeStyle = PLATFORM_STROKE_STYLE;
      context.fillStyle = pd.fillStyle;
      context.globalAlpha = pd.opacity;

      // If you switch the order of the following two
      // calls, the stroke will appear thicker.
      
      context.strokeRect(pd.left, top, pd.width, pd.height);
      context.fillRect  (pd.left, top, pd.width, pd.height);
   }

   context.restore(); // Restore context attributes
}

function drawRunner() {
   context.drawImage(runnerImage,
      STARTING_RUNNER_LEFT,
      calculatePlatformTop(STARTING_RUNNER_TRACK) - runnerImage.height);
}

function draw(now) {
   drawBackground();
   drawPlatforms();
   drawRunner();
}

function startGame() {
   draw();
}

// Launch game

initializeImages();

Mã JavaScript truy cập phần tử canvas và sau đó nhận được một tham chiếu đến bối cảnh 2D của của khung nền ảnh. Rồi mã này sử dụng phương thức drawImage() của bối cảnh đó để vẽ các hình ảnh nền sau và nhân vật. Trong trường hợp này, tôi đang sử dụng biến thể có ba đối số của phương thức drawImage() để vẽ các hình ảnh tại một vị trí (x,y) cụ thể trong khung nền ảnh.

Hàm drawPlatforms() vẽ các bậc thềm bằng cách vẽ các hình chữ nhật rỗng hoặc vẽ các hình chữ nhật tô đặc sau khi thiết lập độ rộng nét vẽ của bối cảnh, kiểu vẽ nét mảnh, kiểu vẽ tô đặc và thuộc tính alpha toàn cục. Lưu ý các cuộc gọi đến phương thức context.save()context.restore(): các giá trị thiết lập thuộc tính giữa những cuộc gọi này chỉ là tạm thời. Tôi sẽ thảo luận các phương thức đó trong phần tiếp theo của loạt bài này.

Trò chơi bắt đầu khi hình ảnh nền sau nạp lên. Bây giờ, hãy bắt đầu theo thứ tự đơn giản vẽ nền sau, các hình ảnh 2D và nhân vật. Thách thức tiếp theo là làm cho những hình ảnh tĩnh đó trở nên sống động.


Phần tiếp theo

Trong phần tiếp theo của loạt bài này, tôi sẽ bắt đầu với một tổng quan về API 2D của bối cảnh khung nền ảnh và sau đó thảo luận về hình ảnh động và đưa các thứ vào chuyển động bằng cách cuộn nền sau. Bạn sẽ được tìm hiểu cách thực hiện thị sai để làm cho các bậc thềm có vẻ như gần hơn so với nền sau và bạn sẽ thấy làm thế nào để bảo đảm rằng các hình ảnh 2D của bạn hoạt động ở một tốc độ không đổi bất kể tốc độ khung hình của hình ảnh động của bạn. Hẹn gặp bạn lần tới nhé.


Tải về

Mô tảTênKích thước
Code for Snail Bait's background and runnerj-html5-game1.zip718KB

Tài nguyên

Học tập

Lấy sản phẩm và công nghệ

  • Replica Island: Bạn có thể tải về mã nguồn dùng cho trò chơi đi cảnh nguồn mở phổ biến này cho Android.

Thảo luận

  • Hãy tham gia vào cộng đồng developerWorks. Kết nối với những người dùng khác trên developerWorks trong khi khám phá các blog, các diễn đàn, các nhóm và các wiki theo hướng nhà phát triển.

Bình luận

developerWorks: Đăng nhập

Các trường được đánh dấu hoa thị là bắt buộc (*).


Bạn cần một ID của IBM?
Bạn quên định danh?


Bạn quên mật khẩu?
Đổi mật khẩu

Bằng việc nhấn Gửi, bạn đã đồng ý với các điều khoản sử dụng developerWorks Điều khoản sử dụng.

 


Ở lần bạn đăng nhập đầu tiên vào trang developerWorks, một hồ sơ cá nhân của bạn được tạo ra. Thông tin trong bản hồ sơ này (tên bạn, nước/vùng lãnh thổ, và tên cơ quan) sẽ được trưng ra cho mọi người và sẽ đi cùng các nội dung mà bạn đăng, trừ khi bạn chọn việc ẩn tên cơ quan của bạn. Bạn có thể cập nhật tài khoản trên trang IBM bất cứ khi nào.

Thông tin gửi đi được đảm bảo an toàn.

Chọn tên hiển thị của bạn



Lần đầu tiên bạn đăng nhập vào trang developerWorks, một bản trích ngang được tạo ra cho bạn, bạn cần phải chọn một tên để hiển thị. Tên hiển thị của bạn sẽ đi kèm theo các nội dung mà bạn đăng tải trên developerWorks.

Tên hiển thị cần có từ 3 đến 30 ký tự. Tên xuất hiện của bạn phải là duy nhất trên trang Cộng đồng developerWorks và vì lí do an ninh nó không phải là địa chỉ email của bạn.

Các trường được đánh dấu hoa thị là bắt buộc (*).

(Tên hiển thị cần có từ 3 đến 30 ký tự)

Bằng việc nhấn Gửi, bạn đã đồng ý với các điều khoản sử dụng developerWorks Điều khoản sử dụng.

 


Thông tin gửi đi được đảm bảo an toàn.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=70
Zone=Nguồn mở
ArticleID=870534
ArticleTitle=Lập trình game 2D trên HTML5, Phần 1: Giới thiệu Snail Bait
publish-date=04162013