Skip to content

Instantly share code, notes, and snippets.

@tjferry14

tjferry14/air-hockey.py Secret

Last active Aug 29, 2015
Embed
What would you like to do?
Air Hockey Project
# Air hockey game for two players. First to seven goals wins.
from scene import *
from sound import *
from copy import copy
from math import modf, floor
import time
import ui
header_font = 'Avenir-Heavy'
font = 'SourceSansPro-Black'
color_dict = {
'Red' : Color(1, 0, 0),
'Green' : Color(0.48627450980392156, 0.9882352941176471, 0),
'Purple' : Color(0.5, 0, 1),
'Blue' : Color(0, 0.5, 1),
'Gold' : Color(1, 0.8431372549019608, 0),
'Aqua' : Color(0.4, 1, 1) }
class Player (object):
def __init__(self, color_name):
color_name = color_name.title()
self.name = color_name + ' Player'
self.color = color_dict[color_name]
def __str__(self):
return self.name
class Puck (object): # Puck object
def __init__(self, pos, scene): # define the puck
self.vector = Vector3(0, 0, 0) # set the puck as a vector
self.pos = pos # set the puck position
def reverse_vector(self):
v = self.vector
self.vector = Vector3(-v.x, -v.y, 0)
# following code sets up the hockey table scene
class HockeyScene (Scene):
def __init__(self, left_player_color='Green', right_player_color='Gold'):
self.left_player = Player(left_player_color)
self.right_player = Player(right_player_color)
self.players = (self.left_player, self.right_player) # sets players in left corner and right corner
def setup(self):
self.start_time = 0.0
self.stop_time = 0.0
self.last_seconds = 0
self.running = False
self.start_time = time.time()
self.running = True
for s in ('Drums_06', 'Woosh_1', 'Powerup_2'): # for these sound effects...
load_effect(s) # load the sound effects in
center = self.size.w / 2 # sets center as center of the screen
middle = self.size.h / 2 # sets middle as middle of the screen
gh = self.size.h / 4 # sets goal height
self.puck_radius = gh / 2.5 # sets puck size/radius
self.puck = self.centered_puck() # makes puck start out centered
# the last time the puck was hit; Used to animate its slow down
self.puck_start_t = 0 # sets puck start to the dead center of the screen
pr = self.puck_radius # set pr to puck radius
self.line_width = self.puck_radius / 6 # sets puck radius inside the center line
lw = self.line_width # sets lw to line width
self.red_line = Rect(center - lw / 2, 0, lw, self.size.h) # sets red center line
self.red_circle = Rect(w=gh, h=gh) # sets circle in the red center line
self.red_circle.center(self.bounds.center()) # set the size of the circle in the center line
self.left_goal = Rect(w=gh, h=gh) # sets left goal as a rectangle
self.right_goal = Rect(w=gh, h=gh) # sets right goal as a rectangle
self.left_goal.center(0, middle) # sets the left goal to be centered
self.right_goal.center(self.size.w, middle) # sets the right goal to be centered
self.winner = None # when someone reaches seven goals, save them to show a message
#self.t = self.puck_start_t
def get_player_color(self):
return self.players
def score_goal(self, player):
player.score += 1 # sets player score to plus one for backend purposes
self.puck = self.centered_puck() # sets up variable for the puck being centered
# following block of code sets the score so when a player reaches 7 they win
for p in self.players:
if p.score > 6:
self.winner = p
break
overlay = Layer(self.bounds) # sets the cover as an overlay, so its not behind the ice
def hide_overlay():
overlay.animate('alpha', 0.0, 1, completion=lambda: overlay.remove_layer())
if self.winner:
message = "%s Wins" % (self.winner) # shows a message for whatever player wins
on_completion = None
self.stop_time = time.time()
self.running = False
self.hide_overlay = hide_overlay
else:
message = "Goal!" # shows a message for when a player scores a goal
on_completion = hide_overlay
self.stop_time = time.time()
self.running = True
# restart the puck on the other player's side
if player == self.left_player:
self.puck.pos.x += self.size.w / 4 + self.puck_radius
else:
self.puck.pos.x -= self.size.w / 4 + self.puck_radius
size = self.puck_radius * 1.0 # sets puck radius
text_layer = TextLayer(message, font, size) # sets text font to Futura
text_layer.frame.center(self.bounds.center()) # sets the text to display from the center
text_layer.frame.y += self.size.h / 4 # sets the text to autosize itself
text_layer.animate('scale_x', 1.3, 0.3, autoreverse=True) # sets the x animation display time/size
text_layer.animate('scale_y', 1.3, 0.3, autoreverse=True) # sets the y animation display time/size
overlay.add_layer(text_layer) # sets the text layer to overlay
#following code sets color, etc. for when a player scores a goal
start_color, end_color = copy(player.color), copy(player.color)
start_color.a = 0
end_color.a = .5
overlay.background = start_color
overlay.animate('background', end_color, .10, completion=on_completion)
self.add_layer(overlay)
play_effect('Woosh_1') # sets effect of puck scoring to Woosh
def centered_puck(self): # set center
pos = Rect(w=self.puck_radius, h=self.puck_radius)
pos.center(self.bounds.center())
return Puck(pos, self)
def move_puck(self): # this lets the puck move by touching it once, like a real air hockey game
puck = self.puck
ease_x = (self.t - self.puck_start_t) / 4.0
ease = max(0, 1 - curve_ease_out(ease_x))
puck.pos.x += puck.vector.x * ease
puck.pos.y += puck.vector.y * ease
if puck.pos.right() > self.size.w and not puck.pos.intersects(self.right_goal): # if puck hits the side, play the drum sound effect
puck.vector.x *= -1
play_effect('Drums_06')
puck.pos.x = min(self.size.w - puck.pos.w, puck.pos.x)
if puck.pos.left() > self.size.w: # sets the left goal
self.score_goal(self.left_player)
return
if puck.pos.left() < 0 and not puck.pos.intersects(self.left_goal):
puck.vector.x *= -1
play_effect('Drums_06')
puck.pos.x = max(0, puck.pos.x)
if puck.pos.right() < 0: # sets the right goal
self.score_goal(self.right_player)
return
if puck.pos.top() > self.size.h or puck.pos.bottom() < 0: # if puck hits the side, play the drum sound effect
puck.vector.y *= -1
play_effect('Drums_06')
puck.pos.y = max(0, puck.pos.y)
puck.pos.y = min(self.size.h - puck.pos.h, puck.pos.y)
def draw(self):
stroke_weight(self.line_width)
background(1, 1, 1)
# red lines on ice in middle
stroke(1.00, 0.40, 0.40)
fill(1.0, 0.0, 0.0)
rect(*self.red_line.as_tuple())
fill(1, 1, 1)
ellipse(*self.red_circle.as_tuple())
# red goal markers
for goal in (self.left_goal, self.right_goal):
rect(*goal.as_tuple())
for touch in self.touches.values():
self.handle_touch(touch)
# black puck
no_stroke()
fill(0, 0, 0)
ellipse(*self.puck.pos.as_tuple())
self.draw_scores()
self.move_puck()
if self.root_layer:
self.root_layer.update(self.dt)
self.root_layer.draw()
if self.winner is not None: # shows message/allows player to tap to play again
c = Rect()
c.center(self.bounds.center())
tint(1, 1, 1)
message = "Tap to Play Again"
y_offset = self.size.h / 4
size = self.puck_radius
text(message, x=c.x, y=c.y-y_offset, font_size=size, font_name=font)
def draw_scores(self): # sets score font/size
for p, x in zip(self.players, (50, self.size.w - 50)):
tint(*p.color.as_tuple())
text(str(p.score), x=x, y=self.size.h- 50, font_size=32, font_name=font)
tint(0.27450980392156865, 0.5098039215686274, 0.7058823529411765)
#Format the elapsed time (dt):
dt = 0.0
if self.running:
dt = (time.time() - self.start_time)
else:
dt = (self.stop_time - self.start_time)
minutes = abs(dt) / 60 # absolute value of dt divided by 60
seconds = abs(dt) % 60 # absolute value times percentage of 60
centiseconds = modf(abs(dt))[0] * 100
s = '%02d:%02d.%02d' % (minutes, seconds, centiseconds)
text(s, x=510, y=self.size.h- 50, font_size=34, font_name=font)
def handle_touch(self, touch):
if self.winner:
# check for winner overlay and clear.
# Overlays to restart game to play again
self.winner = None
self.hide_overlay()
del self.hide_overlay
self.left_player.score = 0
self.right_player.score = 0
# stops the clock
self.stop_time = time.time()
self.running = False
# resets the clock
self.start_time = 0.0
self.last_seconds = 0
self.stop_time = 0.0
# starts the clock
self.start_time = time.time()
self.running = True
return
tx = touch.location.x # sets x touch location...
ty = touch.location.y # sets y touch location...
finger = Rect(tx - 20, ty - 20, 40, 40) # sets touch location for finger size
if finger.intersects(self.puck.pos):
dx = tx - touch.prev_location.x
dy = ty - touch.prev_location.y
# Richochet for unmoving finger, to make it like a real air hockey game
if abs(dx) < 5 and abs(dy) < 5:
self.puck.reverse_vector()
# Slide puck out from the finger. No puck holding.
if dx == 0: dx = self.puck.vector.x
if dy == 0: dy = self.puck.vector.y
if not any([dx, dy]): return
while finger.intersects(self.puck.pos):
self.puck.pos.x += dx
self.puck.pos.y += dy
else:
self.step = 0
self.puck_start_t = self.t
self.puck.vector = Vector3(dx * .8, dy * .8, 0)
self.puck.pos.x += dx
self.puck.pos.y += dy
def play_tapped(sender):
global colors
if 'red' in colors:
scene_view.scene = HockeyScene('Red', 'Blue')
if 'green' in colors:
scene_view.scene = HockeyScene('Green', 'Gold')
if 'purple' in colors:
scene_view.scene = HockeyScene('Purple', 'Aqua')
else:
scene_view.scene = HockeyScene('Green', 'Gold')
root_view.add_subview(scene_view)
sender.superview.close()
def color_tapped(sender):
global colors
sender.tint_color = 'white'
sender.image = ui.Image.named('ionicons-checkmark-round-24')
colors = [sender.name]
def play_game(sender):
ui.load_view('colors').present(style='sheet', hide_title_bar=True)
root_view = ui.load_view('air-hockey')
root_view.background_color = (0.40, 0.80, 1.00)
root_view['playgame'].font = (header_font, 45)
root_view.present(orientations=['landscape'], hide_title_bar=True)
scene_view = SceneView(frame=root_view.frame)
scene_view.flex = 'WH'
[{"class":"View","attributes":{"background_color":"RGBA(1.000000,1.000000,1.000000,1.000000)","tint_color":"RGBA(0.000000,0.478000,1.000000,1.000000)","enabled":true,"border_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","flex":""},"frame":"{{0, 0}, {980, 955}}","nodes":[{"class":"Button","attributes":{"tint_color":"RGBA(1.000000,1.000000,1.000000,1.000000)","font_size":45,"enabled":true,"font_bold":false,"name":"playgame","flex":"","border_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","action":"play_game","uuid":"A2AE2DF5-4F00-4C3F-8BA8-D55D96E9BC75","background_color":"RGBA(0.344898,0.928571,0.265306,1.000000)","title":"Play Game"},"frame":"{{309, 342}, {405.5, 97.5}}","nodes":[]},{"class":"View","attributes":{"name":"left_stick","background_color":"RGBA(0.928571,0.716754,0.198980,1.000000)","uuid":"0227DB7A-B2EE-4D0A-8ED4-15F21514FDAB","enabled":true,"border_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","flex":""},"frame":"{{119, 387}, {40, 261}}","nodes":[]},{"class":"View","attributes":{"name":"right_stick","background_color":"RGBA(0.928571,0.716754,0.198980,1.000000)","uuid":"6029AA23-D717-4158-982D-34CFF6F0A3E1","enabled":true,"border_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","flex":""},"frame":"{{885, 387}, {40, 261}}","nodes":[]},{"class":"View","attributes":{"name":"view3","background_color":"RGBA(0.928571,0.716754,0.198980,1.000000)","uuid":"C06A6833-8BCF-4A0A-9A45-A0661C915AE5","enabled":true,"border_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","flex":""},"frame":"{{119, 608}, {100, 40}}","nodes":[]},{"class":"View","attributes":{"name":"view4","background_color":"RGBA(0.928571,0.716754,0.198980,1.000000)","uuid":"BCCA6DCC-2926-4CC9-A3B5-C9F60BB373C7","enabled":true,"border_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","flex":""},"frame":"{{825, 608}, {100, 40}}","nodes":[]},{"class":"View","attributes":{"tint_color":"RGBA(1.000000,1.000000,1.000000,1.000000)","enabled":true,"flex":"","name":"view5","border_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","background_color":"RGBA(1.000000,1.000000,1.000000,1.000000)","uuid":"73240C0B-10FB-47AF-A201-85BE2D9C213A"},"frame":"{{119, 410}, {40, 20}}","nodes":[]},{"class":"View","attributes":{"tint_color":"RGBA(1.000000,1.000000,1.000000,1.000000)","enabled":true,"flex":"","name":"view6","border_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","background_color":"RGBA(1.000000,1.000000,1.000000,1.000000)","uuid":"FCAAC126-BD78-495C-937C-37703237B60A"},"frame":"{{885, 410}, {40, 20}}","nodes":[]},{"class":"View","attributes":{"tint_color":"RGBA(1.000000,1.000000,1.000000,1.000000)","enabled":true,"flex":"","name":"view7","border_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","background_color":"RGBA(1.000000,1.000000,1.000000,1.000000)","uuid":"1FCB58EA-461E-4B75-8176-C3FE8371C543"},"frame":"{{119, 466}, {40, 20}}","nodes":[]},{"class":"View","attributes":{"tint_color":"RGBA(1.000000,1.000000,1.000000,1.000000)","enabled":true,"flex":"","name":"view8","border_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","background_color":"RGBA(1.000000,1.000000,1.000000,1.000000)","uuid":"DC369E05-4B03-43EC-9FA5-3D821845B47A"},"frame":"{{885, 466}, {40, 20}}","nodes":[]},{"class":"View","attributes":{"tint_color":"RGBA(1.000000,1.000000,1.000000,1.000000)","enabled":true,"flex":"","name":"view9","border_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","background_color":"RGBA(1.000000,0.285714,0.285714,1.000000)","uuid":"FD5C98E8-DC26-4029-A908-DF23E0DB51D0"},"frame":"{{119, 438}, {40, 20}}","nodes":[]},{"class":"View","attributes":{"tint_color":"RGBA(1.000000,1.000000,1.000000,1.000000)","enabled":true,"flex":"","name":"view10","border_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","background_color":"RGBA(1.000000,0.285714,0.285714,1.000000)","uuid":"629030E1-32E4-46B8-AFED-6BFFC4BC052C"},"frame":"{{885, 438}, {40, 20}}","nodes":[]},{"class":"Label","attributes":{"font_size":64,"enabled":true,"text":"Halla Hockey","font_name":"Bauer","name":"gameheader","flex":"","border_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","text_color":"RGBA(1.000000,1.000000,1.000000,1.000000)","alignment":"center","uuid":"50B2FEE6-82CC-4BC9-9493-D172FC390A7B"},"frame":"{{167.5, 6}, {690, 135.5}}","nodes":[]}]}]
[{"class":"View","attributes":{"background_color":"RGBA(1.000000,1.000000,1.000000,1.000000)","tint_color":"RGBA(0.000000,0.478000,1.000000,1.000000)","enabled":true,"border_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","flex":""},"frame":"{{0, 0}, {540, 575}}","nodes":[{"class":"Label","attributes":{"font_size":33,"enabled":true,"text":"Game Options","font_name":"Avenir-Heavy","name":"label1","flex":"","border_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","text_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","alignment":"center","uuid":"85248D84-7B83-4F2E-9734-8E62BE0ACEB3"},"frame":"{{139, 18}, {262, 53.5}}","nodes":[]},{"class":"Label","attributes":{"font_size":17,"enabled":true,"text":"Player 1","flex":"","name":"label2","border_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","text_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","alignment":"center","uuid":"13DAF118-8B87-4779-8031-931DC2F1394E"},"frame":"{{195, 150.5}, {150, 32}}","nodes":[]},{"class":"Label","attributes":{"font_size":17,"enabled":true,"text":"Player 2","flex":"","name":"label3","border_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","text_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","alignment":"center","uuid":"6E66F87A-5A2B-415B-97EA-D76F84382C8B"},"frame":"{{195, 340}, {150, 32}}","nodes":[]},{"class":"Button","attributes":{"font_size":15,"enabled":true,"flex":"","font_bold":false,"name":"red","uuid":"680A273E-0DC8-4B25-BFA6-CB3A160BCFCF","border_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","action":"color_tapped","background_color":"RGBA(1.000000,0.000000,0.000000,1.000000)","title":""},"frame":"{{21.5, 209.5}, {32, 32}}","nodes":[]},{"class":"Button","attributes":{"font_size":15,"enabled":true,"flex":"","font_bold":false,"name":"green","uuid":"327CEC0D-92FD-41F5-8E75-8840023C627A","border_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","action":"color_tapped","background_color":"RGBA(0.203265,0.857143,0.122449,1.000000)","title":""},"frame":"{{254, 209.5}, {32, 32}}","nodes":[]},{"class":"Button","attributes":{"font_size":15,"enabled":true,"flex":"","font_bold":false,"name":"purple","uuid":"D12EC798-440D-4247-BEA2-CA4D8D60240B","border_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","action":"color_tapped","background_color":"RGBA(0.896735,0.132653,0.928571,1.000000)","title":""},"frame":"{{499, 209.5}, {32, 32}}","nodes":[]},{"class":"Button","attributes":{"font_size":15,"enabled":true,"flex":"","font_bold":false,"name":"blue","uuid":"EF1C1DE6-2B37-4550-8D3F-617E20077DCD","border_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","action":"color_tapped","background_color":"RGBA(0.100000,0.000000,1.000000,1.000000)","title":""},"frame":"{{21.5, 420}, {32, 32}}","nodes":[]},{"class":"Button","attributes":{"font_size":15,"enabled":true,"flex":"","font_bold":false,"name":"gold","uuid":"E56151B1-FA75-43E6-94A8-550C4F76E463","border_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","action":"color_tapped","background_color":"RGBA(0.928571,0.896735,0.132653,1.000000)","title":""},"frame":"{{254, 420}, {32, 32}}","nodes":[]},{"class":"Button","attributes":{"font_size":15,"enabled":true,"flex":"","font_bold":false,"name":"aqua","uuid":"F3729010-7B06-4742-85CB-7AE0C77D440C","border_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","action":"color_tapped","background_color":"RGBA(0.132653,0.928571,0.904694,1.000000)","title":""},"frame":"{{499, 420}, {32, 32}}","nodes":[]},{"class":"Button","attributes":{"font_size":15,"enabled":true,"tint_color":"RGBA(1.000000,1.000000,1.000000,1.000000)","font_bold":false,"name":"button1","flex":"","border_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","action":"play_tapped","uuid":"416DA351-0133-4DAA-BC5C-E9A34F15C487","background_color":"RGBA(0.224490,0.785714,0.263775,1.000000)","title":"Play!"},"frame":"{{230, 519}, {80, 40}}","nodes":[]}]}]
@jsbain

This comment has been minimized.

Copy link

@jsbain jsbain commented Oct 8, 2014

Line 317, you are creating a NEW HockeyScene! Set this to game instead?
Alternatively, why not have a HockeyScene take arguments that define the color, so that your color selection view can be completely independent of the game ( and thus you could reuse it)

@jsbain

This comment has been minimized.

Copy link

@jsbain jsbain commented Oct 8, 2014

Also, since your HockeyScene does not have an init, none of those self.xxxx will have been created until you run the scene. Anything you need to access from the outside should be created in init instead.

@cclauss

This comment has been minimized.

Copy link

@cclauss cclauss commented Oct 11, 2014

color_dict = {
    'Red'    : scene.Color(1, 0, 0),
    'Green'  : scene.Color(0.48627450980392156, 0.9882352941176471, 0),
    'Purple' : scene.Color(0.5, 0, 1),
    'Blue'   : scene.Color(0, 0.5, 1),
    'Gold'   : scene.Color(1, 0.8431372549019608, 0),
    'Aqua'   : scene.Color(0.4, 1, 1) }

class Player (object):
    def __init__(self, color_name):
        color_name = color_name.title()
        self.name  = color_name + ' Player'
        self.color = color_dict[color_name]
        self.score = 0

# [ ... ]

class HockeyScene (Scene):
    def __init__(self, left_player_color='Green', right_player_color='Gold'):
        self.left_player  = Player(left_player_color)
        self.right_player = Player(right_player_color)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.