3 import sys, os, time, random, pygame
10 # use integers to control volume which avoids float rounding error issues
14 def __init__(self, volume=1.0, channel_id=0):
15 self.volume = int(volume*100)
19 self.channel_id = channel_id
20 self.channel = None # flag to show this uses pygame.mixer.music
21 elif channel_id < pygame.mixer.get_num_channels():
22 self.channel_id = channel_id
23 self.channel = pygame.mixer.Channel(self.channel_id)
25 def set_volume(self, new_level):
26 if new_level >= Channel.MIN and new_level <= Channel.MAX:
27 self.volume = new_level
28 if self.channel == None:
29 pygame.mixer.music.set_volume(self.volume/100)
31 self.channel.set_volume(self.volume/100)
32 if DEBUG: print("Channel.set_volume(", self.volume, ")")
34 if self.volume+self.step <= Channel.MAX:
35 self.set_volume(self.volume + self.step)
37 if self.volume-self.step >= Channel.MIN:
38 self.set_volume(self.volume - self.step)
41 def queue(self, sound):
42 if self.channel != None:
43 self.channel.queue(sound)
45 return self.channel.get_queue() if self.channel != None else None
46 def fadeout(self, time):
47 if self.channel == None:
48 pygame.mixer.music.fadeout(time)
50 self.channel.fadeout(time)
51 def play(self, sound, loops=0, maxtime=0, fade_ms=0):
52 if self.channel != None:
53 # allow 'sound' to be a path string or an object
54 sound = pygame.mixer.Sound(sound)
55 self.channel.play(sound, loops, maxtime, fade_ms)
57 pygame.mixer.music.load(sound)
58 pygame.mixer.music.play(loops)
62 if self.channel == None:
63 pygame.mixer.music.unpause()
65 self.channel.unpause()
68 if self.channel == None:
69 pygame.mixer.music.pause()
72 def playlist(self, path):
73 self.music_path = path
74 self.playlist = [ f for f in os.listdir(self.music_path) if os.path.isfile(os.path.join(self.music_path, f)) ]
75 random.shuffle(self.playlist)
76 if DEBUG: print ("playlist=", self.playlist)
77 self.playlist_index = -1 # flag for 'start from the beginning of list'
79 pygame.mixer.music.set_endevent(pygame.USEREVENT)
80 def playlist_next(self):
81 self.playlist_index = self.playlist_index + 1 if self.playlist_index < len(self.playlist)-1 else 0
82 if DEBUG: print("Channel.playlist_next() = [%d] %s" %(self.playlist_index, self.playlist[self.playlist_index] ))
83 pygame.mixer.music.load(os.path.join(self.music_path, self.playlist[self.playlist_index]))
84 pygame.mixer.music.play()
86 # container for all game control and configuration items
89 soundtrack = Channel(channel_id=-1)
90 sound_effects = Channel(channel_id=0)
91 def __init__(self, width=0, height=0, title=""):
92 self.config.update({'width':width, 'height':height})
93 self.config.update({'title':title})
94 self.config.update({'sound':1})
96 self.resolution = width, height
99 class SeamlessBackground:
104 def __init__(self, filename, offset=0):
105 if os.path.exists(filename):
106 self.image_path = filename
107 self.image = pygame.image.load(self.image_path) #.convert()
109 self.width = self.image.get_size()[0]
110 if self.image == None:
111 print("Failed to load image", filename)
113 def draw(self, screen, x_pos, step):
114 """ x_pos == -1 requests the full screen be painted, not just the damaged left/right margins
116 #if DEBUG: print("SeamlessBackground.draw(screen, %d, %d)" % (x_pos, step))
117 if self.image != None:
118 resolution = screen.get_size()
119 # adjust x_pos to be x coord of damaged rectangle (left or right side of screen)
121 x_pos += resolution[0] - step
124 elif step == 0 and x_pos >= 0:
125 # nothing to do if not drawing the initial background
128 bk_x_start = x_pos % self.width if x_pos != -1 else 0
129 bk_rect = pygame.Rect(bk_x_start, 0, resolution[0] if x_pos == -1 else abs(step), resolution[1])
130 patch = pygame.Surface((bk_rect.width, bk_rect.height))
131 patch.blit(self.image, (0, 0), bk_rect)
132 remainder = self.width - bk_x_start
133 if remainder < abs(step):
134 # need more pixels because we're just wrapped around the background image
135 patch.blit(self.image, (4,0), pygame.Rect(0, 0, remainder, resolution[1]))
136 #if DEBUG: print("Wrap-around, remainder=%d" % remainder)
138 scr_x_dest = resolution[0] - step if step > 0 else 0
139 #if DEBUG: print("x_pos=%d, scr_x_dest=%d, bk_x_start=%d, bk_rect=%s" % (x_pos, scr_x_dest, bk_x_start, bk_rect))
140 screen.blit(patch, (scr_x_dest, 0))
142 print("No image to draw")
146 myGame = Game(900, 600)
147 flags = pygame.OPENGL & pygame.DOUBLEBUF
150 # main loop sleep (100 milliseconds)
153 pygame.display.set_caption("TJ's platform scroller")
156 stepping = 8 # pixels scrolled each flip
157 background_colour = (20, 20, 64)
158 screen = pygame.display.set_mode(myGame.resolution, flags, depth)
159 resolution = screen.get_size()
160 screen.fill(background_colour)
163 background_image = SeamlessBackground(os.path.join("resources", "binary.jpg"))
164 background_image.draw(screen, -1, 0) # -1 flags initial background drawing
165 pygame.display.flip()
168 blip = pygame.Surface((abs(stepping), abs(stepping)), pygame.HWSURFACE)
169 blip_colour = (128, 128, 128)
170 blip.fill(blip_colour)
173 y = resolution[1] / 2
175 if myGame.config['sound'] == 1:
176 myGame.soundtrack.playlist(os.path.join(os.getcwd(), "resources", "Music"))
184 if x_pos + movement >= 0:
190 # change colour randomly
191 d_red = random.randrange(-1,2) *8
192 d_green = random.randrange(-1,2) *8
193 d_blue = random.randrange(-1,2) *8
194 # build a new tuple, ensuring all values are valid
195 blip_colour = tuple( b+d if b+d < 256 and b+d >=0 else b for b, d in zip(blip_colour, (d_red, d_green, d_blue)) )
196 blip.fill(blip_colour)
198 # change position up or down randomly by one step
199 dy = random.randrange(-1,2) * abs(stepping)
200 if((y + dy) < resolution[1] and (y + dy) >= 0):
201 # screen.fill(background_colour, pygame.Rect(resolution[0] - abs(stepping), y, abs(stepping), abs(stepping) ))
206 #print(y, blip_colour)
209 screen.scroll(-movement, 0)
210 background_image.draw(screen, x_pos, movement)
211 screen.blit(blip, (resolution[0] - abs(stepping), y))
212 pygame.display.flip()
214 # manage the frame-rate
218 for event in pygame.event.get():
219 if event.type == pygame.QUIT or (event.type == pygame.KEYUP and event.key == pygame.K_q):
221 if event.type == pygame.KEYUP:
225 elif k == pygame.K_7:
226 myGame.soundtrack.pause()
227 elif k == pygame.K_l:
228 auto_scroll = True if auto_scroll == False else False
230 if event.type == pygame.USEREVENT:
231 if DEBUG: print("pygame.USEREVENT received; calling playlist_next()")
232 myGame.soundtrack.playlist_next()
236 keys_pressed = pygame.key.get_pressed()
237 if sum(keys_pressed):
238 if keys_pressed[pygame.K_6]:
240 myGame.soundtrack.down()
241 elif keys_pressed[pygame.K_8]:
243 myGame.soundtrack.up()
244 elif keys_pressed[pygame.K_d]:
246 elif keys_pressed[pygame.K_a]:
249 myGame.soundtrack.fadeout(5000)
251 pygame.display.quit()
254 if __name__ == '__main__':