Tutorial:Drawing Order (Tiếng Việt)

Vì LÖVE không có sẵn một hệ thống xếp thứ tự z (z-ordering) (đặc điểm này đã được đề nghị đưa vào phiên bản tương lai, nhưng chưa phải ưu tiên), bạn có thể gặp vấn đề khi thử lập trò chơi có nhiệm vụ vẽ một nhân vật đứng trước hoặc sau các vật thể, tùy theo vị trí tương đối giữa chúng; điều này thường thấy ở các trò chơi thể loại khám phá. Bài hướng dẫn này sẽ cho bạn thấy cách làm rất đơn giản.

Lưu ý: Trong bài này, khi tôi nhắc đến một vật thể "đứng trước" nhân vật, thì có nghĩa là vật thể này ở giữa nhân vật và màn hình (tức là bạn). Còn "đứng sau" nghĩa là nhân vật ở giữa màn hình và vật thể đó.

Trước hết, ta cần một số nguyên liệu:

-- ảnh nền
scene = love.graphics.newImage("room.png")
-- nhân vật 
person = love.graphics.newImage("guy.png")
-- vật thể
object = love.graphics.newImage("ball.png")

Được rồi, bây giờ khi đã có đủ nguyên liệu, ta cần vài biến để chứa vị trí của nhân vật và các vật thể:

-- vị trí nhân vật
character = {400,400} -- x,y

-- một loạt vật thể, mỗi cái lại có vị trí riêng
objects = {}
objects[1] = {550,370}
objects[2] = {220,390}
objects[3] = {600,410}
objects[4] = {300,450}
objects[5] = {400,530}

Hãy đảm bảo rằng danh sách các vật thể vừa lập đã được xếp thứ tự theo vị trí y. Điều này trở nên quan trọng khi vẽ chúng. Một cách áp dụng chung, đầu tiên bạn có thể dùng hàm table.sort của Lua.

function orderY(a,b)
  return a[2] < b[2]
end
table.sort(objects, orderY)

Dù vậy, bạn phải cẩn thận không được trực tiếp lưu chỉ số cho một đối tượng, vì các chỉ số đối tượng sẽ thay đổi sau khi sắp xếp. (Bạn có thể muốn tạo bản sao chỉ phục vụ cho việc vẽ.)

Để vẽ những đối tượng này, ta sẽ dùng

love.graphics.draw(object, objects[i][1] - object:getWidth()/2, objects[i][2] -  object:getHeight())

Tham số object chỉ định hình ảnh mà ta chuẩn bị vẽ, còn hai tham số tiếp theo chỉ định đâu là góc trái của hình ảnh cần được vẽ lên màn hình. Ta đem trừ đi một nửa bề rộng và chiều cao của hình vẽ để đặt điểm giữa cạnh đáy của hình vào đúng các tọa độ x và y của object[i].

Được rồi, bây giờ ta đã sẵn sàng vẽ mọi thứ, coi như là bạn đã tạo được cách nào đó giúp di chuyển nhân vật quanh màn hình (nếu không thì đáng thất vọng vì sẽ không thể thấy sự khác biệt nào cả). Nếu bạn không biết cách làm điều đó, tôi gợi ý bạn nên xem qua một số hướng dẫn khác. Để đơn giản, tôi cho bạn thấy toàn bộ mã lệnh của hàm love.draw rồi giải thích điều gì đã xảy ra:

function love.draw() 
    love.graphics.draw(scene, love.graphics:getWidth() / 2 - scene:getWidth()/2,
        love.graphics:getHeight() / 2 - scene:getHeight() / 2) -- vẽ ở tâm màn hình
 
    local drawn = false -- true, nếu nhân vật đã được vẽ
 
    for i,v in ipairs(objects) do
        if not drawn and objects[i][2] > character[2] then
            love.graphics.draw(person, character[1] - person:getWidth()/2, character[2] - person:getHeight())
            drawn = true
        end
	love.graphics.draw(object, objects[i][1] - object:getWidth()/2, objects[i][2] - object:getHeight())
   end
 
   if not drawn then -- nếu nhân vật nằm dưới tất cả các vật thể thì nó sẽ không được vẽ trong vòng lặp for
      love.graphics.draw(person, character[1] - person:getWidth()/2, character[2] - person:getHeight())
   end
 
   -- chỗ này dành cho mọi vật thể khác đứng trước nền 
 
end

Trước hết ta vẽ khung cảnh như thông thường, nó ở phía sau (cũng như bất kì vật thể nào ở phía trước sẽ luôn ở phía trước). Sau đó tôi tạo nên một biến có tên drawn. Biến này sẽ cho ta biết liệu nhân vật đã được vẽ chưa, để đảm bảo rằng nó sẽ không được vẽ lại nhiều lần khiến cho hình nhân vật sẽ xuất hiện phía trước những vật thể mà lẽ ra không phải vậy. Tiếp theo tôi đi vào một vòng lặp for và lặp qua tất cả những vật thể hiện có (vốn được xếp từ xa nhất về gần nhất). Sau đó tôi kiểm tra xem liệu vật thể có cần được đặt trước nhân vật không, nếu đúng thì bạn cần phải vẽ nhân vật (đồng thời ấn định giá trị của biến drawn). Lý do giải thích việc này là vì vòng lặp này sẽ tiến hành và vẽ các vật thể (ở ví dụ này là cùng một hình ảnh, nhưng không nhất thiết phải vậy) đến khi bắt gặp một đối tượng cần được xếp trước nhân vật; lúc đó thì vẽ nhân vật và rồi tiếp tục vẽ nốt những vật thể còn lại. Nếu đến cuối vòng lặp rồi mà vẫn chưa vẽ nhân vật thì điều này nghĩa là nhân vật đứng trước tất cả vật thể, và vì vậy sẽ vẽ nhân vật sau cùng.

Toàn bộ chỉ có vậy. Hãy tải về chương trình ví dụ và chạy thử. Có thể nó rất đơn giản nhưng bài hướng dẫn này chỉ nói về thứ tự vẽ và nếu bạn muốn biết cách kiểm tra xem nhân vật có va chạm với các vật thể khác hay không, hoặc nếu bạn muốn nhân vật đi đến nơi ta kích chuột thì bạn phải tự làm lấy (ít nhất là trước khi có một bài hướng dẫn về điều này). Nếu hướng dẫn trên vẫn chưa thật dễ hiểu, bạn hãy tự nhiên gửi ý kiến bình luận vào diễn đàn.

Xem thêm

Có một cách làm khác, thu phóng được theo tỉ lệ: Skip list:Drawing Order



Ngôn ngữ khác