RG Tiled + Behaviors (Entity Component System)

Ugh… I just typed up a long post about this, and then promptly destroyed it by accident.

So, apologies if this is not super illuminating, but I just can’t bring myself to type it again. :frowning:

RGTiled

SSK2 include a "Tiled Level Loader and Drawing Library’ called RGTiled.  It is similar to many of the other loaders you can find (list below), but also includes these features:

  • Supports embedded and external tilesets.
  • Supports defining bodies using Tiled physics editor. (Default, Rectangle, Polygon.)
    • Supports multi-body too.
    • Circular bodies can not be edited, but can be defined from properties.
  • Can edit physics properties from Tiled
  • One-line Load
  • One-line Drawing
  • ForEach Iterator
  • User Defined Builders - You can provide custom code for just those objects that need more features than are available via the standard RGTiled standar features.
  • Behaviors - Attach code to objects by defining named properies w/ optional arguments to the behavior.
  • Stitchers - i.e. Stitch multiple levels together.

There is way too much to discuss here so I made a video and as time permits I’ll make more posts, demos etc.

https://www.youtube.com/watch?v=KYihtSvuH-s

Other Tiled Loaders++

Behaviors / Scripted Entity Component System

I added a new feature to SSK that allows you to pre-define arbitrary packages of code and to ‘late attach and configure’ them.

While I call these Behaviors, you may be more familiar with the term ‘Entity Component System’.

These packages let you give display objects arbitrary complex behaviors in just a few lines of code.

When time permits I will make some examples, videos, etc.  for I’ll just tease you with some random code.

local obj = display.newCircle( centerX, centerY, 40 ) -- Silly way to give the object an initial color, make it jiggle, take focus when you touch it, -- and change color when you touch and release it. ssk.behaviors.add( obj, "b\_fillColor", "color=FF33dd" ) ssk.behaviors.add( obj, "b\_jiggler", "x1 = -10, x2 = 10, y1 = 0, y2 = 0, period = 100" ) ssk.behaviors.add( obj, "b\_touch\_color", { began\_color = "#FF0000", end\_color = "#FFFFFF"} ) ssk.behaviors.add( obj, "b\_touch\_focus" )

The jiggler behavior:

-- ============================================================= -- Copyright Roaming Gamer, LLC. 2008-2018 (All Rights Reserved) -- ============================================================= local mRand = math.random local getTimer = system.getTimer -- local mod = {} function mod.new( settings ) local behavior = {} behavior.ll = {} -- local listeners obj:event() ) ==\> ex: obj:addEventListener( "collision" ) behavior.gl = {} -- global listeners Runtime:event() ==\> ex: Runtime:addEventListener( "enterFrame", obj ) -- --table.dump(settings) local x0 local y0 local lastT local x1 = settings.x1 or 0 local y1 = settings.y1 or 0 local x2 = settings.x2 or 0 local y2 = settings.y2 or 0 local period = settings.period or 0 -- function behavior.onCreate( obj ) lastT = getTimer() x0 = obj.x y0 = obj.y end -- function behavior.gl.enterFrame( self ) local curT = getTimer() if( curT - lastT \< period ) then return end lastT = curT self.x = x0 + mRand(x1, x2) self.y = y0 + mRand(y1, y2) end -- return behavior end return mod

The touch color behavior:

-- ============================================================= -- Copyright Roaming Gamer, LLC. 2008-2018 (All Rights Reserved) -- ============================================================= local mod = {} function mod.new( settings ) local behavior = {} behavior.ll = {} -- local listeners obj:event() ) ==\> ex: obj:addEventListener( "collision" ) behavior.gl = {} -- global listeners Runtime:event() ==\> ex: Runtime:addEventListener( "enterFrame", obj ) -- local beganColor = settings.began\_color or {1,0,0,1} local endedColor = settings.end\_color or {1,1,1,1} if( type(beganColor) == "string" ) then beganColor = hexcolor(beganColor) end if( type(endedColor) == "string" ) then endedColor = hexcolor(endedColor) end -- Ensure we have a four-value color coded for i = 1, 4 do beganColor[i] = (beganColor[i] ~= nil) and beganColor[i] or 1 endedColor[i] = (endedColor[i] ~= nil) and endedColor[i] or 1 end -- function behavior.ll.touch( self, event ) if( event.phase == "began" ) then self:setFillColor(unpack(beganColor)) elseif( event.phase == "ended" ) then self:setFillColor(unpack(endedColor)) end return true end -- return behavior end return mod

The ‘take focus on touch’ behavior:

