Referencing previous frames values Python Pygame - python

I have a really simple piece of code. What I would like to do as the program loops at 60 fps is I want to reference the previous mouse click state. i.e. I have 'one' as the variable for mouse click state, 0 for not clicked and 1 for clicked. What I want to happen is If the mouse is currently clicked i.e. one = 1 & the previous value of one was 0 i.e. unclicked then save the value of mx and my which are the mouse co-ordinates. Please see the code below:
PDimageFull = pygame.image.load('F:\Project files\coils\PDsinwaveResize.jpg')
PDresX = 300
PDresY = 720
gameDisplay = pygame.display.set_mode((Display_Width,Display_Height))
pygame.display.set_caption('PD diagnostic tool')
clock = pygame.time.Clock()
def PDimage(x, y):
gameDisplay.blit(PDimageFull, (x, y))
# Defining our main programing loop
def mainProgram_loop():
dx1 = (Display_Width-PDresX)
dy1 = (Display_Height-PDresY)
gameExit = False
# Event handling
while not gameExit:
mx, my = pygame.mouse.get_pos()
one, two, three = pygame.mouse.get_pressed()
for event in pygame.event.get():
if event.type == pygame.QUIT:
gameExit = True
# This expression controls the movement of the overall PD graph
# The section in the IF statement defines the boundary by which you can move the object
if one == 1 and mx > dx1 and mx < dx1 + PDresX and my > dy1 and my < dy1+PDresY:
dx1 = dx1 + (mx - PDresX)
dy1 = dy1 + (my - PDresY)
gameDisplay.fill(white)
PDimage(dx1, dy1)
pygame.display.update()
clock.tick(60)
mainProgram_loop()

Just use another variable:
prev_one = one
one, two, three = pygame.mouse.get_pressed()
if prev_one == 0 and one == 1:
print 'mouse was clicked this frame'
Be aware that you will have to initialise one with a default value at the start of your script.

Related

Generate tiles in piano tiles consecutively in pygame

I have created a simple piano tiles game clone in pygame.
Everything working fine except the way i am generating tiles after every certain interval, but as the game speed increases this leaves a gap between two tiles.
In the original version of the game, there's no lag (0 distance ) between two incoming tiles.
Here's a preview of the game:
Currently I am generating tiles like this:
ADDBLOCK = pygame.USEREVENT + 1
ADDTIME = 650
pygame.time.set_timer(ADDBLOCK, ADDTIME)
if event.type == ADDBLOCK:
x_col = random.randint(0,3)
block = Block(win, (67.5 * x_col, -120))
block_group.add(block)
But with time, these tiles speed increase so there's remain a gap between generation of two tiles as shown by red line in the preview. Is there any way to generate tiles consecutively?
Source Code
Use a variable number to know how many tiles have been generated since the start. This variable will start with 0, then you will add 1 to this variable every time a tile is generated.
Then, you can use a variable like scrolling which increases continuously. You will add this scrolling to every tile y pos to render them.
Now you just have to add a tile which y position is like -tile_height - tile_height * number.
If that doesn't make sense to you, look at this MRE:
import pygame
from pygame.locals import *
from random import randint
pygame.init()
screen = pygame.display.set_mode((240, 480))
clock = pygame.time.Clock()
number_of_tiles = 0
tile_height = 150
tile_surf = pygame.Surface((60, tile_height))
tiles = [] # [column, y pos]
scrolling = 0
score = 0
speed = lambda: 200 + 5*score # increase with the score
time_passed = 0
while True:
click = None # used to click on a tile
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
exit()
if event.type == MOUSEBUTTONDOWN:
click = event.pos
screen.fill((150, 200, 255))
if scrolling > number_of_tiles * tile_height:
# new tile
# use "while" instead of "if" if you want to go at really high speed
tiles.append([randint(0, 3), -tile_height - number_of_tiles * tile_height])
number_of_tiles += 1
for x, y in tiles:
screen.blit(tile_surf, (60 * x, y + scrolling))
if y + scrolling > 480: # delete any tile that is no longer visible
tiles.remove([x, y])
if click is not None and Rect((60 * x, y + scrolling), tile_surf.get_size())
.collidepoint(click):
tiles.remove([x, y]) # delete any tile that has been clicked
score += 1 # used to calculate speed
scrolling += speed() * time_passed
pygame.display.flip()
time_passed = clock.tick() / 1000

Pygame how to detect clicks from a list of images

For my levels screen I use images for each level button, there are 15 on a page. I was able to blit them all to the screen in a nice layout using some iteration and math, I have been trying to figure out a way to do this with the click detection aswell. But I do not want to type "var = get_rect" 15 times a page.
All of the images are in a list for iteration,
self.levels_p1 = [self.l1, self.l2, self.l3, self.l4, self.l5,
self.l6, self.l7, self.l8, self.l9, self.l10,
self.l11, self.l12, self.l13, self.l14, self.l15,]
and are added to the screen in a 5 x 3 like this,
for i, lvl in enumerate(self.levels_p1, start=1):
x = ((WIDTH - 100) * (i % 5) // 6) + 17
y = HEIGHT // 10 + ((i // 5) * 125)
if i % 5 == 0:
x = ((WIDTH - 100) * 5 // 6) + 17
y = HEIGHT // 10 + (((i // 5) - 1) * 125)
self.screen.blit(lvl, (x, y))
if self.level_unlocked < i:
self.screen.blit(self.level_shade, (x, y))
And I have been detecting clicks like this,
mousepos = pg.mouse.get_pos()
for event in pg.event.get():
if event.type == pg.QUIT:
pg.quit()
if event.type == pg.MOUSEBUTTONDOWN:
#to menu
if self.back_rect.collidepoint(*mousepos):
on_levels = False
self.swipe.play()
self.start()
#to levels2 page
if self.arrow_rect.collidepoint(*mousepos):
on_levels = False
self.swipe.play()
self.levels_page2()
#to level 1
if self.l1_rect.collidepoint(*mousepos):
on_levels = False
self.swipe.play()
self.load_level(1)
But that will only work for all the levels if I manually define a rect for all of the buttons, which is what I am trying to avoid.
I was wondering if there is a good way to detect the clicks on each and every one of these buttons? Similarly to how I displayed them in the 2nd code bit.
Much appreciated.
pygame.Surface.blit returns a rectangle with the area of the affected pixels:
self.rect = self.screen.blit(self.level_shade, (x, y))
Alternatively you can get the bounding rectangle of a pygame.Surface by get_rect(). The location of this rectangle is (0, 0). Hence, you have to set the position by a keyword argument:
self.rect = self.level_shade.get_rect(topleft = (x, y))
if self.arrow_rect.collidepoint(*mousepos)
I don't know what you're packing on the line above. If you want to detect clicks, I think you should get mouse position first. I'll do something like this:
for event in pg.event.get():
if event.type == pg.QUIT:
pg.quit()
if event.type == pg.MOUSEBUTTONDOWN:
#to menu
mousepos = pg.mouse.get_pos() #Here lies the magic and no need to unpack
if self.back_rect.collidepoint(mousepos):
on_levels = False
self.swipe.play()
self.start()
#to levels2 page
if self.arrow_rect.collidepoint(mousepos):
on_levels = False
self.swipe.play()
self.levels_page2()
#to level 1
if self.l1_rect.collidepoint(mousepos):
on_levels = False
self.swipe.play()
self.load_level(1)
If that solves your problem please let me know and of it doesn't still let me know!
Edit: Try if/elif block instead of just if.
mousepos = pg.mouse.get_pos()
lvls = []
for i, lvl in enumerate(self.levels_p1):
#x, y topleft for 5x3 grid with easement and centered
x = ((WIDTH - 100) * (i % 5) // 5) + 110
y = HEIGHT // 10 + ((i // 5) * 125)
#add to screen
temp = self.screen.blit(lvl, (x, y))
#if not unlocked
if self.level_unlocked < i:
#darken
self.screen.blit(self.level_shade, (x, y))
#if unlocked
else:
#enlarged version if hovering over and unlocked
if temp.collidepoint(*mousepos):
self.screen.blit(self.levels_1l[i], (x-6, y-6))
#rect list for click detection
lvls.append(temp)
#back button interactive
if self.back_rect.collidepoint(*mousepos):
self.screen.blit(self.t_back2, self.back_rect2) # bigger
else:
self.screen.blit(self.t_back, self.back_rect) # normal
#arrow button interactive
if self.arrow_rect.collidepoint(*mousepos):
self.screen.blit(self.arrowr2, self.arrow_rect2) # bigger
else:
self.screen.blit(self.arrowr, self.arrow_rect) # normal
pg.display.flip()
#all button presses
for event in pg.event.get():
if event.type == pg.QUIT:
pg.quit()
if event.type == pg.MOUSEBUTTONDOWN:
#to menu
if self.back_rect.collidepoint(*mousepos):
on_levels = False
self.swipe.play()
self.start()
#to levels2 page
if self.arrow_rect.collidepoint(*mousepos):
on_levels = False
self.swipe.play()
self.levels_page2()
#to level
for i, val in enumerate(lvls):
#if clicked pos = level pos
if lvls[i].collidepoint(*mousepos):
on_levels = False
self.swipe.play()
#level to load
self.load_level(i+1)

How adjust code that works only if I move the mouse

My code should be right, however it works only if I move my mouse, even if I didn't even set up one.
the code should draw concentric circles moving.
I have even tried by blocking the mouse, but it still doesn't work
import pygame
import math
import time
pygame.init()
screen = pygame.display.set_mode([800,600])
black = (0,0,0)
keep_going = True
white = (255,255,255)
freq = 10
num_circles = 0
radius = 0
while keep_going:
for event in pygame.event.get():
if event.type == pygame.QUIT:
keep_going = False
radius = radius + 1
num_circles = math.ceil(radius / freq)
screen.fill(white)
radiusMax = num_circles * freq
pace = freq / radiusMax
for y in range(num_circles, 1, -1):
radiusY = int(((pace * (num_circles - y)) + pace) * radiusMax) + (radius % freq)
pygame.draw.circle(screen, black,(400, 300), radiusY, 1)
pygame.display.update()
pygame.quit()
I would like to know why the hell I get this kind of bug, how to solve it, and if it's not possible, a possible solution.
The issue is caused, because you do all the code in the event loop. The code in the event loop is executed only, when an event occurs, such as mouse movement (pygame.MOUSEMOTION).
Do the drawing in the main loop rather than the event loop, to solve the issue:
while keep_going:
for event in pygame.event.get():
if event.type == pygame.QUIT:
keep_going = False
#<--
#<--
#while input() != "quit":
#for x in range(1): #importante
raggio = raggio + 1
num_cerchi = math.ceil(raggio / freq)
screen.fill(white)
raggioMax = num_cerchi * freq
passo = freq / raggioMax
#time.sleep(5)
for y in range(num_cerchi, 1, -1):
# 1, -1
raggioY = int(((passo * (num_cerchi - y)) + passo) * raggioMax) + (raggio % freq)
pygame.draw.circle(screen, black,(400, 300), raggioY, 1)
#raggio = raggio+1 #importante
clock.tick(60)
pygame.display.update()
pygame.quit()

I get an incorrect position from .get_rect()?

I'm currently working on a school project where I'm making a "hexcells" similar game in pygame and now I'm trying to blit an a new image if the user has clicked a current image. It will blit an image in the top left area, if clicked in the top left area, but not if I click any of the existing images. I told the program to print the coordinates from the images with help of the .get_rect() function, but it remains the same whereever I click and the coordinates aren't even where a image is. Can someone help me understand how this works and help me blit the new images on top of the existing images? Code below is not the entire document, however there is so much garbage/trash/unused code so I'd thought I spare you the time of looking at irrelevant code. Also sorry if the formatting is wrong or the information isn't enough, I tried my best.
import pygame, sys
from pygame.locals import *
#Magic numbers
fps = 30
winW = 640
winH = 480
boxSize = 40
gapSize = 75
boardW = 3
boardH = 3
xMargin = int((winW - (boardW * (boxSize + gapSize))) / 2)
yMargin = int((winW - (boardW * (boxSize + gapSize))) / 2)
#Lil bit o' color R G B
NAVYBLUE = ( 60, 60, 100)
correctCords = [[175,275,375],[375,275,175]]
bgColor = NAVYBLUE
unC = pygame.image.load("unC.png")
cor = pygame.image.load("correct.png")
inc = pygame.image.load("wrong.png")
correct = "Correct"
inCorrect = "Incorrect"
def main():
global FPSCLOCK, DISPLAYSURF
pygame.init()
FPSCLOCK = pygame.time.Clock()
DISPLAYSURF = pygame.display.set_mode((winW, winH))
mousex = 0 #stores x-coordinate of mouse event
mousey = 0 #stores y-coordinate of mouse event
pygame.display.set_caption("Branches")
DISPLAYSURF.fill(bgColor)
gridGame(inCorrect, correct,gapSize,xMargin,yMargin,boxSize)
while True:
mouseClicked = False
for event in pygame.event.get():
if event.type == QUIT or (event.type == KEYUP and event.key == K_ESCAPE):
pygame.quit()
sys.exit()
elif event.type == MOUSEMOTION:
mousex,mousey = event.pos
elif event.type == MOUSEBUTTONUP:
mousex, mousey = event.pos
pos = pygame.mouse.get_pos()
mouseClicked = True
unCa = unC.get_rect()
corA = cor.get_rect()
print unCa
print corA
print pos
if unCa.collidepoint(pos):
DISPLAYSURF.blit(cor,(mousey,mousex))
"""lada = unC.get_rect()
lada =
if mousex and mousey == lada:
for x in correctCords:
for y in x:
for z in x:
if mousey and mousex == z and y:
DISPLAYSURF.blit(cor,(mousey,mousex))
print lada"""
pygame.display.update()
FPSCLOCK.tick(fps)
def gridGame(inCorrect, correct,gapSize,xMargin,yMargin,boxSize):
grid = []
cordX = []
cordY = []
correctRecs = []
#cordinates = []
#cordinates.append([])
#cordinates.append([])
#cordinates.append([])
#this is basically getBoard() all over again
#This part will arrange the actual backend grid
for row in range(3):
grid.append([])
#cordinates[0].append(gapSize+(row+1)*100)
#cordinates[1].append(gapSize+(row+1)*100)
#cordinates[2].append(gapSize+(row+1)*100)
for column in range(3):
grid[row].append(inCorrect)
for row in range(3):
cordX.append([])
for column in range(3):
cordX[row].append(gapSize+(row+1)*100)
for row in range(3):
cordY.append([])
for column in range(3):
cordY[row].append(gapSize+(column+1)*100)
#print cordX[0][0], cordY[0][0]
grid[0][2] = correct
grid[1][1] = correct
grid[2][0] = correct
#Y-AXEL SKRIVS FoRST ([Y][X])
#print cordinates[2][1]
DISPLAYSURF.blit(cor,(100,100))
#Let's draw it as well
for row in range(3):
for column in range(3):
DISPLAYSURF.blit(unC,(gapSize+(row+1)*100,gapSize+(column+1)*100))
main()
Also real sorry about the horrible variable naming and occasional swedish comments.
unCa = unC.get_rect() gives you only image size - so use it only once at start (before while True) - and later use the same unCa all the time to keep image position and change it.
btw: better use more readable names - like unC_rect
ie.
# move 10 pixel to the right
unC_rect.x += 10
# set new position
unC_rect.x = 10
unC_rect.right = 100
unC_rect.topleft = (10, 200)
unC_rect.center = (10, 200)
# center on screen
unC_rect.center = DISPLAYSURF.get_rect().center
etc.
And then use this rect to blit image
blit(unC, unC_rect)
and check collision with other rect
if unC_rect.colliderect(other_rect):
or with point - like mouse position
elif event.type == MOUSEMOTION:
if unC_rect.collidepoint(pygame.mouse.get_pos()):
hover = True
# shorter
elif event.type == MOUSEMOTION:
hover = unC_rect.collidepoint(pygame.mouse.get_pos()):

Pygame rotation trouble

Recently began a new pygame project, asteroids. I've been working on getting an image to rotate in the direction of the cursor, which i'm finding irritatingly hard. Any help is appreciated, here is the code for the rotation so far:
import sys, pygame, math, time;
from pygame.locals import *;
spaceship = ('spaceship.png')
mouse_c = ('crosshair.png')
backg = ('background.jpg')
fire_beam = ('beam.png')
pygame.init()
screen = pygame.display.set_mode((800, 600))
bk = pygame.image.load(backg).convert_alpha()
mousec = pygame.image.load(mouse_c).convert_alpha()
space_ship = pygame.image.load(spaceship).convert_alpha()
f_beam = pygame.image.load(fire_beam).convert_alpha()
clock = pygame.time.Clock()
pygame.mouse.set_visible(False)
x, y = 357, 300 #position of space_ship, (line 38 btw, second from bottom)
while True:
screen.blit(bk, (0, 0))
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
elif event.type == MOUSEBUTTONDOWN and event.button == 1:
print("Left Button Pressed")
elif event.type == MOUSEBUTTONDOWN and event.button == 3:
print("Right Button Pressed")
if event.type == MOUSEMOTION:
clock.tick(60)
x1, y1 = pygame.mouse.get_pos()
x2, y2 = x, y
dx, dy = x2 - x1, y2 - y1
rads = math.atan2(dx, dy)
degs = math.degrees(rads)
pygame.transform.rotate(space_ship, (degs))
print degs #Prints correct output..
pygame.display.update() #the image flickers, but does not rotate
pos = pygame.mouse.get_pos()
screen.blit(mousec, (pos))
screen.blit(space_ship, (375, 300))
pygame.display.update()
From the documentation: "A Surface transform is an operation that moves or resizes the pixels. All these functions take a Surface to operate on and return a new Surface with the results."
As it is, you operate the transform, but throw away the result.
You could just change the line
pygame.transform.rotate(space_ship, (degs))
to:
space_ship = pygame.transform.rotate(space_ship, (degs))
In order to see it working, but it would not be a good :
you'd need to re-calculate your degrees to have only the difference
in degrees from one iteration to the next - but worse, successive raster-picture
transforms would degenerate your spaceship into an amorphous blob of pixels very fast.
The right thing to do is to keep a reference to your original spacehsip pictures, and always rotate that instead.
So, before your main loop, you do:
space_ship_image = pygame.image.load(spaceship).convert_alpha()
And inside the loop, the aforementioned:
space_ship = pygame.transform.rotate(space_ship_image, (degs))
BTW, raster artifacts being what they are, yo probably will want to use a larger
version of your ship in your "spaceship.png" file, and use "rotozoom" instead of "rotate",
scaling down as you rotate the ship to get your final image.

Categories

Resources