Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Scrolling

in General Posts: 1,392

Hi All,

There are many ways to scroll backgrounds from simple sprite drawing to 3D skybox rotation. I have tried my hand at them all but still find myself not knowing the best way to build code for each separate, with different requirements, projects.

One instance I have is drilling a large 2D map which is populated with objects that change move and interact - sprite or mesh?

Another instance involves a psuedo 3D scrolling terrain which is large but also wraps on the edges. It uses a 3D object for the hero and the plain scrolls in all directions.

Finally, not got to this stage yet, but a full infinite scrolling terrain with 3D hero and multi level shaded landscape.

Some of the recent posts from @dave1707 have whetted my appetite again and I have been digging out the old half finished projects.

Just wondering if some of our better programmers could fire in a little help to point me and other users in the direction of best options, and things to consider when trying to code them.

Comments

  • Posts: 1,392

    Hi All,

    Just a more specific query, if you have a large mesh terrain which would display well over the limits of the screen - can you select a subset of the mesh for display? Objective to minimise resources and keep tight control on screen output.

    This can be done with sprites and large images.

  • dave1707dave1707 Mod
    edited February 7 Posts: 7,912

    @Bri_G I hacked one of my games to show scrolling over a terrain that measures 5,000,000 by 5,000,000 pixels. You can go even higher depending on the map you use. If the map gets spread to thin, it doesn’t look too good. I tried 1,000,000,000 by 1,000,000,000 pixels and the FPS was still around 60. I don’t think you need a terrain that large. At the speed I have the ship going (100 pixels per second) it would take about 115 days to go 1,000,000,000 pixels. So that’s probably a large enough terrain for a game without having to resort to subsets.

    PS. Placing objects on the terrain can probably be done in subsets.

    displayMode(FULLSCREEN)
    
    function setup() 
        fill(255)
        hgx=Gravity.x
        speed,ey,ang=2,45,0
        cameraX,cameraZ=0,0
        scene = craft.scene()
        scene.camera.position = vec3(cameraX,0,cameraZ)
        scene.camera.eulerAngles=vec3(0,ey,0)
        scene.sun.rotation = quat.eulerAngles(45,0,45)
        scene.ambientColor = color(90,90,90)
        skyMaterial = scene.sky.material
        skyMaterial.sky = color(252, 255, 0, 255)
        skyMaterial.horizon = color(0, 203, 255, 255)    
        createFloor()
    end
    
    function update(dt)
        scene:update(dt)
        scene.camera.position = vec3(cameraX,1,cameraZ)
        scene.camera.eulerAngles=vec3(0,ey,0)
    end
    
    function draw()
        background(0)
        update(DeltaTime)
        scene:draw()
        ey=ey-ang
        xVel=speed*math.sin(math.rad(ey))
        zVel=speed*math.cos(math.rad(ey)) 
        xDir=math.sin(math.rad(ey))
        zDir=math.cos(math.rad(ey)) 
        cameraX=cameraX+xVel
        cameraZ=cameraZ+zVel    
        gx=Gravity.x
        ang=ang+(gx-hgx)*4
        hgx=gx
        pushMatrix()
        translate(WIDTH/2,HEIGHT/2-100)
        rotate(ang*-30)
        sprite("Tyrian Remastered:Boss A",0,0,300)
        translate()
        popMatrix()
        text(1/DeltaTime,WIDTH/2,HEIGHT-25)
        text((cameraX//1).."  "..(cameraZ//1),WIDTH/2,HEIGHT-50)
    end
    
    function createFloor(x,z)
        c1=scene:entity()
        w=c1:add(craft.rigidbody,STATIC)
        c1.model = craft.model.cube(vec3(5000000,1,5000000))
        c1.position=vec3(x,0,z)
        c1.material = craft.material("Materials:Standard")
        c1.material.map = readImage("Surfaces:Basic Bricks AO")
        c1.material.offsetRepeat=vec4(0,0,5000,5000)
    end
    
  • edited February 8 Posts: 1,392

    @dave1707 - very impressive but it slightly misses the point. You have stretched one icon over the full size of the texture. In a large terrain Iwould like to fill a texture with multiple small images. Whilst trying to generate that I came across a real oddity - explain this:


    displayMode(FULLSCREEN) function setup() txture() end function draw() end function txture() -- plain = image(256*48,256*48) tl = 0 spriteMode(CENTER) setContext(plain) for y = 0, 255 do for x = 0, 255 do sprite("Cargo Bot:Crate Blue 2",x*42+21,y*42+21,42,42) tl = tl+1 if tl == 27 then tl = 0 end end end setContext() end

    Note my iPad has a lot of memory.

  • SimeonSimeon Admin Mod
    Posts: 5,054

    @Bri_G this is an infinite scrolling sprite canvas modulated by noise. Turn on DebugDraw to see the clipping window version

    -- Scroller
    
    function setup()
        displayMode(STANDARD)
    
        parameter.boolean("DebugDraw", false)
    
        pos = vec2(WIDTH/2, HEIGHT/2)
    
        print("Drag the screen to scroll the map")
    end
    
    function draw()
        -- This sets a dark background color 
        background(40, 40, 50)
    
        -- This sets the line thickness
        strokeWidth(5)
    
        translate(pos.x, pos.y)
    
        local tileSize = 128
        local grid = vec2(WIDTH/tileSize, HEIGHT/tileSize)
    
        if DebugDraw then
            grid = vec2(512/tileSize, 512/tileSize)
            translate(WIDTH/4, HEIGHT/4)
        end
    
        local boundsStart = -pos + (grid * tileSize)/2
    
        spriteMode(CENTER)
        stroke(200, 29, 29, 255)
        noFill()
    
        local gridStart = vec2(math.floor(boundsStart.x/tileSize), math.floor(boundsStart.y/tileSize))
        local tileStart = gridStart * tileSize
        local p = vec2()
    
        for x = -grid.x/2, grid.x/2 + 1 do
            for y = -grid.y/2, grid.y/2 + 1 do
                p = tileStart + vec2(tileSize * x, tileSize * y)
    
                local shade = math.max((noise(100 + p.x * 0.05, 100 + p.y * 0.05, 100) + 1.0)/2.0, 0)
                shade = math.min(shade + 0.5, 1.0)
                tint(255 * shade, 255 * shade, 255 * shade)            
    
                sprite("Blocks:Grass Top", p.x, p.y)        
            end
        end    
    
        if DebugDraw then
            rectMode(CENTER)
            rect(boundsStart.x, boundsStart.y, grid.x * tileSize, grid.y * tileSize)
        end
    end
    
    function touched(touch)
        if touch.state == MOVING then
            pos = pos + vec2(touch.deltaX, touch.deltaY)
        end
    end
    
  • Posts: 417
    For my terrain i use the terrain class developed by @ignatz. It uses a mesh, so one can have hills and valleys in the terrain. I modified it to use the @loopspace pseudoMesh instead of Mesh, this way it can be converted to a craft model and react to the craft lighting and physics.
  • dave1707dave1707 Mod
    Posts: 7,912

    @Bri_G Here’s another version. I didn’t try to solve every issue with this, but just to give an idea. It starts out with 1 terrain area, but it creates a random new area as you fly over a blank area. The more you fly the larger your terrain gets. Still not sure if this helps any, but was interesting to write.

    displayMode(FULLSCREEN)
    
    function setup() 
        map={"Surfaces:Basic Bricks AO","Surfaces:Basic Bricks Color",
        "Surfaces:Basic Bricks Normal","Surfaces:Basic Bricks Roughness",
        "Surfaces:Desert Cliff AO","Surfaces:Desert Cliff Color",
        "Surfaces:Desert Cliff Height","Surfaces:Desert Cliff Normal",
        "Surfaces:Desert Cliff Roughness"}
    
        size=20    
        fill(255)
        hgx=Gravity.x
        speed=.2
        ey,ang=45,0
        cameraX,cameraZ=0,0
    
        scene = craft.scene()
        scene.camera.position = vec3(cameraX,0,cameraZ)
        scene.camera.eulerAngles=vec3(0,ey,0)
        scene.sun.rotation = quat.eulerAngles(45,0,45)
        scene.ambientColor = color(90,90,90)
    
        skyMaterial = scene.sky.material
        skyMaterial.sky = color(0, 185, 255, 255)
        skyMaterial.horizon = color(116, 226, 198, 255)    
    
        mat={}
        createFloor(vec3(0,0,0))   
        table.insert(mat,vec2(cameraX//size,cameraZ//size))
    end
    
    function update(dt)
        scene:update(dt)
        scene.camera.position = vec3(cameraX,1,cameraZ)
        scene.camera.eulerAngles=vec3(0,ey,0)
    end
    
    function draw()
        background(0)
        update(DeltaTime)
        scene:draw()
        ey=ey-ang
        xVel=speed*math.sin(math.rad(ey))
        zVel=speed*math.cos(math.rad(ey)) 
        xDir=math.sin(math.rad(ey))
        zDir=math.cos(math.rad(ey)) 
        cameraX=cameraX+xVel
        cameraZ=cameraZ+zVel    
        gx=Gravity.x
        ang=ang+(gx-hgx)*4
        hgx=gx
        pushMatrix()
        translate(WIDTH/2,HEIGHT/2-300)
        rotate(ang*-30)
        sprite("Tyrian Remastered:Boss A",0,0,300)
        translate()
        popMatrix()
        text(1/DeltaTime,WIDTH/2,HEIGHT-25)
        text((cameraX//1).."  "..(cameraZ//1),WIDTH/2,HEIGHT-50)
    
        px=cameraX//size
        py=cameraZ//size
        found=false
        for z=1,#mat do
            if px==mat[z].x and py==mat[z].y then
                found=true
                break
            end
        end 
        if not found then
            table.insert(mat,vec2(px,py))
            createFloor(vec3((px)*size,0,(py)*size))   
        end
    end
    
    function createFloor(pos)
        local c1=scene:entity()
        c1.model = craft.model.cube(vec3(size,.1,size))
        c1.position=vec3(pos.x+size/2,pos.y,pos.z+size/2)
        c1.material = craft.material("Materials:Standard")
        c1.material.map = readImage(map[math.random(9)])
        c1.material.offsetRepeat=vec4(0,0,size,size)
    end
    
  • Posts: 1,392

    @Simeon - thanks for that neat demo, surprising how little code it took to achieve that. Few things I can apply that to, keep me busy for a while. Thanks again.

  • Posts: 1,392

    @dave1707 - that’s really neat, never seen that done before, it has given me a few ideas. Could be a problem if it keeps on expanding - how easy would it be to duplicate the one you are about to leave beyond the one you are about to move into? That way you could have infinite terrain in just a few graphic areas.

  • dave1707dave1707 Mod
    Posts: 7,912

    @Bri_G That’s what I was doing in a previous version. I was keeping the eight grids around me and deleting the terrain that I didn’t need anymore. But I figured it was better to keep everything. I’m not sure how many grids can be created before the fps starts to drop.

  • Posts: 1,392

    @Simeon - just noted a peculiarity in your scrolling demo. When I run the demo and scroll, say right to left, there is a flickering on with the new tiles being added on the right. Replace Standard mode with Overlay and the flickering disappears. The Overlay mode is drawing more of the tiled surface but appears faster. Is there an issue with calculations in the Standard mode eliminating the tiles below the parameter window?

  • SimeonSimeon Admin Mod
    edited February 9 Posts: 5,054

    @Bri_G odd, I don't see any flickering in STANDARD or FULLSCREEN modes. They are supposed to go all the way to the edges of the screen (you shouldn't be able to see the tiles appear and disappear unless "DebugDraw" is on).

    It could be my iPad screen size (Pro 11") is more neatly divisible by the tileSize.

    Try changing line 27 to this:

        local grid = vec2(math.floor(WIDTH/tileSize)+1, math.floor(HEIGHT/tileSize)+1)
    

    Edit: Or try +2 if that's not enough

  • Posts: 1,392

    @Simeon - bingo, got that in one. The modification made that smooth. Is there any advantage in changing the size of the parameter window?

  • SimeonSimeon Admin Mod
    Posts: 5,054

    Size of the parameter window? As in whether Codea is in Fullscreen or Standard size?

  • dave1707dave1707 Mod
    edited February 9 Posts: 7,912

    @Bri_G Here’s the other scroll I had. It’s set for a 3x3 grid terrain. It creates new terrain in front of you and deletes the terrain behind you to keep you in the middle. It can be changed to 5x5 or 7x7, etc. Because I’m scrolling fast, you can see a slight delay as new terrain is created and the old terrain deleted. If you move at a slower speed, it might not be as noticeable. You can also change the size of each grid. I guess this can be considered an infinite terrain.

    PS. This eventually crashes, but I don’t know if it me or a problem with Codea not releasing memory.

    displayMode(FULLSCREEN)
    
    function setup() 
        map={"Surfaces:Basic Bricks AO","Surfaces:Basic Bricks Color",
        "Surfaces:Basic Bricks Normal","Surfaces:Basic Bricks Roughness",
        "Surfaces:Desert Cliff AO","Surfaces:Desert Cliff Color",
        "Surfaces:Desert Cliff Height","Surfaces:Desert Cliff Normal",
        "Surfaces:Desert Cliff Roughness","Surfaces:Stone Brick Color",
        "Surfaces:Stone Brick Height","Surfaces:Stone Brick Metalness",
        "Surfaces:Stone Brick Normal","Surfaces:Stone Brick Roughness",}
    
        terrainSize=1  -- 1=3x3 grid  2=5x5 grid 3=7x7 grid
        size=30 -- size of each grid   
        hgx=Gravity.x
        speed,ey,ang=0,0,0
        cameraX,cameraZ=0,0
    
        scene = craft.scene()
        scene.camera.position = vec3(cameraX,0,cameraZ)
        scene.camera.eulerAngles=vec3(0,ey,0)
        scene.sun.rotation = quat.eulerAngles(45,0,45)
        scene.ambientColor = color(90,90,90)
    
        skyMaterial = scene.sky.material
        skyMaterial.sky = color(0, 185, 255, 255)
        skyMaterial.horizon = color(116, 226, 198, 255)    
    
        tab={}
        px,py=0,0
        newPosition(vec3(0,0,0))
        fill(255)
    end
    
    function update(dt)
        scene:update(dt)
        scene.camera.position = vec3(cameraX,1,cameraZ)
        scene.camera.eulerAngles=vec3(0,ey,0)
    end
    
    function draw()
        background(0)
        update(DeltaTime)
        scene:draw()
        ey=ey-ang
        local xVel=speed*math.sin(math.rad(ey))
        local zVel=speed*math.cos(math.rad(ey)) 
        local xDir=math.sin(math.rad(ey))
        local zDir=math.cos(math.rad(ey)) 
        cameraX=cameraX+xVel
        cameraZ=cameraZ+zVel    
        local gx=Gravity.x
        ang=ang+(gx-hgx)*4
        hgx=gx
    
        pushMatrix()
        translate(WIDTH/2,HEIGHT/2-300)
        rotate(ang*-30)
        sprite("Tyrian Remastered:Boss A",0,0,300)
        translate()
        popMatrix()
    
        px=cameraX//size
        py=cameraZ//size   
        text(px.."  "..py,WIDTH/2,HEIGHT-25)
        if speed==0 then
            text("tap screen to start",WIDTH/2,HEIGHT-50)
        else
            text("tap screen to stop",WIDTH/2,HEIGHT-50)
        end
        newPosition()
    end
    
    function newPosition()
        r=math.random(1000000)
        for x=-terrainSize,terrainSize do
            for y=-terrainSize,terrainSize do
                a=string.format("%d%d",px+x,py+y)
                if tab[a]==nil then
                    local c=createFloor(vec3(px+x,0,py+y))
                    tab[a]={x=px+x,y=py+y,r=r,c=c}
                else
                    tab[a].r=r
                end
            end
        end 
        if hx~=px or hy~=py then
            oldPosition(hr)
        end
        hr=r  
        hx,hy=px,py
    end
    
    function oldPosition(r)
        for a,b in pairs(tab) do
            if b.r==r then
                tab[a].c:destroy()     
                tab[a]=nil    
            end        
        end
    end
    
    function createFloor(pos)
        local c1=scene:entity()
        c1.model = craft.model.cube(vec3(size,.1,size))
        c1.position=vec3(pos.x*size,pos.y,pos.z*size)
        c1.material = craft.material("Materials:Standard")
        c1.material.map = readImage(map[math.random(#map)])
        c1.material.offsetRepeat=vec4(0,0,size,size)
        return c1
    end
    
    function touched(t)
        if t.state==BEGAN then
            if speed>0 then
                speed=0
            else
                speed=.1
            end
        end
    end
    
  • Posts: 1,392

    @Simeon - forget my last note the issue I was discussing was due, as you pointed out, to the matching of integral tile sizes and will vary from tile size to tile size.

    I always set up in Overlay mode as then you only have two possible set-up orientations to deal with. Which just made me think - in the past someone mentioned setting up their own template - is this still possible? And, if so how?

    Also, you might consider letting people build their own templates, which would all list below the naming field for new projects, so that you can select it when starting a new project. I think this has been suggested before.

  • SimeonSimeon Admin Mod
    Posts: 5,054
    @Bri_G good idea. Maybe I’ll make it if you put projects in a folder called “Template” then they show up in the template picker

    @dave1707 I’m so going to see if I can reproduce this crash with your new project
  • Posts: 1,392

    @dave1707 - just ran your new terrain demo, really smooth - going to see if I can play a few tunes around that. Oh, ran it 20 times for few minutes and stopped it without crashing. Probably memory, which of your machines are you seeing it on and is it consistent across them?

  • dave1707dave1707 Mod
    Posts: 7,912

    @Bri_G I’m using a 16gb iPad Air and that’s what I see all the crashes on. I have a 128gb iPad Pro, but I rarely use it. When I try those programs on it, it doesnt crash. Maybe I just don’t wait long enough.

Sign In or Register to comment.