-- ============================================================= -- Copyright Roaming Gamer, LLC. 2008-2018 (All Rights Reserved) -- ============================================================= local mod = {} function mod.new( settings ) local behavior = {} behavior.ll = {} -- local listeners obj:event() ) ==\> ex: obj:addEventListener( "collision" ) behavior.gl = {} -- global listeners Runtime:event() ==\> ex: Runtime:addEventListener( "enterFrame", obj ) -- function behavior.ll.touch( self, event ) if( event.phase == "began" ) then behavior.\_isFocus = true display.getCurrentStage():setFocus( self, event.id ) elseif( behavior.\_isFocus ) then if( event.phase == "ended" ) then timer.performWithDelay( 1, function() behavior.\_isFocus = false display.getCurrentStage():setFocus( self, nil ) end ) end end return true end -- return behavior end return mod

As an interesting side-effect/feature, if you use ‘Behaviors’ you can now add as many instances of the same listener type to an object as you want to an object.

i.e. If you have ever wished you could have two collision listeners for the same object, it is now possible.  In fact, this is the core enabling feature that allows the Behaviors/ECS code to function.

You can now quite literally do this:

local obj = display.newCircle( centerX, centerY, 40 ) local function collision1( self, event ) ... some code end local function collision2( self, event ) ... some code end ssk.behaviors.addEventListener( obj, 'collision', collision1 ) ssk.behaviors.addEventListener( obj, 'collision', collision2 )

Are you going to update the SSK2 docs about this feature being a work in progress? That might throw some people off.

I’m working on it, but I have limited time and need to earn $ first.  :frowning:

Just finished watching the video, great job with the loader! The behaviors are particularly interesting.

Also, a quick question, does RGTiled support collection of images as well as tilesets, or just tilesets?

PS: Good luck with your game development!

Sorry for the terminology confusion.  This is a problem in game design.  Loosey goosey usage of terms causing issues.

I believe, Tiled uses the term ‘tile set’ for both ‘collections’ (of discrete images) and ‘images in a sheet’

RGTiled only supports ‘collections’ of images.  i.e. Discrete images, not multiple images in a sheet.

Click ‘New Tileset’ button, then choose: ‘Collection of Images’.  Then choose either ‘embed in map’ true or false. Both are supported.

The other option ‘based on tileset image’… well I have no idea what it does.

The based on tileset image makes a non-embedded TSX file with the separated into tile sizes of your choosing.

Honestly, I’ve always found the collection of images to be better than the tileset. The problem is the separation of the tiles, they may not always come out evenly.

I still think there is some confusion here.

  1. I use discrete images ONLY.  I do not use a single sheet of images.

  2. I prefer the ‘not embedded’ option because it produces a *.tsx file that I can then copy (along with the images folder) into other projects.

I find the ‘emedded’ option to be terrible because it is completely non-portable.

I’ll make some videos on my process later when I get a chance and that will clarify all of this.

:slight_smile:

Cool, thanks!

As an interesting side-effect/feature, if you use ‘Behaviors’ you can now add as many instances of the same listener type to an object as you want to an object.

i.e. If you have ever wished you could have two collision listeners for the same object, it is now possible.  In fact, this is the core enabling feature that allows the Behaviors/ECS code to function.

You can now quite literally do this:

local obj = display.newCircle( centerX, centerY, 40 ) local function collision1( self, event ) ... some code end local function collision2( self, event ) ... some code end ssk.behaviors.addEventListener( obj, 'collision', collision1 ) ssk.behaviors.addEventListener( obj, 'collision', collision2 )

Are you going to update the SSK2 docs about this feature being a work in progress? That might throw some people off.

I’m working on it, but I have limited time and need to earn $ first.  :frowning:

Just finished watching the video, great job with the loader! The behaviors are particularly interesting.

Also, a quick question, does RGTiled support collection of images as well as tilesets, or just tilesets?

PS: Good luck with your game development!

Sorry for the terminology confusion.  This is a problem in game design.  Loosey goosey usage of terms causing issues.

I believe, Tiled uses the term ‘tile set’ for both ‘collections’ (of discrete images) and ‘images in a sheet’

RGTiled only supports ‘collections’ of images.  i.e. Discrete images, not multiple images in a sheet.

Click ‘New Tileset’ button, then choose: ‘Collection of Images’.  Then choose either ‘embed in map’ true or false. Both are supported.

The other option ‘based on tileset image’… well I have no idea what it does.

The based on tileset image makes a non-embedded TSX file with the separated into tile sizes of your choosing.

Honestly, I’ve always found the collection of images to be better than the tileset. The problem is the separation of the tiles, they may not always come out evenly.

I still think there is some confusion here.

  1. I use discrete images ONLY.  I do not use a single sheet of images.

  2. I prefer the ‘not embedded’ option because it produces a *.tsx file that I can then copy (along with the images folder) into other projects.

I find the ‘emedded’ option to be terrible because it is completely non-portable.

I’ll make some videos on my process later when I get a chance and that will clarify all of this.

:slight_smile:

Cool, thanks!