I'm trying to build a simple version of an aim trainer using pygame and have decided to change the original mouse pointer to a crosshair (an image). When testing if the image has been blitted on the mouse rect I noticed that the image is significantly lagging behind the position of the mouse.
I have tried to tamper with the FPS of the game by setting the clock.tick() to different integers. Tried loading the images in a different part of the code. However nothing seems to change the lag.
import pygame
pygame.init()
from win32api import GetSystemMetrics ## screen size getter so that theres no need to use coordinates, can be run on multiple resolutions
screen_width = GetSystemMetrics(0) ## get screen width and hieght
screen_hieght = GetSystemMetrics(1)
class GameWindow():
screen = pygame.display.set_mode((0,0), pygame.FULLSCREEN)
pygame.display.toggle_fullscreen()
caption = pygame.display.set_caption("Alex's FPS Trainer")
main_screen_font = pygame.font.SysFont('consolas', 100)
main_screen_background = pygame.image.load("fps_background.jpg") ## loading images
xhair_image = pygame.image.load("crosshair.png")
def __init__(self):
self.background = pygame.transform.scale(GameWindow.main_screen_background, (screen_width, screen_hieght))
self.title = self.main_screen_font.render("Alex's FPS Aim Trainer", 1, (245, 66, 66))
self.run()
def blit(self):
self.screen.blit(self.background, (0, 0))
self.screen.blit(self.title, (screen_width/2 - screen_width/3.5, screen_hieght/5))
self.screen.blit(GameWindow.xhair_image, (pygame.mouse.get_pos()[0] - 13.5,pygame.mouse.get_pos()[1] - 13.5)) ## centres mouse
def createButton(self):
pass
def run(self):
run = True
while run:
pygame.time.Clock().tick(120)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE: ## temporary quit key
run = False
if event.type == pygame.MOUSEBUTTONDOWN: ## detect clicks
print("Mouse pressed")
self.blit()
pygame.display.update()
pygame.quit()
GameWindow = GameWindow()
I was hoping that the image would follow the mouse without lag since it is important for the crosshair in an aim trainer to follow the mouse well.
The easiest solution is to change the mouse cursor by win32api.SetCursor():
win32api.SetCursor(cursor)
A cursor can be loaded by win32gui.LoadImage() from a .cur file:
cursor = win32gui.LoadImage(0, "cursor.cur", win32con.IMAGE_CURSOR,
0, 0, win32con.LR_LOADFROMFILE)
or from an .ico file.
cursor = win32gui.LoadImage(0, path + "cursor.ico", win32con.IMAGE_ICON,
0, 0, win32con.LR_LOADFROMFILE)
See also Cursors and LoadImage.
To avoid flickering it is important to make the "pygame" cursor invisible by pygame.mouse.set_visible()
pygame.mouse.set_visible(False)
Set the cursor immediately before pygame.display.update() respectively pygame.display.flip(), to make it "visible" and to give pygame no chance to "hide" it.
win32api.SetCursor(cursor)
pygame.display.update()
See the example application:
import pygame
import win32api, win32gui, win32con
pygame.init()
screen = pygame.display.set_mode((640, 480))
cursor = win32gui.LoadImage(0, path + "cursor.ico", win32con.IMAGE_ICON,
0, 0, win32con.LR_LOADFROMFILE)
pygame.mouse.set_visible(False)
run = True
while run:
pygame.time.Clock().tick(120)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
win32api.SetCursor(cursor)
pygame.display.update()
pygame.quit()
Related
Here's the code. I have both images correctly named in the same folder, so thats not the issue. Both the runner.png and the rectangle for the button don't show, but the background does.
class gamewindow():
def __init__(self):
pygame.init()
self.game = pygame.display.set_mode((1300, 768))
background = pygame.image.load('temporarybackground.jpg')
self.green = (1,255,1)
self.buttonstart = pygame.draw.rect(self.game, self.green,(100,100, 20, 20))
self.runner = pygame.image.load('runner.png')
from sys import exit
while True:
self.game.fill((0,0,0))
self.game.blit(background, (0, 0))
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
exit()
pygame.display.flip()
JasonHarper's comment is correct. You're not drawing the button or runner in the loop. When you redraw the background, the other objects are erased.
Corrected code:
class gamewindow():
def __init__(self):
pygame.init()
self.game = pygame.display.set_mode((1300, 768))
background = pygame.image.load('temporarybackground.jpg')
self.green = (1,255,1)
self.runner = pygame.image.load('runner.png')
from sys import exit
while True:
self.game.fill((0,0,0)) # clear screen
self.game.blit(background, (0, 0)) # draw background
self.game.blit(self.runner, (50, 0)) # draw runner
pygame.draw.rect(self.game, self.green,(100,100, 20, 20)) # draw button
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
exit()
pygame.display.flip()
You also have your game code in a class constructor, which seems odd. It may be better to put your game in a standard function.
I am trying to allow resizing for this app, I put the RESIZABLE flag, but when I try to resize, it messes up! Try my code.
It is a grid program, when the window resizes I want the grid to also resize/shrink.
import pygame,math
from pygame.locals import *
# Define some colors
black = ( 0, 0, 0)
white = ( 255, 255, 255)
green = ( 0, 255, 0)
red = ( 255, 0, 0)
# This sets the width and height of each grid location
width=50
height=20
size=[500,500]
# This sets the margin between each cell
margin=1
# Initialize pygame
pygame.init()
# Set the height and width of the screen
screen=pygame.display.set_mode(size,RESIZABLE)
# Set title of screen
pygame.display.set_caption("My Game")
#Loop until the user clicks the close button.
done=False
# Used to manage how fast the screen updates
clock=pygame.time.Clock()
# -------- 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
if event.type == pygame.MOUSEBUTTONDOWN:
height+=10
# Set the screen background
screen.fill(black)
# Draw the grid
for row in range(int(math.ceil(size[1]/height))+1):
for column in range(int(math.ceil(size[0]/width))+1):
color = white
pygame.draw.rect(screen,color,[(margin+width)*column+margin,(margin+height)*row+margin,width,height])
# Limit to 20 frames per second
clock.tick(20)
# Go ahead and update the screen with what we've drawn.
pygame.display.flip()
# Be IDLE friendly. If you forget this line, the program will 'hang'
# on exit.
pygame.quit ()
Please tell me whats wrong, thanks.
The answer for this problem (allow the Pygame window and the surface inside it to resize) is simply to recreate the resizable window with an updated size, when the user changes its dimensions (done on pygame.VIDEORESIZE events).
>>> import pygame
>>> help(pygame.display.set_mode)
Help on built-in function set_mode in module pygame.display:
set_mode(...)
set_mode(size=(0, 0), flags=0, depth=0, display=0, vsync=0) -> Surface
Initialize a window or screen for display
>>>
This removes all previous content on the window surface, so below
there's a process to continue with the current window content.
Some example code:
import pygame, sys
pygame.init()
# Create the window, saving it to a variable.
surface = pygame.display.set_mode((350, 250), pygame.RESIZABLE)
pygame.display.set_caption("Example resizable window")
while True:
surface.fill((255,255,255))
# Draw a red rectangle that resizes with the window.
pygame.draw.rect(surface, (200,0,0), (surface.get_width()/3,
surface.get_height()/3, surface.get_width()/3,
surface.get_height()/3))
pygame.display.update()
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
pygame.quit()
sys.exit()
if event.type == pygame.VIDEORESIZE:
# There's some code to add back window content here.
surface = pygame.display.set_mode((event.w, event.h),
pygame.RESIZABLE)
How to continue with the current window content:
Here's some steps to add back the previous window content:
make a second variable, set to the value of the old window surface variable.
create the new window, storing it as the old variable.
draw the second surface onto the first one (old variable) - use the blit function.
use this variable and delete the new variable (optional, use del) to not use extra memory.
Some example code for the above steps (replaces pygame.VIDEORESIZE event if statement):
if event.type == pygame.VIDEORESIZE:
old_surface_saved = surface
surface = pygame.display.set_mode((event.w, event.h),
pygame.RESIZABLE)
# On the next line, if only part of the window
# needs to be copied, there's some other options.
surface.blit(old_surface_saved, (0,0))
del old_surface_saved
You are not updating your width, height, or size when the window changes.
From the docs: http://www.pygame.org/docs/ref/display.html
If the display is set with the pygame.RESIZABLE flag,
pygame.VIDEORESIZE events will be sent when the user adjusts the
window dimensions.
You can get the new size, w, h from the event VIDEORESIZE http://www.pygame.org/docs/ref/event.html
A simple Hello World window that is resizable, plus I was playing around with classes.
Broken down into two files, one for defining the colour constants.
import pygame, sys
from pygame.locals import *
from colors import *
# Data Definition
class helloWorld:
'''Create a resizable hello world window'''
def __init__(self):
pygame.init()
self.width = 300
self.height = 300
DISPLAYSURF = pygame.display.set_mode((self.width,self.height), RESIZABLE)
DISPLAYSURF.fill(WHITE)
def run(self):
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
elif event.type == VIDEORESIZE:
self.CreateWindow(event.w,event.h)
pygame.display.update()
def CreateWindow(self,width,height):
'''Updates the window width and height '''
pygame.display.set_caption("Press ESC to quit")
DISPLAYSURF = pygame.display.set_mode((width,height),RESIZABLE)
DISPLAYSURF.fill(WHITE)
if __name__ == '__main__':
helloWorld().run()
colors.py:
BLACK = (0, 0,0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
YELLOW = (255, 255, 0)
BLUE = (0,0,255)
GREEN = (0,255,0)
A simple way to do which I found was the following code snippet
# Imports
from vars import *
from pygame.locals import *
# Main init
pygame.init()
# Basic vars
run = True
s_width = 1000
s_height = 600
# Making display screen. Don't forget the last tag!
screen = pygame.display.set_mode((s_width, s_height), RESIZABLE)
# Main loop
while run:
# event detection
for event in pygame.event.get():
if event.type == QUIT:
run = False
# The part which matters for our purposes
if event.type == WINDOWRESIZED:
s_width, s_height = screen.get_width(), screen.get_height()
if event.type == KEYDOWN:
if event.key == K_ESCAPE:
run = False
# Test line to see if the window resizing works properly
pygame.draw.line(screen, (255, 255, 255), (int(0.3*s_width), int(0.25*s_height)), (int(0.8*s_width), int(0.25*s_height)))
# Final flip
pygame.display.flip()
# Quit
pygame.quit()
What this does is allows the pygame window to be resized. But since you often have the placing and sizes of a lot of elements/sprites depending on the s_width and s_height, it also detects when the window size is changed and adjusts the dimensions accordingly.
First, You don't detect the new window size before redrawing the screen.
Add the get_size() method at line 45 and it works:
#--------------------------------------------------------------
# Draw the grid
size = pygame.display.get_surface().get_size() // size update
for row in range(int(math.ceil(size[1]/height))+1):
#---------------------------------------------------------
Then you work with a fixed cell size (50, 20) and fill as many cells as possible. If You want to GROW/SHRINK the cells when resizing the window, You will have to define the NUMBER of cells per line/row, then calculate the cell size, then draw them.
Whenever I try to include "screen.fill(insert value here)" to my pygame code, the window instantly crashes. I can comment it out and my code works just fine. I'm really not sure why.
I've tried moving the line around, using other people's code in part - no luck.
import pygame
pygame.init()
win = pygame.display.set_mode((1000, 800))
pygame.display.set_caption("Tetris")
img=pygame.image.load("C:\\Users\\Charlotte\\Desktop\\tetris_grid.png")
win.blit(img,(450, 150))
running = True
while running:
screen.fill(0, 0, 0)
pygame.display.update()
pygame.time.delay(100)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
pygame.quit()
When I run the code with that line in it, the window opens and then it instantly closes.
If you are trying to fill the screen with black, the color must be passed as a tuple:
win.fill((0, 0, 0))
Also, you never assign screen, maybe you intended to use win?
The doc for Surface.fill.
The color parameter to fill() has to be either a single grayscale value or a tuple of 3 RGB or 4 RGBA components. So it has to be either:
win.fill(0)
or
win.fill((0, 0, 0))
Further you've to blit() the image in the main loop. To continuously draw the scene in the main application loop, you've to:
handle the events
clear the window
draw the scene (blit the image)
update the display
Furthermore I recommend to use tick() of pygame.time.Clock rather than pygame.time.delay() tpo control the frames per second.
import pygame
pygame.init()
win = pygame.display.set_mode((1000, 800))
pygame.display.set_caption("Tetris")
clock = pygame.time.Clock()
img=pygame.image.load("C:\\Users\\Charlotte\\Desktop\\tetris_grid.png")
running = True
while running:
clock.tick(60)
# handle the events
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# clear the display
win.fill(0)
# draw the scene
win.blit(img,(450, 150))
# update the display
pygame.display.update()
pygame.quit()
Does anyone know if there is a way to make pygame scripts run one after another?
This is my module below, I would like to know if I could get them to deploy at certain intervals, that is, I would like 'A_long_time_ago' to run, last for 5 seconds then close and immediately load 'sounds' and 'logo', then 3 seconds later for them to close, transitioning into 'Title_Crawl'.
The Module
from A_long_time_ago import *
from sounds import *
from logo import *
from Title_Crawl import *
A_long_time_ago
import pygame
import time
pygame.init()
screen = pygame.display.set_mode((1376, 760))
clock = pygame.time.Clock()
done = False
font = pygame.font.SysFont("franklingothicbook", 40)
text = font.render("A long time ago in a galaxy far, far away....", True, (75, 213, 238))
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
done = True
screen.fill((0, 0, 0))
screen.blit(text,
(700 - text.get_width() // 2, 380 - text.get_height() // 2))
pygame.display.flip()
clock.tick(60)
sounds
import pygame
pygame.mixer.init()
pygame.mixer.pre_init(44100, -16, 2, 2048)
pygame.init()
pygame.mixer.music.load('The_Theme.wav')
pygame.mixer.music.play()
logo
import pygame
import os
_image_library = {}
def get_image(path):
global _image_library
image = _image_library.get(path)
if image == None:
canonicalized_path = path.replace('/', os.sep).replace('\\', os.sep)
image = pygame.image.load(canonicalized_path)
_image_library[path] = image
return image
pygame.init()
screen = pygame.display.set_mode((1000, 800))
done = False
clock = pygame.time.Clock()
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
screen.fill((0, 0, 0))
screen.blit(get_image('sw.png'), (10, 10))
pygame.display.flip()
clock.tick(60)
Title_Crawl
#!/usr/bin/python
import pygame
from pygame.locals import *
pygame.init()
pygame.display.set_caption('Title Crawl')
screen = pygame.display.set_mode((1000, 800))
screen_r = screen.get_rect()
font = pygame.font.SysFont("franklingothicdemibold", 40)
clock = pygame.time.Clock()
def main():
crawl = ["Star Wars - The Wilds"," ","It is a dark time for the Galaxy. The evil Dark","Lord, Vitiate is rising to power. Alone, a single", "spec is on a trip, a trip that will ultimately", "rectify the wrongs of the galaxy. The keepers ", "of peace are dying out and the DARK SIDE is", "lurking, a conniving force determined to", "become the omniarch."]
texts = []
# we render the text once, since it's easier to work with surfaces
# also, font rendering is a performance killer
for i, line in enumerate(crawl):
s = font.render(line, 1, (229, 177, 58))
# we also create a Rect for each Surface.
# whenever you use rects with surfaces, it may be a good idea to use sprites instead
# we give each rect the correct starting position
r = s.get_rect(centerx=screen_r.centerx, y=screen_r.bottom + i * 45)
texts.append((r, s))
while True:
for e in pygame.event.get():
if e.type == QUIT or e.type == KEYDOWN and e.key == pygame.K_ESCAPE:
return
screen.fill((0, 0, 0))
for r, s in texts:
# now we just move each rect by one pixel each frame
r.move_ip(0, -1)
# and drawing is as simple as this
screen.blit(s, r)
# if all rects have left the screen, we exit
if not screen_r.collidelistall([r for (r, _) in texts]):
return
# only call this once so the screen does not flicker
pygame.display.flip()
# cap framerate at 60 FPS
clock.tick(60)
if __name__ == '__main__':
main()
The best way to do this would be to make them into classes and give them a run method, and then instantiate each one on a timer and call its run method to do what it needs to do.
I can't get a very simple pygame script to work:
import pygame
class MainWindow(object):
def __init__(self):
pygame.init()
pygame.display.set_caption('Game')
pygame.mouse.set_visible(True)
# pygame.mouse.set_visible(False) # this doesn't work either!
screen = pygame.display.set_mode((640,480), 0, 32)
pygame.mixer.init()
while True:
print pygame.mouse.get_pos()
pygame.mixer.quit()
pygame.quit()
MainWindow()
This just outputs (0,0) as I move the mouse around the window:
(0, 0)
(0, 0)
(0, 0)
(0, 0)
(0, 0)
Can anyone check this?
Edit - fixed code:
import pygame
class MainWindow(object):
def __init__(self):
pygame.init()
pygame.display.set_caption('Game')
pygame.mouse.set_visible(True)
# pygame.mouse.set_visible(False) # this doesn't work either!
screen = pygame.display.set_mode((640,480), 0, 32)
pygame.mixer.init()
while True:
for event in pygame.event.get():
if event.type == pygame.MOUSEMOTION:
print pygame.mouse.get_pos()
pygame.mixer.quit()
pygame.quit()
MainWindow()
Pygame will constantly dispatch events while it's running. These need to be handled in some way or pygame will hang and not do anything. Simplest way to fix it is by adding this to your main loop:
...
while True:
for event in pygame.event.get():
pass
print pygame.mouse.get_pos()
...
I never used this before, but I found that
Pygame: Mouse Specific Axis Detection
You need to wait until an event happens. I assume this empties the stack and allows you to get the data later on.
for event in pygame.event.get()