Golf game boilerplateA small Bejeweled-like game in PygameSimple top down shooter gameSnake game in PygamePython/Pygame Fighting GameSimon memory game in pygameFlappy Bird Style Game in Space SettingMouse Click Controlled Meteor Avoidance GameSimple Python Pygame GameFirst Pong gamePython Pygame treasure hunt game
Teaching indefinite integrals that require special-casing
How can a jailer prevent the Forge Cleric's Artisan's Blessing from being used?
Can I Retrieve Email Addresses from BCC?
Is there a problem with hiding "forgot password" until it's needed?
Can a Bard use an arcane focus?
Greatest common substring
A workplace installs custom certificates on personal devices, can this be used to decrypt HTTPS traffic?
My boss asked me to take a one-day class, then signs it up as a day off
How do I repair my stair bannister?
Lightning Web Component - do I need to track changes for every single input field in a form
Java - What do constructor type arguments mean when placed *before* the type?
Is there a good way to store credentials outside of a password manager?
node command while defining a coordinate in TikZ
For airliners, what prevents wing strikes on landing in bad weather?
How will losing mobility of one hand affect my career as a programmer?
What is the opposite of 'gravitas'?
Perfect riffle shuffles
Did US corporations pay demonstrators in the German demonstrations against article 13?
Stereotypical names
Who must act to prevent Brexit on March 29th?
The most efficient algorithm to find all possible integer pairs which sum to a given integer
Female=gender counterpart?
Can the electrostatic force be infinite in magnitude?
Partial sums of primes
Golf game boilerplate
A small Bejeweled-like game in PygameSimple top down shooter gameSnake game in PygamePython/Pygame Fighting GameSimon memory game in pygameFlappy Bird Style Game in Space SettingMouse Click Controlled Meteor Avoidance GameSimple Python Pygame GameFirst Pong gamePython Pygame treasure hunt game
$begingroup$
I wrote a program in pygame that basically acts as a physics engine for a ball. You can hit the ball around and your strokes are counted, as well as an extra stroke for going out of bounds. If I do further develop this, I'd make the angle and power display toggleable, but I do like showing them right now:
import pygame as pg
import math
SCREEN_WIDTH = 1500
SCREEN_HEIGHT = 800
WINDOW_COLOR = (100, 100, 100)
BALL_COLOR = (255, 255, 255)
BALL_OUTLINE_COLOR = (255, 0, 0)
LINE_COLOR = (0, 0, 255)
ALINE_COLOR = (0, 0, 0)
START_X = int(.5 * SCREEN_WIDTH)
START_Y = int(.99 * SCREEN_HEIGHT)
POWER_MULTIPLIER = .85
SPEED_MULTIPLIER = 2
BALL_RADIUS = 10
pg.init()
pg.display.set_caption('Golf')
window = pg.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pg.event.set_grab(True)
pg.mouse.set_cursor((8, 8), (0, 0), (0, 0, 0, 0, 0, 0, 0, 0), (0, 0, 0, 0, 0, 0, 0, 0))
strokeFont = pg.font.SysFont("monospace", 50)
STROKECOLOR = (255, 255, 0)
powerFont = pg.font.SysFont("arial", 15, bold=True)
POWERCOLOR = (0, 255, 0)
angleFont = pg.font.SysFont("arial", 15, bold=True)
ANGLECOLOR = (0, 255, 0)
penaltyFont = pg.font.SysFont("georgia", 40, bold=True)
PENALTYCOLOR = (255, 0, 0)
class Ball(object):
def __init__(self, x, y, rad, c, oc):
self.x = x
self.y = y
self.radius = rad
self.color = c
self.outlinecolor = oc
def show(self, window):
pg.draw.circle(window, self.outlinecolor, (self.x, self.y), self.radius)
pg.draw.circle(window, self.color, (self.x, self.y), self.radius - int(.4 * self.radius))
@staticmethod
def path(x, y, p, a, t):
vx, vy = p * math.cos(a), p * math.sin(a) #Velocities
dx, dy = vx * t, vy * t - 4.9 * t ** 2 #Distances Traveled
print(' x-pos: %spx' % str(round(dx + x)))
print(' y-pos: %spx' % str(round(abs(dy - y))))
return round(dx + x), round(y - dy)
@staticmethod
def quadrant(x,y,xm,ym):
if ym < y and xm > x:
return 1
elif ym < y and xm < x:
return 2
elif ym > y and xm < x:
return 3
elif ym > y and xm > x:
return 4
else:
return False
def draw_window():
window.fill(WINDOW_COLOR)
ball.show(window)
if not shoot:
arrow(window, ALINE_COLOR, ALINE_COLOR, aline[0], aline[1], 5)
arrow(window, LINE_COLOR, LINE_COLOR, line[0], line[1], 5)
stroke_text = 'Strokes: %s' % strokes
stroke_label = strokeFont.render(stroke_text, 1, STROKECOLOR)
if not strokes:
window.blit(stroke_label, (SCREEN_WIDTH - .21 * SCREEN_WIDTH, SCREEN_HEIGHT - .985 * SCREEN_HEIGHT))
else:
window.blit(stroke_label, (SCREEN_WIDTH - (.21+.02*math.floor(math.log10(strokes))) * SCREEN_WIDTH, SCREEN_HEIGHT - .985 * SCREEN_HEIGHT))
power_text = 'Shot Strength: %sN' % power_display
power_label = powerFont.render(power_text, 1, POWERCOLOR)
if not shoot: window.blit(power_label, (cursor_pos[0] + .008 * SCREEN_WIDTH, cursor_pos[1]))
angle_text = 'Angle: %s°' % angle_display
angle_label = angleFont.render(angle_text, 1, ANGLECOLOR)
if not shoot: window.blit(angle_label, (ball.x - .06 * SCREEN_WIDTH, ball.y - .01 * SCREEN_HEIGHT))
if Penalty:
penalty_text = 'Out of Bounds! +1 Stroke'
penalty_label = penaltyFont.render(penalty_text, 1, PENALTYCOLOR)
penalty_rect = penalty_label.get_rect(center=(SCREEN_WIDTH/2, .225*SCREEN_HEIGHT))
window.blit(penalty_label, penalty_rect)
pg.display.flip()
def angle(cursor_pos):
x, y, xm, ym = ball.x, ball.y, cursor_pos[0], cursor_pos[1]
if x-xm:
angle = math.atan((y - ym) / (x - xm))
elif y > ym:
angle = math.pi/2
else:
angle = 3*math.pi/2
q = ball.quadrant(x,y,xm,ym)
if q: angle = math.pi*math.floor(q/2) - angle
if round(angle*180/math.pi) == 360:
angle = 0
if x > xm and round(angle*180/math.pi) == 0:
angle = math.pi
return angle
def arrow(screen, lcolor, tricolor, start, end, trirad):
pg.draw.line(screen, lcolor, start, end, 2)
rotation = math.degrees(math.atan2(start[1] - end[1], end[0] - start[0])) + 90
pg.draw.polygon(screen, tricolor, ((end[0] + trirad * math.sin(math.radians(rotation)),
end[1] + trirad * math.cos(math.radians(rotation))),
(end[0] + trirad * math.sin(math.radians(rotation - 120)),
end[1] + trirad * math.cos(math.radians(rotation - 120))),
(end[0] + trirad * math.sin(math.radians(rotation + 120)),
end[1] + trirad * math.cos(math.radians(rotation + 120)))))
def distance(x,y):
return math.sqrt(x**2 + y**2)
x, y, time, power, ang, strokes = 0, 0, 0, 0, 0, 0
xb, yb = None, None
shoot, Penalty = False, False
p_ticks = 0
ball = Ball(START_X, START_Y, BALL_RADIUS, BALL_COLOR, BALL_OUTLINE_COLOR)
quit = False
BARRIER = 1
try:
while not quit:
seconds=(pg.time.get_ticks()-p_ticks)/1000
if seconds > 1.2: Penalty = False
cursor_pos = pg.mouse.get_pos()
line = [(ball.x, ball.y), cursor_pos]
line_ball_x, line_ball_y = cursor_pos[0] - ball.x, cursor_pos[1] - ball.y
aline = [(ball.x, ball.y), (ball.x + .015 * SCREEN_WIDTH, ball.y)]
if not shoot:
power_display = round(
distance(line_ball_x, line_ball_y) * POWER_MULTIPLIER / 10)
angle_display = round(angle(cursor_pos) * 180 / math.pi)
if shoot:
if ball.y < SCREEN_HEIGHT:
if BARRIER < ball.x < SCREEN_WIDTH:
time += .3 * SPEED_MULTIPLIER
print('n time: %ss' % round(time, 2))
po = ball.path(x, y, power, ang, time)
ball.x, ball.y = po[0], po[1]
else:
print('Out of Bounds!')
Penalty = True
p_ticks = pg.time.get_ticks()
strokes += 1
shoot = False
if BARRIER < xb < SCREEN_WIDTH:
ball.x = xb
else:
ball.x = START_X
ball.y = yb
else:
shoot = False
ball.y = START_Y
for event in pg.event.get():
if event.type == pg.QUIT:
quit = True
if event.type == pg.KEYDOWN:
if event.key == pg.K_ESCAPE:
quit = True
if event.type == pg.MOUSEBUTTONDOWN:
if not shoot:
shoot = True
x, y = ball.x, ball.y
xb, yb = ball.x, ball.y
time, power = 0, (
distance(line_ball_x, line_ball_y)) * POWER_MULTIPLIER / 10
print('nnBall Hit!')
print('npower: %sN' % round(power, 2))
ang = angle(cursor_pos)
print('angle: %s°' % round(ang * 180 / math.pi, 2))
print('cos(a): %s' % round(math.cos(ang), 2)), print('sin(a): %s' % round(math.sin(ang), 2))
strokes += 1
draw_window()
print("nShutting down...")
pg.quit()
except Exception as error:
print(f'A fatal error (error) has occurred. The program is shutting down.')
pg.quit()
Feedback of any kind is very welcome!
python pygame physics
$endgroup$
add a comment |
$begingroup$
I wrote a program in pygame that basically acts as a physics engine for a ball. You can hit the ball around and your strokes are counted, as well as an extra stroke for going out of bounds. If I do further develop this, I'd make the angle and power display toggleable, but I do like showing them right now:
import pygame as pg
import math
SCREEN_WIDTH = 1500
SCREEN_HEIGHT = 800
WINDOW_COLOR = (100, 100, 100)
BALL_COLOR = (255, 255, 255)
BALL_OUTLINE_COLOR = (255, 0, 0)
LINE_COLOR = (0, 0, 255)
ALINE_COLOR = (0, 0, 0)
START_X = int(.5 * SCREEN_WIDTH)
START_Y = int(.99 * SCREEN_HEIGHT)
POWER_MULTIPLIER = .85
SPEED_MULTIPLIER = 2
BALL_RADIUS = 10
pg.init()
pg.display.set_caption('Golf')
window = pg.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pg.event.set_grab(True)
pg.mouse.set_cursor((8, 8), (0, 0), (0, 0, 0, 0, 0, 0, 0, 0), (0, 0, 0, 0, 0, 0, 0, 0))
strokeFont = pg.font.SysFont("monospace", 50)
STROKECOLOR = (255, 255, 0)
powerFont = pg.font.SysFont("arial", 15, bold=True)
POWERCOLOR = (0, 255, 0)
angleFont = pg.font.SysFont("arial", 15, bold=True)
ANGLECOLOR = (0, 255, 0)
penaltyFont = pg.font.SysFont("georgia", 40, bold=True)
PENALTYCOLOR = (255, 0, 0)
class Ball(object):
def __init__(self, x, y, rad, c, oc):
self.x = x
self.y = y
self.radius = rad
self.color = c
self.outlinecolor = oc
def show(self, window):
pg.draw.circle(window, self.outlinecolor, (self.x, self.y), self.radius)
pg.draw.circle(window, self.color, (self.x, self.y), self.radius - int(.4 * self.radius))
@staticmethod
def path(x, y, p, a, t):
vx, vy = p * math.cos(a), p * math.sin(a) #Velocities
dx, dy = vx * t, vy * t - 4.9 * t ** 2 #Distances Traveled
print(' x-pos: %spx' % str(round(dx + x)))
print(' y-pos: %spx' % str(round(abs(dy - y))))
return round(dx + x), round(y - dy)
@staticmethod
def quadrant(x,y,xm,ym):
if ym < y and xm > x:
return 1
elif ym < y and xm < x:
return 2
elif ym > y and xm < x:
return 3
elif ym > y and xm > x:
return 4
else:
return False
def draw_window():
window.fill(WINDOW_COLOR)
ball.show(window)
if not shoot:
arrow(window, ALINE_COLOR, ALINE_COLOR, aline[0], aline[1], 5)
arrow(window, LINE_COLOR, LINE_COLOR, line[0], line[1], 5)
stroke_text = 'Strokes: %s' % strokes
stroke_label = strokeFont.render(stroke_text, 1, STROKECOLOR)
if not strokes:
window.blit(stroke_label, (SCREEN_WIDTH - .21 * SCREEN_WIDTH, SCREEN_HEIGHT - .985 * SCREEN_HEIGHT))
else:
window.blit(stroke_label, (SCREEN_WIDTH - (.21+.02*math.floor(math.log10(strokes))) * SCREEN_WIDTH, SCREEN_HEIGHT - .985 * SCREEN_HEIGHT))
power_text = 'Shot Strength: %sN' % power_display
power_label = powerFont.render(power_text, 1, POWERCOLOR)
if not shoot: window.blit(power_label, (cursor_pos[0] + .008 * SCREEN_WIDTH, cursor_pos[1]))
angle_text = 'Angle: %s°' % angle_display
angle_label = angleFont.render(angle_text, 1, ANGLECOLOR)
if not shoot: window.blit(angle_label, (ball.x - .06 * SCREEN_WIDTH, ball.y - .01 * SCREEN_HEIGHT))
if Penalty:
penalty_text = 'Out of Bounds! +1 Stroke'
penalty_label = penaltyFont.render(penalty_text, 1, PENALTYCOLOR)
penalty_rect = penalty_label.get_rect(center=(SCREEN_WIDTH/2, .225*SCREEN_HEIGHT))
window.blit(penalty_label, penalty_rect)
pg.display.flip()
def angle(cursor_pos):
x, y, xm, ym = ball.x, ball.y, cursor_pos[0], cursor_pos[1]
if x-xm:
angle = math.atan((y - ym) / (x - xm))
elif y > ym:
angle = math.pi/2
else:
angle = 3*math.pi/2
q = ball.quadrant(x,y,xm,ym)
if q: angle = math.pi*math.floor(q/2) - angle
if round(angle*180/math.pi) == 360:
angle = 0
if x > xm and round(angle*180/math.pi) == 0:
angle = math.pi
return angle
def arrow(screen, lcolor, tricolor, start, end, trirad):
pg.draw.line(screen, lcolor, start, end, 2)
rotation = math.degrees(math.atan2(start[1] - end[1], end[0] - start[0])) + 90
pg.draw.polygon(screen, tricolor, ((end[0] + trirad * math.sin(math.radians(rotation)),
end[1] + trirad * math.cos(math.radians(rotation))),
(end[0] + trirad * math.sin(math.radians(rotation - 120)),
end[1] + trirad * math.cos(math.radians(rotation - 120))),
(end[0] + trirad * math.sin(math.radians(rotation + 120)),
end[1] + trirad * math.cos(math.radians(rotation + 120)))))
def distance(x,y):
return math.sqrt(x**2 + y**2)
x, y, time, power, ang, strokes = 0, 0, 0, 0, 0, 0
xb, yb = None, None
shoot, Penalty = False, False
p_ticks = 0
ball = Ball(START_X, START_Y, BALL_RADIUS, BALL_COLOR, BALL_OUTLINE_COLOR)
quit = False
BARRIER = 1
try:
while not quit:
seconds=(pg.time.get_ticks()-p_ticks)/1000
if seconds > 1.2: Penalty = False
cursor_pos = pg.mouse.get_pos()
line = [(ball.x, ball.y), cursor_pos]
line_ball_x, line_ball_y = cursor_pos[0] - ball.x, cursor_pos[1] - ball.y
aline = [(ball.x, ball.y), (ball.x + .015 * SCREEN_WIDTH, ball.y)]
if not shoot:
power_display = round(
distance(line_ball_x, line_ball_y) * POWER_MULTIPLIER / 10)
angle_display = round(angle(cursor_pos) * 180 / math.pi)
if shoot:
if ball.y < SCREEN_HEIGHT:
if BARRIER < ball.x < SCREEN_WIDTH:
time += .3 * SPEED_MULTIPLIER
print('n time: %ss' % round(time, 2))
po = ball.path(x, y, power, ang, time)
ball.x, ball.y = po[0], po[1]
else:
print('Out of Bounds!')
Penalty = True
p_ticks = pg.time.get_ticks()
strokes += 1
shoot = False
if BARRIER < xb < SCREEN_WIDTH:
ball.x = xb
else:
ball.x = START_X
ball.y = yb
else:
shoot = False
ball.y = START_Y
for event in pg.event.get():
if event.type == pg.QUIT:
quit = True
if event.type == pg.KEYDOWN:
if event.key == pg.K_ESCAPE:
quit = True
if event.type == pg.MOUSEBUTTONDOWN:
if not shoot:
shoot = True
x, y = ball.x, ball.y
xb, yb = ball.x, ball.y
time, power = 0, (
distance(line_ball_x, line_ball_y)) * POWER_MULTIPLIER / 10
print('nnBall Hit!')
print('npower: %sN' % round(power, 2))
ang = angle(cursor_pos)
print('angle: %s°' % round(ang * 180 / math.pi, 2))
print('cos(a): %s' % round(math.cos(ang), 2)), print('sin(a): %s' % round(math.sin(ang), 2))
strokes += 1
draw_window()
print("nShutting down...")
pg.quit()
except Exception as error:
print(f'A fatal error (error) has occurred. The program is shutting down.')
pg.quit()
Feedback of any kind is very welcome!
python pygame physics
$endgroup$
add a comment |
$begingroup$
I wrote a program in pygame that basically acts as a physics engine for a ball. You can hit the ball around and your strokes are counted, as well as an extra stroke for going out of bounds. If I do further develop this, I'd make the angle and power display toggleable, but I do like showing them right now:
import pygame as pg
import math
SCREEN_WIDTH = 1500
SCREEN_HEIGHT = 800
WINDOW_COLOR = (100, 100, 100)
BALL_COLOR = (255, 255, 255)
BALL_OUTLINE_COLOR = (255, 0, 0)
LINE_COLOR = (0, 0, 255)
ALINE_COLOR = (0, 0, 0)
START_X = int(.5 * SCREEN_WIDTH)
START_Y = int(.99 * SCREEN_HEIGHT)
POWER_MULTIPLIER = .85
SPEED_MULTIPLIER = 2
BALL_RADIUS = 10
pg.init()
pg.display.set_caption('Golf')
window = pg.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pg.event.set_grab(True)
pg.mouse.set_cursor((8, 8), (0, 0), (0, 0, 0, 0, 0, 0, 0, 0), (0, 0, 0, 0, 0, 0, 0, 0))
strokeFont = pg.font.SysFont("monospace", 50)
STROKECOLOR = (255, 255, 0)
powerFont = pg.font.SysFont("arial", 15, bold=True)
POWERCOLOR = (0, 255, 0)
angleFont = pg.font.SysFont("arial", 15, bold=True)
ANGLECOLOR = (0, 255, 0)
penaltyFont = pg.font.SysFont("georgia", 40, bold=True)
PENALTYCOLOR = (255, 0, 0)
class Ball(object):
def __init__(self, x, y, rad, c, oc):
self.x = x
self.y = y
self.radius = rad
self.color = c
self.outlinecolor = oc
def show(self, window):
pg.draw.circle(window, self.outlinecolor, (self.x, self.y), self.radius)
pg.draw.circle(window, self.color, (self.x, self.y), self.radius - int(.4 * self.radius))
@staticmethod
def path(x, y, p, a, t):
vx, vy = p * math.cos(a), p * math.sin(a) #Velocities
dx, dy = vx * t, vy * t - 4.9 * t ** 2 #Distances Traveled
print(' x-pos: %spx' % str(round(dx + x)))
print(' y-pos: %spx' % str(round(abs(dy - y))))
return round(dx + x), round(y - dy)
@staticmethod
def quadrant(x,y,xm,ym):
if ym < y and xm > x:
return 1
elif ym < y and xm < x:
return 2
elif ym > y and xm < x:
return 3
elif ym > y and xm > x:
return 4
else:
return False
def draw_window():
window.fill(WINDOW_COLOR)
ball.show(window)
if not shoot:
arrow(window, ALINE_COLOR, ALINE_COLOR, aline[0], aline[1], 5)
arrow(window, LINE_COLOR, LINE_COLOR, line[0], line[1], 5)
stroke_text = 'Strokes: %s' % strokes
stroke_label = strokeFont.render(stroke_text, 1, STROKECOLOR)
if not strokes:
window.blit(stroke_label, (SCREEN_WIDTH - .21 * SCREEN_WIDTH, SCREEN_HEIGHT - .985 * SCREEN_HEIGHT))
else:
window.blit(stroke_label, (SCREEN_WIDTH - (.21+.02*math.floor(math.log10(strokes))) * SCREEN_WIDTH, SCREEN_HEIGHT - .985 * SCREEN_HEIGHT))
power_text = 'Shot Strength: %sN' % power_display
power_label = powerFont.render(power_text, 1, POWERCOLOR)
if not shoot: window.blit(power_label, (cursor_pos[0] + .008 * SCREEN_WIDTH, cursor_pos[1]))
angle_text = 'Angle: %s°' % angle_display
angle_label = angleFont.render(angle_text, 1, ANGLECOLOR)
if not shoot: window.blit(angle_label, (ball.x - .06 * SCREEN_WIDTH, ball.y - .01 * SCREEN_HEIGHT))
if Penalty:
penalty_text = 'Out of Bounds! +1 Stroke'
penalty_label = penaltyFont.render(penalty_text, 1, PENALTYCOLOR)
penalty_rect = penalty_label.get_rect(center=(SCREEN_WIDTH/2, .225*SCREEN_HEIGHT))
window.blit(penalty_label, penalty_rect)
pg.display.flip()
def angle(cursor_pos):
x, y, xm, ym = ball.x, ball.y, cursor_pos[0], cursor_pos[1]
if x-xm:
angle = math.atan((y - ym) / (x - xm))
elif y > ym:
angle = math.pi/2
else:
angle = 3*math.pi/2
q = ball.quadrant(x,y,xm,ym)
if q: angle = math.pi*math.floor(q/2) - angle
if round(angle*180/math.pi) == 360:
angle = 0
if x > xm and round(angle*180/math.pi) == 0:
angle = math.pi
return angle
def arrow(screen, lcolor, tricolor, start, end, trirad):
pg.draw.line(screen, lcolor, start, end, 2)
rotation = math.degrees(math.atan2(start[1] - end[1], end[0] - start[0])) + 90
pg.draw.polygon(screen, tricolor, ((end[0] + trirad * math.sin(math.radians(rotation)),
end[1] + trirad * math.cos(math.radians(rotation))),
(end[0] + trirad * math.sin(math.radians(rotation - 120)),
end[1] + trirad * math.cos(math.radians(rotation - 120))),
(end[0] + trirad * math.sin(math.radians(rotation + 120)),
end[1] + trirad * math.cos(math.radians(rotation + 120)))))
def distance(x,y):
return math.sqrt(x**2 + y**2)
x, y, time, power, ang, strokes = 0, 0, 0, 0, 0, 0
xb, yb = None, None
shoot, Penalty = False, False
p_ticks = 0
ball = Ball(START_X, START_Y, BALL_RADIUS, BALL_COLOR, BALL_OUTLINE_COLOR)
quit = False
BARRIER = 1
try:
while not quit:
seconds=(pg.time.get_ticks()-p_ticks)/1000
if seconds > 1.2: Penalty = False
cursor_pos = pg.mouse.get_pos()
line = [(ball.x, ball.y), cursor_pos]
line_ball_x, line_ball_y = cursor_pos[0] - ball.x, cursor_pos[1] - ball.y
aline = [(ball.x, ball.y), (ball.x + .015 * SCREEN_WIDTH, ball.y)]
if not shoot:
power_display = round(
distance(line_ball_x, line_ball_y) * POWER_MULTIPLIER / 10)
angle_display = round(angle(cursor_pos) * 180 / math.pi)
if shoot:
if ball.y < SCREEN_HEIGHT:
if BARRIER < ball.x < SCREEN_WIDTH:
time += .3 * SPEED_MULTIPLIER
print('n time: %ss' % round(time, 2))
po = ball.path(x, y, power, ang, time)
ball.x, ball.y = po[0], po[1]
else:
print('Out of Bounds!')
Penalty = True
p_ticks = pg.time.get_ticks()
strokes += 1
shoot = False
if BARRIER < xb < SCREEN_WIDTH:
ball.x = xb
else:
ball.x = START_X
ball.y = yb
else:
shoot = False
ball.y = START_Y
for event in pg.event.get():
if event.type == pg.QUIT:
quit = True
if event.type == pg.KEYDOWN:
if event.key == pg.K_ESCAPE:
quit = True
if event.type == pg.MOUSEBUTTONDOWN:
if not shoot:
shoot = True
x, y = ball.x, ball.y
xb, yb = ball.x, ball.y
time, power = 0, (
distance(line_ball_x, line_ball_y)) * POWER_MULTIPLIER / 10
print('nnBall Hit!')
print('npower: %sN' % round(power, 2))
ang = angle(cursor_pos)
print('angle: %s°' % round(ang * 180 / math.pi, 2))
print('cos(a): %s' % round(math.cos(ang), 2)), print('sin(a): %s' % round(math.sin(ang), 2))
strokes += 1
draw_window()
print("nShutting down...")
pg.quit()
except Exception as error:
print(f'A fatal error (error) has occurred. The program is shutting down.')
pg.quit()
Feedback of any kind is very welcome!
python pygame physics
$endgroup$
I wrote a program in pygame that basically acts as a physics engine for a ball. You can hit the ball around and your strokes are counted, as well as an extra stroke for going out of bounds. If I do further develop this, I'd make the angle and power display toggleable, but I do like showing them right now:
import pygame as pg
import math
SCREEN_WIDTH = 1500
SCREEN_HEIGHT = 800
WINDOW_COLOR = (100, 100, 100)
BALL_COLOR = (255, 255, 255)
BALL_OUTLINE_COLOR = (255, 0, 0)
LINE_COLOR = (0, 0, 255)
ALINE_COLOR = (0, 0, 0)
START_X = int(.5 * SCREEN_WIDTH)
START_Y = int(.99 * SCREEN_HEIGHT)
POWER_MULTIPLIER = .85
SPEED_MULTIPLIER = 2
BALL_RADIUS = 10
pg.init()
pg.display.set_caption('Golf')
window = pg.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pg.event.set_grab(True)
pg.mouse.set_cursor((8, 8), (0, 0), (0, 0, 0, 0, 0, 0, 0, 0), (0, 0, 0, 0, 0, 0, 0, 0))
strokeFont = pg.font.SysFont("monospace", 50)
STROKECOLOR = (255, 255, 0)
powerFont = pg.font.SysFont("arial", 15, bold=True)
POWERCOLOR = (0, 255, 0)
angleFont = pg.font.SysFont("arial", 15, bold=True)
ANGLECOLOR = (0, 255, 0)
penaltyFont = pg.font.SysFont("georgia", 40, bold=True)
PENALTYCOLOR = (255, 0, 0)
class Ball(object):
def __init__(self, x, y, rad, c, oc):
self.x = x
self.y = y
self.radius = rad
self.color = c
self.outlinecolor = oc
def show(self, window):
pg.draw.circle(window, self.outlinecolor, (self.x, self.y), self.radius)
pg.draw.circle(window, self.color, (self.x, self.y), self.radius - int(.4 * self.radius))
@staticmethod
def path(x, y, p, a, t):
vx, vy = p * math.cos(a), p * math.sin(a) #Velocities
dx, dy = vx * t, vy * t - 4.9 * t ** 2 #Distances Traveled
print(' x-pos: %spx' % str(round(dx + x)))
print(' y-pos: %spx' % str(round(abs(dy - y))))
return round(dx + x), round(y - dy)
@staticmethod
def quadrant(x,y,xm,ym):
if ym < y and xm > x:
return 1
elif ym < y and xm < x:
return 2
elif ym > y and xm < x:
return 3
elif ym > y and xm > x:
return 4
else:
return False
def draw_window():
window.fill(WINDOW_COLOR)
ball.show(window)
if not shoot:
arrow(window, ALINE_COLOR, ALINE_COLOR, aline[0], aline[1], 5)
arrow(window, LINE_COLOR, LINE_COLOR, line[0], line[1], 5)
stroke_text = 'Strokes: %s' % strokes
stroke_label = strokeFont.render(stroke_text, 1, STROKECOLOR)
if not strokes:
window.blit(stroke_label, (SCREEN_WIDTH - .21 * SCREEN_WIDTH, SCREEN_HEIGHT - .985 * SCREEN_HEIGHT))
else:
window.blit(stroke_label, (SCREEN_WIDTH - (.21+.02*math.floor(math.log10(strokes))) * SCREEN_WIDTH, SCREEN_HEIGHT - .985 * SCREEN_HEIGHT))
power_text = 'Shot Strength: %sN' % power_display
power_label = powerFont.render(power_text, 1, POWERCOLOR)
if not shoot: window.blit(power_label, (cursor_pos[0] + .008 * SCREEN_WIDTH, cursor_pos[1]))
angle_text = 'Angle: %s°' % angle_display
angle_label = angleFont.render(angle_text, 1, ANGLECOLOR)
if not shoot: window.blit(angle_label, (ball.x - .06 * SCREEN_WIDTH, ball.y - .01 * SCREEN_HEIGHT))
if Penalty:
penalty_text = 'Out of Bounds! +1 Stroke'
penalty_label = penaltyFont.render(penalty_text, 1, PENALTYCOLOR)
penalty_rect = penalty_label.get_rect(center=(SCREEN_WIDTH/2, .225*SCREEN_HEIGHT))
window.blit(penalty_label, penalty_rect)
pg.display.flip()
def angle(cursor_pos):
x, y, xm, ym = ball.x, ball.y, cursor_pos[0], cursor_pos[1]
if x-xm:
angle = math.atan((y - ym) / (x - xm))
elif y > ym:
angle = math.pi/2
else:
angle = 3*math.pi/2
q = ball.quadrant(x,y,xm,ym)
if q: angle = math.pi*math.floor(q/2) - angle
if round(angle*180/math.pi) == 360:
angle = 0
if x > xm and round(angle*180/math.pi) == 0:
angle = math.pi
return angle
def arrow(screen, lcolor, tricolor, start, end, trirad):
pg.draw.line(screen, lcolor, start, end, 2)
rotation = math.degrees(math.atan2(start[1] - end[1], end[0] - start[0])) + 90
pg.draw.polygon(screen, tricolor, ((end[0] + trirad * math.sin(math.radians(rotation)),
end[1] + trirad * math.cos(math.radians(rotation))),
(end[0] + trirad * math.sin(math.radians(rotation - 120)),
end[1] + trirad * math.cos(math.radians(rotation - 120))),
(end[0] + trirad * math.sin(math.radians(rotation + 120)),
end[1] + trirad * math.cos(math.radians(rotation + 120)))))
def distance(x,y):
return math.sqrt(x**2 + y**2)
x, y, time, power, ang, strokes = 0, 0, 0, 0, 0, 0
xb, yb = None, None
shoot, Penalty = False, False
p_ticks = 0
ball = Ball(START_X, START_Y, BALL_RADIUS, BALL_COLOR, BALL_OUTLINE_COLOR)
quit = False
BARRIER = 1
try:
while not quit:
seconds=(pg.time.get_ticks()-p_ticks)/1000
if seconds > 1.2: Penalty = False
cursor_pos = pg.mouse.get_pos()
line = [(ball.x, ball.y), cursor_pos]
line_ball_x, line_ball_y = cursor_pos[0] - ball.x, cursor_pos[1] - ball.y
aline = [(ball.x, ball.y), (ball.x + .015 * SCREEN_WIDTH, ball.y)]
if not shoot:
power_display = round(
distance(line_ball_x, line_ball_y) * POWER_MULTIPLIER / 10)
angle_display = round(angle(cursor_pos) * 180 / math.pi)
if shoot:
if ball.y < SCREEN_HEIGHT:
if BARRIER < ball.x < SCREEN_WIDTH:
time += .3 * SPEED_MULTIPLIER
print('n time: %ss' % round(time, 2))
po = ball.path(x, y, power, ang, time)
ball.x, ball.y = po[0], po[1]
else:
print('Out of Bounds!')
Penalty = True
p_ticks = pg.time.get_ticks()
strokes += 1
shoot = False
if BARRIER < xb < SCREEN_WIDTH:
ball.x = xb
else:
ball.x = START_X
ball.y = yb
else:
shoot = False
ball.y = START_Y
for event in pg.event.get():
if event.type == pg.QUIT:
quit = True
if event.type == pg.KEYDOWN:
if event.key == pg.K_ESCAPE:
quit = True
if event.type == pg.MOUSEBUTTONDOWN:
if not shoot:
shoot = True
x, y = ball.x, ball.y
xb, yb = ball.x, ball.y
time, power = 0, (
distance(line_ball_x, line_ball_y)) * POWER_MULTIPLIER / 10
print('nnBall Hit!')
print('npower: %sN' % round(power, 2))
ang = angle(cursor_pos)
print('angle: %s°' % round(ang * 180 / math.pi, 2))
print('cos(a): %s' % round(math.cos(ang), 2)), print('sin(a): %s' % round(math.sin(ang), 2))
strokes += 1
draw_window()
print("nShutting down...")
pg.quit()
except Exception as error:
print(f'A fatal error (error) has occurred. The program is shutting down.')
pg.quit()
Feedback of any kind is very welcome!
python pygame physics
python pygame physics
edited 3 hours ago
Alex
653313
653313
asked 4 hours ago
alec935alec935
1755
1755
add a comment |
add a comment |
2 Answers
2
active
oldest
votes
$begingroup$
Overall it isn't bad.
Direct imports for common symbols
Based on your discretion, certain often-used and unambiguous symbols can be imported without their module namespace, i.e.
from pg.font import SysFont
# ...
strokeFont = SysFont("monospace", 50)
snake_case
i.e. stroke_font
for variables and function names. Also, Penalty
should be lower-case because it isn't a class.
debug printing
This kind of thing:
print(' x-pos: %spx' % str(round(dx + x)))
can be improved in a few ways. Firstly, it looks like a debugging output and not actual game content, so typically you won't want to print this at all. That doesn't mean that you have to delete it, though - you can use actual Python logging at level debug to be able to select at the top level whether these statements are printed.
Also: do you really need round? Could you instead go
print(f' x-pos: dx + x:.0fpx')
f-strings
As in the previous example, you should consider using the new syntactical sugar of f-strings instead of the %
operator.
Global clutter
It's tempting in Python to add a bunch of stuff (x, y, time, power, etc.) to the global scope. Don't give in! Put these into a game state object. Break up your global code into multiple subroutines, potentially in methods of the game state object.
Shadowing
Don't call something time
. time
is already a thing in Python.
Math
I kind of had to jump through some hoops to take advantage of atan2
. I don't recommend doing this, but here's a one-liner alternative to your quadrant
function:
return int(4 + 2/pi*atan2(y - ym, xm - x)) % 4 + 1
$endgroup$
add a comment |
$begingroup$
Some of this is nit-pickery, some is more fundamental:
Import Order
PEP-8 suggests an ordering to imports. No reason not to use it:
Imports should be grouped in the following order:
Standard library imports.
Related third party imports.
Local application/library specific imports.
You should put a blank line between each group of imports.
Code Organization: Constants
You have a bunch of "constants" defined. They're all-caps, which is good. They're declared together and at the top of the file, which is good. But they really shouldn't be global constants.
For example, you have a Ball
class. Yet there are global constants named BALL_COLOR
and BALL_OUTLINE_COLOR
and BALL_RADIUS
. Why is that? If they're related to your class, make them class constants.
class Ball:
BODY_COLOR = (255, 255, 255)
OUTLINE_COLOR = (255, 0, 0)
RADIUS = 10
Code Organization: Types
In the same vein, you make a lot of use of tuples. But you just create them in-line and rely on convention to access them. Why not go ahead and use a collections.namedtuple
or even two?
import collections
Size = collections.namedtuple('Size', 'width height')
Position = collections.namedtuple('Position', 'x y')
WINDOW_SIZE = Size(width=1500, height=800)
START_POS = Position(x=0.5 * WINDOW_SIZE.width, y=0.99 * WINDOW_SIZE.height)
Code Organization: Functions
You have a lot of stuff at module scope. Sooner or later you'll want to either write a unit test, or run the debugger, or load your code into the command-line Python REPL. All of this is made easier if you move the module-scope statements into a main
function, or some other-named function.
def main():
pg.init()
pg.display.set_caption('Golf')
... etc ...
You have a set of font/color variables that you create at module scope. There aren't currently enough drawing functions to create a Window
class or anything, but you might consider putting them into a Config
class. (And using snake_case
names.)
Also, you have a lot of pygame
boilerplate mixed in with your game logic. I'd suggest separating the boilerplate into separate functions, something like:
while still_playing:
handle_events()
update()
render() # You call this "draw_window()" which is fine.
Most of your logic, of course, will be in update()
. In fact, since it mostly has to do with updating the position of the Ball
object, it should mostly be in a call to ball.update_position(delay)
(or some such name).
You make use of a pair of temporaries x
and y
, but it seems like you could replace those with an old-position attribute on the Ball
, or a second Ball object, or something.
$endgroup$
add a comment |
Your Answer
StackExchange.ifUsing("editor", function ()
return StackExchange.using("mathjaxEditing", function ()
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
);
);
, "mathjax-editing");
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "196"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f216199%2fgolf-game-boilerplate%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
$begingroup$
Overall it isn't bad.
Direct imports for common symbols
Based on your discretion, certain often-used and unambiguous symbols can be imported without their module namespace, i.e.
from pg.font import SysFont
# ...
strokeFont = SysFont("monospace", 50)
snake_case
i.e. stroke_font
for variables and function names. Also, Penalty
should be lower-case because it isn't a class.
debug printing
This kind of thing:
print(' x-pos: %spx' % str(round(dx + x)))
can be improved in a few ways. Firstly, it looks like a debugging output and not actual game content, so typically you won't want to print this at all. That doesn't mean that you have to delete it, though - you can use actual Python logging at level debug to be able to select at the top level whether these statements are printed.
Also: do you really need round? Could you instead go
print(f' x-pos: dx + x:.0fpx')
f-strings
As in the previous example, you should consider using the new syntactical sugar of f-strings instead of the %
operator.
Global clutter
It's tempting in Python to add a bunch of stuff (x, y, time, power, etc.) to the global scope. Don't give in! Put these into a game state object. Break up your global code into multiple subroutines, potentially in methods of the game state object.
Shadowing
Don't call something time
. time
is already a thing in Python.
Math
I kind of had to jump through some hoops to take advantage of atan2
. I don't recommend doing this, but here's a one-liner alternative to your quadrant
function:
return int(4 + 2/pi*atan2(y - ym, xm - x)) % 4 + 1
$endgroup$
add a comment |
$begingroup$
Overall it isn't bad.
Direct imports for common symbols
Based on your discretion, certain often-used and unambiguous symbols can be imported without their module namespace, i.e.
from pg.font import SysFont
# ...
strokeFont = SysFont("monospace", 50)
snake_case
i.e. stroke_font
for variables and function names. Also, Penalty
should be lower-case because it isn't a class.
debug printing
This kind of thing:
print(' x-pos: %spx' % str(round(dx + x)))
can be improved in a few ways. Firstly, it looks like a debugging output and not actual game content, so typically you won't want to print this at all. That doesn't mean that you have to delete it, though - you can use actual Python logging at level debug to be able to select at the top level whether these statements are printed.
Also: do you really need round? Could you instead go
print(f' x-pos: dx + x:.0fpx')
f-strings
As in the previous example, you should consider using the new syntactical sugar of f-strings instead of the %
operator.
Global clutter
It's tempting in Python to add a bunch of stuff (x, y, time, power, etc.) to the global scope. Don't give in! Put these into a game state object. Break up your global code into multiple subroutines, potentially in methods of the game state object.
Shadowing
Don't call something time
. time
is already a thing in Python.
Math
I kind of had to jump through some hoops to take advantage of atan2
. I don't recommend doing this, but here's a one-liner alternative to your quadrant
function:
return int(4 + 2/pi*atan2(y - ym, xm - x)) % 4 + 1
$endgroup$
add a comment |
$begingroup$
Overall it isn't bad.
Direct imports for common symbols
Based on your discretion, certain often-used and unambiguous symbols can be imported without their module namespace, i.e.
from pg.font import SysFont
# ...
strokeFont = SysFont("monospace", 50)
snake_case
i.e. stroke_font
for variables and function names. Also, Penalty
should be lower-case because it isn't a class.
debug printing
This kind of thing:
print(' x-pos: %spx' % str(round(dx + x)))
can be improved in a few ways. Firstly, it looks like a debugging output and not actual game content, so typically you won't want to print this at all. That doesn't mean that you have to delete it, though - you can use actual Python logging at level debug to be able to select at the top level whether these statements are printed.
Also: do you really need round? Could you instead go
print(f' x-pos: dx + x:.0fpx')
f-strings
As in the previous example, you should consider using the new syntactical sugar of f-strings instead of the %
operator.
Global clutter
It's tempting in Python to add a bunch of stuff (x, y, time, power, etc.) to the global scope. Don't give in! Put these into a game state object. Break up your global code into multiple subroutines, potentially in methods of the game state object.
Shadowing
Don't call something time
. time
is already a thing in Python.
Math
I kind of had to jump through some hoops to take advantage of atan2
. I don't recommend doing this, but here's a one-liner alternative to your quadrant
function:
return int(4 + 2/pi*atan2(y - ym, xm - x)) % 4 + 1
$endgroup$
Overall it isn't bad.
Direct imports for common symbols
Based on your discretion, certain often-used and unambiguous symbols can be imported without their module namespace, i.e.
from pg.font import SysFont
# ...
strokeFont = SysFont("monospace", 50)
snake_case
i.e. stroke_font
for variables and function names. Also, Penalty
should be lower-case because it isn't a class.
debug printing
This kind of thing:
print(' x-pos: %spx' % str(round(dx + x)))
can be improved in a few ways. Firstly, it looks like a debugging output and not actual game content, so typically you won't want to print this at all. That doesn't mean that you have to delete it, though - you can use actual Python logging at level debug to be able to select at the top level whether these statements are printed.
Also: do you really need round? Could you instead go
print(f' x-pos: dx + x:.0fpx')
f-strings
As in the previous example, you should consider using the new syntactical sugar of f-strings instead of the %
operator.
Global clutter
It's tempting in Python to add a bunch of stuff (x, y, time, power, etc.) to the global scope. Don't give in! Put these into a game state object. Break up your global code into multiple subroutines, potentially in methods of the game state object.
Shadowing
Don't call something time
. time
is already a thing in Python.
Math
I kind of had to jump through some hoops to take advantage of atan2
. I don't recommend doing this, but here's a one-liner alternative to your quadrant
function:
return int(4 + 2/pi*atan2(y - ym, xm - x)) % 4 + 1
edited 1 hour ago
answered 1 hour ago
ReinderienReinderien
4,395822
4,395822
add a comment |
add a comment |
$begingroup$
Some of this is nit-pickery, some is more fundamental:
Import Order
PEP-8 suggests an ordering to imports. No reason not to use it:
Imports should be grouped in the following order:
Standard library imports.
Related third party imports.
Local application/library specific imports.
You should put a blank line between each group of imports.
Code Organization: Constants
You have a bunch of "constants" defined. They're all-caps, which is good. They're declared together and at the top of the file, which is good. But they really shouldn't be global constants.
For example, you have a Ball
class. Yet there are global constants named BALL_COLOR
and BALL_OUTLINE_COLOR
and BALL_RADIUS
. Why is that? If they're related to your class, make them class constants.
class Ball:
BODY_COLOR = (255, 255, 255)
OUTLINE_COLOR = (255, 0, 0)
RADIUS = 10
Code Organization: Types
In the same vein, you make a lot of use of tuples. But you just create them in-line and rely on convention to access them. Why not go ahead and use a collections.namedtuple
or even two?
import collections
Size = collections.namedtuple('Size', 'width height')
Position = collections.namedtuple('Position', 'x y')
WINDOW_SIZE = Size(width=1500, height=800)
START_POS = Position(x=0.5 * WINDOW_SIZE.width, y=0.99 * WINDOW_SIZE.height)
Code Organization: Functions
You have a lot of stuff at module scope. Sooner or later you'll want to either write a unit test, or run the debugger, or load your code into the command-line Python REPL. All of this is made easier if you move the module-scope statements into a main
function, or some other-named function.
def main():
pg.init()
pg.display.set_caption('Golf')
... etc ...
You have a set of font/color variables that you create at module scope. There aren't currently enough drawing functions to create a Window
class or anything, but you might consider putting them into a Config
class. (And using snake_case
names.)
Also, you have a lot of pygame
boilerplate mixed in with your game logic. I'd suggest separating the boilerplate into separate functions, something like:
while still_playing:
handle_events()
update()
render() # You call this "draw_window()" which is fine.
Most of your logic, of course, will be in update()
. In fact, since it mostly has to do with updating the position of the Ball
object, it should mostly be in a call to ball.update_position(delay)
(or some such name).
You make use of a pair of temporaries x
and y
, but it seems like you could replace those with an old-position attribute on the Ball
, or a second Ball object, or something.
$endgroup$
add a comment |
$begingroup$
Some of this is nit-pickery, some is more fundamental:
Import Order
PEP-8 suggests an ordering to imports. No reason not to use it:
Imports should be grouped in the following order:
Standard library imports.
Related third party imports.
Local application/library specific imports.
You should put a blank line between each group of imports.
Code Organization: Constants
You have a bunch of "constants" defined. They're all-caps, which is good. They're declared together and at the top of the file, which is good. But they really shouldn't be global constants.
For example, you have a Ball
class. Yet there are global constants named BALL_COLOR
and BALL_OUTLINE_COLOR
and BALL_RADIUS
. Why is that? If they're related to your class, make them class constants.
class Ball:
BODY_COLOR = (255, 255, 255)
OUTLINE_COLOR = (255, 0, 0)
RADIUS = 10
Code Organization: Types
In the same vein, you make a lot of use of tuples. But you just create them in-line and rely on convention to access them. Why not go ahead and use a collections.namedtuple
or even two?
import collections
Size = collections.namedtuple('Size', 'width height')
Position = collections.namedtuple('Position', 'x y')
WINDOW_SIZE = Size(width=1500, height=800)
START_POS = Position(x=0.5 * WINDOW_SIZE.width, y=0.99 * WINDOW_SIZE.height)
Code Organization: Functions
You have a lot of stuff at module scope. Sooner or later you'll want to either write a unit test, or run the debugger, or load your code into the command-line Python REPL. All of this is made easier if you move the module-scope statements into a main
function, or some other-named function.
def main():
pg.init()
pg.display.set_caption('Golf')
... etc ...
You have a set of font/color variables that you create at module scope. There aren't currently enough drawing functions to create a Window
class or anything, but you might consider putting them into a Config
class. (And using snake_case
names.)
Also, you have a lot of pygame
boilerplate mixed in with your game logic. I'd suggest separating the boilerplate into separate functions, something like:
while still_playing:
handle_events()
update()
render() # You call this "draw_window()" which is fine.
Most of your logic, of course, will be in update()
. In fact, since it mostly has to do with updating the position of the Ball
object, it should mostly be in a call to ball.update_position(delay)
(or some such name).
You make use of a pair of temporaries x
and y
, but it seems like you could replace those with an old-position attribute on the Ball
, or a second Ball object, or something.
$endgroup$
add a comment |
$begingroup$
Some of this is nit-pickery, some is more fundamental:
Import Order
PEP-8 suggests an ordering to imports. No reason not to use it:
Imports should be grouped in the following order:
Standard library imports.
Related third party imports.
Local application/library specific imports.
You should put a blank line between each group of imports.
Code Organization: Constants
You have a bunch of "constants" defined. They're all-caps, which is good. They're declared together and at the top of the file, which is good. But they really shouldn't be global constants.
For example, you have a Ball
class. Yet there are global constants named BALL_COLOR
and BALL_OUTLINE_COLOR
and BALL_RADIUS
. Why is that? If they're related to your class, make them class constants.
class Ball:
BODY_COLOR = (255, 255, 255)
OUTLINE_COLOR = (255, 0, 0)
RADIUS = 10
Code Organization: Types
In the same vein, you make a lot of use of tuples. But you just create them in-line and rely on convention to access them. Why not go ahead and use a collections.namedtuple
or even two?
import collections
Size = collections.namedtuple('Size', 'width height')
Position = collections.namedtuple('Position', 'x y')
WINDOW_SIZE = Size(width=1500, height=800)
START_POS = Position(x=0.5 * WINDOW_SIZE.width, y=0.99 * WINDOW_SIZE.height)
Code Organization: Functions
You have a lot of stuff at module scope. Sooner or later you'll want to either write a unit test, or run the debugger, or load your code into the command-line Python REPL. All of this is made easier if you move the module-scope statements into a main
function, or some other-named function.
def main():
pg.init()
pg.display.set_caption('Golf')
... etc ...
You have a set of font/color variables that you create at module scope. There aren't currently enough drawing functions to create a Window
class or anything, but you might consider putting them into a Config
class. (And using snake_case
names.)
Also, you have a lot of pygame
boilerplate mixed in with your game logic. I'd suggest separating the boilerplate into separate functions, something like:
while still_playing:
handle_events()
update()
render() # You call this "draw_window()" which is fine.
Most of your logic, of course, will be in update()
. In fact, since it mostly has to do with updating the position of the Ball
object, it should mostly be in a call to ball.update_position(delay)
(or some such name).
You make use of a pair of temporaries x
and y
, but it seems like you could replace those with an old-position attribute on the Ball
, or a second Ball object, or something.
$endgroup$
Some of this is nit-pickery, some is more fundamental:
Import Order
PEP-8 suggests an ordering to imports. No reason not to use it:
Imports should be grouped in the following order:
Standard library imports.
Related third party imports.
Local application/library specific imports.
You should put a blank line between each group of imports.
Code Organization: Constants
You have a bunch of "constants" defined. They're all-caps, which is good. They're declared together and at the top of the file, which is good. But they really shouldn't be global constants.
For example, you have a Ball
class. Yet there are global constants named BALL_COLOR
and BALL_OUTLINE_COLOR
and BALL_RADIUS
. Why is that? If they're related to your class, make them class constants.
class Ball:
BODY_COLOR = (255, 255, 255)
OUTLINE_COLOR = (255, 0, 0)
RADIUS = 10
Code Organization: Types
In the same vein, you make a lot of use of tuples. But you just create them in-line and rely on convention to access them. Why not go ahead and use a collections.namedtuple
or even two?
import collections
Size = collections.namedtuple('Size', 'width height')
Position = collections.namedtuple('Position', 'x y')
WINDOW_SIZE = Size(width=1500, height=800)
START_POS = Position(x=0.5 * WINDOW_SIZE.width, y=0.99 * WINDOW_SIZE.height)
Code Organization: Functions
You have a lot of stuff at module scope. Sooner or later you'll want to either write a unit test, or run the debugger, or load your code into the command-line Python REPL. All of this is made easier if you move the module-scope statements into a main
function, or some other-named function.
def main():
pg.init()
pg.display.set_caption('Golf')
... etc ...
You have a set of font/color variables that you create at module scope. There aren't currently enough drawing functions to create a Window
class or anything, but you might consider putting them into a Config
class. (And using snake_case
names.)
Also, you have a lot of pygame
boilerplate mixed in with your game logic. I'd suggest separating the boilerplate into separate functions, something like:
while still_playing:
handle_events()
update()
render() # You call this "draw_window()" which is fine.
Most of your logic, of course, will be in update()
. In fact, since it mostly has to do with updating the position of the Ball
object, it should mostly be in a call to ball.update_position(delay)
(or some such name).
You make use of a pair of temporaries x
and y
, but it seems like you could replace those with an old-position attribute on the Ball
, or a second Ball object, or something.
answered 40 mins ago
Austin HastingsAustin Hastings
7,3821233
7,3821233
add a comment |
add a comment |
Thanks for contributing an answer to Code Review Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
Use MathJax to format equations. MathJax reference.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f216199%2fgolf-game-boilerplate%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown