This repository has been archived by the owner on Mar 8, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 142
The Power of Lua and Mixins #2
Labels
Comments
Excellent post, @adonaac. Can I link it into my class implementation's README ? |
Sure! |
In Object Creation:
|
@Kinrany Thanks for pointing that out, I changed it according to what I meant. |
Hm, recently I'm found some idea/paradigm(and libs for Love2d for this) that named ECS and it's contains some ideas like using mixins by this way, but without OOP or with less using of it. Just maybe this will be interesting for somebody. |
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
2014-01-07 06:30
This post deals with OOP and Lua, and since Lua has no OOP built-in by default I'll make use of a library. kikito has a great OOP library made for Lua that supports mixins. rxi also has one (it's the one I use now). Either way, as stated in one of those links: "Mixins can be used for sharing methods between classes, without requiring them to inherit from the same father". If you've read anything about component based system design, then you've read something like that quote but probably without the word mixin anywhere near it. Since I've been using mixins as the foundation of the entity code in Kara, in this post I'll explain their advantages and what you can possibly get out of them.
Objects, attributes and methods in Lua
The one thing to remember about Lua is that everything in it is a table, except the things that aren't. But pretty much all objects are going to be made out of tables (essentially hash tables) that have attribute and method names as keys and attribute values and functions as values. So, for instance, this:
... is, for all that matters to this post, the same as having a table named
object
with keysinit
,x
andy
:Mixins
If you've looked at this mixins explanation it should be pretty clear what's going on. In case it isn't: an
include
call simply adds the defined functions of a certain mixin to a certain class, which means adding a new key to the table that is the object. In a similar fashion, a function that changes an object's attributes can be defined:This example is exactly the same as the first one up there, except that instead of directly setting the
x
andy
attributes, the mixin does it. Theinclude
call adds themixinFunctionInit
function to the class, then calling that function makes changing the object's attributes possible (by passing self, a reference to the object being modified). It's a very easy and cheap way of getting some flexibility into your objects. That flexibility can get you the following:Reusability
Component based systems were mentioned, and mixins are a way of getting there. For instance, these are the initial calls for two classes in Kara, the Hammer and a Blaster (an enemy):
Notice how the
PhysicsRectangle
,Timer
andSteerable
mixins repeat themselves? That's reusability in its purest form.PhysicsRectangle
is a simple wrapper mixin for LÖVE's box2d implementation, and, as you may guess, makes it so that the object becomes a physics object of rectangular shape. TheTimer
mixin implements a wrapper for the main Timer class, containing calls likeafter
(do something aftern
seconds) orevery
(do something everyn
seconds). A bit off-topic, but since you can pass functions as arguments in Lua, timers are pretty nifty and have pretty simple calls that look like this:Anyway, lastly, the
Steerable
mixin implements steering behaviors, which are used to control how far and in which way something moves towards a target. With a bit of work steering behaviors can be used in all sorts of ways to achieve basic entity behaviors while allowing for the possibility of more complex ones later on. In any case, those three mixins were reused with changes only to the variables passed in on their initialization and sometimes on their update function. Otherwise, it's all the same code that can be used by multiple classes. Consider the alternative, where reuse would have to rely on inheritance and somehow Hammer and Blaster would have to be connected in some weird way, even though they're completely different things (one is something the Player uses as a tool of attack, the other is an Enemy).And consider the other alternative, which is the normal component based system. It usually uses some sort of message passing or another mechanism to get variables from other components in
the same object (kinda yucky!). While the mixin based system simply changes those attributes directly, because when you're coding a mixin you always have a reference to the object you're
coding to via the
self
parameter.Object Creation
The mutability offered by mixins (in this case Lua is also important) also helps when you're creating objects. For instance, this is the single call that I need to create new objects in my game:
And then this gets called every frame to create the objects inside the
to_be_created
list (this must be done because I can't create objects while box2d runs its own update cycle,and it usually happens that the
create
function is called precisely while box2d is running):Essentially, whenever I create a class like this:
"Entity = class('Entity')"
I'm actually creating a global variable called Entity that holds the Entity class definition (it's the table that is used as a prototype for creating Entity instances). Since Lua's global state is inside a table (and that table is called_G
), I can access the class by going through that global table and then just calling its constructor with the appropriate parameters passed. And the clevererest part of this is actually thesettings
table. All entities in Kara derive from an Entity class, which looks like this:The last few lines being the most important ones, as the attributes of the class are changed according to the settings table. So whenever an object is created you get a choice to add whatever attributes (or even functions) you want to an object, which adds a lot of flexibility, since otherwise you'd have to create many different classes (or add various attributes to the same class) for all different sorts of random properties that can happen in a game.
Readability
Reusable code is locked inside mixins and entity specific code is clearly laid out on that entity's file. This is a huge advantage whenever you're reading some entity's code because you never get lost into what is relevant and what isn't. For example, look at the code for the smoke/dust class:
The entity specific code here happens in the constructor after the
visualInit
call. If I ever look back at this in another month, year or decade I will know that if I want to change the smoke's behavior I need to change something in that block of entity specific code. If I want to change how to smoke looks I have to change the first parameter in thevisualInit
call. If I have to change something in how the fading in or out of the effect works, I have to change something in thefaderInit
call. Everything is very properly divided and hidden and there's a clear way of knowing where everything is and what everything does.ENDING
Hopefully if you could understand any of that you can see why Lua is great and why mixins are the best. There are certainly drawbacks to using this, but if you're coding by yourself or with only another programmer then the rules don't matter because you can do anything you want. You are a shining star and the world is only waiting for you to shine your bright beautiful light upon it.
The text was updated successfully, but these errors were encountered: