[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

[pygame] Masking Test: Simulated Lighting (Follow-Up)



I played with the idea more and was able to make a moving light source by, each frame, making a solid black mask, using pygame.draw.circle to draw a transparent circle on it, then blitting the holey mask onto the screen. This turned everything black except where the "light" had been placed, and the light's position could be changed each frame.

But a problem appeared in trying to make the process more efficient, by drawing a lightmask on its own, once, then each blitting it onto the black mask, then blitting the black mask onto the screen. Doing it that way would be better for complex, gradient lights such as the big one seen in my previous code. That is, you'd make a (radius*2,radius*2) surface that's black with a gradually-shaded circle of translucency, once, then blit it onto solid black each frame. But when I tried that,
Pygame took me too literally. I think what happened was that when I blitted the lightmask onto the black mask, the transparent areas, being transparent, didn't affect the black mask and so didn't create a hole!


As-is, the method is only really suitable for sharp-edged lights -- those that can be drawn with a single call to pygame.draw.circle -- and even then it's a framerate-wounder.

## <code>
SCREEN_SIZE = (800,600)

light_size = 100
light_location = (200,200)

## Setup
import pygame
from pygame.locals import *
pygame.init()
screen = pygame.display.set_mode(SCREEN_SIZE)

## A black mask for the screen.
mask = pygame.surface.Surface(SCREEN_SIZE).convert_alpha()

## A moving light mask drawn atop the black mask.
lightmask = pygame.surface.Surface((light_size,light_size)).convert_alpha()

#### An inefficiently-drawn shaded "light"
##radius = 200
##t = 255
##delta = 3
##while radius > 50:
##    pygame.draw.circle(mask,(0,0,0,t),light_location,radius)
##    t -= delta
##    radius -= delta
##pygame.draw.circle(mask,(0,0,0,95),light_location,radius)

def DrawScreen():
    ## A blue screen with a couple of white squares
    screen.fill((0,0,255))
    pygame.draw.rect(screen,(255,255,255),(100,100,100,100))
    pygame.draw.rect(screen,(255,255,255),(300,250,100,100))

def CreateLightMask():
    """Call once. Only needed for second method."""
    lightmask.fill((0,0,0,255)) ## Opaque black except where changed
    pygame.draw.circle(lightmask,(0,0,0,0),(50,50),50)

def DrawMask():
    """Inefficient method."""
    ## Blank the mask
    mask.fill((0,200,0,255))
    ## Put the light onto it, using manual drawing
    pygame.draw.circle(mask,(0,0,0,0),light_location,50)
    ## Cover the screen with the partly-translucent mask
    screen.blit(mask,(0,0))

def DrawMask_SecondMethod():
    """For some reason this method doesn't work."""
    ## Blank the mask
    mask.fill((0,0,0,255)) ## Try changing one of the RGB values to 255.
    ## Put the light onto it, by blitting the pre-drawn lightmask
    mask.blit(lightmask,(0,0))
    ## Cover the screen with the partly-translucent mask
    screen.blit(mask,(0,0))


## MAIN LOOP ##

CreateLightMask() ## Draw the light source for the first time

## Test FPS.
frames_drawn = 0
ticks = pygame.time.get_ticks()

done = False
while not done:

    for event in pygame.event.get():
        if event.type == QUIT:
            done = True

        elif event.type == KEYDOWN:
            if event.key == K_ESCAPE:
                done = True

    ## Move the light a bit
    light_location = (light_location[0] + 5, light_location[1]+2)

    DrawScreen()
    DrawMask() ## Working method
##    DrawMask_SecondMethod() ## Non-working method

    ## Make it so
    pygame.display.update()

    frames_drawn += 1

## FPS rating.
print "Done. FPS: %d" % ((frames_drawn*1000)/(pygame.time.get_ticks()-ticks))
## </code>