Trong loạt bài này, David Geary sẽ hướng dẫn bạn từng bước thực hiện trò chơi
video HTML5 2D. Sprites — đối tượng đồ họa mà bạn có thể gán các hành vi đến
— là một trong những khía cạnh cơ bản nhất và quan trọng của trò chơi video.
Trong phần này, bạn sẽ học làm thế nào để thực hiện sprite để tạo nên sự chuyển
động của các nhân vật trong Snail Bait.
35 trang |
Chia sẻ: lylyngoc | Lượt xem: 1607 | Lượt tải: 2
Bạn đang xem trước 20 trang tài liệu Lập trình game 2D trên HTML5, Phần 4: Sprites, để xem tài liệu hoàn chỉnh bạn click vào nút DOWNLOAD ở trên
Lập trình game 2D trên
HTML5, Phần 4: Sprites
Tiến hành phân vai các nhân vật trong Snail Bait
Trong loạt bài này, David Geary sẽ hướng dẫn bạn từng bước thực hiện trò chơi
video HTML5 2D. Sprites — đối tượng đồ họa mà bạn có thể gán các hành vi đến
— là một trong những khía cạnh cơ bản nhất và quan trọng của trò chơi video.
Trong phần này, bạn sẽ học làm thế nào để thực hiện sprite để tạo nên sự chuyển
động của các nhân vật trong Snail Bait.
Giống như các lĩnh vực nghệ thuật khác — chẳng hạn như phim, kịch và tiểu
thuyết — trò chơi có một dàn các nhân vật, mỗi nhân vật có một vai trò cụ thể. Ví
dụ, Snail Bait có nhân vật "Runner" (giữ vai chính của trò chơi), các đồng tiền,
ngọc ruby, ngọc sapphire, các con ong, con dơi, các nút bấm, và một con ốc sên,
phần lớn chúng đều có trong Hình 1. Trong bài viết đầu tiên của loạt bài này (xem
phần Các hình ảnh 2D: Phân vai các nhân vật), tôi đã thảo luận về các nhân vật này
và vai trò của chúng.
Hình 1. Các nhân vật của Snail Bait
Mỗi nhân vật trong Snail Bait là một sprite. Sprites là đối tượng đồ họa mà bạn có
thể cấp cho nó các hành vi; ví dụ, nhân vật có thể chạy, nhảy, rơi, và va chạm với
các sprite khác, trong khi đó, các viên ngọc lấp lánh, nhúc nhích lên xuống, và biến
mất khi chúng va chạm với nhân vật.
Sử dụng thuật ngữ Sprite
Những người thực hiện bộ xử lý hiển thị video của nhạc cụ Texas 9918A lần đầu
tiên sử dụng thuật ngữ sprite cho các nhân vật hoạt hình. (Trong tiếng Anh chuẩn,
từ sprite — đến từ tiếng Latin spiritus — nghĩa là yêu tinh hay thần tiên.) Sprites
cũng được thực hiện trong cả phần cứng và phần mềm; ví dụ máy tính Commodore
Amiga sản xuất năm 1985 đã hỗ trợ đến 8 sprite về phần cứng.
Vì sprite là một trong những khía cạnh cơ bản nhất của bất kỳ trò chơi, và vì trò
chơi thường có nhiều sprite, nó có ý nghĩa để đóng gói khả năng cơ bản của chúng
trong các đối tượng có thể tái sử dụng. Trong bài viết này, bạn sẽ học làm thế nào
để:
Thực hiện một đối tượng Sprite mà bạn có thể tái sử dụng trong bất kỳ trò
chơi nào.
Tách rời sprite từ các đối tượng vẽ chúng (sprite artists) để linh động vào
lúc khởi chạy.
Dùng sprite sheets để giảm thời gian khởi động và các yêu cầu về bộ nhớ.
Tạo sprite với siêu dữ liệu.
Kết hợp sprite vào một vòng lặp.
Các đối tượng Sprite
Tôi thiết lập sprite của Snail Bait như là các đối tượng Javascript để có thể sử dụng
trong bất kỳ trò chơi nào, vì vậy sprite sẽ nằm ở tệp riêng. Và tôi sẽ gọi tệp đó
trong đoạn mã HTML của Snail Bait như sau: .
Bảng 1 liệt kê các thuộc tính của Sprite:
Bảng 1. Các thuộc tính của Sprite
Thuộc
tính
Chú giải
artist Đối tượng để vẽ một sprite.
behaviors Một mảng các trạng thái, mỗi trạng thái là các
thao tác chuyển động của sprite trong một vài
Thuộc
tính
Chú giải
kiểu hình dạng.
left Tọa độ X ở góc trên bên trái của sprite.
top Tọa độ Y ở góc trên bên trái của sprite.
width Chiều ngang của sprite được tính theo pixel.
height Chiều cao của sprite được tính theo pixel.
opacity
Trạng thái mờ, trong suốt hoặc kết hợp cả hai
trạng thái của sprite.
type
Một dòng chú thích loại của sprite, như bat
(con dơi), bee (con ong), hay runner (nhân vật).
velocityX
Tốc độ ngang của sprite, được tính theo số
pixel trên giây.
velocityY
Tốc độ đứng của sprite, được tính theo số pixel
trên giây.
visible
Tình trạng hiển thị của sprite. Nếu giá trị là
false thì sprite sẽ không được vẽ.
Sprite là những đối tượng đơn giản để duy trì thông tin tọa độ và kích cỡ của nó
(thường được biết như là một bounding box (hộp biên)), tốc độ và tình trạng hiển
thị. Nó còn có kiểu riêng để phân biệt sprite này với sprite khác và độ mờ của nó,
nghĩa là từng phần riêng của sprite có thể trong suốt.
Sprite còn được ủy quyền cho các đối tượng khác để vẽ nó hoặc thao tác tới nó,
tương ứng với artist và behaviors.
Liệt kê 1 là đoạn mã khởi tạo một Sprite, và nó thiết lập một số giá trị mặc định
cho các thuộc tính của sprite:
Liệt kê 1. Khởi tạo Sprite
var Sprite = function (type, artist, behaviors) { // constructor
this.type = type || '';
this.artist = artist || undefined;
this.behaviors = behaviors || [];
this.left = 0;
this.top = 0;
this.width = 10; // Something other than zero, which makes no sense
this.height = 10; // Something other than zero, which makes no sense
this.velocityX = 0;
this.velocityY = 0;
this.opacity = 1.0;
this.visible = true;
return this;
};
Trình diễn và hành vi
Dấu hiệu của các phương thức của Sprite bắt buộc phải tách riêng mối liên hệ giữa
sự trình diễn và hành vi: phương thức draw() dùng Canvas để vẽ sprite, trong khi
đó phương thức update() được thiết kế để cập nhật trạng thái của sprite dựa trên
thời gian hiện tại và tần số khung hình. Các hành vi không thể vẽ và các artist (đối
tượng đồ họa) không thể cập nhật trạng thái.
Tất cả các tham số của hàm khởi tạo trong Liệt kê 1 là tùy chọn. Nếu bạn không
khởi tạo các hành vi, hàm khởi tạo mặc định tạo một mảng rỗng, và nếu bạn tạo
sprite mà không chỉ định kiểu dữ liệu thì kiểu dữ liệu của nó là một chuỗi rỗng.
Nếu bạn không chỉ định một artist (đối tượng đồ họa), thì nó sẽ không khởi tạo.
Bên cạnh các thuộc tính, sprite còn có hai phương thức được liệt kê trong Bảng 2:
Bảng 2. Các phương thức của Sprite
Phương thức Chú giải
draw(context)
Gọi đến phương thức draw() của artist trong sprite nếu sprite được
hiện lên và có một artist.
update(time,
fps)
Gọi đến phương thức update() cho mỗi hành vi của sprite.
Liệt kê 2 hiển thị đoạn mã thực thi của các phương thức trong Bảng 2:
Liệt kê 2. Thực thi các phương thức của Sprite
Sprite.prototype = { // methods
draw: function (context) {
context.save();
// Calls to save() and restore() make the globalAlpha setting temporary
context.globalAlpha = this.opacity;
if (this.artist && this.visible) {
this.artist.draw(this, context);
}
context.restore();
},
update: function (time, fps) {
for (var i=0; i < this.behaviors.length; ++i) {
if (this.behaviors[i] === undefined) { // Modified while looping?
return;
}
this.behaviors[i].execute(this, time, fps);
}
}
};
Tốc độ của Sprite: Được xác định bằng số pixel trên giây (pixel/second)
Nếu bạn đã đọc qua bài viết thứ hai của loạt bài này (xem tại mục Chuyển động
theo thời gian), sự di chuyển của sprite phải độc lập với tần số khung hình của hoạt
cảnh trong trò chơi. Và vì vậy, tốc độ của sprite được tính bằng công thức:
pixels/second.
Bạn có thể thấy trong Liệt kê 1 và Liệt kê 2, sprite thì không phức tạp lắm. Hầu hết
sự phức tạp xoay quanh việc sprite được đóng gói vào trong đối tượng đồ họa và
các hành vi của nó. Thật cần thiết nếu bạn hiểu rằng bạn có thể thay đổi đối tượng
đồ họa và các hành vi của sprite trong lúc chạy chương trình (run time) bởi vì
sprite được tách ra từ những đối tượng đó. Thực tế, bạn sẽ thấy trong bài tiếp theo
của loạt bài này, rằng hoàn toàn có thể — và mong muốn — thực hiện những hành
vi chung được dùng cho nhiều sprite khác nhau.
Bây giờ chúng ta sẽ xem cách mà sprite được thực hiện, bạn hãy sẵn sàng xem
cách thực hiện đối tượng đồ họa của sprite.
Đối tượng đồ họa và các trang của Sprite (Artist và Sprite sheet)
Đối tượng đồ họa của Sprite có thể được thực hiện theo một trong ba cách:
Đối tượng Stroke and fill: Vẽ các kiểu đồ họa nguyên thủy như dòng, cung,
và đường cong
Đối tượng Image: Vẽ hình ảnh 2D thông qua ngữ cảnh phương thức
drawImage()
Đối tượng Sprite sheet: Vẽ hình ảnh từ một trang Sprite (giống như phương
thức drawImage())
Bất kể kiểu đối tượng đồ họa nào mà bạn thấy trong Liệt kê 2, đều phải thực hiện
một yêu cầu: Chúng phải là đối tượng thực hiện phương thức vẽ draw() mà nó cần
một sprite và một ngữ cảnh Canvas 2D (Canvas 2D context) làm tham số.
Kế tiếp tôi sẽ thảo luận về mỗi loại đối tượng đồ họa và giải thích về Sprite sheet.
Các đối tượng đường nét và phối màu (Stroke và fill)
Các đối tượng đường nét và phối màu không có một quy tắc thực hiện tiêu chuẩn;
thay vào đó, bạn thực hiện chúng theo phong cách bất kỳ bằng cách sử dụng khả
năng đồ họa của ngữ cảnh Canvas 2D. Liệt kê 3 hiển thị cách thực hiện các đối
tượng đường nét và phối màu để vẽ các sprite nền tảng của Smail Bait:
Liệt kê 3. Các đối tượng đường nét và phối màu
// Stroke and fill artists draw with Canvas 2D drawing primitives
var SnailBait = function (canvasId) { // constructor
...
this.platformArtist = {
draw: function (sprite, context) {
var top;
context.save();
top = snailBait.calculatePlatformTop(sprite.track);
// Calls to save() and restore() make the following settings temporary
context.lineWidth = snailBait.PLATFORM_STROKE_WIDTH;
context.strokeStyle = snailBait.PLATFORM_STROKE_STYLE;
context.fillStyle = sprite.fillStyle;
context.strokeRect(sprite.left, top, sprite.width, sprite.height);
context.fillRect (sprite.left, top, sprite.width, sprite.height);
context.restore();
}
},
};
Khung nền, như khi bạn thấy từ Hình 1, đơn giản chỉ là các hình chữ nhật. Đối
tượng đồ họa của khung nền trong Liệt kê 3 vẽ các hình chữ nhật đó với phương
thức strokeRect() và fillRect() của ngữ cảnh Canvas 2D. Bài viết thứ hai trong loạt
bài này (xem phần Tổng quan về Canvas của HTML5) để có nhiều thông tin về các
phương thức này. Tọa độ và kích cỡ của hình chữ nhật được xác định bởi biên bao
bên ngoài.
Đối tượng Image
Không giống như các đối tượng đường nét và phối màu, đối tượng image có thể
thực hiện nó một cách đồng nhất, được hiển thị trong Liệt kê 4:
Liệt kê 4. Đối tượng Image
// ImageArtists draw an image
var ImageArtist = function (imageUrl) { // constructor
this.image = new Image();
this.image.src = imageUrl;
};
ImageArtist.prototype = { // methods
draw: function (sprite, context) {
context.drawImage(this.image, sprite.left, sprite.top);
}
};
Bạn tạo ra hình ảnh bằng URL của nó, và phương thức draw() của đối tượng đồ
họa vẽ toàn bộ hình ảnh tại vị trí của sprite.
Snail Bait không sử dụng đối tượng Image, bởi vì sẽ hiệu quả hơn khi vẽ từ Sprite
sheet.
Các Sprite sheet
Một trong những cách nhanh chóng để đảm bảo rằng trang web của bạn tải nhanh
hơn thì giảm số yêu cầu HTTP đến mức tối thiểu vừa đủ. Phần lớn các trò chơi sử
dụng nhiều hình ảnh, và thời gian tải trang sẽ bị ảnh hưởng nếu cứ phải thực hiện
các yêu cầu HTTP cho từng tấm hình. Vì nguyên nhân đó, các nhà lập trình trò
chơi HTML5 tạo ra một tấm hình lớn duy nhất chứa tất cả các hình ảnh trong trò
chơi của họ. Tấm hình duy nhất đó có tên là Sprite sheet. Hình 2 hiển thị Sprite
sheet của Snail Bait:
Hình 2. Sprite sheet của Snail Bait
Cho một Sprite sheet, bạn cần một cách để vẽ một hình chữ nhật cụ thể của sprite
lên khung hình ảnh. May thay, ngữ cảnh Canvas 2D cho bạn dễ dàng làm điều đó
với phương thức drawImage(). Kỹ thuật đó được sử dụng bởi đối tượng Sprite
sheet.
Các đối tượng Sprite sheet
Liệt kê 5 hiển thị cách thực hiện đối tượng Sprite sheet:
Liệt kê 5. Đối tượng Sprite sheet
// Sprite sheet artists draw an image from a sprite sheet
SpriteSheetArtist = function (spritesheet, cells) { // constructor
this.cells = cells;
this.spritesheet = spritesheet;
this.cellIndex = 0;
};
SpriteSheetArtist.prototype = { // methods
advance: function () {
if (this.cellIndex == this.cells.length-1) {
this.cellIndex = 0;
}
else {
this.cellIndex++;
}
},
draw: function (sprite, context) {
var cell = this.cells[this.cellIndex];
context.drawImage(this.spritesheet,
cell.left, cell.top, // source x, source y
cell.width, cell.height, // source width, source height
sprite.left, sprite.top, // destination x, destination y
cell.width, cell.height); // destination width, destination height
}
};
Bạn khởi tạo đối tượng vẽ Sprite sheet với một tham chiếu đến một Sprite sheet và
một mảng chứa những đường biên bao quanh hình, được gọi là các ô (cells). Các ô
này thể hiện một hình chữ nhật trong Sprite sheet, chứa hình ảnh đơn lẻ của Sprite
sheet.
Đối tượng vẽ Sprite sheet cũng đồng thời lưu một chỉ mục (index) cho từng ô.
Phương thức draw() của Sprite sheet sử dụng chỉ mục đó để truy cập ô hiện thời và
sau đó sử dụng phiên bản chín-tham-số của ngữ cảnh Canvas 2D là drawImage()
để vẽ nội dung của ô đó trong khung hình ảnh ở vị trí của sprite.
Phương thức advance() của đối tượng vẽ Sprite sheet còn sử dụng để trỏ sang ô kế
tiếp trong Sprite sheet, và trở lại ô ban đầu khi con trỏ chỉ mục tới ô cuối cùng.
Tiếp theo gọi tới phương thức draw() của đối tượng vẽ Sprite sheet để vẽ hình
tương ứng. Bằng cách liên tục thúc đẩy việc lập chỉ mục và vẽ hình, đối tượng
Sprite sheet có thể vẽ một tập hợp các ảnh liên tục từ Sprite sheet.
Đối tượng vẽ Sprite sheet, như bạn thấy từ Liệt kê 5, dễ dàng thực hiện. Chúng
cũng dễ sử dụng; bạn chỉ việc khởi tạo đối tượng vẽ với Sprite sheet và các ô, và
sau đó gọi phương thức advance() và draw(). Phần khó khăn là xác định các ô.
Xác định các ô chứa trong Sprite sheet
Đoạn mã trong Liệt kê 6 xác định các ô từ Sprite sheet của Snail Bait cho các con
dơi, con ong và con ốc trong trò chơi:
Liệt kê 6. Xác định các ô Sprite sheet của Snail Bait
var BAT_CELLS_HEIGHT = 34,
BEE_CELLS_WIDTH = 50,
BEE_CELLS_HEIGHT = 50,
...
SNAIL_CELLS_WIDTH = 64,
SNAIL_CELLS_HEIGHT = 34,
...
// Spritesheet cells................................................
batCells = [
{ left: 1, top: 0, width: 32, height: BAT_CELLS_HEIGHT },
{ left: 38, top: 0, width: 46, height: BAT_CELLS_HEIGHT },
{ left: 90, top: 0, width: 32, height: BAT_CELLS_HEIGHT },
{ left: 129, top: 0, width: 46, height: BAT_CELLS_HEIGHT },
],
beeCells = [
{ left: 5, top: 234, width: BEE_CELLS_WIDTH, height:
BEE_CELLS_HEIGHT },
{ left: 75, top: 234, width: BEE_CELLS_WIDTH, height:
BEE_CELLS_HEIGHT },
{ left: 145, top: 234, width: BEE_CELLS_WIDTH, height:
BEE_CELLS_HEIGHT }
],
...
snailCells = [
{ left: 142, top: 466, width: SNAIL_CELLS_WIDTH, height:
SNAIL_CELLS_HEIGHT },
{ left: 75, top: 466, width: SNAIL_CELLS_WIDTH, height:
SNAIL_CELLS_HEIGHT },
{ left: 2, top: 466, width: SNAIL_CELLS_WIDTH, height:
SNAIL_CELLS_HEIGHT },
];
Việc xác định các khung bao quanh từng ô là một công việc tẻ nhạt, do đó, cần đầu
tư thời gian để thực hiện một công cụ hỗ trợ là việc làm cần thiết. Hình 3 cho thấy
một công cụ hỗ trợ như vậy, nó có sẵn tại trang Core HTML Canvas
Hình 3. Một bộ định vị Sprite sheet đơn giản
Những công cụ cần thiết cho nhà phát triển trò chơi
Công việc của một nhà phát triển trò chơi không phải lúc nào cũng vui và thú vị.
Các nhà phát triển trò chơi dành rất nhiều thời gian vào công việc tẻ nhạt như là
quyết định các ô Sprite sheet và thiết kế các mức độ của trò chơi. Hầu hết các nhà
phát triển trò chơi dành thời gian để phát triển công cụ, như một trong những thể
hiện trong Liệt kê 3, để giúp họ hoàn thành những nhiệm vụ tẻ nhạt.
Ứng dụng được hiển thị trong Liệt kê 3 hiển thị hình ảnh và theo dõi sự di chuyển
của chuột trong hình ảnh. Khi bạn di chuyển chuột, ứng dụng vẽ đường dẫn và cập
nhật ở góc trên bên trái của ứng dụng hiển thị vị trí hiện tại của con trỏ chuột.
Công cụ này giúp bạn dễ dàng để xác định khung giới hạn cho mỗi hình ảnh và
Sprite sheet.
Bây giờ bạn đã có một ý tưởng tốt để thực hiện các mảng sprite và đối tượng vẽ
của chúng, giờ là lúc để xem làm thế nào mà Snail Bait có thể tạo và khởi chạy
sprite của nó.
Tạo và khởi chạy sprite của Snail Bait
Snail Bait định nghĩa các mảng chứa các sprite, được hiển thị trong Liệt kê 7:
Liệt kê 7. Định nghĩa các mảng sprite trong hàm khởi tạo của trò chơi
var SnailBait = function (canvasId) { // constructor
...
this.bats = [],
this.bees = [],
this.buttons = [],
this.coins = [],
this.platforms = [],
this.rubies = [],
this.sapphires = [],
this.snails = [],
this.runner = new Sprite('runner', this.runnerArtist);
this.sprites = [ this.runner ]; // Add other sprites later
...
};
Mỗi mảng trong Liệt kê 7 chứa các sprite cùng kiểu; ví dụ, mảng bats chứa sprite
con dơi, mảng bees chứa các sprite con ong, và cứ thế. Trò chơi cũng duy trì mảng
chứa tất cả sprite của nó. Không cần thiết để có các mảng riêng lẻ cho con ong, con
dơi, v.v... — chúng dư thừa — nhưng chúng lại tạo thuận lợi về hiệu suất; ví dụ,
khi trò chơi kiểm tra xem liệu nhân vật có đáp xuống nền hay không, sẽ hiệu quả
hơn để lặp qua mảng platforms hơn là lặp qua mảng sprites để tìm kiếm bậc thềm.
Liệt kê 7 cũng chỉ ra làm thế nào để tạo sprite nhân vật và thêm nó vào mảng
sprites. Chúng ta không có mảng nhân vật, vì trò chơi chỉ có một nhân vật duy
nhất. Chú ý, trò chơi khởi chạy nhân vật với một kiểu — runner — và một đối
tượng đồ họa, nhưng nó không chỉ định hành vi khi nhân vật được khởi chạy. Về
những hành vi, tôi sẽ thảo luận chúng trong bài kế tiếp, được thêm sau bằng đoạn
mã.
Khi trò chơi bắt đầu, Snail Bait (cùng với những thứ khác) khởi chạy phương thức
createSprites() như bạn thấy trong Liệt kê 8:
Liệt kê 8. Khởi động trò chơi
SnailBait.prototype = { // methods
...
start: function () {
this.createSprites();
this.initializeImages();
this.equipRunner();
this.splashToast('Good Luck!');
},
};
Phương thức createSprites() tạo tất cả sprite của trò chơi ngoại trừ sprite của nhân
vật, được hiển thị trong Liệt kê 9:
Liệt kê 9. Tạo và khởi chạy các sprite của Snail Bait
SnailBait.prototype = { // methods
...
createSprites: function() {
this.createPlatformSprites();
this.createBatSprites();
this.createBeeSprites();
this.createButtonSprites();
this.createCoinSprites();
this.createRubySprites();
this.createSapphireSprites();
this.createSnailSprites();
this.initializeSprites();
this.addSpritesToSpriteArray();
},
createSprites() khởi chạy hàm trợ giúp để tạo những kiểu khác nhau của sprite,
theo sau là các phương thức khởi tạo các sprite và thêm chúng vào mảng sprites.
Việc thực hiện các hàm trợ giúp này được hiển thị trong Liệt kê 10:
Liệt kê 10. Tạo các sprite riêng lẻ
SnailBait.prototype = { // methods
...
createBatSprites: function () {
var bat, batArtist = new SpriteSheetArtist(this.spritesheet, this.batCells),
redEyeBatArtist = new SpriteSheetArtist(this.spritesheet, this.batRedEyeCells);
for (var i = 0; i < this.batData.length; ++i) {
if (i % 2 === 0) bat = new Sprite('bat', batArtist);
else bat = new Sprite('bat', redEyeBatArtist);
bat.width = this.BAT_CELLS_WIDTH;
bat.height = this.BAT_CELLS_HEIGHT;
this.bats.push(bat);
}
},
createBeeSprites: function () {
var bee, beeArtist = new SpriteSheetArtist(this.spritesheet, this.beeCells);
for (var i = 0; i < this.beeData.length; ++i) {
bee = new Sprite('bee', beeArtist);
bee.width = this.BEE_CELLS_WIDTH;
bee.height = this.BEE_CELLS_HEIGHT;
this.bees.push(bee);
}
},
createButtonSprites: function () {
var button, buttonArtist = new SpriteSheetArtist(this.spritesheet,
this.buttonCells),
goldButtonArtist = new SpriteSheetArtist(this.spritesheet,
this.goldButtonCells);
for (var i = 0; i < this.buttonData.length; ++i) {
if (i === this.buttonData.length - 1) {
button = new Sprite('button', goldButtonArtist);
}
else {
button = new Sprite('button', buttonArtist);
}
button.width = this.BUTTON_CELLS_WIDTH;
button.height = this.BUTTON_CELLS_HEIGHT;
button.velocityX = this.BUTTON_PACE_VELOCITY;
button.direction = this.RIGHT;
this.buttons.push(button);
}
},
createCoinSprites: function () {
var coin, coinArtist = new SpriteSheetArtist(this.spritesheet, this.coinCells);
for (var i = 0; i < this.coinData.length; ++i) {
coin