How to draw bubbles and turn them animated into circles - python

I am trying to make a python program to draw a line and turn it into a circle with an animation using pygame, yet I haven't even gotten through the drawing-the-line code. I have noticed that python is changing the wrong or both items in a list that contains the starting point when the user presses down the left click, stored as the first item, and the current point of the user's mouse as the second.
This is generally what I want it to do: https://youtu.be/vlqZ0LubXCA
Here are the outcomes with and without the lines that update the 2nd item:
with:
without:
As you can see, or read in the descriptions, the line is necessary to cover the previous frame.
I have marked the lines that change the outcome with arrows:
import pygame, PIL, random
print('\n')
#data
bubbles = []
color_options = [[87, 184, 222]]
pressed = False
released = False
bubline_start = []
background = [50, 25, 25]
size = [500, 500]
#pygame
display = pygame.display.set_mode(size)
pygame.init()
#functions
def new_bub_color():
color_index = random.randint(0, len(color_options)-1)
lvl = random.randrange(85, 115)
bub_color = []
for val in color_options[color_index]:
bub_color.append(val*(lvl/100))
return bub_color
def bubble_line():
global display, pressed, bubline_start, released, bubbles, color_options
if len(bubbles) > 0:
if not bubbles[-1][0] == 0:
#first frame of click
bub_color = new_bub_color()
bubbles.append([0, bub_color, [bubline_start, list(pygame.mouse.get_pos())]])
pygame.draw.line(display, bub_color, bubline_start, pygame.mouse.get_pos())
else:
#draw after drags
pygame.draw.line(display, bubbles[-1][1], bubbles[-1][2][0], list(pygame.mouse.get_pos()))
bubbles[-1][2][1] = list(pygame.mouse.get_pos())# <-- HERE
else:
#first bubble
bub_color = new_bub_color()
bubbles.append([0, bub_color, [bubline_start, list(pygame.mouse.get_pos())]])
pygame.draw.line(display, bub_color, bubline_start, pygame.mouse.get_pos())
if released:
bubbles[-1][0] = 1
bubbles[-1][2][1] = list(pygame.mouse.get_pos())# <-- HERE
released = False
def cover_prev_frame():
global bubbles, background, size
min_pos = []
max_pos = []
for bubble in bubbles:
min_pos = bubble[2][0]
max_pos = bubble[2][0]
for point in bubble[2]:
#x min and max
if point[0] < min_pos[0]:
min_pos[0] = point[0]
elif point[0] > max_pos[0]:
max_pos[0] = point[0]
#y min and max
if point[1] < min_pos[1]:
min_pos[1] = point[1]
elif point[1] > max_pos[1]:
max_pos[1] = point[1]
max_pos = [max_pos[0]-min_pos[0]+1, max_pos[1]-min_pos[1]+1]
if type(background) == str:
#image background
later = True
elif type(background) == list:
#solid color background
pygame.draw.rect(display, background, pygame.Rect(min_pos, max_pos))
while True:
pygame.event.pump()
events = pygame.event.get()
for event in events:
if event.type == pygame.QUIT:
pygame.quit()
elif event.type == pygame.MOUSEBUTTONDOWN and not pressed:
bubline_start = list(pygame.mouse.get_pos())
pressed = True
elif event.type == pygame.MOUSEBUTTONUP and pressed:
pressed = False
released = True
cover_prev_frame()
if pressed or released:
bubble_line()
try:
pygame.display.update()
except:
break

if not bubbles[-1][0] == 0: is False as long as the mouse is not released. Therefore add many line segments, each starting at bubline_start and ending at the current mouse position.
You must redraw the scene in each frame. bubbles is a list of bubbles and each bubble has a list of points. Add a new point to the last bubble in the list while the mouse is held down. Start a new bubble when the mouse is pressed and end a bubble when it is released. This greatly simplifies your code.
Minimal example
import pygame, random
size = [500, 500]
pygame.init()
display = pygame.display.set_mode(size)
clock = pygame.time.Clock()
pressed = False
bubbles = []
background = [50, 25, 25]
run = True
while run:
clock.tick(100)
events = pygame.event.get()
for event in events:
if event.type == pygame.QUIT:
run = False
elif event.type == pygame.MOUSEBUTTONDOWN:
start_pos = list(event.pos)
bubble_color = pygame.Color(0)
bubble_color.hsla = (random.randrange(0, 360), 100, 50, 100)
bubbles.append((bubble_color, [start_pos]))
pressed = True
elif event.type == pygame.MOUSEMOTION and pressed:
new_pos = list(event.pos)
if len(bubbles[-1][1]) > 0 and bubbles[-1][1] != new_pos:
bubbles[-1][1].append(new_pos)
elif event.type == pygame.MOUSEBUTTONUP:
pressed = False
end_pos = list(event.pos)
if len(bubbles[-1][1]) > 0 and bubbles[-1][1] != end_pos:
bubbles[-1][1].append(list(event.pos))
display.fill(background)
for i, bubble in enumerate(bubbles):
if len(bubble[1]) > 1:
closed = not pressed or i < len(bubbles) - 1
pygame.draw.lines(display, bubble[0], closed, bubble[1], 3)
pygame.display.update()
pygame.quit()
For the animation I propose to create a class that represents a bubble and a method animate that slowly turns the polygon into a circle.
Minimal example
import pygame, random
size = [500, 500]
pygame.init()
display = pygame.display.set_mode(size)
clock = pygame.time.Clock()
class Bubble:
def __init__(self, start):
self.color = pygame.Color(0)
self.color.hsla = (random.randrange(0, 360), 100, 50, 100)
self.points = [list(start)]
self.closed = False
self.finished = False
def add_point(self, point, close):
self.points.append(list(point))
self.closed = close
if self.closed:
x_, y_ = list(zip(*self.points))
x0, y0, x1, y1 = min(x_), min(y_), max(x_), max(y_)
rect = pygame.Rect(x0, y0, x1-x0, y1-y0)
self.center = rect.center
self.radius = max(*rect.size) // 2
def animate(self):
if self.closed and not self.finished:
cpt = pygame.math.Vector2(self.center) + (0.5, 0.5)
self.finished = True
for i, p in enumerate(self.points):
pt = pygame.math.Vector2(p)
v = pt - cpt
l = v.magnitude()
if l + 0.5 < self.radius:
self.finished = False
v.scale_to_length(min(self.radius, l+0.5))
pt = cpt + v
self.points[i] = [pt.x, pt.y]
def draw(self, surf):
if self.finished:
pygame.draw.circle(surf, self.color, self.center, self.radius, 3)
elif len(self.points) > 1:
pygame.draw.lines(surf, self.color, self.closed, self.points, 3)
bubbles = []
pressed = False
background = [50, 25, 25]
run = True
while run:
clock.tick(100)
events = pygame.event.get()
for event in events:
if event.type == pygame.QUIT:
run = False
elif event.type == pygame.MOUSEBUTTONDOWN:
bubbles.append(Bubble(event.pos))
pressed = True
elif event.type == pygame.MOUSEMOTION and pressed:
bubbles[-1].add_point(event.pos, False)
elif event.type == pygame.MOUSEBUTTONUP:
bubbles[-1].add_point(event.pos, True)
pressed = False
for bubble in bubbles:
bubble.animate()
display.fill(background)
for bubble in bubbles:
bubble.draw(display)
pygame.display.update()
pygame.quit()

Related

Is Pygame Drawing a Line and I Just Can't See It?

I'm new to OOP and trying to get the gist of using Classes and Methods. In this particular case, I've used a class to create two red nodes and managed to use MOUSEBUTTONDOWN with my class.
However, when I try to use a MOUSEBUTTONDOWN event to draw a line, nothing seems to happen. I've
used test print statements in multiple places to ensure that I'm "reaching" my class and that
the method is executing. Nothing, however, can seem to make my red line appear.
I've also moved the draw statement out of the method to near the end of my game loop and it
appears correctly.
What am I misunderstanding about classes and methods?
import pygame
class Rnode():
def __init__(self, x, y, image_rednode):
self.x = x
self.y = y
self.image_rednode = image_rednode
self.rect = self.image_rednode.get_rect()
self.rect.topleft = (x, y)
self.clicked = False
self.wired = False
# draw node line
def put(self):
screen.blit(self.image_rednode, (self.x, self.y))
#get mouse position
pos = pygame.mouse.get_pos()
#check mouseover and clicked
if self.rect.collidepoint(pos):
if pygame.mouse.get_pressed()[0] == 1 and self.clicked == False:
self.clicked = True
print('gotcha' + str(self))
self.wired = True
if pygame.mouse.get_pressed()[0] == 0:
self.clicked = False
def draw_line(self):
if pygame.mouse.get_pressed()[0]:
self.pos = pygame.mouse.get_pos()
pygame.draw.line(screen,red,(self.x + 15, self.y + 15),(self.pos), 3)
# these are the colors
green = (48, 141, 70)
grey = (211, 211, 211)
lime = (201, 223, 202)
purplish = (116,137,192)
orange = (234,168,0)
brown = (59,47,47)
blue = (0,91,150)
red = (255,8,0)
screen = pygame.display.set_mode((1280, 720))
pygame.display.set_caption('Classy, Baby!')
running = 1
xb = pygame.image.load(r'xb7white.png').convert_alpha()
rednode = pygame.image.load('redhole.svg').convert_alpha()
rednode = pygame.transform.scale(rednode, (100, 100))
# make node instances
r1 = Rnode(300, 300, rednode)
r2 = Rnode(500, 300, rednode)
while running:
screen.fill((0, 0, 0))
event = pygame.event.poll()
if event.type == pygame.QUIT:
running = 0
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
pygame.quit()
# if event.type == pygame.MOUSEBUTTONDOWN: # if the user pressed a mouse button
# pos = pygame.mouse.get_pos() # get the mouse pos
# if g1.rect.collidepoint(pos):
r1.put()
r2.put()
if r1.wired:
r1.draw_line()
pygame.display.flip()
pygame.mouse.get_pressed() is not an event, but gives the current state of the mouse buttons. Rnode represents a node and should not draw a line or handle the events. Handle the event in an event loop and add the lines to a list:
import pygame
class Rnode():
def __init__(self, x, y, image_rednode):
self.image_rednode = image_rednode
self.rect = self.image_rednode.get_rect(center = (x, y))
def put(self):
screen.blit(self.image_rednode, self.rect)
class Line():
def __init__(self, nodeFrom, nodeTo):
self.form = nodeFrom
self.to = nodeTo
def draw(self):
p1 = self.form.rect.center
p2 = self.to.rect.center
pygame.draw.line(screen, "yellow", p1, p2, 3)
screen = pygame.display.set_mode((300, 300))
pygame.display.set_caption('Classy, Baby!')
clock = pygame.time.Clock()
#rednode = pygame.image.load('redhole.svg').convert_alpha()
#rednode = pygame.transform.scale(rednode, (100, 100))
rednode = pygame.Surface((40, 40), pygame.SRCALPHA)
pygame.draw.circle(rednode, "red", (20, 20), 20)
nodes = [
Rnode(100, 100, rednode), Rnode(200, 100, rednode),
Rnode(100, 200, rednode), Rnode(200, 200, rednode)]
lines = []
start = None
running = 1
while running:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = 0
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
running = 0
if event.type == pygame.MOUSEBUTTONDOWN:
for node in nodes:
if node.rect.collidepoint(event.pos):
if start and start != node:
lines.append(Line(start, node))
start = None
else:
start = node
break
screen.fill((0, 0, 0))
for line in lines:
line.draw()
if start:
pygame.draw.line(screen, "yellow", start.rect.center, pygame.mouse.get_pos(), 3)
for node in nodes:
node.put()
pygame.display.flip()
pygame.quit()

Moving objects in pygame using keyboard module

I'm creating 2-player PONG game with pygame.I have my Racquets on both sides.
One of them move with W and S and another with UP and DOWN arrow.
I'm using this code to move Racquets:
chx = 0.051
chy = 0.051
def ychangeneg():
global y
if y <= 4:
return
else:
y -= chy
return
def ychangepos():
global y
if y >= 327:
return
else:
y += chy
return
def y1changeneg():
global y1
if y1 <= 4:
return
else:
y1 -= chy
return
def y1changepos():
global y1
if y1 >= 327:
return
else:
y1 += chy
return
while True:
for event in pygame.event.get():
keyQ = pygame.key.get_pressed()
if event.type == pygame.QUIT:
system("cls")
quit()
keyboard.add_hotkey("w",lambda:ychangeneg())
keyboard.add_hotkey("s",lambda:ychangepos())
keyboard.add_hotkey("up",lambda:y1changeneg())
keyboard.add_hotkey("down",lambda:y1changepos())
chy variable changes y of Racquet and moves it.But I have these problems:
When I start holding a key,Racquet starts moving with delay and then becomes faster
When I holding 2 key (W and UP arrow) at a same time, Racquets don't move
At first, I found some codes that using key= pygame.key.get_pressed() but when you hold a key with this code, It moves but not continuously.
Do not mix the pygame.key module with the python keyboard module.
See How can I make a sprite move when key is held down and that changes the y-coordinate, depending on the keys pressed:
def move_y(y, keys, up, down):
new_y = y + (keys[down] - keys[up]) * chy
return max(4, min(327, new_y))
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
system("cls")
quit()
keyQ = pygame.key.get_pressed()
y = move_y(y, keyQ, pygame.K_w, pygame.K_s)
y1 = move_y(y1, keyQ, pygame.K_UP, pygame.K_DOWN)
Minimal example:
import pygame
pygame.init()
window = pygame.display.set_mode((400, 350))
clock = pygame.time.Clock()
paddle1 = pygame.Rect(10, 0, 10, 20)
paddle1.centery = window.get_rect().centery
paddle2 = pygame.Rect(380, 0, 10, 20)
paddle2.centery = window.get_rect().centery
chy = 10
def move_y(y, keys, up, down):
new_y = y + (keys[down] - keys[up]) * chy
return max(4, min(327, new_y))
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
keyQ = pygame.key.get_pressed()
paddle1.y = move_y(paddle1.y, keyQ, pygame.K_w, pygame.K_s)
paddle2.y = move_y(paddle2.y, keyQ, pygame.K_UP, pygame.K_DOWN)
window.fill(0)
pygame.draw.rect(window, (255, 255, 255), paddle1)
pygame.draw.rect(window, (255, 255, 255), paddle2)
pygame.display.flip()
clock.tick(60)
pygame.quit()
exit()
I'd suggest not using the keyboard module. Instead look, which key is pressed by using pygame.key.get_pressed(). Check if the key is in there, and if so then change the coordinates.
somewhat like this:
pressed_keys = pygame.key.get_pressed()
if pressed_keys[pygame.UP]: # this evaluates to true if the up key is pressed
ychangeneg()
if pressed_keys[pygame.DOWN]:
ychangepos()
# and so on...

Snake game error code (IndexError: list index out of range)

Im creating a snake Pygame with a menu and am fine tuning the bugs and such when I come across the error
IndexError: list index out of range
the error actually appears after I open the tab itself and move the cursor over it
I have a faint idea of what it actually means but I am quite new to python and coding in general so I would appreciate it if someone could explain and show a solution,
thank you very much and here is the code
import pygame
import sys
import random
import time
pygame.init()
WHITE = (255, 255, 255)
YELLOW = (255, 255, 102)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
DARKRED = (125, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
screenWidth = 800
screenHeight = 800
screen = pygame.display.set_mode((screenWidth, screenHeight))
pygame.display.set_caption('Snake Game')
clock = pygame.time.Clock()
snakeBlock = 10
snakeSpeed = 15
fontTitle = pygame.font.SysFont("arial",100)
fontStyle = pygame.font.SysFont("ariel", 50)
scoreFont = pygame.font.SysFont("ariel", 35)
def score(score):
value = scoreFont.render(" Score: " + str(score), True, BLACK)
screen.blit(value, [50, 50])
def snake(snakeBlock, snake_list):
for x in snake_list:
pygame.draw.rect(screen, GREEN, [x[0], x[1], snakeBlock, snakeBlock])
def message(msg, colour):
msg = fontStyle.render(msg, True, BLACK)
screen.blit(msg, [screenWidth / 20, screenHeight / 2])
def gameLoop():
gameOver = False
gameEnd = False
instructions = False
game = True
intro = True
main = True
x1 = screenWidth / 2
y1 = screenHeight / 2
dx = 0
dy = 0
snakeList = []
snakeLength = 2
foodx = round(random.randrange(0, screenWidth - snakeBlock) / 10.0) * 10.0
foody = round(random.randrange(0, screenHeight - snakeBlock) / 10.0) * 10.0
def menu(titles):
buttonTitleFont = pygame.font.SysFont("arial", 52)
selection = []
rectWidth = 400
rectHeight = 60
x = int(screen.get_width()/2 - rectWidth/2)
y = 450
length = len(titles)
num = 0
hover = False
# creates the Rects (containers) for the buttons
for i in range (0,length,1):
choiceRect = pygame.Rect(x,y,rectWidth,rectHeight)
selection.append(choiceRect)
y += 100
#main loop in menu
menu = True
while menu:
for event in pygame.event.get():
if event.type == pygame.QUIT:
menu = False
pygame.quit()
sys.exit()
if event.type ==pygame.MOUSEMOTION: # if mouse moved
hover = False
mx, my = pygame.mouse.get_pos() # get the mouse position
for i in range (length):
if selection[i].collidepoint((mx,my)): # check if x,y of mouse is in a button
num = i
hover = True
if event.type == pygame.MOUSEBUTTONDOWN and hover == True: #if mouse is in button
menu = False # and has been clicked
# draw all buttons
for choice in selection:
pygame.draw.rect(screen,WHITE,choice,0)
# redraw selected button in another colour
pygame.draw.rect(screen,GREEN,selection[num],0)
# draw all the titles on the buttons
x = int(screen.get_width()/2 - 150)
y = 450
for i in range(0,length,1):
buttonTitle = buttonTitleFont.render(titles[i],True,BLACK)
screen.blit(buttonTitle,(x,y))
y += 100
pygame.display.update()
return num
while main:
for event in pygame.event.get(): # check for any events (i.e key press, mouse click etc.)
if event.type ==pygame.QUIT: # check to see if it was "x" at top right of screen
main = False # set the "main" variable to False to exit while loop
while intro:
for event in pygame.event.get():
if event.type == pygame.QUIT:
main = False
intro = False
screen.fill(BLACK)
menuMain = ["Launch", "Instructions","QUIT"]
mainMenu = True
mainInt = True
while mainInt:
for event in pygame.event.get():
if event.type == pygame.QUIT:
main = False
intro = False
mainInt = False
screen.fill(BLACK)
#Centers the rendered tiles
textTitle = fontTitle.render("Snake", True, GREEN )
textW = textTitle.get_width()
textH = textTitle.get_height()
xTitle = int(screenWidth/2 - textW/2)
yTitle = int(screenHeight/4 - textH/2)
screen.blit(textTitle, (xTitle,yTitle))
pygame.display.update()
# in the intro, this asks the user where they would like to go
if mainMenu ==True:
choose = menu(menuMain)
if choose == 0:
menu = False
intro = False
mainInt = False
mainMenu = False
game = True
screen.fill(BLACK)
elif choose ==1:
menu = False
instructions = True
mainMenu = False
screen.fill(BLACK)
pygame.display.update()
else:
menu = False
main = False
intro = False
mainInt = False
mainMenu = False
while game:
if gameOver == True:
game = False
while gameEnd == True:
screen.fill(DARKRED)
message("You Lost! Press C to Play Again or Q to Quit", RED)
score(snakeLength - 1)
pygame.display.update()
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_q:
gameOver = True
gameEnd = False
if event.key == pygame.K_c:
gameLoop()
for event in pygame.event.get():
if event.type == pygame.QUIT:
gameOver = True
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
dx = -snakeBlock
dy = 0
elif event.key == pygame.K_RIGHT:
dx = snakeBlock
dy = 0
elif event.key == pygame.K_UP:
dx = 0
dy = -snakeBlock
elif event.key == pygame.K_DOWN:
dx = 0
dy = snakeBlock
if x1 >= screenWidth or x1 < 0 or y1 >= screenHeight or y1 < 0:
gameEnd = True
x1 += dx
y1 += dy
screen.fill(WHITE)
pygame.draw.rect(screen, RED, [foodx, foody, snakeBlock, snakeBlock])
snakeHead = []
snakeHead.append(x1)
snakeHead.append(y1)
snakeList.append(snakeHead)
if len(snakeList) > snakeLength:
del snakeList[0]
for x in snakeList[:-1]:
if x == snakeHead:
gameEnd = True
snake(snakeBlock, snakeList)
score(snakeLength - 1)
pygame.display.update()
if x1 == foodx and y1 == foody:
foodx = round(random.randrange(0, screenWidth - snakeBlock) / 10.0) * 10.0
foody = round(random.randrange(0, screenHeight - snakeBlock) / 10.0) * 10.0
snakeLength += 1
clock.tick(snakeSpeed)
pygame.quit()
quit()
gameLoop()
I see that in your code, you're referencing selection[some_index] multiple times. If you look into what selection actually is, you'll find that it's an array with one rectangle object inside of it:
[<rect(200, 450, 400, 60)>]
This rectangle never changes, so I suggest referencing it directly by calling
selection[0]
For example, here is the snippet of code that's giving you the error.
for i in range (length):
if selection[i].collidepoint((mx,my)): # check if x,y of mouse is in a button
num = i
hover = True
Because selection is an array with one element that never changes, you get an Index out of range error after the first iteration of your for loop. Perhaps this looks familiar:
IndexError: list index out of range
What you can do to fix this is get rid of the loop (because it's unnecessary - you shouldn't be looping through indices if there's only one rectangle in the selection list!) and extracting the rectangle at the 0-th index.
if selection[0].collidepoint((mx,my)): # check if x,y of mouse is in a button
num = i
hover = True
This isn't the only bug I ran into in your code, but I believe this will help you on your way!

Inaccurate object movement with mouse in Pygame

I'm attempting to make a near carbon copy of this game:
https://yppedia.puzzlepirates.com/Carpentry
It uses pentominos, which are objects made of 5 blocks. I have a Piece class, that stores each of these blocks in a list. When I click on a Block to move it, I'm also moving every other block that shares the same parent Piece object, so I can drag the entire piece around with my mouse.
The problem I'm having is that when I click one of these blocks, the piece moves away from my cursor. The dragging is fine, but I want it to follow the cursor more precisely.
I am using a Mouse class so I can implement simple collisions between mouse clicks and the Blocks, but I think this is the cause of my problems.
Edit: I could probably resolve this by hardcoding changes to the x and y positions of each block, but ideally I'd prefer a more modular solution because I think I'm misunderstanding how the mouse position works in pygame.
import pygame
import sys
import collections
import random
pygame.init()
FPS = 25
fpsClock = pygame.time.Clock()
# -----------SCREEN----------------#
WIN_WIDTH = 680 # width of window
WIN_HEIGHT = 500 # height of window
DISPLAY = (WIN_WIDTH, WIN_HEIGHT) # variable for screen display
DEPTH = 32 # standard
FLAGS = 0 # standard
screen = pygame.display.set_mode(DISPLAY, FLAGS, DEPTH)
pygame.display.set_caption('Carpentry')
# ---------------------------------#
# ---------------colours-----------#
WOOD = (182, 155, 76)
RED = (255, 0, 0)
BLACK = (0, 0, 0)
# ---------------------------------#
blocks = pygame.sprite.Group()
pieces = []
class Block(pygame.sprite.Sprite):
def __init__(self, x, y, parent):
super().__init__()
self.image = pygame.Surface([15, 15])
self.colour = WOOD
self.parent = parent
self.image.fill(self.colour)
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
self.original_x = x
self.original_y = y
self.drag = False
class Mouse(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.Surface([15, 15])
self.colour = RED
self.image.fill(self.colour)
self.rect = self.image.get_rect()
self.rect.x = pygame.mouse.get_pos()[0]
self.rect.y = pygame.mouse.get_pos()[1]
def update_mouse(self):
self.rect.x = pygame.mouse.get_pos()[0]
self.rect.y = pygame.mouse.get_pos()[1]
class Piece:
def __init__(self):
self.blocks = []
self.c = 0
def insert(self, template):
for direction in template:
self.blocks.append([])
x = 0
y = 0
for line in direction:
for character in line:
if character == "O":
my_block = Block(x, y, self)
self.blocks[self.c].append(my_block)
blocks.add(my_block)
x += 15
y += 15
x = 0
self.c += 1
J_TEMPLATE = [['..O..',
'..O..',
'..O..',
'.OO..',
'.....']]
templates = {}
my_piece = Piece()
my_piece.insert(J_TEMPLATE)
pieces.append(my_piece)
mouse = Mouse()
while True:
screen.fill(BLACK)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
elif event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1:
bool = pygame.sprite.spritecollide(mouse, blocks, False)
if len(bool) > 0:
target = bool[0]
target.drag = True
for block in blocks:
if block.parent == target.parent: #if blocks are part of the same piece
block.drag = True
else:
for block in blocks:
block.drag = False
mouse.update_mouse()
for block in blocks:
if block.drag == True:
block.rect.x = mouse.rect.x + block.original_x
block.rect.y = mouse.rect.y + block.original_y
blocks.draw(screen)
pygame.display.update()
fpsClock.tick(FPS)
I'd do it in this way: If a block was clicked, assign its parent piece to a variable and calculate the offsets for the blocks like so: block.offset = block.rect.topleft - Vec(event.pos). At the top of the file you need to import from pygame.math import Vector2 as Vec. Now you can check for pygame.MOUSEMOTION events and move the blocks to the new event.pos + block.offset. The Mouse class is not needed anymore.
selected = None
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
elif event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1:
if selected: # If a piece was selected ...
selected = None # Deselect it.
else:
# Find a colliding block.
collided_sprites = [block for block in blocks
if block.rect.collidepoint(event.pos)]
if collided_sprites: # If we clicked a block ...
# Select the parent of the block.
selected = collided_sprites[0].parent
# Calculate the offsets for the blocks.
for block in selected.blocks[0]:
block.offset = block.rect.topleft - Vec(event.pos)
elif event.type == pygame.MOUSEMOTION:
if selected:
# Update the block positions by adding their offsets to the mouse pos.
for block in selected.blocks[0]:
block.rect.topleft = event.pos + block.offset
screen.fill(BLACK)
blocks.draw(screen)
pygame.display.update()
fpsClock.tick(25)

Pygame - How to stop an image from leaving the edge of the screen?

A section of jetfighterx leaves the screen when the mouse hovers over the edge of the window, this causes tarantula to explode from time to time as soon as it respawns to the top of the window, how can I stop this from happening (without the use of classes)?
Code:
import pygame, sys, pygame.mixer
from pygame.locals import *
import random
pygame.init()
bif = "space.jpg"
jf = "spacefightersprite.png"
enemy = "TarantulaSpaceFighter.png"
laser = pygame.mixer.Sound("LaserBlast.wav")
explosionsound = pygame.mixer.Sound("Explosion.wav")
screen = pygame.display.set_mode((1000,900),0,32)
caption = pygame.display.set_caption("Jet Fighter X")
background = pygame.image.load(bif).convert()
jetfighterx = pygame.image.load(jf)
jetfighterx = pygame.transform.scale(jetfighterx, (400,400))
tarantula = pygame.image.load(enemy)
tarantula = pygame.transform.scale(tarantula, (100,100))
laserblast = pygame.image.load("C:\Python27\laser.png")
explosion=pygame.image.load("C:\Python27\explosion.png")
explosion=pygame.transform.scale(explosion, (150,150))
ex,ey = 450,0
movex,movey = 0,0
clock = pygame.time.Clock()
speed = 300
shoot_y = 0
laser_fired = False
collision = False
alive = True
explo_timer = 25
while True:
pygame.mouse.set_visible(False)
mx,my = pygame.mouse.get_pos()
jetfighterx_rect = jetfighterx.get_rect(center=(mx, my))
jetfighterx_rect = jetfighterx_rect.inflate(-200,-200)
tarantula_rect = tarantula.get_rect(center=(ex, ey))
tarantula_rect = tarantula_rect.inflate(-180,-200)
# Check for player inputs
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == KEYDOWN:
if event.key == K_ESCAPE or event.key == K_q:
sys.exit()
if event.type == MOUSEBUTTONDOWN:
laser_fired = True
laser.play()
shoot_y = my-200
shoot_x = mx-16
# Update Game
milli = clock.tick()
seconds = milli/1000.
dmy = seconds * speed
ey += dmy
if ey > 900:
explo_timer = 25
collision = False
alive = True
ey = 0
ex = random.randint(50,900)
if laser_fired:
shoot_y -= 10
if shoot_y < 0:
laser_fired = False
else:
laserblast_rect = laserblast.get_rect(center=(shoot_x, shoot_y))
if laserblast_rect.colliderect(tarantula_rect):
explosionsound.play()
collision = True
alive = False
if jetfighterx_rect.colliderect(tarantula_rect) and alive:
explosionsound.play()
collision = True
alive = False
# Draw on screen
screen.blit(background, (0,0))
screen.blit(jetfighterx,(mx-200,my-200))
if not collision:
screen.blit(tarantula, (ex, ey))
elif collision:
explo_timer-=2
if explo_timer > 0 and alive == False:
screen.blit(explosion, (ex, ey-50))
if laser_fired:
screen.blit(laserblast, (shoot_x, shoot_y))
pygame.display.update()
Just add a limit that does not allow the fighter to move within x pixels of the border.
Assuming that the x,y coordinates of the centre of your fighter are jetfighter_x, jetfighter_y (You will need to change the variable names to whatever your code has) then write something like this:
LBuffer = 16
RBuffer = 1000 - 16
TBuffer = 900 - 16
BBuffer = 16
if jetfighter_x > RBuffer:
jetfighter_x = RBuffer
if jetfighter_x < LBuffer:
jetfighter_x = LBuffer
if jetfighter_y > TBuffer:
jetfighter_y = TBuffer
if jetfighter_y < BBuffer:
jetfighter_y = BBuffer
This should prevent the center of the ship from getting closer than 16 pixels from the edge. Obviously you will need to tweak this to accommodate the size of your ship. (The buffer for the sides would be the width of the image/2 .Respectively the buffer for the top and bottom would be the height of the image/2).

Categories