This translation is community contributed and may not be up to date. We only maintain the English version of the documentation. Read this manual in English
Defold motoru, betik (script) yazımı için gömülü Lua dilini kullanır. Lua güçlü, hızlı ve başka yazılımlara gömülmesi kolay, hafif bir dinamik dildir. Video oyunlarında betik dili olarak yaygın biçimde kullanılır. Lua programları basit bir yordamsal sözdizimiyle yazılır. Dil dinamik türlendirmelidir ve bir bayt kodu (bytecode) yorumlayıcısı tarafından çalıştırılır. Artımlı çöp toplama (incremental garbage collection) ile otomatik bellek yönetimi sunar.
Bu kılavuz, genel olarak Lua programlamanın temellerine ve Defold’da Lua ile çalışırken dikkat etmeniz gerekenlere kısa bir giriş sunar. Python, Perl, Ruby, JavaScript veya benzer bir dinamik dil konusunda biraz deneyiminiz varsa oldukça hızlı ilerleyebilirsiniz. Programlamaya yeni başlıyorsanız başlangıç düzeyine yönelik bir Lua kitabıyla başlamak isteyebilirsiniz. Aralarından seçim yapabileceğiniz pek çok kitap vardır.
Defold, Lua’nın oyunlarda ve performansın kritik olduğu diğer yazılımlarda kullanıma uygun, yüksek ölçüde optimize edilmiş bir sürümü olan LuaJIT kullanır. Lua 5.1’den geçişte tam uyumluluk sağlar ve tüm standart Lua kütüphanesi işlevleriyle Lua/C API işlevlerinin tamamını destekler.
LuaJIT ayrıca çeşitli dil uzantıları ile bazı Lua 5.2 ve 5.3 özellikleri ekler.
Defold’un tüm platformlarda aynı şekilde çalışmasını hedefliyoruz, ancak şu anda platformlar arasında Lua dili sürümü açısından birkaç küçük farklılık bulunuyor:
Oyununuzun desteklenen tüm platformlarda çalışmasını garantilemek için YALNIZCA Lua 5.1 dil özelliklerini kullanmanızı önemle öneririz.
Defold, Lua 5.1 standart kütüphanelerinin tamamının yanı sıra bir soket ve bir bit işlemleri kütüphanesi içerir:
assert(), error(), print(), ipairs(), require() vb.)Tüm kütüphaneler API başvuru belgelerinde açıklanmıştır.
Programların basit, okunması kolay bir sözdizimi vardır. Her deyim ayrı bir satıra yazılır ve deyimin sonunu işaretlemek gerekmez. İsterseniz deyimleri ayırmak için noktalı virgül ; kullanabilirsiniz. Kod bloklarının sınırları anahtar sözcüklerle belirlenir ve bloklar end anahtar sözcüğüyle biter. Yorumlar bir blok halinde veya satır sonuna kadar yazılabilir:
--[[
Here is a block of comments that can run
over several lines in the source file.
--]]
a = 10
b = 20 ; c = 30 -- two statements on one line
if my_variable == 3 then
call_some_function(true) -- Here is a line comment
else
call_another_function(false)
end
Lua dinamik türlendirmelidir; yani değişkenlerin türü yoktur, ancak değerlerin vardır. Statik türlendirmeli dillerden farklı olarak, istediğiniz herhangi bir değeri herhangi bir değişkene atayabilirsiniz.
Lua’da sekiz temel tür vardır:
nilnil değeri vardır. Genellikle yararlı bir değerin bulunmadığını ifade eder; örneğin değer atanmamış değişkenlerde kullanılır.
print(my_var) -- will print 'nil' since 'my_var' is not yet assigned a value
true veya false değerini alır. false veya nil olan koşullar yanlış olarak değerlendirilir. Diğer tüm değerler doğru olarak değerlendirilir.
flag = true
if flag then
print("flag is true")
else
print("flag is false")
end
if my_var then
print("my_var is not nil nor false!")
end
if not my_var then
print("my_var is either nil or false!")
end
print(10) --> prints '10'
print(10.0) --> '10'
print(10.000000000001) --> '10.000000000001'
a = 5 -- integer
b = 7/3 -- float
print(a - b) --> '2.6666666666667'
\0) dahil herhangi bir 8 bitlik değeri içerebilen, değişmez bayt dizileridir. Lua bir dizenin içeriği hakkında varsayımda bulunmaz; bu nedenle dizelerde istediğiniz veriyi saklayabilirsiniz. Dize sabitleri tek veya çift tırnak içinde yazılır. Lua, çalışma sırasında sayılarla dizeler arasında dönüşüm yapar. Dizeler .. işleciyle birleştirilebilir.
Dizeler, aşağıdaki C tarzı kaçış dizilerini içerebilir:
| Dizi | Karakter |
|---|---|
\a |
zil |
\b |
geri silme |
\f |
sayfa ilerletme |
\n |
yeni satır |
\r |
satır başı |
\t |
yatay sekme |
\v |
dikey sekme |
\\ |
ters eğik çizgi |
\" |
çift tırnak |
\' |
tek tırnak |
\[ |
sol köşeli ayraç |
\] |
sağ köşeli ayraç |
\ddd |
sayısal değeriyle belirtilen karakter; burada ddd, en fazla üç ondalık rakamdan oluşan bir dizidir |
my_string = "hello"
another_string = 'world'
print(my_string .. another_string) --> "helloworld"
print("10.2" + 1) --> 11.2
print(my_string + 1) -- error, can't convert "hello"
print(my_string .. 1) --> "hello1"
print("one\nstring") --> one
--> string
print("\097bc") --> "abc"
multi_line_string = [[
Here is a chunk of text that runs over several lines. This is all
put into the string and is sometimes very handy.
]]
function name(param1, param2) ... end) sunar.
-- Assign 'my_plus' to function
my_plus = function(p, q)
return p + q
end
print(my_plus(4, 5)) --> 9
-- Convenient syntax to assign function to variable 'my_mult'
function my_mult(p, q)
return p * q
end
print(my_mult(4, 5)) --> 20
-- Takes a function as parameter 'func'
function operate(func, p, q)
return func(p, q) -- Calls the provided function with parameters 'p' and 'q'
end
print(operate(my_plus, 4, 5)) --> 9
print(operate(my_mult, 4, 5)) --> 20
-- Create an adder function and return it
function create_adder(n)
return function(a)
return a + n
end
end
adder = create_adder(2)
print(adder(3)) --> 5
print(adder(10)) --> 12
0 değil, 1 olur.
-- Initialize a table as a sequence
weekdays = {"Sunday", "Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday"}
print(weekdays[1]) --> "Sunday"
print(weekdays[5]) --> "Thursday"
-- Initialize a table as a record with sequence values
moons = { Earth = { "Moon" },
Uranus = { "Puck", "Miranda", "Ariel", "Umbriel", "Titania", "Oberon" } }
print(moons.Uranus[3]) --> "Ariel"
-- Build a table from an empty constructor {}
a = 1
t = {}
t[1] = "first"
t[a + 1] = "second"
t.x = 1 -- same as t["x"] = 1
-- Iterate over the table key, value pairs
for key, value in pairs(t) do
print(key, value)
end
--> 1 first
--> 2 second
--> x 1
u = t -- u now refers to the same table as t
u[1] = "changed"
for key, value in pairs(t) do -- still iterating over t!
print(key, value)
end
--> 1 changed
--> 2 second
--> x 1
userdata, herhangi bir C verisinin Lua değişkenlerinde saklanabilmesini sağlar. Defold, karma değerlerini (hash), URL nesnelerini (url), matematik nesnelerini (vector3, vector4, matrix4, quaternion), oyun nesnelerini (game object), GUI düğümlerini (node), işleme yüklemlerini (predicate), işleme hedeflerini (render_target) ve işleme sabiti arabelleklerini (constant_buffer) saklamak için Lua userdata nesnelerini kullanır+, -, *, /, tekli - (işaret değiştirme) ve üs alma işleci ^.
a = -1
print(a * 2 + 3 / 4^5) --> -1.9970703125
Lua, çalışma sırasında sayılarla dizeler arasında otomatik dönüşüm sağlar. Bir dizeye uygulanan her sayısal işlem, dizeyi sayıya dönüştürmeyi dener:
print("10" + 1) --> 11
< (küçüktür), > (büyüktür), <= (küçüktür veya eşittir), >= (büyüktür veya eşittir), == (eşittir), ~= (eşit değildir). Bu işleçler her zaman true veya false döndürür. Farklı türlerdeki değerler farklı kabul edilir. Türler aynıysa değerlerine göre karşılaştırılırlar. Lua; tabloları, userdata değerlerini ve işlevleri başvurularına göre karşılaştırır. Bu türden iki değer, yalnızca aynı nesneye başvuruyorlarsa eşit kabul edilir.
a = 5
b = 6
if a <= b then
print("a is less than or equal to b")
end
print("A" < "a") --> true
print("aa" < "ab") --> true
print(10 == "10") --> false
print(tostring(10) == "10") --> true
and, or ve not. and, ilk bağımsız değişkeni false ise onu, aksi halde ikinci bağımsız değişkeni döndürür. or, ilk bağımsız değişkeni false değilse onu, aksi halde ikinci bağımsız değişkeni döndürür.
print(true or false) --> true
print(true and false) --> false
print(not false) --> true
if a == 5 and b == 6 then
print("a is 5 and b is 6")
end
.. işleciyle birleştirilebilir. Sayılar birleştirme sırasında dizeye dönüştürülür.
print("donkey" .. "kong") --> "donkeykong"
print(1 .. 2) --> "12"
#. Bir dizenin uzunluğu, içerdiği bayt sayısıdır. Bir tablonun uzunluğu, sıralı dizisinin uzunluğudur; yani 1‘den başlayarak yukarı doğru numaralandırılmış ve değeri nil olmayan indekslerin sayısıdır. Not: Sıralı dizide nil değerli “boşluklar” varsa uzunluk, bir nil değerinden önce gelen herhangi bir indeks olabilir.
s = "donkey"
print(#s) --> 6
t = { "a", "b", "c", "d" }
print(#t) --> 4
u = { a = 1, b = 2, c = 3 }
print(#u) --> 0
v = { "a", "b", nil }
print(#v) --> 2
Lua, yaygın akış denetimi yapılarını sunar.
then bölümünü, aksi halde isteğe bağlı else bölümünü yürütür. if deyimlerini iç içe yazmak yerine elseif kullanabilirsiniz. Bu, Lua’da bulunmayan switch deyiminin yerini alır.
a = 5
b = 4
if a < b then
print("a is smaller than b")
end
if a == '1' then
print("a is 1")
elseif a == '2' then
print("a is 2")
elseif a == '3' then
print("a is 3")
else
print("I have no idea what a is...")
end
weekdays = {"Sunday", "Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday"}
-- Print each weekday
i = 1
while weekdays[i] do
print(weekdays[i])
i = i + 1
end
weekdays = {"Sunday", "Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday"}
-- Print each weekday
i = 0
repeat
i = i + 1
print(weekdays[i])
until weekdays[i] == "Saturday"
for döngüsü vardır: sayısal ve genel. Sayısal for, 2 veya 3 sayısal değer alırken genel for, bir yineleyici (iterator) işlevinin döndürdüğü tüm değerleri dolaşır.
-- Print the numbers 1 to 10
for i = 1, 10 do
print(i)
end
-- Print the numbers 1 to 10 and increment with 2 each time
for i = 1, 10, 2 do
print(i)
end
-- Print the numbers 10 to 1
for i=10, 1, -1 do
print(i)
end
t = { "a", "b", "c", "d" }
-- Iterate over the sequence and print the values
for i, v in ipairs(t) do
print(v)
end
for, while veya repeat döngüsünün iç bloğundan çıkmak için break deyimini kullanın. Bir işlevden değer döndürmek ya da işlevin yürütülmesini bitirip çağırana dönmek için return kullanın. break veya return, yalnızca bir bloğun son deyimi olarak yer alabilir.
a = 1
while true do
a = a + 1
if a >= 100 then
break
end
end
function my_add(a, b)
return a + b
end
print(my_add(10, 12)) --> 22
Bildirdiğiniz tüm değişkenler varsayılan olarak geneldir (global); yani Lua çalışma zamanı bağlamının her yerinden kullanılabilirler. Değişkenleri açıkça local olarak bildirebilirsiniz; bu durumda değişken yalnızca geçerli kapsam içinde var olur.
Her Lua kaynak dosyası ayrı bir kapsam tanımlar. Bir dosyanın en üst düzeyindeki local bildirimleri, değişkenin o Lua betik dosyasına yerel olduğu anlamına gelir. Her işlev yeni bir iç kapsam oluşturur ve her denetim yapısı bloğu ek kapsamlar oluşturur. do ve end anahtar sözcükleriyle açıkça bir kapsam oluşturabilirsiniz. Lua sözcüksel kapsam (lexical scoping) kullanır; yani bir kapsam, kendisini çevreleyen kapsamdaki yerel değişkenlere tam erişim sağlar. Yerel değişkenlerin kullanılmadan önce bildirilmesi gerektiğine dikkat edin.
function my_func(a, b)
-- 'a' and 'b' are local to this function and available through its scope
do
local x = 1
end
print(x) --> nil. 'x' is not available outside the do-end scope
print(foo) --> nil. 'foo' is declared after 'my_func'
print(foo_global) --> "value 2"
end
local foo = "value 1"
foo_global = "value 2"
print(foo) --> "value 1". 'foo' is available in the topmost scope after declaration.
Bir betik dosyasında işlevleri local olarak bildirirseniz (ki bu genellikle iyi bir fikirdir) kodun sıralamasına dikkat etmeniz gerekir. Birbirini çağıran işlevleriniz varsa ileri bildirimler kullanabilirsiniz.
local func2 -- Forward declare 'func2'
local function func1(a)
print("func1")
func2(a)
end
function func2(a) -- or func2 = function(a)
print("func2")
if a < 10 then
func1(a + 1)
end
end
function init(self)
func1(1)
end
Başka bir işlevin içinde bir işlev yazarsanız bu işlev de kendisini çevreleyen işlevin yerel değişkenlerine tam erişim sağlar. Bu çok güçlü bir yapıdır.
function create_counter(x)
-- 'x' is a local variable in 'create_counter'
return function()
x = x + 1
return x
end
end
count1 = create_counter(10)
count2 = create_counter(20)
print(count1()) --> 11
print(count2()) --> 21
print(count1()) --> 12
Bir blokta bildirilen yerel değişkenler, çevreleyen bloktaki aynı adlı değişkenleri gölgeler.
my_global = "global"
print(my_global) -->"global"
local v = "local"
print(v) --> "local"
local function test(v)
print(v)
end
function init(self)
v = "apple"
print(v) --> "apple"
test("banana") --> "banana"
end
İşlevler baştan sona yürütülür ve onları yarıda durdurmanın bir yolu yoktur. Eş yordamlar bunu yapmanıza olanak tanır ve bu bazı durumlarda çok kullanışlı olabilir. Bir oyun nesnesini 1. kareden 5. kareye kadar, y konumu 0‘dan belirli y konumlarına taşıdığımız, kare kare ilerleyen çok özel bir animasyon oluşturmak istediğimizi varsayalım. Bunu update() işlevinde (aşağıya bakın) bir sayaç ve konum listesiyle çözebiliriz. Ancak eş yordam kullanarak genişletmesi ve üzerinde çalışması kolay, çok temiz bir uygulama elde ederiz. Durumun tamamı eş yordamın kendi içinde tutulur.
Bir eş yordam yürütmeye ara verdiğinde denetimi çağırana geri verir, ancak yürütmenin hangi noktasında olduğunu hatırlar; böylece daha sonra o noktadan devam edebilir.
-- This is our coroutine
local function sequence(self)
coroutine.yield(120)
coroutine.yield(320)
coroutine.yield(510)
coroutine.yield(240)
return 440 -- return the final value
end
function init(self)
self.co = coroutine.create(sequence) -- Create the coroutine. 'self.co' is a thread object
go.set_position(vmath.vector3(100, 0, 0)) -- Set initial position
end
function update(self, dt)
local status, y_pos = coroutine.resume(self.co, self) -- Continue execution of coroutine.
if status then
-- If the coroutine is still not terminated/dead, use its yielded return value as a new position
go.set_position(vmath.vector3(100, y_pos, 0))
end
end
Bildirdiğiniz tüm değişkenler varsayılan olarak geneldir; yani Lua çalışma zamanı bağlamının her yerinden kullanılabilirler. Defold’un game.project dosyasında bu bağlamı denetleyen bir shared_state ayarı vardır. Seçenek etkinse tüm betikler, GUI betikleri ve işleme betiği (render script) aynı Lua bağlamında değerlendirilir ve genel değişkenler her yerden görünür. Seçenek etkin değilse motor; betikleri, GUI betiklerini ve işleme betiğini ayrı bağlamlarda yürütür.

Defold, aynı betik dosyasını birkaç ayrı oyun nesnesi bileşeninde (component) kullanmanıza izin verir. Yerel olarak bildirilen tüm değişkenler, aynı betik dosyasını çalıştıran bileşenler arasında paylaşılır.
-- 'my_global_value' will be available from all scripts, gui_scripts, render script and modules (Lua files)
my_global_value = "global scope"
-- this value will be shared through all component instances that use this particular script file
local script_value = "script scope"
function init(self, dt)
-- This value will be available on this script component instance
self.foo = "self scope"
-- this value will be available inside init() and after it's declaration
local local_foo = "local scope"
print(local_foo)
end
function update(self, dt)
print(self.foo)
print(my_global_value)
print(script_value)
print(local_foo) -- will print nil, since local_foo is only visible in init()
end
Akıcı bir şekilde 60 FPS hızında çalışması hedeflenen yüksek performanslı bir oyunda, küçük performans hataları deneyimi büyük ölçüde etkileyebilir. Dikkat edilmesi gereken bazı basit genel noktalar ve ilk bakışta sorunlu görünmeyebilecek bazı durumlar vardır.
Basit noktalardan başlayalım. Gereksiz döngüler içermeyen, doğrudan anlaşılır kod yazmak genellikle iyi bir fikirdir. Bazen öğe listelerini dolaşmanız gerekir, ancak liste yeterince büyükse dikkatli olun. Bu örnek, oldukça iyi bir dizüstü bilgisayarda 1 milisaniyeden biraz daha uzun sürede çalışır. Her karenin yalnızca 16 milisaniye sürdüğü (60 FPS hızında) ve motorun, işleme betiğinin, fizik benzetiminin vb. bu sürenin bir kısmını kullandığı düşünüldüğünde, bu süre belirleyici olabilir.
local t = socket.gettime()
local table = {}
for i=1,2000 do
table[i] = vmath.vector3(i, i, i)
end
print((socket.gettime() - t) * 1000)
-- DEBUG:SCRIPT: 0.40388
Şüphelendiğiniz kodun performansını ölçmek için socket.gettime() işlevinin döndürdüğü değeri (sistemin zaman başlangıcından beri geçen saniye sayısı) kullanın.
Lua’nın çöp toplama işlemi varsayılan olarak arka planda otomatik çalışır ve Lua çalışma zamanı ortamının ayırdığı belleği geri kazanır. Çok miktarda çöp toplamak zaman alabileceğinden, çöp toplama gerektiren nesnelerin sayısını düşük tutmak yararlıdır:
local v = 42)local s = "some_string" yazmak yeni bir nesne oluşturur ve bu nesneyi s değişkenine atar. Yerel s değişkeninin kendisi çöp oluşturmaz, ancak dize nesnesi çöp oluşturur. Aynı dizeyi birden fazla kez kullanmanın ek bellek maliyeti yoktur.{ ... }) yeni bir tablo oluşturulur.function () ... end deyimini yürütmek)function(v, ...) end), işlev her çağrıldığında üç nokta için bir tablo oluşturur (Lua’nın 5.2 öncesi sürümlerinde veya LuaJIT kullanılmıyorsa).dofile() ve dostring()Yeni nesneler oluşturmak yerine elinizdekileri yeniden kullanabileceğiniz pek çok durum vardır. Örneğin, her update() işlevinin sonunda aşağıdaki kullanım yaygındır:
-- Reset velocity
self.velocity = vmath.vector3()
Her vmath.vector3() çağrısının yeni bir nesne oluşturduğunu unutmak kolaydır. Bir vector3 nesnesinin ne kadar bellek kullandığını bulalım:
print(collectgarbage("count") * 1024) -- 88634
local v = vmath.vector3()
print(collectgarbage("count") * 1024) -- 88704. 70 bytes in total has been allocated
collectgarbage() çağrıları arasında 70 bayt eklenmiştir, ancak buna vector3 nesnesi dışındaki bellek ayırmaları da dahildir. collectgarbage() sonucunun her yazdırılışında, kendi başına 22 bayt çöp ekleyen bir dize oluşturulur:
print(collectgarbage("count") * 1024) -- 88611
print(collectgarbage("count") * 1024) -- 88633. 22 bytes allocated
Yani bir vector3 nesnesi 70-22=48 bayt yer kaplar. Bu fazla değildir, ancak 60 FPS hızındaki bir oyunda her karede bir tane oluşturursanız bir anda saniyede 2,8 kB çöp ortaya çıkar. Her biri her karede bir vector3 oluşturan 360 betik bileşeniyle, saniyede 1 MB çöp üretilir. Miktarlar çok hızlı birikebilir. Lua çalışma zamanı ortamı çöp toplarken, özellikle mobil platformlarda, değerli milisaniyelerinizin çoğunu tüketebilir.
Bellek ayırmalarından kaçınmanın bir yolu, bir vector3 oluşturup aynı nesneyle çalışmaya devam etmektir. Örneğin, bir vector3 nesnesini sıfırlamak için aşağıdaki yapıyı kullanabiliriz:
-- Instead of doing self.velocity = vmath.vector3() which creates a new object
-- we zero an existing velocity vector object's components
self.velocity.x = 0
self.velocity.y = 0
self.velocity.z = 0
Varsayılan çöp toplama düzeni, zamanlamanın kritik olduğu bazı uygulamalar için en uygun seçenek olmayabilir. Oyununuzda veya uygulamanızda takılmalar görüyorsanız collectgarbage() Lua işleviyle Lua’nın çöp toplama biçimini ayarlamak isteyebilirsiniz. Örneğin, düşük bir step değeriyle toplayıcıyı her karede kısa bir süre çalıştırabilirsiniz. Oyununuzun veya uygulamanızın ne kadar bellek tükettiği hakkında fikir edinmek için geçerli çöp miktarını bayt cinsinden şöyle yazdırabilirsiniz:
print(collectgarbage("count") * 1024)
Yaygın bir uygulama tasarımı konusu, ortak davranışlar için kodun nasıl yapılandırılacağıdır. Birkaç yaklaşım mümkündür.

Ayrıca modül kodunun bir oyun nesnesinin iç yapısını doğrudan değiştirmesi mümkün olsa da (bir modül işlevine self aktararak), çok sıkı bir bağımlılık oluşturacağınız için bunu yapmaktan kesinlikle kaçınmanızı öneririz.

go.property() URL değeri kullanılabilir.

Bu düzenin yararı, hedef nesneyi içeren bir koleksiyona (collection) bir davranış oyun nesnesi bırakabilmenizdir. Hiçbir ek kod gerekmez.
Çok sayıda oyun nesnesini yönetmeniz gereken durumlarda bu tasarım tercih edilmez; çünkü davranış nesnesi her örnek (instance) için çoğaltılır ve her nesnenin bellek maliyeti vardır.