I Imagine A Color Picker Has Been Done A Thousand Times

General discussion about LÖVE, Lua, game development, puns, and unicorns.
Post Reply
User avatar
kingnoob
Prole
Posts: 29
Joined: Thu Dec 21, 2023 6:52 am

I Imagine A Color Picker Has Been Done A Thousand Times

Post by kingnoob »

I just did one as part of my learning curve.
press "r" and ("down arrow" or "up arrow")
or
press "g" and ("down arrow" or "up arrow")
or
press "b" and ("down arrow" or "up arrow")
to adjust the color levels.

Code: Select all

function love.load()
   love.window.setFullscreen(true, "desktop")
   width, height = love.window.getDesktopDimensions(1)
   red = 512
   green = 512
   blue = 512
end

function love.update(dt)
    down = love.keyboard.isDown("escape")
    if down == true then
       love.event.quit() 
    end
    down = love.keyboard.isDown("r")
    if down == true then
        down = love.keyboard.isDown("up")
        if down == true then
            red = red + 1
            if red > 1020 then red = 1020 end
        end
        down = love.keyboard.isDown("down")
        if down == true then
            red = red - 1
            if red < 0 then red = 0 end
        end
    end
    down = love.keyboard.isDown("g")
    if down == true then
        down = love.keyboard.isDown("up")
        if down == true then
            green = green + 1
            if green > 1020 then green = 1020 end
        end
        down = love.keyboard.isDown("down")
        if down == true then
            green = green - 1
            if green < 0 then green = 0 end
        end
    end 
    down = love.keyboard.isDown("b")
    if down == true then
        down = love.keyboard.isDown("up")
        if down == true then
            blue = blue + 1
            if blue > 1020 then blue = 1020 end
        end
        down = love.keyboard.isDown("down")
        if down == true then
            blue = blue - 1
            if blue < 0 then blue = 0 end
        end
    end  
end

function love.draw()
    love.graphics.setColor(1, 0, 0)
    love.graphics.print("Red", width/2-200, height/2-200, 0, 2, 2)
    love.graphics.print(math.floor(red/4), width/2, height/2-200, 0, 2, 2)
    love.graphics.setColor(0, 1, 0)
    love.graphics.print("green", width/2-200, height/2-160, 0, 2, 2)
    love.graphics.print(math.floor(green/4), width/2, height/2-160, 0, 2, 2)    
    love.graphics.setColor(0.24, 0.24, 1)
    love.graphics.print("blue", width/2-200, height/2-120, 0, 2, 2)
    love.graphics.print(math.floor(blue/4), width/2, height/2-120, 0, 2, 2)    

    love.graphics.setColor(red/1020, green/1020, blue/1020)
    love.graphics.rectangle("fill", width/2-200, height/2-80, 400, 400)
end
User avatar
milon
Party member
Posts: 472
Joined: Thu Jan 18, 2018 9:14 pm

Re: I Imagine A Color Picker Has Been Done A Thousand Times

Post by milon »

Nice job!

I did a bit of tweaking and added comments to explain my changes. You didn't do anything wrong - I just like passing on things I've learned.
Also, you can now use PageUp and PageDown to increase/decrease faster. :)

Code: Select all

-- local variables declared outside of functions are accessible within functions too (within the same .lua file)
-- also, defining them as local variables avoids accumulating a ton of globals (which generally doesn't matter until you're working with multiple files)
local square, ry, gy, by, y = {} -- the empty table gets assigned to square and the rest remain nil variables, but they exist!
love.window.setMode(800, 600, {resizable=true}) -- just showing off how resize works; do whatever you like, of course
local font = love.graphics.newFont(20) -- just to make it easier to read
love.graphics.setFont(font) -- remember to actually use the font  :)
local lineHeight = font:getHeight() -- used to calculate label placement; see https://love2d.org/wiki/Text:getHeight

local red =   255 -- you're using a 0-255 range anyway, so don't bother with extra conversions
local green = 255
local blue =  255

function love.load()
	love.resize(800, 600)
	-- about the only use I have for love.load is to call various init functions after everything is defined and loaded
	-- this same function call could come anytime after love.resize() is defined
	-- if it doesn't get called, then the square (etc) values aren't defined and love.draw() crashes
end

function love.resize(w, h)
    -- this gets called automatically when the window is resized (including if you start in fullscreen mode)
    -- let's use it for all the dimension calculations, rather than re-calculate every frame
    
    -- square
    square.x = math.floor(w / 3)
    square.w = square.x
    square.y = math.floor(h / 3)
    square.h = square.y
    
    -- label positions
    -- we only need the y-value since x is the same for everything
    by = square.y - lineHeight * 2
    gy = by - lineHeight * 1.5
    ry = gy - lineHeight * 1.5
    y = ry - lineHeight * 1.5
end

