I get an incorrect position from .get_rect()? - python

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()):

Related

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 to rotate pymunk joints at will?

I'm trying to create a walking spider like this:
I considered using a SimpleMotor at the pink and red joints and control them using the rate function. But when I tried, I get an error that the function is not callable.
self.motorJoint1.rate(0.0) TypeError: 'float' object is not callable
I don't see any other functions in the pymunk API that allow controlling the joints at will. Is there really no function or am I missing something?
Basically in the run loop I want to specify rotations to the joints at certain points of time, to not just make the spider walk, but to eventually be able to use Neural Networks to allow it to experiment with various configurations of leg positions and figure out which ones can make it walk:
angle1 = 30
angle2 = 10
redJoint1.rotate(angle1)
pinkJoint2.rotate(angle2)
if angle1 < 50:
angle1 = angle1 + 1
Is it possible at all to achieve such a level of control over joints using Pymunk? To be able to stop moving the legs (without needing to put the body to sleep), or to rotate the leg joints to whatever angle the spider 'wishes to' at any point in time?
Sample code would be a great help.
From the servo example I took a hint and implemented this basic leg:
import sys
import pygame
from pygame.locals import USEREVENT, QUIT, KEYDOWN, KEYUP, K_s, K_r, K_q, K_ESCAPE, K_UP, K_DOWN, K_RIGHT, K_LEFT
from pygame.color import THECOLORS
import pymunk
from pymunk import Vec2d
import pymunk.pygame_util
class Simulator(object):
def __init__(self):
self.display_flags = 0
self.display_size = (600, 600)
self.space = pymunk.Space()
self.space.gravity = (0.0, -1900.0)
#self.space.damping = 0.999 # to prevent it from blowing up.
# Pymunk physics coordinates start from the lower right-hand corner of the screen.
self.ground_y = 100
ground = pymunk.Segment(self.space.static_body, (5, self.ground_y), (595, self.ground_y), 1.0)
ground.friction = 1.0
self.space.add(ground)
self.screen = None
self.draw_options = None
def reset_bodies(self):
for body in self.space.bodies:
if not hasattr(body, 'start_position'):
continue
body.position = Vec2d(body.start_position)
body.force = 0, 0
body.torque = 0
body.velocity = 0, 0
body.angular_velocity = 0
body.angle = body.start_angle
def draw(self):
self.screen.fill(THECOLORS["white"])### Clear the screen
self.space.debug_draw(self.draw_options)### Draw space
pygame.display.flip()### All done, lets flip the display
def main(self):
pygame.init()
self.screen = pygame.display.set_mode(self.display_size, self.display_flags)
width, height = self.screen.get_size()
self.draw_options = pymunk.pygame_util.DrawOptions(self.screen)
def to_pygame(p):
return int(p.x), int(-p.y+height) #Small hack to convert pymunk to pygame coordinates
def from_pygame(p):
return to_pygame(p)
clock = pygame.time.Clock()
running = True
font = pygame.font.Font(None, 16)
# Create the spider
chassisXY = Vec2d(self.display_size[0]/2, self.ground_y+100)
chWd = 70; chHt = 50
chassisMass = 10
legWd_a = 50; legHt_a = 5
legWd_b = 100; legHt_b = 5
legMass = 1
relativeAnguVel = 0
#---chassis
chassis_b = pymunk.Body(chassisMass, pymunk.moment_for_box(chassisMass, (chWd, chHt)))
chassis_b.position = chassisXY
chassis_shape = pymunk.Poly.create_box(chassis_b, (chWd, chHt))
chassis_shape.color = 200, 200, 200, 100
print("chassis position");print(chassis_b.position)
#---first left leg a
leftLeg_1a_body = pymunk.Body(legMass, pymunk.moment_for_box(legMass, (legWd_a, legHt_a)))
leftLeg_1a_body.position = chassisXY - ((chWd/2)+(legWd_a/2), 0)
leftLeg_1a_shape = pymunk.Poly.create_box(leftLeg_1a_body, (legWd_a, legHt_a))
leftLeg_1a_shape.color = 255, 0, 0, 100
#---first left leg b
leftLeg_1b_body = pymunk.Body(legMass, pymunk.moment_for_box(legMass, (legWd_b, legHt_b)))
leftLeg_1b_body.position = leftLeg_1a_body.position - ((legWd_a/2)+(legWd_b/2), 0)
leftLeg_1b_shape = pymunk.Poly.create_box(leftLeg_1b_body, (legWd_b, legHt_b))
leftLeg_1b_shape.color = 0, 255, 0, 100
#---first right leg a
rightLeg_1a_body = pymunk.Body(legMass, pymunk.moment_for_box(legMass, (legWd_a, legHt_a)))
rightLeg_1a_body.position = chassisXY + ((chWd/2)+(legWd_a/2), 0)
rightLeg_1a_shape = pymunk.Poly.create_box(rightLeg_1a_body, (legWd_a, legHt_a))
rightLeg_1a_shape.color = 255, 0, 0, 100
#---first right leg b
rightLeg_1b_body = pymunk.Body(legMass, pymunk.moment_for_box(legMass, (legWd_b, legHt_b)))
rightLeg_1b_body.position = rightLeg_1a_body.position + ((legWd_a/2)+(legWd_b/2), 0)
rightLeg_1b_shape = pymunk.Poly.create_box(rightLeg_1b_body, (legWd_b, legHt_b))
rightLeg_1b_shape.color = 0, 255, 0, 100
#---link left leg b with left leg a
pj_ba1left = pymunk.PinJoint(leftLeg_1b_body, leftLeg_1a_body, (legWd_b/2,0), (-legWd_a/2,0))#anchor point coordinates are wrt the body; not the space
motor_ba1Left = pymunk.SimpleMotor(leftLeg_1b_body, leftLeg_1a_body, relativeAnguVel)
#---link left leg a with chassis
pj_ac1left = pymunk.PinJoint(leftLeg_1a_body, chassis_b, (legWd_a/2,0), (-chWd/2, 0))
motor_ac1Left = pymunk.SimpleMotor(leftLeg_1a_body, chassis_b, relativeAnguVel)
#---link right leg b with right leg a
pj_ba1Right = pymunk.PinJoint(rightLeg_1b_body, rightLeg_1a_body, (-legWd_b/2,0), (legWd_a/2,0))#anchor point coordinates are wrt the body; not the space
motor_ba1Right = pymunk.SimpleMotor(rightLeg_1b_body, rightLeg_1a_body, relativeAnguVel)
#---link right leg a with chassis
pj_ac1Right = pymunk.PinJoint(rightLeg_1a_body, chassis_b, (-legWd_a/2,0), (chWd/2, 0))
motor_ac1Right = pymunk.SimpleMotor(rightLeg_1a_body, chassis_b, relativeAnguVel)
self.space.add(chassis_b, chassis_shape)
self.space.add(leftLeg_1a_body, leftLeg_1a_shape, rightLeg_1a_body, rightLeg_1a_shape)
self.space.add(leftLeg_1b_body, leftLeg_1b_shape, rightLeg_1b_body, rightLeg_1b_shape)
self.space.add(pj_ba1left, motor_ba1Left, pj_ac1left, motor_ac1Left)
self.space.add(pj_ba1Right, motor_ba1Right, pj_ac1Right, motor_ac1Right)
#---prevent collisions with ShapeFilter
shape_filter = pymunk.ShapeFilter(group=1)
chassis_shape.filter = shape_filter
leftLeg_1a_shape.filter = shape_filter
rightLeg_1a_shape.filter = shape_filter
leftLeg_1b_shape.filter = shape_filter
rightLeg_1b_shape.filter = shape_filter
simulate = False
rotationRate = 2
while running:
for event in pygame.event.get():
if event.type == QUIT or (event.type == KEYDOWN and event.key in (K_q, K_ESCAPE)):
#running = False
sys.exit(0)
elif event.type == KEYDOWN and event.key == K_s:
# Start/stop simulation.
simulate = not simulate
elif event.type == KEYDOWN and event.key == K_r:
# Reset.
# simulate = False
self.reset_bodies()
elif event.type == KEYDOWN and event.key == K_UP:
motor_ba1Left.rate = rotationRate
elif event.type == KEYDOWN and event.key == K_DOWN:
motor_ba1Left.rate = -rotationRate
elif event.type == KEYDOWN and event.key == K_LEFT:
motor_ac1Left.rate = rotationRate
elif event.type == KEYDOWN and event.key == K_RIGHT:
motor_ac1Left.rate = -rotationRate
elif event.type == KEYUP:
motor_ba1Left.rate = 0
motor_ac1Left.rate = 0
self.draw()
### Update physics
fps = 50
iterations = 25
dt = 1.0/float(fps)/float(iterations)
if simulate:
for x in range(iterations): # 10 iterations to get a more stable simulation
self.space.step(dt)
pygame.display.flip()
clock.tick(fps)
if __name__ == '__main__':
sim = Simulator()
sim.main()
It can be controlled with the up, left, right and down arrow keys after first pressing the s key to start the simulation. I've also made sure the variables are created properly linked with each other and named well.
The part about making the joints move to a desired angle is yet to be implemented, but perhaps that could be calculated by taking the x,y positions of the ends of the joints and using a formula to calculate the angle and then move the motor until it reaches a desired angle.
If there's a better way, do let me know by posting an answer or editing this one.

Pygame program is running slow. Why?

I'm making sort of a Tamagotchi project on pygame, and at this early stage the program is running really slow. Do you have any hints as to why?
Also, is there any way to speed it up?
This is my code so far:
import pygame
import time
pygame.init()
def draw_grid(grid):
for i in range(0,len(grid)):
for j in range(0,len(grid[i])):
area = (j*10, i*10, 8, 8)
if grid[i][j] == 0:
fill = True
color = 0,0,0
elif grid[i][j] == 1:
fill = False
color = 0,0,0
elif grid[i][j] == 2:
fill = False
color = 255,0,0
square = pygame.draw.rect(screen, color, area, fill)
def make_empty_grid(width,height):
grid = []
for i in range(height):
zeros = []
for j in range(width):
zeros.append(0)
grid.append(zeros)
return grid
def draw_item(item, x, y):
for i in range(0, len(item)):
for j in range(0, len(item[i])):
grid[i + x][j + y] = item[i][j]
size = width, height = 600, 400
#rects =
screen = pygame.display.set_mode(size)
running = True
#beige background
background = 213, 230, 172
black = 0, 0, 0
red = 255, 0, 0
image1 = [
[1,0,1,1,1,0,1],
[0,1,0,0,0,1,0],
[1,0,1,0,1,0,1],
[1,0,0,0,0,0,1],
[1,0,1,1,1,0,1],
[1,0,0,0,0,0,1],
[0,1,1,1,1,1,0]
]
image2 = [
[1,0,1,1,1,0,1],
[0,1,0,0,0,1,0],
[1,0,1,0,1,0,1],
[1,0,0,0,0,0,1],
[1,0,0,1,0,0,1],
[1,0,0,0,0,0,1],
[0,1,1,1,1,1,0]
]
bars = [
[2,2,2,2,2,2,2,2,2,2],
[2,2,2,2,2,2,2,2,2,2]
]
tamaPosX = 27
tamaPosY = 8
curImage = image1
while running:
# Get the window input
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
tamaPosX -= 1
if event.key == pygame.K_RIGHT:
tamaPosX += 1
if event.key == pygame.K_UP:
tamaPosY -= 1
if event.key == pygame.K_DOWN:
tamaPosY += 1
screen.fill(background)
grid = make_empty_grid(width,height)
if curImage is image1:
curImage = image2
else:
curImage = image1
draw_item(curImage, tamaPosY, tamaPosX)
draw_item(bars, 35, 1)
draw_grid(grid)
pygame.display.flip()
You could try profiling the code:
https://docs.python.org/2/library/profile.html
Mostly I expect it's because you have many nested for loops. Some of them seem unnecessary. Nested for loops are not fast in Python.
Your width and height are constant, you don't really need to create a whole new empty grid at each tick.
import copy
empty_grid = [[0] * width for _ in range(height)]
def make_empty_grid():
return copy.deepcopy(empty_grid)
Instead of copying your items into the grid, then drawing the grid, why not have draw_item call pygame.draw directly? As it is you are iterating over the entire grid twice - once to put the items in the grid, once the draw the grid.
Edit: That's maybe a bad suggestion, I see that even the grid elements with value 0 are drawn a different colour than the background. I'm not sure what you're doing.
You don't need to recalculate your empty grid each time. Store it in a surface as a "background image", then you can just blit it to the screen each time instead of clearing it, recalculating it, and then blitting it. Also when drawing items just draw items not the whole screen. You can use display.update and pass the items rects old and new since they are the only things that are changing.
You should also store your items as surfaces or sprites instead of recalculating them every time through the loop too.

