BGE: simple day and night cycles using bgl

As part of creating an engrossing game world you often need to be able to manipulate the game environment. In this tutorial we’ll build on the worldTime.py class from last time and use it to drive a simple day and night cycle. We’ll look at 2 parametric functions (sine wave and semicircles) to create the day and night transition and use bgl to change the background colour.

Setting the sun

Here I’ve used an empty at the centre of the game world and parented a brightly coloured sphere and a sun lamp to it. Then, as the empty rotates as a fraction of time passed in the game, the sun and lamp rotate to. This is all handled by a short python script attached to the empty:

import bge

DEG_TO_RAD = 0.01745

own = bge.logic.getCurrentController().owner

t = (bge.time.hour+bge.time.minute/60)*(-15*DEG_TO_RAD)

own.worldOrientation = [0,t,0]

Just like the previous time tutorial, the time class is instanced in another script and assigned as a bge global variable for use in other scripts. For the sun to go the over way, just change -15 to 15. Why is the step size 15? Because 360/24 = 15.

Finally, as a last little touch, there is one final sun lamp, with a low energy value and set to a bluish colour so that when the main sun is out of view some light is still cast in the scene. We could add some blur/bloom effects to the sun and adjust it’s colour as it moves around, but I’ll leave that to you to figure out.

Painting the sky

import bgl
import bge
import math

scene = bge.logic.getCurrentScene()

def skyColour():
    # sky colours for day, night and the difference between them
    night = [0.05, 0.035, 0.063]
    day = [0.103, 0.499, 1]
    diff = [n-d for n,d in zip(night, day)]

    # create a sine wave that repeats after 24 hours of game time
    # the final value, y, should be between 0 and 1
    a = -0.5
    h = 0.5
    c = 1
    b = (2*math.pi)
    x = math.sin((b * (bge.time.hour+bge.time.minute/60)/24) + c)
    y = a*x+h

    r = night[0] - diff[0]*y
    g = night[1] - diff[1]*y
    b = night[2] - diff[2]*y
    bgl.glClearColor(r, g, b, 1)
    bgl.glClear(bgl.GL_COLOR_BUFFER_BIT)
    return

scene.pre_draw.append(skyColour)

The function skyColour() decides the current colour of the background based on the game time. We list the RBG colour of both the day and night sky and subtract one from another to get the difference between the two. We then need to take the time elapsed in the game and turn this into a fraction of the difference between the sky colours and then add this fraction to the base colour (night). To do this we use a sine wave.

sunAsSine

Since a sine wave repeats its self over a period we can define, with peaks and troughs it is perfect for our needs. We can define a sine wave as:

y = A sin(Bx+C)+h

Where, A is the amplitude, B is the period (2π), x is the x axis coordinate, C is the phase shift and h is the horizontal shift. With some tweaking of the variables we get a y value that falls between 0 and 1, with it’s peak occurring at 12:00, game time. There is already plenty on the internet that discusses sine waves, and do a far better job at explaining them than I could do. I found this one particularly clear explanation.

Since we’re not drawing a graph, for us x is the game time. As we want the wave to repeat every 24 hours, we define x as the current game time as a fraction of 24. To smooth over the colour transitions we also add the minutes to this as a fraction of an hour.

By playing with the values (mainly, A, C and h) you can alter the day/night cycle and the sky colour. The only thing you can’t do is make the day longer than the night, or vice-versa, since both the peaks and troughs are identical. However, by playing with where you position the horizon geometry in relation to the sun and the variables in the sine equation you can create the effect of longer days.

While using a sine function gives us a smooth, but sharp transition between night and day, we could also use a parametric semicircle function for a more even change over:

sunAsSemicircle

A semicircle is defined as:

y = A sin(t)+cy

x = B cos(t)+cx

Since we don’t need an x position we can do away with that equation. Just like the sine wave method, A is the amplitude and cx and cy is the centre of the semicircle, this can be thought of as our phase shift. Parameter t is defined as 0 ≤ t ≤ π. To calculate t in terms of game time, we convert the game time into radians and divide by 2

def skyColourAsSemicircle():
    night = [0.05, 0.035, 0.063]
    day = [0.103, 0.499, 1]
    diff = [n-d for n,d in zip(night, day)]

    a = 2
    c = -1
    t = ((bge.time.hour+bge.time.minute/60))*((15*math.pi/180)/2)
    y = (math.sin(t)*a+c)
    if y > 1:
        y = 1
    elif y < 0:
        y = 0

    r = night[0] - diff[0]*y
    g = night[1] - diff[1]*y
    b = night[2] - diff[2]*y
    bgl.glClearColor(r, g, b, 1)
    bgl.glClear(bgl.GL_COLOR_BUFFER_BIT)

This time around we’re also clamping the value of y between 0 and 1 (lines 10-13). This stops the sky colour from going outside of the day and night colours we’ve defined and allows us to use bigger values of A and c to create more constant day/night colours and more extreme transitions between them.

clampedSunSemi

You could apply the same clamping method to the sine version. Notice the sharper fall-off around the day peak:

clampedSunSine

With our sky colouring sorted the next step is to draw it to the background. To do this we use the bgl module, a python wrapper for openGL functions. we use .glClearColour() to set the new clear colour (the colour the screen clears to), this takes 4 values, red, green, blue values and alpha. On its own this function does nothing. For the new colour to take effect we need to clear the colour buffer with .glClear(), which takes the buffer to be cleared, in our case the constant GL_COLOUR_BUFFER_BIT.

The final step is to add our new skyColour() function (or whichever method you decide) to the list of functions to be called before the BGE’s rendering step by appending it to scene.pre_draw list (line 29 in the 1st sky colour example).

At the end of the day

While this method does not allow us to use some of the fantastic light scattering effects used by some GLSL shaders, it does have the advantage of working in both multi-texture and GLSL rendering modes. If you wanted to use more complete sky shaders you can use the outputted y value from the functions discussed to drive other sky shaders.

Since both the sun’s motion and the sky colour are derived from the game time, speeding up, slowing down or pausing the game time (like we did in the previous tutorial) will cause the sky and sun to adjust accordingly, without having to explicitly update them. The only thing not accounted for, is saving/loading previous times. To do this you would just have to add the loaded time to the variable c. Finally, you could take this further by adding seasons by changing some of the parameters and the amount of clamping depending on the game month to create shorter or longer days.

As always, any comments, questions or feedback, drop it below. Laters

Resources

Example day/night cycle .blend

Sine wave tutorial

Game time tutorial

Advertisements

~ by Jay on June 1, 2014.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

w

Connecting to %s

 
%d bloggers like this: