Quick Links: Download Gideros Studio | Gideros Documentation | Gideros community chat | DONATE
updating textfields dynamically? — Gideros Forum

updating textfields dynamically?

HolonistHolonist Member
edited January 2016 in General questions
Hi,

What do I want to achieve?
I want to create a Textfield T that listens to a certain variable X. Whenever X's value changes, the T should update its text to the value of X.

Example
Frame 1:
player.strength = 10 //had the same value in the last frame
// display.tfplayerstrength does nothing

Frame 2:
player.strength = 11
// display.tfplayerstrength detects there is a change in player.strength
display.tfplayerstrength:setText(player.strength)

I hope you get the idea.

What's the problem?
In some engines you have a game loop and a graphics loop - both are executed each frame. In the game loop X gets updated, and in the graphics loop T just displays X's value anyway. Sounds bad, because this implies both loops update everything all the time. Contrary to expectation this can be really fast.

Now when you try to do it this way in Gideros, you will have a bad time. Have about 30 or 50 textfields, all updating their text every frame, you get extreme performance problems. So you have to detect when a value has to be changed.

What do I want to avoid?
I do not want to write a controller class that does all the checks like:

did player.strength change? if so, update display.tfplayerstrength.
did player.vitality change? if so, update display.tfplayervitality.
did player.exp change? if so, update display.tfplayerxp.
...

I want a Textfield that's able to check for itself if a variable has changed.
Basically, what I need, is to pass a variable reference into an object.

I'm thinking along the lines of this:
BoundTextfield = Core.class(TextField)
 
function BoundTextfield:init(font, text, var)
 
	TextField.init(self, font, text)
	self.var = var
 
end
But the problem is, when I pass "var", I'm sure it passes the value, and not a reference to the variable itself.

Does anyone know how to achieve what I want?

Comments

  • piepie Member
    Accepted Answer
    If I recall correctly you can write all the variables that need update in a table, then you pass 2 parameters: table (which go by reference) and index.
    local valuestab = {health = 50, armor = 25... }
    Bt = Boundtextfield.new (valuestab, "health")
    Another option is to "inject" the reference to the table from outside of the constructor, but you have to "delay" the text on init because you miss the reference at first:
    Bt = Boundtextfield.new("health")
    Bt.reftab = valuestab
    I think I used the first way, I can check it as soon as I get to my laptop.
    :)

    Likes: Holonist

    +1 -1 (+1 / -0 )Share on Facebook
  • HolonistHolonist Member
    edited January 2016
    Super, with your tipps I got it to work.

    Messy code for examination:
    --main.lua
     
    player = {}
    player.vit = 10
    player.str = 5
     
    bt = BoundTextfield.new(nil, "", player, "vit")
    bt:setPosition(100,100)
    stage:addChild(bt)
     
     
     
    button = TextField.new(nil, "increase value")
    button:setScale(2)
    button:setPosition(100,140)
     
    stage:addChild(button)
     
    button:addEventListener(Event.MOUSE_UP, function()
    	player.vit = player.vit + 1
    	print(player.vit)
    end)
     
     
     
    --BoundTextfield.lua
     
    BoundTextfield = Core.class(TextField)
     
    function BoundTextfield:init(font, text, obj, attr)
     
    	self.refObj = obj
    	self.refAttr = attr
     
    	self.oldVal = self.refObj[self.refAttr]
     
    	self:setText(self.refObj[self.refAttr])
     
    	self:addEventListener(Event.ENTER_FRAME, self.onEnterFrame, self)
     
    end
     
    function BoundTextfield:onEnterFrame()
     
    	if( self.refObj[self.refAttr] ~= self.oldVal ) then
     
    		print("BoundTextfield updating") -- verify we don't execute this every frame
    		self:setText(self.refObj[self.refAttr])
    		self.oldVal = self.refObj[self.refAttr]
     
    	end
     
    end
    Boundtextfield is basically a Textfield, but with a stored table reference and a stored key for said table. Also it has an oldVal, that simply represents the last string that was used.

    in onEnterFrame(), the Textfield checks if the value refObj[refAttr] (for example player["vit"]) has changed, and then updates the text accordingly. Of course "oldVal" also has to be adjusted, or we end up updating the text every frame.
  • piepie Member
    @Holonist it should work even with local objects, unless you need to access them from "the outside" :)
    --BoundTextfield.lua
     
    BoundTextfield = Core.class(TextField)
     
    function BoundTextfield:init(font, text, obj, attr)
     
    	local refObj = obj
    	local refAttr = attr
     
    	local oldVal = refObj[refAttr]
     
    	self:setText(refObj[refAttr])
     
     local function onEnterFrame()
     
    	if( refObj[refAttr] ~= oldVal ) then
     
    		print("BoundTextfield updating") -- verify we don't execute this every frame
    		self:setText(refObj[refAttr])
    		oldVal = refObj[refAttr]
     
    	end
     
    end
     
    	self:addEventListener(Event.ENTER_FRAME, onEnterFrame, self)
     
    end
     
     
     
    --main.lua
     
    player = {}
    player.vit = 10
    player.str = 5
     
    bt = BoundTextfield.new(nil, "", player, "vit")
    bt:setPosition(100,100)
    stage:addChild(bt)
     
     
     
    button = TextField.new(nil, "increase value")
    button:setScale(2)
    button:setPosition(100,140)
     
    stage:addChild(button)
     
    button:addEventListener(Event.MOUSE_UP, function()
    	player.vit = player.vit + 1
    	print(player.vit)
    end)
    You could also check if it's better to use oldVal as you're doing right now or if it's faster to use
    local oldVal = tonumber(self:getText() )
    (I don't think so, I believe that value type conversion is a "slow" process, but maybe... :D )
  • HolonistHolonist Member
    edited January 2016

    You could also check if it's better to use oldVal as you're doing right now or if it's faster to use
    local oldVal = tonumber(self:getText() )
    (I don't think so, I believe that value type conversion is a "slow" process, but maybe... :D )
    Not a bad idea, storing oldVal in an extra variable also seemed redundant to me. Also I think the performance of type conversion is not an issue (with a reasonable amount of textfields.)

    But you may want to only display rounded numbers, while still keeping the original floating point number.
    Edit: Then again, this would mean the textfield would get updated when decimals change, but the display value would stay the same (kinda dumb). There's definitely optimization potential, but anyway, getting to this to work already solved a very big issue for me.
  • HolonistHolonist Member
    edited January 2016
    @pie

    just realized what you mean with local objects. Well what would that change?
    From my experience it's almost always better to use object-bound variables so you can access them later.
  • piepie Member
    edited January 2016
    @Holonist I am not sure that tonumber() is a light process, maybe it is if used once, but in this scenario it's called n times on every frame :)

    About the local things:
    http://lua-users.org/wiki/OptimisingUsingLocalVariables

    You won't notice performance improvements until you have "many" objects, but I can tell you that this works for real :D
    of course, the downside is that you lose access on local objects, so you can't always do everything local.

    Likes: Holonist

    +1 -1 (+1 / -0 )Share on Facebook
  • Just to note that updating textfields is not efficient, unless the characters are cached in TTfont
  • @Holonist, I'm not sure, check this link https://github.com/intoitgames/gideros-display may be contain useful approach for you :/
Sign In or Register to comment.