I need help making a faster way to draw this...

Back to Codea! The title says it all, the 2D Minecraft Project I’m doing is going to require WAAY more drawing than the simple thing I have so far, so I need to find a way to draw things faster so that the framerate is not affected. Here’s the code:

-- 2D Minecraft

-- Use this function to perform your initial setup
function setup()
    displayMode(FULLSCREEN)
    supportedOrientations(LANDSCAPE_ANY)
    
    version = 0.1
    saveProjectInfo("Author","Enro Corp")
    saveProjectInfo("Description", "My quest to create a fun 2D Minecraft begins...")
    saveProjectData("Version",version)
    
    worldCreate:init()
    
end

-- This function gets called once every frame
function draw()
    -- This sets a dark background color 
    background(0)

    -- This sets the line thickness
    strokeWidth(5);rectMode(CENTER);
    noStroke();fill(255)
    font("ArialMT");fontSize(10)
    text("Version "..readProjectData("Version"),WIDTH/2,HEIGHT - 50)

    -- Do your drawing here
    worldCreate:draw()
    
    for r,row in ipairs(level) do
        for c,cell in ipairs(row) do
            if r == 10 then
                local grass,img
                grass = mesh()
                img = readImage("Documents:blockGrass")
                grass.texture = img
                grass:addRect(-16 + c*32,-16 + r*32,32,32)
                grass:draw()
            else
                local dirt,img
                dirt = mesh()
                img = readImage("Documents:blockDirt")
                dirt.texture = img
                dirt:addRect(-16 + c*32,-16 + r*32,32,32)
                dirt:draw()
            end
        end
    end
    
end

WORLD CREATE CLASS

worldCreate = class()

function worldCreate:init()
    -- you can accept and set parameters here
    height = 102
    width = 10
    level = {}
    index = {}
    
    for i = 1,width do
        level[i] = {}
        for j = 1,height do
            level[i][j] = math.random(6)
        end
    end
    
    for i = 1,width do
        index[i] = {}
        for j = 1,height do
            index[i][j] = math.random(6)
        end
    end

end

function worldCreate:draw()
    -- Codea does not automatically call this method
    for r,row in ipairs(index) do
        for c,cell in ipairs(row) do
            text(r.."/"..c, WIDTH - width*40 + c*35,HEIGHT - height*40 + r*35)
        end
    end
    
end

Sounds like the first thing you should aim for is to draw all your similar blocks (e.g. dirt) as one mesh. So you have a structure like:

blockMeshes = {}

blockMeshes["dirt"] = mesh()
blockMeshes["dirt"]:addRect( ... )
...
blockMeshes["dirt"]:draw() -- one draw for all dirt blocks

Then you can take this even further and pack all your dirt, grass, stone and other textures into one image. Then you can use one mesh to render all your blocks. This would be the fastest solution.

Edit: Also I would suggest constructing your meshes once, outside your draw function. Then modifying them as needed on interaction.

Have a look at this previous discussion which compares meshes and sprites for an example of what @Simeon is suggesting

http://twolivesleft.com/Codea/Talk/discussion/1378/sprites-vs-meshes/p1

Like this?
(Didn’t test the code, away from Codea atm)

-- Mesh Atlas

-- Use this function to perform your initial setup
function setup()
    print("Hello World!")
    texmesh = mesh()
    
end

-- This function gets called once every frame
function draw()
    -- This sets a dark background color 
    background(0)

    -- This sets the line thickness
    img = readImage("Documents:Minecraft Tileset")
    texmesh.texture = img
    texmesh.addRect(WIDTH/2,HEIGHT/2,32,32)
    texmesh:texCoord(img,12,12)
    texmesh:draw()

    -- Do your drawing here
    
end


okay so I’m using Codea again and I feel like I’m really close to figuring it out. But I havn’t. Here’s what I think I’m supposed to be doing:

dirtmesh = mesh()
img = readImage("Documents:Minecraft Tileset")
dirtmesh.texture = img
idx = dirtmesh:addRect(WIDTH/2, HEIGHT/2, 32, 32)
dirtmesh:setRectTex(idx,0,0,1,1)

So the “0,0,1,1” gives me the entire tileset, whenever I change some of the digits (for example: “0, .480, .32, .32” I get the image zoomed up and stuff which is what I’m assuming you guys told me to do. make different submeshes that all use a super image filled with all the minecraft tiles I’ll need.
So my problem is that I have no idea what to put in the “setRectTex()” to make the mesh be on the appropriate image. Why doesn’t it go by pixel coordinates? I don’t think it does because I’ve put in “0 [for the left edge], 480 [for the bottom edge] and 32,32 [for the size that the tile is]” and I just got some weird tv static image but with random colors from my tileset O.O

Please help me better understand so that I can put in the proper coordinates

edited
Was writing this while you posted the above. You are pretty much there!

Couple of changes


-- Mesh Atlas

-- Use this function to perform your initial setup
function setup()
    texmesh = mesh()
    img = readImage("Cargo Bot:Codea Icon")
    texmesh.texture = img
    
end

-- This function gets called once every frame
function draw()
    -- This sets a dark background color 
    background(0)
    texmesh:clear()
    -- This sets the line thickness
 
    local idx = texmesh:addRect(WIDTH/2,HEIGHT/2,32,32)
    texmesh:setRectTex(idx,0,0,1,1)
  
    
    texmesh:draw()

    -- Do your drawing here
    
end

The important bit for selecting a subsection of your image is the line

    texmesh:setRectTex(idx,0,0,1,1)

idx is the identifier for the rectangle you have just added to the mesh, the next two values 0,0 are the bottom left location of the portion of the image you want to add and the next two are the size of the portion of the image. These values are in proportion to the full image.

For example

    texmesh:setRectTex(idx,0,0,0.5,1)

Will display the left half of the image while

    texmesh:setRectTex(idx,0.5,0.5,0.5,0.5)

Will display the top right hand quarter of the image.

These will be scaled and stretched to fit the size used in addRect on the previous line.

Okay so setRectTex() takes values from 0 to 1, where 0 is the first pixel on your axis, and 1 is the last.

Say your Minecraft tileset image is 500x500 pixels. It’s divided into a grid 100 individual tiles, each tile is 50x50 pixels. Your dirt tile is the first tile in the image, the one in the upper left corner.

You can refer to it like this:

idx = dirtmesh:addRect(WIDTH/2, HEIGHT/2, 32, 32)
dirtmesh:setRectTex(idx, 0, 0, 50/500, 50/500)

What is this saying? It’s saying set the span of texture to use on the rect to go from (0,0) in your texture coordinate space, to (50/500, 50/500) — or (0.1, 0.1). Because 50 pixels across in a 500-pixel-wide texture is 10% (0.1) of the texture.

If you wanted to refer to the second tile in your tileset texture, you could do it like this:

idx = dirtmesh:addRect(WIDTH/2, HEIGHT/2, 32, 32)
dirtmesh:setRectTex(idx, 50/500, 0, 50/500, 50/500)

It’s starting to look like a pattern. We can now write a generic function to index any of our 100 tiles in this hypothetical Minecraft tileset image:

function setMeshTile( theMesh, rectIndex, img, tileX, tileY )
    -- We assume our tiles are 50x50 pixels square
    local tileSize = 50

    -- tileX and tileY are assumed to start at 1, 1 and go up to 10, 10

    theMesh:setRectTex( rectIndex, (tileX - 1) * (tileSize/img.width),
                                   (tileY - 1) * (tileSize/img.height),
                                   (tileSize/img.width),
                                   (tileSize/img.height) )
end

okay so with you guys’ help I’ve changed my code a bit to do what you showed me.

-- Mesh Atlas

-- Use this function to perform your initial setup
function setup()
    // mts = Minecraft TileSet
    mts = {}
    
    mts["dirt"] = mesh()
    idx = mts["dirt"]:addRect(16,HEIGHT/2,32,32)
    
    mts["index"] = mesh()
    idi = mts["index"]:addRect(WIDTH/2,HEIGHT - 128,256,256)
    
    
    img = readImage("Documents:Minecraft Tileset")
    
end

-- This function gets called once every frame
function draw()
    -- This sets a dark background color 
    background(0)

    -- This sets the line thickness
    noSmooth() --gives it the trademark blocky texture
    --VARIABLE
    mts["dirt"]:setRectTex(idx,3/16,15/16,32/512,32/512)
    mts["dirt"].texture = img
    mts["dirt"]:draw()

    -- Do your drawing here
    
end

I have it set to the grass and I just have to change the “3/16” to “2/16” because the dirt texture is the second image out of the 16 images in the first row.

How exactly do I get my code to make dirt and grass rectangles all across the Width of the screen?

btw thanks you guys I appreciate it and hopefully once I’m started off I’ll be able to do some of this on my own :slight_smile:

Okay, can you guys give me an example of a touch function that works with tables?

I’m not sure I’m explaining my request properly :\

worldCreate = class()

function worldCreate:init()
    -- you can accept and set parameters here
    wWidth = WIDTH/64
    wHeight = (HEIGHT/4)/32
    world = {}
    
    for r = 1, wWidth do
        world[r] = {}
        for c = 1,wHeight do
            world[r][c] = math.random(2)
        end
    end
end

function worldCreate:draw()
    -- Codea does not automatically call this method
    
    pushStyle()
    noStroke();noSmooth()
    for r,row in pairs(world) do
        for c,col in pairs(row) do
            if c == wHeight then
                fill(0,255,0)
                rect(-64 + (r*64),-64 + c*64,64,64)
            elseif c < wHeight and c > wHeight/2 and col == 2 then
                fill(175, 166, 147, 255)
                rect(-64 + (r*64),-64 + c*64,64,64)
            elseif c < wHeight/2 and c > 0 and col == 1 then
                fill(125, 121, 114, 255)
                rect(-64 + (r*64),-64 + c*64,64,64)
            else
                fill(148, 95, 25, 255)
                rect(-64 + (r*64),-64 + c*64,64,64)
            end
        end
    end
    popStyle()

my v0.1 (without textures for now)

How do I go about making a touch function to “destroy” one of the rectangles if you touch that specific one? On the ENDED touch state

I have written code for this, try =

worldCreate = class()

function worldCreate:init()
    -- you can accept and set parameters here
    wWidth = WIDTH/64
    wHeight = (HEIGHT/4)/32
    self.world = {}
    
    for r = 1, wWidth do
        self.world[r] = {}
        for c = 1,wHeight do
            self.world[r][c] = math.random(2)
        end
    end
end

function worldCreate:draw()
    -- Codea does not automatically call this method
    pushStyle()
    noStroke();noSmooth()
    for r,row in pairs(self.world) do
        for c, col in pairs(row) do
            if c == wHeight then
                fill(0,255,0)
                rect(-64 + (r*64),-64 + c*64,64,64)
            elseif c < wHeight and c > wHeight/2 and col == 2 then
                fill(175, 166, 147, 255)
                rect(-64 + (r*64),-64 + c*64,64,64)
             elseif c < wHeight/2 and c > 0 and col == 1 then
                fill(125, 121, 114, 255)
                rect(-64 + (r*64),-64 + c*64,64,64)
            else
                fill(148, 95, 25, 255)
                rect(-64 + (r*64),-64 + c*64,64,64)
            end
        end
    end
    popStyle()
end

function worldCreate:touched(touch)
    -- This is where the magic happens
    if touch.y < HEIGHT / 2 and touch.state == ENDED then
        print("Co-ordinates of your touch = ("..math.ceil(touch.x / 64)..", "..math.ceil(touch.y / 64)..")")
        self.world[math.ceil(touch.x / 64)][math.ceil(touch.y / 64)] = nil -- remove the entry from the table
    end
end

function setup()
    world = worldCreate()
end

function draw()
    background(0, 0, 0, 255) -- try remove this ;)
    world:draw()
