The MOUSEBUTTONDOWN command activates itself without me clicking - python

so i have started making my own game for a project and after the spaceship shoots when you click the mouse it fires again without me pressing anything and it goes on forever. I have included what i have so far below
from livewires import games
import pygame
games.init(screen_width = 840, screen_height = 480, fps = 50)
class Spaceship(games.Sprite):
"""Creates the Spaceship"""
ship_image = games.load_image("spaceship.bmp")
def __init__(self):
super(Spaceship, self).__init__(image = Spaceship.ship_image,
x = games.mouse.x,
bottom = games.screen.height)
def update(self):
""" Move to mouse x position. """
self.x = games.mouse.x
if self.left < 0:
self.left = 0
if self.right > games.screen.width:
self.right = games.screen.width
#Makes the laser spawn on the spaceship
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN:
new_laser = Laser(self.x, self.y)
games.screen.add(new_laser)
class Laser(games.Sprite):
"""Creates the laser"""
laser_image = games.load_image("laser1.png")
def __init__(self,spaceship_x, spaceship_y):
super(Laser, self).__init__(image = Laser.laser_image,
x = spaceship_x, y = spaceship_y,
dy = -6)
#class enemy(game.Sprite):
def main():
"""Runs the game"""
background = games.load_image("featured-space-policy.jpg", transparent = False)
games.screen.background = background
ship = Spaceship()
games.screen.add(ship)
games.mouse.is_visible = False
games.screen.mainloop()
main()

Just think about it,
Since you set up an event listen for MOUSEBUTTONDOWN, so this will trigger the Laser function but how it will stop?,
So, you need to set up another listener for MOUSEBUTTONUP which will stop the firing of the laser, something like this:
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN:
new_laser = Laser(self.x, self.y)
games.screen.add(new_laser)
else event.type == pygame.MOUSEBUTTONUP:
remove_laser()#something like that, I'm not sure if it's the right method.

Related

How do I make pygame recognize a single mouse click as a single mouse click?

