Million-Tile Engine; any interest? Feature requests?

As for earlier paid beta access, I’d open that up if sellbox had a built-in system for dispensing updates to previous buyers. They say one is in the works for this month. I guess the price will be… I don’t know. Less than 20 dollars, I’m thinking.

I may also put the original, horrendously convoluted, extraordinarily optimized, ginormous Corona SDK Engine seen in the videos in the OP up for something like 80 bucks for anyone with the courage and fortitude to try to tackle it.

That’ll be something for another day, though. Immediately in the future are more improvements and fixes for MTE, and then another unrelated engine project afterwards, continuing the mission of making Corona do what it can’t. [import]uid: 99903 topic_id: 32457 reply_id: 142205[/import]

Thanks, sounds like I’ll be able to do what I want which is the sandbox scenario. Yeah, I’m waiting for Sellbox to add that feature too - however you do get the e-mail address of every customer so you could distribute updates that way. [import]uid: 93133 topic_id: 32457 reply_id: 142232[/import]

If it’s of any use, we use Sellfy to sell our GG libs and it allows us to send out emails/updates/newsletters to all customers etc. There’s also Gumroad and Merchee however I haven’t tried those yet. [import]uid: 119420 topic_id: 32457 reply_id: 142241[/import]

Thanks for the tip. I’ll look into those as well. [import]uid: 99903 topic_id: 32457 reply_id: 142254[/import]

Hi Dyson,

I am also interested in your work. I’m in a very early state of develoing a 2D puzzle game with maps which can be created via Tiled or (alter hopefully) with an ingame editor.

I hope to see your work soo puchasable.

Imho, I think $10 is to cheap, because I also expect to have a little support on code questions when puchasing an product. No idea how much support I can get for $10? [import]uid: 200446 topic_id: 32457 reply_id: 142282[/import]

Looks cool!

C [import]uid: 147322 topic_id: 32457 reply_id: 142260[/import]

Sorry for the super ginormous delay in replying, StarCrunch and others! richard9 did not leave me wanting for suggestions and bugs to fix and features to consider. The pace of things on my end has been hectic.

I appreciated the offers to beta test from the rest of you, but I’m not in need of any more beta testers. Keeping up with just one has been hard enough! “Soon,” I will make the engine available in a beta-ish state for $19.99, and then push the price up to $29.99 after the remaining bugs and annoyances you find are taken care of. I know I’ve said like five different prices in the last five weeks, but $29.99 feels just about right, so I don’t plan on changing it again. “Soon” is probably on the order of a few weeks.

StarCrunch;
The Million Tile Engine does not dispatch any events. The culling system works transparently, and a user must call an engine function to paint/erase a tile manually, so there shouldn’t be any surprises as to when something happens. I think I’m going to need that elaboration to see where your question’s coming from. I’m always on the lookout for potential engine improvements.

[import]uid: 99903 topic_id: 32457 reply_id: 143488[/import]

“a user must call an engine function to paint/erase a tile manually”

This may answer my question, actually. I assumed there would be a lot of magic going on, say you lay down a few key listeners, install a display group, and let it go to town. In hindsight, that of course kills a lot of generality you’d be after.

Given that, the following may be superfluous… here goes! Actually, I’ll try to just frame a couple questions I have.

First off, what I use at the moment is fairly basic and thus far I only deal in (and think in terms of) static, medium-sized, room-ish maps. It’s the follow-up project(s) where I have something broader in mind… “soon” (well, I hope).[1]

In my current case, I maintain several “views”: tiles themselves; “objects” that occupy a cell; event regions, which are really just coherent, multi-tile objects. I haven’t yet seen the MTE docs (got discouraged when my attempt to open the Skydrive files ended spectacularly, with a reboot :stuck_out_tongue: ), but it looks like these basically amount to big bags of properties.

Each view is a separate “map”, and if a certain option is checked, you can show the other maps, albeit faded out, from a given view (so you know, say, what tile you’re putting your object on). Is that supported? (Even something brute force is fine, in my case, as all level construction is offline.)[2]

The paint / erase question has to do with bookkeeping I need for some property-linking features (to avoid manually hooking things up via names / ID’s), mostly “did one of the linked things get erased or overwritten?”. I’m not sure if that’s still germane. I’ll get back to you after I see some docs, if so.

Anyhow, that’s what I can think of, off hand.

[1] - For reference, and in case there’s anything worth stealing, “what I use” is found here, in all its undocumented and unaesthetic glory: https://github.com/ggcrunchy/corona-sdk-snippets/archive/master.zip, mostly in the editor directory, scenes in scene/EditorSetup , scene/MapEditor , and overlay/Link , plus some specializations in OnEditorEvent() functions throughout the code. In the app itself, it’s the “Editor” snippet, bottom of the list.

I ended up needing more than the initial design allowed, so… it could be better. Lots of shoehorning. I think I know what a version 2.0 would call for, but I don’t know that it’s worth salvaging (obviously I’m looking into “the competition”…).

[2] - Actually, maybe the “levels” stuff could be hijacked for this purpose? I’ll take a look at the docs later. On a machine that doesn’t take ages to boot back up, just in case. :stuck_out_tongue: [import]uid: 27791 topic_id: 32457 reply_id: 143523[/import]

Hi, Dyson.

I’ll echo the other interested parties on testing, though I don’t know if I’d get around to doing anything useful.

Out of curiosity, does the editor dispatch any events when you paint / erase / etc. a tile? I can elaborate a little on where this question is coming from (heading out the door…), though the gist is “how would I migrate my workflow over from using my own (lame) editor?” [import]uid: 27791 topic_id: 32457 reply_id: 142818[/import]

I’ll have to look for things to steal some other time when I’m not so swamped. :stuck_out_tongue: Thanks for the link, though. Sorry about SkyDrive! I’ll have to find a better less crashy place to host the docs. The docs themselves are now outdated. Updating them is on my to-do-list.

The main purpose of Million-Tile Engine is to give developers the freedom to make maps as large or small as they want and still have them run smoothly. The smallest possible size is… well, I haven’t tested with a 1x1 map yet. The largest is constrained only by device memory. For example, the literal million-tile 1000x1000 maps I was working with before starting on this sellable engine tended to be about 30MB by themselves, for a single layer. Contrast this to other solutions which bog down as you go past a few thousand tiles.

Each layer you create in Tiled gets its own Display Group in MTE. You can retrieve a reference to this display group by calling getLayerObj(index), where index is the numerical index of the layer counting up from the lowest. You can then change whether the group is visible or not using all the usual properties, like .isVisible = false or .alpha = 0.2.

As for creating the contents of the views you mention, you’re right that this would mostly boil down to piles of properties.

I’m not sure I understand your property-linking question, but methods are available for retrieving and setting properties. Storing tile properties at every location on the map would drastically increase memory usage, but I could code the engine to store the tile properties of an onscreen tile in the displayObject holding that tile if it would be helpful. This way you could just getTileObj() and then read the properties table attached to it.
[import]uid: 99903 topic_id: 32457 reply_id: 143665[/import]

Sorry for the super ginormous delay in replying, StarCrunch and others! richard9 did not leave me wanting for suggestions and bugs to fix and features to consider. The pace of things on my end has been hectic.

I appreciated the offers to beta test from the rest of you, but I’m not in need of any more beta testers. Keeping up with just one has been hard enough! “Soon,” I will make the engine available in a beta-ish state for $19.99, and then push the price up to $29.99 after the remaining bugs and annoyances you find are taken care of. I know I’ve said like five different prices in the last five weeks, but $29.99 feels just about right, so I don’t plan on changing it again. “Soon” is probably on the order of a few weeks.

StarCrunch;
The Million Tile Engine does not dispatch any events. The culling system works transparently, and a user must call an engine function to paint/erase a tile manually, so there shouldn’t be any surprises as to when something happens. I think I’m going to need that elaboration to see where your question’s coming from. I’m always on the lookout for potential engine improvements.

[import]uid: 99903 topic_id: 32457 reply_id: 143488[/import]

“a user must call an engine function to paint/erase a tile manually”

This may answer my question, actually. I assumed there would be a lot of magic going on, say you lay down a few key listeners, install a display group, and let it go to town. In hindsight, that of course kills a lot of generality you’d be after.

Given that, the following may be superfluous… here goes! Actually, I’ll try to just frame a couple questions I have.

First off, what I use at the moment is fairly basic and thus far I only deal in (and think in terms of) static, medium-sized, room-ish maps. It’s the follow-up project(s) where I have something broader in mind… “soon” (well, I hope).[1]

In my current case, I maintain several “views”: tiles themselves; “objects” that occupy a cell; event regions, which are really just coherent, multi-tile objects. I haven’t yet seen the MTE docs (got discouraged when my attempt to open the Skydrive files ended spectacularly, with a reboot :stuck_out_tongue: ), but it looks like these basically amount to big bags of properties.

Each view is a separate “map”, and if a certain option is checked, you can show the other maps, albeit faded out, from a given view (so you know, say, what tile you’re putting your object on). Is that supported? (Even something brute force is fine, in my case, as all level construction is offline.)[2]

The paint / erase question has to do with bookkeeping I need for some property-linking features (to avoid manually hooking things up via names / ID’s), mostly “did one of the linked things get erased or overwritten?”. I’m not sure if that’s still germane. I’ll get back to you after I see some docs, if so.

Anyhow, that’s what I can think of, off hand.

[1] - For reference, and in case there’s anything worth stealing, “what I use” is found here, in all its undocumented and unaesthetic glory: https://github.com/ggcrunchy/corona-sdk-snippets/archive/master.zip, mostly in the editor directory, scenes in scene/EditorSetup , scene/MapEditor , and overlay/Link , plus some specializations in OnEditorEvent() functions throughout the code. In the app itself, it’s the “Editor” snippet, bottom of the list.

I ended up needing more than the initial design allowed, so… it could be better. Lots of shoehorning. I think I know what a version 2.0 would call for, but I don’t know that it’s worth salvaging (obviously I’m looking into “the competition”…).

[2] - Actually, maybe the “levels” stuff could be hijacked for this purpose? I’ll take a look at the docs later. On a machine that doesn’t take ages to boot back up, just in case. :stuck_out_tongue: [import]uid: 27791 topic_id: 32457 reply_id: 143523[/import]

I’ll have to look for things to steal some other time when I’m not so swamped. :stuck_out_tongue: Thanks for the link, though. Sorry about SkyDrive! I’ll have to find a better less crashy place to host the docs. The docs themselves are now outdated. Updating them is on my to-do-list.

The main purpose of Million-Tile Engine is to give developers the freedom to make maps as large or small as they want and still have them run smoothly. The smallest possible size is… well, I haven’t tested with a 1x1 map yet. The largest is constrained only by device memory. For example, the literal million-tile 1000x1000 maps I was working with before starting on this sellable engine tended to be about 30MB by themselves, for a single layer. Contrast this to other solutions which bog down as you go past a few thousand tiles.

Each layer you create in Tiled gets its own Display Group in MTE. You can retrieve a reference to this display group by calling getLayerObj(index), where index is the numerical index of the layer counting up from the lowest. You can then change whether the group is visible or not using all the usual properties, like .isVisible = false or .alpha = 0.2.

As for creating the contents of the views you mention, you’re right that this would mostly boil down to piles of properties.

I’m not sure I understand your property-linking question, but methods are available for retrieving and setting properties. Storing tile properties at every location on the map would drastically increase memory usage, but I could code the engine to store the tile properties of an onscreen tile in the displayObject holding that tile if it would be helpful. This way you could just getTileObj() and then read the properties table attached to it.
[import]uid: 99903 topic_id: 32457 reply_id: 143665[/import]

What do 200 lines of code get you? Collision detection, managed movement with available easing functions, objects, pusher plates, advanced perspective rendering, animated tiles. The Million Tile Engine is not merely a fast culling solution. It brings powerful integrated features driven by simple function calls to bear.

Image Hosted by ImageShack.us

Image Hosted by ImageShack.us

Image Hosted by ImageShack.us

Image Hosted by ImageShack.us

Image Hosted by ImageShack.us

Image Hosted by ImageShack.us

–VIDEO–
http://www.youtube.com/watch?v=0ILi0haOYco

Want to move your player 3 pixels across the screen? That’s easy enough. How about 300 pixels over 1000ms using inOutQuad easing? A little harder, but easily doable. What if the layer is scaled by 1.2? Suddenly a pixel in your world is 1.2 pixels on the screen. Everything changes. Unless you’re using MTE.

