I would generally recommend to avoid making high volume calls into Lua. That is partly about performance but mostly about engine architecture.
To get the perf bit out of the way: the bridging code between C++/Lua has overhead compared to just calling Lua from Lua or calling C++ from C++. Of course, even good Lua itself is significantly slower than decent C++ code. Some constructs very common to games - like such simple things as a 2D vector type - incur even more overhead in Lua due to its inability to have user-defined value types; that's even worse as treating such "fundamental" mathematical concepts as reference types opens one to all sorts of non-obvious bugs and hardships. The lack of static typing in Lua also starts to really hurt as the size of the code grows and the need for good accurate code navigation grows with it. In short, write as little real "game code" in Lua as possible for the best perf and likely best developer experience.
Now, on to engine architecture. You don't want a "sprite" to have its own render function, independent of whether you're working in Lua or C++ or any language. You also likely don't want many other sprite-specific functions that would be called frequently like that. Most of these features are much better suited to dedicated systems that handle all the specifics. For instance, for rendering a sprite, your sprite object should instead create a node in the graphics scene graph with the required properties (position, size, texture, etc.) and the graphics renderer then can figure out how to put the "sprite" on the screen every frame all by itself. This has several advantages:
First, it insulates the rendering code from the rest of the engine. You don't necessarily want to have a core renderer being dependent on your scripting module, or on physics, or input, or AI, or so on. Rendering should be fairly self-contained. That's going to make your code easier to read, easier to maintain, easier to optimize, and just all-around easier in general.
Second, putting everything related to rendering a sprite under the renderer's control allows for a lot of tricks that are very difficult to pull off otherwise. For instance, resource swapping, texture streaming, multi-threaded rendering, culling, shadowing, etc. If the graphics engine has to call back into game code or script code every time it needs to render, it's going to be very hard to branch that renderer out across multiple CPU cores and likewise hard to do many multi-pass algorithms.
The above are just as true for systems like physics, AI, and so on.
The idea then is that your game objects (sprites) should tell the low-level systems what to create and when to change them, rather than the low-level systems constantly re-querying the game objects every frame. Your script might then look something like:
-- asteroid.lua
function init(self)
self.transform = game.transform.create{
position=random_position(),
scale=random_scale(),
rotation=random(0, 2*PI)
}
self.renderable = game.graphics.createRenderable{
texture='/images/asteroid.png',
transform=self.transform
}
self.body = game.physics.createRigidBody{
shape=game.physics.CIRCLE,
linear_velocity=random_direction() * random_speed(),
angular_velocity=random_number()
transform=self.transform
}
end
The individual systems like physics now have no idea that scripts exist or that other systems exist. In fact, the only thing binding these systems together at all here are that (a) they depend on a common Transform
object so they can agree on a position, rotation, and scale, and (b) there's a Lua table somewhere that has handles to the individual systems' internal objects.
The structure above never needs to call into Lua after the object is initialized. You could of course still use Lua to hookup object-specific logic, like code to run on collision events or the like, for the high-level game scripting. The core systems though should have nothing to do with Lua.
The general architecture here is a form of component-oriented design, and with the muddling of the term one could also see it as a loose form of an "ECS" architecture (Entity-Component-System architecture).