How do I make it so that my variable only goes up by one when i click a button in pygame and also how I can make it so that the text updates [duplicate] - pygame

This question already has answers here:
Pygame mouse clicking detection
(4 answers)
Closed 1 year ago.
Im trying to make an Idle Clicker Game using pygame for school and Im running into some problems and id really appreciate some help
I need some help with my program, mainly I have two problems, for one when I click the "Upgrade Whetstone Button" While the variable Whetstone level goes up the text won't, and because of the way ive done it I cant Just blit some text onto the screen so I'd like some help there and secondly when i press the upgrade button and i have it printed on the screen the variable Whetstone Level goes up by more than one and I want it to go up one level for one click and so i was wondering what i could do to change it so that it only goes up by one level
Thanks in advance
import pygame
pygame.init()
pygame.font.init()
colours = {"White" : (255, 255, 255), "Black" : (0, 0, 0), "Red" : (255, 0, 0), "Blue" : (0, 0, 255), "Green" : (0, 255, 0)}
clickdamage = 1
Whetstone_Level = 0
class Screen():
def __init__(self, title, width=400, height=600, fill=colours["White"]):
self.title=title
self.width=width
self.height = height
self.fill = fill
self.current = False
def makeCurrent(self):
pygame.display.set_caption(self.title)
self.current = True
self.screen = pygame.display.set_mode((self.width, self.height))
def endCurrent(self):
self.current = False
def checkUpdate(self):
return self.current
def screenUpdate(self):
if(self.current):
self.screen.fill(self.fill)
def returnTitle(self):
return self.screen
class Button():
def __init__(self, x, y, sx, sy, bcolour, fbcolour, font, fontsize, fcolour, text):
self.x = x
self.y = y
self.sx = sx
self.sy = sy
self.bcolour = bcolour
self.fbcolour = fbcolour
self.fcolour = fcolour
self.fontsize = fontsize
self.text = text
self.current = False
self.buttonf = pygame.font.SysFont(font, fontsize)
def showButton(self, display):
if(self.current):
pygame.draw.rect(display, self.fbcolour, (self.x, self.y, self.sx, self.sy))
else:
pygame.draw.rect(display, self.bcolour, (self.x, self.y, self.sx, self.sy))
textsurface = self.buttonf.render(self.text, False, self.fcolour)
display.blit(textsurface, ((self.x + (self.sx/2) - (self.fontsize/2)*(len(self.text)/2) - 5,(self.y + (self.sy/2) -(self.fontsize/2) - 4))))
def focusCheck(self, mousepos, mouseclick):
if(mousepos[0] >= self.x and mousepos[0] <= self.x + self.sx and mousepos[1] >= self.y and mousepos[1] <= self.y + self.sy):
self.current = True
return mouseclick[0]
else:
self.current = False
return False
class Enemy(pygame.sprite.Sprite):
def __init__(self, dx, dy, filename):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load(filename).convert()
self.rect = self.image.get_rect()
self.rect.x = dx
self.rect.y = dy
def draw(self, screen):
screen.blit(self.image, self.rect)
menuScreen = Screen("Menu Screen")
screen2 = Screen("Screen 2")
win = menuScreen.makeCurrent()
done = False
font = pygame.font.Font('freesansbold.ttf', 32)
clickdamagelevel = Button(160, 10, 150, 50, colours["Black"], colours["Red"], "arial", 15, colours["White"], str(Whetstone_Level))
clickdamageupgrade = Button(10, 10, 150, 50, colours["Black"], colours["Red"], "arial", 15, colours["White"], "Whetstone")
shopButton = Button(125, 500, 150, 50, colours["Black"], colours["Red"], "arial", 20, colours["White"], "Shop")
DungeonButton = Button(125, 500, 150, 50, colours["Black"], colours["Blue"], "arial", 20, colours["White"], "Dungeon")
hitboxButton = Button(80, 50, 280, 400, colours["White"], colours["Red"], "arial", 20, colours["White"], "")
goblin = Enemy(0 , 20, "images\goblin-resized.png")
goblin2 = Enemy(0 , 20, "images\goblin.jpg")
toggle = False
while not done:
menuScreen.screenUpdate()
screen2.screenUpdate()
mouse_pos = pygame.mouse.get_pos()
mouse_click = pygame.mouse.get_pressed()
keys = pygame.key.get_pressed()
if menuScreen.checkUpdate():
screen2button = shopButton.focusCheck(mouse_pos, mouse_click)
attack = hitboxButton.focusCheck(mouse_pos, mouse_click)
hitboxButton.showButton(menuScreen.returnTitle())
goblin.draw(menuScreen.screen)
shopButton.showButton(menuScreen.returnTitle())
if attack:
print("hitpoints - click damage")
if screen2button:
win = screen2.makeCurrent()
menuScreen.endCurrent()
elif screen2.checkUpdate():
returnm = DungeonButton.focusCheck(mouse_pos, mouse_click)
DungeonButton.showButton(screen2.returnTitle())
clickdamageupgrade.showButton(screen2.returnTitle())
clickdamagelevel.showButton(screen2.returnTitle())
if clickdamageupgrade.focusCheck(mouse_pos, mouse_click):
Whetstone_Level += 1
print(Whetstone_Level)
clickdamagelevel.showButton(screen2.returnTitle())
if returnm:
win = menuScreen.makeCurrent()
screen2.endCurrent()
for event in pygame.event.get():
if(event.type == pygame.QUIT):
done = True
pygame.display.update()
pygame.quit()

You can use pygame.MOUSEBUTTONDOWN to register one mouse click per mouse button down.
In your case, you can use your mouse_click button variable. Move your event loop before all of the if statements.
mouse_click = False
for event in pygame.event.get():
if(event.type == pygame.QUIT):
done = True
if event.type == pygame.MOUSEBUTTONDOWN:
mouse_click = True
if menuScreen.checkUpdate():
#...
This also means your foucsCheck member function should return mouseclick and not a subscript of it.
def focusCheck(self, mousepos, mouseclick):
if(mousepos[0] >= self.x and mousepos[0] <= self.x + self.sx and mousepos[1] >= self.y and mousepos[1] <= self.y + self.sy):
self.current = True
return mouseclick #mouseclick[0]
That fixes the problem of registering single click.
For the second problem, it would be ideal if you could change just the text, but since your button class does not allow that, you can recreate the button with the updated Whetstone_Level variable every time it is clicked. That being said, i would recommend you to modify your class slightly to facilitate this.
if clickdamageupgrade.focusCheck(mouse_pos, mouse_click):
clickdamagelevel = Button(160, 10, 150, 50, colours["Black"], colours["Red"], "arial", 15, colours["White"], str(Whetstone_Level))
Updated code.
import pygame
pygame.init()
pygame.font.init()
colours = {"White" : (255, 255, 255), "Black" : (0, 0, 0), "Red" : (255, 0, 0), "Blue" : (0, 0, 255), "Green" : (0, 255, 0)}
clickdamage = 1
Whetstone_Level = 0
class Screen():
def __init__(self, title, width=400, height=600, fill=colours["White"]):
self.title=title
self.width=width
self.height = height
self.fill = fill
self.current = False
def makeCurrent(self):
pygame.display.set_caption(self.title)
self.current = True
self.screen = pygame.display.set_mode((self.width, self.height))
def endCurrent(self):
self.current = False
def checkUpdate(self):
return self.current
def screenUpdate(self):
if(self.current):
self.screen.fill(self.fill)
def returnTitle(self):
return self.screen
class Button():
def __init__(self, x, y, sx, sy, bcolour, fbcolour, font, fontsize, fcolour, text):
self.x = x
self.y = y
self.sx = sx
self.sy = sy
self.bcolour = bcolour
self.fbcolour = fbcolour
self.fcolour = fcolour
self.fontsize = fontsize
self.text = text
self.current = False
self.buttonf = pygame.font.SysFont(font, fontsize)
def showButton(self, display):
if(self.current):
pygame.draw.rect(display, self.fbcolour, (self.x, self.y, self.sx, self.sy))
else:
pygame.draw.rect(display, self.bcolour, (self.x, self.y, self.sx, self.sy))
textsurface = self.buttonf.render(self.text, False, self.fcolour)
display.blit(textsurface, ((self.x + (self.sx/2) - (self.fontsize/2)*(len(self.text)/2) - 5,(self.y + (self.sy/2) -(self.fontsize/2) - 4))))
def focusCheck(self, mousepos, mouseclick):
if(mousepos[0] >= self.x and mousepos[0] <= self.x + self.sx and mousepos[1] >= self.y and mousepos[1] <= self.y + self.sy):
self.current = True
return mouseclick
else:
self.current = False
return False
class Enemy(pygame.sprite.Sprite):
def __init__(self, dx, dy, filename):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load(filename).convert()
self.rect = self.image.get_rect()
self.rect.x = dx
self.rect.y = dy
def draw(self, screen):
screen.blit(self.image, self.rect)
menuScreen = Screen("Menu Screen")
screen2 = Screen("Screen 2")
win = menuScreen.makeCurrent()
done = False
font = pygame.font.Font('freesansbold.ttf', 32)
clickdamagelevel = Button(160, 10, 150, 50, colours["Black"], colours["Red"], "arial", 15, colours["White"], str(Whetstone_Level))
clickdamageupgrade = Button(10, 10, 150, 50, colours["Black"], colours["Red"], "arial", 15, colours["White"], "Whetstone")
shopButton = Button(125, 500, 150, 50, colours["Black"], colours["Red"], "arial", 20, colours["White"], "Shop")
DungeonButton = Button(125, 500, 150, 50, colours["Black"], colours["Blue"], "arial", 20, colours["White"], "Dungeon")
hitboxButton = Button(80, 50, 280, 400, colours["White"], colours["Red"], "arial", 20, colours["White"], "")
goblin = Enemy(0 , 20, "images\goblin-resized.png")
goblin2 = Enemy(0 , 20, "images\goblin.jpg")
toggle = False
while not done:
menuScreen.screenUpdate()
screen2.screenUpdate()
mouse_pos = pygame.mouse.get_pos()
keys = pygame.key.get_pressed()
mouse_click = False
for event in pygame.event.get():
if(event.type == pygame.QUIT):
done = True
if event.type == pygame.MOUSEBUTTONDOWN:
mouse_click = True
if menuScreen.checkUpdate():
screen2button = shopButton.focusCheck(mouse_pos, mouse_click)
attack = hitboxButton.focusCheck(mouse_pos, mouse_click)
hitboxButton.showButton(menuScreen.returnTitle())
goblin.draw(menuScreen.screen)
shopButton.showButton(menuScreen.returnTitle())
if attack:
print("hitpoints - click damage")
if screen2button:
win = screen2.makeCurrent()
menuScreen.endCurrent()
elif screen2.checkUpdate():
returnm = DungeonButton.focusCheck(mouse_pos, mouse_click)
DungeonButton.showButton(screen2.returnTitle())
clickdamageupgrade.showButton(screen2.returnTitle())
clickdamagelevel.showButton(screen2.returnTitle())
if clickdamageupgrade.focusCheck(mouse_pos, mouse_click):
Whetstone_Level += 1
clickdamagelevel = Button(160, 10, 150, 50, colours["Black"], colours["Red"], "arial", 15, colours["White"], str(Whetstone_Level))
print(Whetstone_Level)
clickdamagelevel.showButton(screen2.returnTitle())
if returnm:
win = menuScreen.makeCurrent()
screen2.endCurrent()
pygame.display.update()
pygame.quit()

