Codea memory limmit?

I’m trying to make a big scrolling map (using tiles to keep editing with setContext() quick), but it seems I run into a memory limmit quicker than I expected/hoped. I initialise the tiles at setup, but I can’t seem to get any more than about 15x15 tiles of 400x400px. When i increase either tile size or nr of tiles Codea seems to crash.

Is there a known memory limmit in codea? I’m hoping maybe I am initialising them wrongly. Or maybe there are some other clever solutions to load and remove images from memory in realtime?

I’m trying to make a big continuous scrolling map, that doesn’t slow down too much when editing.

Yes, there is a memory limit.

You can either break the big tile map into pieces that are within the limit, and just draw the pieces you need, or else you can use a mesh with a tiling shader, which is obviously more complex.

Here is some code for a tiled mesh of any size you like. The last parameter in CreteTileMesh lets you scale the image, eg if you want to reduce it by half before tiling it, use 0.5.

function setup()
    img=readImage("Cargo Bot:Starry Background")
    m=CreateTileMesh(3000,5000,img,0.5)
end

function draw()
    background(40, 40, 50)
    m:draw()
end

function CreateTileMesh(meshWidth,meshHeight,img,imgScale)
    local m=mesh()   
    --create mesh to cover the whole screen
    local v,t={},{}
    --now calculate how many times the image is used along the x and z axes
    --use these as the maximum texture settings
    --the shader will just use the fractional part of the texture mapping
    --(the shader only requires one line to change, to do this)
    local tilesWide=WIDTH/(img.width*imgScale)
    local tilesHigh=HEIGHT/img.height/imgScale
    local x1,x2,y1,y2=0,WIDTH,0,HEIGHT
    local tx1,tx2,tz1,tz2=0,tilesWide,0,tilesHigh
    v[1]=vec3(x1,y1,0) t[1]=vec2(tx1,tz1)
    v[2]=vec3(x2,y1,0) t[2]=vec2(tx2,tz1)
    v[3]=vec3(x2,y2,0) t[3]=vec2(tx2,tz2)
    v[4]=vec3(x1,y2,0) t[4]=vec2(tx1,tz2)
    v[5]=vec3(x1,y1,0) t[5]=vec2(tx1,tz1)
    v[6]=vec3(x2,y2,0) t[6]=vec2(tx2,tz2)
    m.vertices=v
    m.texCoords=t
    m:setColors(color(255))
    m.texture=img
    m.shader=shader(TileShader.vertexShader,TileShader.fragmentShader)
    return m
end

TileShader = {
vertexShader = [[
uniform mat4 modelViewProjection;
attribute vec4 position;
attribute vec4 color;
attribute vec2 texCoord;
varying lowp vec4 vColor;
varying highp vec2 vTexCoord;

void main()
{
    vColor=color;
    vTexCoord = texCoord;
    gl_Position = modelViewProjection * position;
}
]],
fragmentShader = [[
precision highp float;
uniform lowp sampler2D texture;
varying lowp vec4 vColor;
varying highp vec2 vTexCoord;

void main()
{
    lowp vec4 col = texture2D( texture, vec2(mod(vTexCoord.x,1.0), mod(vTexCoord.y,1.0)));
    gl_FragColor = col;
}
]]}

Thanks ignatz, I think I’m currently working on your first suggestion, drawing only the tiles that I need displayed, however I would still need to store all the individual tile images into memory somewhere right? (because they won’t be the same repeated image, but rather parts of one big continuous image)

So currently I create a bunch of empty images at startup (the tiles), which I would then draw in proceduraly, and save in a 2d array. But I quickly hit codea’s memory limit.

Or do you mean I could draw and save the individual tile images to HD using saveData and then load them back in as needed ?

I’d like to avoid shaders for now until I’ve got a better understanding of them.

Generally most games would use some kind of repeating texture, precisely to avoid memory issues. One exception that springs to mind are graphic adventure games with hand-drawn assets. Is there a good reason for each tile to be a unique image? You can get very good textures where there’s no obvious join, eg: http://www.spiralgraphics.biz/packs/browse.htm

