Jump to content

More amazing things you can do with Lua in Leadwerks 5

JoshMK

1,599 views

Our implementation of Lua in Leadwerks 5 is shaping up to be a dream come true. Below are some of the great improvements that are being made.

Access STL Containers in Lua

You can access STL containers directly from Lua:

for n = 1, #entity.kids do
	entity.kids[n]:Move(1,0,0)
end
while #entity.kids > 0 do
	entity.kids[1]:SetParent(nil)
end

In fact, verbose commands like CountChildren() and GetChild() are no longer needed at all. On the C++ side you can use this:

for (int n=0; n<entity->kids.size(); n++) {
	entity->kids[n]->Move(1,0,0);
}
while (entity->kids.size()) {
	entity->kids[0]->SetParent(nullptr);
}

Note that in C++ arrays start with 0 and in Lua they start with 1.

This also allows us to return STL contains from functions or accept them as arguments. No more ForEachEntity... callbacks are needed:

local aabb = AABB(-10,10,0,5,-10,10)
local entities = world:GetEntitiesInAABB(aabb)
for n=1,#entities do
	entities[n]:AddForce(0,10,0)
end

Super Pro User-defined Values

There will be no more self.entity or entity.script conventions in Leadwerks 5. Functions and user-defined values will be attached directly to the entity itself. The example below shows user-defined values that persist even when the entity goes out of scope of the Lua virtual machine:

--Create child
local a = CreateBox(world,1,1,1)

--Set a user-defined value
a.health = 100

--Create parent
local b = CreateBox(world,1,1,1)

--Set parent
a:SetParent(b,true)

--Let child go out of scope (parent keeps it from being deleted in C++)
a = nil

--Collect garbage
collectgarbage()

--Get the child
local c = b.kids[1]

--Check the value
print(c.health) --prints '100'

In Leadwerks 4 an entity script might look like this:

function Script:Update()
	self.entity:Turn(self.speed,0,0)
end

In Leadwerks 5 it is simpler because self is the actual entity:

function Entity:Update()
	self:Turn(self.speed,0,0)
end

In Leadwerks 4 you have to check to see if an entity has a script attached and us that to store all user-defined values:

if world:Pick(v1,v2,pickinfo) then
	if pickinfo.entity.script~=nil then
		if type(pickinfo.entity.script.TakeDamage)=="function" then
			pickinfo.entity.script:TakeDamage(10)
		end
	end
end

Leadwerks 5 is a lot simpler. You just check if the functions exists on the entity and then call it:

if world:Pick(v1,v2,pickinfo) then
	if type(pickinfo.entity.TakeDamage)=="function" then
		pickinfo.entity:TakeDamage(10)
	end
end

You can even assign custom properties to entities without worrying whether they have a script attached:

function Entity:Collision( collidedentity, position, normal, speed )
	collidedentity.lasthitobject = self --No script? No problem!
end

In fact all a script does is attach some functions and values to an entity and then it is gone. There is no fundamental difference between a scripted and non-scripted entity.

Casting Objects

Casting objects in Leadwerks 4 uses syntax that is a little awkward. I actually had to look up the tolua.cast function just now because I couldn't remember the order of the arguments:

local a = Model:Box()
local b = Model:Box()
a:SetParent(b)
local entity = b:GetChild(0)
local model = tolua.cast(entity,"Model")

Casting is simpler and more intuitive in Leadwerks 5:

local a = CreateBox()
local b = CreateBox()
a:SetParent(b)
local entity = b:kids[1]
local model = Model(entity)

If the entity is not a model then the casting function will just return nil.

A big thanks goes out to the developers of sol2, an awesome modern Lua binding library with support for C++11 smart pointers.

  • Like 2


6 Comments


Recommended Comments

Incidentally, this would be better for really large numbers (thousands) of children because it removes objects from the end of the vector.

while #entity.kids > 0 do
	entity.kids[#entity.kids]:SetParent(nil)
end

I am also looking into creating a new version of std::vector where the [] operator is read-only, since changing the entity kids elements could cause serious problems.

Share this comment


Link to comment

That looks amazing! There is so much overhead disappearing this way. Looking forward to using it.

So you would get something like this?

--le4
Script.playerEntity = nil --entity
local playerScript = nil

function Script:Start()
	--Get the script of the  referenced player entity and call its hello function
	playerScript = self.playerEntity.script
	playerScript:Hello()
end

--le5?
Script.playerEntity = nil --entity

function Script:Start()
	self.playerEntity:Hello()
end

 

Share this comment


Link to comment

I actually find it a little disorienting after being so used to the le4 way of doing things but I love the simplicity.

Share this comment


Link to comment

I think you are right about that. Our brains are all wired up to using these standard layouts after so long. Still, I see it as a positive evolution of the code.

Share this comment


Link to comment

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Add a comment...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...