function love.update(dt)
    -- note that I got rid of the 'down' variable completely, and just use an if structure instead
    
    if love.keyboard.isDown("escape") then love.event.quit() end
    
    -- red
    if love.keyboard.isDown("r") then
        if love.keyboard.isDown("up") and red < 255 then -- instead of changing the variable and then fixing it, let's just make sure it's okay to change it before actually doing so
            red = red + 1
        elseif love.keyboard.isDown("down") and red > 0 then -- use one elseif instead of multiple if blocks. A user *could* press both Up and Down at the same time.
            red = red - 1
        elseif love.keyboard.isDown("pageup") and red < 246 then -- we can use pageup/pagedown to navigate faster too
            red = red + 10
        elseif love.keyboard.isDown("pagedown") and red > 9 then
            red = red - 10
        end
    end
    
    -- green
    if love.keyboard.isDown("g") then
	    -- this isn't really the best way to structure things, but at least it's clear and extensible
        if love.keyboard.isDown("up") and green < 255 then
            green = green + 1
        elseif love.keyboard.isDown("down") and green > 0 then
            green = green - 1
        elseif love.keyboard.isDown("pageup") and green < 246 then
            green = green + 10
        elseif love.keyboard.isDown("pagedown") and green > 9 then
            green = green - 10
        end
    end
    
    -- blue
    if love.keyboard.isDown("b") then
        if love.keyboard.isDown("up") and blue < 255 then
            blue = blue + 1
        elseif love.keyboard.isDown("down") and blue > 0 then
            blue = blue - 1
        elseif love.keyboard.isDown("pageup") and blue < 246 then
            blue = blue + 10
        elseif love.keyboard.isDown("pagedown") and blue > 9 then
            blue = blue - 10
        end
    end
end

function love.draw()
    local label
    
    -- headers
    love.graphics.setColor(1, 1, 1)
    label = "COLOR  INT        HEX    DEC"
    love.graphics.print(label, square.x, y)
    
    -- red
    love.graphics.setColor(1, 0, 0)
    label = "red        "..red.."       0x"..string.format("%x", red).."     "..(red/255)
    -- NOTE: doing a lot of string concatenation is actually bad performance.  It's fine for this, but for performance-critical code it's better to put all the strings in order in a table and then call table.concat(tableName) instead.
    if love.keyboard.isDown("r") then label = label:upper() end
    love.graphics.print(label, square.x, ry)
    
    -- green
    love.graphics.setColor(0, 1, 0)
    label = "green    "..green.."       0x"..string.format("%x", green).."     "..(green/255)
    if love.keyboard.isDown("g") then label = label:upper() end
    love.graphics.print(label, square.x, gy)
    
    -- blue
    love.graphics.setColor(0, 0, 1)
    label = "blue      "..blue.."       0x"..string.format("%x", blue).."     "..(blue/255)
    if love.keyboard.isDown("b") then label = label:upper() end
    love.graphics.print(label, square.x, by)
    
    -- color square
    love.graphics.setColor(red/255, green/255, blue/255)
    love.graphics.rectangle("fill", square.x, square.y, square.w, square.h)
end
Any code samples/ideas by me should be considered Public Domain (no attribution needed) license unless otherwise stated.
RNavega
Party member
Posts: 355
Joined: Sun Aug 16, 2020 1:28 pm

Re: I Imagine A Color Picker Has Been Done A Thousand Times

Post by RNavega »

When it comes to reacting to keys you have love.keyboard.isDown(), as well as the love.keypressed() / love.keyreleased() handlers.
Using milon's great example, I wanted to test if the same can be done with those handlers. I mean, how they compare to using isDown().

What I found was: there's more code involved because you're forced to keep track of the presses yourself, like remembering what action(s) that the user is doing on that frame (kind of the same job that love.keyboard.isDown() was already doing for you).

Code: Select all

local square = {}
local ry, gy, by, y
love.window.setMode(800, 600, {resizable=true})
local font = love.graphics.newFont(20)
love.graphics.setFont(font)
local lineHeight = font:getHeight()

local red =   255
local green = 255
local blue =  255

local SPEED_NORMAL = 1.0
local SPEED_FAST = 10.0

local actions = {
    changeR = false,
    changeG = false,
    changeB = false,
    positiveSpeed = 0.0,
    negativeSpeed = 0.0,
}


function love.load()
	love.resize(800, 600)
end


function love.resize(w, h)
    square.x = math.floor(w / 3)
    square.w = square.x
    square.y = math.floor(h / 3)
    square.h = square.y

    by = square.y - lineHeight * 2
    gy = by - lineHeight * 1.5
    ry = gy - lineHeight * 1.5
    y = ry - lineHeight * 1.5
end


function love.keypressed(key)
    if key == 'escape' then
        love.event.quit()
    elseif key == 'r' then
        actions.changeR = true
    elseif key == 'g' then
        actions.changeG = true
    elseif key == 'b' then
        actions.changeB = true
    elseif key == 'up' then
        actions.positiveSpeed = SPEED_NORMAL
    elseif key == 'down' then
        actions.negativeSpeed = -SPEED_NORMAL
    elseif key == 'pageup' then
        actions.positiveSpeed = SPEED_FAST
    elseif key == 'pagedown' then
        actions.negativeSpeed = -SPEED_FAST
    end
