Million Tile Engine Beta Release

Hi Dyson,

What an update!! Many thanks for your work on this! The inclusion of touch scroll / pinch before the 1.0 release was a great surprise so many thanks for this! Storing the color value for tinting/lighting is also going to be a huge help to me and means I can now look at including lighting effects in the future. This is all great and many thanks again.

Color

Like John I noticed the use of graphics 1.0 colour values (0-255) rather than 2.0 (0-1) and agree consistency with 2.0 would be very helpful (even though I personally prefer the old 1.0 value system).

Touch

I also wondered: would it be possible to include some custom events in the touch scroll / pinch code? Something like…

Runtime:dispatchEvent( { name = "mteTouchBegan", data = {} } ) Runtime:dispatchEvent( { name = "mteTouchEnded", data = {} } )

… (where “data” is optional and whatever you choose to include). It’s just that in my current code I’ve added these events and it allows me to pause certain game elements and trigger others based on the fact the camera view is no longer focused on the player sprite. It would be really helpful if these were included (or something similiar) and I’m sure others would benefit too as at the moment our external code can’t detect when these important touch events have occurred.

Stitching

Finally, I’m looking forward to seeing the map stitching in action and wondered if you could answer a query. I’m considering a game where the map is randomly generated and the idea is to stitch together selected preset mini-maps (composed in Tiled) to dynamically create a unique larger game map and experience each time. I’m thinking each mini-map would be a grid of 9 tiles (3 x 3) and the final stitched map could be comprised of any number of these (let’s say 30 x 30 stitched mini-maps). Would something like this be possible with the new stitching algorithms? Do you envisage any performance problems in doing this? Could sprites regularly, quickly and efficiently move from one mini-map to another, with smooth movement transitions, given your implementation? This really would be fantastic if possible.

Many thanks again!

Hi Dyson,

I’ve just about got my head around storyboard scenes now but when I look over your example code with storyboard used your first scene is willEnterScene in the two examples (IsometricStoryboard and RotateConstrainStoryboard) rather than createScene.  I can’t find any references online to leaving out the createScene so I’m wondering why you do this and what the are benefits or otherwise?   It certainly doesn’t seem to cause any issues from what I’ve seen so far.

Thanks.

Hi Dyson,

another query I’m afraid.  I just decided to upgrade from  version 958 to 984.  In my code I’ve used the following for some of the touch control work  " scaleFactor =  mte.blockScaleX / mte.worldScaleX.    "

Re-running my code with 984 threw up errors as neither  mte.blockScaleX  nor  mte.worldScaleX  seem to exist any longer.   I’m guessing you’ll need to be doing something similar in your touch control implementation but I can’t be sure what the correct variables I can use from mte to replace these.

I will look at using your touch implementation, which is a great idea, at some time but at the moment I’ve got mine working exactly as I need so I’m not keen to change it at this stage.

As your code is now almost out of beta and you’re focusing on documentation more can I make a request that you issue some release notes showing what’s changed?  For instance, where functions or variables are renamed/ altered /deprecated in some way.  I had a similar issue recently when I discovered that mte.goto had changed name (I know for good reason) and it took some hunting to understand why it had gone and what the new function was called.

Thanks

A quick question,

Now that I’ve enableTouchScroll(), I can’t just touch a spot on the map and have my sprite move there, how do I differentiate between touch to move a sprite vs touch scrolling?

(I currently use Runtime:addEventListener(“touch”, mtemove) to call my moveto routine)

Thanks, Greg

The .color property is not a temporary fix, rather it is a workaround for the SDK’s lack of a getFillColor property. The engine has no way of looking up the fill color of an object. When the lighting system tints an object the original tint is lost forever unless it is stored somewhere by the user. The new .color property fills that need. Whenever lighting is in use the developer must use .color if he or she wishes to change the tint of an object. 

If I’m wrong about the lack of a getFillColor or functionally identical function than please let me know! It would do away with the need for a developer to set .color manually.

All that being said, I’m still trying to nail down exactly what the default behavior of MTE should be in regards to lighting. Sprite’s are lit based on their lighting parameter, a simple boolean. At the moment the default setting is true; any tinting will be lost unless you set .color. If you set the sprite’s lighting parameter to false the engine won’t overwrite your fill color and you can tint the sprite manually using Corona’s native functions. 

Do you think lighting’s default should be false instead of true? Or, perhaps, “vector” sprites could default to false while others default to true? Feedback is welcome!

I used the 0-255 range because MTE uses this internally and still requires this range when using Tiled properties to set Tiled Object colors. But you guys are right about it causing confusion. I’ll go through the engine and samples and bring everything up to date with the new 0-1 range this week.

AppDeveloperGuy;

Yes, I can add in those events. That’s easy enough.

What I hope to accomplish with the new map stitching feature is the seamless expansion of a map. Using a function similar to loadMap, you will specify what map you want to load and where in the pre-existing map to place it (including outside of the current bounds). If any part of the new map is outside the current map bounds the engine will increase the size of the map table to accommodate. It will then load the new map tiles directly into the current map. It will also loop through all the Tiled properties and tilesets in the new map and attempt to merge them into the current map’s properties and tileset table. The result is that you won’t have to do anything special when moving a sprite into the new map, because the new map isn’t a seperate object. If you have a 40x40 map and stick a second 40x40 map to it’s right edge, moving into the new map would be as simple as moving to locX = 41. All you’ll have to watch out for is the load time of a map. Loading a large map will cause a noticeable delay in gameplay.

Coldwilson;

I used willEnterScene because there is only a single scene; it is reloaded and reused when the map changes. The scene is never destroyed, so createScene will only ever fire once. What I needed to do is load a new map and sprites whenever the scene was entered- even if it was entered from itself. 

Scalefactor is an old concept which is no longer valid. Actually users were never meant to even see scaleFactor, let alone have to use it. In 958 the blockScale determined the size of the tiles, but the resulting coordinate system did not match that of the map file. If your tiles were 32x32 in size, but you set blockScale to 72, the tiles would be rendered at 72x72. This meant the center of a tile 1,1 on the map was 16,16, but the center of tile 1,1 in MTE would be 36,36. Scalefactor translated between these values whenever the user tried accessing them using an MTE function.

This is not longer the case. The engine scales the group instead of each tile, so a 32x32 tile with ALWAYS be 32x32 and it’s center will ALWAYS be 16,16, no matter how large or small it appears on the screen.

Long story short, you can get rid of scaleFactor. The touchScroll implementation only uses it here:

--Uncomment code block at line 341 to enable pinch zoom / touch scroll if isDragging then local velX = (startX - currentX) / scaleFactor / mapObj.xScale local velY = (startY - currentY) / scaleFactor / mapObj.yScale ...

Simply cut it out as such;

--Uncomment code block at line 341 to enable pinch zoom / touch scroll if isDragging then local velX = (startX - currentX) / mapObj.xScale local velY = (startY - currentY) / mapObj.yScale ...

Documentation has not been my strong point, I admit! I’m hoping to rectify that shortly.

Greg;

Go to line 312 of mte.lua and comment it out; 

return result

Apologies, that was an oversight on my part. Basically, if an object or the Runtime receives a touch event and returns anything (even the boolean true) the touch won’t propagate to other objects.

Hi dyson,  that worked great!,   I switched my sprite move to a tap event so that it wouldn’t move with the map scroll.

Thanks again for the great work, Greg

Hey dyson,

Color: Great stuff.

Touch: Brilliant. Many thanks!

Stitching: This sounds perfect! Very flexible and lots of potential!

I might have missed it but is it possible to zoom in and out programmatically without pinching the screen (I don’t have the api doc to check at present)? I’m planning on adding zoom in and zoom out buttons (or using a widget candy slider) that zoom in and out incrementally to min and max values. Is there any way to set these?

You’ve probably also already considered this but with the recent move from storyboard to composer in the latest public release, if you’re updating the demos and examples anyway it’s probably worth updating to composer too. Like you aren’t busy enough. :wink:

Thanks again as always!

Like you aren’t busy enough.

I know, right? 

Yes, I considered rewriting the current samples for composer, but as usual it came down to a time budget issue. There’s only so much time and other things like bug fixes had priority. 

You can call mte.zoom(scale, time, easing) to programmatically change the map scale. Alternatively you can retrieve the map object with mte.getMapObj() and use a Corona transition to accomplish the same thing.

Excellent, thanks.

Hi Dyson,

As i asked earlier i am using spine with MTE , When i add  more characters in my map, there is some lag in the movement when the characters are under the camera . Why is this problem occurring ? Is there any solution to over come.

Thanks,

Kumar KS. 

That is a problem with the really sloppy early code for Spine. You’ll notice similar lag if you insert a spine character into a scroll view.

Hello I just bought this engine, first impressions of everything it for sure delivers what it says. All demo’s run perfectly/flawless on device for performance and documentation is actually pretty solid to easily find what I want.

Quick question all I did was was run Castle Demo and I also uploaded it to my phone, why is there that black line over the main sprite for both device and simulator, definitely don’t want that. How do I get rid of it?

Also I wanted to reload monsters/zones as I get to certain areas (like zelda), not have one giant zone so I can get monsters to respawn at certain points, possibly only load up zones/monsters when I get to edge of maps. How would I approach this, through the stitching method? Or can MTE handle this through a totally different method?

Hello Azmar, I’m glad you’re liking MTE so far!

The black line is an edge artifact caused by OpenGL linear interpolation used to scale 2D graphics. If you look at spriteSheet.png you’ll notice that all the sprite animation frames have shadows baked into them. What you’re seeing is the very bottom of the shadow on the the frame above the current animation frame. You can fix this one of two ways. First, you can use a program such as TexturePacker to generate an extruded imagesheet. Extrusion adds a perimeter of transparent pixels around each imagesheet frame. Other than extruding the imagesheet, you can set CoronaSDK to use nearest neighbor interpolation:

display.setDefault("magTextureFilter", "nearest") display.setDefault("minTextureFilter", "nearest")

You could approach spawning/despawning monsters dozens of different ways. This is a gameplay mechanic beyond the scope of MTE. I whipped up a quick-and-dirty implementation to see what it might entail:

--Get the map and set the size of your map regions/screens local map = mte.getMap() local regionSize = 16 --Define your sprites local spriteTemplates = { { "spriteSheet.png", {width = 32, height = 32, numFrames = 96}, { {name = "0", frames = {85, 86}, time = 400, loopCount = 0}, {name = "90", frames = {73, 74}, time = 400, loopCount = 0}, {name = "180", frames = {49, 50}, time = 400, loopCount = 0}, {name = "270", frames = {61, 62}, time = 400, loopCount = 0} } }, { "spriteSheet.png", {width = 32, height = 32, numFrames = 96}, { {name = "0", frames = {88, 89}, time = 400, loopCount = 0}, {name = "90", frames = {76, 77}, time = 400, loopCount = 0}, {name = "180", frames = {52, 53}, time = 400, loopCount = 0}, {name = "270", frames = {64, 65}, time = 400, loopCount = 0} } } } --Create a table for holding the sprites in a given region local spriteData = {} for x = 1, math.floor(map.width / regionSize), 1 do spriteData[x] = {} end --Add sprites to map regions 1,1 and 1,2 spriteData[1][1] = { {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} } spriteData[1][2] = { {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 2, 0, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} } --Create array for storing active sprites local sprites = {} mte.update() local function gameLoop( event ) --Calculate the current map region local regionX, regionY = math.ceil(player.locX / regionSize), math.ceil(player.locY / regionSize) --If the player has entered a new region if not player.regionX or player.regionX ~= regionX or player.regionY ~= regionY then local time = 1000 if not player.regionX then time = 1 end mte.constrainCamera({ loc = {(regionX - 1) \* regionSize + 1, (regionY - 1) \* regionSize + 1, (regionX - 1) \* regionSize + regionSize, (regionY - 1) \* regionSize + regionSize}, time = time, transition = easing.inOutQuad }) print("Change region:", regionX, regionY) player.regionX = regionX player.regionY = regionY --Destroy the sprites from the previous region if desired for i = #sprites, 1, -1 do mte.removeSprite(sprites[i]) sprites[i] = nil end --Create the sprites for the new region if spriteData[regionX][regionY] then for x = 1, regionSize, 1 do for y = 1, regionSize, 1 do if spriteData[regionX][regionY][x][y] ~= 0 then local spriteType = spriteData[regionX][regionY][x][y] local imageSheet = spriteTemplates[spriteType][1] local frameData = spriteTemplates[spriteType][2] local spriteSheet = graphics.newImageSheet(imageSheet, frameData) local sequenceData = spriteTemplates[spriteType][3] sprites[#sprites + 1] = display.newSprite(spriteSheet, sequenceData) local setup = { kind = "sprite", layer = mte.getSpriteLayer(1), locY = (regionY - 1) \* regionSize + x, locX = (regionX - 1) \* regionSize + y } mte.addSprite(sprites[#sprites], setup) end end end end end --...the rest of the enterFrame event... end

https://www.youtube.com/watch?v=YPiD5bqCouk

Thank you very much for this exceptional answer and the video :slight_smile: I’m having lots of fun using MTE, I tried adding some more functionality to your robot movement, noticed its missing a few options (I know only demo) like holding down mouse when off dpad it should stop moving and the animation should be standing still when he stops moving in general. I know this is stuff we should be writing ourselves, and figured I could help expand the demos in a small way for beginners like me, and helps me learn lua quicker!

Lighting DEMO:

local m = “n”

local function move( event )
    if event.phase == “ended” or event.phase == “cancelled” then
        movement = nil
    elseif event.target.id then
        movement = event.target.id
        if (movement == “up”) then
            m = “u”
        elseif (movement == “down”) then
            m = “d”
        elseif (movement == “left”) then
            m = “l”
        elseif (movement == “right”) then
            m = “r”
        end
    end
  

    return true
end


local function gameLoop( event )
    if not player.isMoving then
        --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
            if (m == “u”) then
                player:setSequence(“stillUp”);
                player:pause()
            elseif (m == “d”) then
                player:setSequence(“stillDown”);
                player:pause()
            elseif (m == “l”) then
                player:setSequence(“stillLeft”);
                player:pause()
            elseif (m == “r”) then
                player:setSequence(“stillRight”);
                player:pause()
            end  
            player:pause()
        end
    end

    mte.debug()
    mte.update()
end

That fixes the animation (stillDown being a standing still down animation) and adds an option to move down facing a wall if you cannot move, still working on the holding down mouse button and stopping movement but its confusing the heck out of me with these handlers and can’t seem to solve it properly using demo, will hopefully update it when I can.

Hi Dyson,

I would like to know what all the things i have to change when i update Mte to v984-7, from its older version. I tried updating but it screwed my project, the spine animations are not playing , totally its behaving differently. 

Pls help me to sort out this. 

A quick bit of feedback. I was messing around with the Castle Demo and changing tilesets. (Accidentally), I used one tileset that was a 64x64 one that was a 32x32 and I ended up with some 64x64 tiles being culled even tough they were still partially on screen when moving around the screen. I can fully understand why this was the case and splicing the 64x64 tiles to 32x32 fixed the issue completely. Is it a feature, is it a bug…you decide.

@Azmar: A problem with setting up the code to play a “standing still” animation is that there are no standing animation frames in the imagesheet. Every frame of animation is one half of a two-frame movement animation. The third frame in each set is a duplicate of the last frame. 

Could you be more specific about what you mean by holding down the mouse button and stopping movement? The Dpad is meant to act like an analog stick- moving the mouse after clicking down on it is like dragging the analog stuck around. Movement should continue until the mouse button is release and the invisible imaginary analog stick is allowed to return to its centered position.

@kumarks102: mte.goto() is now mte.setCamera().

The map coordinates now match the original scale of the map, such that setting blockScale to 64 does not actually increase the size of the tiles to 64x64, rather it scales the group object holding the map so that the tiles appear that size on the screen. If you’re using a calculated scaleFactor from mte.worldScale and mte.blockScale you’ll have to rework those calculations to use just the native scale of your map.

