Jump to content

Rainbow Colored Car


shaio

Recommended Posts

I'm trying to make a script that changes the color of the car automatically, but I want it to be rainbow colored. I've only gotten it to do random colors. I've seen on many servers where they have rainbow colors, it like switches from turquoise blue to blue and then purple, and then green, and then yellow and what not..

function rgbToggle(player,cmd,value) 
    if not value then 
        outputChatBox("Syntax: /rgb (on/off)",player,0,255,255) 
    end 
    if (value == "on") then 
        setElementData(getPedOccupiedVehicle(player),"rgb","on") 
        outputChatBox("Enabled.",player,0,255,255) 
    elseif (value == "off") then 
        setElementData(getPedOccupiedVehicle(player),"rgb","off") 
        outputChatBox("Disabled.",player,0,255,255) 
    end 
end 
addCommandHandler("rgb",rgbToggle) 
  
setTimer(function() 
    for _,v in ipairs(getElementsByType("vehicle")) do 
        if (getElementData(v,"rgb") == "on") then 
            setVehicleColor(v,math.random(0,255),math.random(0,255),math.random(0,255),math.random(0,255),math.random(0,255),math.random(0,255)) 
        end 
    end 
end,300,0) 

Link to comment

Firstly, in order to the color transition look smooth, you need to do it clientside using the onClient(Pre/Hud)Render family of events. Timers can be too fast for clients with low FPS and too slow for clients with high FPS, so smooth transition is not guaranteed when using them.

About the actual question, I guess that you want your transition to look like a real rainbow. Due to the sun light composition and phenomena which occurs when it hits water in a certain way, a rainbow allows you to see the whole visible color spectrum:

cotr-visible-color-spectrum-of-light-01.gif

As you can see, the highest frecuency (lowest wavelength, 400 nanometers) color is violet, while the lowest frecuency (highest wavelength, 700 nanometers) color is red. We can use interpolateBetween to get the intermediate colors' wavelength and therefore get a rainbow colored car, but how the hell can we transform back the wavelength to actual RGB values usable in setVehicleColor? The answer is simple: the wavelengthToRGBA useful function (I assume truncating the alpha value does not break the color translation in this case).

So the code you need to get everything working is:

-- Ensure this variable gets set to the vehicle you want the effect on 
local veh = getPedOccupiedVehicle(localPlayer) 
  
-- USEFUL FUNCTIONS -- 
local function wavelengthToRGBA(length) 
    -- Copied and pasted from the wiki 
    local r, g, b, factor 
    if (length >= 380 and length < 440) then 
        r, g, b = -(length - 440)/(440 - 380), 0, 1 
    elseif (length < 489) then 
        r, g, b = 0, (length - 440)/(490 - 440), 1 
    elseif (length < 510) then 
        r, g, b = 0, 1, -(length - 510)/(510 - 490) 
    elseif (length < 580) then 
        r, g, b = (length - 510)/(580 - 510), 1, 0 
    elseif (length < 645) then 
        r, g, b = 1, -(length - 645)/(645 - 580), 0 
    elseif (length < 780) then 
        r, g, b = 1, 0, 0 
    else 
        r, g, b = 0, 0, 0 
    end 
    if (length >= 380 and length < 420) then 
        factor = 0.3 + 0.7*(length - 380)/(420 - 380) 
    elseif (length < 701) then 
        factor = 1 
    elseif (length < 780) then 
        factor = 0.3 + 0.7*(780 - length)/(780 - 700) 
    else 
        factor = 0 
    end 
    return r*255, g*255, b*255, factor*255 
end 
  
-- ACTUAL SCRIPT -- 
local startTime = getTickCount() 
local function rainbowColorVehicle() 
    -- Periodic [0, 1] progress value with 3000 ms period 
    local progress = math.fmod(getTickCount() - startTime, 3000) / 3000 
    -- You can invert the color order replacing 400 with 700 and viceversa 
    -- The transition can look more interesting by using another easing function 
    local length = interpolateBetween(400, 0, 0, 700, 0, 0, progress, "Linear") 
    -- Get and apply our color 
    local r, g, b = wavelengthToRGBA(length) 
    setVehicleColor(veh, r, g, b, r, g, b, r, g, b, r, g, b) 
end 
addEventHandler("onClientPreRender", root, rainbowColorVehicle) 

A disclaimer: this might not be the most efficient algorithm out there to achieve this, but providing that you know what the undolatory theory of light is it should be very understandable and easy to modify or mantain.

Link to comment
Firstly, in order to the color transition look smooth...

You're original code didnt work. I tried to make it work both serverside and client side with the cmd i made for it. I want to have an enable and disable feature, to do that I use element data on the vehicle and then loop all vehicles.

function rgbToggle(player,cmd,value) 
    if not value then 
        outputChatBox("Syntax: /rgb (on/off)",player,0,255,255) 
    end 
    if (value == "on") then 
        setElementData(getPedOccupiedVehicle(player),"rgb","on") 
        outputChatBox("Enabled.",player,0,255,255) 
    elseif (value == "off") then 
        setElementData(getPedOccupiedVehicle(player),"rgb","off") 
        outputChatBox("Disabled.",player,0,255,255) 
    else 
        outputChatBox(value.." is not a valid argument!",player,0,255,255) 
    end 
end 
addCommandHandler("rgb",rgbToggle) 
  
local function wavelengthToRGBA(length) 
    local r, g, b, factor 
    if (length >= 380 and length < 440) then 
        r, g, b = -(length - 440)/(440 - 380), 0, 1 
    elseif (length < 489) then 
        r, g, b = 0, (length - 440)/(490 - 440), 1 
    elseif (length < 510) then 
        r, g, b = 0, 1, -(length - 510)/(510 - 490) 
    elseif (length < 580) then 
        r, g, b = (length - 510)/(580 - 510), 1, 0 
    elseif (length < 645) then 
        r, g, b = 1, -(length - 645)/(645 - 580), 0 
    elseif (length < 780) then 
        r, g, b = 1, 0, 0 
    else 
        r, g, b = 0, 0, 0 
    end 
    if (length >= 380 and length < 420) then 
        factor = 0.3 + 0.7*(length - 380)/(420 - 380) 
    elseif (length < 701) then 
        factor = 1 
    elseif (length < 780) then 
        factor = 0.3 + 0.7*(780 - length)/(780 - 700) 
    else 
        factor = 0 
    end 
    return r*255, g*255, b*255, factor*255 
end 
  
local startTime = getTickCount() 
local function rainbowColorVehicle() 
    for _,v in ipairs(getElementsByType("vehicle")) do 
        if (getElementData(v,"rgb") == "on") then 
            local progress = math.fmod(getTickCount() - startTime, 3000) / 3000 
            local length = interpolateBetween(400, 0, 0, 700, 0, 0, progress, "Linear") 
            local r, g, b = wavelengthToRGBA(length) 
            setVehicleColor(v, r, g, b, r, g, b, r, g, b, r, g, b) 
        end 
    end 
end 
addEventHandler("onClientPreRender", getRootElement(), rainbowColorVehicle) 

Link to comment

My code does work, I have just tested it. I wouldn't want to waste the time of both of us with scripts which are wrong, don't you think? :wink:

The problem with the script is that the code I posted is clientside only, and the command handler you added is designed for the serverside, so it won't obviously work because it assumes and requires different arguments to work. Other than that, the code is working like a charm.

Here you have the fixed version of your own code:

function rgbToggle(_,value) 
    if not value then 
        outputChatBox("Syntax: /rgb (on/off)",0,255,255) 
    end 
    if (value == "on") then 
        setElementData(getPedOccupiedVehicle(localPlayer),"rgb","on") 
        outputChatBox("Enabled.",0,255,255) 
    elseif (value == "off") then 
        setElementData(getPedOccupiedVehicle(localPlayer),"rgb","off") 
        outputChatBox("Disabled.",0,255,255) 
    else 
        outputChatBox(value.." is not a valid argument!",0,255,255) 
    end 
end 
addCommandHandler("rgb",rgbToggle) 
      
