How to stop cursor lag in Pygame? - pygame

I have created a shooting game using Pygame in which one's cursor turns into a cross-hair and moves around shooting static images. However, the lag on the cross-hair is simply unbearable as the image is continually redrawn and a new background imposed over it every clock tick.
My code is as below:
import pygame, sys, random
class Crosshair(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.image.load("ch.png").convert_alpha()
self.rect = self.image.get_rect()
self.gunshot = pygame.mixer.Sound("gs.wav")
def shoot(self):
self.gunshot.play()
pygame.sprite.spritecollide(crosshair, target_group, True)
def update(self):
self.rect.center = pygame.mouse.get_pos()
class Target(pygame.sprite.Sprite):
def __init__(self, pos_x, pos_y):
super().__init__()
self.image = pygame.image.load("al.png").convert_alpha()
self.rect = self.image.get_rect()
self.rect.center = [pos_x, pos_y]
pygame.init()
clock = pygame.time.Clock()
screen_width = 1920
screen_height = 1080
screen = pygame.display.set_mode((screen_width, screen_height))
background = pygame.image.load("hoc.png").convert_alpha()
pygame.mouse.set_visible(False)
crosshair = Crosshair()
crosshair_group = pygame.sprite.Group()
crosshair_group.add(crosshair)
target_group = pygame.sprite.Group()
for target in range(20):
new_target = Target(random.randrange(0, screen_width), random.randrange(0, screen_height))
target_group.add(new_target)
Running = True
while Running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN:
crosshair.shoot()
pygame.display.flip()
screen.blit(background, (0, 0))
target_group.draw(screen)
crosshair_group.draw(screen)
crosshair_group.update()
clock.tick(60)
Is there any way to make the cross-hair motion smoother? Most of the code is taken almost directly from a Youtube tutorial, but for some reason I am experiencing this problem when others are not.

It is a matter of indentation. You must update and draw the scene in the application loop rather than the event loop. The application loop is executed once per frame, but the event loop is only executed when an event occurse. Actually the call of clock.tick(60) slows down your application after each event instead of each frame:
while Running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN:
crosshair.shoot()
# INDENTATION
#<--|
pygame.display.flip()
screen.blit(background, (0, 0))
target_group.draw(screen)
crosshair_group.draw(screen)
crosshair_group.update()
clock.tick(60)

Related

how can i add an image background? [duplicate]

new to pygame just wondering how i would go about adding a background image into the game itself? this is my code so far, i've been using the bg as a way to import my image but the py file itself refuses to load up.
import pygame
import sys
from pygame.locals import *
clock = pygame.time.Clock()
screen = pygame.display.set_mode((600,500))
bg = pygame.image.load("images\space.png")
pygame.mouse.set_visible(0)
ship = pygame.image.load("images\ship.png")
ship_top = screen.get_height() - ship.get_height()
ship_left = screen.get_width()/2 - ship.get_width()/2
screen.blit(ship, (ship_left,ship_top))
shot = pygame.image.load("images\shot.png")
shoot_y = 0
pygame.display.set_caption('galaxy invaders')
while True:
clock.tick(60)
screen.fill((r,0,0))
screen.blit(bg.(0,0))
x,y = pygame.mouse.get_pos()
screen.blit(ship, (x-ship.get_width()/2, ship_top))
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == MOUSEBUTTONDOWN:
shoot_y = 500
shoot_x = x
if shoot_y > 0:
screen.blit(shot, (shoot_x, shoot_y))
shoot_y -= 10
pygame.display.update()
For background I always make an image the size of my game window or smaller then before all of the images are displayed, I blit that image to 0,0.
bg = pygame.image.load("bg.png")
#INSIDE OF THE GAME LOOP
gameDisplay.blit(bg, (0, 0))
#REST OF ITEMS ARE BLIT'D TO SCREEN.
Hope this helps.
This problem can be easily solved. You will need an image the size of your screen for your background. Please remember to add pygame.init() at the beginning of your game to be able to start it and its abilities. A function for this picture can be used like this:
class Background(pygame.sprite.Sprite):
def __init__(self, image_file, location):
pygame.sprite.Sprite.__init__(self) #call Sprite initializer
self.image = pygame.image.load(image_file)
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = location
This will allow the program to load your image through this function when you call it like this:
BackGround = Background('background_image.png', [0,0])
And you will also need these two lines in your while loop:
screen.fill([255, 255, 255])
screen.blit(BackGround.image, BackGround.rect)
This will fill your screen white and put the background image over it but under your other sprites and objects.
Suggestions:
You should make another class for your other sprite (maybe the reason why the image is not appearing). An example could be like:
class Ship(pygame.sprite.Sprite):
def __init__(self, image_file, speed, location):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load(image_file)
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = location
You could then "activate" it like this:
ship = Ship("images\ship.png", [a, b])
Select the coordinates for a and b. You can then blit the image on to the screen like this but after your background blit statement:
screen.blit(ship.image, ship.rect)
I hope this helps you!
First of all, none of this will work because you did not initialize Pygame after importing it. Also, the pictures won't be loaded because the backslash indicates an escape seqeunce. Lastly, you should fix your indentation.
import pygame
import sys
from pygame.locals import *
pygame.init() # initialize pygame
clock = pygame.time.Clock()
screen = pygame.display.set_mode((600,500))
# os.path.join properly forms a cross-platform relative path
# by joining directory names
bg = pygame.image.load(os.path.join("images", "space.png"))
pygame.mouse.set_visible(0)
ship = pygame.image.load(os.path.join("images", "ship.png"))
ship_top = screen.get_height() - ship.get_height()
ship_left = screen.get_width()/2 - ship.get_width()/2
screen.blit(ship, (ship_left,ship_top))
shot = pygame.image.load(os.path.join("images", "space.png"))
shoot_y = 0
pygame.display.set_caption('galaxy invaders')
# fix indentation
while True:
clock.tick(60)
screen.blit(bg, (0,0))
x,y = pygame.mouse.get_pos()
screen.blit(ship, (x-ship.get_width()/2, ship_top))
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == MOUSEBUTTONDOWN:
shoot_y = 500
shoot_x = x
if shoot_y > 0:
screen.blit(shot, (shoot_x, shoot_y))
shoot_y -= 10
pygame.display.update()

pygame background image not showing

I am working on a pygame space invaders game. When I try to blit the background image it doesn't work. I am fairly new to pygame so I don't know what to do. I tried downloading another picture and using that but the problem persists.
import pygame
import os
import time
import random
import math
import sys
pygame.init()
screen = pygame.display.set_mode((750,750))
pygame.display.set_caption("Space Invaders")
FPS = 60
clock = pygame.time.Clock()
#Ships
RED_SPACE_SHIP = pygame.image.load(os.path.join('assets','pixel_ship_red_small.png'))
GREEN_SPACE_SHIP = pygame.image.load(os.path.join('assets','pixel_ship_green_small.png'))
BLUE_SPACE_SHIP = pygame.image.load(os.path.join('assets','pixel_ship_blue_small.png'))
YELLOW_SPACE_SHIP = pygame.image.load(os.path.join('assets','pixel_ship_yellow.png'))
#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'))
YELLOW_LASER = pygame.image.load(os.path.join('assets','pixel_laser_yellow.png'))
GREEN_LASER = pygame.image.load(os.path.join('assets','pixel_laser_green.png'))
#Background
BG = pygame.transform.scale2x(pygame.image.load(os.path.join('assets','background-black.png')).convert_alpha())
def main():
run = True
def redraw_window():
screen.blit(BG, (0, 0))
pygame.display.update()
while True:
clock.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
screen.blit(BG, (0,0))
pygame.display.update
clock.tick(120)
pygame.quit()
You need parenthesis when calling display update:
screen.blit(BG, (0,0))
pygame.display.update() # need parenthesis
clock.tick(120)

Change colour of sprite rect from group

I have a group of rects, they display in a row. I want them to change their colour when they have been clicked, until they are clicked again
I have this code so far to create the sprites:
class DrawableRect(pygame.sprite.Sprite):
def __init__(self,color,width,height,value=0):
super().__init__()
self.image = pygame.Surface([width, height])
self.image.fill(color)
self.rect = self.image.get_rect()
self.value = value
self.x = 0
self.y = 0
def change_value(self,color,value):
self.image.fill(color)
self.value=value
def DrawRects(start_x, start_y, rect_spacing, colour_list):
current_x_pos = start_x
for rect_num in range(0,8):
rect = DrawableRect(colour_list[rect_num], boxW, boxH)
rect.rect.x = current_x_pos
rect.rect.y = start_y
current_x_pos = current_x_pos + rect.rect.width + rect_spacing
rects.add(rect)
rects.draw(screen)
The idea of the app is for each rectangle to represent a bit, and when pressed it alternates between 0 and 1, the makeup of each bit displays the decimal equivalent somewhere.
I read that groups are unordered therefore indexing wouldn't work, is that true?
Here's an example I've modified to suit your purposes. I have a bunch of sprites (coloured rectangles) in a sprite group and I change* the colour of any sprite that collides with the mouse pointer when a mouse button is pressed.
Here's the code, you're probably most interested in the change_color() method and the MOUSEBUTTONUP event handling code.
import random
import pygame
screen_width, screen_height = 640, 480
def get_random_position():
"""return a random (x,y) position in the screen"""
return (random.randint(0, screen_width - 1), #randint includes both endpoints.
random.randint(0, screen_height - 1))
color_list = ["red", "orange", "yellow", "green", "cyan", "blue", "blueviolet"]
colors = [pygame.color.Color(c) for c in color_list]
class PowerUp(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
width, height = 64, 32
self.image = pygame.Surface([width, height])
self.clicked = False # track whether we've been clicked or not
# initialise color
self.color = random.choice(colors)
self.image.fill(self.color)
# Fetch the rectangle object that has the dimensions of the image
self.rect = self.image.get_rect()
# then move to a random position
self.update()
def update(self):
#move to a random position
self.rect.center = get_random_position()
def random_color(self):
# randomise color
self.clicked = not self.clicked
if self.clicked:
color = random.choice(colors)
else:
color = self.color
self.image.fill(color)
if __name__ == "__main__":
pygame.init()
screen = pygame.display.set_mode((screen_width, screen_height))
pygame.display.set_caption('Sprite Color Switch Demo')
clock = pygame.time.Clock() #for limiting FPS
FPS = 60
exit_demo = False
pygame.key.set_repeat(300, 200)
#create a sprite group to track the power ups.
power_ups = pygame.sprite.Group()
for _ in range(10):
power_ups.add(PowerUp()) # create a new power up and add it to the group.
# main loop
while not exit_demo:
for event in pygame.event.get():
if event.type == pygame.QUIT:
exit_demo = True
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
exit_demo = True
elif event.key == pygame.K_SPACE:
power_ups.update()
elif event.type == pygame.MOUSEBUTTONUP:
# check for collision
for p in power_ups:
if p.rect.collidepoint(event.pos): # maybe use event?
p.random_color()
screen.fill(pygame.Color("black")) # use black background
power_ups.draw(screen)
pygame.display.update()
clock.tick(FPS)
pygame.quit()
quit()
Let me know if you have any questions. Obviously this doesn't do row alignment of the sprites, I think you have a handle on that. I would suggest that you have all of your screen drawing operations in one place so your code can be clearer.
*The new colour is randomised from a short list, so there's a 14% chance it won't change from the starting colour.

Pygame: Creating bullets - 'Bullet' object is not callable

I am trying to have pygame create a new Bullet object every time the mouse is pressed. It works on the first bullet, but on the second Bullet I am getting a "'Bullet' object is not callable' error.
class Bullet(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((20,40))
self.image.fill(black)
self.rect = self.image.get_rect()
self.rect.x = player_1.rect.x
self.rect.y = player_1.rect.y
#self.image = pygame.image.load("C:/Users/Thomas/Desktop/rocket.png")
self.width = self.image.get_width()
self.height = self.image.get_height()
self.speed = 20
self.damage_radius = 0
self.damage = 0
self.angle = 0
def draw(self):
self.image.fill(black)
screen.blit(self.image, (self.rect.x, self.rect.y))
def delete(self):
pass
def fire(self, mouse):
"""Determine agnle of which rocket is fired"""
pass
def update(self):
self.rect.y -= self.speed
def game_intro():
intro = True
while intro:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
screen.fill(intro_bg_color)
large_text = pygame.font.Font("C:/Users/Thomas/Desktop/Python Sketches/Fonts/Quesat Black Demo.OTF", 100)
player_1 = Player((100,100), 20, blue)
while True:
pygame.display.update()
clock.tick(FPS)
mouse = pygame.mouse.get_pos()
gameEvent = pygame.event.get()
"""Draw"""
"""This wil be for advancing bullets in playing field"""
screen.fill(bg_color)
player_1.draw(mouse)
for Bullet in bullet_list:
Bullet.update()
if Bullet.rect.y < 0:
bullet_list.remove(Bullet)
Bullet.draw()
for event in gameEvent:
if event.type == pygame.QUIT:
pygame.quit()
elif event.type == pygame.MOUSEBUTTONDOWN and pygame.mouse.get_pressed()[0]:
mouse_is_pressed = True # Used to determine if the mouse is held or not
print("Mouse pressed")
"""Generate new bullet"""
bullet = Bullet()
all_sprites_list.add(bullet)
bullet_list.add(bullet)
bullet.rect.x = player_1.rect.x
bullet.rect.y = player_1.rect.y
elif event.type == pygame.MOUSEBUTTONUP and pygame.mouse.get_rel()[0]:
mouse_is_pressed = False
print("Mouse button released")
When mouse is pressed, generate new Bullet, add it to bullet_list. This is down how an example I looked at is done and I just can't solve the issues. Help is greatly appreciated!
I took this block of code:
for Bullet in bullet_list:
Bullet.update()
Bullet.draw()
and change the "Bullet" to "bullet" and the code finally started working.
for bullet in bullet_list:
bullet.update()
bullet.draw()

pygame, getting a sprite to move to cursor position on click

I was trying to make a tower defense sort of game in pygame. When I click on the screen, I want it to be able to move the tower sprite to the position. Currently, when I run the program, it moves the sprite to the cursor with out clicking, then does not let me move it again.
import pygame
import time
pygame.init()
WINDOWWIDTH = 1800
WINDOWHEIGHT = 1800
screen = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))
background_colour = (188,69,80)
GAMETITLE = "Tower Defence"
def main():
pygame.display.set_caption(GAMETITLE)
clock = pygame.time.Clock()
spritegroup =pygame.sprite.Group()
sprite =pygame.sprite.Sprite()
tower = sprite.image = pygame.image.load("tower.png")
sprite.image = tower
sprite.rect = sprite.image.get_rect()
sprite.rect.x = 10
sprite.rect.y = 10
sprite.add(spritegroup)
while True:
screen.fill(background_colour)
pygame.draw.rect(screen, (255,255,255), ((0, 100), (1100, 90)))
pygame.draw.rect(screen, (255, 255,255), ((1010, 100), (100, 600)))
pygame.draw.rect(screen, (255,255,255), ((1010, 700), (2400, 90)))
spritegroup.draw(screen)
pygame.display.update()
clock.tick(30)
if pygame.mouse.get_pressed():
cursorPos = pygame.mouse.get_pos()
sprite.rect.x = cursorPos[0]
sprite.rect.y = cursorPos[1]
main()
what is happening is that pygame.mouse.get_pressed() returns a tuple. if statements return true if they are not null. this means that regardless if it is pressed it will return True. then the pygame event queue will fill with mouse events and never get processed. your event loop should look something like:
for event in pygame.event.get():
if event.type == pygame.event.QUIT:
pygame.quit()
if event.type == pygame.MOUSEBUTTONDOWN:
#stuff to do on click