Pygame - move two objects while clicking and dragging one of them - python

I must be able to move both: green and red (pair) by clicking and dragging one of them, while keeping their position relative to each other. I am not sure how to do it with the code I already have. Should I create another class for red rectangles or use Parent Child classes? Help greatly appreciated.
import pygame as pg
WHITE = (255, 255, 255)
GREEN = (33, 133, 33)
RED = (255, 0, 0)
SCREEN = pg.display.set_mode((700, 500))
clock = pg.time.Clock()
cT = 140 # cycle time
cNum = 3 # cyle number
class Rectangle(pg.sprite.Sprite):
def __init__(self, color, width, height, x, y):
super().__init__()
self.height = height
self.color = color
self.image = pg.Surface((width, height))
self.image.fill(color)
self.rect = self.image.get_rect(topleft=(x, y))
def move(self, rel_x, rel_y):
self.rect.move_ip(rel_x, rel_y)
def collidepoint(self, pos):
return self.rect.collidepoint(pos)
class Rect1():
def __init__(self, color, width, height, x, y, all_sprites):
self.height = height
self.selected1 = False
self.rect = pg.Rect((x, y, width, height))
self.rectangles = []
for z in range(0, cNum * cT, cT):
rect = Rectangle(color, width, height, x, self.rect.y - z + cT)
self.rectangles.append(rect)
all_sprites.add(rect)
def move(self, rel_x, rel_y):
self.rect.move_ip(rel_x, rel_y)
for r in self.rectangles:
r.move(rel_x, rel_y)
def collidepoint(self, pos):
for r in self.rectangles:
if r.rect.collidepoint(pos):
return True
def handle_event(self, event):
if event.type == pg.MOUSEBUTTONDOWN:
if self.collidepoint(event.pos):
self.selected1 = True
elif event.type == pg.MOUSEBUTTONUP:
self.selected1 = False
elif event.type == pg.MOUSEMOTION:
if self.selected1 :
self.move(0, event.rel[1])
class Connection():
def __init__(self, rect_1, rect_2, vel):
self.rect_1 = rect_1
self.rect_2 = rect_2
self.vel = vel
def draw(self, screen):
for r_1 in self.rect_1.rectangles:
for r_2 in self.rect_2.rectangles:
dist1 = r_2.rect.x - r_1.rect.x
velocity = int(dist1 * self.vel) # green wave angle
A_start_x, A_start_y = r_1.rect.topright
A_end_x, A_end_y = r_1.rect.bottomright
B_start_x, B_start_y = r_2.rect.topleft
B_end_x, B_end_y = r_2.rect.bottomleft
if B_start_y < A_start_y - velocity and B_end_y > A_start_y - velocity:
start_pos = (A_start_x, A_start_y)
end_pos = (B_start_x, A_start_y - velocity) # minus 50
pg.draw.aaline(screen, GREEN, start_pos, end_pos, 1)
if B_end_y > A_end_y - velocity and B_start_y < A_end_y - velocity:
start_pos = (A_end_x, A_end_y-1)
end_pos = (B_end_x, A_end_y - 1 - velocity) # minus 50
pg.draw.aaline(screen, RED, start_pos, end_pos, 1)
def main():
pg.init()
all_sprites = pg.sprite.Group()
objects = [
Rect1(GREEN, 10, 58, 55, 244, all_sprites ),
Rect1(GREEN, 10, 111, 188, 226, all_sprites ),
Rect1(RED, 10, 68, 69, 222, all_sprites),
Rect1(RED, 10, 121, 211, 202, all_sprites),]
conns = [
Connection(objects[0], objects[1], 0.1),
Connection(objects[2], objects[3], -0.1),]
done = False
clock = pg.time.Clock()
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
for o in objects:
o.handle_event(event)
SCREEN.fill(WHITE)
all_sprites.draw(SCREEN)
for c in conns:
c.draw(SCREEN)
pg.display.update()
clock.tick(60)
pg.quit()
if __name__ == '__main__':
main()

You could associate the rectangles by storing a reference to the other rectangle as an attribute (e.g. self.associated) and then update the associated rects as well in the handle_event method: self.associated.move(0, event.rel[1])
class Rect1():
def __init__(self, color, width, height, x, y, all_sprites, associated=None):
self.associated = associated
# Snip ...
def handle_event(self, event):
# Snip ...
elif event.type == pg.MOUSEMOTION:
if self.selected1 :
self.move(0, event.rel[1])
self.associated.move(0, event.rel[1])
def main():
# Snip ...
green_short = Rect1(GREEN, 10, 58, 55, 244, all_sprites)
red_short = Rect1(RED, 10, 68, 69, 222, all_sprites, green_short)
green_short.associated = red_short
green_long = Rect1(GREEN, 10, 111, 188, 226, all_sprites)
red_long = Rect1(RED, 10, 121, 211, 202, all_sprites, green_long)
green_long.associated = red_long
objects = [green_short, green_long, red_short, red_long]

