Python Forum
Thread Rating:
  • 1 Vote(s) - 4 Average
  • 1
  • 2
  • 3
  • 4
  • 5
[PyGame] Common Tasks
#11
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:
Reply
#12
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:
Reply
#13
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:
Reply
#14
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:
Reply
#15
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:
Reply
#16
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:
Reply
#17
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:
Reply
#18
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:
Reply
#19
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:
Reply
#20
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:
Reply


Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020