Related

I dont know where this error is coming from

Im trying to play my code but i get this error 'video system not initialized' and i dont even know where that could be coming from. Here is my code so far:
#Imports
import sys as pygame
import pygame as pg
from random import randint, uniform
vec = pg.math.Vector2
#Player variables
WIDTH = 1200
HEIGHT = 660
FPS = 60
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
YELLOW = (255, 255, 0)
DARKGRAY = (40, 40, 40)
#----------------------------------------------------------------------------
# Player properties
Player_SIZE = 32
MAX_SPEED = 5
MAX_FORCE = 0.5
APPROACH_RADIUS = 100
all_sprites = pg.sprite.Group()
class Player(pg.sprite.Sprite):
def __init__(self):
self.groups = all_sprites
pg.sprite.Sprite.__init__(self, self.groups)
self.image = pg.Surface((Player_SIZE, Player_SIZE))
self.image.fill(BLUE)
self.rect = self.image.get_rect()
self.pos = vec(randint(0, WIDTH), randint(0, HEIGHT))
self.vel = vec(MAX_SPEED, 0).rotate(uniform(0, 360))
self.acc = vec(0, 0)
self.rect.center = self.pos
def follow_mouse(self):
mpos = pg.mouse.get_pos()
self.acc = (mpos - self.pos).normalize() * 0.5
def seek(self, target):
self.desired = (target - self.pos).normalize() * MAX_SPEED
steer = (self.desired - self.vel)
if steer.length() > MAX_FORCE:
steer.scale_to_length(MAX_FORCE)
return steer
def seek_with_approach(self, target):
self.desired = (target - self.pos)
dist = self.desired.length()
self.desired.normalize_ip()
if dist < APPROACH_RADIUS:
self.desired *= dist / APPROACH_RADIUS * MAX_SPEED
else:
self.desired *= MAX_SPEED
steer = (self.desired - self.vel)
if steer.length() > MAX_FORCE:
steer.scale_to_length(MAX_FORCE)
return steer
def update(self):
# self.follow_mouse()
self.acc = self.seek_with_approach(pg.mouse.get_pos())
# equations of motion
self.vel += self.acc
if self.vel.length() > MAX_SPEED:
self.vel.scale_to_length(MAX_SPEED)
self.pos += self.vel
if self.pos.x > WIDTH:
self.pos.x = 0
if self.pos.x < 0:
self.pos.x = WIDTH
if self.pos.y > HEIGHT:
self.pos.y = 0
if self.pos.y < 0:
self.pos.y = HEIGHT
self.rect.center = self.pos
#Enemy properties
MAX_SPEED1 = 4.5
ppos = pg.mouse.get_pos()
class Enemy(pg.sprite.Sprite):
def __init__(self):
self.groups = all_sprites
pg.sprite.Sprite.__init__(self, self.groups)
self.image = pg.Surface((Player_SIZE, Player_SIZE))
self.image.fill(RED)
self.rect = self.image.get_rect()
self.pos = vec(randint(0, WIDTH), randint(0, HEIGHT))
self.vel = vec(MAX_SPEED1, 0).rotate(uniform(0, 360))
self.acc = vec(0, 0)
self.rect.center = self.pos
def follow_player(self):
self.acc = (ppos - self.pos).normalize() * 0.5
def seek(self, target):
self.desired = (ppos - self.pos).normalize() * MAX_SPEED1
steer = (self.desired - self.vel)
if steer.length() > MAX_FORCE:
steer.scale_to_length(MAX_FORCE)
return steer
def seek_with_approach(self, target):
self.desired = (ppos() - self.pos)
dist = self.desired.length()
self.desired.normalize_ip()
if dist < APPROACH_RADIUS:
self.desired *= dist / APPROACH_RADIUS * MAX_SPEED
else:
self.desired *= MAX_SPEED1
steer = (self.desired - self.vel)
if steer.length() > MAX_FORCE:
steer.scale_to_length(MAX_FORCE)
return steer
def update(self):
# self.follow_mouse()
self.acc = self.seek_with_approach(Player)
# equations of motion
self.vel += self.acc
if self.vel.length() > MAX_SPEED1:
self.vel.scale_to_length(MAX_SPEED1)
self.pos += self.vel
if self.pos.x > WIDTH:
self.pos.x = 0
if self.pos.x < 0:
self.pos.x = WIDTH
if self.pos.y > HEIGHT:
self.pos.y = 0
if self.pos.y < 0:
self.pos.y = HEIGHT
self.rect.center = self.pos
#----------------------------------------------------------------------------
#Display
pg.init()
screen = pg.display.set_mode((WIDTH, HEIGHT))
clock = pg.time.Clock()
pg.display.set_caption("Maze")
Icon = pg.image.load('Logo.png')
pg.display.set_icon(Icon)
#Main loop
all_sprites = pg.sprite.Group()
Player()
Enemy()
paused = False
running = True
while running:
clock.tick(FPS)
for event in pg.event.get():
if event.type == pg.QUIT:
running = False
if event.type == pg.KEYDOWN:
if event.key == pg.K_SPACE:
paused = not paused
if event.key == pg.K_m:
Player()
Enemy()
if not paused:
all_sprites.update()
screen.fill(DARKGRAY)
all_sprites.draw(screen)
pg.display.flip()
pg.quit()
I know it's long but Ireally dont know where the problem could be coming from.
Any help? (There is no problem with the indent im only having trouble pasting the code here)
You have to call pygame.init() before you can use pygame.mouse.get_pos(); or, in your case, pg.init(), since you imported pygame as pg.
I don't know why you import sys as pygame; that quite confusing.
Also, getting the mouse position once that the start of your game is not helpful. Everytime you want to know the mouse position, you should call .mouse.get_pos() again.

Pygame joystick doesn't show multiple joysticks [duplicate]

import pygame
import os
import random
import time
import json
from pygame import joystick
pygame.font.init()
pygame.init()
WIDTH, HEIGHT = 1920, 1000
WIN = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("space invaders")
WHITE = (255, 255, 255)
#load images
RED_SPACE_SHIP = pygame.transform.scale(pygame.image.load(os.path.join("Assets", "pixel_ship_red_small.png")), (125, 100))
GREEN_SPACE_SHIP = pygame.transform.scale(pygame.image.load(os.path.join("Assets", "pixel_ship_green_small.png")), (125, 100))
BLUE_SPACE_SHIP = pygame.transform.scale(pygame.image.load(os.path.join("Assets", "pixel_ship_blue_small.png")), (125, 100))
#player
YELLOW_SPACE_SHIP = pygame.transform.rotate(pygame.transform.scale(pygame.image.load(os.path.join("Assets", "spaceship_yellow.png")), (154, 121)), 180)
#lasers
RED_LASER = pygame.image.load(os.path.join("Assets", "pixel_laser_red.png"))
BLUE_LASER = pygame.image.load(os.path.join("Assets", "pixel_laser_blue.png"))
GREEN_LASER = pygame.image.load(os.path.join("Assets", "pixel_laser_green.png"))
#player laser
YELLOW_LASER = pygame.image.load(os.path.join("Assets", "pixel_laser_yellow.png"))
#background
BG = pygame.transform.scale(pygame.image.load(
os.path.join('Assets', 'space.png')), (WIDTH, HEIGHT))
class Laser():
def __init__(self, x, y, img):
self.x = x
self.y = y
self.img = img
self.mask = pygame.mask.from_surface(self.img)
def draw(self, window):
window.blit(self.img, (self.x, self.y))
def move(self, vel):
self.y += vel
def off_screen(self, height):
return not(self.y <= height and self.y >= 0)
def collision(self, obj):
return collide(self, obj)
class Ship:
COOLDOWN = 30
def __init__(self, x, y, health = 100):
self.x = x
self.y = y
self.health = health
self.ship_img = None
self.laser_img = None
self.lasers = []
self.cool_down_counter = 0
def draw(self, window):
window.blit(self.ship_img, (self.x, self.y))
for laser in self.lasers:
laser.draw(window)
def move_lasers(self, vel, obj):
self.cooldown()
for laser in self.lasers:
laser.move(vel)
if laser.off_screen(HEIGHT):
self.lasers.remove(laser)
elif laser.collision(obj):
obj.health -= 10
self.lasers.remove(laser)
def cooldown(self):
if self.cool_down_counter >= self.COOLDOWN:
self.cool_down_counter = 0
elif self.cool_down_counter > 0:
self.cool_down_counter += 1
def shoot(self):
if self.cool_down_counter == 0:
laser = Laser(self.x, self.y, self.laser_img)
self.lasers.append(laser)
self.cool_down_counter = 1
def get_width(self):
return self.ship_img.get_width()
def get_height(self):
return self.ship_img.get_height()
class Player(Ship):
def __init__(self, x, y, health = 100):
super().__init__(x, y, health)
self.ship_img = YELLOW_SPACE_SHIP
self.laser_img = YELLOW_LASER
self.mask = pygame.mask.from_surface(self.ship_img)
self.max_health = health
def shoot(self):
if self.cool_down_counter == 0:
laser = Laser(self.x + 26, self.y - 57, self.laser_img)
self.lasers.append(laser)
self.cool_down_counter = 1
def move_lasers(self, vel, objs):
self.cooldown()
for laser in self.lasers:
laser.move(vel)
if laser.off_screen(HEIGHT):
self.lasers.remove(laser)
else:
for obj in objs:
if laser.collision(obj):
objs.remove(obj)
if laser in self.lasers:
self.lasers.remove(laser)
def draw(self, window):
super().draw(window)
self.healthbar(window)
def healthbar(self, window):
pygame.draw.rect(window, (255, 0, 0), (self.x, self.y + self.ship_img.get_height() + 10, self.ship_img.get_width(), 10))
pygame.draw.rect(window, (0, 255, 0), (self.x, self.y + self.ship_img.get_height() + 10, self.ship_img.get_width()* (self.health/self.max_health),10))
class Enemy(Ship):
COLOR_MAP = {
"red": (RED_SPACE_SHIP, RED_LASER),
"green": (GREEN_SPACE_SHIP, GREEN_LASER),
"blue": (BLUE_SPACE_SHIP, BLUE_LASER)
}
def __init__(self, x, y, color, health = 100):
super().__init__(x, y, health)
self.ship_img, self.laser_img = self.COLOR_MAP[color]
self.mask = pygame.mask.from_surface(self.ship_img)
def move(self, vel):
self.y += vel
def shoot(self):
if self.cool_down_counter == 0:
laser = Laser(self.x - 3, self.y + 20, self.laser_img)
self.lasers.append(laser)
self.cool_down_counter = 1
def collide(obj1, obj2):
offset_x = obj2.x - obj1.x
offset_y = obj2.y - obj1.y
return obj1.mask.overlap(obj2.mask, (offset_x, offset_y)) != None
def main():
run = True
FPS = 60
level = 0
lives = 5
lost = False
lost_count = 0
main_font = pygame.font.SysFont("comicsans", 75)
lost_font = pygame.font.SysFont("comicsans", 500)
enemies = []
wave_length = 5
player_vel = 7
laser_vel = 7
enemy_vel = 1
player = Player(WIDTH // 2, 650)
clock = pygame.time.Clock()
def redraw_window():
WIN.blit(BG, (0,0))
#draw text
level_label = main_font.render(f"Level: {level}", 1, WHITE)
lives_label = main_font.render(f"Lives: {lives}", 1, WHITE)
WIN.blit(lives_label, (10, 10))
WIN.blit(level_label, (WIDTH - level_label.get_width() - 10, 10))
player.draw(WIN)
for enemy in enemies:
enemy.draw(WIN)
if lost:
lost_label = lost_font.render("You Lost!", 1, (WHITE))
WIN.blit(lost_label, (WIDTH/2 - lost_label.get_width()/2, HEIGHT / 2 - lost_label.get_height() / 2))
pygame.display.update()
joysticks = []
for i in range(pygame.joystick.get_count()):
joysticks.append(pygame.joystick.Joystick(i))
for joystick in joysticks:
pygame.joystick.init()
print(pygame.joystick.get_init())
with open(os.path.join("ps4_keys.json"), 'r+') as file:
button_keys = json.load(file)
# 0: Left analog horizonal, 1: left analog verticle, 2: right analog horizonal
# 3: right analog verticle, 4: left Triger, 5: Right Trigger
analog_keys = {0:0, 1:0, 2:0, 3:0, 4:-1, 5:-1}
while run:
clock.tick(FPS)
redraw_window()
if lives <= 0 or player.health <= 0:
lost = True
lost_count += 1
if lost:
if lost_count > FPS * 3:
run = False
else:
continue
if len(enemies) == 0:
level += 1
wave_length += 5
for i in range(wave_length):
enemy = Enemy(random.randrange(100, WIDTH-100), random.randrange(-1500, -100), random.choice(["red", "blue", "green"]))
enemies.append(enemy)
for event in pygame.event.get():
if event.type == pygame.QUIT:
quit()
if event.type == pygame.JOYAXISMOTION:
analog_keys[event.axis] = event.value
print(analog_keys)
if abs(analog_keys[0]) > .4:
if analog_keys[0] < -.7:
player.x -= 7
else:
continue
if analog_keys[0] < .7:
player.x += 7
for enemy in enemies[:]:
enemy.move(enemy_vel)
enemy.move_lasers(laser_vel, player)
if random.randrange(0, 120) == 1:
enemy.shoot()
if collide(enemy, player):
player.health -= 10
enemies.remove(enemy)
elif enemy.y + enemy.get_height() > HEIGHT:
lives -= 1
enemies.remove(enemy)
player.move_lasers(-laser_vel, enemies)
def main_menu():
title_font = pygame.font.SysFont("comicsans", 150)
run = True
while run:
WIN.blit(BG, (0,0))
title_label = title_font.render("Click the mouse to begin...", 1, WHITE)
WIN.blit(title_label, (WIDTH / 2 - title_label.get_width() / 2, HEIGHT / 2 - title_label.get_height() / 2))
pygame.display.update()
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.MOUSEBUTTONDOWN:
main()
pygame.quit()
main_menu()
^^^
so this is my code (here is the JSON file as well):
{
"x": 0,
"circle": 1,
"square": 2,
"triangle": 3,
"share": 4,
"PS": 5,
"options": 6,
"left_stick_click": 7,
"right_stick_click": 8,
"L1": 9,
"R1": 10,
"up_arrow": 11,
"down_arrow": 12,
"left_arrow": 13,
"right_arrow": 14,
"touchpad": 15
}
I'm trying to make the player be controlled by the controller left joystick and it returns no errors but my player does not move and it is printing true from the print(pygame.joystick.get_init()) and printing the joystick amounts from: print(analog_keys) but the player does not move. Any idea why?
Do not use the JOYAXISMOTION event. The event does not occur continuously, it only occurs once when the axis changes.
Use pygame.joystick.Joystick.get_axis to get the current position of the axis and move the player depending on the axis:
def main():
# [...]
while run:
# [...]
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if joysticks:
joystick = joysticks[0]
axis_x, axis_y = (joystick.get_axis(0), joystick.get_axis(1))
if abs(axis_x) > 0.1:
player.x += round(7 * axis_x)
if abs(axis_y) > 0.1:
player.y += round(7 * axis_y)
# [...]
Minimal example:
import pygame
from pygame.locals import *
pygame.init()
window = pygame.display.set_mode((300, 300))
clock = pygame.time.Clock()
x, y = window.get_rect().center
if pygame.joystick.get_count() > 0:
joystick = pygame.joystick.Joystick(0)
joystick.init()
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
axis_x, axis_y = (joystick.get_axis(0), joystick.get_axis(1))
if abs(axis_x) > 0.1:
x = (x + round(7 * axis_x)) % window.get_width()
if abs(axis_y) > 0.1:
y = (y + round(7 * axis_y)) % window.get_height()
window.fill(0)
pygame.draw.circle(window, (255, 0, 0), (x, y), 10)
pygame.display.flip()
clock.tick(60)
pygame.quit()
exit()

How to calculate the minimum translation vector between a circle and a rectangle?

I am aware of how to detect collision with circle and rectangles but I am not sure how to find the minimum translation vector between the two. I know how to do it with the SAT collision detection algorithm, but that is too complicated for my simple implementation right now.
I am really not sure what to do except change the x-coordinate appropriately.
Here is the code. When you pressed the down button, what I would want is that the circle automatically is "shoved" to the left (since it is already positioned a bit left to the center) when constantly having the down button pressed i.e. it is moving down but sliding to the left.
import pygame
if __name__ == "__main__":
pygame.init()
display = pygame.display.set_mode((500, 500))
display.fill((255, 255, 255))
circle_x = 240
circle_y = 50
pygame.draw.circle(display, (0, 0, 255), (circle_x, circle_y), 50)
pygame.draw.rect(display, (0, 255, 255), (240, 250, 20, 250))
pygame.display.update()
vel = 1
is_down_held = False
clock = pygame.time.Clock()
while True:
pressed_keys = pygame.key.get_pressed()
if pressed_keys[pygame.K_DOWN]:
circle_y += vel
display.fill((255, 255, 255))
pygame.draw.circle(display, (0, 0, 255), (circle_x, circle_y), 50)
pygame.draw.rect(display, (0, 255, 255), (240, 250, 20, 250))
pygame.display.update()
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
dt = clock.tick(60)
dt /= 1000
I tried moving the x-coordinate with some constant, however, it looks unrealistic and is actually passing through the rectangle sometimes (since it actually doesn't detect the rectangle).
Okay, it seems like I had to implement SAT collision detection at the end anyways. To calculate the minimum translation vector between a rectangle and a circle, you go through the necessary axes for the shapes (see https://jcharry.com/blog/physengine10 and https://www.sevenson.com.au/actionscript/sat/ for an explanation on that) then you take the smallest overlap axis for when there is a collision. Below is the code using pygame GUI library:
import math
import pygame
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
self.magnitude = math.sqrt(x ** 2 + y ** 2)
if self.magnitude != 0:
self.direction_x = -x / self.magnitude
self.direction_y = -y / self.magnitude
else:
self.direction_x = 0
self.direction_y = 0
def normalize(self):
if self.magnitude != 0:
self.x /= self.magnitude
self.y /= self.magnitude
self.magnitude = 1
def project_vector(vector1, vector2):
return get_dot_product(vector1, get_unit_vector(vector2))
def get_dot_product(vector1, vector2):
return (vector1.x * vector2.x) + (vector1.y * vector2.y)
def get_normal(vector):
return Vector(vector.y, -vector.x)
def get_vector(point):
return Vector(point[0], point[1])
def scale_vector(vector, magnitude):
return Vector(vector.x*magnitude, vector.y*magnitude)
def get_unit_vector(vector):
if vector.magnitude != 0:
return scale_vector(vector, 1 / vector.magnitude)
else:
return scale_vector(vector, 0)
def get_closest_point(circle_centre, rectangle_points):
closest_distance = float('inf')
closest_point = None
for point in rectangle_points:
distance = (circle_centre[0] - point[0])**2 + (circle_centre[1] - point[1])**2
if distance <= closest_distance:
closest_distance = distance
closest_point = point
return closest_point
def is_collision(circle_centre, rectangle_points):
closest_point = get_closest_point(circle_centre, rectangle_points)
rectangle_edge_vectors = []
for point in rectangle_points:
rectangle_edge_vectors += [get_vector(point)]
rectangle_edge_normals = []
for i in range(len(rectangle_points) - 1):
rectangle_edge_normals += [get_normal(get_vector((rectangle_points[i + 1][0] - rectangle_points[i][0], rectangle_points[i + 1][1] - rectangle_points[i][1])))]
rectangle_edge_normals += [get_normal(get_vector((rectangle_points[0][0] - rectangle_points[len(rectangle_points) - 1][0], rectangle_points[0][1] - rectangle_points[len(rectangle_points) - 1][1])))]
rectangle_edge_normals += [get_vector((circle_centre[0] - closest_point[0], circle_centre[1] - closest_point[1]))]
axes = rectangle_edge_normals
vectors = rectangle_edge_vectors
for axis in axes:
current_rect_max_x = float('-inf')
current_rect_min_x = float('inf')
for vector in vectors:
current_rect_projection = project_vector(vector, axis)
if current_rect_projection >= current_rect_max_x:
current_rect_max_x = current_rect_projection
if current_rect_projection <= current_rect_min_x:
current_rect_min_x = current_rect_projection
current_circle_projection = project_vector(get_vector(circle_centre), axis)
current_circle_max_x = current_circle_projection + 25
current_circle_min_x = current_circle_projection - 25
if current_rect_min_x > current_circle_max_x or current_circle_min_x > current_rect_max_x:
return False
return True
def get_minimum_translation_vector(circle_centre, rectangle_points):
closest_point = get_closest_point(circle_centre, rectangle_points)
rectangle_edge_vectors = []
for point in rectangle_points:
rectangle_edge_vectors += [get_vector(point)]
rectangle_edge_normals = []
for i in range(len(rectangle_points) - 1):
rectangle_edge_normals += [get_normal(get_vector((rectangle_points[i + 1][0] - rectangle_points[i][0], rectangle_points[i + 1][1] - rectangle_points[i][1])))]
rectangle_edge_normals += [get_normal(get_vector((rectangle_points[0][0] - rectangle_points[len(rectangle_points) - 1][0], rectangle_points[0][1] - rectangle_points[len(rectangle_points) - 1][1])))]
rectangle_edge_normals += [get_vector((circle_centre[0] - closest_point[0], circle_centre[1] - closest_point[1]))]
axes = rectangle_edge_normals
for axis in axes:
axis.normalize()
vectors = rectangle_edge_vectors
minimum_translation_vector = Vector(axes[0].x, axes[0].y)
minimum_translation_vector.magnitude = float('inf')
current_minimum_translation_vector = Vector(axes[0].x, axes[0].y)
current_minimum_translation_vector.magnitude = float('inf')
for axis in axes:
current_rect_max_x = float('-inf')
current_rect_min_x = float('inf')
for vector in vectors:
current_rect_projection = project_vector(vector, axis)
if current_rect_projection >= current_rect_max_x:
current_rect_max_x = current_rect_projection
if current_rect_projection <= current_rect_min_x:
current_rect_min_x = current_rect_projection
current_circle_projection = project_vector(get_vector(circle_centre), axis)
current_circle_max_x = current_circle_projection + 25
current_circle_min_x = current_circle_projection - 25
current_minimum_translation_vector = axis
current_minimum_translation_vector.magnitude = abs(current_circle_min_x - current_rect_max_x)
if current_minimum_translation_vector.magnitude <= minimum_translation_vector.magnitude:
minimum_translation_vector = axis
minimum_translation_vector.magnitude = current_minimum_translation_vector.magnitude
return minimum_translation_vector
if __name__ == "__main__":
pygame.init()
display = pygame.display.set_mode((500, 500))
rectangle_points_main = [(250, 250), (300, 250), (300, 300), (250, 300)]
circle_centre_main = (0, 0)
clock = pygame.time.Clock()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
circle_centre_main = (pygame.mouse.get_pos()[0], pygame.mouse.get_pos()[1])
display.fill((255, 255, 255))
if is_collision(circle_centre_main, rectangle_points_main):
pygame.draw.circle(display, (255, 0, 0), circle_centre_main, 25)
minimum_translation_vector_main = get_minimum_translation_vector(circle_centre_main, rectangle_points_main)
dx = minimum_translation_vector_main.magnitude * minimum_translation_vector_main.direction_x
dy = minimum_translation_vector_main.magnitude * minimum_translation_vector_main.direction_y
rectangle_points_main = [(rectangle_points_main[0][0] + dx, rectangle_points_main[0][1] + dy),
(rectangle_points_main[1][0] + dx, rectangle_points_main[1][1] + dy),
(rectangle_points_main[2][0] + dx, rectangle_points_main[2][1] + dy),
(rectangle_points_main[3][0] + dx, rectangle_points_main[3][1] + dy)]
else:
pygame.draw.circle(display, (0, 0, 255), circle_centre_main, 25)
pygame.draw.rect(display, (0, 255, 0), (rectangle_points_main[0][0], rectangle_points_main[0][1], 50, 50))
dt = clock.tick(60)
dt /= 1000
pygame.display.update()

AtrributeError with basic sprite collision

Just started with sprite collision in pygame. When this code is run, an AttributeError pops up that says ''Group' object has no attribute 'rect''. I can't figure out why this error occurs. Suggestions?
from random import randint
import pygame
pygame.init()
white = [255,255,255]
blue = [0,0,255]
red = [255,0,0]
size = (400,400)
screen = pygame.display.set_mode(size)
pygame.display.set_caption('Simple character')
clock = pygame.time.Clock()
class Ball(pygame.sprite.Sprite):
def __init__(self, xDelta, yDelta, color):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface([20,20])
self.image.fill(white)
self.image.set_colorkey(white)
self.xDelta = xDelta
self.yDelta = yDelta
self.color = color
self.rect = self.image.get_rect()
pygame.draw.circle(self.image, self.color,
[(self.rect.x + 10), (self.rect.y + 10)], 10)
def update(self):
self.rect.x += self.xDelta
self.rect.y += self.yDelta
if self.rect.y <= 0 or self.rect.y >= 380:
self.yDelta *= -1
if self.rect.x >= 380 or self.rect.x <= 0:
self.xDelta *= -1
allSprites = pygame.sprite.Group()
blues = pygame.sprite.Group()
reds = pygame.sprite.Group()
for i in range(5):
circle = Ball(randint(1, 10), randint(1, 10), blue)
circle.rect.x = randint(0,380)
circle.rect.y = randint(0,380)
blues.add(circle)
allSprites.add(circle)
circle2 = Ball(randint(1, 10), randint(1, 10), red)
circle2.rect.x = randint(0,380)
circle2.rect.y = randint(0,380)
reds.add(circle2)
allSprites.add(circle2)
play = True
while play:
clock.tick(20)
for event in pygame.event.get():
if event.type == pygame.QUIT:
play = False
allSprites.update()
collision = pygame.sprite.spritecollide(blues, reds, True)
for circle in collision:
circle.xDelta = circle.yDelta = 0
screen.fill(white)
allSprites.draw(screen)
pygame.display.flip()
pygame.quit()
You are calling spritecollide on two objects of class Group. The function takes a Sprite and a Group.
spritecollide(sprite, group, dokill, collided = None) -> Sprite_list
What you could do is loop through all the blues sprites, and call spritecollide with one blue ball.
for blueball in blues.sprites:
collision = pygame.sprite.spritecollide(blues, reds, True)
for circle in collision:
circle.xDelta = circle.yDelta = 0

Pygame lab: creating Rectangle class with "draw" & "move" method

I'm working on a lab 8 on programarcadegames: http://programarcadegames.com/index.php?chapter=lab_classes_and_graphics - and got stuck with move method in a Rectangle class. Will appreciate any hints.
import pygame
black = ( 0, 0, 0)
white = ( 255, 255, 255)
green = ( 0, 255, 0)
red = ( 255, 0, 0)
class Rectangle():
def draw(self, x, y, height, width, screen):
self.x = x
self.y = y
self.height = height
self.width = width
self.screen = screen
pygame.draw.rect(self.screen, white, [self.x, self.y, self.height, self.width])
print "meth draw: x,y", self.x, self.y
def move(self, change_x, change_y):
self.change_x = change_x
self.change_y = change_y
self.x += self.change_x
self.y += self.change_y
if self.x > 300 or self.x < 0:
self.change_x = -self.change_x
if self.y > 300 or self.y < 0:
self.change_y = -self.change_y
print "x,y,s_x,s_y", self.x, self.y, self.change_x, self.change_y # debug
pygame.init()
size = [300,300]
screen = pygame.display.set_mode(size)
pygame.display.set_caption("My Game")
done = False
clock = pygame.time.Clock()
myObject = Rectangle()
# -------- Main Program Loop -----------
while done == False:
# ALL EVENT PROCESSING SHOULD GO BELOW THIS COMMENT
for event in pygame.event.get(): # User did something
if event.type == pygame.QUIT: # If user clicked close
done = True # Flag that we are done so we exit this loop
screen.fill(black)
myObject.draw(100, 100, 50, 50, screen)
myObject.move(10, 10)
pygame.display.flip()
clock.tick(20)
pygame.quit()
You have to change draw() because in every loop you draw retangle in the same place so you can't see result of your move.
class Rectangle:
def __init__(self, x, y, width, height, screen):
self.x = x
self.y = y
self.width = width
self.height = height
self.screen = screen
def draw(self):
pygame.draw.rect(self.screen, white, [self.x, self.y, self.height, self.width])
print "meth draw: x,y", self.x, self.y
def move(self, change_x, change_y):
# rest of your code
# rest of your code
myObject = Rectangle(100, 100, 50, 50, screen)
# rest of your code
myObject.move(10, 10) # move to new place
myObject.draw() # draw in new place
Very good hints furas - especially on order of drawing on screen. I did play a little bit more, set few debugs (prints) and eventually got it sorted. The issue was with "move" method - as each time the screen was drawn - I was resetting "change_x" and "change_y" to values given in method call. Of course that was wrong. What I did is I created extra method "speed" and I'm calling it BEFORE main program loop.
The correct version is listed below. For the sake of clarity I changed "change_x" and "change_y" to "speed_x" and "speed_y".
We may close this topic
import pygame
# Define some colors
black = ( 0, 0, 0)
white = ( 255, 255, 255)
green = ( 0, 255, 0)
red = ( 255, 0, 0)
class Rectangle():
def __init__(self, x, y, width, height):
self.x = x
self.y = y
self.height = height
self.width = width
def speed(self,*speed):
self.speed_x = speed[0]
self.speed_y = speed[1]
def draw(self, screen):
self.screen = screen
pygame.draw.rect(self.screen, white, [self.x, self.y, self.height, self.width])
print "meth draw: x,y", self.x, self.y
def move(self):
print "self.x", self.x
print "self.speed_x:", self.speed_x
self.x += self.speed_x
self.y += self.speed_y
if self.x > 250 or self.x < 0:
self.speed_x = -self.speed_x
if self.y > 250 or self.y < 0:
self.speed_y = -self.speed_y
pygame.init()
# Set the width and height of the screen [width,height]
size = [300,300]
screen = pygame.display.set_mode(size)
pygame.display.set_caption("My Game")
done = False
clock = pygame.time.Clock()
myObject = Rectangle(100, 100, 50, 50,)
myObject.speed(2,4)
# -------- Main Program Loop -----------
while done == False:
for event in pygame.event.get(): # User did something
if event.type == pygame.QUIT: # If user clicked close
done = True # Flag that we are done so we exit this loop
screen.fill(black)
myObject.move()
myObject.draw(screen)
pygame.display.flip()
clock.tick(20)
pygame.quit()