Skip to content

Instantly share code, notes, and snippets.

@horstjens
Last active September 27, 2018 15:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save horstjens/fed9c3dd30e24028d41c2ef584b1bc58 to your computer and use it in GitHub Desktop.
Save horstjens/fed9c3dd30e24028d41c2ef584b1bc58 to your computer and use it in GitHub Desktop.
pygame vector template game
# -*- coding: utf-8 -*-
"""003_static_blit_pretty_template.py"""
import pygame
import random
def make_text(msg="pygame is cool", fontcolor=(255, 0, 255), fontsize=42, font=None):
"""returns pygame surface with text. You still need to blit the surface."""
myfont = pygame.font.SysFont(font, fontsize)
mytext = myfont.render(msg, True, fontcolor)
mytext = mytext.convert_alpha()
return mytext
def write(background, text, x=50, y=150, color=(0,0,0),
fontsize=None, center=False):
"""write text on pygame surface. """
if fontsize is None:
fontsize = 24
font = pygame.font.SysFont('mono', fontsize, bold=True)
fw, fh = font.size(text)
surface = font.render(text, True, color)
if center: # center text around x,y
background.blit(surface, (x-fw//2, y-fh//2))
else: # topleft corner is x,y
background.blit(surface, (x,y))
class Flytext(pygame.sprite.Sprite):
def __init__(self, x, y, text="hallo", color=(255, 0, 0),
dx=0, dy=-50, duration=2, acceleration_factor = 1.0, delay = 0, fontsize=22):
"""a text flying upward and for a short time and disappearing"""
self._layer = 7 # order of sprite layers (before / behind other sprites)
pygame.sprite.Sprite.__init__(self, self.groups) # THIS LINE IS IMPORTANT !!
self.text = text
self.r, self.g, self.b = color[0], color[1], color[2]
self.dx = dx
self.dy = dy
self.x, self.y = x, y
self.duration = duration # duration of flight in seconds
self.acc = acceleration_factor # if < 1, Text moves slower. if > 1, text moves faster.
self.image = make_text(self.text, (self.r, self.g, self.b), fontsize) # font 22
self.rect = self.image.get_rect()
self.rect.center = (self.x, self.y)
self.time = 0 - delay
def update(self, seconds):
self.time += seconds
if self.time < 0:
self.rect.center = (-100,-100)
else:
self.y += self.dy * seconds
self.x += self.dx * seconds
self.dy *= self.acc # slower and slower
self.dx *= self.acc
self.rect.center = (self.x, self.y)
if self.time > self.duration:
self.kill() # remove Sprite from screen and from groups
class FlyingObject(pygame.sprite.Sprite):
number = 0
def __init__(self, **kwargs):
self._default_parameters(**kwargs) # named parameters
self._overwrite_parameters() # overwrite some parameters
pygame.sprite.Sprite.__init__(self, self.groups)
self.number = FlyingObject.number
FlyingObject.number += 1
self.create_image()
self.image0 = self.image.copy()
self.rect = self.image.get_rect()
self.width = self.rect.width
self.height = self.rect.height
self.rotate_image(self.angle)
if self.has_hitpointbar:
Hitpointbar(mothership=self)
def _overwrite_parameters(self):
""" use this method to overwrite attributes before create_image() is called"""
pass
def _default_parameters(self, **kwargs):
"""get unlimited named arguments and turn them into attributes
default values for missing keywords"""
for key, arg in kwargs.items():
setattr(self, key, arg) # make an attribute (self. ) out of an parameter
if "layer" not in kwargs:
self._layer = 4
else:
self._layer = self.layer
if "static" not in kwargs:
self.static = False
if "control_method" not in kwargs:
self.control_method = None
if "move" in kwargs:
self.speed = self.move.length()
self.angle = pygame.math.Vector2(1,0).angle_to(self.move)
else:
if "speed" not in kwargs:
self.speed = 50
if "angle" not in kwargs:
self.angle = 0 # facing right?
# create move from speed and angle
self.move = pygame.math.Vector2(self.speed,0)
self.move.rotate(self.angle)
if "pos" not in kwargs:
if "x" in kwargs and "y" in kwargs:
self.pos = pygame.math.Vector2(self.x,-self.y)
else:
self.pos = pygame.math.Vector2(100,-200)
if "color" not in kwargs:
#self.color = None
self.color = (random.randint(0,255), random.randint(0,255), random.randint(0,255))
if "hitpoints" not in kwargs:
self.hitpoints = 100
self.hitpointsfull = self.hitpoints # makes a copy
if "mass" not in kwargs:
self.mass = 10
if "damage" not in kwargs:
self.damage = 10
if "bounce_on_edge" not in kwargs:
self.bounce_on_edge = False
if "kill_on_edge" not in kwargs:
self.kill_on_edge = False
if "max_age" not in kwargs:
self.max_age = None
if "age" not in kwargs:
self.age = 0 # age in seconds
if "radius" not in kwargs:
self.radius = 10
if "has_hitpointbar" not in kwargs:
self.has_hitpointbar = False
if "pen_size" not in kwargs:
self.pen_size = 1
if "start_color" not in kwargs:
self.start_color = None
if "end_color" not in kwargs:
self.end_color = None
if "color_cycle_time" not in kwargs:
self.color_cycle_time = 1.0 # seconds
#repaint
if self.start_color is not None and self.end_color is not None:
#self.create_image()
self.red = self.start_color[0]
self.green = self.start_color[1]
self.blue = self.start_color[2]
self.delta_red = self.end_color[0] - self.red
self.delta_green = self.end_color[1] - self.green
self.delta_blue = self.end_color[2] - self.blue
self.red_sign = 1
self.green_sign = 1
self.blue_sign = 1
def create_image(self):
self.image = pygame.Surface((2 * self.radius, 2* self.radius))
pygame.draw.polygon(self.image, self.color, [ (0,0), (2*self.radius,self.radius),(0, 2*self.radius),])
self.image.set_colorkey((0,0,0))
self.image = self.image.convert_alpha() # faster blitting with transparent color
def rotate_image(self, angle):
"""rotates the original image (image0) for angle degrees"""
oldcenter = self.rect.center
self.image = pygame.transform.rotate(self.image0, angle)
self.rect = self.image.get_rect()
self.rect.center = oldcenter
def control(self):
keyboard = False
if self.control_method == "cursor":
keyboard = True
upkey = pygame.K_UP
downkey = pygame.K_DOWN
leftkey = pygame.K_LEFT
rightkey = pygame.K_RIGHT
if self.control_method == "wasd":
keyboard = True
upkey = pygame.K_w
downkey = pygame.K_s
leftkey = pygame.K_a
rightkey = pygame.K_d
if self.control_method == "ijkl":
keyboard = True
upkey = pygame.K_i
downkey = pygame.K_k
leftkey = pygame.K_j
rightkey = pygame.K_l
if keyboard:
# --- keyboard control ----
pressedkeys = pygame.key.get_pressed() # keys that you can press all the time
if pressedkeys[leftkey]:
#self.dx -=1
self.angle += 1
if pressedkeys[rightkey]:
#self.dx +=1
self.angle -= 1
if pressedkeys[upkey]:
#self.dy -= 1
self.speed += 1
if pressedkeys[downkey]:
#self.dy += 1
self.speed -= 1
#print("speed", self.speed)
self.move = pygame.math.Vector2(self.speed, 0)
self.move = self.move.rotate(self.angle)
def reflect(self, wallvector):
"""changing angle of Sprite by reflecting it's move vector with a given wallvector"""
self.move.reflect_ip(wallvector)
self.angle = pygame.math.Vector2(1,0).angle_to(self.move)
for _ in range(5):
Fragment(pos=pygame.math.Vector2(self.pos[0], self.pos[1]))
def wallcheck(self):
"""check if Sprite is touching screen edge"""
if self.kill_on_edge:
if (self.pos[0] - self.width // 2 < 0 or
self.pos[1] + self.height // 2 > 0 or
self.pos[0] + self.width //2 > PygView.width or
self.pos[1] - self.height // 2 < -PygView.height):
#print(self.width, self.height, "i kill on edge")
self.kill()
elif self.bounce_on_edge:
h = pygame.math.Vector2(1,0) # ... horizontal vector
v = pygame.math.Vector2(0,1) # ... vertical vector
if self.pos[0] - self.width // 2 < 0:
self.pos[0] = self.width // 2 + 0
self.reflect(h)
elif self.pos[0] + self.width // 2 > PygView.width:
self.pos[0] = PygView.width - self.width // 2 - 0
self.reflect(h)
if self.pos[1] + self.height // 2 > 0:
self.pos[1] = - self.height // 2 - 0
self.reflect(v)
elif self.pos[1] - self.height // 2 < -PygView.height:
self.pos[1] = - PygView.height + self.height //2 + 0
self.reflect(v)
def update(self, seconds):
self.age += seconds
# agekill?
if self.max_age is not None:
if self.age > self.max_age:
self.kill()
# repaint
#repaint
if self.start_color is not None and self.end_color is not None:
self.red += int(round(self.delta_red * seconds / self.color_cycle_time * self.red_sign, 0))
if self.red > 255:
self.red = 255
self.red_sign *= -1
elif self.red < 0:
self.red = 0
self.red_sign *= -1
self.green += int(round(self.delta_green * seconds / self.color_cycle_time * self.green_sign, 0))
if self.green > 255:
self.green = 255
self.green_sign *= -1
elif self.green < 0:
self.green = 0
self.green_sign *= -1
self.blue += int(round(self.delta_blue * seconds / self.color_cycle_time * self.blue_sign, 0))
if self.blue > 255:
self.blue = 255
self.blue_sign *= -1
elif self.blue < 0:
self.blue = 0
self.blue_sign *= -1
print(self.red, self.green, self.blue)
self.create_image()
self.image0 = self.image.copy()
self.rotate_image(self.angle)
# calculate movement
self.oldangle = self.angle
self.control()
self.pos += self.move * seconds
self.wallcheck()
if self.angle != self.oldangle:
self.rotate_image(self.angle) # only necessary if angle is changed
self.rect.center = (self.pos[0], -self.pos[1])
# ---- print!!! ---
#print("pos, move, angle, speed:", self.pos, self.move, self.angle, self.speed)
class Hitpointbar(FlyingObject):
def create_image(self):
good = (0,255,0) # green
medium = (255,255,0) # yellow
bad = (255,128,0) # orange
critical = (255,0,0) # red
wfull = self.mothership.rect.width
#hp = self.mothership.hitpoints
#hpfull = self.mothership.hitpointsfull
p = self.mothership.hitpoints / self.mothership.hitpointsfull
w = int(wfull * abs(p))
# define hitpointbarcolor
hp = self.mothership.hitpoints
if hp > 80:
color = good
elif hp > 50:
color = medium
elif hp > 25:
color = bad
else:
color = critical
self.image = pygame.Surface((wfull,5))
pygame.draw.rect(self.image, color, (0,0,w,5))
pygame.draw.rect(self.image, (200,200,200), (0,0,wfull,5),1)
self.image.set_colorkey((0,0,0))
self.image = self.image.convert_alpha() # faster blitting with transparent color
def update(self, seconds):
self.rect.x = self.mothership.rect.x
self.rect.y = self.mothership.rect.y - 10
self.create_image()
class Fragment(FlyingObject):
def _overwrite_parameters(self):
self.radius = random.randint(1, 4)
self.speed = random.randint(50, 300)
self.max_age = random.random() * 1.5
self.angle = random.random() * 360
self.color = ( random.randint(128,255),
random.randint(0, 90),
0 )
def create_image(self):
self.image= pygame.Surface((self.radius*2, self.radius*2))
pygame.draw.circle(self.image, self.color, (self.radius, self.radius),
self.radius)
self.image.set_colorkey((0,0,0))
class Rocket(FlyingObject):
def _overwrite_parameters(self):
self.radius = 10
self.kill_on_edge = True
self.color = self.mothership.color
self.pos = pygame.math.Vector2(self.mothership.pos[0],
self.mothership.pos[1])
self.angle += self.mothership.angle
self.speed = self.mothership.speed + self.speed
def create_image(self):
self.image = pygame.Surface((20,10))
pygame.draw.polygon(self.image, self.color, [ (0,0), (15,0),
(20,5), ( 15,10), ( 0,10)])
pygame.draw.polygon(self.image, (150,150,150), [ (5,2), (10,2), (15,5), (10, 8), (5,8)])
self.image.set_colorkey((0,0,0))
self.image = self.image.convert_alpha() # faster blitting with transparent color
self.image0 = self.image.copy()
class Spaceship(FlyingObject):
def create_image(self):
slim = 20
self.image = pygame.Surface((2*self.radius,2*self.radius))
d = self.radius * 2
if self.start_color is not None and self.end_color is not None:
self.color = (self.red, self.green, self.blue)
pygame.draw.polygon(self.image, self.color,
((0,0), (d, d*0.5), (0, d), (d*0.5, d*0.5), (0, 0)), self.pen_size)
#pygame.draw.circle(self.surface, color, (radius, radius), radius) # draw blue filled circle on ball surface
#self.surface = self.surface.convert() # for faster blitting.
# to avoid the black background, make black the transparent color:
self.image.set_colorkey((0,0,0))
self.image = self.image.convert_alpha() # faster blitting with transparent color
class Turret(FlyingObject):
def create_image(self):
self.image = pygame.Surface((120,120))
#self.radius = 40
pygame.draw.circle(self.image,(100,100,100), (50,50), (self.radius))
pygame.draw.rect(self.image,(150,150,150), (50,50 - 10, 120, 20))
self.image.set_colorkey((0,0,0))
self.image = self.image.convert_alpha()
class PygView():
width = 0
height = 0
def __init__(self, width=640, height=400, fps=30):
"""Initialize pygame, window, background, font,...
default arguments """
pygame.init()
pygame.display.set_caption("Press ESC to quit")
self.width = width
self.height = height
PygView.width = width
PygView.height = height
self.screen = pygame.display.set_mode((self.width, self.height), pygame.DOUBLEBUF)
self.background = pygame.Surface(self.screen.get_size()).convert()
self.background.fill((110,110,140)) # fill background white
self.clock = pygame.time.Clock()
self.fps = fps
self.playtime = 0.0
self.font = pygame.font.SysFont('mono', 24, bold=True)
self.preparesprites()
def preparesprites(self):
self.allgroup = pygame.sprite.LayeredUpdates()
self.shipgroup = pygame.sprite.Group()
self.rocketgroup = pygame.sprite.Group()
self.bargroup = pygame.sprite.Group()
#...allocate sprites to groups
Flytext.groups = self.allgroup
Hitpointbar.groups = self.allgroup, self.bargroup
FlyingObject.groups = self.allgroup
Spaceship.groups = self.allgroup, self.shipgroup
Rocket.groups = self.allgroup, self.rocketgroup
# ... create the sprites ...
self.player1 = Spaceship(color_cycle_time = 0.2, start_color = (0, 255, 255), end_color = (255, 0, 0), pen_size = 3, radius = 50, x=100, y=200, color=(0,200,0), bounce_on_edge=True, control_method="wasd", has_hitpointbar=True)
self.player2 = Spaceship(start_color = (255, 255, 0), end_color = (0, 0, 255), pen_size = 3, radius = 50, x=400, y=200, color=(100,200,100), bounce_on_edge=True, control_method = "ijkl", has_hitpointbar=True)
#self.test1 = FlyingObject(bounce_on_edge=True, control_method = "cursor")
self.turret1 = Turret(x=300, y= 300, speed = 0)
def paint(self):
"""painting on the surface"""
# pygame.draw.line(Surface, color, start, end, width)
pygame.draw.line(self.background, (0,255,0), (10,10), (50,100))
pygame.draw.line(self.background, (0,0,0), (0,0), (1000,500))
pygame.draw.line(self.background, (255,255,0), (1000,0), (0,500))
# ...
pygame.draw.ellipse(self.background, (200,110,100), (0,PygView.height - 60,100,60))
pygame.draw.ellipse(self.background, (0,200,200), (PygView.width - 45, 0,20,90))
for x in range(100,19,-20):
pygame.draw.circle(self.background, (0,0,random.randint(0,255)), (PygView.width // 2, PygView.height // 2), x)
#pygame.draw.circle(self.background, (0,0,0), (200,80), (36))
#pygame.draw.rect(self.background, (0,255,100), (PygView.width / 2 - 50 , PygView.height / 2 - 25, 100, 50))
pygame.draw.line(self.background, (255,40,255), (PygView.width, 0), (0, PygView.height))
pygame.draw.line(self.background, (255,90,205), (0,0), (PygView.width,PygView.height))
# pygame.draw.rect(Surface, color, Rect, width=0): return Rect
pygame.draw.rect(self.background, (0,255,0), (50,50,100,25)) # rect: (x1, y1, width, height)
pygame.draw.rect(self.background, (0,0,255), (70,70,80,30))
# pygame.draw.circle(Surface, color, pos, radius, width=0): return Rect
pygame.draw.circle(self.background, (0,200,0), (200,50), 35)
# pygame.draw.polygon(Surface, color, pointlist, width=0): return Rect
pygame.draw.polygon(self.background, (0,180,0), ((250,100),(300,0),(350,50)))
# pygame.draw.arc(Surface, color, Rect, start_angle, stop_angle, width=1): return Rect
pygame.draw.arc(self.background, (0,150,0),(400,10,150,100), 0, 3.14) # radiant instead of grad
#...
#pygame.draw.polygon(self.background, (0,255,188), ((370,300), (320,200), (185,200), (190,200), (170,300)))
pygame.draw.polygon(self.background, (255,165,0), ((PygView.width / 2, 0), (PygView.width / 2 - 90, PygView.height - 80), (PygView.width / 2, PygView.height), (PygView.width / 2 + 90, PygView.height - 80), (PygView.width / 2, 0)))
def kill_ship_and_hitpointbar(self, ship):
""" kills the Hitpointbar of a ship and the Ship itself.
necessary: Hitpointbar has attribute self.mothership (the Ship),
and Hitpointbar is member of PygView.bargroup"""
for b in self.bargroup:
if b.mothership == ship:
b.kill()
break
ship.kill()
def run(self):
self.paint()
running = True
while running:
milliseconds = self.clock.tick(self.fps)
seconds = milliseconds / 1000.0
self.playtime += seconds
text = "FPS: {:6.3} PLAYTIME: {:6.3} SECONDS".format(self.clock.get_fps(), self.playtime)
write(self.screen, text, x=PygView.width //2, y = 15, center=True)
h = pygame.math.Vector2(1,0) # .... horizontal
v = pygame.math.Vector2(0,1) # .....vertical
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
# keys that you press once and release
if event.key == pygame.K_ESCAPE:
running = False
if event.key == pygame.K_TAB:
if self.player1 in self.shipgroup:
Rocket(mothership=self.player1, speed=100)
if event.key == pygame.K_SPACE:
if self.player2 in self.shipgroup:
Rocket(mothership=self.player2, speed=100)
#if event.key == pygame.K_RETURN:
# Rocket(self.player3)
# ---- mouse events ----
left,middle,right = pygame.mouse.get_pressed() # Mouse buttons
if left: # left button was pressed?
for f in range(random.randint(10,20)):
Fragment(x=pygame.mouse.get_pos()[0],
y=pygame.mouse.get_pos()[1])
if right: # right mouse button pressed
Flytext(pygame.mouse.get_pos()[0], pygame.mouse.get_pos()[1],
"hallo")
# ---- collision detection -----
# ---- spaciship and rockets ----
for ship in self.shipgroup:
crashgroup = pygame.sprite.spritecollide(ship, self.rocketgroup,
False, pygame.sprite.collide_circle)
for rocket in crashgroup:
if rocket.mothership == ship:
continue
ship.hitpoints -= rocket.damage
if ship.hitpoints < 1:
self.kill_ship_and_hitpointbar(ship)
for f in range(10):
Fragment(x=rocket.pos.x, y=-rocket.pos.y)
rocket.kill()
pygame.display.flip()
self.screen.blit(self.background, (0, 0))
self.allgroup.update(seconds)
self.allgroup.draw(self.screen)
pygame.display.set_caption("player1: x:{:.2f} y:{:.2f} angle:{:.2f} speed:{:.2f}, move: {}, winkel: {:.2f} ".format(
self.player1.pos.x, self.player1.pos.y, self.player1.angle, self.player1.speed, self.player1.move, self.player1.move.angle_to(pygame.math.Vector2(1,0))))
# --- paint tails ----
#for ship in self.shipgroup:
# oldpos = (ship.pos.x, ship.pos.y)
# for n, pos in enumerate(ship.tail):
# pygame.draw.line(self.screen, (255-n,255-n,255-n), (oldpos[0], -oldpos[1]), (pos[0], -pos[1]))
# oldpos = pos
pygame.quit()
if __name__ == '__main__':
PygView(1000, 500).run() # call with width of window and fps
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment