Posts: 5,151
Threads: 396
Joined: Sep 2016
Color Surface
An example of 2 different methods to draw a new color over a surface within pygame
#no other libs requires other than pygame
def colorize(image, newColor):
"""
Create a "colorized" copy of a surface (replaces RGB values with the given color, preserving the per-pixel alphas of
original).
:param image: Surface to create a colorized copy of
:param newColor: RGB color to use (original alpha values are preserved)
:return: New colorized Surface instance
"""
image = image.copy()
# zero out RGB values
image.fill((0, 0, 0, 255), None, pg.BLEND_RGBA_MULT)
# add in new RGB values
image.fill(newColor[0:3] + (0,), None, pg.BLEND_RGBA_ADD)
return image
#requires numpy module to be installed
def color_surface(surface, red, green, blue):
'''requires numpy module on users end'''
arr = pg.surfarray.pixels3d(surface)
arr[:,:,0] = red
arr[:,:,1] = green
arr[:,:,2] = blue
Recommended Tutorials:
Posts: 5,151
Threads: 396
Joined: Sep 2016
Resizable Window
Go to the corner of the window and make it smaller
import pygame as pg
import random
class Ball:
def __init__(self, screen_rect):
self.screen_rect = screen_rect
self.image = pg.Surface([50,50]).convert()
self.image.fill((255,0,0))
self.rect = self.image.get_rect()
self.speed_init = 10
self.speed = self.speed_init
self.set_ball()
def set_ball(self):
self.vel = [random.choice([-1,1]), 0]
self.rect.center = self.screen_rect.center
self.true_pos = list(self.rect.center)
self.speed = self.speed_init
def move(self):
if self.rect.left <= 0:
self.vel[0] *= -1
elif self.rect.right >= self.screen_rect.right:
self.vel[0] *= -1
self.true_pos[0] += self.vel[0] * self.speed
#self.true_pos[1] += self.vel[1] * self.speed
self.rect.center = self.true_pos
def update(self, screen_rect):
self.screen_rect = screen_rect
self.move()
def draw(self, surf):
surf.blit(self.image, self.rect)
class Control:
def __init__(self):
self.resolutions = [(300,200), (600,400),(800, 600), (928, 696)]
self.render_size = self.resolutions[-1] #largest
self.screen = pg.display.set_mode(self.resolutions[-1], pg.RESIZABLE)
self.screen_rect = self.screen.get_rect()
self.render_surf = pg.Surface(self.render_size).convert()
#pg.event.clear(pg.VIDEORESIZE)
self.clock = pg.time.Clock()
self.done = False
self.fps = 60
self.ball = Ball(self.screen_rect)
def event_loop(self):
for event in pg.event.get():
if event.type == pg.QUIT:
self.done = True
elif event.type == pg.VIDEORESIZE:
self.on_resize(event.size)
#pg.event.clear(pg.VIDEORESIZE)
def on_resize(self, size):
if size == self.screen_rect.size:
return
res_index = self.resolutions.index(self.screen_rect.size)
adjust = 1 if size > self.screen_rect.size else -1
if 0 <= res_index+adjust < len(self.resolutions):
new_size = self.resolutions[res_index+adjust]
else:
new_size = self.screen_rect.size
self.screen = pg.display.set_mode(new_size, pg.RESIZABLE)
self.screen_rect.size = new_size
self.set_scale()
def set_scale(self):
w_ratio = self.render_size[0]/float(self.screen_rect.w)
h_ratio = self.render_size[1]/float(self.screen_rect.h)
self.scale = (w_ratio, h_ratio)
def update(self):
self.ball.update(self.render_surf.get_rect()) #give obj updated screen size
def render(self):
if self.render_size != self.screen_rect.size:
scale_args = (self.render_surf, self.screen_rect.size, self.screen)
pg.transform.smoothscale(*scale_args)
else:
self.screen.blit(self.render_surf, (0, 0))
self.render_surf.fill((255,255,255))
self.ball.draw(self.render_surf)
def game_loop(self):
while not self.done:
self.event_loop()
self.update()
self.render()
pg.display.update()
self.clock.tick(self.fps)
pg.init()
app = Control()
app.game_loop()
pg.quit()
Recommended Tutorials:
Posts: 5,151
Threads: 396
Joined: Sep 2016
Rotate Around a Point
LEFT or RIGHT / A or D keys to move around the point.
import pygame as pg
import math
class Rotator:
def __init__(self, screen_rect):
self.radar = screen_rect.center
self.radar_len = 100
self.angle = 0
self.image = pg.Surface([25,25]).convert()
self.get_pos()
self.rect = self.image.get_rect(center=(self.x, self.y))
self.image.fill((255,0,0))
self.speed = 200
def render(self, screen):
screen.blit(self.image, self.rect)
pg.draw.line(screen, (255,255,255), self.radar, (self.x,self.y), 1)
def update(self, seconds):
keys = pg.key.get_pressed()
self.get_pos()
self.rect.center = (self.x, self.y)
if keys[pg.K_RIGHT] or keys[pg.K_d]:
self.angle -= self.speed * seconds
elif keys[pg.K_LEFT] or keys[pg.K_a]:
self.angle += self.speed * seconds
def get_pos(self):
self.x = self.radar[0] + math.cos(math.radians(self.angle)) * self.radar_len
self.y = self.radar[1] + math.sin(math.radians(self.angle)) * self.radar_len
if __name__ == '__main__':
running = True
pg.init()
screen = pg.display.set_mode((600,400))
screen_rect = screen.get_rect()
rotator = Rotator(screen_rect)
clock = pg.time.Clock()
seconds = 0
while running:
screen.fill((0,0,0))
for event in pg.event.get():
if event.type == pg.QUIT:
running = False
elif event.type == pg.MOUSEBUTTONDOWN:
rotator.radar = pg.mouse.get_pos()
rotator.update(seconds)
rotator.render(screen)
pg.display.set_caption('x,y:{} {}'.format(rotator.x, rotator.y))
pg.display.update()
milli = clock.tick(60)
seconds = milli / 1000.0
Recommended Tutorials:
Posts: 5,151
Threads: 396
Joined: Sep 2016
Draw Shape to Surface
Here is an example of a pygame rect drawn to a custom surface. You can only see the half circle because its positioned at the bottom of that surface. As for the surface not showing, it has disappeared in the background because its color is set as the color key to hide. This can be useful to create odd shapes. An example would be trying to follow a clickable or mouse highlight pattern from a background for example.
import pygame
pygame.init()
screen = pygame.display.set_mode((800,600))
screen_rect = screen.get_rect()
circle = pygame.Surface([500,300]).convert()
circle.fill((255,0,255)) #make abnormal bg color
circle.set_colorkey((255,0,255)) #hide bg
pygame.draw.circle(circle, (200,200,0), screen_rect.center, 25, 0)
circle_rect = circle.get_rect()
running = True
while running:
screen.fill((255,255,255))
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
keys = pygame.key.get_pressed()
if keys[pygame.K_RIGHT]:
circle_rect.x += 1
elif keys[pygame.K_LEFT]:
circle_rect.x -= 1
screen.blit(circle, circle_rect)
pygame.display.update()
Recommended Tutorials:
Posts: 5,151
Threads: 396
Joined: Sep 2016
Basic button class example
import pygame as pg
pg.init()
def flip_color():
global bg_white
bg_white = not bg_white
class Button:
def __init__(self, rect, command):
self.color = (255,0,0)
self.rect = pg.Rect(rect)
self.image = pg.Surface(self.rect.size)
self.image.fill(self.color)
self.command = command
def render(self, screen):
screen.blit(self.image, self.rect)
def get_event(self, event):
if event.type == pg.MOUSEBUTTONDOWN and event.button == 1:
if self.rect.collidepoint(pg.mouse.get_pos()):
self.command()
screen = pg.display.set_mode((800,600))
screen_rect = screen.get_rect()
running = True
bg_white = False
btn = Button((10,10,105,25), flip_color)
while running:
for event in pg.event.get():
if event.type == pg.QUIT:
running = False
btn.get_event(event)
if bg_white:
screen.fill((255,255,255))
else:
screen.fill((0,0,0))
btn.render(screen)
pg.display.update()
Recommended Tutorials:
Posts: 5,151
Threads: 396
Joined: Sep 2016
Separate objects apart sequentailly
import pygame as pg
class Field:
width = 100
height = 100
buffer_right = 10
bugger_top = 10
def __init__(self, position):
self.color = (255,0,0)
self.image = pg.Surface([self.width, self.height]).convert()
self.image.fill(self.color)
self.rect = self.image.get_rect(topleft=position)
def draw(self, screen):
screen.blit(self.image, self.rect)
def set_fields():
fields = []
for row in range(4):
for col in range(4):
#number in row * its width + (buffer), number in col * its hieght + (buffer)
field = Field((row*Field.width+(row*Field.buffer_right), col*Field.height+(col*Field.bugger_top)))
fields.append(field)
return fields
pg.init()
screen = pg.display.set_mode((800,600))
screen_rect = screen.get_rect()
done = False
fields = set_fields()
while not done:
screen.fill((0,0,0))
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
for field in fields:
field.draw(screen)
pg.display.update()
Recommended Tutorials:
Posts: 5,151
Threads: 396
Joined: Sep 2016
Feb-01-2020, 07:40 PM
(This post was last modified: Feb-06-2020, 11:56 AM by metulburr.)
Particle Effects
simple particle effects
fire particle effect
import pygame as pg
import traceback
from random import uniform, choice
vec = pg.math.Vector2
vec3 = pg.math.Vector3
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
class Custom_color(pg.Color):
'''
This is an attempt of creating an object that can
be used as a pygame.Color, but also has vector operations.
'''
def __init__(self, color):
self.vec = vec3(color[0:3])
super().__init__(int(color[0]),
int(color[1]),
int(color[2]))
def update(self):
self.r = int(self.vec.x)
self.g = int(self.vec.y)
self.b = int(self.vec.z)
class Game:
def __init__(self):
pg.init()
self.clock = pg.time.Clock()
self.screen = pg.display.set_mode((800, 600))
self.screen_rect = self.screen.get_rect()
self.fps = 60
self.all_sprites = pg.sprite.Group()
try:
p_image_strip = pg.image.load('fire_particles_strip.png'
).convert_alpha()
self.p_images = [p_image_strip.subsurface(((i * 40, 0), (40, 40)))
for i in range(4)]
except:
# if loading the image fails, create some dummy images
self.p_images = []
for i in range(4):
surf = pg.Surface((40, 40), pg.SRCALPHA)
surf.fill(WHITE)
self.p_images.append(surf)
def create_particle(self, pos):
# create a list of colors for a gradient over time
# this looks something like a fire:
colors = [
(255, 255, 255),
(255, 200, 0),
(200, 50, 0),
(60, 60, 80)
]
# instantiate a paticle with a reference to the particle images
# and the list of colors
Particle(self, pos, self.p_images, colors)
def update(self):
# create particles every frame
self.create_particle((self.screen_rect.w / 4,
self.screen_rect.h - 30))
self.create_particle((self.screen_rect.w / 4 * 3,
self.screen_rect.h - 30))
self.all_sprites.update()
def draw(self):
self.screen.fill(BLACK)
for s in self.all_sprites:
s.draw(self.screen)
pg.display.update()
def run(self):
self.running = True
while self.running:
self.clock.tick(self.fps)
caption = 'FPS: {} | number of particles: {}'.format(
int(self.clock.get_fps()),
len(self.all_sprites))
pg.display.set_caption(caption)
for event in pg.event.get():
if event.type == pg.QUIT:
self.running = False
self.update()
self.draw()
pg.quit()
class Particle(pg.sprite.Sprite):
def __init__(self, game, pos, images=None, colors=[]):
super().__init__()
self.game = game
self.game.all_sprites.add(self)
if images:
# if initialized with a list of images, choose one at random
self.original_image = choice(self.game.p_images).copy()
self.image = self.original_image.copy()
self.rect = self.image.get_rect()
else:
# if not initialized with images, draw a circle
self.image = pg.Surface((20, 20))
self.image.set_colorkey((0, 0, 0))
self.rect = self.image.get_rect()
pg.draw.ellipse(self.image, (255, 255, 255), self.rect)
# make a
self.colors = [vec3(x) for x in colors]
self.color = Custom_color(self.colors[0])
self.alpha = 255
if len(self.colors) > 1:
self.prev_color = vec3(self.colors[0])
self.target_color = vec3(self.colors[1])
self.target_index = 1
self.lerp_dist = 0
self.lerp_speed = 0.05
self.pos = vec(pos)
self.rect.topleft = self.pos
# set random velocity vector
self.vel = vec(uniform(-1, 1), uniform(-5, -1))
def update(self):
# add velocity to position
self.pos += self.vel
# update rect
self.rect.topleft = self.pos
# reduce alpha gradually
self.alpha -= 2
if self.alpha < 0:
self.kill()
else:
self.color.a = int(self.alpha)
def draw(self, screen):
self.blend_colors()
self.image = self.original_image.copy()
self.image.fill(self.color, None, pg.BLEND_RGBA_MULT)
screen.blit(self.image, self.pos)
def blend_colors(self):
if len(self.colors) > 1:
if self.lerp_dist < 1:
# linear interpolation between previous and target color
self.color.vec = self.prev_color.lerp(self.target_color,
self.lerp_dist)
self.color.update()
self.lerp_dist += self.lerp_speed
else:
# if lerp distance reached 1, set the next target color
self.target_index += 1
if self.target_index < len(self.colors):
self.prev_color = self.target_color
self.target_color = self.colors[self.target_index]
self.lerp_dist = 0
if __name__ == '__main__':
try:
g = Game()
g.run()
except Exception:
traceback.print_exc()
pg.quit()
Recommended Tutorials:
Posts: 5,151
Threads: 396
Joined: Sep 2016
Feb-02-2020, 06:20 PM
(This post was last modified: Feb-06-2020, 11:57 AM by metulburr.)
RPG map making and loading
Orthogonal map (top-down) loading from spritesheet. This repo shows a small example of using PyTMX to load tmx files generated from Tiled Editor.
Tiled is a map editor to easily place your sprites by hand and allocated collision positions, etc. PyTMX loads these maps. The repo provided includes PyTMX with it and has a predefined tmx map. It shows a small simple example of these libraries combined together to make a map and a character colliding with it.
main.py is file to run in this directory. It is the main game loop and loads the level created and player to move around. As coded: the player is colored red in grass, and blue when a collision is detected when it would be out of bounds.
Recommended Tutorials:
Posts: 5,151
Threads: 396
Joined: Sep 2016
Feb-06-2020, 11:57 AM
(This post was last modified: Feb-06-2020, 11:57 AM by metulburr.)
Basic multiline text
import pygame as pg
import pygame.freetype
def main():
pg.init()
screen = pg.display.set_mode((400, 600))
clock = pg.time.Clock()
# define the font size and spacing
fontsize = 36
margin_x = 10 # pixels from the left
margin_y = 10 # pixels from the top
spacing_y = 4 # pixels between lines
# initialize a font (Arial)
font = pygame.freetype.SysFont('Arial', fontsize)
# a sample string with line breaks
sample_text = ('This is the first line.\n' +
'This is the second line.\n' +
'This is the third line.\n\n' +
'This is the fifth line.')
# create a list from the string and render it to surfaces
rendered_fonts =[]
for i, line in enumerate(sample_text.split('\n')):
txt_surf, txt_rect = font.render(line, pg.Color('black'))
# set the text rect to a position based on the line number and
# spacing parameters
txt_rect.topleft = (margin_x, margin_y + i * (fontsize + spacing_y))
rendered_fonts.append((txt_surf, txt_rect))
# main loop
running = True
while running:
for event in pg.event.get():
if event.type == pg.QUIT:
running = False
# draw the text
screen.fill(pg.Color('white'))
for txt_surf, txt_rect in rendered_fonts:
screen.blit(txt_surf, txt_rect)
clock.tick(60)
pg.display.flip()
pg.quit()
if __name__ == '__main__':
main()
Recommended Tutorials:
Posts: 5,151
Threads: 396
Joined: Sep 2016
Jul-06-2020, 10:46 AM
(This post was last modified: Aug-25-2020, 10:59 PM by metulburr.)
Space Invaders (player and player's shots)
import pygame as pg
pg.init()
class Laser:
def __init__(self, loc):
self.image = pg.Surface((5,40)).convert()
self.image.fill((255,255,0))
self.rect = self.image.get_rect(center=loc)
self.speed = 5
def update(self):
self.rect.y -= self.speed
def render(self, surf):
surf.blit(self.image, self.rect)
class Player:
def __init__(self, screen_rect):
self.screen_rect = screen_rect
#self.image = pg.image.load('spaceship.png').convert()
self.image = pg.Surface([50,50]) #to avoid loading an image
self.image.fill((0,0,255)) #to avoid loading an image
#self.image.set_colorkey((255,0,255)) #if loading an image, settings colorkey for it
self.transformed_image = pg.transform.rotate(self.image, 180) #if image, rotate it
start_buffer = 300
self.rect = self.image.get_rect(
center=(screen_rect.centerx, screen_rect.centery + start_buffer)
)
self.dx = 300
self.lasers = []
def get_event(self, event):
if event.type == pg.KEYDOWN:
if event.key == pg.K_SPACE:
self.lasers.append(Laser(self.rect.center))
def update(self, keys, dt):
self.rect.clamp_ip(self.screen_rect)
if keys[pg.K_LEFT]:
self.rect.x -= self.dx * dt
elif keys[pg.K_RIGHT]:
self.rect.x += self.dx * dt
for laser in self.lasers:
laser.update()
def draw(self, surf):
for laser in self.lasers:
laser.render(surf)
surf.blit(self.transformed_image, self.rect)
screen = pg.display.set_mode((800,600))
screen_rect = screen.get_rect()
player = Player(screen_rect)
clock = pg.time.Clock()
done = False
while not done:
keys = pg.key.get_pressed()
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
player.get_event(event)
screen.fill((0,0,0))
delta_time = clock.tick(60)/1000.0
player.update(keys, delta_time)
player.draw(screen)
pg.display.update()
Recommended Tutorials:
|