sprite.getLevelPosX() and sprite.getLevelPosY() have been removed. A sprite’s x and y properties are now equal to its level position in most cases. sprite.levelPosX and sprite.levelPosY are equal to its level position in all cases.

The kind parameter of the setup table for addSprite(sprite,setup) now accepts the value “group” for sprites which are group objects composed of multiple objects. 

When you updated your project and tried to run it, did it give you any error messages?

@Dave8091: Thanks for the feedback, Dave! That is expected behavior of the culling system. You can adjust when tiles are culled by setting the cullingMargin parameter of setCamera. For example, if your map’s native scale is 32x32 and you’ve set the scale in MTE to 2, setting cullingMargin = {64, 64, 64, 64} will keep those 64x64 tiles from culling early. You want each value of cullingMargin to be half the dimension of your larger tiles, multiplied by the scale of the map.

Hi Dyson,

Mte version 0958

display.setStatusBar( display.HiddenStatusBar )

local mte = require(“mte”)

mte.enableSpriteSorting = true --Needed for Example 5.

mte.spriteSortResolution = 10

mte.loadMap(“1760_1248_32tmx.tmx”) --Load a map.

local blockScale = 24

local isoOverDraw = 1

local map = mte.getMap()

locX = math.ceil(map.width/2)

locY = math.ceil(map.height/2)

mte.setCamera({ locX = locX, locY = locY, blockScale = blockScale, isoOverDraw = isoOverDraw})

local mapProp = mte.getMap()

mte.constrainCamera({loc = {1,1,mapProp.width,mapProp.height}, holdSprite= false, transition = easing.linear, time = 300})

local image = display.newRect(locX,locY, 20, 50)

local setup = {

kind = “sprite”, 

layer =  mte.getSpriteLayer(1), 

locX = locX, 

locY = locY,

levelWidth = 64,

levelHeight = 64,

name = “player”

}

mte.addSprite(image, setup)

mte.setCameraFocus(image)

local cancelMovement = function(event)

mte.cancelSpriteMove(image)

mte.moveSpriteTo({sprite = image, locX = math.random(1,50), locY =  math.random(1,30), time = 3000})

end

timer.performWithDelay(3000, cancelMovement,-1)

local gameLoop = function(event)

mte.update() --Required to process camera and display map.

mte.debug() --Displays onscreen position/fps/memory text.

end

Runtime:addEventListener(“enterFrame”, gameLoop)

This is the code which i am using as a sample program. Tile size is 32x32, When my  object is under the constrain camera there is some lag y is that happening . Pls check and tell me as soon as possible.

That reminds me, Kumarks, you also have to change:

local mte = require("mte")

to:

local mte = require("mte").createMTE()

When you upgrade from 0v958 to 0v984.

I tried your code using 0v984 and did not experience any lag. Is the lag happening in the simulator or on an actual phone or tablet? How many layers are there in your map, and what does the “Total Tiles” debug readout say when you’re experiencing the lag? Could you post the contents of your config.lua?

Edit: Can I make objects or sprite in Tiled as my *NPC’s/monsters* and lets say influence them with mte.moveSpriteTo ? I have seen the examples for creating them in the main etc to move them, but would like to do it through Tiled. Having troubles identifying the sprite/object in my code through Tiled.

 I set sprites to layer 2 following the demo code, and I know my guy starts at layer one following the castle demo, if I give them physics.addBody(dynamic…etc), their physics are influenced by every layer above and beneath it and my character can interact with them layers below and above them…how do I make sure I can only interact with them if I am on the same layer as them and make sure they are on a specific layer and stay on that layer?

Also been doing my “pathfinding” based on light sources from sprites, it makes a lot of sense to do it this way and can easily implement, but I been noticing a good chunk of fps drop with doing this method, especially when i start going nuts on the dpad with all these lightListeners. I made sure to change the location of my functions above and below certain parts of main.lua etc, which makes a difference but still a noticeable drop of fps. Should I not be doing this through light sources?