Observer Pattern in Lua

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
Post Reply
User avatar
MarekkPie
Inner party member
Posts: 587
Joined: Wed Dec 28, 2011 4:48 pm
Contact:

Observer Pattern in Lua

Post by MarekkPie »

So when I was trying to make my LD48, I wanted to use an Entity-Component system. I didn't really plan it out all that well and ended up have to hold some variables inside the Entity to share across multiple components. Not really what you want for Entity-Component.

Doing a bit more reading, I saw that one of the ways to decouple the components from the entity is through a messaging system. Each component has a publish method that is then received up by the entity and distributed to all the other attached components, which can either accept or refuse the message

One thing that is confusing me, is how to implement the receive portion for the entity. For LOVE, you could probably just toss it in a method that gets called during the update phase, but observer patterns aren't supposed to require a game loop :)

The other option I can think of is to reference the entity inside the component, but again, that makes the entity and component tightly coupled.

Anyone have any thoughts/insights into implementing the Observer pattern in Lua, without either referencing the observer in the messengers or forcing periodic polling?
User avatar
Inny
Party member
Posts: 652
Joined: Fri Jan 30, 2009 3:41 am
Location: New York

Re: Observer Pattern in Lua

Post by Inny »

Design Patterns exist as a way to address deficiencies in a language. Things like the Entity-Component pattern exist to allow you to build up objects for your game via the idea that you should "Prefer Composition over Inheritance", rather than having deeply complexy and convoluted taxonomies and hierarchies of object classes. Luckily, you can get away without needing an Entity-Component system in Lua. The reason is Lua is a language with Duck Typing. Meaning that you can bypass the need to build up your entity classes with component classes, and just copy those components into the entity classes directly.

Of course, code always says more that words, so lets see some code:

Code: Select all

function player_input_control(self, dt)
  if love.keyboard.isDown("space") then self:shoot() end
end

function demo_input_control(self, dt)
  self.demo_time = (self.demo_time or 0) + dt
  if self.demo_time >= 5 then
    self.demo_time = self.demo_time - 5
    self:shoot()
  end
end

-- Change the player Entity's "Update" component
if current_level == "demo" then
  player.update = demo_input_control
else
  player.update = player_input_control
end
As for the observer pattern, that's a useful piece of code to be had, and kikito made a nice one: https://github.com/kikito/beholder.lua
User avatar
MarekkPie
Inner party member
Posts: 587
Joined: Wed Dec 28, 2011 4:48 pm
Contact:

Re: Observer Pattern in Lua

Post by MarekkPie »

Your example seems to conflate Entity-Component with Strategy. Components handle more information than merely an algorithm. I completely understand why Strategy is unimportant in Lua, but Entity-Component is a bit different.

For example, Entity-Component wouldn't allow self:shoot(), like you have in your methods, since you would have to assume the entity implements the shoot mechanic.
User avatar
kikito
Inner party member
Posts: 3153
Joined: Sat Oct 03, 2009 5:22 pm
Location: Madrid, Spain
Contact:

Re: Observer Pattern in Lua

Post by kikito »

Anyone have any thoughts/insights into implementing the Observer pattern in Lua, without either referencing the observer in the messengers or forcing periodic polling?
I don't think it's possible in pure Lua. You might be able to do something like that if you basically modify Lua itself (from C). But I don't think it's worth it.

If maintaining a reference to the "interesting object" feels "wrong" to you, maybe using a function which references it from its closure might feel less bad.
When I write def I mean function.
User avatar
MarekkPie
Inner party member
Posts: 587
Joined: Wed Dec 28, 2011 4:48 pm
Contact:

Re: Observer Pattern in Lua

Post by MarekkPie »

I actually think I found something that would work:

http://lua-users.org/wiki/AlternativeObserverPattern

I'd have to force all the components to have a publish method...but I was going to do that anyways. Here is what I have so far:

Code: Select all

