Powder simulation

Tapping on the screen will create random particles. To change the powder, tap on the buttons on the sides.
Material properties:
Water - flows around and through things randomly and spread out quickly
Grass - mostly stays in place but falls apart without dirt below it
Dirt - solid
Random - one powder, can be water, grass, dirt, powder, or acid
Acid - digs holes in solids and destroys water. Eventually disappears

--# Main

--FallingSand Single Install
--Installer created by @Briarfox
--- This will pull the FallingSand project into Codea for you
-- Instructions:
-- * Create a new project in Codea named FallingSand If you chose another name please change the variable Below
--This is case sensitive
ProjectName = "FallingSand"
-- * Paste this into the Main (not from the raw view, as iSafari will escape special characters)
-- * Make sure there is a single tab in the project
-- * Run and wait for success!
-- If all went well, you should have a FallingSand project now


function setup()
    local jsonCode
    getJsonLib()
end

function getJsonLib()
    local tabs = listProjectTabs()
    if #tabs == 1 then
        print("Attempting to load json...")
        local handleSuccess = function(data)
            --saveProjectTab("json", data)
            jsonCode = data
            --sound(SOUND_POWERUP, 42179)
            print("json code loaded...")
            if jsonCode then
                print("Attempting to pull project...")
                l = loadstring(jsonCode)
                l()
               GetProject() 
            end
        end
        http.request("https://dl.dropboxusercontent.com/s/9e4nvqeu4hsux2q/Json.lua?token_hash=AAFyMB98j4bnt_1gawf9wSke52hsoC7hsIvARcTuZNeOEw&dl=1", handleSuccess)
        end
end

function GetProject()
    local projectCheck = listProjectTabs(ProjectName)
    if #projectCheck ~= 0 then
   local handleSuccess = function(data,i,j)
        local gist = json.decode(data)
        local projName = ProjectName
        if gist.files["1aTabOrder"] then
            print("***Tab Order Found***")
            local taborder = gist.files["1aTabOrder"].content
            local strStart =1
            local strEnd =0
            strStart = string.find(taborder,"#",strEnd)
            strEnd = string.find(taborder,"\
",strStart)
            while strStart do
                local tmp = string.sub(taborder,strStart+1,strEnd-1)
                local name = ProjectName..":"..tmp
                tmp = tmp..".lua"
                saveProjectTab(name,gist.files[tmp].content)
                strStart = string.find(taborder,"#",strEnd)
                strEnd = string.find(taborder,"\
",strStart)
            
            end    
        else
            for k,v in pairs(gist.files) do
                local name = ProjectName .. ":" .. string.gsub(k,".lua","")
                saveProjectTab(name, v.content)
            end
        end
        sound(SOUND_PICKUP, 11797)
        print("Success!")
    end
    local handleFailure = function(data)
        sound(SOUND_EXPLODE, 32351)
        print(data)
        
    end 
    http.request("https://api.github.com/gists/e9e7c8733b4b0c459730030e6911df36",handleSuccess, handleFailure)
    else
        output.clear()
            sound(SOUND_EXPLODE, 32351)
            print([[ERROR
Project name incorrect! 
Please make sure the variable ProjectName = "your project" matches the project name.
This is case sensitive!]])
        end
end

Suggestions on performance would be much appreciated. I want it to perform at least 40fps with grid size WIDTH,HEIGHT.

@em2 I get this error.

Main:327: attempt to call a nil value (global 'hsl2rgb')
stack traceback:
	Main:327: in function <Main:326>

Does it work now? The old link won’t work anymore; use the installer.

It worked for me. That’s a pretty neat simulator!
Frame-rate tip: don’t print stuff into the output every frame or the game will get increasingly laggy

Well, one quick observation on performance: you don’t need to do two deep copies of the grid on every update. Once you’ve made your ‘next’ grid at the beginning of the frame, you can just assign it to ‘grid’ once you’ve finished updating it so it’ll be ready for the next frame.

function Grid:touched(touch) -- Codea does not automatically call this method local cc, hcc = self.cellSize,self.cellSize/2 if touch.state == ENDED then return end local x, y = math.floor(touch.x/cc), math.floor(touch.y/cc) self:set(x, y, true) end
That’ll make a significant difference with lots of simultaneous touches; iterating the whole grid to find the coordinate is clearly not needed, but admittedly not a bottleneck most of the time.

Also current version doesn’t seem to be using the different particles you mention.

Oh yeah I was computing those coordinates slightly wrong. Should really go to bed, but will be trivial to get right.

@xinaes Are you trying to find the x and y of the cell that’s being touched? A while ago (like a year), I made a pixel art painting project. I still have the formula I used in there somewhere!

Here it is! This should work. I haven’t really looked at all of @em2’s code, but if it works the way I think it does, I’m quite certain the below code will work.

size = --Put the amount of cells in each dimenion here
self:set(math.ceil(size/math.min(WIDTH,HEIGHT) * t.x),math.ceil(size/math.min(WIDTH,HEIGHT) * t.y),true)

Edit: Nevermind, that way didn’t work properly. So I tried it like this with the same formula and it seemed to work

self.grid[math.ceil(20/math.min(WIDTH,HEIGHT) * touch.x)][math.ceil(20/math.min(WIDTH,HEIGHT) * touch.y)] = true

Thanks for your suggestions. I should have checked the code I uploaded. When I used AutoGist to back up my project name, I mistyped it (with an extra space). I updated the installer with the new version above.
Hopefully it should work with all the special powder stuff.
P.S. I actually did create my own Pixel Art app. I’ll post the link below.

Link to pixel art: https://codea.io/talk/discussion/8577/pixel-art-1-0-3#latest

This is one of those cool projects people post here where it is really impressive on its own, and even more so because it is so few lines of code!

Is the grass setting supposed to draw static pixels and also draw gravity-affected pixels at the same time? That’s what it does currently. The dirt setting draws only static pixels, which is how I’d assumed grass would work, and it confused me.

You didn’t ask for design suggestions, but I have a couple, for what it’s worth, which is of course not much:

  1. I know the reset button will work as a “clear screen” button, but it would still be nice to have a dedicated “clear screen” button.
  2. The initial setting drops apparently-randomly-colored pixels wherever you draw. I like this mode. It seems like it is impossible to get back to it after you select any of the other materials.
  3. The random button randomly chooses one of the other settings and stays on it, but looking at it I had assumed that it would randomly pick one of the other settings every time you started a new touch. In other words, I assumed its randomness was a persistent setting, not a one-time thing. I think it would be cool if you made it work that way!
  4. I like the small pixel size, but for me it is a little too small for me to feel able to accurately target any one cell with my finger. Personally I would like to see it at 1.5x or 2x the current size.

Overall, great though.

The grass is really supposed to be “held in place” by the dirt, which is static. (grass needs to be planted in dirt). As for the pixel size, I made it small so that playing with it would feel more “powdery”, and I am too lazy to add in zoom features (like in my pixel art project). All in all, however, those are all great suggestions! Thanks a lot, @UberGoober.

I just wanted to be sure it’s working how you intended. It sounds like grass should only fall unless held up by dirt, which isn’t exactly what happens. But also I understand it’s just a fun little thing you did that you may not be interested enough to put more time into. That’s great too.

I just tried installing this project. It gives no error messages, but also does absolutely nothing. The installation just doesn’t happen. Any ideas? I created one tab. You don’t mention whether it needs to be named anything special, so I just named it “Stuff”

@rbytes did you see the comments telling you where to change a string to be the exact name of your project?

If that string isn’t changed, the project won’t get loaded.