Yes, I have uploaded an example of what I mean.
[https://youtube.com/watch?v=wxVXkXRW9qQ&feature=youtu.be]
Currently I’m just moving one big 2000*2000 image for the walls, but it’s still fairly small. Also using setContext to edit the map causes big delays for big images.

I want to make a larger map than in the xample vid, without larger delays when editing.

@Kirl

That’s cute! Love the animation. How are you animating the blobs?

Is the gravity set by device orientation?

I don’t know if you’re planning on adding hand-drawn assets, but as the graphics currently stand, I see no reason at all why the background has to be stored as essentially a massive bitmap. Especially if you want the map to get much bigger you just going to run into memory issues which are entirely avoidable. The scenery looks like its made up entirely of geometric shapes. Primitives, or a mesh, are the way to go here. If the background contains physics bodies (it looks like it does), then use the points of the bodies as the basis of a mesh. The triangulate function will wind the triangles for you.

Thanks! Yes, I’m rotating the device in the vid. The blobs are a collection of mesh points, which spring back to a set distance from eachother. I don’t use the codea physics implementation, every blob point looks for non transparent pixels on a bitmap.

I want to use a bitmap because of my collision method. The geometric shapes are only a random start fill, but you edit the walls by drawing and erasing the bitmap. It works with any shape, and it isn’t affected by the complexity, size or the nr shapes. So there are a lot of benefits of using bitmaps, the only drawback is the memory, which isn’t a problem if I can load the tiles dynamically.

I’m going to test how quickly image data saves and loads and see if thats an option. If it works I could make a huge scrollmap size!

@Kirl that’s very impressive. So it’s basically your own physics engine (or is it adapted from an open source engine?), resolving collisions etc. Well, I guess that means you have a good reason to use bitmaps.

Let us know how you get on with dynamic loading.

@Kirl

Your game looks awesome! I might be wrong, but it sounds a similar problem to creating a large level with destructable terrain. Here’s something I did a while back when looking at creating a worms/lemmings type game. It might give a different perspective and stimulate an alternative approach. This a scrollable, dynamic 10,000 by 5000 pixel level - but you should be fine going larger too.

-- destructable terrain in a large level
--by West

-- Use this function to perform your initial setup
local numObjs = 50
local obj

function setup()
    displayMode(FULLSCREEN)
    globalx=0
    globaly=0
    levelmaxx=10000
    levelmaxy=2000
    
    globalxspd=0
    globalyspd=0
    globalxacc=0
    globalyacc=0
    
    mapx=550
    mapy=20
    mapw=WIDTH-600
    maph=60
    touchdeceleration=0.95
    mainx=10
    mainw=WIDTH-2*mainx
    mainy=100
    mainh=HEIGHT-110
    
    touches={}
    tsup={} --tsup contains the supplementary info about the start position of the touch
    points={}
    pulse={}
    parameter.boolean("includeTarget",false)
    parameter.boolean("includeTrail",false)
    parameter.boolean("includeStart",false)
    parameter.boolean("includeDuration",false)
    parameter.boolean("includeHistory",false)
    parameter.boolean("joinHistory",false)
    parameter.boolean("includePulse",false)
    parameter.boolean("includeClassification",false)
    
    obj={}
    for i=1,numObjs do
        table.insert(obj, {x=math.random(levelmaxx),y=math.random(levelmaxy),w=50+math.random(50),h=50+math.random(50),img=readImage("Cargo Bot:Crate Green "..math.random(3))})
    end
    for c=0,levelmaxx,100 do
        table.insert(obj,{x=c,y=100,w=100,h=100,img=readImage("Cargo Bot:Crate Yellow "..math.random(3))})
    end
end

-- This function gets called once every frame
function draw()
    -- This sets a dark background color
    background(0,0,0)
    
    globalxspd = globalxspd *touchdeceleration
    globalyspd=globalyspd*touchdeceleration
    if math.abs(globalxspd)<0.1 then globalxspd=0 end
    if math.abs(globalyspd)<0.1 then globalyspd=0 end
    globalx = globalx + globalxspd
    globaly = globaly + globalyspd
    
    if globalx>levelmaxx-WIDTH then
        globalx=levelmaxx-WIDTH
        
    end
    if globalx<0 then
        globalx=0
    end
    if globaly>levelmaxy-HEIGHT then
        globaly=levelmaxy-HEIGHT
    end
    if globaly<0 then
        globaly=0
    end
    for i=1,#obj do
        --check a certain point in the screen coords poix,poiy
        local poix=WIDTH/2
        local poiy=HEIGHT/2
        if poix>(obj[i].x-globalx)-obj[i].w/2 and poix<(obj[i].x-globalx)+obj[i].w/2
        and poiy>(obj[i].y-globaly)-obj[i].h/2 and poiy<(obj[i].y-globaly)+obj[i].h/2 then
            local  b1r,b1g,b1b,b1a=obj[i].img:get(math.floor((poix-(obj[i].x-globalx)+obj[i].w/2)*obj[i].img.width/obj[i].w+1),math.floor((poiy-(obj[i].y-globaly)+obj[i].h/2)*obj[i].img.height/obj[i].h+1))
        end
        -- only render if its onscreen
        if (obj[i].x-globalx)+obj[i].w>mainx and (obj[i].x-globalx)-obj[i].w<(mainx+mainw)
        and (obj[i].y-globaly)+obj[i].h>mainy and (obj[i].y-globaly)-obj[i].h<(mainy+mainh) then
            sprite(obj[i].img,obj[i].x-globalx,obj[i].y-globaly,obj[i].w,obj[i].h)
        end
        if math.abs(CurrentTouch.x-(obj[i].x-globalx))<obj[i].w and math.abs(CurrentTouch.y-(obj[i].y-globaly))<obj[i].h then
            setContext(obj[i].img)
            sprite("Dropbox:Hole",(CurrentTouch.x-(obj[i].x-globalx)+obj[i].w/2)*obj[i].img.width/obj[i].w,(CurrentTouch.y-(obj[i].y-globaly)+obj[i].h/2)*obj[i].img.height/obj[i].h,20*obj[i].img.width/obj[i].w,20*obj[i].img.height/obj[i].h)
            setContext()
        end
        
    end
    --draw surround
    fill(50)
    rect(-1,0,mainx,HEIGHT)
    rect(mainx+mainw,0,WIDTH-mainx-mainw+1,HEIGHT)
    rect(0,-1,WIDTH,mainy)
    rect(0,mainy+mainh,WIDTH,HEIGHT-mainy-mainh+1)
    --draw map
    fill(0)
    rect(mapx,mapy,mapw,maph)
    for i=1,#obj do
        local px=mapx+mapw*(obj[i].x)/(levelmaxx)
        local py=mapy+maph*obj[i].y/(levelmaxy)
        sprite(obj[i].img,px,py,5,5)
    end
    --draw map reticle
    noFill()
    strokeWidth(3)
    stroke(0,0,255)
    local leftedge=mapx+(mapw)*(globalx)/(levelmaxx)
    local rightedge=mapx+(mapw)*(mainw+globalx)/(levelmaxx)
    local bottom=mapy+(maph)*globaly/levelmaxy
    local top=mapy+(maph)*(mainh+globaly)/levelmaxy
    line(leftedge,bottom,leftedge,top)
    line(rightedge,bottom,rightedge,top)
    line(rightedge,bottom,leftedge,bottom)
    line(rightedge,top,leftedge,top)
    noStroke()
    --move screen via map
            
    strokeWidth(2)
    for i,t in pairs(touches) do
        if t.x>mapx and t.x<(mapx+mapw) and t.y>mapy and t.y<mapy+maph then
            globalx=(levelmaxx*(t.x-mapx)/(mapw))-WIDTH/2
            globaly=(levelmaxy*(t.y-(mapy))/maph)-HEIGHT/2
            globalxspd=0
            globalyspd=0
        end
        
        fill(255)
        stroke(255)
        if includeStart==true then
            line(tsup[i].tstartx,tsup[i].tstarty,t.x,t.y)
            ellipse(tsup[i].tstartx,tsup[i].tstarty,10)
        end
        fill(255)
        if includeDuration==true then
            local formattime=math.floor(10*(ElapsedTime-tsup[i].starttime))/10
            text(formattime,t.x,t.y+50)
        end
        ellipse(t.x,t.y,10)
        if includeTarget==true then
            local endpt=math.max(1200-800*(ElapsedTime-tsup[i].starttime),50+10*math.sin(5*ElapsedTime))
            local spin=ElapsedTime*40
            pushMatrix()
            translate(t.x,t.y)
            rotate(spin)
            translate(-endpt,0)
            sprite("Cargo Bot:How Arrow",0,0)
            translate(endpt,0)
            rotate(90)
            translate(-endpt,0)
            sprite("Cargo Bot:How Arrow",0,0)
            translate(endpt,0)
            rotate(90)
            translate(-endpt,0)
            sprite("Cargo Bot:How Arrow",0,0)
            translate(endpt,0)
            rotate(90)
            translate(-endpt,0)
            sprite("Cargo Bot:How Arrow",0,0)
            translate(endpt,0)
            popMatrix()
        end
        --draw path of touch
        if includeTrail==true then
            fadespeed=500
            for j,p in pairs(tsup[i].path) do
                trailfade=math.max((p.age-ElapsedTime)*fadespeed,-255)
                stroke(255,255,255,255+trailfade)
                fill(255,255,255,255+trailfade)
                if trailfade>-255 then
                    -- ellipse(p.pos.x,p.pos.y,5)
                    if j>1 then
                        line(prevx,prevy,p.pos.x,p.pos.y)
                    end
                end
                prevx=p.pos.x
                prevy=p.pos.y
            end
        end
    end
    
    noStroke()
    
end

function touched(touch)
    if touch.state==MOVING then
        --record path
        if tsup[touch.id]~=nil then
            table.insert(tsup[touch.id].path,{pos=vec2(touch.x,touch.y),age=ElapsedTime})
        end
    end
    if touch.state==ENDED or touch.state==CANCELLED then
        processTouch(touch)
        touches[touch.id] = nil
        tsup[touch.id]=nil
    else
        touches[touch.id] = touch
        --if there is no supplementary info associated with the current touch then add it
        if tsup[touch.id]==nil then
            tsup[touch.id]={tstartx=touch.x,tstarty=touch.y,starttime=ElapsedTime,path={}}
        end
    end
end

function processTouch(touch)
    if #tsup[touch.id].path>1 then
        if tsup[touch.id].path[#tsup[touch.id].path].pos:dist(tsup[touch.id].path[#tsup[touch.id].path-1].pos)>1 then
            globalxspd=-2*(tsup[touch.id].path[#tsup[touch.id].path].pos.x-tsup[touch.id].path[#tsup[touch.id].path-1].pos.x)
            globalyspd=-2*(tsup[touch.id].path[#tsup[touch.id].path].pos.y-tsup[touch.id].path[#tsup[touch.id].path-1].pos.y)
        end
        --   print(tsup[touch.id].path[#tsup[touch.id].path].pos.x)
    end
    if ElapsedTime-tsup[touch.id].starttime<0.2 then
        --very short event
        if tsup[touch.id]==nil or vec2(touch.x,touch.y):dist(vec2(tsup[touch.id].tstartx,tsup[touch.id].tstarty))<10 then
            --tap
            
            --maybe check in window
            globalxspd=0
            globalyspd=0
        end
    end
    if includeHistory==true then
        table.insert(points,vec2(touch.x,touch.y)) --add a point to the points table
    end
    if includePulse==true then
        table.insert(pulse,{x=touch.x,y=touch.y,r=8,rate=1}) --add a new pulse
    end
    if includeClassification==true then
        if ElapsedTime-tsup[touch.id].starttime<0.2 then
            --very short event
            if tsup[touch.id]==nil then
                print("tap")
            elseif vec2(touch.x,touch.y):dist(vec2(tsup[touch.id].tstartx,tsup[touch.id].tstarty))<10 then
                print("tap")
            else
                print("dash")
            end
        elseif ElapsedTime-tsup[touch.id].starttime<1 then
            --a slightly longer gesture
            if touch.x>tsup[touch.id].tstartx+25 and math.abs(touch.y-tsup[touch.id].tstarty)<50 then
                print("swipe right")
            elseif touch.x<tsup[touch.id].tstartx-25 and math.abs(touch.y-tsup[touch.id].tstarty)<50 then
                print("swipe left")
            elseif touch.y>tsup[touch.id].tstarty+25 and math.abs(touch.x-tsup[touch.id].tstartx)<50 then
                print("swipe up")
            elseif touch.y<tsup[touch.id].tstarty-25 and math.abs(touch.x-tsup[touch.id].tstartx)<50 then
                print("swipe down")
            end
        end
    end
end

Oops. Just noticed there is a custome sprite - Dropbox:hole. This is just a black circle with a transparent background - should be straightforward to create your own, but give us a shout if you need a hand

Here’s an updated version

-- destructable terrain in a large level
--by West

-- Use this function to perform your initial setup
local numObjs = 50
local obj

function setup()
    displayMode(FULLSCREEN)
    globalx=0
    globaly=0
    levelmaxx=10000
    levelmaxy=2000
    
    globalxspd=0
    globalyspd=0
    globalxacc=0
    globalyacc=0
    
    mapx=550
    mapy=20
    mapw=WIDTH-600
    maph=60
    touchdeceleration=0.95
    mainx=10
    mainw=WIDTH-2*mainx
    mainy=100
    mainh=HEIGHT-110
    
    touches={}
    tsup={} --tsup contains the supplementary info about the start position of the touch
    points={}
    pulse={}
    parameter.boolean("includeTarget",false)
    parameter.boolean("includeTrail",false)
    parameter.boolean("includeStart",false)
    parameter.boolean("includeDuration",false)
    parameter.boolean("includeHistory",false)
    parameter.boolean("joinHistory",false)
    parameter.boolean("includePulse",false)
    parameter.boolean("includeClassification",false)
    
    obj={}
    for i=1,numObjs do
        table.insert(obj, {x=math.random(levelmaxx),y=math.random(levelmaxy),w=50+math.random(50),h=50+math.random(50),img=readImage("Cargo Bot:Crate Green "..math.random(3))})
    end
    for c=0,levelmaxx,100 do
        table.insert(obj,{x=c,y=100,w=100,h=100,img=readImage("Cargo Bot:Crate Yellow "..math.random(3))})
    end
    
    --create hole image
    holeimg=image(50,50)
    setContext(holeimg)
    fill(0)
    ellipse(25,25,50)
    setContext()
end

-- This function gets called once every frame
function draw()
    -- This sets a dark background color
    background(0,0,0)
    
    globalxspd = globalxspd *touchdeceleration
    globalyspd=globalyspd*touchdeceleration
    if math.abs(globalxspd)<0.1 then globalxspd=0 end
    if math.abs(globalyspd)<0.1 then globalyspd=0 end
    globalx = globalx + globalxspd
    globaly = globaly + globalyspd
    
    if globalx>levelmaxx-WIDTH then
        globalx=levelmaxx-WIDTH
        
    end
    if globalx<0 then
        globalx=0
    end
    if globaly>levelmaxy-HEIGHT then
        globaly=levelmaxy-HEIGHT
    end
    if globaly<0 then
        globaly=0
    end
    for i=1,#obj do
        --check a certain point in the screen coords poix,poiy
        local poix=WIDTH/2
        local poiy=HEIGHT/2
        if poix>(obj[i].x-globalx)-obj[i].w/2 and poix<(obj[i].x-globalx)+obj[i].w/2
        and poiy>(obj[i].y-globaly)-obj[i].h/2 and poiy<(obj[i].y-globaly)+obj[i].h/2 then
            local  b1r,b1g,b1b,b1a=obj[i].img:get(math.floor((poix-(obj[i].x-globalx)+obj[i].w/2)*obj[i].img.width/obj[i].w+1),math.floor((poiy-(obj[i].y-globaly)+obj[i].h/2)*obj[i].img.height/obj[i].h+1))
        end
        -- only render if its onscreen
        if (obj[i].x-globalx)+obj[i].w>mainx and (obj[i].x-globalx)-obj[i].w<(mainx+mainw)
        and (obj[i].y-globaly)+obj[i].h>mainy and (obj[i].y-globaly)-obj[i].h<(mainy+mainh) then
            sprite(obj[i].img,obj[i].x-globalx,obj[i].y-globaly,obj[i].w,obj[i].h)
        end
        if math.abs(CurrentTouch.x-(obj[i].x-globalx))<obj[i].w and math.abs(CurrentTouch.y-(obj[i].y-globaly))<obj[i].h then
            setContext(obj[i].img)
            --sprite("Dropbox:Hole",(CurrentTouch.x-(obj[i].x-globalx)+obj[i].w/2)*obj[i].img.width/obj[i].w,(CurrentTouch.y-(obj[i].y-globaly)+obj[i].h/2)*obj[i].img.height/obj[i].h,20*obj[i].img.width/obj[i].w,20*obj[i].img.height/obj[i].h)
            sprite(holeimg,(CurrentTouch.x-(obj[i].x-globalx)+obj[i].w/2)*obj[i].img.width/obj[i].w,(CurrentTouch.y-(obj[i].y-globaly)+obj[i].h/2)*obj[i].img.height/obj[i].h,20*obj[i].img.width/obj[i].w,20*obj[i].img.height/obj[i].h)
            setContext()
        end
        
    end
    --draw surround
    fill(50)
    rect(-1,0,mainx,HEIGHT)
    rect(mainx+mainw,0,WIDTH-mainx-mainw+1,HEIGHT)
    rect(0,-1,WIDTH,mainy)
    rect(0,mainy+mainh,WIDTH,HEIGHT-mainy-mainh+1)
    --draw map
    fill(0)
    rect(mapx,mapy,mapw,maph)
    for i=1,#obj do
        local px=mapx+mapw*(obj[i].x)/(levelmaxx)
        local py=mapy+maph*obj[i].y/(levelmaxy)
        sprite(obj[i].img,px,py,5,5)
    end
    --draw map reticle
    noFill()
    strokeWidth(3)
    stroke(0,0,255)
    local leftedge=mapx+(mapw)*(globalx)/(levelmaxx)
    local rightedge=mapx+(mapw)*(mainw+globalx)/(levelmaxx)
    local bottom=mapy+(maph)*globaly/levelmaxy
    local top=mapy+(maph)*(mainh+globaly)/levelmaxy
    line(leftedge,bottom,leftedge,top)
    line(rightedge,bottom,rightedge,top)
    line(rightedge,bottom,leftedge,bottom)
    line(rightedge,top,leftedge,top)
    noStroke()
    --move screen via map
            
    strokeWidth(2)
    for i,t in pairs(touches) do
        if t.x>mapx and t.x<(mapx+mapw) and t.y>mapy and t.y<mapy+maph then
            globalx=(levelmaxx*(t.x-mapx)/(mapw))-WIDTH/2
            globaly=(levelmaxy*(t.y-(mapy))/maph)-HEIGHT/2
            globalxspd=0
            globalyspd=0
        end
        
        fill(255)
        stroke(255)
        if includeStart==true then
            line(tsup[i].tstartx,tsup[i].tstarty,t.x,t.y)
            ellipse(tsup[i].tstartx,tsup[i].tstarty,10)
        end
        fill(255)
        if includeDuration==true then
            local formattime=math.floor(10*(ElapsedTime-tsup[i].starttime))/10
            text(formattime,t.x,t.y+50)
        end
        ellipse(t.x,t.y,10)
        if includeTarget==true then
            local endpt=math.max(1200-800*(ElapsedTime-tsup[i].starttime),50+10*math.sin(5*ElapsedTime))
            local spin=ElapsedTime*40
            pushMatrix()
            translate(t.x,t.y)
            rotate(spin)
            translate(-endpt,0)
            sprite("Cargo Bot:How Arrow",0,0)
            translate(endpt,0)
            rotate(90)
            translate(-endpt,0)
            sprite("Cargo Bot:How Arrow",0,0)
            translate(endpt,0)
            rotate(90)
            translate(-endpt,0)
            sprite("Cargo Bot:How Arrow",0,0)
            translate(endpt,0)
            rotate(90)
            translate(-endpt,0)
            sprite("Cargo Bot:How Arrow",0,0)
            translate(endpt,0)
            popMatrix()
        end
        --draw path of touch
        if includeTrail==true then
            fadespeed=500
            for j,p in pairs(tsup[i].path) do
                trailfade=math.max((p.age-ElapsedTime)*fadespeed,-255)
                stroke(255,255,255,255+trailfade)
                fill(255,255,255,255+trailfade)
                if trailfade>-255 then
                    -- ellipse(p.pos.x,p.pos.y,5)
                    if j>1 then
                        line(prevx,prevy,p.pos.x,p.pos.y)
                    end
                end
                prevx=p.pos.x
                prevy=p.pos.y
            end
        end
    end
    
    noStroke()
    
end

function touched(touch)
    if touch.state==MOVING then
        --record path
        if tsup[touch.id]~=nil then
            table.insert(tsup[touch.id].path,{pos=vec2(touch.x,touch.y),age=ElapsedTime})
        end
    end
    if touch.state==ENDED or touch.state==CANCELLED then
        processTouch(touch)
        touches[touch.id] = nil
        tsup[touch.id]=nil
    else
        touches[touch.id] = touch
        --if there is no supplementary info associated with the current touch then add it
        if tsup[touch.id]==nil then
            tsup[touch.id]={tstartx=touch.x,tstarty=touch.y,starttime=ElapsedTime,path={}}
        end
    end
end

function processTouch(touch)
    if #tsup[touch.id].path>1 then
        if tsup[touch.id].path[#tsup[touch.id].path].pos:dist(tsup[touch.id].path[#tsup[touch.id].path-1].pos)>1 then
            globalxspd=-2*(tsup[touch.id].path[#tsup[touch.id].path].pos.x-tsup[touch.id].path[#tsup[touch.id].path-1].pos.x)
            globalyspd=-2*(tsup[touch.id].path[#tsup[touch.id].path].pos.y-tsup[touch.id].path[#tsup[touch.id].path-1].pos.y)
        end
        --   print(tsup[touch.id].path[#tsup[touch.id].path].pos.x)
    end
    if ElapsedTime-tsup[touch.id].starttime<0.2 then
        --very short event
        if tsup[touch.id]==nil or vec2(touch.x,touch.y):dist(vec2(tsup[touch.id].tstartx,tsup[touch.id].tstarty))<10 then
            --tap
            
            --maybe check in window
            globalxspd=0
            globalyspd=0
        end
    end
    if includeHistory==true then
        table.insert(points,vec2(touch.x,touch.y)) --add a point to the points table
    end
    if includePulse==true then
        table.insert(pulse,{x=touch.x,y=touch.y,r=8,rate=1}) --add a new pulse
    end
    if includeClassification==true then
        if ElapsedTime-tsup[touch.id].starttime<0.2 then
            --very short event
            if tsup[touch.id]==nil then
                print("tap")
            elseif vec2(touch.x,touch.y):dist(vec2(tsup[touch.id].tstartx,tsup[touch.id].tstarty))<10 then
                print("tap")
            else
                print("dash")
            end
        elseif ElapsedTime-tsup[touch.id].starttime<1 then
            --a slightly longer gesture
            if touch.x>tsup[touch.id].tstartx+25 and math.abs(touch.y-tsup[touch.id].tstarty)<50 then
                print("swipe right")
            elseif touch.x<tsup[touch.id].tstartx-25 and math.abs(touch.y-tsup[touch.id].tstarty)<50 then
                print("swipe left")
            elseif touch.y>tsup[touch.id].tstarty+25 and math.abs(touch.x-tsup[touch.id].tstartx)<50 then
                print("swipe up")
            elseif touch.y<tsup[touch.id].tstarty-25 and math.abs(touch.x-tsup[touch.id].tstartx)<50 then
                print("swipe down")
            end
        end
    end
end

@yojimbo2000
The physics is my own aproach, but it’s probably much simpler than you might expect. The points move towards the blob center until they’re over a transparent pixel. =)

@west
Thanks for sharing, it works quite nice. Can you explain your aproach in a nutshell?

https://www.youtube.com/watch?v=wXSAMtTJjzM&feature=youtu.be

It seems dynamically loading in tiles from memory is actualy possible, I now have a scrolling test map of 50000*50000 px! I save the generated images (2500 tiles) to Documents, so that’s quite mess, is there any way to save them to a sub folder with saveimage()?

I don’t think you can have sub folders within Documents or Dropbox, but you can save them into the Project’s own folder (you’ll only be able to browse them from asset pickers within the project, they won’t show up in the Assets drawer on the main Codea project page). AFAIR you use “Project:” as the path (not the actual name of your project). The disadvantage to this approach is that it’s a bit tricky for other apps to access the assets. I’m not sure what happens when you duplicate your project (presumably the assets get copied).

Alternatively (a bit of a hack) you could give each image the same prefix (eg beginning with z to keep them out of the way at the bottom of the asset picker)

@Kirl the approach is as follows:

  1. create a bunch of sprite objects to put on the screen and record their position, size, etc in a table

  2. convert the screen position of the touch to a global co-ordinate then to check to see if it lies within any of the sprite objects

  3. if it does, then stamp the black hole image at that point on the instance of the sprite image.

It also does a check on each sprite object and only displays it if the coordinates are in the current field of view

Excellent, thanks for the tip to save in the project itself @yojimbo2000, that’s much better! And thanks for the explanation @west.

I’m chuffed it turned out to be quick enough to load images in real time, thanks for the help and suggestions! =)