Placing a sprite at a coordinate using a for loop

I am currently trying to place wall sprites at certain coordinates based on a string from a text file.
What I would like for it to do is read the textfile(getting the string from it). Then based on the character at the index of the string, place a wall sprite at certain coordinates. If it is not a certain character, then just skip placing a wall sprite and move on.
In this case, I want to use # to place a wall. It wont let me post images yet so ill draw it.
TEXTFILE:
###...
As you can see, I place 3 #'s to tell the script I want 3 walls and thats it.
The 3 dots after them are not #, so there should be no wall placed.
In this case it works, as it shows up in my game like such
"# = Wall"
"P = Player"
###
P
But when I try to make a hole in my wall using this kind of placement
TEXTFILE:
###...###
It shows up in my game like such, with no hole in between the two sections of #'s
"# = Wall"
"P = Player"
#########
P
Here is my full main script so far(minus classes), let me know if you need more information. Thanks again!
#I - Import and Initialize
import pygame, IceBackground, Player, Wall, IceBlock
pygame.init()
#D - Display Configuration
screen = pygame.display.set_mode((700, 600))
def main():
pygame.display.set_caption("Sliders")
#E - Entities(Just background for now)
background = pygame.Surface(screen.get_size())
background = background.convert()
background.fill((213, 220, 255))
screen.blit(background, (0, 0))
icebackground = IceBackground.IceBG()
player = Player.Box(90, 150)
iceblock = IceBlock.Box(90, 0)
coords = []
with open("TestLevel.txt") as file:
testLevel = file.read()
print(testLevel)
file.close()
strLength = len(testLevel)
xcoord = 0
ycoord = 0
for i in range(strLength):
print(i)
coordInsert = (xcoord, ycoord)
coords.insert(i, coordInsert)
if testLevel[i] == "#":
print("Wall placed!")
print(coordInsert)
print("\n")
walls = [Wall.Box(xcoord, ycoord) for xcoord, ycoord in coords]
elif testLevel[i] != "#":
print("Nothing placed")
print(coordInsert)
print("\n")
xcoord += 30
#iceblock = [IceBlock.Box(xcoord, ycoord) for xcoord, ycoord, in coords]
backgroundSprites = pygame.sprite.Group(icebackground)
wallSprites = pygame.sprite.Group(*walls)
playerSprites = pygame.sprite.Group(player)
#A - Assign values to key variables
clock = pygame.time.Clock()
keepGoing = True
#L - Set up the Main Loop
while keepGoing:
#T - Timer to set frame rate
clock.tick(60)
#E - Event Handling
for event in pygame.event.get():
if event.type == pygame.QUIT:
keepGoing = False
hitWalls = pygame.sprite.spritecollide(player, wallSprites, False)
if hitWalls:
if player.dx > 0:
player.dx = 0
player.rect.centerx = player.rect.centerx - 10
if player.dx < 0:
player.dx = 0
player.rect.centerx = player.rect.centerx + 10
if player.dy > 0:
player.dy = 0
player.rect.centery = player.rect.centery - 10
if player.dy < 0:
player.dy = 0
player.rect.centery = player.rect.centery + 10
#R - Refresh Display
backgroundSprites.update()
wallSprites.update()
playerSprites.update()
backgroundSprites.draw(screen)
wallSprites.draw(screen)
playerSprites.draw(screen)
pygame.display.flip()
if __name__ == "__main__":
main()
You forgot to increment ycoord by 30 when xcoord == 270. If you don't do so, all your sprite will be aligned.

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