Right now I learn 3D model animations in Defold and make tests by the official manual.
I did find a mistake in the manual. ‘nil’ doesn’t work as empty argument #4. Therefore I cannot launch Completion callbacks as argument #5. But everything work well without argument #4 and #5.
Can you please create a ticket on GitHub? The workaround is to use an empty table. But we’d like to solve the bug too.
Is empty table a variable
anim_empty_table = {}
?
Thanx! I learn Lua and manuals at no much free time.
About GitHub, I know how to clone repository on local hard drive after Bevy Engine, but unfortunatly no more. But I will try.
Click on this link and provide the requested information to create a bug report: https://github.com/defold/defold/issues/new?assignees=&labels=bug&projects=&template=bug_report.md&title=
empty_table = {}
An empty table succusfully works as argument #4. Thanks.
I created GitHub issue about error with
nil
as argument #4.
Thanks! Let’s fix it!
The Problem
The problem is that model.play_anim(url, anim, playback, [play_properties], [complete_function])
does not accept nil for play_properties
despite the reference manual stating that the argument is optional.
Component source code structure
Since this is an issue with arguments passed to a Defold Lua API function we know it is an issue in the Lua/script layer of the Defold engine code. For each Defold component there exist one file setting up the scripting layer with Lua bindings etc and one file for the actual component code which is running in the engine.
- script_model.cpp: https://github.com/defold/defold/blob/dev/engine/gamesys/src/gamesys/scripts/script_model.cpp
- comp_model.cpp: https://github.com/defold/defold/blob/dev/engine/gamesys/src/gamesys/components/comp_model.cpp
Lua script bindings
Each script_foobar.cpp
has a section of code which defines the script functions that are available to developers. This is basically a mapping between the Lua function name and a C function. Here’s the bindings for script_model.cpp
:
We see that for "play_anim"
we map to a C function LuaModelComp_PlayAnim()
:
{"play_anim", LuaModelComp_PlayAnim},
LuaModelComp_PlayAnim
Ok, let’s look at the function! Here it is:
Every such function which defines a Lua binding of a function works the same way. The function will get a Lua state as it’s first and only argument:
static int LuaModelComp_PlayAnim(lua_State* L)
{
The Lua state works as a stack of values that you can manipulate in different ways. You can check how many values that are on the stack, you can check the type of a specific value, you can push and pop values and so on. You can learn more about how the Lua state works in the official Lua manual.
In LuaModelComp_PlayAnim()
we check the arguments:
// The second argument should be a hash or string (the animation to play)
dmhash_t anim_id = dmScript::CheckHashOrString(L, 2);
// The third argument should be an integer (the playback mode)
lua_Integer playback = luaL_checkinteger(L, 3);
// The first argument should be a URL
// Here we resolve the URL of the model component (the receiver) and we also resolve from which script the function was called (the sender)
dmMessage::URL receiver;
dmMessage::URL sender;
dmScript::ResolveURL(L, 1, &receiver, &sender);
Now, for the fourth argument we do things a little bit differently. We check if there are four or more arguments (top
is the number of arguments). If there are, we also check that the fourth argument is a table:
if (top > 3) // table with args
{
luaL_checktype(L, 4, LUA_TTABLE);
What this tells us is that we only proceed to check the fourth argument is if there are four or more arguments. We do not check the fourth argument if there are only three arguments. This is why this call will work (there are only three arguments):
model.play_anim("#Mixamo_Y_bot_player", "Mixamo_punch", go.PLAYBACK_FORWARD)
While this won’t (there are five arguments):
model.play_anim("#Mixamo_Y_bot_player", "Mixamo_punch", go.PLAYBACK_FORWARD, nil, idle_return)
The solution
The solution to the problem is simple. We should only check the fourth argument if there are four or more arguments and the fourth one is not nil. We can do this like this:
if (top > 3 && !lua_isnil(L, 4)) // table with args
{
luaL_checktype(L, 4, LUA_TTABLE);
The lua_isnil(L, 4)
will return true
if the fourth value on the stack is nil
.
Submitting a PR
I have applied the fix in a branch, based off the dev
branch on GitHub. I’ve named the branch Issue-9095-model-play-anim-options-can-be-nil
. From this branch I created a pull request on GitHub: