Blinking rectangle in pygame - python

I am trying to learn pyGame. And I ran into a problem. I have a rectangle which can be moved with arrow buttons. Then I created another thread which generates smaller rectangles which can be picked up. But when I run my game, the small generated rectangles blink too much. How can I make them be stable? I think I dont understand time concept here well. Could someone explain me etc
My code:
import pygame
import random
import threading
import thread
import sys
import time
pygame.init()
screen = pygame.display.set_mode((400, 300))
done = False
is_blue = True
entityList = []
x = 30
y = 30
clock = pygame.time.Clock()
class Entity():
def __init__(self, x, y):
self.x = x
self.y = y
def getX(self):
return self.x
def getY(self):
return self.y
def drawStuff(entityList):
# pygame.draw.rect(screen, (255, 100, 0), pygame.Rect(55, 45, 10, 10))
for x in range (0, entityList.__len__()):
pygame.draw.rect(screen, (255, 100, 0), pygame.Rect(entityList[x].getX(), entityList[x].getY(), 10, 10))
pygame.display.flip()
clock.tick(60)
class EntityManager(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
while True:
entityList = generateEntities()
drawStuff(entityList)
def endJob(self):
thread.exit()
time.sleep(2)
def detect_collision(x,y):
if x > 340:
x -= 1
if y > 240:
y -= 1
if y < 0:
y += 1
if x < 0:
x += 1
return x,y
def generateEntities():
itemlist = []
for x in range (0,4):
x = random.randint(1,339)
y = random.randint(1,239)
entity = Entity(x,y)
itemlist.append(entity)
return itemlist
entityList = generateEntities()
a = EntityManager()
a.setDaemon(True)
a.start()
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE:
is_blue = not is_blue
pressed = pygame.key.get_pressed()
if pressed[pygame.K_UP]:
y -= 1
x,y = detect_collision(x, y)
if pressed[pygame.K_DOWN]:
y += 1
x,y = detect_collision(x, y)
if pressed[pygame.K_LEFT]:
x -= 1
x,y = detect_collision(x, y)
if pressed[pygame.K_RIGHT]:
x += 1
x,y = detect_collision(x, y)
screen.fill((0, 0, 0))
if is_blue: color = (0, 128, 255)
else: color = (255, 100, 0)
pygame.draw.rect(screen, color, pygame.Rect(x, y, 60, 60))
pygame.display.flip()
clock.tick(60)

clock.tick() actually means FPS (frames per second); instead of changing it, change another variable.
Watch this tutorial, it might help you understand FPS.
Issues about smoothness are sometimes about the size of your object and how many pixels you are moving it when key events are True. Be careful about that; imagine you have a little box and you are trying to move it. If you move it with big steps, it seems 'laggy', but if you move it with little steps then it may look smoother. Hope you understand what I mean.
For your second question please check this documentation.

Okay, I figured it out now. OMG, I feel such an idiot after this. But I forgive myself that I am also doing parallell learning of Python. I used to code in Java more.
Anyway: problem was that my drawStuff function drew things in function but when it left this function, it did nothing. Changes were made locally. I solved this problem by using global keyword. I made in my code changes that thread computes entity coordinates and then main loop does update. This leads me to another idea that maybe there should be some game info object which gets updated all the time and all rectangles info go to that game info object.
Anyway, soon I will edit this post code too to see what i did

Related

How to create and move 2 or more objects simultaneously

I was trying to create a function that will load an image and move the image downwards while loading another image and still moving with the same speed as the first. But I don't know how to do it, all I can do for now is to load the next image when the y coordinate of the first is at 500
def aliens():
s = 0
i = "345678"
c = 1
b = int("".join(random.sample(i, c)))
h = 1
while h < 2:
while s <= b:
alien = pygame.image.load("ali.jpg")
alien = pygame.transform.scale(alien, (100, 100))
pos = "0123456789"
l = 3
x = int("".join(random.sample(pos, l)))
y = 0
if x > 500:
h = 3
crash = False
s +=1
while not crash :
scn.blit(alien, (x, y))
y +=2
pygame.display.flip()
if y > 500:
crash = True
The core of your problem is the fact that you are trying to do too many things in the same function. Try to follow the SRP (Single Responsability Principle).
Personnally I would use OOP (Object Oriented Programming) here but I don't know if you are familar with the concept, so first let's stick to the basics.
Here is how I would separate things up, if I wasn't allowed to use OOP:
First I would define two constants:
ALIEN_SIZE = 100
ALIEN_IMG = pg.image.load("green_square.png").convert()
ALIEN_IMG = pg.transform.scale(ALIEN_IMG, (ALIEN_SIZE, ALIEN_SIZE))
Since you are always using the same values, there is no need to load your image all the time. Loading your image just once will have a very positive impact on game's performances.
(Note that I used a diffrent image since you did not provide the image you use but feel free to put the link to your image back)
Then I would define an alien list:
aliens = []
I don't know how you plan to spawn aliens in your project ? For this example I used a custom event with a timer:
# Custom event
SPAWN_ALIEN = pg.USEREVENT
pg.time.set_timer(SPAWN_ALIEN, 2000)
# This function handles all pygame events
def handle_evt():
for evt in pg.event.get():
if evt.type == pg.QUIT:
exit()
if evt.type == SPAWN_ALIEN:
spawn_alien()
# This function spawns an alien
def spawn_alien():
pos = [rd.randint(0, screen.get_width() - ALIEN_SIZE), 0]
aliens.append(pos)
To make aliens move, I use a update() function:
# update aliens position and check if they leave the screen
def update():
for alien in aliens:
move_alien(alien)
if out_of_screen(alien):
kill_alien(alien)
# This function update alien's y position
def move_alien(alien):
alien[1]+=1
# This function remove the given alien from the aliens list
def kill_alien(alien):
aliens.remove(alien)
# This function returns True if the given position is outside screen's boundries
def out_of_screen(pos):
x, y = pos
if x < 0 or x > screen.get_width():
return True
elif y < 0 or y > screen.get_height():
return True
return False
Finally to render things :
def draw():
# Clear screen
screen.fill((0,0,0))
# Draw aliens
for alien in aliens:
screen.blit(ALIEN_IMG, alien)
Here is the full code, so you can see how everything is interacting :
# General imports
import pygame as pg
import random as rd
import sys
# Init
pg.init()
# Vars & consts
screen = pg.display.set_mode((500, 500))
pg.display.set_caption("Example")
FPS = 60
clock = pg.time.Clock()
ALIEN_SIZE = 100
ALIEN_IMG = pg.image.load("green_square.png").convert()
ALIEN_IMG = pg.transform.scale(ALIEN_IMG, (ALIEN_SIZE, ALIEN_SIZE))
# Custom event
SPAWN_ALIEN = pg.USEREVENT
pg.time.set_timer(SPAWN_ALIEN, 2000)
# Main functions
def update():
for alien in aliens:
move_alien(alien)
if out_of_screen(alien):
kill_alien(alien)
def draw():
# Clear screen
screen.fill((0,0,0))
# Draw aliens
for alien in aliens:
screen.blit(ALIEN_IMG, alien)
def handle_evt():
for evt in pg.event.get():
if evt.type == pg.QUIT:
exit()
if evt.type == SPAWN_ALIEN:
spawn_alien()
def exit():
pg.quit()
sys.exit()
# Other functions
def spawn_alien():
pos = [rd.randint(0, screen.get_width() - ALIEN_SIZE), 0]
aliens.append(pos)
def move_alien(alien):
alien[1]+=1
def kill_alien(alien):
aliens.remove(alien)
def out_of_screen(pos):
x, y = pos
if x < 0 or x > screen.get_width():
return True
elif y < 0 or y > screen.get_height():
return True
return False
# Main loop
if __name__ == '__main__':
aliens = []
spawn_alien()
while True:
handle_evt()
update()
draw()
clock.tick(FPS)
pg.display.flip()
And just in case, here is an OOP approach :
# General imports
import pygame as pg
import random as rd
import sys
# Init
pg.init()
# Vars
screen = pg.display.set_mode((500, 500))
pg.display.set_caption("Example")
FPS = 60
clock = pg.time.Clock()
# Class
class Alien():
LIST = []
SIZE = 100
IMG = pg.image.load("green_square.png").convert()
IMG = pg.transform.scale(IMG, (SIZE, SIZE))
def __init__(self, screen_width):
self.img = Alien.IMG
self.pos = (rd.randint(0, screen_width - Alien.SIZE), 0)
# Add alien to the list
Alien.LIST.append(self)
def move(self):
x, y = self.pos
self.pos = (x, y+1)
def has_crashed(self, boundries):
x, y = self.pos
if x < 0 or x > boundries[0]:
return True
elif y < 0 or y > boundries[1]:
return True
return False
def kill(self):
Alien.LIST.remove(self)
# Custom event
SPAWN_ALIEN = pg.USEREVENT
pg.time.set_timer(SPAWN_ALIEN, 2000)
# Main functions
def update():
for alien in Alien.LIST:
alien.move()
if alien.has_crashed(screen.get_size()):
alien.kill()
def draw():
# Clear screen
screen.fill((0,0,0))
# Draw aliens
for alien in Alien.LIST:
screen.blit(alien.img, alien.pos)
def handle_evt():
for evt in pg.event.get():
if evt.type == pg.QUIT:
exit()
if evt.type == SPAWN_ALIEN:
spawn_alien()
def exit():
pg.quit()
sys.exit()
# Other functions
def spawn_alien():
Alien(screen.get_width())
# Main loop
if __name__ == '__main__':
while True:
handle_evt()
update()
draw()
clock.tick(FPS)
pg.display.flip()
I hope all those informations can help you solve your problem. It's hard to know if this, really is the behavior you wanted for your aliens. That's because your variables names are not exactly explicit. One letter variable names are rarely a good idea. But anyway even if you need them to move a little diffrently, you can take inspiration of this code organization.
Have fun !

Can't figure out how to make background scenes run in Pygame when "levels" change

I am new to python and wanted to create a game to test some knowledge. Please bare with my mistakes. I would also really appreciate an example written out if possible so I can wrap my head around the concept.
I couldn't figure out how to post the entire code without formatting nightmares so I put it in github sorry if this is against terms!
I removed most of the dialogue because it was very very long.
Some background about the same:
It was written as a text-based only game first, then I heard about pygame wanted to figure out how to put the game into the window. So first I created all of the code, which ran perfectly fine even with sound effects / music, but then after trying to figure out pygame's game loop, I can't figure out how to change the background image.
I have 5 scenes that I want 5 images for, that change when I change the scene with user input. Even now, the music changes, but the background images don't change after blitting the first one in the 'naming' scene. I don't really understand how the pygame loop works in conjunction with the game engine I have currently. I know it's looping through the pygame loop as I tested it with printing things, but when trying to add images or saying if scene == naming: blit image, it doesn't work. I also tried putting the images into each of the scenes. I also tried putting the images into a class called background with all 5 scenes and images to each. Can't figure it out.
Any help would be greatly appreciated!
-M
Here is an example of how to use scenes. This is not the only way of doing it, but i though it would be easy to use/understand. I also noticed in your code that you used input(). which is annoying if your using a window, so i also added a button and a textbox input that kinda works.
import pygame as pg
pg.init() # initialize the pg
win = pg.display.set_mode((1080, 720)) # create the game window
pg.display.set_caption("Puppy Love")
white = (255,255,255)
black = (0,0,0)
clock = pg.time.Clock()
fps = 60
class Scene:
def __init__(self):
self.sceneNum = 1
def Scene_1(self):
win.fill(white)
if next_scene_button.draw(): #if clicked button
self.sceneNum += 1
name_Box.Draw()
#anything else you want to draw
def Scene_2(self):
win.fill((0,0,255))
if next_scene_button.draw():
self.sceneNum = 1
#...
def Draw_Scene(self):
if self.sceneNum == 1:
self.Scene_1()
elif self.sceneNum == 2:
self.Scene_2()
class Button:
def __init__(self,x,y,w,h,text):
self.x = x
self.y = y
self.w = w
self.h = h
self.text = text
self.font = pg.font.Font(pg.font.match_font("Calibri"),20)
def draw(self):
button_clicked = False
col = (150,150,150)
if mouse_pos[0] > self.x and mouse_pos[0] < self.x + self.w:
if mouse_pos[1] > self.y and mouse_pos[1] < self.y + self.h:
col = (200,200,200)
if click:
button_clicked = True
pg.draw.rect(win,col,(self.x,self.y,self.w,self.h))
obj = self.font.render(self.text,True, black)
win.blit(obj,(self.x + (self.w-obj.get_width())//2,self.y + (self.h-obj.get_height())//2)) #center the text
return button_clicked #return if the button was clicked or not
class TextBox:
def __init__(self,x = 0, y = 0, w = 0, h = 0, text = "", background = False, font_size = 30, font = "Calibri", text_colour = (0,0,0)):
self.x = x
self.y = y
self.w = w
self.h = h
self.text_colour = text_colour
self.text = text
self.background = background
self.font = pg.font.Font(pg.font.match_font(font),font_size)
def Add_char(self,key):
if key == 8:
self.text = self.text[:-1]
else:
char = pg.key.name(key)
self.text += char
def Draw(self):
if self.background:
pg.draw.rect(win, self.background, (self.x,self.y,self.w,self.h))
obj = self.font.render(self.text,True,self.text_colour)
win.blit(obj,(self.x,self.y))
def Get_pos(self):
return (self.x,self.y)
name_Box = TextBox(x=100, y=200, w=150, h=40, background=(200,200,200))
Scenes = Scene()
next_scene_button = Button(400,400,100,50,"next scene")
click = False
mouse_pos = [0,0]
running = True
while running:
clock.tick(fps)
Scenes.Draw_Scene()
mouse_pos = pg.mouse.get_pos()
click = False
pg.display.update() #no diference between .flip() and .update()
for event in pg.event.get():
if event.type == pg.QUIT:
pg.quit()
running = False
if event.type == pg.MOUSEBUTTONDOWN:
click = True
if event.type == pg.KEYDOWN:
if Scenes.sceneNum == 1:
name_Box.Add_char(event.key)
The main thing to take away from this is to only use one pygame.display.flip(). Hopefully you can use this to improve your program

Pygame Rectangle and Mouse pos Collidepoint not working

I'm making a basic game where I have a surface and everytime I click on the surface it moves 5 pixels to the right. The program is working just fine without the checkCollide(event) function, but when I put the that condition it doesn't move. What is wrong?
My code until now is this
import pygame, sys
from pygame.locals import *
pygame.init()
DISPLAYSURF = pygame.display.set_mode((300,300))
def checkCollide(event):
k = 0
a,b = event.pos
x = P1[0].get_rect()
if x.collidepoint(a,b):
return True
return False
CP1 = [(150, 150)
,(155, 150)
,(160, 150)
,(165, 150)
,(170, 150)
,(175, 150)
,(180, 150)
,(185, 150)
,(190, 150)]
statp1_1 = 0
WHITE = (255,255,255)
DISPLAYSURF.fill(WHITE)
while True: # the main game loop
P1 = [pygame.image.load('PAzul.png'),CP1[statp1_1],statp1_1]
DISPLAYSURF.blit(P1[0], P1[1])
e = pygame.event.get()
for event in e:
if event.type == MOUSEBUTTONUP:
a = checkCollide(event)
if a:
DISPLAYSURF.fill(WHITE)
statp1_1 +=1
if event.type == QUIT:
pygame.quit()
sys.exit()
pygame.display.update()
Thank you
Check your logic in these lines of your function:
x = P1[0][0].get_rect()
if x.collidepoint(a,b):
return True
return False
Your code hinges on this bit:
a = checkCollide(event)
if a:
DISPLAYSURF.fill(WHITE)
So you're never evaluating this piece to be true.
I just realized what was wrong. When I do x = P1[0].get_rect() it creates a surface with topleft at (0,0).
What I needed to do was change the position of the rectangle using x.topleft = P1[1]
I've got some tips for you. First store the rect in the P1 list (it contains only the image and the rect in the following example, but maybe you could also add the statp1_1 index to it). Now we can just move this rect, if the user clicks on it (in the example I set the topleft attribute to the next point). Read the comments for some more tips. One thing you need to fix is to prevent the game from crashing when the statp1_1 index gets too big.
import sys
import pygame
pygame.init()
DISPLAYSURF = pygame.display.set_mode((300, 300))
WHITE = (255, 255, 255)
# Don't load images in your while loop, otherwise they have to
# be loaded again and again from your hard drive.
# Also, convert loaded images to improve the performance.
P1_IMAGE = pygame.image.load('PAzul.png').convert() # or .convert_alpha()
# Look up `list comprehension` if you don't know what this is.
CP1 = [(150+x, 150) for x in range(0, 41, 5)]
statp1_1 = 0
# Now P1 just contains the image and the rect which stores the position.
P1 = [P1_IMAGE, P1_IMAGE.get_rect(topleft=CP1[statp1_1])]
clock = pygame.time.Clock() # Use this clock to limit the frame rate.
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.MOUSEBUTTONUP:
if P1[1].collidepoint(event.pos):
print('clicked')
statp1_1 += 1
# Set the rect.topleft attribute to CP1[statp1_1].
P1[1].topleft = CP1[statp1_1]
DISPLAYSURF.fill(WHITE)
DISPLAYSURF.blit(P1[0], P1[1]) # Blit image at rect.topleft.
pygame.display.update()
clock.tick(30) # Limit frame rate to 30 fps.

Pygame Tetris Issue

I've been writing a very rudimentary version of Tetris for Pygame in python 3.6. The only features so far are the blocks falling, making them fall faster, moving left and right, and when a block hits the ground, a new one spawns.
However, there is the issue. When the first block hits the ground, blocks spawn indefinitely at the top of the screen. I scoured the code and also showed it to my friend, and we couldn't find the issue. I scrapped the code and rewrote it, and the problem persisted. Does anyone here see it?
Thanks
P.S.
I'm pretty sure that the top 2/3 of the code is not the issue.
import pygame, random
screen = pygame.display.set_mode((400,600))
pygame.display.set_caption("Tetris")
done = False
fast = False
locked = False
fallingblocks = []
setblocks = []
clock = pygame.time.Clock()
fallcooldown = 0
class Block:
def __init__(self, x, y, color):
self.x = x
self.y = y
self.color = color
fallingblocks.append(self)
self.rect = pygame.Rect(self.x + 1, self.y, 23, 25) #game only cares if falling block collides on the top or bottom, not side
def fall(self):
self.y += 25
def move(self):
if pressed[pygame.K_a] or pressed[pygame.K_LEFT]: self.x -= 25
if pressed[pygame.K_d] or pressed[pygame.K_RIGHT]: self.x += 25
def drawblock(self): #just to make it look nice
pygame.draw.rect(screen, self.color[0], pygame.Rect(self.x,self.y,25,25))
pygame.draw.polygon(screen, self.color[1], ((self.x,self.y),(self.x+3,self.y+3),(self.x+21,self.y+3),(self.x+24,self.y)))
pygame.draw.polygon(screen, self.color[2], ((self.x,self.y),(self.x+3,self.y+3),(self.x+3,self.y+21),(self.x,self.y+24)))
pygame.draw.polygon(screen, self.color[3], ((self.x,self.y+24),(self.x+3,self.y+21),(self.x+21,self.y+21),(self.x+24,self.y+24)))
pygame.draw.polygon(screen, self.color[4], ((self.x+24,self.y+24),(self.x+21,self.y+21),(self.x+21,self.y+3),(self.x+24,self.y)))
def spawn():
blocknum = random.randint(0,6)
if blocknum == 0:
#I block
colors = [(129,184,231),
(179,223,250),
(146,202,238),
(76,126,189),
(96,157,213)]
Block(175,0,colors)
Block(175,25,colors)
Block(175,50,colors)
Block(175,75,colors)
elif blocknum == 1:
#J block
colors = [(77,110,177),
(149,178,229),
(104,145,203),
(49,63,136),
(63,85,158)]
Block(200,0,colors)
Block(200,25,colors)
Block(200,50,colors)
Block(175,50,colors)
elif blocknum == 2:
#L block
colors = [(219,127,44),
(243,191,122),
(229,158,69),
(166,71,43),
(193,98,44)]
Block(175,0,colors)
Block(175,25,colors)
Block(175,50,colors)
Block(200,50,colors)
elif blocknum == 3:
#O block
colors = [(248,222,49),
(246,243,139),
(245,235,86),
(183,160,54),
(213,190,55)]
Block(175,0,colors)
Block(175,25,colors)
Block(200,0,colors)
Block(200,25,colors)
elif blocknum == 4:
#S block
colors = [(156,195,76),
(204,218,127),
(174,208,79),
(109,157,75),
(140,183,93)]
Block(175,0,colors)
Block(175,25,colors)
Block(200,0,colors)
Block(150,25,colors)
elif blocknum == 5:
#Z block
colors = [(204,42,40),
(226,138,132),
(213,90,69),
(151,34,42),
(181,37,43)]
Block(175,0,colors)
Block(225,25,colors)
Block(200,0,colors)
Block(200,25,colors)
else:
#T block
colors = [(147,68,149),
(187,145,194),
(156,101,167),
(108,45,123),
(128,47,135)]
Block(175,0,colors)
Block(175,25,colors)
Block(200,0,colors)
Block(150,0,colors)
spawn()
#Pretty sure that everything above here is not the issue
while not done: #main loop
screen.fill((0,0,32))
pressed = pygame.key.get_pressed()
for fallingblock in fallingblocks:
fallingblock.drawblock()
fallingblock.move()
for setblock in setblocks:
setblock.drawblock()
if fallcooldown >= 50: #makes all pieces fall at once
for fallingblock in fallingblocks:
fallingblock.fall()
fallcooldown = 0
pygame.display.flip()
if pressed[pygame.K_SPACE]: #if you want the piece to go the ground instantly
fast = True
if fast: fallcooldown = 50 #falling movements
elif pressed[pygame.K_DOWN]: fallcooldown += 8 #goes faster
else: fallcooldown += 1 #default speed
for fallingblock in fallingblocks:
for setblock in setblocks:
if fallingblock.rect.colliderect(setblock.rect): #if fallingblock collides with setblock
locked = True
if fallingblock.y >= 575 and not locked: #if block hits the bottom
locked = True
if locked: #if block is in final state
setblocks += fallingblocks
fallingblocks = []
spawn()
locked = False
clock.tick(50)
pygame.display.flip()
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
The problem is caused in the fall method of the Block. You only change the y attribute but never move the rect of the block, so it actually stays at the top of the screen the whole time. So remove the x, y attributes and just use self.rect.x and self.rect.y, or alternatively set self.rect.y = self.y.
To debug the code I first printed print(locked, len(fallingblocks), len(setblocks)) above the if locked: line to confirm that locked was always True after the first block touched the ground. Then I tried to comment out the collision detection with the setblocks and the continuous spawning stopped. The next step was to print the rects of the setblocks and fallingblocks and it revealed that the y-pos of the rects was always 0 or 25 and never changed. I looked at the movement code in the fall method and noticed that the rect doesn't get moved.
There are more issues, but I think you should try to continue with the debugging first and ask new questions if you still have trouble.

How to make a mouse event for clicks on a masked image [Pygame]

So I'm really new to programming and I'm working on a game that is sort of like Cookie Clicker, but with a twist (Mining). It is made in python/pygame. Anyways I have an image of a boulder and I want to add a rock to my inventory in the game every time i click on it.
Someone helped me out setting up the point_collide in my class. I will confess and say I don't fully understand how it works but its supposed to detect if my mouse is on a non-transparent part of my rock image.
I want the game to only give you a rock if your clicking on the NON-transparent parts of the boulder image I have blitted to the middle of the screen.
Short Question: How can I setup the game to register clicks only on my masked image?
PS: I know it'd be better to learn the fundamentals of programming first, but I have learned so much just by diving straight into a project (it keeps me going and alot more fun then reading a book).
Link to code: https://www.refheap.com/88634
The Code:
import pygame, sys
from pygame.locals import *
from datetime import datetime
if (__name__ == "__main__"):
pygame.init()
pygame.font.init()
pygame.display.set_caption("Miner Click")
clock = pygame.time.Clock()
screen = pygame.display.set_mode((960,600))
width = 960
height = 600
GREEN = (0,255,0)
RED = (255,0,0)
BLUE = (0,0,255)
BLACK = (0,0,0)
WHITE = (255,255,255)
BROWN = (84,27,1)
GREY = (198,198,198)
greenbg = pygame.image.load("greenbg.jpg").convert()
rockbutton = pygame.image.load("rockbutton.png").convert_alpha()
woodbutton = pygame.image.load("woodbutton.png").convert_alpha()
pygame.mouse.set_visible(True)
pick = pygame.image.load("pick.png").convert_alpha()
axe = pygame.image.load("axesmall.png").convert_alpha()
rockwidth = 544
rockheight = 274
clicks = 0
wood = 0
stonefont = pygame.font.SysFont("verdana", 29, True)
woodfont = pygame.font.SysFont("verdana", 29, True)
clicktext = stonefont.render('Rock: ' +str(clicks), 2, (GREY))
woodtext = woodfont.render('Wood: ' +str(wood), 2, (BROWN))
boxsize = clicktext.get_rect()
RocksX = 125
WoodX = 113
class Rock(pygame.sprite.Sprite):
def __init__(self, color = BLUE, width = 544, height = 274):
super(Rock, self ).__init__()
self.image = pygame.Surface((width, height))
self.set_properties()
self.image.fill(color)
def set_properties(self):
self.rect = self.image.get_rect()
self.origin_x = self.rect.centerx
self.origin_y = self.rect.centery
def set_position(self, x, y):
self.rect.x = 250
self.rect.y = 230
def set_image(self, filename = None):
if (filename != None):
self.image = pygame.image.load(filename).convert_alpha()
def point_collide(self, point):
x, y = point
x -= self.rect.x
y -= self.rect.y
try:
return self.mask.get_at((x,y))
except IndexError:
return False
#below is my clueless attempt at getting it to work
def checkForCursorPressed(x,y):
if pygame.mouse.get_pressed() and pygame.mouse.get_pos() == (x,y):
clicks+=1
coordfont = pygame.font.SysFont("verdana", 12, True)
rock_group = pygame.sprite.Group()
rock = Rock()
rock.set_image("rock.png")
rock.set_position(width/2, height/2)
rock_group.add(rock)
while True:
clock.tick(60)
screen.fill((255,255,255))
screen.blit(greenbg, (0,0))
x,y = pygame.mouse.get_pos()
coords = x,y
now = datetime.now()
date = '%s/%s/%s' % (now.month, now.day, now.year)
label = coordfont.render("Coordinates: "+str(coords), 1, (GREY))
date = coordfont.render("Date: "+str(date), 1, (GREY))
screen.blit(date, (650,10))
screen.blit(label, (790, 10))
screen.blit(rockbutton, (25,25))
screen.blit(woodbutton, (25,100))
clicktext = stonefont.render(' ' +str(clicks), 2, (GREY))
woodtext = woodfont.render(' ' +str(wood), 2, (BROWN))
screen.blit(clicktext, [RocksX,38])
screen.blit(woodtext, [139,WoodX])
rock_group.draw(screen)
screen.blit(pick, (x-10,y-50))
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == KEYDOWN and event.key == K_ESCAPE:
sys.exit()
pygame.display.update()
#in case i need the below again
#if x>249 and x<(795) and y>210 and y<(484):
Clicking will create a MOUSEBUTTONDOWN event, so you should be able to deal with clicks within your event-processing loop, e.g.:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == KEYDOWN and event.key == K_ESCAPE:
sys.exit()
elif event.type == MOUSEBUTTONDOWN:
click_position = event.pos
if rock.point_collide(click_position):
print('Clicked within the rock')
clicks += 1
# Any other events that have to happen
# when the rock is clicked
I also don't understand how point_collide() works and I get an AttributeError: 'Rock' does not have attribute 'mask'.
So I would use an other possibility to detect if one clicked on non-transparent parts of the image, using colorkey.
The colorkey defines the transparent color when blitting. In my case it's white:
def set_image(self, filename = None):
...
#sets colorkey to white, depends on the image
self.image.set_colorkey((255,255,255))
New version of point_collide():
def point_collide(self, point):
x, y = point
x -= self.rect.x
y -= self.rect.y
#detects if click hits the image
if 0 <= x < self.image.get_width():
if 0 <= y < self.image.get_height():
#detects if color at clicking position != colorkey-color(transparent)
if self.image.get_at((x,y))[0:3] != self.image.get_colorkey()[0:3]:
return True
return False
How to get mouse events has been answered yet.
Ok I got it working :)
If anyone else is trying to do something similar I'll explain here.
Put the below in your code (this solves the problem 'Rock has no attribute mask' error I got)
self.mask = pygame.mask.from_surface(self.image)
Here is where I put it in my code (in my set_image def)
def set_image(self, filename = None):
if (filename != None):
self.image = pygame.image.load(filename).convert_alpha()
self.mask = pygame.mask.from_surface(self.image)
Now combine this code with Marius and it works perfectly!

Categories

Resources