Related

Why does this variable change value despite not being referenced?

I need to create a base rectangle, and the blitted rectangle (default_rect and rect) for both scaling and scrolling purposes. I'm trying to change the y value of the RECT individually, but it somehow changes the value of default_rect also? I have no idea why. Perhaps the issue is coming from somewhere else. I have tried to create a minimum reproducible example to show what I'm talking about.
class Element(pg.sprite.Sprite):
def __init__(self, img, x, y, placement="center"):
super().__init__()
self.image = img
if placement == "topright":
self.rect = self.image.get_rect(topright=(x, y))
if placement == "center":
self.rect = self.image.get_rect(center=(x, y))
self.default_rect = self.rect
print("Test")
def update_rect(self, y_offset):
self.rect.centery = self.default_rect.centery + y_offset
When calling this update_rect() function, I see no reason why the value of self.default_rect.centery should be affected at all. This is the only place I reference default_rect.centery. I do not think that the issue is coming from the __init__ running multiple times because "Test" is only printed when initalising.
Rest of the minimum reproducible example. The rectangle continuously increases it's y position to -1,000,000 in seconds (without fps limit).
import pygame as pg
pg.init()
screen_width, screen_height = 800, 600
screen = pg.display.set_mode((screen_width, screen_height))
grey = (150, 150, 150)
dark_grey = (60, 60, 60)
class Element(pg.sprite.Sprite):
def __init__(self, img, x, y, placement="center"):
super().__init__()
self.image = img
if placement == "topright":
self.rect = self.image.get_rect(topright=(x, y))
if placement == "center":
self.rect = self.image.get_rect(center=(x, y))
self.default_rect = self.rect
print("Test")
def update_rect(self, y_offset):
self.rect.centery = self.default_rect.centery + y_offset
class ScrollBar(Element):
def __init__(self, x, y, total_bar_w, total_bar_h, total_h):
img = pg.Surface((total_bar_w, total_bar_h))
img.fill(dark_grey)
super().__init__(img, x, y, "topright")
bar_w = 0.98*total_bar_w
bar_h = (total_bar_h / total_h) * total_bar_h
bar_img = pg.Surface((bar_w, bar_h))
bar_img.fill(grey)
self.bar = Element(bar_img, x, y, "topright")
self.total_h = total_h
self.ratio = self.total_h / self.rect.h
self.offset = 0
self.y_offset = 0
self.pressed = False
self.active = False
def update(self):
pos = pg.mouse.get_pos()
click = pg.mouse.get_pressed()[0]
# Check if the slider bar was pressed
if click and self.rect.collidepoint(pos):
self.active = True
# Checking if mouse was released or mouse left the allowed zone
if not click or abs(pos[0] - self.rect.midleft[0]) > self.rect.w*3:
self.active = False
if self.active:
self.bar.rect.centery = pos[1]
if self.bar.rect.top < 0:
self.bar.rect.top = 0
if self.bar.rect.bottom > self.rect.top + self.rect.h:
self.bar.rect.bottom = self.rect.top + self.rect.h
# Calculate y offset for elements. Multiply by negative
# to allow us to add the offset rather than subtract
self.y_offset = (self.bar.rect.centery - self.bar.rect.h / 2) * -self.ratio
def draw(self):
screen.blit(self.image, (self.rect.x, self.rect.y))
screen.blit(self.bar.image, (self.bar.rect.x, self.bar.rect.y))
class Button(Element):
def __init__(self, x, y, w, h):
super().__init__(pg.Surface((w, h)), x, y, "center")
self.clicked = False
def update(self):
self.update_rect(scroll_bar.y_offset)
action = False
if self.rect.collidepoint(pg.mouse.get_pos()):
if pg.mouse.get_pressed()[0] and not self.clicked:
self.clicked = True
action = True
if not pg.mouse.get_pressed()[0]:
self.clicked = False
return action
def draw(self):
pg.draw.rect(screen, "blue", self.rect)
button1 = Button(400, 100, 200, 150)
button2 = Button(400, 300, 200, 150)
button3 = Button(400, 500, 200, 150)
buttons = [button1, button2, button3]
total_height = screen_height * 2
scroll_bar = ScrollBar(screen_width, 0, 0.05 * screen_width, screen_height, total_height)
run = True
while run:
screen.fill("white")
for button in buttons:
button.draw()
if button.update():
print("Button's y value is: " + str(button.rect.y))
scroll_bar.update()
scroll_bar.draw()
for e in pg.event.get():
if e.type == pg.QUIT:
run = False
if e.type == pg.KEYDOWN:
if e.key == pg.K_SPACE:
print(f"{button1.rect.y} / {button2.rect.y} / {button3.rect.y}")
if e.key == pg.K_r:
print(scroll_bar.y_offset)
pg.display.update()
When you assign self.default_rect = self.rect in __init__() both variables are references to the same object. So later in update_rect() when you change self.rect.centery, the change is reflected in self.default_rect because they both refer to the same object.
To understand how this works, check out the excelent talk by Ned Batchelder about names and values.

looping image with fade between them [duplicate]

So I am trying to fade my screen out and back in after completing a level using PyGame. My problem is that only the fadeout() works and not the fadein(). When calling the fadein() the screen turns black for a few seconds then suddenly shows the next level. I can't find the problem, any ideas?
def fadeout():
fadeout = pg.Surface((screen_width, screen_height))
fadeout = fadeout.convert()
fadeout.fill(black)
for i in range(255):
fadeout.set_alpha(i)
screen.blit(fadeout, (0, 0))
pg.display.update()
def fadein():
fadein = pg.Surface((screen_width, screen_height))
fadein = fadein.convert()
fadein.fill(black)
for i in range(255):
fadein.set_alpha(255-i)
screen.blit(fadein, (0, 0))
pg.display.update()
Your problem is that you fade in to a black screen, so you don't see any effect. A black screen with a black half-translucent Surface drawn on top is still a black Surface.
You should render the first frame of your level, and blit that Surface to the screen before blitting the fadein surface onto the screen.
Here's a simple example that I hacked together. Press a key to switch from one scene to the next.
import pygame
import random
from itertools import cycle
class Cloud(pygame.sprite.Sprite):
def __init__(self, x, y):
super().__init__()
self.image = pygame.Surface((50, 20))
self.image.set_colorkey((11, 12, 13))
self.image.fill((11, 12, 13))
pygame.draw.ellipse(self.image, pygame.Color('white'), self.image.get_rect())
self.rect = self.image.get_rect(topleft=(x,y))
def update(self, dt, events):
self.rect.move_ip(dt/10, 0)
if self.rect.left >= pygame.display.get_surface().get_rect().width:
self.rect.right = 0
class DayScene:
def __init__(self):
self.clouds = pygame.sprite.Group(Cloud(0, 30), Cloud(100, 40), Cloud(400, 50))
def draw(self, screen):
screen.fill(pygame.Color('lightblue'))
self.clouds.draw(screen)
def update(self, dt, events):
self.clouds.update(dt, events)
class NightScene:
def __init__(self):
sr = pygame.display.get_surface().get_rect()
self.sky = pygame.Surface(sr.size)
self.sky.fill((50,0,50))
for x in random.sample(range(sr.width), 50):
pygame.draw.circle(self.sky, (200, 200, 0), (x, random.randint(0, sr.height)), 1)
self.clouds = pygame.sprite.Group(Cloud(70, 70), Cloud(60, 40), Cloud(0, 50), Cloud(140, 10), Cloud(100, 20))
def draw(self, screen):
screen.blit(self.sky, (0, 0))
self.clouds.draw(screen)
def update(self, dt, events):
self.clouds.update(dt, events)
class Fader:
def __init__(self, scenes):
self.scenes = cycle(scenes)
self.scene = next(self.scenes)
self.fading = None
self.alpha = 0
sr = pygame.display.get_surface().get_rect()
self.veil = pygame.Surface(sr.size)
self.veil.fill((0, 0, 0))
def next(self):
if not self.fading:
self.fading = 'OUT'
self.alpha = 0
def draw(self, screen):
self.scene.draw(screen)
if self.fading:
self.veil.set_alpha(self.alpha)
screen.blit(self.veil, (0, 0))
def update(self, dt, events):
self.scene.update(dt, events)
if self.fading == 'OUT':
self.alpha += 8
if self.alpha >= 255:
self.fading = 'IN'
self.scene = next(self.scenes)
else:
self.alpha -= 8
if self.alpha <= 0:
self.fading = None
def main():
screen_width, screen_height = 300, 300
screen = pygame.display.set_mode((screen_width, screen_height))
clock = pygame.time.Clock()
dt = 0
fader = Fader([DayScene(), NightScene()])
while True:
events = pygame.event.get()
for e in events:
if e.type == pygame.QUIT:
return
if e.type == pygame.KEYDOWN:
fader.next()
fader.draw(screen)
fader.update(dt, events)
pygame.display.flip()
dt = clock.tick(30)
main()
By abstracting each scene into it's own class and delegating the scene change to the Fader class, we're able to let the scenes continue (or add a simple if statement to prevent that) and to handle events while fading.

How to add afterimages in pygame?

Since character movement is grid based, characters look a bit odd when going from square to square as they just appear from one square onto another. To make the movement feel more natural, I wanted to add "afterimages" so that there would be a simulation of smooth movement.
Demonstrational image:
Since my code has characters moving directly onto the next square, I don't know how to blit sprites in between.
if IDO.get_key(pygame.K_RIGHT):
if PhaseFocus == 0 or PhaseFocus == 2:
Reticlex +=1
if Currently_Selected != 0 and Currently_Selected.Phase == 2:
if Currently_Selected.x != Reticlex:
Currently_Selected.x = Reticlex
if Currently_Selected.x != Reticley:
Currently_Selected.y = Reticley
if IDO.get_key(pygame.K_LEFT):
if PhaseFocus == 0 or PhaseFocus == 2:
Reticlex -=1
if Currently_Selected != 0 and Currently_Selected.Phase == 2:
if Currently_Selected.x != Reticlex:
Currently_Selected.x = Reticlex
if Currently_Selected.x != Reticley:
Currently_Selected.y = Reticley
When the currently selected character is in Phase 2 (carried around) they should have these afterimages.
Let's see how we can do this.
Step 1: Basic setup
We start with an "empty" pygame game, like this:
import pygame
def main():
screen = pygame.display.set_mode((640, 480))
clock = pygame.time.Clock()
while True:
events = pygame.event.get()
for e in events:
if e.type == pygame.QUIT:
return
screen.fill((30, 30, 30))
pygame.display.flip()
clock.tick(60)
if __name__ == '__main__':
main()
We have a Clock, a single game loop and handle the QUIT event. I always store the events from event.get() in a variable in case I want to iterate over them again, e.g. when a Sprite wants to listen for an event. Always call event.get() only once per frame!
Step 2: A grid
We want a grid based game, so let's draw a grid. Also, we store a bunch of Rects in a list so it will be easy for us to lookup the screen coordinates of an object. We want to center objects in their tile, so using the Rect class will do the dirty work for us.
import pygame
TILESIZE = 32
GRID_W, GRID_H = (20, 15)
def create_grid():
surf = pygame.Surface((TILESIZE * GRID_W, TILESIZE * GRID_H))
surf.set_colorkey((2, 2, 2))
surf.fill((2, 2, 2))
grid = []
for y in range(GRID_H):
line = []
for x in range(GRID_W):
r = pygame.Rect(x * TILESIZE, y * TILESIZE, TILESIZE, TILESIZE)
line.append(r)
pygame.draw.rect(surf, pygame.Color('grey'), r, 1)
grid.append(line)
return grid, surf
def main():
screen = pygame.display.set_mode((TILESIZE * GRID_W, TILESIZE * GRID_H))
clock = pygame.time.Clock()
grid, grid_surf = create_grid()
while True:
events = pygame.event.get()
for e in events:
if e.type == pygame.QUIT:
return
screen.fill((30, 30, 30))
screen.blit(grid_surf, (0, 0))
pygame.display.flip()
clock.tick(60)
Step 3: Something's moving on the grid
We now create an actor that travels on this grid. In this case it's a simple Sprite that moves every second.
import pygame
import random
TILESIZE = 32
GRID_W, GRID_H = (20, 15)
LOOKUP = None
class Actor(pygame.sprite.Sprite):
def __init__(self, grid_pos):
super().__init__()
self.image = pygame.Surface((TILESIZE // 2, TILESIZE // 2))
self.rect = self.image.get_rect()
self.pos = pygame.Vector2()
self.update_pos(grid_pos)
self.image.fill(pygame.Color('dodgerblue'))
self.timeout = 1000
def update_pos(self, grid_pos):
self.grid_pos = grid_pos
self.rect.center = get_grid_rect(grid_pos).center
self.pos = pygame.Vector2(self.rect.topleft)
def move_random(self):
d = random.choice([-1, 1])
x, y = self.grid_pos
if random.randint(0, 2):
x += d
else:
y += d
self.update_pos((x, y))
def update(self, events, dt):
self.timeout -= dt
if self.timeout <= 0:
self.timeout = 1000
self.move_random()
def get_grid_rect(pos):
x, y = pos
return LOOKUP[y][x]
def create_grid():
surf = pygame.Surface((TILESIZE * GRID_W, TILESIZE * GRID_H))
surf.set_colorkey((2, 2, 2))
surf.fill((2, 2, 2))
grid = []
for y in range(GRID_H):
line = []
for x in range(GRID_W):
r = pygame.Rect(x * TILESIZE, y * TILESIZE, TILESIZE, TILESIZE)
line.append(r)
pygame.draw.rect(surf, pygame.Color('grey'), r, 1)
grid.append(line)
return grid, surf
def main():
screen = pygame.display.set_mode((TILESIZE * GRID_W, TILESIZE * GRID_H))
dt, clock = 0, pygame.time.Clock()
grid, grid_surf = create_grid()
global LOOKUP
LOOKUP = grid
sprites = pygame.sprite.Group(Actor((9, 6)))
while True:
events = pygame.event.get()
for e in events:
if e.type == pygame.QUIT:
return
sprites.update(events, dt)
screen.fill((30, 30, 30))
screen.blit(grid_surf, (0, 0))
sprites.draw(screen)
pygame.display.flip()
dt = clock.tick(60)
if __name__ == '__main__':
main()
Step 4: Smooth movement
Instead of always drawing the Actor that the center of the tiles, we set a target_grid_pos when we want to move. Each frame, we move the Actor a little bit until we reached our target tile. We use pygame's Vector2 class and it's lerp and distance_to methods.
import pygame
import random
TILESIZE = 32
GRID_W, GRID_H = (20, 15)
LOOKUP = None
class Actor(pygame.sprite.Sprite):
def __init__(self, grid_pos):
super().__init__()
self.image = pygame.Surface((TILESIZE // 2, TILESIZE // 2))
self.rect = self.image.get_rect()
self.pos = pygame.Vector2()
self.update_pos(grid_pos)
self.image.fill(pygame.Color('dodgerblue'))
self.timeout = 1000
def update_pos(self, grid_pos):#
self.target_pos = None
self.target_grid_pos = None
self.grid_pos = grid_pos
self.rect.center = get_grid_rect(grid_pos).center
self.pos = pygame.Vector2(self.rect.center)
def move_random(self):
d = random.choice([-1, 1])
x, y = self.grid_pos
if random.randint(0, 2):
x += d
else:
y += d
self.target_pos = pygame.Vector2(get_grid_rect((x, y)).center)
self.target_grid_pos = (x, y)
def update(self, events, dt):
self.timeout -= dt
if self.timeout <= 0:
self.timeout = 1000
self.move_random()
if self.target_grid_pos:
self.pos = self.pos.lerp(self.target_pos, 0.1)
if self.pos.distance_to(self.target_pos) < 1:
self.update_pos(self.target_grid_pos)
self.rect.center = self.pos
def get_grid_rect(pos):
x, y = pos
return LOOKUP[y][x]
def create_grid():
surf = pygame.Surface((TILESIZE * GRID_W, TILESIZE * GRID_H))
surf.set_colorkey((2, 2, 2))
surf.fill((2, 2, 2))
grid = []
for y in range(GRID_H):
line = []
for x in range(GRID_W):
r = pygame.Rect(x * TILESIZE, y * TILESIZE, TILESIZE, TILESIZE)
line.append(r)
pygame.draw.rect(surf, pygame.Color('grey'), r, 1)
grid.append(line)
return grid, surf
def main():
screen = pygame.display.set_mode((TILESIZE * GRID_W, TILESIZE * GRID_H))
dt, clock = 0, pygame.time.Clock()
grid, grid_surf = create_grid()
global LOOKUP
LOOKUP = grid
sprites = pygame.sprite.Group(Actor((9, 6)))
while True:
events = pygame.event.get()
for e in events:
if e.type == pygame.QUIT:
return
sprites.update(events, dt)
screen.fill((30, 30, 30))
screen.blit(grid_surf, (0, 0))
sprites.draw(screen)
pygame.display.flip()
dt = clock.tick(60)
if __name__ == '__main__':
main()
Step 5: Adding the effect
Every 300 ms, we create a copy of the Actor's image, blit it at the Actor's position, and make sure to delete it after 200ms. In a real game, you probably want to cache the images or use pre-rendered ones.
I switched the Group for LayeredUpdates so we can make sure the original images is always drawn above the "shadows".
I also added an image (from rltiles) instead of a simple rect so it looks nicer in this demo.
import pygame
import random
import gzip
import base64
TILESIZE = 64
GRID_W, GRID_H = (10, 7)
LOOKUP = None
class Shadow(pygame.sprite.Sprite):
def __init__(self, source):
super().__init__()
self._layer = 5
self.image = source.image.copy().convert_alpha()
self.image.fill((0, 0, 200, 100), special_flags=pygame.BLEND_ADD)
self.rect = source.rect.copy()
self.timeout = 200
def update(self, events, dt):
self.timeout -= dt
if self.timeout <= 0:
self.kill()
class Actor(pygame.sprite.Sprite):
def __init__(self, grid_pos):
super().__init__()
self._layer = 10
data, size = gzip.decompress(base64.b64decode(HYDRA)), (64, 64)
self.image = pygame.image.fromstring(data, size, "RGB")
self.image.set_colorkey((71, 108, 108))
self.rect = self.image.get_rect()
self.pos = pygame.Vector2()
self.update_pos(grid_pos)
self.timeout = 1000
self.shadow_timeout = 100
def update_pos(self, grid_pos):#
self.target_pos = None
self.target_grid_pos = None
self.grid_pos = grid_pos
self.rect.center = get_grid_rect(grid_pos).center
self.pos = pygame.Vector2(self.rect.center)
def move_random(self):
d = random.choice([-1, 1])
x, y = self.grid_pos
if random.randint(0, 2):
x += d
else:
y += d
self.target_pos = pygame.Vector2(get_grid_rect((x, y)).center)
self.target_grid_pos = (x, y)
def update(self, events, dt):
self.timeout -= dt
if self.timeout <= 0:
self.timeout = 1000
self.move_random()
if self.target_grid_pos:
self.shadow_timeout -= dt
if self.shadow_timeout <= 0:
self.shadow_timeout = 100
self.groups()[0].add(Shadow(self))
self.pos = self.pos.lerp(self.target_pos, 0.1)
if self.pos.distance_to(self.target_pos) < 1:
self.update_pos(self.target_grid_pos)
self.rect.center = self.pos
def get_grid_rect(pos):
x, y = pos
return LOOKUP[y][x]
def create_grid():
surf = pygame.Surface((TILESIZE * GRID_W, TILESIZE * GRID_H))
surf.set_colorkey((2, 2, 2))
surf.fill((2, 2, 2))
grid = []
for y in range(GRID_H):
line = []
for x in range(GRID_W):
r = pygame.Rect(x * TILESIZE, y * TILESIZE, TILESIZE, TILESIZE)
line.append(r)
pygame.draw.rect(surf, pygame.Color('grey'), r, 1)
grid.append(line)
return grid, surf
def main():
screen = pygame.display.set_mode((TILESIZE * GRID_W, TILESIZE * GRID_H))
dt, clock = 0, pygame.time.Clock()
grid, grid_surf = create_grid()
global LOOKUP
LOOKUP = grid
sprites = pygame.sprite.LayeredUpdates(Actor((4, 3)))
while True:
events = pygame.event.get()
for e in events:
if e.type == pygame.QUIT:
return
sprites.update(events, dt)
screen.fill((30, 30, 30))
screen.blit(grid_surf, (0, 0))
sprites.draw(screen)
pygame.display.flip()
dt = clock.tick(60)
HYDRA = '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'
if __name__ == '__main__':
main()

Combine effects to menu in pygame

Hey guys am developing a game with pygame. The idea of the game is that when a user click on start button on the menu(which appear on the first before starting a game) he must see the two balls bouncing on the pygame window.
For this i have two python files.
bounceball.py
This python file makes the two ball bounce on the pygame window which is made by me.The code of bounceball.py is
here.(Sorry for pasting it on pastebin since its a long code)
menu.py
This python file creates a menu which i have found from the internet and this also works fine.The code of menu.py is here
But the Problem is that when a user clicks on the Start button from the menu it didnt do anything .The thing i want is when a user clicks on start button from menu he must see the ball boucing which i have coded in bounceball.py on the pygame window.How can i link my bounceball.py to the menu.
I have tried to acheive this by many methods but it didnt helped me ..
Hope you guys can help me in acheieving this ..Any help would be appreciated ..Thanks in advance
It could be done better but at least it works.
menu.py
#!/usr/bin/python
import sys
import pygame
import bounceball
#----------------------------------------------------------------------
WHITE = (255, 255, 255)
RED = (255, 0, 0)
BLACK = ( 0, 0, 0)
#----------------------------------------------------------------------
class MenuItem(pygame.font.Font):
def __init__(self, text, font=None, font_size=30,
font_color=WHITE, (pos_x, pos_y)=(0, 0)):
pygame.font.Font.__init__(self, font, font_size)
self.text = text
self.font_size = font_size
self.font_color = font_color
self.label = self.render(self.text, 1, self.font_color)
self.width = self.label.get_rect().width
self.height = self.label.get_rect().height
self.dimensions = (self.width, self.height)
self.pos_x = pos_x
self.pos_y = pos_y
self.position = pos_x, pos_y
def is_mouse_selection(self, (posx, posy)):
if (posx >= self.pos_x and posx <= self.pos_x + self.width) and \
(posy >= self.pos_y and posy <= self.pos_y + self.height):
return True
return False
def set_position(self, x, y):
self.position = (x, y)
self.pos_x = x
self.pos_y = y
def set_font_color(self, rgb_tuple):
self.font_color = rgb_tuple
self.label = self.render(self.text, 1, self.font_color)
#----------------------------------------------------------------------
class GameMenu():
def __init__(self, screen, items, funcs, bg_color=BLACK, font=None, font_size=30,
font_color=WHITE):
self.screen = screen
self.scr_width = self.screen.get_rect().width
self.scr_height = self.screen.get_rect().height
self.bg_color = bg_color
self.clock = pygame.time.Clock()
self.funcs = funcs
self.items = []
for index, item in enumerate(items):
menu_item = MenuItem(item, font, font_size, font_color)
# t_h: total height of text block
t_h = len(items) * menu_item.height
pos_x = (self.scr_width / 2) - (menu_item.width / 2)
pos_y = (self.scr_height / 2) - (t_h / 2) + (index * menu_item.height)
menu_item.set_position(pos_x, pos_y)
self.items.append(menu_item)
self.mouse_is_visible = True
self.cur_item = None
def set_mouse_visibility(self):
if self.mouse_is_visible:
pygame.mouse.set_visible(True)
else:
pygame.mouse.set_visible(False)
def set_keyboard_selection(self, key):
"""
Marks the MenuItem chosen via up and down keys.
"""
for item in self.items:
# Return all to neutral
item.set_italic(False)
item.set_font_color(WHITE)
if self.cur_item is None:
self.cur_item = 0
else:
# Find the chosen item
if key == pygame.K_UP and \
self.cur_item > 0:
self.cur_item -= 1
elif key == pygame.K_UP and \
self.cur_item == 0:
self.cur_item = len(self.items) - 1
elif key == pygame.K_DOWN and \
self.cur_item < len(self.items) - 1:
self.cur_item += 1
elif key == pygame.K_DOWN and \
self.cur_item == len(self.items) - 1:
self.cur_item = 0
self.items[self.cur_item].set_italic(True)
self.items[self.cur_item].set_font_color(RED)
# Finally check if Enter or Space is pressed
if key == pygame.K_SPACE or key == pygame.K_RETURN:
text = self.items[self.cur_item].text
self.funcs[text]()
def set_mouse_selection(self, item, mpos):
"""Marks the MenuItem the mouse cursor hovers on."""
if item.is_mouse_selection(mpos):
item.set_font_color(RED)
item.set_italic(True)
else:
item.set_font_color(WHITE)
item.set_italic(False)
def run(self):
mainloop = True
while mainloop:
# Limit frame speed to 50 FPS
self.clock.tick(50)
mpos = pygame.mouse.get_pos()
for event in pygame.event.get():
if event.type == pygame.QUIT:
mainloop = False
if event.type == pygame.KEYDOWN:
self.mouse_is_visible = False
self.set_keyboard_selection(event.key)
if event.type == pygame.MOUSEBUTTONDOWN:
for item in self.items:
if item.is_mouse_selection(mpos):
self.funcs[item.text]()
if pygame.mouse.get_rel() != (0, 0):
self.mouse_is_visible = True
self.cur_item = None
self.set_mouse_visibility()
# Redraw the background
self.screen.fill(self.bg_color)
for item in self.items:
if self.mouse_is_visible:
self.set_mouse_selection(item, mpos)
self.screen.blit(item.label, item.position)
pygame.display.flip()
#----------------------------------------------------------------------
def run_bounceball():
print "run bounceball"
bounceball.run(screen)
#----------------------------------------------------------------------
if __name__ == "__main__":
pygame.init()
# Creating the screen
screen = pygame.display.set_mode((300, 300), 0, 32)
menu_items = ('Start', 'Quit')
funcs = {'Start': run_bounceball,
'Quit': sys.exit}
pygame.display.set_caption('Game Menu')
gm = GameMenu(screen, funcs.keys(), funcs)
gm.run()
bounceball.py
import pygame
import math
from itertools import cycle
#----------------------------------------------------------------------
# some simple vector helper functions, stolen from http://stackoverflow.com/a/4114962/142637
def magnitude(v):
return math.sqrt(sum(v[i]*v[i] for i in range(len(v))))
def add(u, v):
return [ u[i]+v[i] for i in range(len(u)) ]
def sub(u, v):
return [ u[i]-v[i] for i in range(len(u)) ]
def dot(u, v):
return sum(u[i]*v[i] for i in range(len(u)))
def normalize(v):
vmag = magnitude(v)
return [ v[i]/vmag for i in range(len(v)) ]
#----------------------------------------------------------------------
class Ball(object):
def __init__(self, path, screen):
self.x, self.y = (0, 0)
self.speed = 2.5
self.color = (200, 200, 200)
self.path = cycle(path)
self.set_target(next(self.path))
self.screen = screen
#property
def pos(self):
return self.x, self.y
# for drawing, we need the position as tuple of ints
# so lets create a helper property
#property
def int_pos(self):
return map(int, self.pos)
#property
def target(self):
return self.t_x, self.t_y
#property
def int_target(self):
return map(int, self.target)
def next_target(self):
self.set_target(self.pos)
self.set_target(next(self.path))
def set_target(self, pos):
self.t_x, self.t_y = pos
def update(self):
# if we won't move, don't calculate new vectors
if self.int_pos == self.int_target:
return self.next_target()
target_vector = sub(self.target, self.pos)
# a threshold to stop moving if the distance is to small.
# it prevents a 'flickering' between two points
if magnitude(target_vector) < 2:
return self.next_target()
# apply the balls's speed to the vector
move_vector = [c * self.speed for c in normalize(target_vector)]
# update position
self.x, self.y = add(self.pos, move_vector)
def draw(self):
pygame.draw.circle(self.screen, self.color, self.int_pos, 4)
#----------------------------------------------------------------------
def run(screen):
#pygame.init() # no need it - inited in menu.py
#screen = pygame.display.set_mode((300, 300)) # no need it - created in menu.py
clock = pygame.time.Clock()
quit = False
path = [(26, 43),
(105, 110),
(45, 225),
(145, 295),
(266, 211),
(178, 134),
(250, 56),
(147, 12)]
path2 = [(26, 43),
(105, 10),
(45, 125),
(150, 134),
(150, 26),
(107, 12)]
ball = Ball(path, screen)
ball.speed = 1.9
ball2 = Ball(path2, screen)
ball2.color = (200, 200, 0)
balls = [ball, ball2]
while not quit:
quit = pygame.event.get(pygame.QUIT)
pygame.event.poll()
map(Ball.update, balls)
screen.fill((0, 0, 0))
map(Ball.draw, balls)
pygame.display.flip()
clock.tick(60)

Pygame draw not drawing what it should

Ok, so I am using pygame.draw to make a stick figure in the class called Entity:
class Entity: #Used with default arguments blited on a 600 by 600 pixel screen
def __init__(self, pos=[300, 300]):
self.pos = pos
self.legR = [10, 25]
self.legL = [-10, -25]
self.armR = [0, 0]
self.armL = [0, 0]
self.body = [30, 5]
self.head = [0, 0, 5]
self.size = [60, 110]
self.color = [0, 0, 0]
self.image = pygame.surface.Surface(self.size)
self.image.fill([255, 255, 255])
def render(self, screen, frame):
self.image = pygame.surface.Surface(self.size)
self.image.fill([255, 255, 255])
pygame.draw.line(self.image, self.color, [self.size[0]/2, self.size[1]/2],
[self.size[0]/2+self.legR[0], self.size[0]/2+self.legR[1]], 5)
pygame.draw.line(self.image, self.color, [self.size[0]/2, self.size[1]/2],
[self.size[0]/2+self.legL[0], self.size[0]/2+self.legL[1]], 5)
pygame.draw.line(self.image, self.color, [self.size[0]/2, self.size[1]/2],
[self.size[0]/2+self.body[0], self.size[0]/2+self.body[1]], self.body[1])
pygame.draw.circle(self.image, self.color,
[self.size[0]/2+self.body[0]+self.head[0], self.size[1]/2+self.body[1]+self.head[1]],
self.head[2])
#pygame.draw.line(self.image, self.color, [self.size/2
screen.blit(self.image, self.pos)
So I run this and it gives me this weird messed up image with a bunch of lines in random directions. It seams to me I do not really understand the function well. Could I please have a example of a render able stick figure with configurable joints? If not, could someone please at least tell me my fatal error? Thanks!
I started writing an example based off your code. For now it just draws 2 legs and his spine:
Note:
Using vectors instead of tuples would let you do return
self.pos+offset vs return (self.pos[0]+offset[0],
self.pos[1]+offset[1])
I use offsets relative a local origin to draw.
code:
import pygame
from pygame.locals import *
pygame.init()
# not normally all global, but simplified demo
color_bg = Color("gray20")
color_fg = Color("gray80")
clock = pygame.time.Clock()
screen = pygame.display.set_mode((600,400))
class Entity():
def __init__(self, pos=(300, 300)):
self.pos = pos
self.armR = (10, 10)
self.armL = (-10, 10)
self.body = (0, -20)
self.head_offset = self.offset(self.body)
def offset(self, offset):
# get offset to draw, relative stickman's hips
return (self.pos[0]+offset[0], self.pos[1]+offset[1])
def render(self):
b = self.pos
#pygame.draw.line( screen, color_fg, (10,10), (20,30) )
o = self.offset( self.armL )
pygame.draw.line( screen, color_fg, b, o )
o = self.offset( self.armR )
pygame.draw.line( screen, color_fg, b, o )
o = self.offset( self.body )
pygame.draw.line( screen, Color("red"), b, o )
class Game():
def __init__(self):
self.e = Entity()
def draw(self):
screen.fill( color_bg )
self.e.render()
pygame.display.flip()
clock.tick(80)
def loop(self):
done=False
while not done:
events = pygame.event.get()
for event in events:
if event.type == pygame.QUIT: done = True
# event: keydown
elif event.type == KEYDOWN:
if event.key == K_ESCAPE or event.key == K_SPACE: done = True
elif event.type == MOUSEMOTION:
self.mouse_loc = event.pos
self.draw()
g = Game()
g.loop()
The main problem I see is that you're using the x-value of your entity's size for the y-value of one of the points on your lines:
pygame.draw.line(self.image, self.color, [self.size[0]/2, self.size[1]/2],
[self.size[0]/2+self.legR[0], self.size[0]/2+self.legR[1]], 5)
The second value in the third argument should be:
self.size[1]/2+self.legR[1]
That will get you want you want rendered, but I would also takes monkey's advice and organize a bit and compartmentalize repeated code into functions.

Categories

Resources