local function wavelengthToRGBA(length) 
    local r, g, b, factor 
    if (length >= 380 and length < 440) then 
        r, g, b = -(length - 440)/(440 - 380), 0, 1 
    elseif (length < 489) then 
        r, g, b = 0, (length - 440)/(490 - 440), 1 
    elseif (length < 510) then 
        r, g, b = 0, 1, -(length - 510)/(510 - 490) 
    elseif (length < 580) then 
        r, g, b = (length - 510)/(580 - 510), 1, 0 
    elseif (length < 645) then 
        r, g, b = 1, -(length - 645)/(645 - 580), 0 
    elseif (length < 780) then 
        r, g, b = 1, 0, 0 
    else 
        r, g, b = 0, 0, 0 
    end 
    if (length >= 380 and length < 420) then 
        factor = 0.3 + 0.7*(length - 380)/(420 - 380) 
    elseif (length < 701) then 
        factor = 1 
    elseif (length < 780) then 
        factor = 0.3 + 0.7*(780 - length)/(780 - 700) 
    else 
        factor = 0 
    end 
    return r*255, g*255, b*255, factor*255 
end 
      
local startTime = getTickCount() 
local function rainbowColorVehicle() 
    for _,v in pairs(getElementsByType("vehicle", root, true)) do 
        if isElementOnScreen(v) and (getElementData(v,"rgb") == "on") then 
            local progress = math.fmod(getTickCount() - startTime, 3000) / 3000 
            local length = interpolateBetween(400, 0, 0, 700, 0, 0, progress, "Linear") 
            local r, g, b = wavelengthToRGBA(length) 
            setVehicleColor(v, r, g, b, r, g, b, r, g, b, r, g, b) 
        end 
    end 
end 
addEventHandler("onClientPreRender", getRootElement(), rainbowColorVehicle) 

I have also optimized the onClientPreRender handler function loop, because looping through every vehicle created is insane and can generate pretty big slowdowns if your server if full of them and that optimization does not modify how the effect looks.

Link to comment

I tried but i cant get it to go slower, except it like cuts the colors in half, one is different kinds of blue and the rest are like red, yellow, and orange and the blue ends up going slow but the red, yellow, and orange just fly through.

Link to comment

Change the line 51:

local progress = math.fmod(getTickCount() - startTime, 3000) / 3000 

To:

local progress = math.fmod(getTickCount() - startTime, time) / time 

Being the time variable the time in milliseconds you want the transition from red to violet to last (of course, remember that you can replace time with a number like I did, or else you will have to define the variable somewhere in the code). To achieve the effect you want, I would try 6000 ms first and then change that value until it looks nice.

You don't have to use onClientPreRender, you could throw it server side with a very low timer. "50ms" which Is 20x smaller then a secound. I'm not quite sure your eyes could spot the difference.

Doing this kind of effects which need frequent updating in the server is definitely so SAMP-ish and unefficient. It will waste bandwidth by syncing the vehicle color each 50 ms, when the clients can already expect that color and apply it by themselves. And in the server you can't get elements by their type which are streamed in so easily, so it will introduce some CPU overhead too. However, if your server has unlimited bandwidth, a hell of CPU and you feel like doing this clientside will make people complain "HEY Y0UR S3RV3R IS SO LAG PLS FIX" only after knowing it is done on their PCs, computing it on the server is the way to go.

Link to comment

I have tried with 6 and 12 seconds and the color transition works as expected: each and every color phase modifies its duration linearly. Can you describe the effect you want more precisely, please?

In case you are referring to the abrupt color "jump" between red and violet, it can be fixed by inverting the progress value after each period (that is, the first period goes from 0-1, the second from 1-0, the third back from 0-1, and so on):

local startTime = getTickCount() 
local lastProgress = 0 
local alternateOrder = false 
local function rainbowColorVehicle() 
    local progress = math.fmod(getTickCount() - startTime, 3000) / 3000 
    if lastProgress > progress then 
        -- Flip interpolation targets after each "restart" 
        alternateOrder = not alternateOrder 
    end 
    -- Save last progress to calculate the above 
    lastProgress = progress 
    -- Take alternateOrder into account to invert the progress 
    progress = alternateOrder and 1 - progress or progress 
    local length = interpolateBetween(400, 0, 0, 650 --[[ Reduced 50 nm here to make the apparent red color be visible less time ]], 0, 0, progress, "Linear") 
    local r, g, b = wavelengthToRGBA(length) 
    for _,v in pairs(getElementsByType("vehicle", root, true)) do 
        if isElementOnScreen(v) and (getElementData(v,"rgb") == "on") then 
            setVehicleColor(v, r, g, b, r, g, b, r, g, b, r, g, b) 
        end 
    end 
end 
addEventHandler("onClientPreRender", getRootElement(), rainbowColorVehicle) 

Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...