Semuanya ialah objek
Semuanya hidup dalam bentuk objek.

Objek
Saya suka bekerja dengan objek, anda boleh menyimpan semua maklumat yang diperlukan kemudian di dalamnya. Lua tidak mempunyai sistem kelas seperti yang saya kenal dalam bahasa lain. Apa yang saya perlukan ialah sesuatu yang membolehkan kelas ditindih atau diperluas kemudian. Maksud saya dari sudut permainan yang menggunakan engine3.
Idea di sebaliknya ialah engine3 boleh dilancarkan supaya semua orang boleh menggunakannya. Permainan itu sendiri boleh kekal peribadi. Kedua-duanya wujud bersebelahan dan saling melengkapi. Selepas beberapa ujian, ia berjaya, dan kini objek boleh dicipta berdasarkan kelas.
Contoh kelas, engine3/core/camera.lua (versi terhad).
local Callback = require "lib.engine3.src.framework.core.callback"
---@class Camera: Object
---@field config Config
---@field gameCanvas Canvas
---@field x integer
---@field y integer
---@field width integer
---@field height integer
---@field followSpeed integer
---@field targetX integer
---@field targetY integer
---@field bound boolean
---@field bounds_min_x integer
---@field bounds_min_y integer
---@field bounds_max_x integer
---@field bounds_max_y integer
---@field callbackAfterStop function
---@field callback Callback
---@field super self
---@overload fun(data: CameraOptions):Camera
local Camera = Object:extend("Camera")
Camera:implement(Callback)
---@class CameraOptions
---@field x? integer Default 0
---@field y? integer Default 0
---@field width? integer Default canvas width
---@field height? integer Default canvas height
---@field followSpeed integer Default 1
function Camera:new(data)
if data == nil then
data = {}
end
self.config = entityManager:findOne(nil, "Config")
self.gameCanvas = entityManager:findOne({name = "gameCanvas"}, "Canvas")
self.x = data.x or 0
self.y = data.y or 0
self.width = data.width or self.gameCanvas:getWidth()
self.height = data.height or self.gameCanvas:getHeight()
self.followSpeed = data.followSpeed or 1
self.targetX = nil
self.targetY = nil
self.bound = true
self.bounds_min_x = nil
self.bounds_min_y = nil
self.bounds_max_x = nil
self.bounds_max_y = nil
self.callbackAfterStop = {}
entityManager:add(self)
end
---@param x integer
---@param y integer
---@param w integer
---@param h integer
function Camera:setBounds(x, y, w, h)
self.bound = true
self.bounds_min_x = x
self.bounds_min_y = y
self.bounds_max_x = x + w
self.bounds_max_y = y + h
end
---@param dt number
function Camera:update(dt)
-- core things
end
function Camera:setBounds(x, y, w, h)
self.bound = true
self.bounds_min_x = x
self.bounds_min_y = y
self.bounds_max_x = x + w
self.bounds_max_y = y + h
end
Katakan kamera dalam permainan perlu berkelakuan sedikit berbeza daripada kamera lalai engine3. Maka saya membuat fail game/framework/core/camera.lua dan merujuk kepada kelas camera engine3. Kemudian saya membuat perubahan saya.
---@class Camera: Object
---@field followEntity Actor|Animation
---@overload fun():Camera
local Camera = require("lib.engine3.src.framework.core.camera"):extend("Camera")
-- extra new function
---@return Actor|Animation
function Camera:getFollowEntity()
-- do things
return self.followEntity
end
-- overwrite the original
---@param dt number
function Camera:update(dt)
if self.followEntity then
-- do something
end
-- optional but possible, call the original camera update
Camera.super.update(self, dt)
end
Entity Manager
Seronok ada semua objek yang berbeza ini (mulai sekarang saya panggil objek sebagai entity), tetapi semuanya perlu boleh dicari dan digunakan di seluruh kod. Jadi perlu dibuat sesuatu untuk mengurus semuanya.
EntityManager:implement(Callback)
function EntityManager:new()
self.entities = {}
self.lastEntityId = 0
self.callbackAfterAdd = {}
self.callbackAfterDelete = {}
end
function EntityManager:createId()
end
function EntityManager:add(entities)
end
function EntityManager:del(entity, args)
...
if entity.release then
entity:release(args)
end
...
end
function EntityManager:delAll(args)
end
function EntityManager:getEntityIndex(entity)
end
function EntityManager:find(filter, className, enitities)
end
function EntityManager:findOne(filter, className, entities)
end
function EntityManager:filter(data, filter)
end
Ini sebenarnya jantung permainan, atau salah satu daripada banyak bahagian penting. Jika ada yang tidak kena di sini, anda ada masalah…
Apabila sesuatu entity dicipta, ia akan ditambah ke Entity Manager dan mungkin dibuang semula. Dalam kelas camera di atas anda sudah nampak bahawa ini dilakukan dengan baris di bawah.
entityManager:add(self)
Setiap kelas yang saya tulis ada baris itu, supaya ia menambah dirinya sendiri melalui entityManager.
Dalam kelas anda juga boleh membuat fungsi release, yang dijalankan secara automatik apabila entityManager membuang objek itu. Ini sangat penting untuk mengawal memori. Anda tidak mahu menyimpan semua aset seperti imej, tetapi mengeluarkannya daripada memori pada satu ketika.
function Camera:release()
-- do things when the entityManger delete this object
end
Penggunaan
Apabila saya ada kelas baharu di mana-mana dan memerlukan entity, contohnya kamera, ia boleh dilakukan dengan beberapa cara.
local cameras = entityManager:find(nil,"Camera")
Namun ini akan memulangkan jadual dengan semua Entity yang dijumpai jenis Camera (kelas). Ini tidak praktikal kerana dalam kes ini sentiasa ada 1 kamera, dan saya mahu 100% pasti bahawa Camera memang wujud.
local camera = entityManager:findOne(nil, "Camera")
function EntityManager:findOne(filter, className, entities)
local objects = self:find(filter, className, entities)
if #objects == 1 then
return objects[1]
elseif #objects > 1 then
error("Cannot get exactly one object, because it was found (" .. #objects .. ") times !")
end
if className ~= nil then
error("findOne() object does not exist :" .. className)
else
dump(filter)
error("findOne() Cannot find object")
end
end
Ini ialah pembalut di sekeliling find, dan memberi ralat apabila objek tidak wujud.
Beberapa contoh lain ialah
local entities = entityManager:find({name = "bird", visible = true}, "Frameset")
local entities = entityManager:find({name = "bird", visible = true}, "Frameset", customEntities)
-- special magic
local entities = entityManager:find({createdBy = Item}, "Frameset")
-- special magic
local entities = entityManager:find({collidableA = "CONTAINS_DATA"}, "Frameset")
-- special magic
local entities = entityManager:find({isObject = require "lib.engine3.src.framework.core.canvas", excludeEntities = self.canvases})
Akhir kata
Semua perkara yang saya cari berkaitan kelas dan objek kini sudah dipenuhi
- kelas
- kelas boleh diperluas
- kelas boleh ditindih
- entity dikawal oleh manager
- entity boleh saya gunakan di mana-mana
Sangat penting untuk membuat pilihan ini secepat mungkin dan menjangka bahawa kod akhir masih akan banyak berubah. Jika anda menyusun semuanya secara berstruktur, anda akan mendapat banyak manfaat kemudian.
Jika Lua tidak lagi mampu memproses bilangan entity, bahagian ini boleh ditukar kepada C!