end

function touched(touch)
    world:touched(touch)
end

The following is untested as I don’t have my iPad with me.

You need a function to read in the screen position where you have touched, something along the lines of:

if currentTouch.state==ENDED then
  lastx=currentTouch.x
  lasty=currentTouch.y
end

Then inside your second for loop (the one one iterating through the columns) check to see if the lastx and lasty values are within the rectangle being created, if they are then set world[r][c]=0 - this will make the rectangle appear as it does in the final “else” part (I assume that’s what you mean by destroy)

The if statement might look something like:

if lastx>(r*64)-64 and lastx<r*64 and lasty>(c*64)-64 and lasty<(r*64) then
  world[r][c]=0
end

Another thread of interest might be this one, which uses sprites

http://twolivesleft.com/Codea/Talk/discussion/comment/11214#Comment_11214

YEEEEESS! to @Jordan!!! Whoo!

Glad to have helped! :smiley: Feel free to use it (I have no licenses or policies or what-what, I’m only 13)

:[ Daaaaarn! I need help again. I swear I looked the code over and there shouldn’t be a reason as to why it doesn’t work when I combined what I learned with the meshes (which render fast enough, btw! Thanks to EVERYONE here). Here’s my worldCreate() class

worldCreate = class()

function worldCreate:init()
    -- you can accept and set parameters here
    wWidth = WIDTH/32 + 2
    wHeight = HEIGHT/2/32
    world = {}
    id = {}
    blocks = {}
    img = readImage("Documents:Minecraft Tileset")
    blocks["grass"] = mesh()
    blocks["dirt"] = mesh()

    for i = 1,wWidth do
        world[i] = {}
        for j = 1, wHeight do
            world[i][j] = math.random(6)
        end
    end
    
    for r,row in pairs(world) do
        for c,col in pairs(row) do
            if c == wHeight then
                id[r] = blocks["grass"]:addRect(-64 + r*32,-32 + c*32,32,32)
                blocks["grass"]:setRectTex(id[r], 3/16,15/16,32/512,32/512)
                blocks["grass"].texture = img
            else
                id[r] = blocks["dirt"]:addRect(-64 + r*32,-32 + c*32,32,32)
                blocks["dirt"]:setRectTex(id[r],2/16,15/16,32/512,32/512)
                blocks["dirt"].texture = img
            end
        end
    end
end

function worldCreate:draw()
    -- Codea does not automatically call this method
    blocks["grass"]:draw()
    blocks["dirt"]:draw()
end

function worldCreate:touched(touch)
    -- Codea does not automatically call this method
    if touch.state == ENDED then
        world[math.ceil(touch.x/32)][math.ceil(touch.y/32)] = nil
    end
end

Hey @RichGala1, I’m assuming you have functions setup(), draw(), and touched() somewhere else, right? And this would be much easier if you posted a link to your “minecraft tileset”

Yes to both those things. I’m gonna see where I’ll put the tileset real quick

As @Jordan said have you instantiated your class? Something like this in your main class

function setup()
    myW=worldCreate()
end

-- This function gets called once every frame
function draw()
    -- This sets a dark background color 
    background(40, 40, 50)

    -- Do your drawing here
    myW:draw()
end

Yeah don’t worry I’m not tht noob anymore :slight_smile:
I put the pic on my twitter. Here’s the link:

pic.twitter.com/55gRt1rj

Everything shows up but I guess the problem is that the “rect()” function was directly connected to my table so it would easily erase with “nil” But my the mesh only needs to set up it’s rectangles and texture stuff once and it’s good to go.
“Nil” won’t get rid of it because it may take it away the rectangle setup from the table but I don’t want to get rid of the setup but that actual part of the mesh.

I suck at explaining myself > <

Changed the code a little. Other than adding new blocks at certain heights and making the framerate go up it isn’t any much different.

worldCreate = class()

function worldCreate:init()
    -- you can accept and set parameters here
    wWidth = WIDTH/32 + 2
    wHeight = HEIGHT/2/32
    world = {}
    id = {}
    blocks = {}
    img = readImage("Documents:Minecraft Tileset")
    blocks["grass"] = mesh()
    blocks["dirt"] = mesh()
    blocks["stone"] = mesh()

    for i = 1,wWidth do
        world[i] = {}
        for j = 1, wHeight do
            world[i][j] = math.random(6)
        end
    end
    
    for r,row in pairs(world) do
        for c,col in pairs(row) do
            if c == wHeight then
                id[r] = blocks["grass"]:addRect(-64 + r*32,-32 + c*32,32,32)
                blocks["grass"]:setRectTex(id[r], 3/16,15/16,32/512,32/512)
                blocks["grass"].texture = img
            elseif c < (wHeight - 2) and c >= 7 and col > 5 then
                id[r] = blocks["stone"]:addRect(-64 + r*32,-32 + c*32,32,32)
                blocks["stone"]:setRectTex(id[r], 6/16,15/16,32/512,32/512)
                blocks["stone"].texture = img
            else
                id[r] = blocks["dirt"]:addRect(-64 + r*32,-32 + c*32,32,32)
                blocks["dirt"]:setRectTex(id[r],2/16,15/16,32/512,32/512)
                blocks["dirt"].texture = img
            end
        end
    end
end

function worldCreate:draw()
    -- Codea does not automatically call this method
    blocks["grass"]:draw()
    blocks["dirt"]:draw()
    blocks["stone"]:draw()
end

function worldCreate:touched(touch)
    -- Codea does not automatically call this method
    if touch.state == ENDED then
        world[math.ceil(touch.x/32)][math.ceil(touch.y/32)] = nil
    end
end

and so you don’t worry :slight_smile:

-- 2DMC Mechanics

-- Use this function to perform your initial setup
function setup()
    displayMode(FULLSCREEN)
    worldCreate:init()
    time = 0
    fc = 0
end

-- This function gets called once every frame
function draw()
    -- This sets a dark background color 
    background(152, 213, 223, 225)
    fc = fc + 1
    time = time + DeltaTime
    text(math.floor(fc/time+ 0.5),WIDTH/2,HEIGHT - 50)

    worldCreate:draw()
end

function touched(touch)
    worldCreate:touched(touch)
end