RX + Defold

Make some experiments with RxLua and defold.

Rx it is a reactive programming. You can read more here http://reactivex.io/.

Rx is based on Observables , Observers, and Operators

An Observer essentially subscribes to an Observable.

The Observable then emits streams of data which the Observer listens and reacts to, setting in motion a chain of operations on the data stream. The real power comes from Operators or “Reactive Extensions” (hence the term Rx) .

Operators allow you to transform, combine, manipulate, and work with the sequences of items emitted by Observables.

My problem
I like mvc and observable pattern. But it hard to use it in defold, because when i worked with go or gui, i need to worked inside it context. I can’t subscribe in gui to model,and change gui when model change event emmited(yes i can, save events and then iterate them on_update, i have lib for that, but it is not comfortable.
I can use messages for it, but messages worked only with go/gui, not with lua modules(i can make module that will be emmit messages for modules, but it is not very comfortable).Also working with messages not very comfortable =)

RXLua
I add 2 methods to RxLua lib.
go and go_distinct. They worked in context of go.
go emmits all events
go_distinct only unique per frame.

Go is very simple. It emmits events when scheduler will be updated.I update it every frame in context of go.

function  Observable:go(scheduler)
    return self:delay(0,scheduler)
end

Rx Example
Model(emmit 5 equals events every frame)

function M:initialize()
	self.rx = RX.Subject()
end

local frame = 0
function M:update(dt, no_save)
	self.ecs_world:update(dt)
	for i=1,5 do
		self.rx:onNext(frame)
	end
	frame = frame + 1
end

Subscription go script

...
    WORLD.rx:go(SCHEDULER):subscribe(function(v)
        print("all:" .. v)
    end)
    WORLD.rx:go_distinct(SCHEDULER):subscribe(function(v)
        print("distinct:" .. v)
    end)
...

function Scene:update(go_self, dt)
    SCHEDULER:update(dt)
end
DEBUG:SCRIPT: [INFO 11:01:02] none: /scenes/logo/logo_scene.lua:21: distinct:45
[5]DEBUG:SCRIPT: [INFO 11:01:02] none: /scenes/logo/logo_scene.lua:18: all:45

I don’t show here power of Rx. When you can:receive value, change it, mix it with other and etc. It contains a lot of methods, you can read it in docs https://github.com/bjornbytes/RxLua/tree/master/doc.

When you read it, you can think that rx complex and redundant. But give it a chance.It is simple but powerful tool. I love it = )

Project
It contains a lot of code, because i use my template for all project. You need
libs/rx
world/world.lua (model of world)
init_controller(update world model)
scenes/logo/logo_scene.lua.(scene controller.Subscribe to world)

11 Likes

Hi @d954mas nice post, I would like to pass functions inside on_message Defold functions, but it’s not possible, so I search more about it and find RXlua, but not clear how to add inside Defold. I try the dependencies link and fetch but not load, I’m new in Defold still not sure how to make it work.

1 Like

Hi, it is not defold library. It is lua library, you need copy lua file.

Not sure how it help you to pass functions, but you can try.

Also look at flow https://github.com/britzl/ludobits
Maybe it will be better work for you

1 Like

Hi @d954mas I try the"ludobits.m.broadcast" but dont work like I spected
I atach the script_1 at 3 gameobjects.

--script_1
local broadcast = require "ludobits.m.broadcast"

function init(self)
	broadcast.register("foo")
end

function final(self)
	broadcast.unregister("foo")
end

function on_message(self, message_id, message, sender)
	if message_id == hash("foo") then
		pprint(message)
	end
end

and script_2 to one game object.

--script_2
local broadcast = require "ludobits.m.broadcast"

function init(self)
	broadcast.send("foo", { something = 123 })
end

I subscribe to the message “foo” in all 3 game objects, when send the message the code execute one time, instead of 3 times. Did you know why don’t execute the same message 3 times?

The order in which init() is called is not guaranteed. In your case the init() of script_1 has probably been run on 1 game object. Then you hit the init() of script_2 where you broadcast a message. And this will happen immediately, before the init() of the other two game objects using script_1.

If you really want to broadcast something in init() you need to delay it. You can use a timer to delay it:

function init(self)
	-- delay until immediately after all init() functions have been run
	timer.delay(0, false, function()
		broadcast.register("foo")
	end)
end
1 Like