end


function love.keyreleased(key)
    if key == 'escape' then
        love.event.quit()
    elseif key == 'r' then
        actions.changeR = false
    elseif key == 'g' then
        actions.changeG = false
    elseif key == 'b' then
        actions.changeB = false
    elseif key == 'up' or key == 'pageup' then
        actions.positiveSpeed = 0.0
    elseif key == 'down' or key == 'pagedown' then
        actions.negativeSpeed = 0.0
    end
end


function love.update(dt)
    local colorChangeSpeed = actions.positiveSpeed + actions.negativeSpeed
    
    if actions.changeR then
        red = red + colorChangeSpeed
        if red > 255 then
            red = 255
        elseif red < 0 then
            red = 0
        end
    end
    if actions.changeG then
        green = green + colorChangeSpeed
        if green > 255 then
            green = 255
        elseif green < 0 then
            green = 0
        end
    end
    if actions.changeB then
        blue = blue + colorChangeSpeed
        if blue > 255 then
            blue = 255
        elseif blue < 0 then
            blue = 0
        end
    end
end


function love.draw()
    local label

    love.graphics.setColor(1, 1, 1)
    label = "COLOR  INT        HEX    DEC"
    love.graphics.print(label, square.x, y)

    love.graphics.setColor(1, 0, 0)
    label = "red        "..red.."       0x"..string.format("%x", red).."     "..(red/255)
    if actions.changeR then label = label:upper() end
    love.graphics.print(label, square.x, ry)

    love.graphics.setColor(0, 1, 0)
    label = "green    "..green.."       0x"..string.format("%x", green).."     "..(green/255)
    if actions.changeG then label = label:upper() end
    love.graphics.print(label, square.x, gy)

    love.graphics.setColor(0, 0, 1)
    label = "blue      "..blue.."       0x"..string.format("%x", blue).."     "..(blue/255)
    if actions.changeB then label = label:upper() end
    love.graphics.print(label, square.x, by)

    love.graphics.setColor(red/255, green/255, blue/255)
    love.graphics.rectangle("fill", square.x, square.y, square.w, square.h)
end
Besides that, the main difference between the two methods seems to be with how you want to react to the keys in your code: when using isDown() you already know what key that you want to test: you're testing for a specific key, even if its name is stored in a variable.
With keypressed() / keyreleased() you don't know what the user will press, and you will do tests to see if that incoming key event is relevant or not (if it represents a certain action in your game).

I'm not sure which method is best suited for a more complex game. Interested in hearing others' opinions on this. Edit: more discussion in this thread: viewtopic.php?p=258098
Last edited by RNavega on Tue Dec 26, 2023 3:44 pm, edited 1 time in total.
User avatar
kingnoob
Prole
Posts: 29
Joined: Thu Dec 21, 2023 6:52 am

Re: I Imagine A Color Picker Has Been Done A Thousand Times

Post by kingnoob »

Nice coding by both! :) However, both are too fast on my machine. Really nice idea to hold down more than one color key at a time. Page down works with two colors but page up does not. So just a minor bug. There are rgb color list online that gives names to various colors. Like SlateBlue rgb(106, 90, 205) or ForestGreen rgb(34, 139, 34) etc. It would be cool if the colors that have names would display their name. Anyway, I'm on to new adventures. Thanks!
User avatar
pgimeno
Party member
Posts: 3656
Joined: Sun Oct 18, 2015 2:58 pm

Re: I Imagine A Color Picker Has Been Done A Thousand Times

Post by pgimeno »

RNavega wrote: Sat Dec 23, 2023 4:35 pm I'm not sure which method is best suited for a more complex game. Interested in hearing others' opinions on this.
If you don't care missing presses or releases within a single frame, then you're good using isScancodeDown (generally speaking, always avoid isDown, for the benefit of non-QWERTY keyboard users).

That's seldom the case, because it's uncommon to press and release a key in the same frame, unless your game's frame rate is low or has lag spikes. But love.keypressed/keyreleased guarantees that these will be caught.

Edit: Sorry, I missed the other thread. I always read threads in chronological order. But the bit about avoiding isDown still applies.
RNavega
Party member
Posts: 355
Joined: Sun Aug 16, 2020 1:28 pm

Re: I Imagine A Color Picker Has Been Done A Thousand Times

Post by RNavega »

pgimeno wrote: Mon Dec 25, 2023 12:53 pm Edit: Sorry, I missed the other thread. I always read threads in chronological order. But the bit about avoiding isDown still applies.
I would've quoted you in that thread in any case heh. Thanks.
Post Reply

Who is online

Users browsing this forum: Ahrefs [Bot], Bing [Bot] and 0 guests