Initial Commit für 1869 RemakeV2
@ -0,0 +1,21 @@
|
||||
#####
|
||||
#Paths / Assets
|
||||
#####
|
||||
import pyglet
|
||||
|
||||
draw_batch = pyglet.graphics.Batch()
|
||||
#-> batch to identify who needs what to do
|
||||
#-> draw_batch = should be drawn
|
||||
|
||||
pyglet.resource.path = ['graphics']
|
||||
pyglet.resource.reindex()
|
||||
|
||||
max_design_intro_image = pyglet.resource.image("Max Design Introscreen.png")
|
||||
artwork_intro_image = pyglet.resource.image("Introscreen_Artwork.png")
|
||||
programmers_intro_image = pyglet.resource.image("Introscreen_Programmers.png")
|
||||
music_intro_image = pyglet.resource.image("Introscreen_Musik.png")
|
||||
title_intro_image = pyglet.resource.image("TitleScreen.png")
|
||||
blackScreen = pyglet.resource.image("Blackscreen.png")
|
||||
configScreen = pyglet.resource.image("ConfigScreen.png")
|
||||
|
||||
|
@ -0,0 +1,41 @@
|
||||
#####
|
||||
#Labels
|
||||
#####
|
||||
|
||||
globalLabels = {
|
||||
'CONFIG':{
|
||||
'1' : "EIN ALTES SPIEL LADEN \"J\" SONST LEERTASTE.",
|
||||
'2' : "WIE VIELE SPIELER 1-4",
|
||||
'3' : "DEN NAMEN DES {} SPIELERS EINGEBEN.",
|
||||
'4' : "\"M\" - MÄNNLICH ODER \"W\" - WEIBLICH ?",
|
||||
'5' : "WIE SOLL DIE FIRMA DES {} SPIELERS HEISSEN ?",
|
||||
'6' : (
|
||||
"IN WELCHEM HAFEN WOLLEN SIE STARTEN?",
|
||||
"\"1\" {} {}",
|
||||
"\"2\" {} {}",
|
||||
"\"3\" {} {}",
|
||||
"\"4\" {} {}",
|
||||
"\"5\" {} {}",
|
||||
),
|
||||
'7': (
|
||||
"SPIELERNAME {}",
|
||||
"GESCHLECHT {}",
|
||||
"FIRMENNAME {}",
|
||||
"STARTHAFEN {}",
|
||||
"",
|
||||
"SIND DIESE EINSTELLUNGEN RICHTIG? \"J\" ODER \"N\"",
|
||||
),
|
||||
'8':(
|
||||
"WIE LANGE WOLLEN SIE SPIELEN?",
|
||||
"A - 5 JAHRE",
|
||||
"B - 10 JAHRE",
|
||||
"C - 15 JAHRE",
|
||||
"D - 20 JAHRE",
|
||||
"E - 26 JAHRE",
|
||||
"",
|
||||
"F - 5 JAHRE \"EINSTEIGER SPIEL\"",
|
||||
),
|
||||
},
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,412 @@
|
||||
# /////
|
||||
# Remake of 1869 in python
|
||||
# ////
|
||||
|
||||
import pyglet
|
||||
import Assets
|
||||
import Labels
|
||||
import Utils
|
||||
import logging # TODO to be implemented
|
||||
|
||||
from pyglet.window import key
|
||||
from pyglet.window import mouse
|
||||
from pyglet.gl import *
|
||||
from scenes import IntroScreen
|
||||
from scenes import ConfigScreen
|
||||
|
||||
glEnable(GL_BLEND)
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
|
||||
|
||||
pyglet.gl.glClearColor(0.1, 0.1, 0.1, 1)
|
||||
|
||||
"""
|
||||
naming conventions:
|
||||
d_varname = dict
|
||||
s_varname = set
|
||||
l_varname = list
|
||||
t_varname = tuple
|
||||
b_varname = batch
|
||||
og_varname = orderedGroup for z relation of graphics
|
||||
"""
|
||||
|
||||
class main(pyglet.window.Window):
|
||||
def __init__(self, width=640, height=480, fps=False, *args, **kwargs):
|
||||
super(main, self).__init__(width, height,
|
||||
caption='1869 Remake', *args, **kwargs)
|
||||
self.x = 0
|
||||
self.y = 0
|
||||
self.WindowMinWidth = width
|
||||
self.WindowMinHeight = height
|
||||
self.currentWindowWidth = width
|
||||
self.currentWindowHeight = height
|
||||
# self.push_handlers(pyglet.window.event.WindowEventLogger())
|
||||
# -> gibt aus welche events an das window geschickt wurden -> gut zum finden von events die manipuliert werden sollen
|
||||
|
||||
self.og_background = pyglet.graphics.OrderedGroup(0)
|
||||
self.og_semi_background = pyglet.graphics.OrderedGroup(1)
|
||||
self.og_semi_foreground = pyglet.graphics.OrderedGroup(2)
|
||||
self.og_foreground = pyglet.graphics.OrderedGroup(3)
|
||||
|
||||
self.b_sprites = pyglet.graphics.Batch()
|
||||
self.b_labels = pyglet.graphics.Batch()
|
||||
self.b_widgets = pyglet.graphics.Batch()
|
||||
|
||||
self.mouse_x = 0
|
||||
self.mouse_y = 0
|
||||
self.alive = 1
|
||||
|
||||
self.activeScene = None
|
||||
self.activeSceneName = None
|
||||
self.sceneTransferPhase = 1
|
||||
self.d_stopWatch = dict()
|
||||
|
||||
self.d_active_SpriteAnimations = dict()
|
||||
self.d_active_LabelAnimations = dict()
|
||||
self.d_active_WidgetAnimations = dict()
|
||||
self.targetScene = 'INTRO'
|
||||
self.d_active_sprites = dict()
|
||||
self.d_active_labels = dict()
|
||||
self.d_active_widgets = dict()
|
||||
|
||||
# TODO check if this widget system for text input can be used to imitate text lieferant from c# code
|
||||
self.text_cursor = self.get_system_mouse_cursor('text')
|
||||
self.focus = None
|
||||
|
||||
#####
|
||||
# system funcs
|
||||
#####
|
||||
def on_resize(self, width, height):
|
||||
width = max(width,self.WindowMinWidth)
|
||||
height = max(height, self.WindowMinHeight)
|
||||
super(main, self).on_resize(width, height)
|
||||
|
||||
currentScaleFactor_x = width/self.currentWindowWidth
|
||||
currentScaleFactor_y = height/self.currentWindowHeight
|
||||
|
||||
self.currentWindowWidth = width
|
||||
self.currentWindowHeight = height
|
||||
if self.d_active_sprites is not None:
|
||||
for keys, sprite in self.d_active_sprites.items():
|
||||
sprite.scale_x = max(width, sprite.image.width)/min(width, sprite.width)
|
||||
sprite.scale_y = max(sprite.image.height, height)/min(sprite.image.height, height)
|
||||
if self.d_active_labels is not None:
|
||||
for keys, label in self.d_active_labels.items():
|
||||
label.content_width = label.content_width*(max(label.content_width, width)/ min(label.content_width, width))
|
||||
label.content_height = label.content_height*(max(label.content_height, height)/ min(label.content_height, height))
|
||||
label.x = label.x* currentScaleFactor_x
|
||||
label.y = label.y* currentScaleFactor_y
|
||||
label.font_size = label.font_size*currentScaleFactor_x
|
||||
if self.d_active_widgets is not None:
|
||||
for keys, widget in self.d_active_widgets.items():
|
||||
#widget.width = width - 110
|
||||
pass
|
||||
|
||||
def on_draw(self):
|
||||
self.render()
|
||||
|
||||
def render(self):
|
||||
self.clear()
|
||||
|
||||
self.b_sprites.draw()
|
||||
self.b_labels.draw()
|
||||
self.b_widgets.draw()
|
||||
|
||||
self.flip()
|
||||
|
||||
def on_close(self):
|
||||
self.alive = 0
|
||||
|
||||
#####
|
||||
# input Handler Funcs
|
||||
#####
|
||||
|
||||
def on_mouse_motion(self, x, y, dx, dy):
|
||||
# forward mouse information to active scene
|
||||
self.activeScene.on_mouse_motion(self, x, y, dx, dy)
|
||||
self.mouse_x = x
|
||||
self.mouse_y = y
|
||||
|
||||
if self.d_active_widgets is not None:
|
||||
for key,widget in self.d_active_widgets.items():
|
||||
if widget.hit_test(x, y):
|
||||
self.set_mouse_cursor(self.text_cursor)
|
||||
break
|
||||
else:
|
||||
self.set_mouse_cursor(None)
|
||||
|
||||
def on_mouse_press(self, x, y, button, modifiers):
|
||||
# forward mouse information to active scene
|
||||
self.activeScene.on_mouse_press(self, x, y, button, modifiers)
|
||||
if button == 1: # Left click
|
||||
pass
|
||||
for key,widget in self.d_active_widgets.items():
|
||||
if widget.hit_test(x, y):
|
||||
self.set_focus(widget)
|
||||
break
|
||||
else:
|
||||
self.set_focus(None)
|
||||
|
||||
if self.focus:
|
||||
self.focus.caret.on_mouse_press(x, y, button, modifiers)
|
||||
|
||||
def on_mouse_drag(self, x, y, dx, dy, buttons, modifiers):
|
||||
if self.focus:
|
||||
self.focus.caret.on_mouse_drag(x, y, dx, dy, buttons, modifiers)
|
||||
|
||||
def on_key_press(self, symbol, modifiers):
|
||||
# forward keypress information to active scene
|
||||
self.activeScene.on_key_press(self, symbol, modifiers)
|
||||
if symbol == key.ESCAPE:
|
||||
self.alive = 0
|
||||
if symbol == key.TAB:
|
||||
if modifiers & key.MOD_SHIFT:
|
||||
dir = -1
|
||||
else:
|
||||
dir = 1
|
||||
|
||||
if self.focus in self.d_active_widgets:
|
||||
i = self.d_active_widgets.index(self.focus)
|
||||
else:
|
||||
i = 0
|
||||
dir = 0
|
||||
|
||||
self.set_focus(self.d_active_widgets[(i + dir) % len(self.d_active_widgets)])
|
||||
|
||||
def on_text(self, text):
|
||||
if self.focus:
|
||||
self.focus.caret.on_text(text)
|
||||
|
||||
def on_text_motion(self, motion):
|
||||
if self.focus:
|
||||
self.focus.caret.on_text_motion(motion)
|
||||
|
||||
def on_text_motion_select(self, motion):
|
||||
if self.focus:
|
||||
self.focus.caret.on_text_motion_select(motion)
|
||||
|
||||
def set_focus(self, focus):
|
||||
if self.focus:
|
||||
self.focus.caret.visible = False
|
||||
self.focus.caret.mark = self.focus.caret.position = 0
|
||||
print('defocused')
|
||||
self.focus = focus
|
||||
if self.focus:
|
||||
print('focused')
|
||||
self.focus.caret.visible = True
|
||||
self.focus.caret.mark = 0
|
||||
self.focus.caret.position = len(self.focus.document.text)
|
||||
#####
|
||||
# stopwatch functions
|
||||
####
|
||||
|
||||
def checkStopwatch(self, obj, targetSeconds):
|
||||
"""
|
||||
tracks time based operations for given objects
|
||||
-> targetSeconds: returns true if current Timer is below /reached targetSeconds
|
||||
"""
|
||||
if obj in self.d_stopWatch:
|
||||
if self.d_stopWatch[obj] <= targetSeconds:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
|
||||
def setStopwatch(self, obj, seconds):
|
||||
if obj not in self.d_stopWatch:
|
||||
self.d_stopWatch[obj] = seconds
|
||||
return True
|
||||
else:
|
||||
# not allowed to set a timer on an object twice
|
||||
return False
|
||||
|
||||
def stopwatch_tick(self):
|
||||
for obj in self.d_stopWatch:
|
||||
self.d_stopWatch[obj] -= 1
|
||||
|
||||
def clearStopwatchTask(self, obj):
|
||||
if obj in self.d_stopWatch:
|
||||
self.d_stopWatch.pop(obj)
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def clearStopwatch(self):
|
||||
self.d_stopWatch.clear()
|
||||
|
||||
#####
|
||||
# resource loaders
|
||||
#####
|
||||
def load_sprites(self):
|
||||
"""
|
||||
load all sprites used in the specific scene
|
||||
"""
|
||||
if isinstance(self.activeScene, IntroScreen.IntroScene):
|
||||
return {
|
||||
'max_design_intro_sprite': pyglet.sprite.Sprite(Assets.max_design_intro_image, batch=self.b_sprites, group=self.og_background),
|
||||
'artwork_intro_sprite': pyglet.sprite.Sprite(Assets.artwork_intro_image, batch=self.b_sprites, group=self.og_background),
|
||||
'programmers_intro_sprite': pyglet.sprite.Sprite(Assets.programmers_intro_image, batch=self.b_sprites, group=self.og_background),
|
||||
'music_intro_sprite': pyglet.sprite.Sprite(Assets.music_intro_image, batch=self.b_sprites, group=self.og_background),
|
||||
'title_intro_sprite': pyglet.sprite.Sprite(Assets.title_intro_image, batch=self.b_sprites, group=self.og_background),
|
||||
'blackscreen': pyglet.sprite.Sprite(Assets.blackScreen, batch=self.b_sprites, group=self.og_foreground),
|
||||
}
|
||||
if isinstance(self.activeScene, ConfigScreen.ConfigScene):
|
||||
return {
|
||||
'configSprite': pyglet.sprite.Sprite(Assets.configScreen, batch=self.b_sprites, group=self.og_background),
|
||||
'blackscreen': pyglet.sprite.Sprite(Assets.blackScreen, batch=self.b_sprites, group=self.og_foreground),
|
||||
}
|
||||
|
||||
def load_labels(self):
|
||||
"""
|
||||
load all labels used in the specific scene
|
||||
"""
|
||||
if isinstance(self.activeScene, IntroScreen.IntroScene):
|
||||
return {
|
||||
'press_enter': pyglet.text.Label(text="Press Enter", font_name=None, font_size=10, color=(255, 255, 255, 0), x=(self.width*7/10), y=(self.height*2/7), anchor_x='center', batch=self.b_labels, group=self.og_semi_foreground),
|
||||
}
|
||||
if isinstance(self.activeScene, ConfigScreen.ConfigScene):
|
||||
return {}#CONFIG has no preset Labels -> they are created by the scene on demand
|
||||
|
||||
def load_widgets(self):
|
||||
"""
|
||||
load all widgets used in the specific scene
|
||||
"""
|
||||
if isinstance(self.activeScene, ConfigScreen.ConfigScene):
|
||||
return {
|
||||
'mainInput': Utils.TextWidget("-TEST TEXT-", x=(self.width/2 )-50, y=self.height/2, width=100 ,batch=self.b_widgets),
|
||||
'textController':Utils.ControledTextBox(x=(self.width/2 )-50, y=(self.height/2) - 50, width=100 , height = 150, batch=self.b_widgets),
|
||||
}
|
||||
|
||||
def load_sounds(self):
|
||||
"""
|
||||
load all sound resources -> probably optimize for scenes
|
||||
"""
|
||||
return {
|
||||
}
|
||||
|
||||
#####
|
||||
# maintanence funcs
|
||||
#####
|
||||
def clear_externalLabel(self, target=None):
|
||||
pass
|
||||
|
||||
def register_externalLabel(self, keyName, text, x, y, width, batch):
|
||||
if keyName in self.d_active_labels:
|
||||
return False
|
||||
else:
|
||||
self.d_active_labels[keyName]
|
||||
|
||||
|
||||
def clear_animationLists(self, target=None):
|
||||
if target in [None, 'sprites']:
|
||||
self.d_active_SpriteAnimations.clear()
|
||||
if target in [None, 'labels']:
|
||||
self.d_active_LabelAnimations.clear()
|
||||
if target in [None, 'widgets']:
|
||||
self.d_active_WidgetAnimations.clear()
|
||||
|
||||
def register_animation(self, anim, wantedAnimation):
|
||||
"""
|
||||
registers anims to be animated by custom animations
|
||||
"""
|
||||
if isinstance(anim, pyglet.sprite.Sprite):
|
||||
if anim in self.d_active_SpriteAnimations:
|
||||
return False
|
||||
else:
|
||||
self.d_active_SpriteAnimations[anim] = wantedAnimation
|
||||
elif isinstance(anim, pyglet.text.Label):
|
||||
if anim in self.d_active_LabelAnimations:
|
||||
return False
|
||||
else:
|
||||
self.d_active_LabelAnimations[anim] = wantedAnimation
|
||||
else:
|
||||
if anim in self.d_active_WidgetAnimations:
|
||||
return False
|
||||
else:
|
||||
self.d_active_WidgetAnimations[anim] = wantedAnimation
|
||||
|
||||
def run_AnimationManager(self):
|
||||
"""
|
||||
animates all registered animations that are currently active
|
||||
"""
|
||||
l_deactivateAnims = list()
|
||||
for sprite, animationType in self.d_active_SpriteAnimations.items():
|
||||
# forward sprite animation infos to active scene
|
||||
self.activeScene.maintain_SpriteAnimations(l_deactivateAnims, sprite, animationType)
|
||||
for label, animationType in self.d_active_LabelAnimations.items():
|
||||
# forward label animation infos to active scene
|
||||
self.activeScene.maintain_LabelAnimations(l_deactivateAnims, label, animationType)
|
||||
for widget, animationType in self.d_active_WidgetAnimations.items():
|
||||
# forward label animation infos to active scene
|
||||
self.activeScene.maintain_WidgetAnimations(l_deactivateAnims, widget, animationType)
|
||||
# when all anims are played out active anims must be cleared for finished ones
|
||||
for anim in l_deactivateAnims:
|
||||
if isinstance(anim, pyglet.sprite.Sprite):
|
||||
self.d_active_SpriteAnimations.pop(anim, None)
|
||||
if isinstance(anim, pyglet.text.Label):
|
||||
self.d_active_LabelAnimations.pop(anim, None)
|
||||
if isinstance(anim, Utils.TextWidget):
|
||||
self.d_active_WidgetAnimations.pop(anim, None)
|
||||
|
||||
def activate_scene(self, scene):
|
||||
self.activeScene = scene
|
||||
self.activeSceneName = self.activeScene.sceneName
|
||||
|
||||
def transferToScene(self, originScene, targetScene):
|
||||
"""
|
||||
transfers from a scene to a scene
|
||||
phase false = no transition
|
||||
phase 1 = origin is deactivated
|
||||
phase 2 = target is loaded
|
||||
"""
|
||||
if self.sceneTransferPhase is False:
|
||||
self.sceneTransferPhase = 1
|
||||
if self.sceneTransferPhase is 1:
|
||||
if self.activeScene is None :
|
||||
self.sceneTransferPhase = 2
|
||||
elif self.activeScene.isAlive is False:
|
||||
self.activeScene.killSprites(self,self.d_active_sprites)
|
||||
self.activeScene.killLabels(self,self.d_active_labels)
|
||||
self.activeScene.killWidgets(self,self.d_active_widgets)
|
||||
self.sceneTransferPhase = 2
|
||||
if self.sceneTransferPhase is 2:
|
||||
if self.targetScene is "INTRO":
|
||||
self.activate_scene(IntroScreen.IntroScene())
|
||||
if self.targetScene is "CONFIG":
|
||||
self.activate_scene(ConfigScreen.ConfigScene())
|
||||
self.d_active_sprites = self.load_sprites()
|
||||
self.d_active_labels = self.load_labels()
|
||||
self.d_active_widgets = self.load_widgets()
|
||||
self.activeScene.prepareSprites(self, self.d_active_sprites)
|
||||
self.activeScene.prepareLabels(self, self.d_active_labels)
|
||||
self.activeScene.prepareWidgets(self,self.d_active_widgets)
|
||||
self.targetScene = False
|
||||
self.sceneTransferPhase = False
|
||||
|
||||
def checkSceneTransfers(self):
|
||||
"""
|
||||
check if any scene transferes have to be made
|
||||
"""
|
||||
#scenetransfer is broken -> repair + more control (e.g. introscene)
|
||||
if self.activeScene is None:
|
||||
self.transferToScene(self.activeScene,self.targetScene)
|
||||
elif self.activeScene.isInTransfer is True or self.sceneTransferPhase is 1:
|
||||
self.transferToScene(self.activeScene,self.targetScene)
|
||||
|
||||
#####
|
||||
# main loop
|
||||
#####
|
||||
def run(self):
|
||||
while self.alive == 1:
|
||||
self.run_AnimationManager()
|
||||
self.checkSceneTransfers()
|
||||
if self.activeScene is not None:
|
||||
self.targetScene = self.activeScene.run(self)
|
||||
self.render()
|
||||
event = self.dispatch_events()
|
||||
|
||||
|
||||
# Programm starting point
|
||||
if __name__ == '__main__':
|
||||
game = main(resizable=True)
|
||||
game.run()
|
@ -0,0 +1,9 @@
|
||||
ToDo:
|
||||
IntroScreen:
|
||||
- done
|
||||
ConfigScreen:
|
||||
- Textfeld informationen verarbeiten
|
||||
- animationsmaschinerie mit IntroScreen vergleichen und anpassen(bei bedarf)
|
||||
- doScene + close funktionen mit intro abgleichen
|
||||
utils:
|
||||
- textcontroller -> mouse daten übergabe zuende implementieren
|
@ -0,0 +1,234 @@
|
||||
###
|
||||
#util functions
|
||||
###
|
||||
import pyglet
|
||||
|
||||
def center_image(image):
|
||||
"""Sets an image's anchor point to its center"""
|
||||
image.anchor_x = image.width // 2
|
||||
image.anchor_y = image.height // 2
|
||||
|
||||
def fadeIn_sprite(sprite , currentOpacity ,speed):
|
||||
if sprite.opacity < 255:
|
||||
if sprite.opacity + speed > 255:
|
||||
sprite.opacity = 255
|
||||
else:
|
||||
sprite.opacity = currentOpacity + speed
|
||||
else:
|
||||
return False # returns False if max reached
|
||||
|
||||
def fadeOut_sprite(sprite, currentOpacity ,speed):
|
||||
if sprite.opacity > 0:
|
||||
if sprite.opacity - speed < 0:
|
||||
sprite.opacity = 0
|
||||
else:
|
||||
sprite.opacity = currentOpacity - speed
|
||||
else:
|
||||
return False # returns False if 0 reached
|
||||
|
||||
def fadeIn_label(label, currentColor,speed):
|
||||
red = currentColor[0]
|
||||
green = currentColor[1]
|
||||
blue = currentColor[2]
|
||||
alpha = currentColor[3]
|
||||
inProgess = True
|
||||
if alpha + speed > 255:
|
||||
alpha = 255
|
||||
inProgess = False
|
||||
else:
|
||||
alpha += speed
|
||||
toggledColor = (red,green,blue,alpha)
|
||||
label.color = toggledColor
|
||||
return inProgess
|
||||
|
||||
def toggle_label(label, currentColor):
|
||||
red = currentColor[0]
|
||||
green = currentColor[1]
|
||||
blue = currentColor[2]
|
||||
alpha = currentColor[3]
|
||||
if alpha > 0:
|
||||
alpha = 0
|
||||
else:
|
||||
alpha = 255
|
||||
toggledColor = (red,green,blue,alpha)
|
||||
label.color = toggledColor
|
||||
return True
|
||||
|
||||
def fading_toggle_label(label, currentColor,remaining_alpha):
|
||||
if remaining_alpha is 0:
|
||||
return True
|
||||
red = currentColor[0]
|
||||
green = currentColor[1]
|
||||
blue = currentColor[2]
|
||||
alpha = currentColor[3]
|
||||
if alpha > 0:
|
||||
alpha = 0
|
||||
else:
|
||||
alpha = remaining_alpha
|
||||
toggledColor = (red,green,blue,alpha)
|
||||
label.color = toggledColor
|
||||
return True
|
||||
|
||||
def fadeIn_widget(widget, Alpha,speed):
|
||||
inProgess = True
|
||||
if Alpha + speed > 255:
|
||||
widget.set_opacity(255)
|
||||
inProgess = False
|
||||
else:
|
||||
widget.set_opacity(Alpha + speed)
|
||||
return inProgess
|
||||
|
||||
def fadeOut_widget(widget, Alpha,speed):
|
||||
inProgess = True
|
||||
if Alpha > 0:
|
||||
if Alpha - speed < 0:
|
||||
widget.set_opacity(0)
|
||||
inProgess = False
|
||||
else:
|
||||
widget.set_opacity(Alpha - speed)
|
||||
return inProgress
|
||||
|
||||
class Rectangle(object):
|
||||
'''Draws a rectangle into a batch.'''
|
||||
def __init__(self, x1, y1, x2, y2, batch):
|
||||
self.vertex_list = batch.add(4, pyglet.gl.GL_QUADS, None,
|
||||
('v2i', [x1, y1, x2, y1, x2, y2, x1, y2]),
|
||||
('c4B', [200, 200, 220, 255] * 4)
|
||||
)
|
||||
|
||||
class TextWidget(object):
|
||||
"""
|
||||
Texteingabe Feld
|
||||
"""
|
||||
def __init__(self, text, x, y, width, batch):
|
||||
self.document = pyglet.text.document.UnformattedDocument(text)
|
||||
self.document.set_style(0, len(self.document.text),
|
||||
dict(color=(0, 0, 0, 0))
|
||||
)
|
||||
font = self.document.get_font()
|
||||
height = font.ascent - font.descent
|
||||
|
||||
# checken wo groups integriert werden können da sich einige sachen überlagern
|
||||
self.layout = pyglet.text.layout.IncrementalTextLayout(
|
||||
self.document, width, height, multiline=False, batch=batch)
|
||||
self.caret = pyglet.text.caret.Caret(self.layout)
|
||||
|
||||
self.layout.x = x
|
||||
self.layout.y = y
|
||||
|
||||
# Rectangular outline
|
||||
pad = 2
|
||||
#self.rectangle = Rectangle(x - pad, y - pad,
|
||||
#x + width + pad, y + height + pad, batch)
|
||||
|
||||
def hit_test(self, x, y):
|
||||
return (0 < x - self.layout.x < self.layout.width and
|
||||
0 < y - self.layout.y < self.layout.height)
|
||||
|
||||
def set_opacity (self,alpha):
|
||||
self.document.set_style(0,len(self.document.text), dict(color=(0,0,0,alpha)))
|
||||
#self.rectangle.opacity = alpha
|
||||
|
||||
def get_opacity (self):
|
||||
color = self.document.get_style_range('color', 0, len(self.document.text))
|
||||
#color = self.rectangle.opacity
|
||||
return color[3]
|
||||
|
||||
class ControledTextBox(object):
|
||||
"""
|
||||
controller for textdisplay, input via text/button selection, sitewise type specific handling
|
||||
3 Data feeds are needed:
|
||||
-> a) basic information about the controller (general size, how many lines, how many columns[and their specific sizes])
|
||||
-> b) dictionary which element
|
||||
-> c) dictionary describing a site-tree with indexes
|
||||
"""
|
||||
def __init__(self, x, y, width, height,batch):
|
||||
"""
|
||||
constructs the basic frame
|
||||
"""
|
||||
self.ContentTree = None
|
||||
self.currentPage = 1
|
||||
self.xPos = x
|
||||
self.yPos = y
|
||||
self.width = width
|
||||
self.height = height
|
||||
|
||||
def render(self):
|
||||
pass
|
||||
#todo: anpassen dass mouse daten übergeben werden
|
||||
"""def on_mouse_motion(self, x, y, dx, dy):
|
||||
# forward mouse information to active scene
|
||||
self.activeScene.on_mouse_motion(self, x, y, dx, dy)
|
||||
self.mouse_x = x
|
||||
self.mouse_y = y
|
||||
|
||||
if self.d_active_widgets is not None:
|
||||
for key,widget in self.d_active_widgets.items():
|
||||
if widget.hit_test(x, y):
|
||||
self.set_mouse_cursor(self.text_cursor)
|
||||
break
|
||||
else:
|
||||
self.set_mouse_cursor(None)
|
||||
|
||||
def on_mouse_press(self, x, y, button, modifiers):
|
||||
# forward mouse information to active scene
|
||||
self.activeScene.on_mouse_press(self, x, y, button, modifiers)
|
||||
if button == 1: # Left click
|
||||
pass
|
||||
for key,widget in self.d_active_widgets.items():
|
||||
if widget.hit_test(x, y):
|
||||
self.set_focus(widget)
|
||||
break
|
||||
else:
|
||||
self.set_focus(None)
|
||||
|
||||
if self.focus:
|
||||
self.focus.caret.on_mouse_press(x, y, button, modifiers)
|
||||
|
||||
def on_mouse_drag(self, x, y, dx, dy, buttons, modifiers):
|
||||
if self.focus:
|
||||
self.focus.caret.on_mouse_drag(x, y, dx, dy, buttons, modifiers)
|
||||
|
||||
def on_key_press(self, symbol, modifiers):
|
||||
# forward keypress information to active scene
|
||||
self.activeScene.on_key_press(self, symbol, modifiers)
|
||||
if symbol == key.ESCAPE:
|
||||
self.alive = 0
|
||||
if symbol == key.TAB:
|
||||
if modifiers & key.MOD_SHIFT:
|
||||
dir = -1
|
||||
else:
|
||||
dir = 1
|
||||
|
||||
if self.focus in self.d_active_widgets:
|
||||
i = self.d_active_widgets.index(self.focus)
|
||||
else:
|
||||
i = 0
|
||||
dir = 0
|
||||
|
||||
self.set_focus(self.d_active_widgets[(i + dir) % len(self.d_active_widgets)])
|
||||
"""
|
||||
|
||||
def setPageStructure(self, d_structure):
|
||||
"""
|
||||
sets the structure of the containing pages
|
||||
types:
|
||||
-TextSelection -> pick a text from a list of texts (returns index of text in the current page)
|
||||
-ListSelection -> pick a listentry gathered from a defined datapool (returns the text of the entry)
|
||||
e.g.: [pageIndex]:(type,typeSpecificDetails)
|
||||
e.g.: "1.1":(TextSelection,rows,maxVisibleRows)
|
||||
"""
|
||||
pass
|
||||
|
||||
def setContent(self, d_pageToContent):
|
||||
"""
|
||||
sets the content of each page (structure must be considered while filling it)
|
||||
e.g.: [pageIndex]:{[index1]:(TEXT1), [index2]:(TEXT2)}
|
||||
"""
|
||||
pass
|
||||
|
||||
def checkInput(self):
|
||||
"""
|
||||
check if there is input to give back to the requester
|
||||
"""
|
||||
pass
|
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 136 KiB |
After Width: | Height: | Size: 37 KiB |
After Width: | Height: | Size: 57 KiB |
After Width: | Height: | Size: 49 KiB |
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 7.2 KiB |
After Width: | Height: | Size: 72 KiB |
@ -0,0 +1,30 @@
|
||||
Copyright (c) 2006-2008 Alex Holkner
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of pyglet nor the names of its
|
||||
contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written
|
||||
permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
@ -0,0 +1,27 @@
|
||||
include LICENSE
|
||||
include NOTICE
|
||||
include README
|
||||
include CHANGELOG
|
||||
include RELEASE_NOTES
|
||||
|
||||
# Public tools
|
||||
include tools/inspect_font.py
|
||||
|
||||
# Examples
|
||||
recursive-include examples *
|
||||
prune examples/**/dist
|
||||
|
||||
# Tests
|
||||
recursive-include tests *
|
||||
recursive-exclude tests/regression/images *.png
|
||||
recursive-exclude tests *.log
|
||||
|
||||
# Docs
|
||||
# removed - docs available separately
|
||||
#recursive-include doc/html *
|
||||
#recursive-include doc/pdf *
|
||||
|
||||
# Development artifacts
|
||||
prune **/.svn
|
||||
recursive-exclude * *.pyc
|
||||
recursive-exclude * *.pyo
|
@ -0,0 +1,49 @@
|
||||
pyglet
|
||||
Copyright 2006-2008 Alex Holkner
|
||||
http://www.pyglet.org
|
||||
|
||||
pyglet includes contributions from the following organisations and
|
||||
individuals:
|
||||
|
||||
Blue Box Devices
|
||||
SR Research
|
||||
|
||||
Jesse Archer
|
||||
Ben Atkin
|
||||
Anthony Baxter
|
||||
Anthony Briggs
|
||||
Andrew Campbell
|
||||
Ondrej Certik
|
||||
Peter Dilley
|
||||
Casey Duncan
|
||||
Dunk Fordyce
|
||||
Alan Green
|
||||
Brian Grogan Jr
|
||||
Richard Jones
|
||||
George LeCompte
|
||||
Matthew Marshall
|
||||
Michael Romer
|
||||
Tobias Sargeant
|
||||
Andreas Schiefer
|
||||
Peter Shinners
|
||||
Nathan Stocks
|
||||
Martin Di Paola
|
||||
Walter Woods
|
||||
anatoly techtonik
|
||||
Juan J. Martinez
|
||||
Txema Vicente
|
||||
Claudio Canepa
|
||||
|
||||
pyglet/libs/win32/constants.py is derived from Python for Windows
|
||||
Extensions. Copyright 1994-2001 Mark Hammond.
|
||||
|
||||
pyglet/image/codecs/pypng.py is derived from png.py. Copyright 2006
|
||||
Johann C. Rocholl.
|
||||
|
||||
contrib/layout/layout/Plex/ is derived from Plex. Copyright Greg Ewing.
|
||||
|
||||
tools/wraptypes/lex.py and tools/wraptypes/yacc.py are derived from ply.
|
||||
Copyright 2001-2006 David M. Beazley.
|
||||
|
||||
pyglet/font/win32query.py is fontquery.py placed into public domain by
|
||||
anatoly techtonik.
|
@ -0,0 +1,30 @@
|
||||
Metadata-Version: 1.1
|
||||
Name: pyglet
|
||||
Version: 1.3.0
|
||||
Summary: Cross-platform windowing and multimedia library
|
||||
Home-page: http://pyglet.readthedocs.org/en/latest/
|
||||
Author: Alex Holkner
|
||||
Author-email: Alex.Holkner@gmail.com
|
||||
License: BSD
|
||||
Download-URL: http://pypi.python.org/pypi/pyglet
|
||||
Description: pyglet provides an object-oriented programming
|
||||
interface for developing games and other visually-rich applications
|
||||
for Windows, Mac OS X and Linux.
|
||||
Platform: UNKNOWN
|
||||
Classifier: Development Status :: 5 - Production/Stable
|
||||
Classifier: Environment :: MacOS X
|
||||
Classifier: Environment :: Win32 (MS Windows)
|
||||
Classifier: Environment :: X11 Applications
|
||||
Classifier: Intended Audience :: Developers
|
||||
Classifier: License :: OSI Approved :: BSD License
|
||||
Classifier: Operating System :: MacOS :: MacOS X
|
||||
Classifier: Operating System :: Microsoft :: Windows
|
||||
Classifier: Operating System :: POSIX :: Linux
|
||||
Classifier: Programming Language :: Python :: 2
|
||||
Classifier: Programming Language :: Python :: 2.7
|
||||
Classifier: Programming Language :: Python :: 3
|
||||
Classifier: Programming Language :: Python :: 3.4
|
||||
Classifier: Programming Language :: Python :: 3.5
|
||||
Classifier: Programming Language :: Python :: 3.6
|
||||
Classifier: Topic :: Games/Entertainment
|
||||
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
@ -0,0 +1,61 @@
|
||||
pyglet
|
||||
======
|
||||
|
||||
http://www.pyglet.org/
|
||||
|
||||
pyglet provides an object-oriented programming interface for developing games
|
||||
and other visually-rich applications for Windows, Mac OS X and Linux.
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
pyglet runs under Python 2.7, and 3.4+. The entire codebase is fully 2/3 dual
|
||||
compatible, making use of the future module for backwards compatibility with
|
||||
legacy Python. Being written in pure Python, it also works on other Python
|
||||
interpreters such as PyPy. pyglet works on the following operating systems:
|
||||
|
||||
* Windows XP or later
|
||||
* Mac OS X 10.3 or later
|
||||
* Linux, with the following libraries (most recent distributions will have
|
||||
these in a default installation):
|
||||
* OpenGL and GLX
|
||||
* GDK 2.0+ or PIL (required for loading images other than PNG and BMP)
|
||||
* Pulseaudio or OpenAL (required for playing audio)
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
If you're reading this README from a source distribution, you can install
|
||||
pyglet with:
|
||||
|
||||
python setup.py install
|
||||
|
||||
There are no compilation steps during the installation; if you prefer,
|
||||
you can simply add this directory to your PYTHONPATH and use pyglet without
|
||||
installing it. You can also copy pyglet directly into your project folder.
|
||||
|
||||
The documentation is available online at pyglet.org, but if you want to
|
||||
build the documentation yourself, please check the README file in the doc
|
||||
directory.
|
||||
|
||||
Support
|
||||
-------
|
||||
|
||||
pyglet has an active developer and user community. If you find a bug, please
|
||||
open an issue at https://bitbucket.org/pyglet/pyglet/issues.
|
||||
|
||||
Please join us on the mailing list at:
|
||||
|
||||
http://groups.google.com/group/pyglet-users
|
||||
|
||||
For more information, visit http://www.pyglet.org
|
||||
|
||||
Testing
|
||||
-------
|
||||
|
||||
pyglet makes use of pytest for it's test suite.
|
||||
|
||||
% py.test tests/
|
||||
|
||||
Please check the documentation for more information about running and writing
|
||||
tests.
|
@ -0,0 +1,240 @@
|
||||
Pyglet 1.3.0
|
||||
============
|
||||
This major release takes Python 3 support to the next level. The entire codebase is now compatible
|
||||
with both Python 2 and Python 3 without the need for 2to3. This should make it easier to develop
|
||||
pyglet and pyglet apps for both Python versions.
|
||||
|
||||
The rest of this release is focussed on code quality and test coverage. There are no API breaking
|
||||
changes, and only a few minor additions. Dozens of bugs have been fixed, and the codebase is in a
|
||||
better state for future improvement and maintainability.
|
||||
|
||||
New features
|
||||
------------
|
||||
- The procedural audio module is now more usable. This module allows synthesis of basic
|
||||
waveforms, such as sine, square, triangle, sawtooth, and simple FM (two operator sine).
|
||||
In addition, several basic amplitude envelopes are now available to apply to generated audio.
|
||||
These include ADSR, linear decay, tremolo, and flat envelopes.
|
||||
|
||||
Improvements
|
||||
------------
|
||||
- Improved font rendering for fonts with negative bearing (#99)
|
||||
- Sprites now have `scale_x` and `scale_y` attributes, allowing for aspect ratio changes. The
|
||||
existing `scale` attribute sets the overall scaling, and functions as before.
|
||||
- Sprites have a new `update` method which allows simultaneous changing of position, scale, and
|
||||
rotation. This reduces some overhead when changing multiple attributes at once.
|
||||
- The pyglet.resource module now defaults to a 2048x2048 texture for it's internal texture atlas,
|
||||
but will fall back to the maximum available size that the GPU can provide.
|
||||
- All modern joysticks and game controllers should now be detected on Linux and Windows.
|
||||
- Refactored and reimplemented pyglet.media. Many improvements to stability. Different drivers
|
||||
should now behave more similar.
|
||||
- WM_CLASS hints are now set on Linux. On modern Linux desktop environments and window managers,
|
||||
this allows for proper tracking of pyglet applications. Previously, pyglet apps may show up as
|
||||
"Unknown" under the active window list in the environment. The window class hints are set
|
||||
to the same name as the Window caption, but will fall back to "pyglet" if the Window caption
|
||||
contains non-ascii characters.
|
||||
- Vastly improved documentation and programming guide.
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
- Limit the minimum window size 1x1 pixel, preventing an OpenGL exception when resizing (#49).
|
||||
- Font module no longer leaks memory when text is changed (#66).
|
||||
- Fix crash on Python 2 when sys.argv[0] has non-ASCII characters (#74).
|
||||
- Windows: Fix crash when opening multiple windows in succession (#81).
|
||||
- Windows: Fix local font loading (#100).
|
||||
- Windows: Italic fonts no longer render parts of their neighbors.
|
||||
- Prevent memory leak from orphaned StreamingSources in long running applications (#103).
|
||||
- Windows: Fix kerning regression (#115)
|
||||
- Windows: Window.set_icon no longer fails when given a Texture instead of ImageData (#136)
|
||||
|
||||
Pyglet 1.2.3
|
||||
============
|
||||
Minor bugfix release.
|
||||
|
||||
Bugfixes:
|
||||
- Windows: Fix issue where ALT key causes app to hang.
|
||||
- Media: Many fixes to PulseAudio and OpenAL media drivers (a.o. #26, #27).
|
||||
- OSX: Fix stealing the menu when already present in cocoa.
|
||||
- Fix multi texturing support (#28).
|
||||
- OSX: Prevent segfault with multiple runs (#37/GC728)
|
||||
- ArchLinux: Fix segmentation fault when using gdk_pixbuf (#25)
|
||||
|
||||
Pyglet 1.2.2
|
||||
============
|
||||
Minor bugfix release. Includes documentation updates for deprecated code.
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
- BB#21: X11: Error on fontconfig cache eviction
|
||||
- BB#23: Windows: Disable ARB context on Intel devices.
|
||||
|
||||
Pyglet 1.2.1
|
||||
============
|
||||
Minor bugfix release. Fixes some issues on Linux.
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
- BB#18: X11: Events not processed while animating
|
||||
- X11: on_resize event not triggered
|
||||
- X11: Fix deletion of PulseAudioPlayer.
|
||||
|
||||
Pyglet 1.2
|
||||
==========
|
||||
The major 1.2 release brings pyglet to Python 3. Pyglet now also runs on 64-bit operating systems
|
||||
Linux, Windows and OS X. The OS X layer has switched to Cocoa.
|
||||
|
||||
Backwards-incompatible API changes:
|
||||
* renamed Label parameter 'halign' to 'align', fix for issue:460
|
||||
* remove unused module 'glext_missing' - everything should already be in 'glext_arb'
|
||||
|
||||
Python support
|
||||
--------------
|
||||
- 2.6 and up
|
||||
- NEW: 3.3 and up
|
||||
|
||||
Platform support
|
||||
----------------
|
||||
- Improved win32 support
|
||||
- Windows: DirectInput support
|
||||
- OSX: Joystick support
|
||||
- Linux: GL 3 support
|
||||
- Linux: ALSA replaced by PulseAudio
|
||||
- Windows: Tablet API support
|
||||
- OSX: Tablet support
|
||||
- Linux: Tablet support
|
||||
- OSX: Cocoa support
|
||||
- OSX: Support for PyObjC 2.3
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
- 294: pyglet.image.get_texture(rectangle=True) returns GL_TEXTURE_2D for tex.target if image is
|
||||
POT
|
||||
- 345: image mouse cursor shows up after fullscreen switch
|
||||
- 347: vowel in Thai language did not display
|
||||
- 353: X11: Wrong keysym reported in on_key_press/release if shift pressed.
|
||||
- 355: wraptypes does not wrap hex or octal enum values properly
|
||||
- 357: Non-conforming ATI cards missing glGenBuffers
|
||||
- 358: ResourceNotFoundException has spelling error in message (in pyglet 1.1.2)
|
||||
- 361: xlib: initial mouse dx/dy incorrect if mouse on window
|
||||
- 362: Support for generic attributes are broken
|
||||
- 363: pyglet.resource no longer finds files within directories
|
||||
- 368: permission issues with some doc and example files
|
||||
- 371: pyglet.sprite uses integer coordinates for drawing
|
||||
- 376: Windows Installer Ambiguous about Supported Python Versions
|
||||
- 377: on_mouse_scroll not being called with latest pyglet revision (from svn) on vista64
|
||||
- 379: pyglet.media.drivers.alsa not in trunk r2421
|
||||
- 380: mac osx HID not working
|
||||
- 381: Missing attribute in VertexDomain when changing the group attribute of a sprite multiple
|
||||
times
|
||||
- 382: evdev device name unicode problem
|
||||
- 387: input events stop being processed
|
||||
- 391: code cleanups for pyglet.image
|
||||
- 392: code cleanups for text and font
|
||||
- 393: code cleanup for input and app
|
||||
- 405: Virtual key codes are not mapped in OS X
|
||||
- 407: random crash with pyglet.clock.tick()
|
||||
- 408: IncrementalTextLayout, when deleted, still renders into its batch on document update
|
||||
- 409: pyglet.media.have_avbin missing in 1.2dev (svn rev 2445)
|
||||
- 411: Problem loading gif animation with alpha
|
||||
- 413: TileableTexture interchanges red and blue in some JPGs
|
||||
- 414: Carbon platform missing support for multiple mouse buttons during drag
|
||||
- 416: Endless loop in pyglet.resource.reindex()
|
||||
- 418: Vertical mouse scroll problem under windows
|
||||
- 422: Documentation: pyglet.resource and pats
|
||||
- 423: glMultiDrawElements called with incorrect arguments in method IndexedVertexDomain.draw
|
||||
- 424: Small documentation error in document layout model page
|
||||
- 426: Attempt to load resource from zipfile with no internal directory structure fail
|
||||
- 429: Exception when attributed text contains multiple trailing newlines
|
||||
- 439: EVENT_CLOSE test can't be passed
|
||||
- 440: tests/window/WINDOW_SET_MOUSE_SYSTEM_CURSOR does not exit when escape is pressed
|
||||
- 443: after the test window.EVENT_SHOW_HIDE process must be killed
|
||||
- 444: tests/resources/RES_LOAD_IMAGE opens a slew of windows and doesn't close them
|
||||
- 460: multiline label will not center align
|
||||
- 463: Min/Mag filter cannot be used with pyglets texture
|
||||
- 467: Setting the mouse position should be exposed to pyglet users
|
||||
- 471: Exception when clearing text of FormattedDocument with IncrementalTextLayout
|
||||
- 485: Wrapper generation (e.g. gengl.py) fails to parse L-prefaced character literals
|
||||
- 487: vendor specific keys on linux are crashing pyglet
|
||||
- 493: GdkPixbuf2ImageDecoder unable to decode images smaller than 1kb
|
||||
- 496: Another OpenGL 1.5 non-conforming video card
|
||||
- 510: Win-amd64 issues
|
||||
- 512: Fix missing parenthesis in docs
|
||||
- 517: tests/window/CONTEXT_SHARE.py glIsList exceptions
|
||||
- 519: Windows test log errors
|
||||
- 523: some incorrect key constants in the programming guide
|
||||
- 524: Pyglet 1.2dev events do not fire on OS X
|
||||
- 529: pyglet crashes on FreeBSD-8/amd64 if fonts are used. [patch included]
|
||||
- 533: pyglet.media.Player broken on Python3
|
||||
- 536: Pitch change functionality with pulseaudio driver.
|
||||
- 538: deleting text before an InlineElement fails to adjust its position properly, causes
|
||||
tracebacks if style changed later
|
||||
- 551: image encoder fails on python 3
|
||||
- 552: Memory leak in pyglet.image.load
|
||||
- 558: Patch for /doc/programming_guide/graphics.txt
|
||||
- 565: Race condition leads to crash calling glDeleteBuffers
|
||||
- 570: xlib 100% CPU usage after post_event
|
||||
- 571: pyglet fails for sys.platform=='linux3'
|
||||
- 572: Patch for /pyglet/lib.py
|
||||
- 579: Failing to load libraries that exist but have fatal problems is _silently_ ignored on Linux
|
||||
and OS X.
|
||||
- 580: image.DDS_RGB_DXT1_LOAD (and similar) throw ImageException
|
||||
- 610: Wrong messages in the NOTICE file
|
||||
- 611: Mouse event incorrect on OS-X
|
||||
- 616: Mention font.Font.have_font() in proguide, and expose font.have_font() for convenience
|
||||
- 624: mouse_motion events get delivered to cocoa windows on start
|
||||
- 625: osx platform segmentation fault when opening input devices
|
||||
- 630: pyglet 1.2alpha1 with Python 3.2
|
||||
- 637: 'pulse' audio driver sets the volume to 100%
|
||||
- 638: Player set_pitch crashes with 'directaudio' driver
|
||||
- 653: Unsupported PNG color type: 3
|
||||
- 657: gdiplus.py : n_delays must be long, not float.
|
||||
- 659: image.save method doesnt catch correct Exception
|
||||
- 660: platform_event_loop.post_event doesn't work for Cocoa loop
|
||||
- 661: Player.next is converted to Player.__next__ for python3
|
||||
- 662: gamepad cannot be found in linux
|
||||
- 664: bug in font/win32query.py on win x64 (its not always occur )
|
||||
- 665: remove_handler does not remove handler
|
||||
- 668: Sync pypng with upstream
|
||||
- 671: Support for local libraries
|
||||
- 672: User preferences shouldn't use ~/.ApplicationName/ but ~/.config/AplicationName/ in Linux
|
||||
- 673: Documentation building requirements are not documented
|
||||
- 674: README errors
|
||||
- 681: Tuple slice on ImageGrid documented but not implemented
|
||||
- 682: Documentation Link on homepage is incorrect.
|
||||
- 683: Improving "contribute" page
|
||||
- 684: Displaying large font fails under very specific conditions
|
||||
- 687: Exposing _draw_list_dirty in batch API.
|
||||
- 688: Doc folder refactorization
|
||||
- 692: docstring style modifications
|
||||
- 696: 2to3 convertsizip_longest to zip_longest
|
||||
- 699: SolidColorImagePattern and CheckerImagePattern appear to fail in python3 and pyglet1.2a
|
||||
- 704: [patch] py3 compatibility problem in pyglet/graphics/__init__.py
|
||||
- 710: resource path when using
|
||||
- 715: Patch for /pyglet/image/codecs/dds.py
|
||||
- 716: FIX: Pulseaudio / ctypes on Py3k
|
||||
- 718: False "ImageDecodeException: Not a GIF stream" in python3.x
|
||||
- 719: .bmp files traceback with py3
|
||||
- 720: tests/image compatibility StringIO vs BytesIO
|
||||
- 721: compatibilty py3 for tests/image TEXTURE_3D.py and TEXTURE_GRID.py
|
||||
- 722: TypeError in graphics test suite with python3.x
|
||||
- 723: py3 compatibility in tests/image MATRIX_RGB.py, MATRIX_RGBA.py
|
||||
- 724: py3 compatibility for png s (bytes vs str)
|
||||
- 727: tabs looking bad, especially in monospace fonts
|
||||
- 729: "ImportError: No module named future" in image.MATRIX_RGB test suite.
|
||||
- 731: Expectations about supported font formats
|
||||
- 734: spurious 'class plain' showing in sphinx rendering of doc/programming_guide/text.txt
|
||||
- 735: py3: BytesIO and disambiguate div for pyglet/image/__init__.py
|
||||
- 736: Pyglet media fails to close PulseAudio instances
|
||||
- 737: 1.2 programming guide mentions ALSA and not Pulse
|
||||
- 739: FIX: Prevent user mouse stupidity
|
||||
- 744: expectations and exploration of fonts in Windows
|
||||
- 747: Document pyglet's "shadow window" functionality
|
||||
- 754: tests/test.py -- AttributeError: 'module' object has no attribute 'platform' (i.e. pyglet
|
||||
has no platform)
|
||||
|
||||
New features
|
||||
------------
|
||||
- New eventloop implementation
|
||||
- Quartz image decoder
|
||||
- Improved documentation
|
||||
- new API: font.have_font(name) return True if named font is installed
|
||||
|
@ -0,0 +1,75 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
'''
|
||||
'''
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id: $'
|
||||
|
||||
import pyglet
|
||||
import sys
|
||||
|
||||
window = pyglet.window.Window()
|
||||
|
||||
@window.event
|
||||
def on_draw():
|
||||
window.clear()
|
||||
|
||||
remote = pyglet.input.get_apple_remote()
|
||||
if not remote:
|
||||
print('Apple IR Remote not available.')
|
||||
sys.exit(0)
|
||||
|
||||
remote.open(window, exclusive=True)
|
||||
|
||||
@remote.select_control.event
|
||||
def on_press():
|
||||
print('Press select')
|
||||
|
||||
@remote.menu_control.event
|
||||
def on_press():
|
||||
print('Press menu')
|
||||
|
||||
@remote.up_control.event
|
||||
def on_press():
|
||||
print('Press up')
|
||||
|
||||
@remote.down_control.event
|
||||
def on_press():
|
||||
print('Press down')
|
||||
|
||||
@remote.left_control.event
|
||||
def on_press():
|
||||
print('Press left')
|
||||
|
||||
@remote.right_control.event
|
||||
def on_press():
|
||||
print('Press right')
|
||||
|
||||
@remote.select_control.event
|
||||
def on_release():
|
||||
print('Release select')
|
||||
|
||||
@remote.menu_control.event
|
||||
def on_release():
|
||||
print('Release menu')
|
||||
|
||||
@remote.up_control.event
|
||||
def on_release():
|
||||
print('Release up')
|
||||
|
||||
@remote.down_control.event
|
||||
def on_release():
|
||||
print('Release down')
|
||||
|
||||
@remote.left_control.event
|
||||
def on_release():
|
||||
print('Release left')
|
||||
|
||||
@remote.right_control.event
|
||||
def on_release():
|
||||
print('Release right')
|
||||
|
||||
pyglet.app.run()
|
@ -0,0 +1,226 @@
|
||||
'''
|
||||
A silly demonstration of how to use the Apple remote.
|
||||
'''
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id: $'
|
||||
|
||||
import pyglet
|
||||
from pyglet.gl import *
|
||||
import sys
|
||||
|
||||
|
||||
class MainWindow(pyglet.window.Window):
|
||||
def __init__(self):
|
||||
super(MainWindow, self).__init__(visible=False)
|
||||
self.set_caption('Apple Remote Example')
|
||||
|
||||
# Look for the Apple Remote device.
|
||||
remote = pyglet.input.get_apple_remote()
|
||||
if not remote:
|
||||
print('Apple IR Remote not available.')
|
||||
sys.exit(0)
|
||||
|
||||
# Open the remote in exclusive mode so that pressing the remote
|
||||
# buttons does not activate Front Row, change volume, etc. while
|
||||
# the remote is being used by our program.
|
||||
remote.open(self, exclusive=True)
|
||||
|
||||
# We push this class onto the remote's event handler stack so that
|
||||
# the on_button_press and on_button_release methods which we define
|
||||
# below will be called for the appropriate remote events.
|
||||
remote.push_handlers(self)
|
||||
|
||||
self.carousel = Carousel()
|
||||
self.setup_opengl()
|
||||
pyglet.clock.schedule_interval(self.update, 1/60.0)
|
||||
|
||||
# Event handler for Apple Remote button press events.
|
||||
# The button parameter is a string specifying the button that was pressed.
|
||||
def on_button_press(self, button):
|
||||
print('on_button_press', button)
|
||||
|
||||
if button == 'up':
|
||||
self.carousel.scroll_up()
|
||||
elif button == 'down':
|
||||
self.carousel.scroll_down()
|
||||
elif button == 'left':
|
||||
self.carousel.step_left()
|
||||
elif button == 'right':
|
||||
self.carousel.step_right()
|
||||
elif button == 'left_hold':
|
||||
self.carousel.rotate_left()
|
||||
elif button == 'right_hold':
|
||||
self.carousel.rotate_right()
|
||||
elif button == 'select' or button == 'select_hold':
|
||||
self.carousel.swap_left()
|
||||
elif button == 'menu' or button == 'menu_hold':
|
||||
self.carousel.swap_right()
|
||||
|
||||
# Event handler for Apple Remote button release events.
|
||||
# The button parameter is a string specifying the button that was released.
|
||||
def on_button_release(self, button):
|
||||
print('on_button_release', button)
|
||||
|
||||
if button == 'left_hold':
|
||||
self.carousel.stop_rotating()
|
||||
elif button == 'right_hold':
|
||||
self.carousel.stop_rotating()
|
||||
|
||||
def on_draw(self):
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
|
||||
glLoadIdentity()
|
||||
gluLookAt(0,3,-12,0,3,0,0,1,0)
|
||||
self.carousel.draw()
|
||||
|
||||
def on_resize(self, width, height):
|
||||
glViewport(0, 0, width, height)
|
||||
glMatrixMode(GL_PROJECTION)
|
||||
glLoadIdentity()
|
||||
aspect = width / float(height)
|
||||
glFrustum(-1,1,-1.8/aspect,0.2/aspect,1,100)
|
||||
glMatrixMode(GL_MODELVIEW)
|
||||
return pyglet.event.EVENT_HANDLED
|
||||
|
||||
def setup_opengl(self):
|
||||
glClearColor(1,1,1,1)
|
||||
glEnable(GL_DEPTH_TEST)
|
||||
glEnable(GL_BLEND)
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
|
||||
|
||||
def update(self, dt):
|
||||
self.carousel.update(dt)
|
||||
|
||||
|
||||
class Carousel:
|
||||
"""A rotating collection of labeled tiles."""
|
||||
def __init__(self):
|
||||
self.num_tiles = 14
|
||||
self.index = 0
|
||||
self.float_index = 0.0
|
||||
self.float_increment = 1.0 / self.num_tiles
|
||||
self.angle = 0
|
||||
self.index_diff = 0
|
||||
self.is_rotating = False
|
||||
self.speed = 4 * self.num_tiles
|
||||
|
||||
# Create the tiles in the carousel.
|
||||
self.tiles = []
|
||||
colors = [(255,0,0), (0,255,0), (0,0,255), (255,255,0), (0,205,205), (128,0,128), (255,165,0)]
|
||||
class Tile:
|
||||
value = 0
|
||||
color = [255,255,255]
|
||||
for i in range(self.num_tiles):
|
||||
tile = Tile()
|
||||
tile.value = i % 26
|
||||
tile.color = colors[i%len(colors)]
|
||||
self.tiles.append(tile)
|
||||
|
||||
# Create glyphs for the characters displayed on the tiles.
|
||||
font = pyglet.font.load('Courier', 64)
|
||||
self.glyphs = font.get_glyphs('ABCDEFGHIJKLMNOPQRSTUVWXYZ')
|
||||
|
||||
def scroll_up(self):
|
||||
"""Increment the character displayed on the main tile."""
|
||||
self.tiles[self.index].value = (self.tiles[self.index].value + 1) % 26
|
||||
|
||||
def scroll_down(self):
|
||||
"""Decrement the character displayed on the main tile."""
|
||||
self.tiles[self.index].value = (self.tiles[self.index].value - 1) % 26
|
||||
|
||||
def swap_left(self):
|
||||
"""Swap the two left tiles."""
|
||||
i = self.index
|
||||
j = (self.index - 1) % self.num_tiles
|
||||
self.tiles[i], self.tiles[j] = self.tiles[j], self.tiles[i]
|
||||
|
||||
def swap_right(self):
|
||||
"""Swap the two right tiles."""
|
||||
i = self.index
|
||||
j = (self.index + 1) % self.num_tiles
|
||||
self.tiles[i], self.tiles[j] = self.tiles[j], self.tiles[i]
|
||||
|
||||
def step_left(self):
|
||||
"""Rotate the carousel one tile to the left."""
|
||||
self.direction = -1
|
||||
self.index_diff += 1.0
|
||||
|
||||
def step_right(self):
|
||||
"""Rotate the carousel one tile to the right."""
|
||||
self.direction = 1
|
||||
self.index_diff += 1.0
|
||||
|
||||
def rotate_left(self):
|
||||
"""Start the carousel rotating continuously to the left."""
|
||||
self.is_rotating = True
|
||||
self.direction = -1
|
||||
|
||||
def rotate_right(self):
|
||||
"""Start the carousel rotating continuously to the right."""
|
||||
self.is_rotating = True
|
||||
self.direction = 1
|
||||
|
||||
def stop_rotating(self):
|
||||
"""Stop continuous rotation and make sure we end up at a tile location."""
|
||||
self.index_diff = round(self.float_index) - self.float_index
|
||||
if self.index_diff < 0:
|
||||
self.direction = -1
|
||||
else:
|
||||
self.direction = 1
|
||||
self.index_diff = abs(self.index_diff)
|
||||
|
||||
def draw(self):
|
||||
glPushMatrix()
|
||||
glRotatef(-self.angle, 0, 1, 0)
|
||||
for i in range(self.num_tiles):
|
||||
self.draw_tile(i)
|
||||
glPopMatrix()
|
||||
|
||||
def draw_tile(self, index):
|
||||
angle = index * (360.0 / self.num_tiles)
|
||||
|
||||
glPushMatrix()
|
||||
glRotatef(angle,0,1,0)
|
||||
glTranslatef(0,0,-7.5)
|
||||
glRotatef(-angle+self.angle,0,1,0)
|
||||
|
||||
texture = self.glyphs[self.tiles[index].value].texture
|
||||
vertex_list = pyglet.graphics.vertex_list(4, 'v2f', ('t3f', texture.tex_coords))
|
||||
vertex_list.vertices[:] = [-1, -1, 1, -1, 1, 1, -1, 1]
|
||||
# Draw tile background.
|
||||
glColor3ub(*self.tiles[index].color)
|
||||
vertex_list.draw(GL_QUADS)
|
||||
# Draw tile label.
|
||||
glBindTexture(texture.target, texture.id)
|
||||
glEnable(texture.target)
|
||||
glColor3ub(0,0,0)
|
||||
vertex_list.vertices[:] = [.8, -.8, -.8, -.8, -.8, .8, .8, .8]
|
||||
glTranslatef(0,0,-.01)
|
||||
vertex_list.draw(GL_QUADS)
|
||||
glDisable(texture.target)
|
||||
glPopMatrix()
|
||||
|
||||
def update(self, dt):
|
||||
if self.is_rotating or self.index_diff:
|
||||
increment = self.direction * self.speed * self.float_increment * dt
|
||||
self.float_index = (self.float_index + increment) % self.num_tiles
|
||||
|
||||
if self.index_diff:
|
||||
self.index_diff -= abs(increment)
|
||||
if self.index_diff < 0:
|
||||
self.index_diff = 0
|
||||
self.float_index = round(self.float_index) % self.num_tiles
|
||||
self.index = int(self.float_index)
|
||||
self.is_rotating = False
|
||||
|
||||
self.angle = (self.float_index / self.num_tiles) * 360
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
window = MainWindow()
|
||||
window.clear()
|
||||
window.flip()
|
||||
window.set_visible(True)
|
||||
pyglet.app.run()
|
@ -0,0 +1,9 @@
|
||||
Astraea
|
||||
=======
|
||||
|
||||
This is an example program that accompanies pyglet (http://www.pyglet.org).
|
||||
|
||||
The source code is licensed under the BSD license, which is quite permissive
|
||||
(see the source header for details).
|
||||
|
||||
All artwork is placed is Copyright 2007 Alex Holkner.
|
After Width: | Height: | Size: 4.2 KiB |
@ -0,0 +1,209 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://web.resource.org/cc/"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="32"
|
||||
height="32"
|
||||
id="svg3070"
|
||||
sodipodi:version="0.32"
|
||||
inkscape:version="0.44.1"
|
||||
sodipodi:docbase="/home/alex/projects/pyglet/examples/astraea/assets"
|
||||
sodipodi:docname="ship.svg"
|
||||
version="1.0"
|
||||
inkscape:export-filename="/home/alex/projects/pyglet/examples/astraea/res/ship.png"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90">
|
||||
<defs
|
||||
id="defs3072">
|
||||
<linearGradient
|
||||
id="linearGradient3119">
|
||||
<stop
|
||||
style="stop-color:white;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3121" />
|
||||
<stop
|
||||
style="stop-color:black;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop3123" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3109">
|
||||
<stop
|
||||
style="stop-color:white;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3111" />
|
||||
<stop
|
||||
style="stop-color:#655c5c;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop3113" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3099">
|
||||
<stop
|
||||
style="stop-color:#6b49ff;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3101" />
|
||||
<stop
|
||||
id="stop3107"
|
||||
offset="0.5"
|
||||
style="stop-color:#eebacf;stop-opacity:1;" />
|
||||
<stop
|
||||
style="stop-color:#4500ff;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop3103" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3109"
|
||||
id="radialGradient3145"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(5.070103e-2,0,0,6.638822e-2,59.18162,49.76278)"
|
||||
cx="302.85715"
|
||||
cy="180.26057"
|
||||
fx="302.85715"
|
||||
fy="180.26057"
|
||||
r="283.35715" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3099"
|
||||
id="linearGradient3149"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="199.5"
|
||||
y1="260.93323"
|
||||
x2="411.92856"
|
||||
y2="260.93323"
|
||||
gradientTransform="matrix(5.070103e-2,0,0,5.639934e-2,59.18162,47.44653)" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3119"
|
||||
id="radialGradient3153"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(6.320242e-2,1.027872e-2,-5.698439e-3,4.335767e-2,56.84709,46.03829)"
|
||||
cx="248.53267"
|
||||
cy="135.56001"
|
||||
fx="248.53267"
|
||||
fy="135.56001"
|
||||
r="94.22625" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3109"
|
||||
id="radialGradient3171"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(5.070103e-2,0,0,6.638822e-2,59.18162,49.76278)"
|
||||
cx="302.85715"
|
||||
cy="180.26057"
|
||||
fx="302.85715"
|
||||
fy="180.26057"
|
||||
r="283.35715" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3099"
|
||||
id="linearGradient3173"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(5.070103e-2,0,0,5.639934e-2,59.18162,47.44653)"
|
||||
x1="199.5"
|
||||
y1="260.93323"
|
||||
x2="411.92856"
|
||||
y2="260.93323" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3119"
|
||||
id="radialGradient3175"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(6.320242e-2,1.027872e-2,-5.698439e-3,4.335767e-2,56.84709,46.03829)"
|
||||
cx="248.53267"
|
||||
cy="135.56001"
|
||||
fx="248.53267"
|
||||
fy="135.56001"
|
||||
r="94.22625" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="black"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
gridtolerance="10000"
|
||||
guidetolerance="10"
|
||||
objecttolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="7.9195959"
|
||||
inkscape:cx="23.023399"
|
||||
inkscape:cy="1.0153475"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showguides="true"
|
||||
inkscape:guide-bbox="true"
|
||||
inkscape:window-width="1230"
|
||||
inkscape:window-height="972"
|
||||
inkscape:window-x="1280"
|
||||
inkscape:window-y="0"
|
||||
height="32px"
|
||||
width="32px" />
|
||||
<metadata
|
||||
id="metadata3075">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-58.43315,-45.9713)">
|
||||
<g
|
||||
id="g3162"
|
||||
transform="matrix(0,1,-1,0,136.5487,-12.6049)"
|
||||
inkscape:export-filename="/home/alex/projects/pyglet/examples/astraea/res/g3162.png"
|
||||
inkscape:export-xdpi="92.260002"
|
||||
inkscape:export-ydpi="92.260002">
|
||||
<g
|
||||
transform="matrix(5.407223e-2,0,0,5.923622e-2,58.21259,46.53637)"
|
||||
style="fill:#878787;fill-opacity:1"
|
||||
id="g3155">
|
||||
<path
|
||||
id="path3080"
|
||||
style="fill:#878787;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="M 39.999991,521.64789 C 39.999991,521.64789 -1.0714379,497.09432 19.999991,413.79075 C 41.07142,330.48718 99.99999,230.57647 197.14285,190.93362 C 197.14285,190.93362 408.57143,190.93362 408.57143,190.93362 C 505.71429,230.57647 565.98215,335.48718 585.71429,413.79075 C 605.44643,492.09432 566.42858,521.64789 566.42858,521.64789 L 39.999991,521.64789 z "
|
||||
sodipodi:nodetypes="czsszcc" />
|
||||
<path
|
||||
style="fill:#878787;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="M 302.85715,-0.4949608 C 293.78238,-0.56477143 200,3.7689443 200,190.93361 C 200,190.93361 144.60089,485.14868 182.82741,499.17944 C 248.05316,523.12006 387.09307,523.87754 439.71284,501.19974 C 469.58981,488.32351 411.42857,189.50504 411.42857,189.50504 C 411.42857,1.5379353 311.75335,-0.42515017 302.85715,-0.4949608 z "
|
||||
id="path3089"
|
||||
sodipodi:nodetypes="cssssz" />
|
||||
<path
|
||||
style="fill:#878787;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:6;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 302.03561,4.833993 C 258.85159,4.833993 208.91203,70.588415 213.14219,146.25535 C 213.96558,160.98368 398.45016,164.68873 399.01025,149.28581 C 402.1541,61.817775 345.21963,4.833993 302.03561,4.833993 z "
|
||||
id="path3117"
|
||||
sodipodi:nodetypes="csss" />
|
||||
</g>
|
||||
<path
|
||||
id="path3143"
|
||||
style="fill:url(#radialGradient3171);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="M 61.209659,76.867127 C 61.209659,76.867127 59.127295,75.482322 60.195639,70.784056 C 61.263982,66.08579 64.25172,60.450892 69.176962,58.215062 C 69.176962,58.215062 79.896607,58.215062 79.896607,58.215062 C 84.821849,60.450892 87.877492,66.367787 88.877931,70.784056 C 89.878371,75.200327 87.900125,76.867127 87.900125,76.867127 L 61.209659,76.867127 z "
|
||||
sodipodi:nodetypes="czsszcc" />
|
||||
<path
|
||||
style="fill:url(#linearGradient3173);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="M 74.536785,47.418616 C 74.076685,47.414679 69.321823,47.659098 69.321823,58.215061 C 69.321823,58.215061 66.513032,74.808597 68.451155,75.599922 C 71.758167,76.950157 78.807632,76.992879 81.475508,75.713866 C 82.990301,74.987654 80.041467,58.134491 80.041467,58.134491 C 80.041467,47.53327 74.987832,47.422553 74.536785,47.418616 z "
|
||||
id="path3147"
|
||||
sodipodi:nodetypes="cssssz" />
|
||||
<path
|
||||
style="fill:url(#radialGradient3175);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:6;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 74.495132,47.719164 C 72.305658,47.719164 69.773672,51.427672 69.988145,55.695237 C 70.029892,56.525905 79.383448,56.734867 79.411846,55.866153 C 79.571242,50.933013 76.684606,47.719164 74.495132,47.719164 z "
|
||||
id="path3151"
|
||||
sodipodi:nodetypes="csss" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 8.7 KiB |
@ -0,0 +1,895 @@
|
||||
#!/usr/bin/env python
|
||||
# ----------------------------------------------------------------------------
|
||||
# pyglet
|
||||
# Copyright (c) 2006-2008 Alex Holkner
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in
|
||||
# the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of pyglet nor the names of its
|
||||
# contributors may be used to endorse or promote products
|
||||
# derived from this software without specific prior written
|
||||
# permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
'''A sprite-based game loosely based on the classic "Asteroids".
|
||||
|
||||
Shoot the asteroids, get high score.
|
||||
|
||||
Left/right: Turn ship
|
||||
Up: Thrusters
|
||||
Space: Shoot
|
||||
'''
|
||||
|
||||
import math
|
||||
import os
|
||||
import random
|
||||
import sys
|
||||
|
||||
import pyglet
|
||||
from pyglet.gl import *
|
||||
from pyglet import resource
|
||||
from pyglet.window import key
|
||||
|
||||
PLAYER_SPIN_SPEED = 360.
|
||||
PLAYER_ACCEL = 200.
|
||||
PLAYER_FIRE_DELAY = 0.1
|
||||
|
||||
BULLET_SPEED = 1000.
|
||||
|
||||
MAX_ASTEROID_SPIN_SPEED = 180.
|
||||
MAX_ASTEROID_SPEED = 100.
|
||||
|
||||
INITIAL_ASTEROIDS = [2, 3, 4, 5]
|
||||
ASTEROID_DEBRIS_COUNT = 3
|
||||
MAX_DIFFICULTY = len(INITIAL_ASTEROIDS) - 1
|
||||
|
||||
ARENA_WIDTH = 640
|
||||
ARENA_HEIGHT = 480
|
||||
|
||||
KEY_FIRE = key.SPACE
|
||||
KEY_PAUSE = key.ESCAPE
|
||||
|
||||
COLLISION_RESOLUTION = 8
|
||||
|
||||
SMOKE_ANIMATION_PERIOD = 0.05
|
||||
EXPLOSION_ANIMATION_PERIOD = 0.07
|
||||
PLAYER_FLASH_PERIOD = 0.15
|
||||
|
||||
GET_READY_DELAY = 1.
|
||||
BEGIN_PLAY_DELAY = 2.
|
||||
LIFE_LOST_DELAY = 2.
|
||||
|
||||
FONT_NAME = ('Verdana', 'Helvetica', 'Arial')
|
||||
|
||||
INSTRUCTIONS = \
|
||||
'''Your ship is lost in a peculiar unchartered area of space-time infested with asteroids! You have no chance for survival except to rack up the highest score possible.
|
||||
|
||||
Left/Right: Turn ship
|
||||
Up: Thrusters
|
||||
Space: Shoot
|
||||
|
||||
Be careful, there's not much friction in space.'''
|
||||
|
||||
def center_anchor(img):
|
||||
img.anchor_x = img.width // 2
|
||||
img.anchor_y = img.height // 2
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# Game objects
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
def wrap(value, width):
|
||||
if value > width:
|
||||
value -= width
|
||||
if value < 0:
|
||||
value += width
|
||||
return value
|
||||
|
||||
def to_radians(degrees):
|
||||
return math.pi * degrees / 180.0
|
||||
|
||||
class WrappingSprite(pyglet.sprite.Sprite):
|
||||
dx = 0
|
||||
dy = 0
|
||||
rotation_speed = 0
|
||||
|
||||
def __init__(self, img, x, y, batch=None):
|
||||
super(WrappingSprite, self).__init__(img, x, y, batch=batch)
|
||||
self.collision_radius = self.image.width // COLLISION_RESOLUTION // 2
|
||||
|
||||
def update(self, dt):
|
||||
x = self.x + self.dx * dt
|
||||
y = self.y + self.dy * dt
|
||||
rotation = self.rotation + self.rotation_speed * dt
|
||||
|
||||
self.x = wrap(x, ARENA_WIDTH)
|
||||
self.y = wrap(y, ARENA_HEIGHT)
|
||||
self.rotation = wrap(rotation, 360.)
|
||||
|
||||
def collision_cells(self):
|
||||
'''Generate a sequence of (x, y) cells this object covers,
|
||||
approximately.'''
|
||||
radius = self.collision_radius
|
||||
cellx = int(self.x / COLLISION_RESOLUTION)
|
||||
celly = int(self.y / COLLISION_RESOLUTION)
|
||||
for y in range(celly - radius, celly + radius + 1):
|
||||
for x in range(cellx - radius, cellx + radius + 1):
|
||||
yield x, y
|
||||
|
||||
class AsteroidSize(object):
|
||||
def __init__(self, filename, points):
|
||||
self.img = resource.image(filename)
|
||||
center_anchor(self.img)
|
||||
self.next_size = None
|
||||
self.points = points
|
||||
|
||||
class Asteroid(WrappingSprite):
|
||||
def __init__(self, size, x, y, batch=None):
|
||||
super(Asteroid, self).__init__(size.img, x, y, batch=batch)
|
||||
self.dx = (random.random() - 0.5) * MAX_ASTEROID_SPEED
|
||||
self.dy = (random.random() - 0.5) * MAX_ASTEROID_SPEED
|
||||
self.size = size
|
||||
self.rotation = random.random() * 360.
|
||||
self.rotation_speed = (random.random() - 0.5) * MAX_ASTEROID_SPIN_SPEED
|
||||
self.hit = False
|
||||
|
||||
def destroy(self):
|
||||
global score
|
||||
score += self.size.points
|
||||
|
||||
# Modifies the asteroids list.
|
||||
next_size = self.size.next_size
|
||||
if next_size:
|
||||
# Spawn debris
|
||||
for i in range(ASTEROID_DEBRIS_COUNT):
|
||||
asteroids.append(Asteroid(next_size, self.x, self.y,
|
||||
batch=self.batch))
|
||||
|
||||
self.delete()
|
||||
asteroids.remove(self)
|
||||
|
||||
class Player(WrappingSprite, key.KeyStateHandler):
|
||||
def __init__(self, img, batch=None):
|
||||
super(Player, self).__init__(img, ARENA_WIDTH // 2, ARENA_HEIGHT // 2,
|
||||
batch=batch)
|
||||
center_anchor(img)
|
||||
self.reset()
|
||||
|
||||
def reset(self):
|
||||
self.x = ARENA_WIDTH // 2
|
||||
self.y = ARENA_HEIGHT // 2
|
||||
self.dx = 0
|
||||
self.dy = 0
|
||||
self.rotation = 0
|
||||
self.fire_timeout = 0
|
||||
self.hit = False
|
||||
self.invincible = True
|
||||
self.visible = True
|
||||
|
||||
self.flash_timeout = 0
|
||||
self.flash_visible = False
|
||||
|
||||
def update(self, dt):
|
||||
# Update rotation
|
||||
if self[key.LEFT]:
|
||||
self.rotation -= PLAYER_SPIN_SPEED * dt
|
||||
if self[key.RIGHT]:
|
||||
self.rotation += PLAYER_SPIN_SPEED * dt
|
||||
|
||||
# Get x/y components of orientation
|
||||
rotation_x = math.cos(to_radians(-self.rotation))
|
||||
rotation_y = math.sin(to_radians(-self.rotation))
|
||||
|
||||
# Update velocity
|
||||
if self[key.UP]:
|
||||
self.dx += PLAYER_ACCEL * rotation_x * dt
|
||||
self.dy += PLAYER_ACCEL * rotation_y * dt
|
||||
|
||||
# Update position
|
||||
super(Player, self).update(dt)
|
||||
|
||||
# Fire bullet?
|
||||
self.fire_timeout -= dt
|
||||
if self[KEY_FIRE] and self.fire_timeout <= 0 and not self.invincible:
|
||||
self.fire_timeout = PLAYER_FIRE_DELAY
|
||||
|
||||
# For simplicity, start the bullet at the player position. If the
|
||||
# ship were bigger, or if bullets moved slower we'd adjust this
|
||||
# based on the orientation of the ship.
|
||||
bullets.append(Bullet(self.x, self.y,
|
||||
rotation_x * BULLET_SPEED,
|
||||
rotation_y * BULLET_SPEED, batch=batch))
|
||||
|
||||
if enable_sound:
|
||||
bullet_sound.play()
|
||||
|
||||
# Update flash (invincible) animation
|
||||
if self.invincible:
|
||||
self.flash_timeout -= dt
|
||||
if self.flash_timeout <= 0:
|
||||
self.flash_timeout = PLAYER_FLASH_PERIOD
|
||||
self.flash_visible = not self.flash_visible
|
||||
else:
|
||||
self.flash_visible = True
|
||||
|
||||
self.opacity = (self.visible and self.flash_visible) and 255 or 0
|
||||
|
||||
class MovingSprite(pyglet.sprite.Sprite):
|
||||
def __init__(self, image, x, y, dx, dy, batch=None):
|
||||
super(MovingSprite, self).__init__(image, x, y, batch=batch)
|
||||
self.dx = dx
|
||||
self.dy = dy
|
||||
|
||||
def update(self, dt):
|
||||
self.x += self.dx * dt
|
||||
self.y += self.dy * dt
|
||||
|
||||
class Bullet(MovingSprite):
|
||||
def __init__(self, x, y, dx, dy, batch=None):
|
||||
super(Bullet, self).__init__(bullet_image, x, y, dx, dy, batch=batch)
|
||||
|
||||
def update(self, dt):
|
||||
self.x += self.dx * dt
|
||||
self.y += self.dy * dt
|
||||
if not (self.x >= 0 and self.x < ARENA_WIDTH and
|
||||
self.y >= 0 and self.y < ARENA_HEIGHT):
|
||||
self.delete()
|
||||
bullets.remove(self)
|
||||
|
||||
class EffectSprite(MovingSprite):
|
||||
def on_animation_end(self):
|
||||
self.delete()
|
||||
animations.remove(self)
|
||||
|
||||
class Starfield(object):
|
||||
def __init__(self, img):
|
||||
self.x = 0
|
||||
self.y = 0
|
||||
self.dx = 0.05
|
||||
self.dy = -0.06
|
||||
self.img = img
|
||||
|
||||
def update(self, dt):
|
||||
self.x += self.dx * dt
|
||||
self.y += self.dy * dt
|
||||
|
||||
def draw(self):
|
||||
# Fiddle with the texture matrix to make the starfield slide slowly
|
||||
# over the window.
|
||||
glMatrixMode(GL_TEXTURE)
|
||||
glPushMatrix()
|
||||
glTranslatef(self.x, self.y, 0)
|
||||
|
||||
self.img.blit(0, 0, width=ARENA_WIDTH, height=ARENA_HEIGHT)
|
||||
|
||||
glPopMatrix()
|
||||
glMatrixMode(GL_MODELVIEW)
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# Overlays, such as menus and "Game Over" banners
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
class Overlay(object):
|
||||
def update(self, dt):
|
||||
pass
|
||||
|
||||
def draw(self):
|
||||
pass
|
||||
|
||||
class Banner(Overlay):
|
||||
def __init__(self, label, dismiss_func=None, timeout=None):
|
||||
self.text = pyglet.text.Label(label,
|
||||
font_name=FONT_NAME,
|
||||
font_size=36,
|
||||
x=ARENA_WIDTH // 2,
|
||||
y=ARENA_HEIGHT // 2,
|
||||
anchor_x='center',
|
||||
anchor_y='center')
|
||||
|
||||
self.dismiss_func = dismiss_func
|
||||
self.timeout = timeout
|
||||
if timeout and dismiss_func:
|
||||
pyglet.clock.schedule_once(dismiss_func, timeout)
|
||||
|
||||
def draw(self):
|
||||
self.text.draw()
|
||||
|
||||
def on_key_press(self, symbol, modifiers):
|
||||
if self.dismiss_func and not self.timeout:
|
||||
self.dismiss_func()
|
||||
return True
|
||||
|
||||
class Menu(Overlay):
|
||||
def __init__(self, title):
|
||||
self.items = []
|
||||
self.title_text = pyglet.text.Label(title,
|
||||
font_name=FONT_NAME,
|
||||
font_size=36,
|
||||
x=ARENA_WIDTH // 2,
|
||||
y=350,
|
||||
anchor_x='center',
|
||||
anchor_y='center')
|
||||
|
||||
def reset(self):
|
||||
self.selected_index = 0
|
||||
self.items[self.selected_index].selected = True
|
||||
|
||||
def on_key_press(self, symbol, modifiers):
|
||||
if symbol == key.DOWN:
|
||||
self.selected_index += 1
|
||||
elif symbol == key.UP:
|
||||
self.selected_index -= 1
|
||||
self.selected_index = min(max(self.selected_index, 0),
|
||||
len(self.items) - 1)
|
||||
|
||||
if symbol in (key.DOWN, key.UP) and enable_sound:
|
||||
bullet_sound.play()
|
||||
|
||||
def on_key_release(self, symbol, modifiers):
|
||||
self.items[self.selected_index].on_key_release(symbol, modifiers)
|
||||
|
||||
def draw(self):
|
||||
self.title_text.draw()
|
||||
for i, item in enumerate(self.items):
|
||||
item.draw(i == self.selected_index)
|
||||
|
||||
class MenuItem(object):
|
||||
pointer_color = (.46, 0, 1.)
|
||||
inverted_pointers = False
|
||||
|
||||
def __init__(self, label, y, activate_func):
|
||||
self.y = y
|
||||
self.text = pyglet.text.Label(label,
|
||||
font_name=FONT_NAME,
|
||||
font_size=14,
|
||||
x=ARENA_WIDTH // 2,
|
||||
y=y,
|
||||
anchor_x='center',
|
||||
anchor_y='center')
|
||||
self.activate_func = activate_func
|
||||
|
||||
def draw_pointer(self, x, y, color, flip=False):
|
||||
# Tint the pointer image to a color
|
||||
glPushAttrib(GL_CURRENT_BIT)
|
||||
glColor3f(*color)
|
||||
if flip:
|
||||
pointer_image_flip.blit(x, y)
|
||||
else:
|
||||
pointer_image.blit(x, y)
|
||||
glPopAttrib()
|
||||
|
||||
def draw(self, selected):
|
||||
self.text.draw()
|
||||
|
||||
if selected:
|
||||
self.draw_pointer(
|
||||
self.text.x - self.text.content_width // 2 -
|
||||
pointer_image.width // 2,
|
||||
self.y,
|
||||
self.pointer_color,
|
||||
self.inverted_pointers)
|
||||
self.draw_pointer(
|
||||
self.text.x + self.text.content_width // 2 +
|
||||
pointer_image.width // 2,
|
||||
self.y,
|
||||
self.pointer_color,
|
||||
not self.inverted_pointers)
|
||||
|
||||
def on_key_release(self, symbol, modifiers):
|
||||
if symbol == key.ENTER and self.activate_func:
|
||||
self.activate_func()
|
||||
if enable_sound:
|
||||
bullet_sound.play()
|
||||
|
||||
class ToggleMenuItem(MenuItem):
|
||||
pointer_color = (.27, .82, .25)
|
||||
inverted_pointers = True
|
||||
|
||||
def __init__(self, label, value, y, toggle_func):
|
||||
self.value = value
|
||||
self.label = label
|
||||
self.toggle_func = toggle_func
|
||||
super(ToggleMenuItem, self).__init__(self.get_label(), y, None)
|
||||
|
||||
def get_label(self):
|
||||
return self.label + (self.value and ': ON' or ': OFF')
|
||||
|
||||
def on_key_release(self, symbol, modifiers):
|
||||
if symbol == key.LEFT or symbol == key.RIGHT:
|
||||
self.value = not self.value
|
||||
self.text.text = self.get_label()
|
||||
self.toggle_func(self.value)
|
||||
if enable_sound:
|
||||
bullet_sound.play()
|
||||
|
||||
class DifficultyMenuItem(MenuItem):
|
||||
pointer_color = (.27, .82, .25)
|
||||
inverted_pointers = True
|
||||
|
||||
def __init__(self, y):
|
||||
super(DifficultyMenuItem, self).__init__(self.get_label(), y, None)
|
||||
|
||||
def get_label(self):
|
||||
if difficulty == 0:
|
||||
return 'Difficulty: Pebbles'
|
||||
elif difficulty == 1:
|
||||
return 'Difficulty: Stones'
|
||||
elif difficulty == 2:
|
||||
return 'Difficulty: Asteroids'
|
||||
elif difficulty == 3:
|
||||
return 'Difficulty: Meteors'
|
||||
else:
|
||||
return 'Difficulty: %d' % difficulty
|
||||
|
||||
def on_key_release(self, symbol, modifiers):
|
||||
global difficulty
|
||||
if symbol == key.LEFT:
|
||||
difficulty -= 1
|
||||
elif symbol == key.RIGHT:
|
||||
difficulty += 1
|
||||
difficulty = min(max(difficulty, 0), MAX_DIFFICULTY)
|
||||
self.text.text = self.get_label()
|
||||
|
||||
if symbol in (key.LEFT, key.RIGHT) and enable_sound:
|
||||
bullet_sound.play()
|
||||
|
||||
class MainMenu(Menu):
|
||||
def __init__(self):
|
||||
super(MainMenu, self).__init__('Astraea')
|
||||
|
||||
self.items.append(MenuItem('New Game', 240, begin_game))
|
||||
self.items.append(MenuItem('Instructions', 200,
|
||||
begin_instructions_menu))
|
||||
self.items.append(MenuItem('Options', 160, begin_options_menu))
|
||||
self.items.append(MenuItem('Quit', 120, sys.exit))
|
||||
self.reset()
|
||||
|
||||
class OptionsMenu(Menu):
|
||||
def __init__(self):
|
||||
super(OptionsMenu, self).__init__('Options')
|
||||
|
||||
self.items.append(DifficultyMenuItem(280))
|
||||
def set_enable_sound(value):
|
||||
global enable_sound
|
||||
enable_sound = value
|
||||
self.items.append(ToggleMenuItem('Sound', enable_sound, 240,
|
||||
set_enable_sound))
|
||||
|
||||
def set_enable_fullscreen(value):
|
||||
win.set_fullscreen(value, width=ARENA_WIDTH, height=ARENA_HEIGHT)
|
||||
self.items.append(ToggleMenuItem('Fullscreen', win.fullscreen, 200,
|
||||
set_enable_fullscreen))
|
||||
|
||||
self.items.append(ToggleMenuItem('Vsync', win.vsync, 160,
|
||||
win.set_vsync))
|
||||
|
||||
def set_show_fps(value):
|
||||
global show_fps
|
||||
show_fps = value
|
||||
self.items.append(ToggleMenuItem('FPS', show_fps, 120, set_show_fps))
|
||||
self.items.append(MenuItem('Ok', 60, begin_main_menu))
|
||||
self.reset()
|
||||
|
||||
class InstructionsMenu(Menu):
|
||||
def __init__(self):
|
||||
super(InstructionsMenu, self).__init__('Instructions')
|
||||
|
||||
self.items.append(MenuItem('Ok', 50, begin_main_menu))
|
||||
self.reset()
|
||||
|
||||
self.instruction_text = pyglet.text.Label(INSTRUCTIONS,
|
||||
font_name=FONT_NAME,
|
||||
font_size=14,
|
||||
x=20, y=300,
|
||||
width=ARENA_WIDTH - 40,
|
||||
anchor_y='top',
|
||||
multiline=True)
|
||||
|
||||
def draw(self):
|
||||
super(InstructionsMenu, self).draw()
|
||||
self.instruction_text.draw()
|
||||
|
||||
class PauseMenu(Menu):
|
||||
def __init__(self):
|
||||
super(PauseMenu, self).__init__('Paused')
|
||||
|
||||
self.items.append(MenuItem('Continue Game', 240, resume_game))
|
||||
self.items.append(MenuItem('Main Menu', 200, end_game))
|
||||
self.reset()
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# Game state functions
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
def check_collisions():
|
||||
# Check for collisions using an approximate uniform grid.
|
||||
#
|
||||
# 1. Mark all grid cells that the bullets are in
|
||||
# 2. Mark all grid cells that the player is in
|
||||
# 3. For each asteroid, check grid cells that are covered for
|
||||
# a collision.
|
||||
#
|
||||
# This is by no means perfect collision detection (in particular,
|
||||
# there are rounding errors, and it doesn't take into account the
|
||||
# arena wrapping). Improving it is left as an exercise for the
|
||||
# reader.
|
||||
|
||||
# The collision grid. It is recreated each iteration, as bullets move
|
||||
# quickly.
|
||||
hit_squares = {}
|
||||
|
||||
# 1. Mark all grid cells that the bullets are in. Assume bullets
|
||||
# occupy a single cell.
|
||||
for bullet in bullets:
|
||||
hit_squares[int(bullet.x / COLLISION_RESOLUTION),
|
||||
int(bullet.y / COLLISION_RESOLUTION)] = bullet
|
||||
|
||||
# 2. Mark all grid cells that the player is in.
|
||||
for x, y in player.collision_cells():
|
||||
hit_squares[x, y] = player
|
||||
|
||||
# 3. Check grid cells of each asteroid for a collision.
|
||||
for asteroid in asteroids:
|
||||
for x, y in asteroid.collision_cells():
|
||||
if (x, y) in hit_squares:
|
||||
asteroid.hit = True
|
||||
hit_squares[x, y].hit = True
|
||||
del hit_squares[x, y]
|
||||
|
||||
def begin_main_menu():
|
||||
set_overlay(MainMenu())
|
||||
|
||||
def begin_options_menu():
|
||||
set_overlay(OptionsMenu())
|
||||
|
||||
def begin_instructions_menu():
|
||||
set_overlay(InstructionsMenu())
|
||||
|
||||
def begin_game():
|
||||
global player_lives
|
||||
global score
|
||||
player_lives = 3
|
||||
score = 0
|
||||
|
||||
begin_clear_background()
|
||||
set_overlay(Banner('Get Ready', begin_first_round, GET_READY_DELAY))
|
||||
|
||||
def begin_first_round(*args):
|
||||
player.reset()
|
||||
player.visible = True
|
||||
begin_round()
|
||||
|
||||
def next_round(*args):
|
||||
global in_game
|
||||
player.invincible = True
|
||||
in_game = False
|
||||
set_overlay(Banner('Get Ready', begin_round, GET_READY_DELAY))
|
||||
|
||||
def begin_round(*args):
|
||||
global asteroids
|
||||
global bullets
|
||||
global animations
|
||||
global in_game
|
||||
asteroids = []
|
||||
for i in range(INITIAL_ASTEROIDS[difficulty]):
|
||||
x = random.random() * ARENA_WIDTH
|
||||
y = random.random() * ARENA_HEIGHT
|
||||
asteroids.append(Asteroid(asteroid_sizes[-1], x, y, wrapping_batch))
|
||||
|
||||
for bullet in bullets:
|
||||
bullet.delete()
|
||||
|
||||
for animation in animations:
|
||||
animation.delete()
|
||||
|
||||
bullets = []
|
||||
animations = []
|
||||
in_game = True
|
||||
set_overlay(None)
|
||||
pyglet.clock.schedule_once(begin_play, BEGIN_PLAY_DELAY)
|
||||
|
||||
def begin_play(*args):
|
||||
player.invincible = False
|
||||
|
||||
def begin_life(*args):
|
||||
player.reset()
|
||||
pyglet.clock.schedule_once(begin_play, BEGIN_PLAY_DELAY)
|
||||
|
||||
def life_lost(*args):
|
||||
global player_lives
|
||||
player_lives -= 1
|
||||
|
||||
if player_lives > 0:
|
||||
begin_life()
|
||||
else:
|
||||
game_over()
|
||||
|
||||
def game_over():
|
||||
set_overlay(Banner('Game Over', end_game))
|
||||
|
||||
def pause_game():
|
||||
global paused
|
||||
paused = True
|
||||
set_overlay(PauseMenu())
|
||||
|
||||
def resume_game():
|
||||
global paused
|
||||
paused = False
|
||||
set_overlay(None)
|
||||
|
||||
def end_game():
|
||||
global in_game
|
||||
global paused
|
||||
paused = False
|
||||
in_game = False
|
||||
player.invincible = True
|
||||
pyglet.clock.unschedule(life_lost)
|
||||
pyglet.clock.unschedule(begin_play)
|
||||
begin_menu_background()
|
||||
set_overlay(MainMenu())
|
||||
|
||||
def set_overlay(new_overlay):
|
||||
global overlay
|
||||
if overlay:
|
||||
win.remove_handlers(overlay)
|
||||
overlay = new_overlay
|
||||
if overlay:
|
||||
win.push_handlers(overlay)
|
||||
|
||||
def begin_menu_background():
|
||||
global asteroids
|
||||
global bullets
|
||||
global animations
|
||||
global in_game
|
||||
global player_lives
|
||||
|
||||
asteroids = []
|
||||
for i in range(11):
|
||||
x = random.random() * ARENA_WIDTH
|
||||
y = random.random() * ARENA_HEIGHT
|
||||
asteroids.append(Asteroid(asteroid_sizes[i // 4], x, y, wrapping_batch))
|
||||
|
||||
for bullet in bullets:
|
||||
bullet.delete()
|
||||
|
||||
for animation in animations:
|
||||
animation.delete()
|
||||
|
||||
bullets = []
|
||||
animations = []
|
||||
in_game = False
|
||||
player_lives = 0
|
||||
player.visible = False
|
||||
|
||||
def begin_clear_background():
|
||||
global asteroids
|
||||
global bullets
|
||||
global animations
|
||||
|
||||
for bullet in bullets:
|
||||
bullet.delete()
|
||||
|
||||
for animation in animations:
|
||||
animation.delete()
|
||||
|
||||
asteroids = []
|
||||
bullets = []
|
||||
animations = []
|
||||
player.visible = False
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# Create window
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
win = pyglet.window.Window(ARENA_WIDTH, ARENA_HEIGHT, caption='Astraea')
|
||||
|
||||
@win.event
|
||||
def on_key_press(symbol, modifiers):
|
||||
# Overrides default Escape key behaviour
|
||||
if symbol == KEY_PAUSE and in_game:
|
||||
if not paused:
|
||||
pause_game()
|
||||
else:
|
||||
resume_game()
|
||||
return True
|
||||
elif symbol == key.ESCAPE:
|
||||
sys.exit()
|
||||
return pyglet.event.EVENT_HANDLED
|
||||
|
||||
@win.event
|
||||
def on_draw():
|
||||
glColor3f(1, 1, 1)
|
||||
|
||||
# Render
|
||||
starfield.draw()
|
||||
|
||||
for (x, y) in ((0, ARENA_HEIGHT), # Top
|
||||
(-ARENA_WIDTH, 0), # Left
|
||||
(0, 0), # Center
|
||||
(ARENA_WIDTH, 0), # Right
|
||||
(0, -ARENA_HEIGHT)): # Bottom
|
||||
glLoadIdentity()
|
||||
glTranslatef(x, y, 0)
|
||||
wrapping_batch.draw()
|
||||
|
||||
glLoadIdentity()
|
||||
batch.draw()
|
||||
|
||||
glLoadIdentity()
|
||||
|
||||
if in_game:
|
||||
# HUD ship lives
|
||||
x = 10 + player.image.width // 2
|
||||
for i in range(player_lives - 1):
|
||||
player.image.blit(x, win.height - player.image.height // 2 - 10, 0)
|
||||
x += player.image.width + 10
|
||||
|
||||
# HUD score
|
||||
score_text.text = str(score)
|
||||
score_text.draw()
|
||||
|
||||
if overlay:
|
||||
overlay.draw()
|
||||
|
||||
if show_fps:
|
||||
fps_display.draw()
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# Load resources
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
batch = pyglet.graphics.Batch()
|
||||
wrapping_batch = pyglet.graphics.Batch()
|
||||
|
||||
resource.path.append('res')
|
||||
resource.reindex()
|
||||
|
||||
asteroid_sizes = [AsteroidSize('asteroid1.png', 100),
|
||||
AsteroidSize('asteroid2.png', 50),
|
||||
AsteroidSize('asteroid3.png', 10)]
|
||||
for small, big in zip(asteroid_sizes[:-1], asteroid_sizes[1:]):
|
||||
big.next_size = small
|
||||
|
||||
bullet_image = resource.image('bullet.png')
|
||||
center_anchor(bullet_image)
|
||||
|
||||
smoke_images_image = resource.image('smoke.png')
|
||||
smoke_images = pyglet.image.ImageGrid(smoke_images_image, 1, 8)
|
||||
for smoke_image in smoke_images:
|
||||
center_anchor(smoke_image)
|
||||
smoke_animation = \
|
||||
pyglet.image.Animation.from_image_sequence(smoke_images,
|
||||
SMOKE_ANIMATION_PERIOD,
|
||||
loop=False)
|
||||
|
||||
explosion_images_image = resource.image('explosion.png')
|
||||
explosion_images = pyglet.image.ImageGrid(explosion_images_image, 2, 8)
|
||||
explosion_images = explosion_images.get_texture_sequence()
|
||||
for explosion_image in explosion_images:
|
||||
center_anchor(explosion_image)
|
||||
explosion_animation = \
|
||||
pyglet.image.Animation.from_image_sequence(explosion_images,
|
||||
EXPLOSION_ANIMATION_PERIOD,
|
||||
loop=False)
|
||||
|
||||
pointer_image = resource.image('pointer.png')
|
||||
pointer_image.anchor_x = pointer_image.width // 2
|
||||
pointer_image.anchor_y = pointer_image.height // 2
|
||||
pointer_image_flip = resource.image('pointer.png', flip_x=True)
|
||||
|
||||
explosion_sound = resource.media('explosion.wav', streaming=False)
|
||||
bullet_sound = resource.media('bullet.wav', streaming=False)
|
||||
|
||||
starfield = Starfield(resource.image('starfield.jpg'))
|
||||
player = Player(resource.image('ship.png'), wrapping_batch)
|
||||
win.push_handlers(player)
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# Global game state vars
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
overlay = None
|
||||
in_game = False
|
||||
paused = False
|
||||
score = 0
|
||||
|
||||
difficulty = 2
|
||||
show_fps = False
|
||||
enable_sound = True
|
||||
|
||||
score_text = pyglet.text.Label('',
|
||||
font_name=FONT_NAME,
|
||||
font_size=18,
|
||||
x=ARENA_WIDTH - 10,
|
||||
y=ARENA_HEIGHT - 10,
|
||||
anchor_x='right',
|
||||
anchor_y='top')
|
||||
|
||||
fps_display = pyglet.window.FPSDisplay(win)
|
||||
|
||||
bullets = []
|
||||
animations = []
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# Game update
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
def update(dt):
|
||||
if overlay:
|
||||
overlay.update(dt)
|
||||
|
||||
if not paused:
|
||||
starfield.update(dt)
|
||||
|
||||
player.update(dt)
|
||||
for asteroid in asteroids:
|
||||
asteroid.update(dt)
|
||||
for bullet in bullets[:]:
|
||||
bullet.update(dt)
|
||||
for animation in animations[:]:
|
||||
animation.update(dt)
|
||||
|
||||
|
||||
if not player.invincible:
|
||||
# Collide bullets and player with asteroids
|
||||
check_collisions()
|
||||
|
||||
# Destroy asteroids that were hit
|
||||
for asteroid in [a for a in asteroids if a.hit]:
|
||||
animations.append(EffectSprite(smoke_animation,
|
||||
asteroid.x, asteroid.y,
|
||||
asteroid.dx, asteroid.dy,
|
||||
batch=batch))
|
||||
asteroid.destroy()
|
||||
if enable_sound:
|
||||
explosion_sound.play()
|
||||
|
||||
# Check if the player was hit
|
||||
if player.hit:
|
||||
animations.append(EffectSprite(explosion_animation,
|
||||
player.x, player.y,
|
||||
player.dx, player.dy,
|
||||
batch=batch))
|
||||
player.invincible = True
|
||||
player.visible = False
|
||||
pyglet.clock.schedule_once(life_lost, LIFE_LOST_DELAY)
|
||||
|
||||
# Check if the area is clear
|
||||
if not asteroids:
|
||||
next_round()
|
||||
pyglet.clock.schedule_interval(update, 1/60.)
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# Start game
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
glEnable(GL_BLEND)
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
|
||||
|
||||
begin_menu_background()
|
||||
begin_main_menu()
|
||||
|
||||
pyglet.app.run()
|
After Width: | Height: | Size: 953 B |
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 6.4 KiB |
After Width: | Height: | Size: 276 B |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 290 B |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 49 KiB |
@ -0,0 +1,92 @@
|
||||
#!/usr/bin/env python
|
||||
# ----------------------------------------------------------------------------
|
||||
# pyglet
|
||||
# Copyright (c) 2006-2008 Alex Holkner
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in
|
||||
# the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of pyglet nor the names of its
|
||||
# contributors may be used to endorse or promote products
|
||||
# derived from this software without specific prior written
|
||||
# permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# An example setup.py that can be used to create both standalone Windows
|
||||
# executables (requires py2exe) and Mac OS X applications (requires py2app).
|
||||
#
|
||||
# On Windows::
|
||||
#
|
||||
# python setup.py py2exe
|
||||
#
|
||||
# On Mac OS X::
|
||||
#
|
||||
# python setup.py py2app
|
||||
#
|
||||
|
||||
from distutils.core import setup
|
||||
|
||||
import os
|
||||
|
||||
# The main entry point of the program
|
||||
script_file = 'astraea.py'
|
||||
|
||||
# Create a list of data files. Add everything in the 'res/' directory.
|
||||
data_files = []
|
||||
for file in os.listdir('res'):
|
||||
file = os.path.join('res', file)
|
||||
if os.path.isfile(file):
|
||||
data_files.append(file)
|
||||
|
||||
# Setup args that apply to all setups, including ordinary distutils.
|
||||
setup_args = dict(
|
||||
data_files=[('res', data_files)]
|
||||
)
|
||||
|
||||
# py2exe options
|
||||
try:
|
||||
import py2exe
|
||||
setup_args.update(dict(
|
||||
windows=[dict(
|
||||
script=script_file,
|
||||
icon_resources=[(1, 'assets/app.ico')],
|
||||
)],
|
||||
))
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
# py2app options
|
||||
try:
|
||||
import py2app
|
||||
setup_args.update(dict(
|
||||
app=[script_file],
|
||||
options=dict(py2app=dict(
|
||||
argv_emulation=True,
|
||||
iconfile='assets/app.icns',
|
||||
)),
|
||||
))
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
setup(**setup_args)
|
@ -0,0 +1,49 @@
|
||||
#!/usr/bin/env python
|
||||
# ----------------------------------------------------------------------------
|
||||
# pyglet
|
||||
# Copyright (c) 2006-2008 Alex Holkner
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in
|
||||
# the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of pyglet nor the names of its
|
||||
# contributors may be used to endorse or promote products
|
||||
# derived from this software without specific prior written
|
||||
# permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
'''Prints all window events to stdout.
|
||||
'''
|
||||
|
||||
import pyglet
|
||||
|
||||
window = pyglet.window.Window(resizable=True)
|
||||
|
||||
@window.event
|
||||
def on_draw():
|
||||
window.clear()
|
||||
|
||||
window.push_handlers(pyglet.window.event.WindowEventLogger())
|
||||
|
||||
pyglet.app.run()
|
@ -0,0 +1,156 @@
|
||||
#!/usr/bin/env python
|
||||
# ----------------------------------------------------------------------------
|
||||
# pyglet
|
||||
# Copyright (c) 2006-2008 Alex Holkner
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in
|
||||
# the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of pyglet nor the names of its
|
||||
# contributors may be used to endorse or promote products
|
||||
# derived from this software without specific prior written
|
||||
# permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
# ----------------------------------------------------------------------------
|
||||
# $Id:$
|
||||
|
||||
'''Demonstrates one way of fixing the display resolution to a certain
|
||||
size, but rendering to the full screen.
|
||||
|
||||
The method used in this example is:
|
||||
|
||||
1. Set the OpenGL viewport to the fixed resolution
|
||||
2. Render the scene using any OpenGL functions (here, just a polygon)
|
||||
3. Copy the framebuffer into a texture
|
||||
4. Reset the OpenGL viewport to the window (full screen) size
|
||||
5. Blit the texture to the framebuffer
|
||||
|
||||
Recent video cards could also render the scene directly to the texture
|
||||
using EXT_framebuffer_object. (This is not demonstrated in this example).
|
||||
'''
|
||||
|
||||
from pyglet.gl import *
|
||||
import pyglet
|
||||
|
||||
# Create a fullscreen window using the user's desktop resolution. You can
|
||||
# also use this technique on ordinary resizable windows.
|
||||
window = pyglet.window.Window(fullscreen=True)
|
||||
|
||||
# Use 320x200 fixed resolution to make the effect completely obvious. You
|
||||
# can change this to a more reasonable value such as 800x600 here.
|
||||
target_resolution = 320, 200
|
||||
|
||||
class FixedResolutionViewport(object):
|
||||
def __init__(self, window, width, height, filtered=False):
|
||||
self.window = window
|
||||
self.width = width
|
||||
self.height = height
|
||||
self.texture = pyglet.image.Texture.create(width, height,
|
||||
rectangle=True)
|
||||
|
||||
if not filtered:
|
||||
# By default the texture will be bilinear filtered when scaled
|
||||
# up. If requested, turn filtering off. This makes the image
|
||||
# aliased, but is more suitable for pixel art.
|
||||
glTexParameteri(self.texture.target,
|
||||
GL_TEXTURE_MAG_FILTER, GL_NEAREST)
|
||||
glTexParameteri(self.texture.target,
|
||||
GL_TEXTURE_MIN_FILTER, GL_NEAREST)
|
||||
|
||||
def begin(self):
|
||||
glViewport(0, 0, self.width, self.height)
|
||||
self.set_fixed_projection()
|
||||
|
||||
def end(self):
|
||||
buffer = pyglet.image.get_buffer_manager().get_color_buffer()
|
||||
self.texture.blit_into(buffer, 0, 0, 0)
|
||||
|
||||
glViewport(0, 0, self.window.width, self.window.height)
|
||||
self.set_window_projection()
|
||||
|
||||
aspect_width = self.window.width / float(self.width)
|
||||
aspect_height = self.window.height / float(self.height)
|
||||
if aspect_width > aspect_height:
|
||||
scale_width = aspect_height * self.width
|
||||
scale_height = aspect_height * self.height
|
||||
else:
|
||||
scale_width = aspect_width * self.width
|
||||
scale_height = aspect_width * self.height
|
||||
x = (self.window.width - scale_width) / 2
|
||||
y = (self.window.height - scale_height) / 2
|
||||
|
||||
glClearColor(0, 0, 0, 1)
|
||||
glClear(GL_COLOR_BUFFER_BIT)
|
||||
glLoadIdentity()
|
||||
glColor3f(1, 1, 1)
|
||||
self.texture.blit(x, y, width=scale_width, height=scale_height)
|
||||
|
||||
def set_fixed_projection(self):
|
||||
# Override this method if you need to change the projection of the
|
||||
# fixed resolution viewport.
|
||||
glMatrixMode(GL_PROJECTION)
|
||||
glLoadIdentity()
|
||||
glOrtho(0, self.width, 0, self.height, -1, 1)
|
||||
glMatrixMode(GL_MODELVIEW)
|
||||
|
||||
def set_window_projection(self):
|
||||
# This is the same as the default window projection, reprinted here
|
||||
# for clarity.
|
||||
glMatrixMode(GL_PROJECTION)
|
||||
glLoadIdentity()
|
||||
glOrtho(0, self.window.width, 0, self.window.height, -1, 1)
|
||||
glMatrixMode(GL_MODELVIEW)
|
||||
|
||||
target_width, target_height = target_resolution
|
||||
viewport = FixedResolutionViewport(window,
|
||||
target_width, target_height, filtered=False)
|
||||
|
||||
def draw_scene():
|
||||
'''Draw the scene, assuming the fixed resolution viewport and projection
|
||||
have been set up. This just draws the rotated polygon.'''
|
||||
glClearColor(1, 1, 1, 1)
|
||||
glClear(GL_COLOR_BUFFER_BIT)
|
||||
|
||||
glLoadIdentity()
|
||||
w, h = target_resolution
|
||||
glTranslatef(w//2, h//2, 0)
|
||||
glRotatef(rotate, 0, 0, 1)
|
||||
glColor3f(1, 0, 0)
|
||||
s = min(w, h) // 3
|
||||
glRectf(-s, -s, s, s)
|
||||
|
||||
rotate = 0
|
||||
def update(dt):
|
||||
global rotate
|
||||
rotate += dt * 20
|
||||
pyglet.clock.schedule_interval(update, 1/60.)
|
||||
|
||||
@window.event
|
||||
def on_draw():
|
||||
viewport.begin()
|
||||
window.clear()
|
||||
draw_scene()
|
||||
viewport.end()
|
||||
|
||||
pyglet.app.run()
|
||||
|
@ -0,0 +1,155 @@
|
||||
#!/usr/bin/env python
|
||||
# ----------------------------------------------------------------------------
|
||||
# pyglet
|
||||
# Copyright (c) 2006-2008 Alex Holkner
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in
|
||||
# the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of pyglet nor the names of its
|
||||
# contributors may be used to endorse or promote products
|
||||
# derived from this software without specific prior written
|
||||
# permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
'''A simple tool that may be used to explore font faces. (Windows only)
|
||||
|
||||
Only the fonts installed in the system are visible.
|
||||
|
||||
Use the left/right cursor keys to change font faces.
|
||||
|
||||
By default only the pyglet safe fonts are shown, toggle the safe flag
|
||||
to see all.
|
||||
|
||||
Don't include tabs in the text sample (see
|
||||
http://pyglet.org/doc-current/programming_guide/text.html#id9 )
|
||||
'''
|
||||
|
||||
from __future__ import print_function, unicode_literals
|
||||
|
||||
import pyglet
|
||||
import pyglet.font.win32query as wq
|
||||
|
||||
|
||||
# support to generate a sample text good to spot monospace compliance.
|
||||
# Chosen to do a table of fields_per_line columns, each column with field_size
|
||||
# characters. Fields are filled with a rolling subset of ASCII characters.
|
||||
class SampleTable(object):
|
||||
field_size = 7
|
||||
gap_size = 3
|
||||
fields_per_line = 7
|
||||
spaces = ' ' * field_size
|
||||
max_chars_per_line = (field_size + gap_size) * fields_per_line - gap_size
|
||||
|
||||
def __init__(self):
|
||||
self.lines = []
|
||||
self.current_line = ''
|
||||
|
||||
def newline(self):
|
||||
self.lines.append(self.current_line)
|
||||
self.current_line = ''
|
||||
|
||||
def add_field(self, s):
|
||||
assert len(s) <= self.field_size
|
||||
to_add = self.spaces[len(s):] + s
|
||||
if self.current_line:
|
||||
to_add = ' ' * self.gap_size + to_add
|
||||
if len(self.current_line) + len(to_add) > self.max_chars_per_line:
|
||||
self.newline()
|
||||
self.add_field(s)
|
||||
else:
|
||||
self.current_line = self.current_line + to_add
|
||||
|
||||
def text(self):
|
||||
return '\n'.join(self.lines)
|
||||
|
||||
def sample_text_monospaced_table():
|
||||
printables = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ '
|
||||
table = SampleTable()
|
||||
for i in range(6):
|
||||
s = printables[i:] + printables[:i]
|
||||
for k in range(0, len(printables), table.field_size):
|
||||
table.add_field(s[k:k + table.field_size])
|
||||
table.newline()
|
||||
return table.text()
|
||||
|
||||
# this worked right with all fonts in a win xp installation
|
||||
def pyglet_safe(fontentry):
|
||||
""" this is heuristic and conservative. YMMV. """
|
||||
return fontentry.vector and fontentry.family != wq.FF_DONTCARE
|
||||
|
||||
|
||||
class Window(pyglet.window.Window):
|
||||
font_num = 0
|
||||
def on_text_motion(self, motion):
|
||||
if motion == pyglet.window.key.MOTION_RIGHT:
|
||||
self.font_num += 1
|
||||
if self.font_num == len(font_names):
|
||||
self.font_num = 0
|
||||
elif motion == pyglet.window.key.MOTION_LEFT:
|
||||
self.font_num -= 1
|
||||
if self.font_num < 0:
|
||||
self.font_num = len(font_names) - 1
|
||||
|
||||
face = font_names[self.font_num]
|
||||
self.head = pyglet.text.Label(face, font_size=16, y=0,
|
||||
anchor_y='bottom')
|
||||
self.text = pyglet.text.Label(sample_text, font_name=face, font_size=12,
|
||||
y=self.height, anchor_y='top', width=self.width, multiline=True)
|
||||
|
||||
def on_draw(self):
|
||||
self.clear()
|
||||
self.head.draw()
|
||||
self.text.draw()
|
||||
|
||||
|
||||
lorem_ipsum = """
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus.
|
||||
Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec
|
||||
consectetur ante hendrerit. Donec et mollis dolor. Praesent et diam eget
|
||||
libero egestas mattis sit amet vitae augue.
|
||||
|
||||
|
||||
"""
|
||||
|
||||
if __name__ == '__main__':
|
||||
print(__doc__)
|
||||
safe = True
|
||||
sample_text = lorem_ipsum + sample_text_monospaced_table()
|
||||
# all fonts known by the OS
|
||||
fontdb = wq.query()
|
||||
|
||||
if safe:
|
||||
candidates = [ f for f in fontdb if pyglet_safe(f)]
|
||||
else:
|
||||
canditates = fontdb
|
||||
|
||||
# theres one fontentry for each charset supported, so reduce names
|
||||
font_names = list(set([f.name for f in candidates]))
|
||||
|
||||
font_names.sort()
|
||||
window = Window(1024, 600)
|
||||
window.on_text_motion(None)
|
||||
pyglet.app.run()
|
||||
|
After Width: | Height: | Size: 5.2 KiB |
After Width: | Height: | Size: 327 B |
After Width: | Height: | Size: 662 B |
After Width: | Height: | Size: 1.4 KiB |
@ -0,0 +1,33 @@
|
||||
import pyglet
|
||||
from game import load, resources
|
||||
|
||||
# Set up a window
|
||||
game_window = pyglet.window.Window(800, 600)
|
||||
|
||||
# Set up the two top labels
|
||||
score_label = pyglet.text.Label(text="Score: 0", x=10, y=575)
|
||||
level_label = pyglet.text.Label(text="Version 1: Static Graphics",
|
||||
x=400, y=575, anchor_x='center')
|
||||
|
||||
# Initialize the player sprite
|
||||
player_ship = pyglet.sprite.Sprite(img=resources.player_image, x=400, y=300)
|
||||
|
||||
# Make three asteroids so we have something to shoot at
|
||||
asteroids = load.asteroids(3, player_ship.position)
|
||||
|
||||
|
||||
@game_window.event
|
||||
def on_draw():
|
||||
game_window.clear()
|
||||
|
||||
player_ship.draw()
|
||||
for asteroid in asteroids:
|
||||
asteroid.draw()
|
||||
|
||||
level_label.draw()
|
||||
score_label.draw()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Tell pyglet to do its thing
|
||||
pyglet.app.run()
|
@ -0,0 +1 @@
|
||||
from . import load, resources
|
@ -0,0 +1,25 @@
|
||||
import pyglet
|
||||
import random
|
||||
import math
|
||||
from . import resources
|
||||
|
||||
|
||||
def distance(point_1=(0, 0), point_2=(0, 0)):
|
||||
"""Returns the distance between two points"""
|
||||
return math.sqrt((point_1[0] - point_2[0]) ** 2 + (point_1[1] - point_2[1]) ** 2)
|
||||
|
||||
|
||||
def asteroids(num_asteroids, player_position):
|
||||
"""Generate asteroid objects with random positions and velocities,
|
||||
not close to the player"""
|
||||
asteroids = []
|
||||
for i in range(num_asteroids):
|
||||
asteroid_x, asteroid_y = player_position
|
||||
while distance((asteroid_x, asteroid_y), player_position) < 100:
|
||||
asteroid_x = random.randint(0, 800)
|
||||
asteroid_y = random.randint(0, 600)
|
||||
new_asteroid = pyglet.sprite.Sprite(img=resources.asteroid_image,
|
||||
x=asteroid_x, y=asteroid_y)
|
||||
new_asteroid.rotation = random.randint(0, 360)
|
||||
asteroids.append(new_asteroid)
|
||||
return asteroids
|
@ -0,0 +1,22 @@
|
||||
import pyglet
|
||||
|
||||
|
||||
def center_image(image):
|
||||
"""Sets an image's anchor point to its center"""
|
||||
image.anchor_x = image.width / 2
|
||||
image.anchor_y = image.height / 2
|
||||
|
||||
|
||||
# Tell pyglet where to find the resources
|
||||
pyglet.resource.path = ['../resources']
|
||||
pyglet.resource.reindex()
|
||||
|
||||
# Load the three main resources and get them to draw centered
|
||||
player_image = pyglet.resource.image("player.png")
|
||||
center_image(player_image)
|
||||
|
||||
bullet_image = pyglet.resource.image("bullet.png")
|
||||
center_image(bullet_image)
|
||||
|
||||
asteroid_image = pyglet.resource.image("asteroid.png")
|
||||
center_image(asteroid_image)
|
@ -0,0 +1,46 @@
|
||||
import pyglet, random, math
|
||||
from game import load, player, resources
|
||||
|
||||
# Set up a window
|
||||
game_window = pyglet.window.Window(800, 600)
|
||||
|
||||
main_batch = pyglet.graphics.Batch()
|
||||
|
||||
# Set up the two top labels
|
||||
score_label = pyglet.text.Label(text="Score: 0", x=10, y=575, batch=main_batch)
|
||||
level_label = pyglet.text.Label(text="Version 2: Basic Motion",
|
||||
x=400, y=575, anchor_x='center', batch=main_batch)
|
||||
|
||||
# Initialize the player sprite
|
||||
player_ship = player.Player(x=400, y=300, batch=main_batch)
|
||||
|
||||
# Make three sprites to represent remaining lives
|
||||
player_lives = load.player_lives(2, main_batch)
|
||||
|
||||
# Make three asteroids so we have something to shoot at
|
||||
asteroids = load.asteroids(3, player_ship.position, main_batch)
|
||||
|
||||
# Store all objects that update each frame in a list
|
||||
game_objects = [player_ship] + asteroids
|
||||
|
||||
# Tell the main window that the player object responds to events
|
||||
game_window.push_handlers(player_ship)
|
||||
|
||||
|
||||
@game_window.event
|
||||
def on_draw():
|
||||
game_window.clear()
|
||||
main_batch.draw()
|
||||
|
||||
|
||||
def update(dt):
|
||||
for obj in game_objects:
|
||||
obj.update(dt)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Update the game 120 times per second
|
||||
pyglet.clock.schedule_interval(update, 1 / 120.0)
|
||||
|
||||
# Tell pyglet to do its thing
|
||||
pyglet.app.run()
|
@ -0,0 +1 @@
|
||||
from . import load, player, resources
|
@ -0,0 +1,36 @@
|
||||
import pyglet, math, random
|
||||
from . import physicalobject, resources
|
||||
|
||||
|
||||
def distance(point_1=(0, 0), point_2=(0, 0)):
|
||||
"""Returns the distance between two points"""
|
||||
return math.sqrt((point_1[0] - point_2[0]) ** 2 + (point_1[1] - point_2[1]) ** 2)
|
||||
|
||||
|
||||
def player_lives(num_icons, batch=None):
|
||||
"""Generate sprites for player life icons"""
|
||||
player_lives = []
|
||||
for i in range(num_icons):
|
||||
new_sprite = pyglet.sprite.Sprite(img=resources.player_image,
|
||||
x=785 - i * 30, y=585,
|
||||
batch=batch)
|
||||
new_sprite.scale = 0.5
|
||||
player_lives.append(new_sprite)
|
||||
return player_lives
|
||||
|
||||
|
||||
def asteroids(num_asteroids, player_position, batch=None):
|
||||
"""Generate asteroid objects with random positions and velocities, not close to the player"""
|
||||
asteroids = []
|
||||
for i in range(num_asteroids):
|
||||
asteroid_x, asteroid_y = player_position
|
||||
while distance((asteroid_x, asteroid_y), player_position) < 100:
|
||||
asteroid_x = random.randint(0, 800)
|
||||
asteroid_y = random.randint(0, 600)
|
||||
new_asteroid = physicalobject.PhysicalObject(img=resources.asteroid_image,
|
||||
x=asteroid_x, y=asteroid_y,
|
||||
batch=batch)
|
||||
new_asteroid.rotation = random.randint(0, 360)
|
||||
new_asteroid.velocity_x, new_asteroid.velocity_y = random.random() * 40, random.random() * 40
|
||||
asteroids.append(new_asteroid)
|
||||
return asteroids
|
@ -0,0 +1,36 @@
|
||||
import pyglet
|
||||
|
||||
|
||||
class PhysicalObject(pyglet.sprite.Sprite):
|
||||
"""A sprite with physical properties such as velocity"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(PhysicalObject, self).__init__(*args, **kwargs)
|
||||
|
||||
# In addition to position, we have velocity
|
||||
self.velocity_x, self.velocity_y = 0.0, 0.0
|
||||
|
||||
def update(self, dt):
|
||||
"""This method should be called every frame."""
|
||||
|
||||
# Update position according to velocity and time
|
||||
self.x += self.velocity_x * dt
|
||||
self.y += self.velocity_y * dt
|
||||
|
||||
# Wrap around the screen if necessary
|
||||
self.check_bounds()
|
||||
|
||||
def check_bounds(self):
|
||||
"""Use the classic Asteroids screen wrapping behavior"""
|
||||
min_x = -self.image.width / 2
|
||||
min_y = -self.image.height / 2
|
||||
max_x = 800 + self.image.width / 2
|
||||
max_y = 600 + self.image.height / 2
|
||||
if self.x < min_x:
|
||||
self.x = max_x
|
||||
elif self.x > max_x:
|
||||
self.x = min_x
|
||||
if self.y < min_y:
|
||||
self.y = max_y
|
||||
elif self.y > max_y:
|
||||
self.y = min_y
|
@ -0,0 +1,48 @@
|
||||
import math
|
||||
from pyglet.window import key
|
||||
from . import physicalobject, resources
|
||||
|
||||
|
||||
class Player(physicalobject.PhysicalObject):
|
||||
"""Physical object that responds to user input"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(Player, self).__init__(img=resources.player_image, *args, **kwargs)
|
||||
|
||||
# Set some easy-to-tweak constants
|
||||
self.thrust = 300.0
|
||||
self.rotate_speed = 200.0
|
||||
|
||||
self.keys = dict(left=False, right=False, up=False)
|
||||
|
||||
def on_key_press(self, symbol, modifiers):
|
||||
if symbol == key.UP:
|
||||
self.keys['up'] = True
|
||||
elif symbol == key.LEFT:
|
||||
self.keys['left'] = True
|
||||
elif symbol == key.RIGHT:
|
||||
self.keys['right'] = True
|
||||
|
||||
def on_key_release(self, symbol, modifiers):
|
||||
if symbol == key.UP:
|
||||
self.keys['up'] = False
|
||||
elif symbol == key.LEFT:
|
||||
self.keys['left'] = False
|
||||
elif symbol == key.RIGHT:
|
||||
self.keys['right'] = False
|
||||
|
||||
def update(self, dt):
|
||||
# Do all the normal physics stuff
|
||||
super(Player, self).update(dt)
|
||||
|
||||
if self.keys['left']:
|
||||
self.rotation -= self.rotate_speed * dt
|
||||
if self.keys['right']:
|
||||
self.rotation += self.rotate_speed * dt
|
||||
|
||||
if self.keys['up']:
|
||||
angle_radians = -math.radians(self.rotation)
|
||||
force_x = math.cos(angle_radians) * self.thrust * dt
|
||||
force_y = math.sin(angle_radians) * self.thrust * dt
|
||||
self.velocity_x += force_x
|
||||
self.velocity_y += force_y
|
@ -0,0 +1,22 @@
|
||||
import pyglet
|
||||
|
||||
|
||||
def center_image(image):
|
||||
"""Sets an image's anchor point to its center"""
|
||||
image.anchor_x = image.width / 2
|
||||
image.anchor_y = image.height / 2
|
||||
|
||||
|
||||
# Tell pyglet where to find the resources
|
||||
pyglet.resource.path = ['../resources']
|
||||
pyglet.resource.reindex()
|
||||
|
||||
# Load the three main resources and get them to draw centered
|
||||
player_image = pyglet.resource.image("player.png")
|
||||
center_image(player_image)
|
||||
|
||||
bullet_image = pyglet.resource.image("bullet.png")
|
||||
center_image(bullet_image)
|
||||
|
||||
asteroid_image = pyglet.resource.image("asteroid.png")
|
||||
center_image(asteroid_image)
|
@ -0,0 +1,68 @@
|
||||
import pyglet, random, math
|
||||
from game import load, player, resources
|
||||
|
||||
# Set up a window
|
||||
game_window = pyglet.window.Window(800, 600)
|
||||
|
||||
main_batch = pyglet.graphics.Batch()
|
||||
|
||||
# Set up the two top labels
|
||||
score_label = pyglet.text.Label(text="Score: 0", x=10, y=575, batch=main_batch)
|
||||
level_label = pyglet.text.Label(text="Version 3: Basic Collision",
|
||||
x=400, y=575, anchor_x='center', batch=main_batch)
|
||||
|
||||
# Initialize the player sprite
|
||||
player_ship = player.Player(x=400, y=300, batch=main_batch)
|
||||
|
||||
# Make three sprites to represent remaining lives
|
||||
player_lives = load.player_lives(2, main_batch)
|
||||
|
||||
# Make three asteroids so we have something to shoot at
|
||||
asteroids = load.asteroids(3, player_ship.position, main_batch)
|
||||
|
||||
# Store all objects that update each frame in a list
|
||||
game_objects = [player_ship] + asteroids
|
||||
|
||||
# Tell the main window that the player object responds to events
|
||||
game_window.push_handlers(player_ship.key_handler)
|
||||
|
||||
|
||||
@game_window.event
|
||||
def on_draw():
|
||||
game_window.clear()
|
||||
main_batch.draw()
|
||||
|
||||
|
||||
def update(dt):
|
||||
for obj in game_objects:
|
||||
obj.update(dt)
|
||||
|
||||
# To avoid handling collisions twice, we employ nested loops of ranges.
|
||||
# This method also avoids the problem of colliding an object with itself.
|
||||
for i in range(len(game_objects)):
|
||||
for j in range(i + 1, len(game_objects)):
|
||||
|
||||
obj_1 = game_objects[i]
|
||||
obj_2 = game_objects[j]
|
||||
|
||||
# Make sure the objects haven't already been killed
|
||||
if not obj_1.dead and not obj_2.dead:
|
||||
if obj_1.collides_with(obj_2):
|
||||
obj_1.handle_collision_with(obj_2)
|
||||
obj_2.handle_collision_with(obj_1)
|
||||
|
||||
# Get rid of dead objects
|
||||
for to_remove in [obj for obj in game_objects if obj.dead]:
|
||||
# Remove the object from any batches it is a member of
|
||||
to_remove.delete()
|
||||
|
||||
# Remove the object from our list
|
||||
game_objects.remove(to_remove)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Update the game 120 times per second
|
||||
pyglet.clock.schedule_interval(update, 1 / 120.0)
|
||||
|
||||
# Tell pyglet to do its thing
|
||||
pyglet.app.run()
|
@ -0,0 +1 @@
|
||||
from . import load, player, resources
|
@ -0,0 +1,32 @@
|
||||
import pyglet
|
||||
import random
|
||||
from . import physicalobject, resources, util
|
||||
|
||||
|
||||
def player_lives(num_icons, batch=None):
|
||||
"""Generate sprites for player life icons"""
|
||||
player_lives = []
|
||||
for i in range(num_icons):
|
||||
new_sprite = pyglet.sprite.Sprite(img=resources.player_image,
|
||||
x=785 - i * 30, y=585,
|
||||
batch=batch)
|
||||
new_sprite.scale = 0.5
|
||||
player_lives.append(new_sprite)
|
||||
return player_lives
|
||||
|
||||
|
||||
def asteroids(num_asteroids, player_position, batch=None):
|
||||
"""Generate asteroid objects with random positions and velocities, not close to the player"""
|
||||
asteroids = []
|
||||
for i in range(num_asteroids):
|
||||
asteroid_x, asteroid_y = player_position
|
||||
while util.distance((asteroid_x, asteroid_y), player_position) < 100:
|
||||
asteroid_x = random.randint(0, 800)
|
||||
asteroid_y = random.randint(0, 600)
|
||||
new_asteroid = physicalobject.PhysicalObject(img=resources.asteroid_image,
|
||||
x=asteroid_x, y=asteroid_y,
|
||||
batch=batch)
|
||||
new_asteroid.rotation = random.randint(0, 360)
|
||||
new_asteroid.velocity_x, new_asteroid.velocity_y = random.random() * 40, random.random() * 40
|
||||
asteroids.append(new_asteroid)
|
||||
return asteroids
|
@ -0,0 +1,55 @@
|
||||
import pyglet
|
||||
from . import util
|
||||
|
||||
|
||||
class PhysicalObject(pyglet.sprite.Sprite):
|
||||
"""A sprite with physical properties such as velocity"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(PhysicalObject, self).__init__(*args, **kwargs)
|
||||
|
||||
# In addition to position, we have velocity
|
||||
self.velocity_x, self.velocity_y = 0.0, 0.0
|
||||
|
||||
# And a flag to remove this object from the game_object list
|
||||
self.dead = False
|
||||
|
||||
def update(self, dt):
|
||||
"""This method should be called every frame."""
|
||||
|
||||
# Update position according to velocity and time
|
||||
self.x += self.velocity_x * dt
|
||||
self.y += self.velocity_y * dt
|
||||
|
||||
# Wrap around the screen if necessary
|
||||
self.check_bounds()
|
||||
|
||||
def check_bounds(self):
|
||||
"""Use the classic Asteroids screen wrapping behavior"""
|
||||
min_x = -self.image.width / 2
|
||||
min_y = -self.image.height / 2
|
||||
max_x = 800 + self.image.width / 2
|
||||
max_y = 600 + self.image.height / 2
|
||||
if self.x < min_x:
|
||||
self.x = max_x
|
||||
if self.y < min_y:
|
||||
self.y = max_y
|
||||
if self.x > max_x:
|
||||
self.x = min_x
|
||||
if self.y > max_y:
|
||||
self.y = min_y
|
||||
|
||||
def collides_with(self, other_object):
|
||||
"""Determine if this object collides with another"""
|
||||
|
||||
# Calculate distance between object centers that would be a collision,
|
||||
# assuming square resources
|
||||
collision_distance = self.image.width / 2 + other_object.image.width / 2
|
||||
|
||||
# Get distance using position tuples
|
||||
actual_distance = util.distance(self.position, other_object.position)
|
||||
|
||||
return (actual_distance <= collision_distance)
|
||||
|
||||
def handle_collision_with(self, other_object):
|
||||
self.dead = True
|
@ -0,0 +1,52 @@
|
||||
import pyglet, math
|
||||
from pyglet.window import key
|
||||
from . import physicalobject, resources
|
||||
|
||||
|
||||
class Player(physicalobject.PhysicalObject):
|
||||
"""Physical object that responds to user input"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(Player, self).__init__(img=resources.player_image, *args, **kwargs)
|
||||
|
||||
# Create a child sprite to show when the ship is thrusting
|
||||
self.engine_sprite = pyglet.sprite.Sprite(img=resources.engine_image, *args, **kwargs)
|
||||
self.engine_sprite.visible = False
|
||||
|
||||
# Set some easy-to-tweak constants
|
||||
self.thrust = 300.0
|
||||
self.rotate_speed = 200.0
|
||||
|
||||
# Let pyglet handle keyboard events for us
|
||||
self.key_handler = key.KeyStateHandler()
|
||||
|
||||
def update(self, dt):
|
||||
# Do all the normal physics stuff
|
||||
super(Player, self).update(dt)
|
||||
|
||||
if self.key_handler[key.LEFT]:
|
||||
self.rotation -= self.rotate_speed * dt
|
||||
if self.key_handler[key.RIGHT]:
|
||||
self.rotation += self.rotate_speed * dt
|
||||
|
||||
if self.key_handler[key.UP]:
|
||||
angle_radians = -math.radians(self.rotation)
|
||||
force_x = math.cos(angle_radians) * self.thrust * dt
|
||||
force_y = math.sin(angle_radians) * self.thrust * dt
|
||||
self.velocity_x += force_x
|
||||
self.velocity_y += force_y
|
||||
|
||||
# If thrusting, update the engine sprite
|
||||
self.engine_sprite.rotation = self.rotation
|
||||
self.engine_sprite.x = self.x
|
||||
self.engine_sprite.y = self.y
|
||||
self.engine_sprite.visible = True
|
||||
else:
|
||||
# Otherwise, hide it
|
||||
self.engine_sprite.visible = False
|
||||
|
||||
def delete(self):
|
||||
# We have a child sprite which must be deleted when this object
|
||||
# is deleted from batches, etc.
|
||||
self.engine_sprite.delete()
|
||||
super(Player, self).delete()
|
@ -0,0 +1,29 @@
|
||||
import pyglet
|
||||
|
||||
|
||||
def center_image(image):
|
||||
"""Sets an image's anchor point to its center"""
|
||||
image.anchor_x = image.width / 2
|
||||
image.anchor_y = image.height / 2
|
||||
|
||||
|
||||
# Tell pyglet where to find the resources
|
||||
pyglet.resource.path = ['../resources']
|
||||
pyglet.resource.reindex()
|
||||
|
||||
# Load the three main resources and get them to draw centered
|
||||
player_image = pyglet.resource.image("player.png")
|
||||
center_image(player_image)
|
||||
|
||||
bullet_image = pyglet.resource.image("bullet.png")
|
||||
center_image(bullet_image)
|
||||
|
||||
asteroid_image = pyglet.resource.image("asteroid.png")
|
||||
center_image(asteroid_image)
|
||||
|
||||
# The engine flame should not be centered on the ship. Rather, it should be shown
|
||||
# behind it. To achieve this effect, we just set the anchor point outside the
|
||||
# image bounds.
|
||||
engine_image = pyglet.resource.image("engine_flame.png")
|
||||
engine_image.anchor_x = engine_image.width * 1.5
|
||||
engine_image.anchor_y = engine_image.height / 2
|
@ -0,0 +1,6 @@
|
||||
import pyglet, math
|
||||
|
||||
|
||||
def distance(point_1=(0, 0), point_2=(0, 0)):
|
||||
"""Returns the distance between two points"""
|
||||
return math.sqrt((point_1[0] - point_2[0]) ** 2 + (point_1[1] - point_2[1]) ** 2)
|
@ -0,0 +1,81 @@
|
||||
import pyglet, random, math
|
||||
from game import load, player, resources
|
||||
|
||||
# Set up a window
|
||||
game_window = pyglet.window.Window(800, 600)
|
||||
|
||||
main_batch = pyglet.graphics.Batch()
|
||||
|
||||
# Set up the two top labels
|
||||
score_label = pyglet.text.Label(text="Score: 0", x=10, y=575, batch=main_batch)
|
||||
level_label = pyglet.text.Label(text="Version 4: Bullets and Structure",
|
||||
x=400, y=575, anchor_x='center', batch=main_batch)
|
||||
|
||||
# Initialize the player sprite
|
||||
player_ship = player.Player(x=400, y=300, batch=main_batch)
|
||||
|
||||
# Make three sprites to represent remaining lives
|
||||
player_lives = load.player_lives(2, main_batch)
|
||||
|
||||
# Make three asteroids so we have something to shoot at
|
||||
asteroids = load.asteroids(3, player_ship.position, main_batch)
|
||||
|
||||
# Store all objects that update each frame in a list
|
||||
game_objects = [player_ship] + asteroids
|
||||
|
||||
# Add any specified event handlers to the event handler stack
|
||||
for obj in game_objects:
|
||||
for handler in obj.event_handlers:
|
||||
game_window.push_handlers(handler)
|
||||
|
||||
|
||||
@game_window.event
|
||||
def on_draw():
|
||||
game_window.clear()
|
||||
main_batch.draw()
|
||||
|
||||
|
||||
def update(dt):
|
||||
# To avoid handling collisions twice, we employ nested loops of ranges.
|
||||
# This method also avoids the problem of colliding an object with itself.
|
||||
for i in range(len(game_objects)):
|
||||
for j in range(i + 1, len(game_objects)):
|
||||
|
||||
obj_1 = game_objects[i]
|
||||
obj_2 = game_objects[j]
|
||||
|
||||
# Make sure the objects haven't already been killed
|
||||
if not obj_1.dead and not obj_2.dead:
|
||||
if obj_1.collides_with(obj_2):
|
||||
obj_1.handle_collision_with(obj_2)
|
||||
obj_2.handle_collision_with(obj_1)
|
||||
|
||||
# Let's not modify the list while traversing it
|
||||
to_add = []
|
||||
|
||||
for obj in game_objects:
|
||||
obj.update(dt)
|
||||
to_add.extend(obj.new_objects)
|
||||
obj.new_objects = []
|
||||
|
||||
# Get rid of dead objects
|
||||
for to_remove in [obj for obj in game_objects if obj.dead]:
|
||||
# If the dying object spawned any new objects, add those to the game_objects list later
|
||||
to_add.extend(obj.new_objects)
|
||||
|
||||
# Remove the object from any batches it is a member of
|
||||
to_remove.delete()
|
||||
|
||||
# Remove the object from our list
|
||||
game_objects.remove(to_remove)
|
||||
|
||||
# Add new objects to the list
|
||||
game_objects.extend(to_add)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Update the game 120 times per second
|
||||
pyglet.clock.schedule_interval(update, 1 / 120.0)
|
||||
|
||||
# Tell pyglet to do its thing
|
||||
pyglet.app.run()
|
@ -0,0 +1 @@
|
||||
from . import load, player, resources
|
@ -0,0 +1,30 @@
|
||||
import random
|
||||
from . import physicalobject, resources
|
||||
|
||||
|
||||
class Asteroid(physicalobject.PhysicalObject):
|
||||
"""An asteroid that divides a little before it dies"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(Asteroid, self).__init__(resources.asteroid_image, *args, **kwargs)
|
||||
|
||||
# Slowly rotate the asteroid as it moves
|
||||
self.rotate_speed = random.random() * 100.0 - 50.0
|
||||
|
||||
def update(self, dt):
|
||||
super(Asteroid, self).update(dt)
|
||||
self.rotation += self.rotate_speed * dt
|
||||
|
||||
def handle_collision_with(self, other_object):
|
||||
super(Asteroid, self).handle_collision_with(other_object)
|
||||
|
||||
# Superclass handles deadness already
|
||||
if self.dead and self.scale > 0.25:
|
||||
num_asteroids = random.randint(2, 3)
|
||||
for i in range(num_asteroids):
|
||||
new_asteroid = Asteroid(x=self.x, y=self.y, batch=self.batch)
|
||||
new_asteroid.rotation = random.randint(0, 360)
|
||||
new_asteroid.velocity_x = random.random() * 70 + self.velocity_x
|
||||
new_asteroid.velocity_y = random.random() * 70 + self.velocity_y
|
||||
new_asteroid.scale = self.scale * 0.5
|
||||
self.new_objects.append(new_asteroid)
|
@ -0,0 +1,18 @@
|
||||
import pyglet
|
||||
from . import physicalobject, resources
|
||||
|
||||
|
||||
class Bullet(physicalobject.PhysicalObject):
|
||||
"""Bullets fired by the player"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(Bullet, self).__init__(resources.bullet_image, *args, **kwargs)
|
||||
|
||||
# Bullets shouldn't stick around forever
|
||||
pyglet.clock.schedule_once(self.die, 0.5)
|
||||
|
||||
# Flag as a bullet
|
||||
self.is_bullet = True
|
||||
|
||||
def die(self, dt):
|
||||
self.dead = True
|
@ -0,0 +1,30 @@
|
||||
import pyglet
|
||||
import random
|
||||
from . import asteroid, resources, util
|
||||
|
||||
|
||||
def player_lives(num_icons, batch=None):
|
||||
"""Generate sprites for player life icons"""
|
||||
player_lives = []
|
||||
for i in range(num_icons):
|
||||
new_sprite = pyglet.sprite.Sprite(img=resources.player_image,
|
||||
x=785 - i * 30, y=585,
|
||||
batch=batch)
|
||||
new_sprite.scale = 0.5
|
||||
player_lives.append(new_sprite)
|
||||
return player_lives
|
||||
|
||||
|
||||
def asteroids(num_asteroids, player_position, batch=None):
|
||||
"""Generate asteroid objects with random positions and velocities, not close to the player"""
|
||||
asteroids = []
|
||||
for i in range(num_asteroids):
|
||||
asteroid_x, asteroid_y = player_position
|
||||
while util.distance((asteroid_x, asteroid_y), player_position) < 100:
|
||||
asteroid_x = random.randint(0, 800)
|
||||
asteroid_y = random.randint(0, 600)
|
||||
new_asteroid = asteroid.Asteroid(x=asteroid_x, y=asteroid_y, batch=batch)
|
||||
new_asteroid.rotation = random.randint(0, 360)
|
||||
new_asteroid.velocity_x, new_asteroid.velocity_y = random.random() * 40, random.random() * 40
|
||||
asteroids.append(new_asteroid)
|
||||
return asteroids
|
@ -0,0 +1,73 @@
|
||||
import pyglet
|
||||
from . import util
|
||||
|
||||
|
||||
class PhysicalObject(pyglet.sprite.Sprite):
|
||||
"""A sprite with physical properties such as velocity"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(PhysicalObject, self).__init__(*args, **kwargs)
|
||||
|
||||
# Velocity
|
||||
self.velocity_x, self.velocity_y = 0.0, 0.0
|
||||
|
||||
# Flags to toggle collision with bullets
|
||||
self.reacts_to_bullets = True
|
||||
self.is_bullet = False
|
||||
|
||||
# Flag to remove this object from the game_object list
|
||||
self.dead = False
|
||||
|
||||
# List of new objects to go in the game_objects list
|
||||
self.new_objects = []
|
||||
|
||||
# Tell the game handler about any event handlers
|
||||
# Only applies to things with keyboard/mouse input
|
||||
self.event_handlers = []
|
||||
|
||||
def update(self, dt):
|
||||
"""This method should be called every frame."""
|
||||
|
||||
# Update position according to velocity and time
|
||||
self.x += self.velocity_x * dt
|
||||
self.y += self.velocity_y * dt
|
||||
|
||||
# Wrap around the screen if necessary
|
||||
self.check_bounds()
|
||||
|
||||
def check_bounds(self):
|
||||
"""Use the classic Asteroids screen wrapping behavior"""
|
||||
min_x = -self.image.width / 2
|
||||
min_y = -self.image.height / 2
|
||||
max_x = 800 + self.image.width / 2
|
||||
max_y = 600 + self.image.height / 2
|
||||
if self.x < min_x:
|
||||
self.x = max_x
|
||||
if self.y < min_y:
|
||||
self.y = max_y
|
||||
if self.x > max_x:
|
||||
self.x = min_x
|
||||
if self.y > max_y:
|
||||
self.y = min_y
|
||||
|
||||
def collides_with(self, other_object):
|
||||
"""Determine if this object collides with another"""
|
||||
|
||||
# Ignore bullet collisions if we're supposed to
|
||||
if not self.reacts_to_bullets and other_object.is_bullet:
|
||||
return False
|
||||
if self.is_bullet and not other_object.reacts_to_bullets:
|
||||
return False
|
||||
|
||||
# Calculate distance between object centers that would be a collision,
|
||||
# assuming square resources
|
||||
collision_distance = self.image.width / 2 + other_object.image.width / 2
|
||||
|
||||
# Get distance using position tuples
|
||||
actual_distance = util.distance(self.position, other_object.position)
|
||||
|
||||
return (actual_distance <= collision_distance)
|
||||
|
||||
def handle_collision_with(self, other_object):
|
||||
if other_object.__class__ is not self.__class__:
|
||||
self.dead = True
|
@ -0,0 +1,83 @@
|
||||
import pyglet, math
|
||||
from pyglet.window import key
|
||||
from . import bullet, physicalobject, resources
|
||||
|
||||
|
||||
class Player(physicalobject.PhysicalObject):
|
||||
"""Physical object that responds to user input"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(Player, self).__init__(img=resources.player_image, *args, **kwargs)
|
||||
|
||||
# Create a child sprite to show when the ship is thrusting
|
||||
self.engine_sprite = pyglet.sprite.Sprite(img=resources.engine_image, *args, **kwargs)
|
||||
self.engine_sprite.visible = False
|
||||
|
||||
# Set some easy-to-tweak constants
|
||||
self.thrust = 300.0
|
||||
self.rotate_speed = 200.0
|
||||
self.bullet_speed = 700.0
|
||||
|
||||
# Player should not collide with own bullets
|
||||
self.reacts_to_bullets = False
|
||||
|
||||
# Tell the game handler about any event handlers
|
||||
self.key_handler = key.KeyStateHandler()
|
||||
self.event_handlers = [self, self.key_handler]
|
||||
|
||||
def update(self, dt):
|
||||
# Do all the normal physics stuff
|
||||
super(Player, self).update(dt)
|
||||
|
||||
if self.key_handler[key.LEFT]:
|
||||
self.rotation -= self.rotate_speed * dt
|
||||
if self.key_handler[key.RIGHT]:
|
||||
self.rotation += self.rotate_speed * dt
|
||||
|
||||
if self.key_handler[key.UP]:
|
||||
# Note: pyglet's rotation attributes are in "negative degrees"
|
||||
angle_radians = -math.radians(self.rotation)
|
||||
force_x = math.cos(angle_radians) * self.thrust * dt
|
||||
force_y = math.sin(angle_radians) * self.thrust * dt
|
||||
self.velocity_x += force_x
|
||||
self.velocity_y += force_y
|
||||
|
||||
# If thrusting, update the engine sprite
|
||||
self.engine_sprite.rotation = self.rotation
|
||||
self.engine_sprite.x = self.x
|
||||
self.engine_sprite.y = self.y
|
||||
self.engine_sprite.visible = True
|
||||
else:
|
||||
# Otherwise, hide it
|
||||
self.engine_sprite.visible = False
|
||||
|
||||
def on_key_press(self, symbol, modifiers):
|
||||
if symbol == key.SPACE:
|
||||
self.fire()
|
||||
|
||||
def fire(self):
|
||||
# Note: pyglet's rotation attributes are in "negative degrees"
|
||||
angle_radians = -math.radians(self.rotation)
|
||||
|
||||
# Create a new bullet just in front of the player
|
||||
ship_radius = self.image.width / 2
|
||||
bullet_x = self.x + math.cos(angle_radians) * ship_radius
|
||||
bullet_y = self.y + math.sin(angle_radians) * ship_radius
|
||||
new_bullet = bullet.Bullet(bullet_x, bullet_y, batch=self.batch)
|
||||
|
||||
# Give it some speed
|
||||
bullet_vx = self.velocity_x + math.cos(angle_radians) * self.bullet_speed
|
||||
bullet_vy = self.velocity_y + math.sin(angle_radians) * self.bullet_speed
|
||||
new_bullet.velocity_x, new_bullet.velocity_y = bullet_vx, bullet_vy
|
||||
|
||||
# Add it to the list of objects to be added to the game_objects list
|
||||
self.new_objects.append(new_bullet)
|
||||
|
||||
# Play the bullet sound
|
||||
resources.bullet_sound.play()
|
||||
|
||||
def delete(self):
|
||||
# We have a child sprite which must be deleted when this object
|
||||
# is deleted from batches, etc.
|
||||
self.engine_sprite.delete()
|
||||
super(Player, self).delete()
|
@ -0,0 +1,32 @@
|
||||
import pyglet
|
||||
|
||||
|
||||
def center_image(image):
|
||||
"""Sets an image's anchor point to its center"""
|
||||
image.anchor_x = image.width / 2
|
||||
image.anchor_y = image.height / 2
|
||||
|
||||
|
||||
# Tell pyglet where to find the resources
|
||||
pyglet.resource.path = ['../resources']
|
||||
pyglet.resource.reindex()
|
||||
|
||||
# Load the three main resources and get them to draw centered
|
||||
player_image = pyglet.resource.image("player.png")
|
||||
center_image(player_image)
|
||||
|
||||
bullet_image = pyglet.resource.image("bullet.png")
|
||||
center_image(bullet_image)
|
||||
|
||||
asteroid_image = pyglet.resource.image("asteroid.png")
|
||||
center_image(asteroid_image)
|
||||
|
||||
# The engine flame should not be centered on the ship. Rather, it should be shown
|
||||
# behind it. To achieve this effect, we just set the anchor point outside the
|
||||
# image bounds.
|
||||
engine_image = pyglet.resource.image("engine_flame.png")
|
||||
engine_image.anchor_x = engine_image.width * 1.5
|
||||
engine_image.anchor_y = engine_image.height / 2
|
||||
|
||||
# Load the bullet sound _without_ streaming so we can play it more than once at a time
|
||||
bullet_sound = pyglet.resource.media("bullet.wav", streaming=False)
|
@ -0,0 +1,12 @@
|
||||
import pyglet, math
|
||||
|
||||
|
||||
def distance(point_1=(0, 0), point_2=(0, 0)):
|
||||
"""Returns the distance between two points"""
|
||||
return math.sqrt((point_1[0] - point_2[0]) ** 2 + (point_1[1] - point_2[1]) ** 2)
|
||||
|
||||
|
||||
def center_image(image):
|
||||
"""Sets an image's anchor point to its center"""
|
||||
image.anchor_x = image.width / 2
|
||||
image.anchor_y = image.height / 2
|
@ -0,0 +1,163 @@
|
||||
import pyglet, random, math
|
||||
from game import asteroid, load, player, resources
|
||||
|
||||
# Set up a window
|
||||
game_window = pyglet.window.Window(800, 600)
|
||||
|
||||
main_batch = pyglet.graphics.Batch()
|
||||
|
||||
# Set up the two top labels
|
||||
score_label = pyglet.text.Label(text="Score: 0", x=10, y=575, batch=main_batch)
|
||||
level_label = pyglet.text.Label(text="Version 5: It's a Game!",
|
||||
x=400, y=575, anchor_x='center', batch=main_batch)
|
||||
|
||||
# Set up the game over label offscreen
|
||||
game_over_label = pyglet.text.Label(text="GAME OVER",
|
||||
x=400, y=-300, anchor_x='center',
|
||||
batch=main_batch, font_size=48)
|
||||
|
||||
counter = pyglet.clock.ClockDisplay()
|
||||
|
||||
player_ship = None
|
||||
player_lives = []
|
||||
score = 0
|
||||
num_asteroids = 3
|
||||
game_objects = []
|
||||
|
||||
# We need to pop off as many event stack frames as we pushed on
|
||||
# every time we reset the level.
|
||||
event_stack_size = 0
|
||||
|
||||
|
||||
def init():
|
||||
global score, num_asteroids
|
||||
|
||||
score = 0
|
||||
score_label.text = "Score: " + str(score)
|
||||
|
||||
num_asteroids = 3
|
||||
reset_level(2)
|
||||
|
||||
|
||||
def reset_level(num_lives=2):
|
||||
global player_ship, player_lives, game_objects, event_stack_size
|
||||
|
||||
# Clear the event stack of any remaining handlers from other levels
|
||||
while event_stack_size > 0:
|
||||
game_window.pop_handlers()
|
||||
event_stack_size -= 1
|
||||
|
||||
for life in player_lives:
|
||||
life.delete()
|
||||
|
||||
# Initialize the player sprite
|
||||
player_ship = player.Player(x=400, y=300, batch=main_batch)
|
||||
|
||||
# Make three sprites to represent remaining lives
|
||||
player_lives = load.player_lives(num_lives, main_batch)
|
||||
|
||||
# Make some asteroids so we have something to shoot at
|
||||
asteroids = load.asteroids(num_asteroids, player_ship.position, main_batch)
|
||||
|
||||
# Store all objects that update each frame in a list
|
||||
game_objects = [player_ship] + asteroids
|
||||
|
||||
# Add any specified event handlers to the event handler stack
|
||||
for obj in game_objects:
|
||||
for handler in obj.event_handlers:
|
||||
game_window.push_handlers(handler)
|
||||
event_stack_size += 1
|
||||
|
||||
|
||||
@game_window.event
|
||||
def on_draw():
|
||||
game_window.clear()
|
||||
main_batch.draw()
|
||||
counter.draw()
|
||||
|
||||
|
||||
def update(dt):
|
||||
global score, num_asteroids
|
||||
|
||||
player_dead = False
|
||||
victory = False
|
||||
|
||||
# To avoid handling collisions twice, we employ nested loops of ranges.
|
||||
# This method also avoids the problem of colliding an object with itself.
|
||||
for i in range(len(game_objects)):
|
||||
for j in range(i + 1, len(game_objects)):
|
||||
|
||||
obj_1 = game_objects[i]
|
||||
obj_2 = game_objects[j]
|
||||
|
||||
# Make sure the objects haven't already been killed
|
||||
if not obj_1.dead and not obj_2.dead:
|
||||
if obj_1.collides_with(obj_2):
|
||||
obj_1.handle_collision_with(obj_2)
|
||||
obj_2.handle_collision_with(obj_1)
|
||||
|
||||
# Let's not modify the list while traversing it
|
||||
to_add = []
|
||||
|
||||
# Check for win condition
|
||||
asteroids_remaining = 0
|
||||
|
||||
for obj in game_objects:
|
||||
obj.update(dt)
|
||||
|
||||
to_add.extend(obj.new_objects)
|
||||
obj.new_objects = []
|
||||
|
||||
# Check for win condition
|
||||
if isinstance(obj, asteroid.Asteroid):
|
||||
asteroids_remaining += 1
|
||||
|
||||
if asteroids_remaining == 0:
|
||||
# Don't act on victory until the end of the time step
|
||||
victory = True
|
||||
|
||||
# Get rid of dead objects
|
||||
for to_remove in [obj for obj in game_objects if obj.dead]:
|
||||
if to_remove == player_ship:
|
||||
player_dead = True
|
||||
# If the dying object spawned any new objects, add those to the
|
||||
# game_objects list later
|
||||
to_add.extend(to_remove.new_objects)
|
||||
|
||||
# Remove the object from any batches it is a member of
|
||||
to_remove.delete()
|
||||
|
||||
# Remove the object from our list
|
||||
game_objects.remove(to_remove)
|
||||
|
||||
# Bump the score if the object to remove is an asteroid
|
||||
if isinstance(to_remove, asteroid.Asteroid):
|
||||
score += 1
|
||||
score_label.text = "Score: " + str(score)
|
||||
|
||||
# Add new objects to the list
|
||||
game_objects.extend(to_add)
|
||||
|
||||
# Check for win/lose conditions
|
||||
if player_dead:
|
||||
# We can just use the length of the player_lives list as the number of lives
|
||||
if len(player_lives) > 0:
|
||||
reset_level(len(player_lives) - 1)
|
||||
else:
|
||||
game_over_label.y = 300
|
||||
elif victory:
|
||||
num_asteroids += 1
|
||||
player_ship.delete()
|
||||
score += 10
|
||||
reset_level(len(player_lives))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Start it up!
|
||||
init()
|
||||
|
||||
# Update the game 120 times per second
|
||||
pyglet.clock.schedule_interval(update, 1 / 120.0)
|
||||
|
||||
# Tell pyglet to do its thing
|
||||
pyglet.app.run()
|
@ -0,0 +1 @@
|
||||
from . import load, player, resources
|
@ -0,0 +1,30 @@
|
||||
import random
|
||||
from . import physicalobject, resources
|
||||
|
||||
|
||||
class Asteroid(physicalobject.PhysicalObject):
|
||||
"""An asteroid that divides a little before it dies"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(Asteroid, self).__init__(resources.asteroid_image, *args, **kwargs)
|
||||
|
||||
# Slowly rotate the asteroid as it moves
|
||||
self.rotate_speed = random.random() * 100.0 - 50.0
|
||||
|
||||
def update(self, dt):
|
||||
super(Asteroid, self).update(dt)
|
||||
self.rotation += self.rotate_speed * dt
|
||||
|
||||
def handle_collision_with(self, other_object):
|
||||
super(Asteroid, self).handle_collision_with(other_object)
|
||||
|
||||
# Superclass handles deadness already
|
||||
if self.dead and self.scale > 0.25:
|
||||
num_asteroids = random.randint(2, 3)
|
||||
for i in range(num_asteroids):
|
||||
new_asteroid = Asteroid(x=self.x, y=self.y, batch=self.batch)
|
||||
new_asteroid.rotation = random.randint(0, 360)
|
||||
new_asteroid.velocity_x = random.random() * 70 + self.velocity_x
|
||||
new_asteroid.velocity_y = random.random() * 70 + self.velocity_y
|
||||
new_asteroid.scale = self.scale * 0.5
|
||||
self.new_objects.append(new_asteroid)
|
@ -0,0 +1,18 @@
|
||||
import pyglet
|
||||
from . import physicalobject, resources
|
||||
|
||||
|
||||
class Bullet(physicalobject.PhysicalObject):
|
||||
"""Bullets fired by the player"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(Bullet, self).__init__(resources.bullet_image, *args, **kwargs)
|
||||
|
||||
# Bullets shouldn't stick around forever
|
||||
pyglet.clock.schedule_once(self.die, 0.5)
|
||||
|
||||
# Flag as a bullet
|
||||
self.is_bullet = True
|
||||
|
||||
def die(self, dt):
|
||||
self.dead = True
|
@ -0,0 +1,30 @@
|
||||
import pyglet
|
||||
import random
|
||||
from . import asteroid, resources, util
|
||||
|
||||
|
||||
def player_lives(num_icons, batch=None):
|
||||
"""Generate sprites for player life icons"""
|
||||
player_lives = []
|
||||
for i in range(num_icons):
|
||||
new_sprite = pyglet.sprite.Sprite(img=resources.player_image,
|
||||
x=785 - i * 30, y=585,
|
||||
batch=batch)
|
||||
new_sprite.scale = 0.5
|
||||
player_lives.append(new_sprite)
|
||||
return player_lives
|
||||
|
||||
|
||||
def asteroids(num_asteroids, player_position, batch=None):
|
||||
"""Generate asteroid objects with random positions and velocities, not close to the player"""
|
||||
asteroids = []
|
||||
for i in range(num_asteroids):
|
||||
asteroid_x, asteroid_y = player_position
|
||||
while util.distance((asteroid_x, asteroid_y), player_position) < 100:
|
||||
asteroid_x = random.randint(0, 800)
|
||||
asteroid_y = random.randint(0, 600)
|
||||
new_asteroid = asteroid.Asteroid(x=asteroid_x, y=asteroid_y, batch=batch)
|
||||
new_asteroid.rotation = random.randint(0, 360)
|
||||
new_asteroid.velocity_x, new_asteroid.velocity_y = random.random() * 40, random.random() * 40
|
||||
asteroids.append(new_asteroid)
|
||||
return asteroids
|
@ -0,0 +1,74 @@
|
||||
import pyglet
|
||||
from . import util
|
||||
|
||||
|
||||
class PhysicalObject(pyglet.sprite.Sprite):
|
||||
"""A sprite with physical properties such as velocity"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(PhysicalObject, self).__init__(*args, **kwargs)
|
||||
|
||||
# Velocity
|
||||
self.velocity_x, self.velocity_y = 0.0, 0.0
|
||||
|
||||
# Flags to toggle collision with bullets
|
||||
self.reacts_to_bullets = True
|
||||
self.is_bullet = False
|
||||
|
||||
# Flag to remove this object from the game_object list
|
||||
self.dead = False
|
||||
|
||||
# List of new objects to go in the game_objects list
|
||||
self.new_objects = []
|
||||
|
||||
# Tell the game handler about any event handlers
|
||||
# Only applies to things with keyboard/mouse input
|
||||
self.event_handlers = []
|
||||
|
||||
def update(self, dt):
|
||||
"""This method should be called every frame."""
|
||||
|
||||
# Update position according to velocity and time
|
||||
self.x += self.velocity_x * dt
|
||||
self.y += self.velocity_y * dt
|
||||
|
||||
# Wrap around the screen if necessary
|
||||
self.check_bounds()
|
||||
|
||||
def check_bounds(self):
|
||||
"""Use the classic Asteroids screen wrapping behavior"""
|
||||
min_x = -self.image.width / 2
|
||||
min_y = -self.image.height / 2
|
||||
max_x = 800 + self.image.width / 2
|
||||
max_y = 600 + self.image.height / 2
|
||||
if self.x < min_x:
|
||||
self.x = max_x
|
||||
if self.y < min_y:
|
||||
self.y = max_y
|
||||
if self.x > max_x:
|
||||
self.x = min_x
|
||||
if self.y > max_y:
|
||||
self.y = min_y
|
||||
|
||||
def collides_with(self, other_object):
|
||||
"""Determine if this object collides with another"""
|
||||
|
||||
# Ignore bullet collisions if we're supposed to
|
||||
if not self.reacts_to_bullets and other_object.is_bullet:
|
||||
return False
|
||||
if self.is_bullet and not other_object.reacts_to_bullets:
|
||||
return False
|
||||
|
||||
# Calculate distance between object centers that would be a collision,
|
||||
# assuming square resources
|
||||
collision_distance = self.image.width * 0.5 * self.scale \
|
||||
+ other_object.image.width * 0.5 * other_object.scale
|
||||
|
||||
# Get distance using position tuples
|
||||
actual_distance = util.distance(self.position, other_object.position)
|
||||
|
||||
return (actual_distance <= collision_distance)
|
||||
|
||||
def handle_collision_with(self, other_object):
|
||||
if other_object.__class__ is not self.__class__:
|
||||
self.dead = True
|
@ -0,0 +1,83 @@
|
||||
import pyglet, math
|
||||
from pyglet.window import key
|
||||
from . import bullet, physicalobject, resources
|
||||
|
||||
|
||||
class Player(physicalobject.PhysicalObject):
|
||||
"""Physical object that responds to user input"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(Player, self).__init__(img=resources.player_image, *args, **kwargs)
|
||||
|
||||
# Create a child sprite to show when the ship is thrusting
|
||||
self.engine_sprite = pyglet.sprite.Sprite(img=resources.engine_image, *args, **kwargs)
|
||||
self.engine_sprite.visible = False
|
||||
|
||||
# Set some easy-to-tweak constants
|
||||
self.thrust = 300.0
|
||||
self.rotate_speed = 200.0
|
||||
self.bullet_speed = 700.0
|
||||
|
||||
# Player should not collide with own bullets
|
||||
self.reacts_to_bullets = False
|
||||
|
||||
# Tell the game handler about any event handlers
|
||||
self.key_handler = key.KeyStateHandler()
|
||||
self.event_handlers = [self, self.key_handler]
|
||||
|
||||
def update(self, dt):
|
||||
# Do all the normal physics stuff
|
||||
super(Player, self).update(dt)
|
||||
|
||||
if self.key_handler[key.LEFT]:
|
||||
self.rotation -= self.rotate_speed * dt
|
||||
if self.key_handler[key.RIGHT]:
|
||||
self.rotation += self.rotate_speed * dt
|
||||
|
||||
if self.key_handler[key.UP]:
|
||||
# Note: pyglet's rotation attributes are in "negative degrees"
|
||||
angle_radians = -math.radians(self.rotation)
|
||||
force_x = math.cos(angle_radians) * self.thrust * dt
|
||||
force_y = math.sin(angle_radians) * self.thrust * dt
|
||||
self.velocity_x += force_x
|
||||
self.velocity_y += force_y
|
||||
|
||||
# If thrusting, update the engine sprite
|
||||
self.engine_sprite.rotation = self.rotation
|
||||
self.engine_sprite.x = self.x
|
||||
self.engine_sprite.y = self.y
|
||||
self.engine_sprite.visible = True
|
||||
else:
|
||||
# Otherwise, hide it
|
||||
self.engine_sprite.visible = False
|
||||
|
||||
def on_key_press(self, symbol, modifiers):
|
||||
if symbol == key.SPACE:
|
||||
self.fire()
|
||||
|
||||
def fire(self):
|
||||
# Note: pyglet's rotation attributes are in "negative degrees"
|
||||
angle_radians = -math.radians(self.rotation)
|
||||
|
||||
# Create a new bullet just in front of the player
|
||||
ship_radius = self.image.width / 2
|
||||
bullet_x = self.x + math.cos(angle_radians) * ship_radius
|
||||
bullet_y = self.y + math.sin(angle_radians) * ship_radius
|
||||
new_bullet = bullet.Bullet(bullet_x, bullet_y, batch=self.batch)
|
||||
|
||||
# Give it some speed
|
||||
bullet_vx = self.velocity_x + math.cos(angle_radians) * self.bullet_speed
|
||||
bullet_vy = self.velocity_y + math.sin(angle_radians) * self.bullet_speed
|
||||
new_bullet.velocity_x, new_bullet.velocity_y = bullet_vx, bullet_vy
|
||||
|
||||
# Add it to the list of objects to be added to the game_objects list
|
||||
self.new_objects.append(new_bullet)
|
||||
|
||||
# Play the bullet sound
|
||||
resources.bullet_sound.play()
|
||||
|
||||
def delete(self):
|
||||
# We have a child sprite which must be deleted when this object
|
||||
# is deleted from batches, etc.
|
||||
self.engine_sprite.delete()
|
||||
super(Player, self).delete()
|
@ -0,0 +1,32 @@
|
||||
import pyglet
|
||||
|
||||
|
||||
def center_image(image):
|
||||
"""Sets an image's anchor point to its center"""
|
||||
image.anchor_x = image.width / 2
|
||||
image.anchor_y = image.height / 2
|
||||
|
||||
|
||||
# Tell pyglet where to find the resources
|
||||
pyglet.resource.path = ['../resources']
|
||||
pyglet.resource.reindex()
|
||||
|
||||
# Load the three main resources and get them to draw centered
|
||||
player_image = pyglet.resource.image("player.png")
|
||||
center_image(player_image)
|
||||
|
||||
bullet_image = pyglet.resource.image("bullet.png")
|
||||
center_image(bullet_image)
|
||||
|
||||
asteroid_image = pyglet.resource.image("asteroid.png")
|
||||
center_image(asteroid_image)
|
||||
|
||||
# The engine flame should not be centered on the ship. Rather, it should be shown
|
||||
# behind it. To achieve this effect, we just set the anchor point outside the
|
||||
# image bounds.
|
||||
engine_image = pyglet.resource.image("engine_flame.png")
|
||||
engine_image.anchor_x = engine_image.width * 1.5
|
||||
engine_image.anchor_y = engine_image.height / 2
|
||||
|
||||
# Load the bullet sound _without_ streaming so we can play it more than once at a time
|
||||
bullet_sound = pyglet.resource.media("bullet.wav", streaming=False)
|
@ -0,0 +1,12 @@
|
||||
import pyglet, math
|
||||
|
||||
|
||||
def distance(point_1=(0, 0), point_2=(0, 0)):
|
||||
"""Returns the distance between two points"""
|
||||
return math.sqrt((point_1[0] - point_2[0]) ** 2 + (point_1[1] - point_2[1]) ** 2)
|
||||
|
||||
|
||||
def center_image(image):
|
||||
"""Sets an image's anchor point to its center"""
|
||||
image.anchor_x = image.width / 2
|
||||
image.anchor_y = image.height / 2
|
@ -0,0 +1,179 @@
|
||||
#!/usr/bin/env python
|
||||
# ----------------------------------------------------------------------------
|
||||
# pyglet
|
||||
# Copyright (c) 2006-2008 Alex Holkner
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in
|
||||
# the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of pyglet nor the names of its
|
||||
# contributors may be used to endorse or promote products
|
||||
# derived from this software without specific prior written
|
||||
# permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
'''Displays a rotating torus using the pyglet.graphics API.
|
||||
|
||||
This example is very similar to examples/opengl.py, but uses the
|
||||
pyglet.graphics API to construct the indexed vertex arrays instead of
|
||||
using OpenGL calls explicitly. This has the advantage that VBOs will
|
||||
be used on supporting hardware automatically.
|
||||
|
||||
The vertex list is added to a batch, allowing it to be easily rendered
|
||||
alongside other vertex lists with minimal overhead.
|
||||
'''
|
||||
|
||||
from math import pi, sin, cos
|
||||
|
||||
import pyglet
|
||||
from pyglet.gl import *
|
||||
|
||||
try:
|
||||
# Try and create a window with multisampling (antialiasing)
|
||||
config = Config(sample_buffers=1, samples=4,
|
||||
depth_size=16, double_buffer=True,)
|
||||
window = pyglet.window.Window(resizable=True, config=config)
|
||||
except pyglet.window.NoSuchConfigException:
|
||||
# Fall back to no multisampling for old hardware
|
||||
window = pyglet.window.Window(resizable=True)
|
||||
|
||||
@window.event
|
||||
def on_resize(width, height):
|
||||
# Override the default on_resize handler to create a 3D projection
|
||||
glViewport(0, 0, width, height)
|
||||
glMatrixMode(GL_PROJECTION)
|
||||
glLoadIdentity()
|
||||
gluPerspective(60., width / float(height), .1, 1000.)
|
||||
glMatrixMode(GL_MODELVIEW)
|
||||
return pyglet.event.EVENT_HANDLED
|
||||
|
||||
def update(dt):
|
||||
global rx, ry, rz
|
||||
rx += dt * 1
|
||||
ry += dt * 80
|
||||
rz += dt * 30
|
||||
rx %= 360
|
||||
ry %= 360
|
||||
rz %= 360
|
||||
pyglet.clock.schedule(update)
|
||||
|
||||
@window.event
|
||||
def on_draw():
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
|
||||
glLoadIdentity()
|
||||
glTranslatef(0, 0, -4)
|
||||
glRotatef(rz, 0, 0, 1)
|
||||
glRotatef(ry, 0, 1, 0)
|
||||
glRotatef(rx, 1, 0, 0)
|
||||
batch.draw()
|
||||
|
||||
def setup():
|
||||
# One-time GL setup
|
||||
glClearColor(1, 1, 1, 1)
|
||||
glColor3f(1, 0, 0)
|
||||
glEnable(GL_DEPTH_TEST)
|
||||
glEnable(GL_CULL_FACE)
|
||||
|
||||
# Uncomment this line for a wireframe view
|
||||
#glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
|
||||
|
||||
# Simple light setup. On Windows GL_LIGHT0 is enabled by default,
|
||||
# but this is not the case on Linux or Mac, so remember to always
|
||||
# include it.
|
||||
glEnable(GL_LIGHTING)
|
||||
glEnable(GL_LIGHT0)
|
||||
glEnable(GL_LIGHT1)
|
||||
|
||||
# Define a simple function to create ctypes arrays of floats:
|
||||
def vec(*args):
|
||||
return (GLfloat * len(args))(*args)
|
||||
|
||||
glLightfv(GL_LIGHT0, GL_POSITION, vec(.5, .5, 1, 0))
|
||||
glLightfv(GL_LIGHT0, GL_SPECULAR, vec(.5, .5, 1, 1))
|
||||
glLightfv(GL_LIGHT0, GL_DIFFUSE, vec(1, 1, 1, 1))
|
||||
glLightfv(GL_LIGHT1, GL_POSITION, vec(1, 0, .5, 0))
|
||||
glLightfv(GL_LIGHT1, GL_DIFFUSE, vec(.5, .5, .5, 1))
|
||||
glLightfv(GL_LIGHT1, GL_SPECULAR, vec(1, 1, 1, 1))
|
||||
|
||||
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, vec(0.5, 0, 0.3, 1))
|
||||
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, vec(1, 1, 1, 1))
|
||||
glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 50)
|
||||
|
||||
class Torus(object):
|
||||
list = None
|
||||
def __init__(self, radius, inner_radius, slices, inner_slices,
|
||||
batch, group=None):
|
||||
# Create the vertex and normal arrays.
|
||||
vertices = []
|
||||
normals = []
|
||||
|
||||
u_step = 2 * pi / (slices - 1)
|
||||
v_step = 2 * pi / (inner_slices - 1)
|
||||
u = 0.
|
||||
for i in range(slices):
|
||||
cos_u = cos(u)
|
||||
sin_u = sin(u)
|
||||
v = 0.
|
||||
for j in range(inner_slices):
|
||||
cos_v = cos(v)
|
||||
sin_v = sin(v)
|
||||
|
||||
d = (radius + inner_radius * cos_v)
|
||||
x = d * cos_u
|
||||
y = d * sin_u
|
||||
z = inner_radius * sin_v
|
||||
|
||||
nx = cos_u * cos_v
|
||||
ny = sin_u * cos_v
|
||||
nz = sin_v
|
||||
|
||||
vertices.extend([x, y, z])
|
||||
normals.extend([nx, ny, nz])
|
||||
v += v_step
|
||||
u += u_step
|
||||
|
||||
# Create a list of triangle indices.
|
||||
indices = []
|
||||
for i in range(slices - 1):
|
||||
for j in range(inner_slices - 1):
|
||||
p = i * inner_slices + j
|
||||
indices.extend([p, p + inner_slices, p + inner_slices + 1])
|
||||
indices.extend([p, p + inner_slices + 1, p + 1])
|
||||
|
||||
self.vertex_list = batch.add_indexed(len(vertices)//3,
|
||||
GL_TRIANGLES,
|
||||
group,
|
||||
indices,
|
||||
('v3f/static', vertices),
|
||||
('n3f/static', normals))
|
||||
|
||||
def delete(self):
|
||||
self.vertex_list.delete()
|
||||
|
||||
setup()
|
||||
batch = pyglet.graphics.Batch()
|
||||
torus = Torus(1, 0.3, 50, 30, batch=batch)
|
||||
rx = ry = rz = 0
|
||||
|
||||
pyglet.app.run()
|
@ -0,0 +1,78 @@
|
||||
#!/usr/bin/env python
|
||||
# ----------------------------------------------------------------------------
|
||||
# pyglet
|
||||
# Copyright (c) 2006-2008 Alex Holkner
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in
|
||||
# the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of pyglet nor the names of its
|
||||
# contributors may be used to endorse or promote products
|
||||
# derived from this software without specific prior written
|
||||
# permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
'''A simple demonstration of the HTMLLabel class, as it might be used on a
|
||||
help or introductory screen.
|
||||
'''
|
||||
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id: $'
|
||||
|
||||
import os
|
||||
import pyglet
|
||||
|
||||
html = '''
|
||||
<h1>HTML labels in pyglet</h1>
|
||||
|
||||
<p align="center"><img src="pyglet.png" /></p>
|
||||
|
||||
<p>HTML labels are a simple way to add formatted text to your application.
|
||||
Different <font face="Helvetica,Arial" size=+2>fonts</font>, <em>styles</em>
|
||||
and <font color=maroon>colours</font> are supported.
|
||||
|
||||
<p>This window has been made resizable; text will reflow to fit the new size.
|
||||
'''
|
||||
|
||||
window = pyglet.window.Window(resizable=True)
|
||||
location = pyglet.resource.FileLocation(os.path.dirname(__file__))
|
||||
label = pyglet.text.HTMLLabel(html, location=location,
|
||||
width=window.width,
|
||||
multiline=True, anchor_y='center')
|
||||
|
||||
@window.event
|
||||
def on_resize(width, height):
|
||||
# Wrap text to the width of the window
|
||||
label.width = window.width
|
||||
|
||||
# Keep text vertically centered in the window
|
||||
label.y = window.height // 2
|
||||
|
||||
@window.event
|
||||
def on_draw():
|
||||
window.clear()
|
||||
label.draw()
|
||||
|
||||
pyglet.gl.glClearColor(1, 1, 1, 1)
|
||||
pyglet.app.run()
|
@ -0,0 +1,68 @@
|
||||
#!/usr/bin/env python
|
||||
# ----------------------------------------------------------------------------
|
||||
# pyglet
|
||||
# Copyright (c) 2006-2008 Alex Holkner
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in
|
||||
# the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of pyglet nor the names of its
|
||||
# contributors may be used to endorse or promote products
|
||||
# derived from this software without specific prior written
|
||||
# permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
'''Convert an image to another file format supported by pyglet.
|
||||
|
||||
Usage::
|
||||
python image_convert.py <src-file> <dest-file>
|
||||
|
||||
'''
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import sys
|
||||
|
||||
import pyglet
|
||||
|
||||
|
||||
def convert(src, dest):
|
||||
if '.dds' in src.lower():
|
||||
# Compressed textures need to be uploaded to the video card before
|
||||
# they can be saved.
|
||||
texture = pyglet.image.load(src).get_texture()
|
||||
texture.save(dest)
|
||||
else:
|
||||
# Otherwise just save the loaded image in the new format.
|
||||
image = pyglet.image.load(src)
|
||||
image.save(dest)
|
||||
|
||||
if __name__ == '__main__':
|
||||
if len(sys.argv) != 3:
|
||||
print(__doc__)
|
||||
sys.exit(1)
|
||||
|
||||
src = sys.argv[1]
|
||||
dest = sys.argv[2]
|
||||
convert(src, dest)
|
@ -0,0 +1,84 @@
|
||||
#!/usr/bin/env python
|
||||
# ----------------------------------------------------------------------------
|
||||
# pyglet
|
||||
# Copyright (c) 2006-2008 Alex Holkner
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in
|
||||
# the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of pyglet nor the names of its
|
||||
# contributors may be used to endorse or promote products
|
||||
# derived from this software without specific prior written
|
||||
# permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
'''Display an image.
|
||||
|
||||
Usage::
|
||||
|
||||
display.py <filename>
|
||||
|
||||
A checkerboard background is visible behind any transparent areas of the
|
||||
image.
|
||||
'''
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import sys
|
||||
|
||||
import pyglet
|
||||
from pyglet.gl import *
|
||||
|
||||
window = pyglet.window.Window(visible=False, resizable=True)
|
||||
|
||||
|
||||
@window.event
|
||||
def on_draw():
|
||||
background.blit_tiled(0, 0, 0, window.width, window.height)
|
||||
img.blit(window.width // 2, window.height // 2, 0)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if len(sys.argv) != 2:
|
||||
print(__doc__)
|
||||
sys.exit(1)
|
||||
|
||||
filename = sys.argv[1]
|
||||
|
||||
img = pyglet.image.load(filename).get_texture(rectangle=True)
|
||||
img.anchor_x = img.width // 2
|
||||
img.anchor_y = img.height // 2
|
||||
|
||||
checks = pyglet.image.create(32, 32, pyglet.image.CheckerImagePattern())
|
||||
background = pyglet.image.TileableTexture.create_for_image(checks)
|
||||
|
||||
# Enable alpha blending, required for image.blit.
|
||||
glEnable(GL_BLEND)
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
|
||||
|
||||
window.width = img.width
|
||||
window.height = img.height
|
||||
window.set_visible()
|
||||
|
||||
pyglet.app.run()
|
@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
'''
|
||||
'''
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id: $'
|
||||
|
||||
import pyglet
|
||||
|
||||
window = pyglet.window.Window()
|
||||
devices = pyglet.input.get_devices()
|
||||
|
||||
|
||||
def watch_control(device, control):
|
||||
@control.event
|
||||
def on_change(value):
|
||||
print('%r: %r.on_change(%r)' % (device, control, value))
|
||||
|
||||
if isinstance(control, pyglet.input.base.Button):
|
||||
@control.event
|
||||
def on_press():
|
||||
print('%r: %r.on_press()' % (device, control))
|
||||
|
||||
@control.event
|
||||
def on_release():
|
||||
print('%r: %r.on_release()' % (device, control))
|
||||
|
||||
print('Devices:')
|
||||
for device in devices:
|
||||
print(' ', device.name, end=' ')
|
||||
try:
|
||||
device.open(window=window)
|
||||
print('OK')
|
||||
|
||||
for control in device.get_controls():
|
||||
print(' ', control.name)
|
||||
watch_control(device, control)
|
||||
|
||||
except pyglet.input.DeviceException:
|
||||
print('Fail')
|
||||
|
||||
pyglet.app.run()
|
@ -0,0 +1,63 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
'''
|
||||
'''
|
||||
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id: $'
|
||||
|
||||
import pyglet
|
||||
from pyglet.gl import *
|
||||
|
||||
joysticks = pyglet.input.get_joysticks()
|
||||
assert joysticks, 'No joystick device is connected'
|
||||
joystick = joysticks[0]
|
||||
joystick.open()
|
||||
|
||||
window = pyglet.window.Window()
|
||||
|
||||
@window.event
|
||||
def on_draw():
|
||||
x = (0.8*joystick.x + 1) * window.width / 2
|
||||
y = (-0.8*joystick.y + 1) * window.height / 2
|
||||
z = joystick.z
|
||||
angle = joystick.rz * 180
|
||||
|
||||
# Axes
|
||||
|
||||
glClear(GL_COLOR_BUFFER_BIT)
|
||||
glColor3f(1, 0, 0)
|
||||
glLoadIdentity()
|
||||
glTranslatef(x, y, 0)
|
||||
glScalef(1 + z, 1 + z, 1 + z)
|
||||
glRotatef(-angle, 0, 0, 1)
|
||||
glBegin(GL_TRIANGLES)
|
||||
glVertex2f(-10, 0)
|
||||
glVertex2f(0, 13)
|
||||
glVertex2f(10, 0)
|
||||
glEnd()
|
||||
|
||||
# Buttons
|
||||
|
||||
glLoadIdentity()
|
||||
x = 10
|
||||
y = 10
|
||||
glPointSize(5)
|
||||
glBegin(GL_POINTS)
|
||||
for button in joystick.buttons:
|
||||
if button:
|
||||
glVertex2f(x, y)
|
||||
x += 20
|
||||
glEnd()
|
||||
|
||||
# Hat
|
||||
|
||||
glColor3f(0, 0, 1)
|
||||
x = window.width / 2
|
||||
y = window.height / 2
|
||||
glBegin(GL_POINTS)
|
||||
glVertex2f(x + joystick.hat_x * 50, y + joystick.hat_y * 50)
|
||||
glEnd()
|
||||
|
||||
pyglet.clock.schedule(lambda dt: None)
|
||||
pyglet.app.run()
|
@ -0,0 +1,94 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
'''Print details of a media file that pyglet can open (requires AVbin).
|
||||
|
||||
Usage::
|
||||
|
||||
media_info.py <filename>
|
||||
|
||||
'''
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id: $'
|
||||
|
||||
import sys
|
||||
import pyglet
|
||||
|
||||
|
||||
def print_avbin_info():
|
||||
from pyglet.media import have_avbin
|
||||
|
||||
if have_avbin():
|
||||
from pyglet.media.sources import avbin
|
||||
print('Using AVbin version %d (FFmpeg r%d)' % (
|
||||
avbin.get_version(),
|
||||
avbin.av.avbin_get_ffmpeg_revision()))
|
||||
else:
|
||||
print('AVbin not available; required for media decoding.')
|
||||
print('http://code.google.com/p/avbin')
|
||||
print()
|
||||
|
||||
|
||||
def print_source_info(source):
|
||||
if source.info:
|
||||
if source.info.title:
|
||||
print('Title: %s' % source.info.title)
|
||||
if source.info.album:
|
||||
print('Album: %s' % source.info.album)
|
||||
if source.info.author:
|
||||
print('Author: %s' % source.info.author)
|
||||
if source.info.year:
|
||||
print('Year: %d' % source.info.year)
|
||||
if source.info.track:
|
||||
print('Track: %d' % source.info.track)
|
||||
if source.info.genre:
|
||||
print('Genre: %s' % source.info.genre)
|
||||
if source.info.copyright:
|
||||
print('Copyright: %s' % source.info.copyright)
|
||||
if source.info.comment:
|
||||
print('Comment: %s' % source.info.comment)
|
||||
|
||||
if source.audio_format:
|
||||
af = source.audio_format
|
||||
print('Audio: %d channel(s), %d bits, %.02f Hz' % (
|
||||
af.channels, af.sample_size, af.sample_rate))
|
||||
|
||||
if source.video_format:
|
||||
vf = source.video_format
|
||||
if vf.frame_rate:
|
||||
frame_rate = '%.02f' % vf.frame_rate
|
||||
else:
|
||||
frame_rate = 'unknown'
|
||||
if vf.sample_aspect >= 1:
|
||||
display_width = vf.sample_aspect * vf.width
|
||||
display_height = vf.height
|
||||
else:
|
||||
display_width = vf.width
|
||||
display_height = vf.sample_aspect / vf.height
|
||||
print('Video: %dx%d at aspect %r (displays at %dx%d), %s fps' % (
|
||||
vf.width, vf.height, vf.sample_aspect,
|
||||
display_width, display_height, frame_rate))
|
||||
|
||||
hours = int(source.duration / 3600)
|
||||
minutes = int(source.duration / 60) % 60
|
||||
seconds = int(source.duration) % 60
|
||||
milliseconds = int(source.duration * 1000) % 1000
|
||||
print('Duration: %d:%02d:%02d.%03d' % (
|
||||
hours, minutes, seconds, milliseconds))
|
||||
|
||||
if __name__ == '__main__':
|
||||
if len(sys.argv) != 2:
|
||||
print(__doc__)
|
||||
print_avbin_info()
|
||||
sys.exit(1)
|
||||
|
||||
print_avbin_info()
|
||||
|
||||
filename = sys.argv[1]
|
||||
try:
|
||||
source = pyglet.media.load(filename, streaming=True)
|
||||
print_source_info(source)
|
||||
except pyglet.media.MediaException:
|
||||
print('Could not open %s' % filename)
|
@ -0,0 +1,345 @@
|
||||
#!/usr/bin/env python
|
||||
# ----------------------------------------------------------------------------
|
||||
# pyglet
|
||||
# Copyright (c) 2006-2008 Alex Holkner
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in
|
||||
# the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of pyglet nor the names of its
|
||||
# contributors may be used to endorse or promote products
|
||||
# derived from this software without specific prior written
|
||||
# permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
'''Audio and video player with simple GUI controls.
|
||||
'''
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id: $'
|
||||
|
||||
import sys
|
||||
|
||||
from pyglet.gl import *
|
||||
import pyglet
|
||||
from pyglet.window import key
|
||||
|
||||
|
||||
def draw_rect(x, y, width, height):
|
||||
glBegin(GL_LINE_LOOP)
|
||||
glVertex2f(x, y)
|
||||
glVertex2f(x + width, y)
|
||||
glVertex2f(x + width, y + height)
|
||||
glVertex2f(x, y + height)
|
||||
glEnd()
|
||||
|
||||
|
||||
class Control(pyglet.event.EventDispatcher):
|
||||
x = y = 0
|
||||
width = height = 10
|
||||
|
||||
def __init__(self, parent):
|
||||
super(Control, self).__init__()
|
||||
self.parent = parent
|
||||
|
||||
def hit_test(self, x, y):
|
||||
return (self.x < x < self.x + self.width and
|
||||
self.y < y < self.y + self.height)
|
||||
|
||||
def capture_events(self):
|
||||
self.parent.push_handlers(self)
|
||||
|
||||
def release_events(self):
|
||||
self.parent.remove_handlers(self)
|
||||
|
||||
|
||||
class Button(Control):
|
||||
charged = False
|
||||
|
||||
def draw(self):
|
||||
if self.charged:
|
||||
glColor3f(1, 0, 0)
|
||||
draw_rect(self.x, self.y, self.width, self.height)
|
||||
glColor3f(1, 1, 1)
|
||||
self.draw_label()
|
||||
|
||||
def on_mouse_press(self, x, y, button, modifiers):
|
||||
self.capture_events()
|
||||
self.charged = True
|
||||
|
||||
def on_mouse_drag(self, x, y, dx, dy, buttons, modifiers):
|
||||
self.charged = self.hit_test(x, y)
|
||||
|
||||
def on_mouse_release(self, x, y, button, modifiers):
|
||||
self.release_events()
|
||||
if self.hit_test(x, y):
|
||||
self.dispatch_event('on_press')
|
||||
self.charged = False
|
||||
|
||||
Button.register_event_type('on_press')
|
||||
|
||||
|
||||
class TextButton(Button):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(TextButton, self).__init__(*args, **kwargs)
|
||||
self._text = pyglet.text.Label('', anchor_x='center', anchor_y='center')
|
||||
|
||||
def draw_label(self):
|
||||
self._text.x = self.x + self.width / 2
|
||||
self._text.y = self.y + self.height / 2
|
||||
self._text.draw()
|
||||
|
||||
def set_text(self, text):
|
||||
self._text.text = text
|
||||
|
||||
text = property(lambda self: self._text.text,
|
||||
set_text)
|
||||
|
||||
|
||||
class Slider(Control):
|
||||
THUMB_WIDTH = 6
|
||||
THUMB_HEIGHT = 10
|
||||
GROOVE_HEIGHT = 2
|
||||
|
||||
def draw(self):
|
||||
center_y = self.y + self.height / 2
|
||||
draw_rect(self.x, center_y - self.GROOVE_HEIGHT / 2,
|
||||
self.width, self.GROOVE_HEIGHT)
|
||||
pos = self.x + self.value * self.width / (self.max - self.min)
|
||||
draw_rect(pos - self.THUMB_WIDTH / 2, center_y - self.THUMB_HEIGHT / 2,
|
||||
self.THUMB_WIDTH, self.THUMB_HEIGHT)
|
||||
|
||||
def coordinate_to_value(self, x):
|
||||
return float(x - self.x) / self.width * (self.max - self.min) + self.min
|
||||
|
||||
def on_mouse_press(self, x, y, button, modifiers):
|
||||
value = self.coordinate_to_value(x)
|
||||
self.capture_events()
|
||||
self.dispatch_event('on_begin_scroll')
|
||||
self.dispatch_event('on_change', value)
|
||||
|
||||
def on_mouse_drag(self, x, y, dx, dy, buttons, modifiers):
|
||||
value = min(max(self.coordinate_to_value(x), self.min), self.max)
|
||||
self.dispatch_event('on_change', value)
|
||||
|
||||
def on_mouse_release(self, x, y, button, modifiers):
|
||||
self.release_events()
|
||||
self.dispatch_event('on_end_scroll')
|
||||
|
||||
Slider.register_event_type('on_begin_scroll')
|
||||
Slider.register_event_type('on_end_scroll')
|
||||
Slider.register_event_type('on_change')
|
||||
|
||||
|
||||
class PlayerWindow(pyglet.window.Window):
|
||||
GUI_WIDTH = 400
|
||||
GUI_HEIGHT = 40
|
||||
GUI_PADDING = 4
|
||||
GUI_BUTTON_HEIGHT = 16
|
||||
|
||||
def __init__(self, player):
|
||||
super(PlayerWindow, self).__init__(caption='Media Player',
|
||||
visible=False,
|
||||
resizable=True)
|
||||
self.player = player
|
||||
self.player.push_handlers(self)
|
||||
# TODO compat #self.player.eos_action = self.player.EOS_PAUSE
|
||||
|
||||
self.slider = Slider(self)
|
||||
self.slider.x = self.GUI_PADDING
|
||||
self.slider.y = self.GUI_PADDING * 2 + self.GUI_BUTTON_HEIGHT
|
||||
self.slider.on_begin_scroll = lambda: player.pause()
|
||||
self.slider.on_end_scroll = lambda: player.play()
|
||||
self.slider.on_change = lambda value: player.seek(value)
|
||||
|
||||
self.play_pause_button = TextButton(self)
|
||||
self.play_pause_button.x = self.GUI_PADDING
|
||||
self.play_pause_button.y = self.GUI_PADDING
|
||||
self.play_pause_button.height = self.GUI_BUTTON_HEIGHT
|
||||
self.play_pause_button.width = 45
|
||||
self.play_pause_button.on_press = self.on_play_pause
|
||||
|
||||
win = self
|
||||
self.window_button = TextButton(self)
|
||||
self.window_button.x = self.play_pause_button.x + \
|
||||
self.play_pause_button.width + self.GUI_PADDING
|
||||
self.window_button.y = self.GUI_PADDING
|
||||
self.window_button.height = self.GUI_BUTTON_HEIGHT
|
||||
self.window_button.width = 90
|
||||
self.window_button.text = 'Windowed'
|
||||
self.window_button.on_press = lambda: win.set_fullscreen(False)
|
||||
|
||||
self.controls = [
|
||||
self.slider,
|
||||
self.play_pause_button,
|
||||
self.window_button,
|
||||
]
|
||||
|
||||
x = self.window_button.x + self.window_button.width + self.GUI_PADDING
|
||||
i = 0
|
||||
for screen in self.display.get_screens():
|
||||
screen_button = TextButton(self)
|
||||
screen_button.x = x
|
||||
screen_button.y = self.GUI_PADDING
|
||||
screen_button.height = self.GUI_BUTTON_HEIGHT
|
||||
screen_button.width = 80
|
||||
screen_button.text = 'Screen %d' % (i + 1)
|
||||
screen_button.on_press = \
|
||||
(lambda s: lambda: win.set_fullscreen(True, screen=s))(screen)
|
||||
self.controls.append(screen_button)
|
||||
i += 1
|
||||
x += screen_button.width + self.GUI_PADDING
|
||||
|
||||
def on_eos(self):
|
||||
self.gui_update_state()
|
||||
|
||||
def gui_update_source(self):
|
||||
if self.player.source:
|
||||
source = self.player.source
|
||||
self.slider.min = 0.
|
||||
self.slider.max = source.duration
|
||||
self.gui_update_state()
|
||||
|
||||
def gui_update_state(self):
|
||||
if self.player.playing:
|
||||
self.play_pause_button.text = 'Pause'
|
||||
else:
|
||||
self.play_pause_button.text = 'Play'
|
||||
|
||||
def get_video_size(self):
|
||||
if not self.player.source or not self.player.source.video_format:
|
||||
return 0, 0
|
||||
video_format = self.player.source.video_format
|
||||
width = video_format.width
|
||||
height = video_format.height
|
||||
if video_format.sample_aspect > 1:
|
||||
width *= video_format.sample_aspect
|
||||
elif video_format.sample_aspect < 1:
|
||||
height /= video_format.sample_aspect
|
||||
return width, height
|
||||
|
||||
def set_default_video_size(self):
|
||||
'''Make the window size just big enough to show the current
|
||||
video and the GUI.'''
|
||||
width = self.GUI_WIDTH
|
||||
height = self.GUI_HEIGHT
|
||||
video_width, video_height = self.get_video_size()
|
||||
width = max(width, video_width)
|
||||
height += video_height
|
||||
self.set_size(int(width), int(height))
|
||||
|
||||
def on_resize(self, width, height):
|
||||
'''Position and size video image.'''
|
||||
super(PlayerWindow, self).on_resize(width, height)
|
||||
|
||||
self.slider.width = width - self.GUI_PADDING * 2
|
||||
|
||||
height -= self.GUI_HEIGHT
|
||||
if height <= 0:
|
||||
return
|
||||
|
||||
video_width, video_height = self.get_video_size()
|
||||
if video_width == 0 or video_height == 0:
|
||||
return
|
||||
|
||||
display_aspect = width / float(height)
|
||||
video_aspect = video_width / float(video_height)
|
||||
if video_aspect > display_aspect:
|
||||
self.video_width = width
|
||||
self.video_height = width / video_aspect
|
||||
else:
|
||||
self.video_height = height
|
||||
self.video_width = height * video_aspect
|
||||
self.video_x = (width - self.video_width) / 2
|
||||
self.video_y = (height - self.video_height) / 2 + self.GUI_HEIGHT
|
||||
|
||||
def on_mouse_press(self, x, y, button, modifiers):
|
||||
for control in self.controls:
|
||||
if control.hit_test(x, y):
|
||||
control.on_mouse_press(x, y, button, modifiers)
|
||||
|
||||
def on_key_press(self, symbol, modifiers):
|
||||
if symbol == key.SPACE:
|
||||
self.on_play_pause()
|
||||
elif symbol == key.ESCAPE:
|
||||
self.dispatch_event('on_close')
|
||||
|
||||
def on_close(self):
|
||||
self.player.pause()
|
||||
self.close()
|
||||
|
||||
def on_play_pause(self):
|
||||
if self.player.playing:
|
||||
self.player.pause()
|
||||
else:
|
||||
if self.player.time >= self.player.source.duration:
|
||||
self.player.seek(0)
|
||||
self.player.play()
|
||||
self.gui_update_state()
|
||||
|
||||
def on_draw(self):
|
||||
self.clear()
|
||||
|
||||
# Video
|
||||
if self.player.source and self.player.source.video_format:
|
||||
self.player.get_texture().blit(self.video_x,
|
||||
self.video_y,
|
||||
width=self.video_width,
|
||||
height=self.video_height)
|
||||
|
||||
# GUI
|
||||
self.slider.value = self.player.time
|
||||
for control in self.controls:
|
||||
control.draw()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if len(sys.argv) < 2:
|
||||
print('Usage: media_player.py <filename> [<filename> ...]')
|
||||
sys.exit(1)
|
||||
|
||||
have_video = False
|
||||
|
||||
for filename in sys.argv[1:]:
|
||||
player = pyglet.media.Player()
|
||||
window = PlayerWindow(player)
|
||||
|
||||
source = pyglet.media.load(filename)
|
||||
player.queue(source)
|
||||
|
||||
have_video = have_video or bool(source.video_format)
|
||||
|
||||
window.gui_update_source()
|
||||
window.set_default_video_size()
|
||||
window.set_visible(True)
|
||||
|
||||
player.play()
|
||||
window.gui_update_state()
|
||||
|
||||
if not have_video:
|
||||
pyglet.clock.schedule_interval(lambda dt: None, 0.2)
|
||||
|
||||
pyglet.app.run()
|
@ -0,0 +1,80 @@
|
||||
#!/usr/bin/env python
|
||||
# ----------------------------------------------------------------------------
|
||||
# pyglet
|
||||
# Copyright (c) 2006-2008 Alex Holkner
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in
|
||||
# the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of pyglet nor the names of its
|
||||
# contributors may be used to endorse or promote products
|
||||
# derived from this software without specific prior written
|
||||
# permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
'''Demonstrates how to manage OpenGL calls between two independent windows.
|
||||
'''
|
||||
|
||||
import pyglet
|
||||
from pyglet.gl import *
|
||||
|
||||
def on_resize(width, height):
|
||||
glViewport(0, 0, width, height)
|
||||
glMatrixMode(GL_PROJECTION)
|
||||
glLoadIdentity()
|
||||
gluPerspective(60., width / float(height), 1., 100.)
|
||||
glMatrixMode(GL_MODELVIEW)
|
||||
|
||||
def setup():
|
||||
glClearColor(1, 1, 1, 1)
|
||||
glColor3f(.5, .5, .5)
|
||||
|
||||
def on_draw():
|
||||
glClear(GL_COLOR_BUFFER_BIT)
|
||||
glLoadIdentity()
|
||||
glTranslatef(0, 0, -5)
|
||||
glRotatef(r, 0, 0, 1)
|
||||
glRectf(-1, -1, 1, 1)
|
||||
|
||||
r = 0
|
||||
def update(dt):
|
||||
global r
|
||||
r += 1
|
||||
if r > 360:
|
||||
r = 0
|
||||
pyglet.clock.schedule_interval(update, 1/20.)
|
||||
|
||||
w1 = pyglet.window.Window(200, 200, caption='First window', resizable=True)
|
||||
w1.on_resize = on_resize
|
||||
w1.on_draw = on_draw
|
||||
w1.switch_to()
|
||||
setup()
|
||||
|
||||
w2 = pyglet.window.Window(300, 300, caption='Second window', resizable=True)
|
||||
w2.on_resize = on_resize
|
||||
w2.on_draw = on_draw
|
||||
w2.switch_to()
|
||||
setup()
|
||||
|
||||
pyglet.app.run()
|
@ -0,0 +1,11 @@
|
||||
noisy
|
||||
=====
|
||||
|
||||
This is an example program that accompanies pyglet (http://www.pyglet.org).
|
||||
Due to licensing restrictions on some of the assets, this game cannot be used
|
||||
for commercial purposes.
|
||||
|
||||
The source code is licensed under the BSD license, which is quite permissive
|
||||
(see the source header for details).
|
||||
|
||||
All artwork and the sound is Copyright 2007 Alex Holkner.
|
After Width: | Height: | Size: 996 B |
@ -0,0 +1,116 @@
|
||||
#!/usr/bin/env python
|
||||
# ----------------------------------------------------------------------------
|
||||
# pyglet
|
||||
# Copyright (c) 2006-2008 Alex Holkner
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in
|
||||
# the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of pyglet nor the names of its
|
||||
# contributors may be used to endorse or promote products
|
||||
# derived from this software without specific prior written
|
||||
# permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
'''Bounces balls around a window and plays noises.
|
||||
|
||||
This is a simple demonstration of how pyglet efficiently manages many sound
|
||||
channels without intervention.
|
||||
'''
|
||||
|
||||
import os
|
||||
import random
|
||||
import sys
|
||||
|
||||
from pyglet.gl import *
|
||||
import pyglet
|
||||
from pyglet.window import key
|
||||
|
||||
BALL_IMAGE = 'ball.png'
|
||||
BALL_SOUND = 'ball.wav'
|
||||
|
||||
if len(sys.argv) > 1:
|
||||
BALL_SOUND = sys.argv[1]
|
||||
|
||||
sound = pyglet.resource.media(BALL_SOUND, streaming=False)
|
||||
|
||||
class Ball(pyglet.sprite.Sprite):
|
||||
ball_image = pyglet.resource.image(BALL_IMAGE)
|
||||
width = ball_image.width
|
||||
height = ball_image.height
|
||||
|
||||
def __init__(self):
|
||||
x = random.random() * (window.width - self.width)
|
||||
y = random.random() * (window.height - self.height)
|
||||
|
||||
super(Ball, self).__init__(self.ball_image, x, y, batch=balls_batch)
|
||||
|
||||
self.dx = (random.random() - 0.5) * 1000
|
||||
self.dy = (random.random() - 0.5) * 1000
|
||||
|
||||
def update(self, dt):
|
||||
if self.x <= 0 or self.x + self.width >= window.width:
|
||||
self.dx *= -1
|
||||
sound.play()
|
||||
if self.y <= 0 or self.y + self.height >= window.height:
|
||||
self.dy *= -1
|
||||
sound.play()
|
||||
self.x += self.dx * dt
|
||||
self.y += self.dy * dt
|
||||
|
||||
self.x = min(max(self.x, 0), window.width - self.width)
|
||||
self.y = min(max(self.y, 0), window.height - self.height)
|
||||
|
||||
window = pyglet.window.Window(640, 480)
|
||||
|
||||
@window.event
|
||||
def on_key_press(symbol, modifiers):
|
||||
if symbol == key.SPACE:
|
||||
balls.append(Ball())
|
||||
elif symbol == key.BACKSPACE:
|
||||
if balls:
|
||||
del balls[-1]
|
||||
elif symbol == key.ESCAPE:
|
||||
window.has_exit = True
|
||||
|
||||
@window.event
|
||||
def on_draw():
|
||||
window.clear()
|
||||
balls_batch.draw()
|
||||
label.draw()
|
||||
|
||||
def update(dt):
|
||||
for ball in balls:
|
||||
ball.update(dt)
|
||||
pyglet.clock.schedule_interval(update, 1/30.)
|
||||
|
||||
balls_batch = pyglet.graphics.Batch()
|
||||
balls = []
|
||||
label = pyglet.text.Label('Press space to add a ball, backspace to remove',
|
||||
font_size=14,
|
||||
x=window.width // 2, y=10,
|
||||
anchor_x='center')
|
||||
|
||||
if __name__ == '__main__':
|
||||
pyglet.app.run()
|
@ -0,0 +1,190 @@
|
||||
#!/usr/bin/env python
|
||||
# ----------------------------------------------------------------------------
|
||||
# pyglet
|
||||
# Copyright (c) 2006-2008 Alex Holkner
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in
|
||||
# the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of pyglet nor the names of its
|
||||
# contributors may be used to endorse or promote products
|
||||
# derived from this software without specific prior written
|
||||
# permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
'''Displays a rotating torus using OpenGL.
|
||||
|
||||
This example demonstrates:
|
||||
|
||||
* Using a 3D projection on a window by overriding the default on_resize
|
||||
handler
|
||||
* Enabling multisampling if available
|
||||
* Drawing a simple 3D primitive using vertex and index arrays
|
||||
* Using a display list
|
||||
* Fixed-pipeline lighting
|
||||
|
||||
'''
|
||||
|
||||
from math import pi, sin, cos
|
||||
|
||||
from pyglet.gl import *
|
||||
import pyglet
|
||||
|
||||
try:
|
||||
# Try and create a window with multisampling (antialiasing)
|
||||
config = Config(sample_buffers=1, samples=4,
|
||||
depth_size=16, double_buffer=True,)
|
||||
window = pyglet.window.Window(resizable=True, config=config)
|
||||
except pyglet.window.NoSuchConfigException:
|
||||
# Fall back to no multisampling for old hardware
|
||||
window = pyglet.window.Window(resizable=True)
|
||||
|
||||
@window.event
|
||||
def on_resize(width, height):
|
||||
# Override the default on_resize handler to create a 3D projection
|
||||
glViewport(0, 0, width, height)
|
||||
glMatrixMode(GL_PROJECTION)
|
||||
glLoadIdentity()
|
||||
gluPerspective(60., width / float(height), .1, 1000.)
|
||||
glMatrixMode(GL_MODELVIEW)
|
||||
return pyglet.event.EVENT_HANDLED
|
||||
|
||||
def update(dt):
|
||||
global rx, ry, rz
|
||||
rx += dt * 1
|
||||
ry += dt * 80
|
||||
rz += dt * 30
|
||||
rx %= 360
|
||||
ry %= 360
|
||||
rz %= 360
|
||||
pyglet.clock.schedule(update)
|
||||
|
||||
@window.event
|
||||
def on_draw():
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
|
||||
glLoadIdentity()
|
||||
glTranslatef(0, 0, -4)
|
||||
glRotatef(rz, 0, 0, 1)
|
||||
glRotatef(ry, 0, 1, 0)
|
||||
glRotatef(rx, 1, 0, 0)
|
||||
torus.draw()
|
||||
|
||||
def setup():
|
||||
# One-time GL setup
|
||||
glClearColor(1, 1, 1, 1)
|
||||
glColor3f(1, 0, 0)
|
||||
glEnable(GL_DEPTH_TEST)
|
||||
glEnable(GL_CULL_FACE)
|
||||
|
||||
# Uncomment this line for a wireframe view
|
||||
#glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
|
||||
|
||||
# Simple light setup. On Windows GL_LIGHT0 is enabled by default,
|
||||
# but this is not the case on Linux or Mac, so remember to always
|
||||
# include it.
|
||||
glEnable(GL_LIGHTING)
|
||||
glEnable(GL_LIGHT0)
|
||||
glEnable(GL_LIGHT1)
|
||||
|
||||
# Define a simple function to create ctypes arrays of floats:
|
||||
def vec(*args):
|
||||
return (GLfloat * len(args))(*args)
|
||||
|
||||
glLightfv(GL_LIGHT0, GL_POSITION, vec(.5, .5, 1, 0))
|
||||
glLightfv(GL_LIGHT0, GL_SPECULAR, vec(.5, .5, 1, 1))
|
||||
glLightfv(GL_LIGHT0, GL_DIFFUSE, vec(1, 1, 1, 1))
|
||||
glLightfv(GL_LIGHT1, GL_POSITION, vec(1, 0, .5, 0))
|
||||
glLightfv(GL_LIGHT1, GL_DIFFUSE, vec(.5, .5, .5, 1))
|
||||
glLightfv(GL_LIGHT1, GL_SPECULAR, vec(1, 1, 1, 1))
|
||||
|
||||
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, vec(0.5, 0, 0.3, 1))
|
||||
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, vec(1, 1, 1, 1))
|
||||
glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 50)
|
||||
|
||||
class Torus(object):
|
||||
def __init__(self, radius, inner_radius, slices, inner_slices):
|
||||
# Create the vertex and normal arrays.
|
||||
vertices = []
|
||||
normals = []
|
||||
|
||||
u_step = 2 * pi / (slices - 1)
|
||||
v_step = 2 * pi / (inner_slices - 1)
|
||||
u = 0.
|
||||
for i in range(slices):
|
||||
cos_u = cos(u)
|
||||
sin_u = sin(u)
|
||||
v = 0.
|
||||
for j in range(inner_slices):
|
||||
cos_v = cos(v)
|
||||
sin_v = sin(v)
|
||||
|
||||
d = (radius + inner_radius * cos_v)
|
||||
x = d * cos_u
|
||||
y = d * sin_u
|
||||
z = inner_radius * sin_v
|
||||
|
||||
nx = cos_u * cos_v
|
||||
ny = sin_u * cos_v
|
||||
nz = sin_v
|
||||
|
||||
vertices.extend([x, y, z])
|
||||
normals.extend([nx, ny, nz])
|
||||
v += v_step
|
||||
u += u_step
|
||||
|
||||
# Create ctypes arrays of the lists
|
||||
vertices = (GLfloat * len(vertices))(*vertices)
|
||||
normals = (GLfloat * len(normals))(*normals)
|
||||
|
||||
# Create a list of triangle indices.
|
||||
indices = []
|
||||
for i in range(slices - 1):
|
||||
for j in range(inner_slices - 1):
|
||||
p = i * inner_slices + j
|
||||
indices.extend([p, p + inner_slices, p + inner_slices + 1])
|
||||
indices.extend([p, p + inner_slices + 1, p + 1])
|
||||
indices = (GLuint * len(indices))(*indices)
|
||||
|
||||
# Compile a display list
|
||||
self.list = glGenLists(1)
|
||||
glNewList(self.list, GL_COMPILE)
|
||||
|
||||
glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT)
|
||||
glEnableClientState(GL_VERTEX_ARRAY)
|
||||
glEnableClientState(GL_NORMAL_ARRAY)
|
||||
glVertexPointer(3, GL_FLOAT, 0, vertices)
|
||||
glNormalPointer(GL_FLOAT, 0, normals)
|
||||
glDrawElements(GL_TRIANGLES, len(indices), GL_UNSIGNED_INT, indices)
|
||||
glPopClientAttrib()
|
||||
|
||||
glEndList()
|
||||
|
||||
def draw(self):
|
||||
glCallList(self.list)
|
||||
|
||||
setup()
|
||||
torus = Torus(1, 0.3, 50, 30)
|
||||
rx = ry = rz = 0
|
||||
|
||||
pyglet.app.run()
|