HeadlineZapper
From techdaze
A sample PyGame application based on the model of a simple Finite State Machine to produce an "RSS-feed reader meets 1980's shoot 'em up" (in 160 lines of code).
see also: OneLoopDrumMachine
from: Tech Day 3.01 Tech Day 3.02
Contents |
Game Concept
Headlines are read from an RSS feed and presented word for word to the player. Every now and then a random "noise" word is displayed.
The goal of the game is to correctly "zap" the noise words.
So actually there are three score "events" in the game:
- If a player presses the "zap" button while a noise word is displayed, their score goes up.
- If a player presses the "zap" button while a normal word is displayed, their score goes down.
- If a player does not zap a noise word, the score should go down.
Screenshots
attachment:basque_thumb.png attachment:green_thumb.png attachment:tomato_thumb.png
Code
attachment:headlinezapper_start.zip
attachment:headlinezapper_final.zip
Starting
Step 1
To start, we might want to read headlines from a feed, and display the words one by one.
You may want to refer to: PythonFeedparser
In the simplest case we want to translate from the feedparser feed object ultimately to a flat list of word images that PyGame can use to draw.
words = [] ... words.append(...)
words = [] for e in feed.entries: for w in e.title.split(): words.append(w) print words
Now you can prepare each word to be drawn to the screen, so we create a "parallel list" of "wordimages".
wordimages = [] for w in words: wordimages.append(font.render(w, True, (255, 255, 255))) wordindex = 0
AND INSIDE THE LOOP:
# DRAW THE SCREEN screen.fill((0, 0, 0)) screen.blit(wordimages[wordindex], (0, 0)) pygame.display.flip() # ADJUST THE WORDINDEX wordindex += 1 if wordindex >= len(words): wordindex = 0
FPS = 30 SECONDS_PER_WORD = 1.0 frames_per_word = SECONDS_PER_WORD * FPS
framecount += 1 if (framecount >= frames_per_word): framecount = 0 # ADJUST THE WORDINDEX wordindex += 1 if wordindex >= len(words): wordindex = 0
State Machine
Final Code
import pygame from pygame.locals import * import sys import feedparser import random pygame.init() pygame.font.init() # we need fonts pygame.mixer.init() # we need sound fontpath = "/var/lib/defoma/fontconfig.d/D/[[DejaVu]]-Sans-[[ExtraLight]].ttf" url = "http://www.eitb24.com/rss/rss-eitb24-home-en.xml" if len(sys.argv) >= 2: url = sys.argv[1] size = width, height = 640, 480 FPS = 30 SECONDS_PER_WORD = 1.0 frames_per_word = SECONDS_PER_WORD * FPS screen = pygame.display.set_mode(size) clock = pygame.time.Clock() feed = feedparser.parse(url) sweepdown = pygame.mixer.Sound("sweep_down_long.wav") mleep = pygame.mixer.Sound("mleep3.wav") beat1 = pygame.mixer.Sound("low1.wav") beat2 = pygame.mixer.Sound("low2.wav") font = pygame.font.Font(fontpath, 36) score_font = pygame.font.Font(fontpath, 272) words = [] entries = feed.entries[:] random.shuffle(entries) for e in entries: for w in e.title.split(): words.append(w) wordimages = [] for w in words: wordimages.append(font.render(w, True, (255, 255, 255))) wordindex = 0 framecount = 0 score = 0 noisewords = "tomato pumpkin cherry Apple Kiwi \"kumquat Banana watermelon Canteloupe CHERRY".split() noisewordimages = [] for w in noisewords: noisewordimages.append(font.render(w, True, (255, 255, 255))) noisewordindex = None REGULAR = 0 NOISE = 1 LOSE = 2 WIN = 3 state = 0 (beatframes, beatcount, framesperbeat) = (0, 0, 40) while 1: # HANDLE EVENTS for event in pygame.event.get(): if event.type == pygame.QUIT or \ (event.type == KEYDOWN and event.key == K_ESCAPE): sys.exit() if event.type == KEYDOWN: if event.key == K_f: pygame.display.toggle_fullscreen() if event.key == K_SPACE: if (state == LOSE or state == WIN): pass elif (state == REGULAR): # LOSE, you zapped a normal word! sweepdown.play() state = LOSE else: # WIN, you zapped a noise word mleep.play() state = WIN ######## ### DRAW THE SCREEN if (state == LOSE): screen.fill((255, 0, 0)) elif (state == WIN): screen.fill((0, 255, 0)) else: screen.fill((0, 0, 0)) # UPDATE / DRAW SCORE score_color = (128, 128, 128) if state == WIN: score_color = (128, 255, 128) score += 1 elif state == LOSE: score_color = (255, 128, 128) score -= 1 score_surf = score_font.render(str(score), True, score_color) screen.blit(score_surf, (50, 200)) if (noisewordindex == None): screen.blit(wordimages[wordindex], (100, 100)) else: screen.blit(noisewordimages[noisewordindex], (100, 100)) pygame.display.flip() beatframes -= 1 if beatframes <= 0: beatcount += 1 beatframes = framesperbeat if (beatcount % 2): beat1.play() else: beat2.play() framesperbeat -= 0.5 # 0.15 frames_per_word -= 0.25 # 0.15 # ADJUST THE STATE framecount += 1 # set the "idle" time depending on state (longer for win and lose) if (state == WIN or state == LOSE): fc = 90 else: fc = frames_per_word if (framecount >= fc): framecount = 0 # NEXT WORD if (state == NOISE): # GO TO LOSE! state = LOSE sweepdown.play() elif (state == LOSE or state == WIN): noisewordindex = None state = REGULAR else: if random.randint(0, 8) == 0: # GO TO NOISE STATE state = NOISE noisewordindex = random.randint(0, len(noisewords)-1) else: # STAY IN REGULAR STATE wordindex += 1 if wordindex >= len(words): wordindex = 0 # MANAGE TIMING (ensures we don't go too fast) clock.tick(FPS)