mte.moveSprite(player, 3, 0)
Move the sprite 3 pixels to the right in relation to the world… whether those pixels match screen pixels 1 to 1, or 1 to 1.2, or 1 to 6. Three pixels in relation to the map, not the screen. Simple.

mte.moseSpriteTo{sprite = player, locX = player.locX + 1, locY = player.locY, time = 300, easing = “inOutQuad”}
You see where this is going. This single line will move the player 1 tile to the right, over the course of 300ms, using inOutQuad easing. And it is aware of the layer scale. All the headaches are taken care of (I use a lot of aspirin). Simple, powerful, and so very useful.

Drawing tiles is half the battle. Moving is the other half. Neither need concern you.

You may have noticed something nifty about the video, like how lower levels LOOK farther away, and higher levels LOOK closer. A cool 3D perspective effect. This required 0 lines of code, and it was all controlled using just one layer property per layer: Scale.

The map has multiple “floors.” Some call this a “sandwich.” This advanced functionality comes from one layer property: Level.

This map makes use of four separate tilesets. The Million Tile Engine will allow you to use as many different tilesets as your heart desires, on as many layers as your heart desires, so long as the tiles they contain are square. The different tilesets don’t even have to be the same number of tiles in width and height.

MTE contains thousands of lines of code.

And here’s the code on the user side c:

-- MTE "CASTLE DEMO" ----------------------------------------------------------  
display.setStatusBar( display.HiddenStatusBar )  
local mte = require "MTE.mte"  
  
--LOAD MAP----------------------------------------  
mte.loadMap("map/CastleDemo6")  
mte.goto({ locX = 53, locY = 43, blockScale = 72})  
  
--SETUP D-PAD-------------------------------------  
local controlGroup = display.newGroup()  
local DpadBack = display.newImageRect(controlGroup, "Dpad.png", 200, 200)  
DpadBack.x = 120  
DpadBack.y = display.viewableContentHeight - 120  
local DpadUp = display.newRect(controlGroup, DpadBack.x - 37, DpadBack.y - 100, 75, 75)  
local DpadDown = display.newRect(controlGroup, DpadBack.x - 37, DpadBack.y + 25, 75, 75)  
local DpadLeft = display.newRect(controlGroup, DpadBack.x - 100, DpadBack.y - 37, 75, 75)  
local DpadRight = display.newRect(controlGroup, DpadBack.x + 25, DpadBack.y - 37, 75, 75)  
DpadBack:toFront()  
  
--HIDE LEVELS-------------------------------------  
local layerObjects = mte.getLayerObj()  
local layers = mte.getLayers()  
for i = 1, #layers, 1 do  
 if layers[i].properties.Level \> 2 then  
 layerObjects[i].isVisible = false  
 end  
end  
  
--PLAY MUSIC--------------------------------------  
local bgm = audio.loadStream("Chardok.mp3")  
audio.play(bgm, {loops = 0, fadein = 2000})  
  
--CREATE PLAYER SPRITE----------------------------  
local spriteSheet = graphics.newImageSheet("spriteSheetTall.png", {width = 32, height = 64, numFrames = 96})  
local sequenceData = {  
 {name = "up", sheet = spriteSheet, frames = {85, 86}, time = 400, loopCount = 0},  
 {name = "down", sheet = spriteSheet, frames = {49, 50}, time = 400, loopCount = 0},  
 {name = "left", sheet = spriteSheet, frames = {61, 62}, time = 400, loopCount = 0},  
 {name = "right", sheet = spriteSheet, frames = {73, 74}, time = 400, loopCount = 0}  
 }  
local player = display.newSprite(spriteSheet, sequenceData)  
local setup = {  
 kind = "sprite",   
 layer = mte.getSpriteLayer(1),   
 locX = 53,   
 locY = 43,  
 levelWidth = 40,  
 levelHeight = 40,  
 }  
mte.addSprite(player, setup)  
mte.setCameraFocus(player)  
  
--DETECT MOVEMENT---------------------------------  
DpadUp.id = "up"  
DpadDown.id = "down"  
DpadLeft.id = "left"  
DpadRight.id = "right"  
local movement = nil  
  
local function move( event )  
 if event.phase == "ended" or event.phase == "cancelled" then  
 movement = nil  
 elseif event.target.id then  
 movement = event.target.id  
 end  
 return true  
end  
  
--DETECT OBSTACLES--------------------------------  
local obstacle = function(level, locX, locY)  
 local detect = mte.getTileProperties({level = level, locX = locX, locY = locY})  
 for i = 1, #detect, 1 do  
 if detect[i].properties then  
 if detect[i].properties.Solid then  
 detect = "stop"  
 player:pause()  
 return detect  
 end  
 end  
 end  
end  
  
local counter = 0  
local toggle = 1  
local moveTime = 260  
  
local atlas = {}  
atlas["left"] = { -1, 0 }  
atlas["right"] = { 1, 0 }  
atlas["up"] = { 0, -1 }  
atlas["down"] = { 0, 1 }  
local function gameLoop( event )  
 if not player.isMoving then  
  
 ---------------------  
 --CHECK FOR OBJECTS--  
 local objects = mte.getObjectProperties({level = player.level, locX = player.locX, locY = player.locY})  
 if objects then  
 for key,value in pairs(objects[1].properties) do  
 if key == "change level" then  
 mte.changeSpriteLayer(player, mte.getSpriteLayer(tonumber(value)))  
 end  
 if key == "show level" then  
 local layerObjects = mte.getLayerObj()  
 local layers = mte.getLayers()  
 if value == "above" then  
 for i = 1, #layers, 1 do  
 if layers[i].properties.Level \> player.level then  
 layerObjects[i].isVisible = true  
 end  
 end  
 elseif value == "below" then  
 for i = 1, #layers, 1 do  
 if layers[i].properties.Level \< player.level then  
 layerObjects[i].isVisible = true  
 end  
 end  
 else  
 for i = 1, #layers, 1 do  
 if layers[i].properties.Level == tonumber(value) then  
 layerObjects[i].isVisible = true  
 end  
 end  
 end  
 end  
 if key == "hide level" then  
 local layerObjects = mte.getLayerObj()  
 local layers = mte.getLayers()  
 if value == "above" then  
 for i = 1, #layers, 1 do  
 if layers[i].properties.Level \> player.level then  
 layerObjects[i].isVisible = false  
 end  
 end  
 elseif value == "below" then  
 for i = 1, #layers, 1 do  
 if layers[i].properties.Level \< player.level then  
 layerObjects[i].isVisible = false  
 end  
 end  
 else  
 for i = 1, #layers, 1 do  
 if layers[i].properties.Level == tonumber(value) then  
 layerObjects[i].isVisible = false  
 end  
 end  
 end  
 end  
 local locX, locY, time = player.locX, player.locY, 300  
 if key == "move to locX" then  
 if value == "random" then  
 locX = player.locX + math.random(1, 3) - 2  
 else  
 locX = tonumber(value)  
 end  
 end  
 if key == "move to locY" then  
 if value == "random" then  
 locY = player.locY + math.random(1, 3) - 2  
 else  
 locY = tonumber(value)  
 end  
 end  
 if math.abs(locX - player.locX) \> 3 or math.abs(locY - player.locY) \> 3 then  
 time = 600  
 end  
 if locX ~= player.locX or locY ~= player.locY then  
 mte.moveSpriteTo({sprite = player, locX = locX, locY = locY, time = time, easing = "inOutQuad"})  
 end  
 end  
 end  
  
 -------------------------  
 --MOVE PLAYER CHARACTER--  
 if movement then  
 local xTile, yTile = player.locX + atlas[movement][1], player.locY + atlas[movement][2]  
 local result = obstacle( player.level, xTile, yTile )  
 if not result then  
 if player.sequence ~= movement then  
 player:setSequence( movement )  
 end  
 player:play()  
 mte.moveSpriteTo( { sprite = player, locX = xTile, locY = yTile, time = moveTime, easing = "linear" } )  
 end  
 else  
 player:pause()  
 end  
 end  
  
 mte.debug()  
 mte.update()  
end  
  
DpadUp:addEventListener("touch", move)  
DpadDown:addEventListener("touch", move)  
DpadLeft:addEventListener("touch", move)  
DpadRight:addEventListener("touch", move)  
  
Runtime:addEventListener("enterFrame", gameLoop)  

Oh, and I’ve altered my release timeline. :wink: [import]uid: 99903 topic_id: 32457 reply_id: 144604[/import]

Nice video. :slight_smile:

In case it means anything to you, I got a definite Illusion of Gaia vibe from it (Inca Ruins at first, then a few of the later areas too). This was actually true from some of your earlier (related?) screenshots too.

Minor non-MTE nitpicks: the size switch at the stairs and coming out from “under” a layer are kind of sudden, but that’s just minor aesthetics.

I did get around to looking at the docs, and what I had in mind should just be doing some pre- and post-processing on my end, before saving and after loading respectively, manually messing with a few properties.

Anyhow, by linking I just mean certain objects reserve source / target slots and then there’s a fancy visual mechanism to connect them (a lot of shader and state machine editors have something along these lines), mostly drag-and-drop. I have that (warp-to-warp, switch-to-event, etc.), just needed to tease out an idea of how everything might fit together. (It is something of a curse, how often I come up with the obvious answer while trying to elaborate. :P) [import]uid: 27791 topic_id: 32457 reply_id: 144745[/import]

The great thing about people coming up with answers while elaborating on their questions is that I don’t have to come up with the answers! Actually I know what you mean, sometimes the answers come while asking the question a few different ways.

I looked up Illusions of Gaia. I’ve never played it myself, but the thing I took away from watching reviews was “good RPG” and “MODE 7 EVERYWHERE.” I do have in mind a way to tilt map view forward and backward or side to side in a mode-7-like fashion, but that isn’t something I’m going to look at in the immediate future.

I continue to work on documentation and other clerical stuff. In the meantime, here’s the same Castle Demo from the perspective of a crazy person. I hope you don’t suffer from vertigo.
http://www.youtube.com/watch?v=7u0Z298yGMY

[import]uid: 99903 topic_id: 32457 reply_id: 144986[/import]

I’m excited about this! I would definitely pay for a fast tilemap system. As much as I like Tiled it doesn’t do well with very large tile maps and it looks like your library handles it well.

I hope this will be available for sale soon :slight_smile: [import]uid: 45650 topic_id: 32457 reply_id: 145087[/import]

I just stumbled upon this thread today and I’m really impressed in what you’ve done. I would love to get my hands on this engine!

Our latest game, House of the Lost, is getting rough reviews on the App store because of it’s poor performance on the iPhone 4, performance which I’ve tracked to Lime and can’t find a good way to fix.

Really awesome work, can’t wait to see more.

[import]uid: 11540 topic_id: 32457 reply_id: 145096[/import]

Hi, Dyson.

I’ll echo the other interested parties on testing, though I don’t know if I’d get around to doing anything useful.

Out of curiosity, does the editor dispatch any events when you paint / erase / etc. a tile? I can elaborate a little on where this question is coming from (heading out the door…), though the gist is “how would I migrate my workflow over from using my own (lame) editor?” [import]uid: 27791 topic_id: 32457 reply_id: 142818[/import]

What do 200 lines of code get you? Collision detection, managed movement with available easing functions, objects, pusher plates, advanced perspective rendering, animated tiles. The Million Tile Engine is not merely a fast culling solution. It brings powerful integrated features driven by simple function calls to bear.

Image Hosted by ImageShack.us

Image Hosted by ImageShack.us

Image Hosted by ImageShack.us

Image Hosted by ImageShack.us

Image Hosted by ImageShack.us

Image Hosted by ImageShack.us

–VIDEO–
http://www.youtube.com/watch?v=0ILi0haOYco

Want to move your player 3 pixels across the screen? That’s easy enough. How about 300 pixels over 1000ms using inOutQuad easing? A little harder, but easily doable. What if the layer is scaled by 1.2? Suddenly a pixel in your world is 1.2 pixels on the screen. Everything changes. Unless you’re using MTE.

mte.moveSprite(player, 3, 0)
Move the sprite 3 pixels to the right in relation to the world… whether those pixels match screen pixels 1 to 1, or 1 to 1.2, or 1 to 6. Three pixels in relation to the map, not the screen. Simple.

mte.moseSpriteTo{sprite = player, locX = player.locX + 1, locY = player.locY, time = 300, easing = “inOutQuad”}
You see where this is going. This single line will move the player 1 tile to the right, over the course of 300ms, using inOutQuad easing. And it is aware of the layer scale. All the headaches are taken care of (I use a lot of aspirin). Simple, powerful, and so very useful.

Drawing tiles is half the battle. Moving is the other half. Neither need concern you.

You may have noticed something nifty about the video, like how lower levels LOOK farther away, and higher levels LOOK closer. A cool 3D perspective effect. This required 0 lines of code, and it was all controlled using just one layer property per layer: Scale.

The map has multiple “floors.” Some call this a “sandwich.” This advanced functionality comes from one layer property: Level.

This map makes use of four separate tilesets. The Million Tile Engine will allow you to use as many different tilesets as your heart desires, on as many layers as your heart desires, so long as the tiles they contain are square. The different tilesets don’t even have to be the same number of tiles in width and height.

MTE contains thousands of lines of code.

And here’s the code on the user side c:

-- MTE "CASTLE DEMO" ----------------------------------------------------------  
display.setStatusBar( display.HiddenStatusBar )  
local mte = require "MTE.mte"  
  
--LOAD MAP----------------------------------------  
mte.loadMap("map/CastleDemo6")  
mte.goto({ locX = 53, locY = 43, blockScale = 72})  
  
--SETUP D-PAD-------------------------------------  
local controlGroup = display.newGroup()  
local DpadBack = display.newImageRect(controlGroup, "Dpad.png", 200, 200)  
DpadBack.x = 120  
DpadBack.y = display.viewableContentHeight - 120  
local DpadUp = display.newRect(controlGroup, DpadBack.x - 37, DpadBack.y - 100, 75, 75)  
local DpadDown = display.newRect(controlGroup, DpadBack.x - 37, DpadBack.y + 25, 75, 75)  
local DpadLeft = display.newRect(controlGroup, DpadBack.x - 100, DpadBack.y - 37, 75, 75)  
local DpadRight = display.newRect(controlGroup, DpadBack.x + 25, DpadBack.y - 37, 75, 75)  
DpadBack:toFront()  
  
--HIDE LEVELS-------------------------------------  
local layerObjects = mte.getLayerObj()  
local layers = mte.getLayers()  
for i = 1, #layers, 1 do  
 if layers[i].properties.Level \> 2 then  
 layerObjects[i].isVisible = false  
 end  
end  
  
--PLAY MUSIC--------------------------------------  
local bgm = audio.loadStream("Chardok.mp3")  
audio.play(bgm, {loops = 0, fadein = 2000})  
  
--CREATE PLAYER SPRITE----------------------------  
local spriteSheet = graphics.newImageSheet("spriteSheetTall.png", {width = 32, height = 64, numFrames = 96})  
local sequenceData = {  
 {name = "up", sheet = spriteSheet, frames = {85, 86}, time = 400, loopCount = 0},  
 {name = "down", sheet = spriteSheet, frames = {49, 50}, time = 400, loopCount = 0},  
 {name = "left", sheet = spriteSheet, frames = {61, 62}, time = 400, loopCount = 0},  
 {name = "right", sheet = spriteSheet, frames = {73, 74}, time = 400, loopCount = 0}  
 }  
local player = display.newSprite(spriteSheet, sequenceData)  
local setup = {  
 kind = "sprite",   
 layer = mte.getSpriteLayer(1),   
 locX = 53,   
 locY = 43,  
 levelWidth = 40,  
 levelHeight = 40,  
 }  
mte.addSprite(player, setup)  
mte.setCameraFocus(player)  
  
--DETECT MOVEMENT---------------------------------  
DpadUp.id = "up"  
DpadDown.id = "down"  
DpadLeft.id = "left"  
DpadRight.id = "right"  
local movement = nil  
  
local function move( event )  
 if event.phase == "ended" or event.phase == "cancelled" then  
 movement = nil  
 elseif event.target.id then  
 movement = event.target.id  
 end  
 return true  
end  
  
--DETECT OBSTACLES--------------------------------  
local obstacle = function(level, locX, locY)  
 local detect = mte.getTileProperties({level = level, locX = locX, locY = locY})  
 for i = 1, #detect, 1 do  
 if detect[i].properties then  
 if detect[i].properties.Solid then  
 detect = "stop"  
 player:pause()  
 return detect  
 end  
 end  
 end  
end  
  
local counter = 0  
local toggle = 1  
local moveTime = 260  
  
local atlas = {}  
atlas["left"] = { -1, 0 }  
atlas["right"] = { 1, 0 }  
atlas["up"] = { 0, -1 }  
atlas["down"] = { 0, 1 }  
local function gameLoop( event )  
 if not player.isMoving then  
  
 ---------------------  
 --CHECK FOR OBJECTS--  
 local objects = mte.getObjectProperties({level = player.level, locX = player.locX, locY = player.locY})  
 if objects then  
 for key,value in pairs(objects[1].properties) do  
 if key == "change level" then  
 mte.changeSpriteLayer(player, mte.getSpriteLayer(tonumber(value)))  
 end  
 if key == "show level" then  
 local layerObjects = mte.getLayerObj()  
 local layers = mte.getLayers()  
 if value == "above" then  
 for i = 1, #layers, 1 do  
 if layers[i].properties.Level \> player.level then  
 layerObjects[i].isVisible = true  
 end  
 end  
 elseif value == "below" then  
 for i = 1, #layers, 1 do  
 if layers[i].properties.Level \< player.level then  
 layerObjects[i].isVisible = true  
 end  
 end  
 else  
 for i = 1, #layers, 1 do  
 if layers[i].properties.Level == tonumber(value) then  
 layerObjects[i].isVisible = true  
 end  
 end  
 end  
 end  
 if key == "hide level" then  
 local layerObjects = mte.getLayerObj()  
 local layers = mte.getLayers()  
 if value == "above" then  
 for i = 1, #layers, 1 do  
 if layers[i].properties.Level \> player.level then  
 layerObjects[i].isVisible = false  
 end  
 end  
 elseif value == "below" then  
 for i = 1, #layers, 1 do  
 if layers[i].properties.Level \< player.level then  
 layerObjects[i].isVisible = false  
 end  
 end  
 else  
 for i = 1, #layers, 1 do  
 if layers[i].properties.Level == tonumber(value) then  
 layerObjects[i].isVisible = false  
 end  
 end  
 end  
 end  
 local locX, locY, time = player.locX, player.locY, 300  
 if key == "move to locX" then  
 if value == "random" then  
 locX = player.locX + math.random(1, 3) - 2  
 else  
 locX = tonumber(value)  
 end  
 end  
 if key == "move to locY" then  
 if value == "random" then  
 locY = player.locY + math.random(1, 3) - 2  
 else  
 locY = tonumber(value)  
 end  
 end  
 if math.abs(locX - player.locX) \> 3 or math.abs(locY - player.locY) \> 3 then  
 time = 600  
 end  
 if locX ~= player.locX or locY ~= player.locY then  
 mte.moveSpriteTo({sprite = player, locX = locX, locY = locY, time = time, easing = "inOutQuad"})  
 end  
 end  
 end  
  
 -------------------------  
 --MOVE PLAYER CHARACTER--  
 if movement then  
 local xTile, yTile = player.locX + atlas[movement][1], player.locY + atlas[movement][2]  
 local result = obstacle( player.level, xTile, yTile )  
 if not result then  
 if player.sequence ~= movement then  
 player:setSequence( movement )  
 end  
 player:play()  
 mte.moveSpriteTo( { sprite = player, locX = xTile, locY = yTile, time = moveTime, easing = "linear" } )  
 end  
 else  
 player:pause()  
 end  
 end  
  
 mte.debug()  
 mte.update()  
end  
  
DpadUp:addEventListener("touch", move)  
DpadDown:addEventListener("touch", move)  
DpadLeft:addEventListener("touch", move)  
DpadRight:addEventListener("touch", move)  
  
Runtime:addEventListener("enterFrame", gameLoop)  

Oh, and I’ve altered my release timeline. :wink: [import]uid: 99903 topic_id: 32457 reply_id: 144604[/import]