function Entity:addComponent(c)
  self.components[#self.components + 1] = c
  c.publish:register(self, self.receive)
end

function Entity:removeComponent(c)
  for k,v in pairs(self.components) do
    if c.id == v.id then
      c.publish:deregister(self)
      table.remove(self.components, k)
    end
  end
end

function Entity:receive(id, msg)
  self:deliver(id, msg)
end

function Entity:deliver(id, msg)
  local id_tbl = type(id) == 'table' and id or {id}
  for _,v1 in ipairs(self.components) do
    for _,v2 in pairs(id_tbl) do
      if v2 == v1.id then
        v1:handle(msg)
      end
    end
  end
end
I haven't tested it yet; the one thing I am concerned with is the scope of self in addComponent()

Code: Select all

  c.publish:register(self, self.receive)
-- without the syntatic sugar
  c.publish.register(self --[[ the object publish ]], self --[[the object entity ]], self.receive --[[ the receive method for entity ]])
Would I need a local this = self to be able to get this to actually work?
User avatar
Inny
Party member
Posts: 652
Joined: Fri Jan 30, 2009 3:41 am
Location: New York

Re: Observer Pattern in Lua

Post by Inny »

MarekkPie wrote:Your example seems to conflate Entity-Component with Strategy. Components handle more information than merely an algorithm. I completely understand why Strategy is unimportant in Lua, but Entity-Component is a bit different.

For example, Entity-Component wouldn't allow self:shoot(), like you have in your methods, since you would have to assume the entity implements the shoot mechanic.
The code example would have been a bit more complicated to demonstrate duck typing, but the point I was trying to make is that your objects, or their metatables, can be compilations of functionality that's duck-typed, i.e., functionality is assumed to be there in some form. For instance, the self:shoot() call doesn't imply that the entity implements shoot, only that the entity has shoot, meaning that shoot can be one of those things that you mixed in before hand.

A better implementation of my idea would be a "Mixin" function, which I think middleclass has that. The idea being is that Components would be a plain lua table, and the mixin function just copies everything from the component into the entity. There would be a few details that I'm hand waving right now, that would need to be dealt with, like if two mixins both defined an update function, one would overwrite the other.
MarekkPie wrote:I actually think I found something that would work:
http://lua-users.org/wiki/AlternativeObserverPattern
Yeah, you can use that too. When I wrote it, I was a working as a Qt software engineer and I basically mimicked the Signals and Slots system. If I tried to write the same code today, I probably would allow for generic parameter binding, rather than forcing the self.

If you continue to use that code, here's the correct way without using the table colon operator. The first parameter needed to be changed.

Code: Select all

  c.publish.register(c.publish --[[ the object publish ]], self --[[the object entity ]], self.receive --[[ the receive method for entity ]])
User avatar
MarekkPie
Inner party member
Posts: 587
Joined: Wed Dec 28, 2011 4:48 pm
Contact:

Re: Observer Pattern in Lua

Post by MarekkPie »

I understand what duck-typing is. Rather than having to create some sort of IQuackable interface, implement that interface into both a Duck class and a Turkey class, and then be confident that I can call both Duck.Quack() and Turkey.Quack(), I just have to go MaybeDuck.Quack(), and during runtime, it'll just check to see if Quack is a function of MaybeDuck.

I also understand that Strategy is trivial when you have first class functions.

But there are some issues with just using duck-typing; mainly holding a state within the component. Again, Entity-Component is not Strategy: components can have states. From Game Programming Patterns:
This pattern bears resemblance to the Gang of Four’s Strategy pattern. Both patterns are about taking part of an object’s behavior and delegating it to a separate subordinate object. The difference is that with the strategy pattern, the separate “strategy” object is usually stateless— it encapsulates an algorithm but no data. It defines how an object behaves but not what it is.

Components are a bit more self-important. They often hold state that describes the object and help define its actual identity. However, the line may blur. You may have some components that don’t need any local state. In that case, you’re free to use the same component instance across multiple container objects. At that point, it really is behaving more akin to a strategy.
User avatar
Robin
The Omniscient
Posts: 6506
Joined: Fri Feb 20, 2009 4:29 pm
Location: The Netherlands
Contact:

Re: Observer Pattern in Lua

Post by Robin »

MarekkPie wrote:I just have to go MaybeDuck.Quack(), and during runtime, it'll just check to see if Quack is a function of MaybeDuck.
Not necessarily.

In dynamic languages like Lua and Python, yes, always, but if you're using Go you can use static duck typing (which does require you to write an interface, but not declare for each type "yeah this implements the Quacker interface").
Help us help you: attach a .love.
User avatar
MarekkPie
Inner party member
Posts: 587
Joined: Wed Dec 28, 2011 4:48 pm
Contact:

Re: Observer Pattern in Lua

Post by MarekkPie »

I know this...

...but I guess I wasn't clear enough.
User avatar
kexisse
Citizen
Posts: 56
Joined: Wed Jun 13, 2012 2:52 pm

Re: Observer Pattern in Lua

Post by kexisse »

I had a similar problem. I rolled my own Entity/Component model.
At first Entity had a few lists that components could add themselves to: drawComponents, updateComponents, collisionComponents.
Entity would call draw, update or onCollide on each of the components that had registered themselves.

However it felt kind of messy. So I swapped to using Beholder.lua. Components can observe or trigger at will, and I prefix the triggers with self to make sure they're only observed by the same entity.

Code: Select all

Beholder.trigger(self, "selected")
...
Beholder.observe(self, "selected", function() [do something] end)
I don't know what the performance is like but it seems quite decoupled and clean :)
Post Reply

Who is online

Users browsing this forum: Bing [Bot], Google [Bot] and 15 guests