The problem is, when I click the arrow in my pygame game, pygame recognizes it as multiple mouse clicks.
What I've tried:
Making a KeyDownListener class like so:
class KeyDownListener:
def __init__(self):
self.down = False
self.is_up = False
def update(self, key_pressed):
if not key_pressed:
self.is_up = True
self.down = False
else:
if self.is_up:
self.down = True
else:
self.down = False
self.is_up = False
But that didn't work because then nothing happened when I clicked the arrow.
My Arrow class:
class Arrow(pygame.sprite.Sprite):
default_imgresizer = pygame.transform.scale
default_imgflipper = pygame.transform.flip
def __init__(self, win, x, y, rotate=False):
pygame.sprite.Sprite.__init__(self)
self.win = win
self.x = x
self.y = y
self.image = pygame.image.load("./arrow.png")
self.image = self.default_imgresizer(self.image, [i // 4 for i in self.image.get_size()])
if rotate:
self.image = self.default_imgflipper(self.image, True, False)
self.rect = self.image.get_rect(center=(self.x, self.y))
def is_pressed(self):
return pygame.mouse.get_pressed(3)[0] and self.rect.collidepoint(pygame.mouse.get_pos())
My event loop:
while running:
for event in pygame.event.get():
if event.type == QUIT:
running = False
screen.blit(computer_screen, (0, 0))
current_profile.win.blit(current_profile.image, current_profile.rect)
arrow_left.win.blit(arrow_left.image, arrow_left.rect)
arrow_right.win.blit(arrow_right.image, arrow_right.rect)
if arrow_right.is_pressed():
with suppress(IndexError):
current_profile_num += 1
current_profile = profiles[current_profile_num]
elif arrow_left.is_pressed():
with suppress(IndexError):
current_profile_num -= 1
current_profile = profiles[current_profile_num]
current_profile.increase_size()
back_button.win.blit(back_button.image, back_button.rect)
if back_button.is_pressed():
return
# boy.self_blit()
pygame.display.update()
You have to use the MOUSEDOWN event instead of pygame.mouse.get_pressed(). While pygame.mouse.get_pressed() returns the current state of the buttons, the MOUSEBUTTONDOWN and MOUSEBUTTONUP occurs only once a button is pressed.

Pygame Class Player not moving when I press key

I created a class of the player and I instantiated it and it doesn't work when I try to move it.
I don't understand what is wrong.
I thought the problem was in the order of the functions but isn't it.
Here is the code:
import pygame
from variabile import *
pygame.init()
screen = pygame.display.set_mode((1000,1000))
def ecranAlb():
WHITE = (255,255,255)
screen.fill(WHITE)
class Player():
def __init__(self,x,y):
self.x = x
self.y = y
def display(self):
rect = pygame.Rect(self.x,self.y,100,100)
pygame.draw.rect(screen,(255,255,0),rect)
def move(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_UP]:
self.x = self.x + 2
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
a = Player(100,100)
ecranAlb()
a.move()
a.display()
pygame.display.flip()
You have to create the instance object of the Player class before the application loop. When you do it in the loop, then the a new object at the initial position is generated in each frame:
a = Player(100,100) # <--- INSERET
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# a = Player(100,100) <--- DELETE
ecranAlb()
a.move()
a.display()
pygame.display.flip()

Object only being drawn when the game window is dragged around

I'm trying to recreate Agar.io in Python using pygame. So far I've been able to somewhat simulate the player movement, but then I tried to generate some "food cells" at a given interval using the pygame.time.set_timer() method, but even though the rest of the game elements are being drawn, such as the screen and the player, these food cells are only drawn (and added to the "food_list" list) when I drag the game window around. I think this might be an issue of me not really knowing how to deal with the event queue.
main.py, which is where the game loop is located:
import sys, pygame
from player import Player
from food import Food
import random
pygame.init()
SCREEN_SIZE = [1024, 768]
WHITE = (255, 255 , 255)
generate_food = pygame.USEREVENT + 1
screen = pygame.display.set_mode(SCREEN_SIZE)
screen.fill(WHITE)
player = Player()
food_list = []
## TODO: Make a separate GameScreen class.
## TODO: Make a list of constants
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
if event.type == generate_food:
food_x = random.randrange(0, 760)
food_y = random.randrange(0, 1000)
food_list.append(Food(food_x, food_y))
pygame.time.set_timer(generate_food, 200)
screen.fill(WHITE)
for f in food_list:
f.draw(screen)
player.update()
player.draw(screen)
pygame.display.flip()
food.py:
import pygame
import random
from cell import Cell
DARK_GREEN = (0, 102, 0)
class Food(Cell):
def __init__(self, x, y):
super().__init__()
self.size = 2
self.image = pygame.Surface([2, 2])
self.image.fill(DARK_GREEN)
self.rect = self.image.get_rect()
self.x = x
self.y = y
def draw(self, screen):
pygame.draw.circle(screen, DARK_GREEN, (self.x, self.y), self.size, 0)
def getX(self):
return self.x
def getY(self):
return self.y
Actually the timer is restarted continuously in the application loop. However pygame.time.set_timer() crates a timer that repeatedly create a USEREVENT in the event queue. Hence it is sufficient to start the timer once before the application loop:
pygame.time.set_timer(generate_food, 200) # <--- ADD
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
if event.type == generate_food:
food_x = random.randrange(0, 760)
food_y = random.randrange(0, 1000)
food_list.append(Food(food_x, food_y))
# pygame.time.set_timer(generate_food, 200) <--- DELTE

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 - blitting for a preview picture

First off, this is a school assignment so I want to be upfront about that. Second I'm just asking for advice on the approach, possible help with the code. I'm working on a MSPAINT style clone using some pre-existing code from our book. The code already has the use of the draw.line when pressing mouse button 1. Teacher wants us to add ability to make circles or rectangles. I'm working on the circle part and I have figured out (thanks to the forums on here) how to implement what I wanted to do with the MOUSEBUTTONDOWN and MOUSEBUTTONUP events.. This has brought me to a new Question.. How would I blit then erase then blit a preview of the circle until it is the size the user wants and they release the MOUSEBUTTON and view the final blit...
while keepGoing:
clock.tick(30)
for event in pygame.event.get():
if event.type == pygame.QUIT:
keepGoing = False
elif event.type == pygame.MOUSEMOTION:
lineEnd = pygame.mouse.get_pos()
if pygame.mouse.get_pressed() == (1,0,0):
pygame.draw.line(background, drawColor, lineStart, lineEnd, lineWidth)
lineStart = lineEnd
elif event.type == pygame.MOUSEBUTTONDOWN and event.button == 3:
circleStart = pygame.mouse.get_pos()
elif event.type == pygame.MOUSEBUTTONUP and event.button == 3:
circleEnd = pygame.mouse.get_pos()
size = (circleEnd[0] - circleStart[0])
pygame.draw.circle(background, drawColor, circleStart, size, lineWidth)
elif event.type == pygame.KEYDOWN:
myData = (event, background, drawColor, lineWidth, keepGoing)
myData = checkKeys(myData)
event, background, drawColor, lineWidth, keepGoing) = myData
Thanks so much
-Ben
So after some thinking this is the best solution I came up with using pygame. Tell me what you think and if it has helped you.
import pygame,sys,math #---- Import modules we will need
pygame.init() #---- Initialize the module
def get_rad(origin_x,origin_y,x,y): #----- Returns the appropriate radius
return math.sqrt((origin_x - x)**2 + (origin_y - y)**2) #----- Distance between 2
#----- points
screen = pygame.display.set_mode((400,400)) #----- Sets up the screen
clock = pygame.time.Clock() #------- Sets up the clock
mouse_button = 0 #--------- This variable is used to determine whether a mouse button
#--------- has been pressed
draw_final_circle = False #---------- This variable lets us know that we should draw the
#---------- final circle
while True: #------ main loop
clock.tick(60) #------ Limit the Fps
mouse_button0 = mouse_button #-------- This variable holds the previous value of
#-------- mouse_button(it will be useful later)
mouse_x,mouse_y = pygame.mouse.get_pos() #----- Get the mosue coordinates
for e in pygame.event.get(): #---- Cycle through events
if e.type == pygame.QUIT: pygame.quit();sys.exit() #--Quit when window is closed
if e.type == pygame.MOUSEBUTTONDOWN: #---- If the mouse button is pressed
if mouse_button == 0: #---- if the mouse button is released
mouse_button = 1 #----- set it to pressed basically
originx,originy = mouse_x,mouse_y #---- keep the mouse_x,mouse_y pos
if e.type == pygame.MOUSEBUTTONUP: #---- if the mouse button is released
if mouse_button == 1: #-------- if it is pressed
mouse_button = 0 #--------- set it to released
screen.fill((255,255,255)) #---- clear the screen
#-------- If a mouse button is pressed and a circle can be drawn (rad>width) ------#
if mouse_button == 1 and get_rad(originx,originy,mouse_x,mouse_y) > 1:
rad = int(get_rad(originx,originy,mouse_x,mouse_y)) #---- get the radius(as int)
pos = mouse_x,mouse_y
pygame.draw.circle(screen,(0,0,0),pos,rad,1) #--- draw the circle
#----------------------------------------------------------------------------------#
#---------- if the button is released but in the previous loop it was pressed -----#
if mouse_button == 0 and mouse_button0 == 1:
draw_final_circle = True #----- set the final circle boolean to True
if draw_final_circle: #----- if the final circle is decided
pygame.draw.circle(screen,(0,0,0),pos,rad,1) #---- keep drawing it
pygame.display.flip() #----- flip the buffer
I suggest you implement the different modes of your drawing program into different classes that represent the current mode and its state. This way, implementing different modes become very easy.
As for a circle drawing mode, you want to take a copy the screen surface when the user presses the mouse button, and blit that copy to the screen every frame.
Then draw your circle on that copy. This way, you basically "erase" the temporary circles.
Here's a simple example. Press SPACE to cycle between the different modes (draw, circle, rect) and TAB for different colors:
import pygame
from math import hypot
from itertools import cycle
from operator import itemgetter
pygame.init()
screen = pygame.display.set_mode((400, 400))
clock = pygame.time.Clock()
colors = cycle(sorted(pygame.color.THECOLORS.iteritems(), key=itemgetter(0)))
color = next(colors)[1]
class DrawMode(object):
def __init__(self):
self.last = None
def handle(self, e):
if e.type == pygame.MOUSEBUTTONDOWN and e.button == 1:
self.last = pygame.mouse.get_pos()
if e.type == pygame.MOUSEBUTTONUP and e.button == 1:
self.last = None
def draw(self, screen):
pos = pygame.mouse.get_pos()
if self.last:
pygame.draw.line(screen, color, self.last, pos)
self.last = pos
class ClickReleaseMode(object):
def __init__(self):
self.tmp = None
self.start = None
def handle(self, e):
if e.type == pygame.MOUSEBUTTONDOWN and e.button == 1:
self.start = pygame.mouse.get_pos()
if e.type == pygame.MOUSEBUTTONUP and e.button == 1:
self.start = self.tmp = None
def draw(self, screen):
if not self.tmp:
self.tmp = screen.copy()
pos = pygame.mouse.get_pos()
screen.blit(self.tmp, (0,0))
if self.start:
self.do_draw(screen, pos)
class CircleMode(ClickReleaseMode):
def __init__(self):
super(CircleMode, self).__init__()
def do_draw(self, screen, pos):
r = hypot(pos[0] - self.start[0], pos[1] - self.start[1])
if r >= 2:
pygame.draw.circle(screen, color, self.start, int(r), 2)
class RectMode(ClickReleaseMode):#
def __init__(self):
super(RectMode, self).__init__()
def do_draw(self, screen, pos):
p = pos[0] - self.start[0], pos[1] - self.start[1]
pygame.draw.rect(screen, color, pygame.Rect(self.start, p), 2)
quit = False
modes = cycle((DrawMode, CircleMode, RectMode))
mode = next(modes)()
while not quit:
quit = pygame.event.get(pygame.QUIT)
for e in pygame.event.get():
if e.type == pygame.KEYDOWN:
if e.key == pygame.K_SPACE:
mode = next(modes)()
print 'enter', mode.__class__.__name__
if e.key == pygame.K_TAB:
name, color = next(colors)
print 'changing color to', name, color
mode.handle(e)
mode.draw(screen)
pygame.display.flip()
clock.tick(60)

Categories