My code isn't working and I don't understand it why, I'm working with module PYGAME and I want do a animation (three images) when I push the key "s". My code is this:
import pygame, sys
from pygame.locals import *
from time import *
pygame.init()
ventana = pygame.display.set_mode((400,300))
pygame.display.set_caption("Hi!")
imgA = pygame.image.load("IMG/fan_azul.png")
imgB = pygame.image.load("IMG/fan_naranja.png")
imgC = pygame.image.load("IMG/fan_rojo.png")
listaImg = [imgA,imgB,imgC]
POS,aux,e = 0,1,0
picture = listaImg[POS]
posX,posY,fondo = 200,100,(50,50,50)
while True:
ventana.fill(fondo)
#ventana.blit(picture,(posX,posY))
for evento in pygame.event.get():
if evento.type == QUIT:
pygame.quit()
sys.exit()
elif evento.type == KEYDOWN:
if evento.key == K_s:
for e in range(0,3):
POS = e
picture = listaImg[POS]
ventana.blit(picture,(posX,posY))
print "TIEMPO MEDIDO: "+str(POS)
pygame.time.wait(1000)
pygame.display.update()
It is important to avoid inner loops that are going to hang up processing events and other drawing to the frame. Remember you are usually trying to get 30 to 60 frames per second, and your K_s handler is taking 3 seconds by itself. Keep the handle events, update state, and draw screen parts all separate. To do an animation, I usually do something like this.
import pygame, sys
from pygame.locals import *
import time
pygame.init()
ventana = pygame.display.set_mode((400,300))
pygame.display.set_caption("Hi!")
FONDO = (50,50,50)
class AnimatedSprite(pygame.sprite.Sprite):
def __init__(self, position, filenames):
pygame.sprite.Sprite.__init__(self)
self.images = [pygame.image.load(filename) for filename in filenames]
self.delay = 0
self.start = 0
self.rect = pygame.Rect(position, (0, 0))
self._set_image(0)
def _set_image(self, image_num):
self.image_num = image_num
self.image = self.images[self.image_num]
self.rect.size = self.image.get_rect().size
def show(self, group, delay=1.0):
group.add(self)
self.delay = delay
self._set_image(0)
self.start = time.time()
def update(self):
if self.alive() and time.time() - self.start > self.delay:
if self.image_num + 1 < len(self.images):
self._set_image(self.image_num + 1)
else:
self.kill()
fan = AnimatedSprite((200, 10), ["IMG/fan_azul.png",
"IMG/fan_naranja.png",
"IMG/fan_rojo.png"])
animation = pygame.sprite.Group()
while True:
# Handle Events
for evento in pygame.event.get():
if evento.type == QUIT:
pygame.quit()
sys.exit()
elif evento.type == KEYDOWN:
if evento.key == K_s:
fan.show(animation)
# Update State
animation.update()
# Draw
ventana.fill(FONDO)
animation.draw(ventana)
pygame.display.update()
Related
import pygame
from sys import exit
import random
WIDTH, HEIGHT = 1400,750
FPS = 60
HEADWAY_GAP = 40
vehicleColor = {0:"red",1:"blue",2:"yellow"}
directions = ["Right", "Left"]
classofVehicle = ["Car","Bike","Bus"]
coord = {"Right":(0,300), "Left":(1400,350)}
objects = {"Right":[],"Left":[]}
pygame.init()
myWindow = pygame.display.set_mode((WIDTH, HEIGHT))
clock = pygame.time.Clock()
road = pygame.image.load("Required_images/Road/Road_final.png")
class Vehicle(pygame.sprite.Sprite):
def __init__(self, ClassofVehicle, Color_Code, direction, pos):
super().__init__()
self.Class = ClassofVehicle
self.Color_Code = Color_Code
self.direction = direction
self.pos = pos
path = "Required_images/"+ClassofVehicle+"/"+ClassofVehicle+"_"+Color_Code+".png"
self.image = pygame.image.load(path)
self.rect = self.image.get_rect()
if self.direction == "Right":
self.rect.midright = pos
else:
self.rect.midleft = pos
self.index = len(objects[direction])-1
def movingVehicles(self):
if self.direction == "Right":
if self.index == 0 or (self.rect.x + self.rect.width < objects[self.direction][self.index].rect.x - HEADWAY_GAP):
self.rect.x += 1
elif self.direction == "Left":
if self.index == 0 or (self.rect.x - self.rect.width > objects[self.direction][self.index].rect.x + HEADWAY_GAP):
self.rect.x -= 1
def update(self):
self.movingVehicles()
Object_timer = pygame.USEREVENT+1
pygame.time.set_timer(Object_timer, 1000)
objectsGroup = pygame.sprite.Group()
def generatingObjects():
Dir = random.choice(directions)
po = coord[Dir]
color_code = random.choice([0,1,2])
cla = random.choice(classofVehicle)
object = Vehicle(cla,vehicleColor[color_code],Dir,po)
objectsGroup.add(object)
objects[Dir].append(object)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
exit()
if event.type == Object_timer:
generatingObjects()
myWindow.fill((0,0,0))
objectsGroup.update()
objectsGroup.draw(myWindow)
pygame.display.update()
clock.tick(FPS)
I have created a sprite class and a sprite group. I also created an user defined event to generate vehicles for every 1000ms. To display the vehicles on the screen instead of looping through all the elements of the sprite group separately I used group_name.draw() method. But when I run the code, some of the images are getting stuck on the screen for some time and are moving after sometime. I tried to look for any error in the logic but I couldn't find it. If you have any knowledge or able to find the error, please help and also any kind of suggestions are appreciated.
It is a matter of Indentation. You have to draw the scene in the application loop instead of after the application loop:
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
exit()
if event.type == Object_timer:
generatingObjects()
# INDENTATION
#->|
myWindow.fill((0,0,0))
objectsGroup.update()
objectsGroup.draw(myWindow)
pygame.display.update()
clock.tick(FPS)
make sure to indent the elements in your while loop and use a pygame.display.flip() at the very end of your loop.
so it looks like
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
exit()
if event.type == Object_timer:
generatingObjects()
myWindow.fill((0,0,0))
objectsGroup.update()
objectsGroup.draw(myWindow)
clock.tick(FPS)
pygame.display.flip()
I previously implemented the following spritesheet code into a prototype that I was working on and it worked perfectly, but I'm attempting to implement it into my current project and it is no longer working and I cannot see any reason why that would be the case.
import math, random, sys
import pygame
from pygame.locals import *
def events():
for event in pygame.event.get():
if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE):
pygame.quit()
sys.exit()
class spritesheet:
def __init__(self, filename, cols, rows):
self.sheet = pygame.image.load(filename).convert_alpha()
self.cols = cols
self.rows = rows
self.totalCellCount = cols * rows
self.rect = self.sheet.get_rect()
w = self.cellWidth = self.rect.width / cols
h = self.cellHeight = self.rect.height / rows
hw, hh = self.cellCenter = (w / 2, h / 2)
self.cells = list([(index % cols * w, index // cols * h, w, h) for index in range(self.totalCellCount)])
self.handle = list([
(0, 0), (-hw, 0), (-w, 0),
(0, -hh), (-hw, -hh), (-w, -hh),
(0, -h), (-hw, -h), (-w, -h),])
def draw(self, surface, cellIndex, x, y, handle = 0):
surface.blit(self.sheet, (x + self.handle[handle][0], y + self.handle[handle][1]), self.cells[cellIndex])
Using the above spritesheet code, I render it to my screen with the below code:
import os,pygame, spritesheet
import time, datetime
black = (0,0,0)
SCREEN_WIDTH = 1920
SCREEN_HEIGHT = 1080
HW = SCREEN_WIDTH/2
HH= SCREEN_HEIGHT/2
# Setup the clock for a decent framerate
clock = pygame.time.Clock()
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
index = 0
def run_game(width, height, fps):
pygame.init()
global screen
screen = pygame.display.set_mode((width, height))
global clock
playing = True
while playing == True:
Render(screen)
#pygame.display.flip()
clock.tick(fps)
def Render(screen):
global index
global clock
CENTER_HANDLE=4
s = spritesheet.spritesheet("art_assets/christopher_working.png", 20,1)
spritesheet.events()
screen.fill(black)
s.draw(screen, index % s.totalCellCount, HW, HH, CENTER_HANDLE)
screen = pygame.transform.scale(screen,(1920,1080))
pygame.display.update()
index += 1
run_game(1920,1080,60)
and it works perfectly fine! When I try to run it in my current project, listed below, it no longer works properly...
import pygame as pg
import battlesystem
import player_dialog
import random
import spritesheet
import sys
from os import path
from settings import *
class Game:
def __init__(self):
pg.mixer.pre_init(44100, -16, 1, 2048)
pg.init()
self.screen = pg.display.set_mode((WIDTH, HEIGHT))
pg.display.set_caption(TITLE)
self.clock = pg.time.Clock()
pg.key.set_repeat(400, 80)
self.max_player_health = 100
self.current_player_health = self.max_player_health
self.current_enemy = ENEMIES['homework']
self.player_damage = 4
self.battle_flag = False
self.dialog_flag = False
self.christopher_working_flag = True
self.index = 0
self.i = 0
self.delay = 0
self.load_data()
def load_data(self):
# holds game folder path
game_folder = path.dirname(__file__)
# holds art assets
img_folder = path.join(game_folder, 'img')
# holds maps
self.map_folder = path.join(game_folder, 'maps')
# holds sounds
snd_folder = path.join(game_folder, 'snd')
# holds music
music_folder = path.join(game_folder, 'music')
self.player_img = pg.image.load(path.join(img_folder, PLAYER_IMG)).convert_alpha()
self.homework_img = pg.image.load(path.join(img_folder, HOMEWORK_IMG)).convert_alpha()
self.christopher_working = spritesheet.spritesheet('img/christopher_working.png', 20,1)
def run(self):
# game loop - set self.playing = False to end game
self.playing = True
while self.playing:
if self.battle_flag == True:
self.current_player_health = battlesystem.battle_system(self.screen, self.current_enemy, self.player_damage, self.current_player_health)
self.battle_flag = False
if self.dialog_flag == True:
player_dialog(temp_dialog)
self.events()
self.draw()
def quit(self):
pg.quit()
sys.exit()
def draw(self):
pg.display.set_caption("{:.2f}".format(self.clock.get_fps()))
#if self.christopher_working_flag == True:
self.screen.fill(BLACK)
self.christopher_working.draw(self.screen, self.index % self.christopher_working.totalCellCount, HW, HH, CENTER_HANDLE)
self.screen = pg.transform.scale(self.screen,(1024, 768))
self.index += 1
print(self.index % self.christopher_working.totalCellCount)
pg.display.update()
def events(self):
# catch all events here
if self.christopher_working_flag == True:
spritesheet.events()
for event in pg.event.get():
if event.type == pg.QUIT:
self.quit()
if event.type == pg.KEYDOWN:
if event.key == pg.K_ESCAPE:
self.quit()
#def update(self):
#if self.player_health <= 0: add this later
g = Game()
while True:
g.run()
I've removed a few lines here and there that were either just comments or code that isn't currently being used (for readability and post length).
When running my current code, the print function shows that my self.index variable is incrementing... but my spritesheet is stuck displaying the first frame of my sprite, never advancing. I cannot see any functional difference between the two implementations. I know that I changed the scope of certain variables, but they should all be accessible when needed.
Can anyone give me any input as to why this isn't working? I'd really appreciate it!
Removing self.screen = pg.transform.scale(self.screen,(1024, 768)) resolved the issue and I'm not entirely sure why. My original image is 640x345, which I realize is a crazy aspect ratio, but I'm still confused as to why the transform doesn't seem to work.
I'm making the program, that blits the 2D array as 2D map of tiles. The self.tile_size variable is used to scale the tile image that will be blit.
I want the Q to make it smaller, and E to make it bigger. The problem is, that output in in pygame window is not what i wanted. The image in link shows what happened, when i ran a program and tried to make image bigger. Image of output
Here's my code:
import pygame
from pygame.locals import *
pygame.init()
import math, sys, os, random, time
from a_star import *
tiles_list = [
pygame.image.load('Floor.png'),
pygame.image.load('Wall.png'),
]
PLAN_W = 20
PLAN_H = 20
PLAN_STATUS = [[True for i in range(PLAN_W)]for j in range(PLAN_H)]
OBSTACLES = [
[0,0],
[0,1],
[0,2]]
obsta(OBSTACLES, PLAN_STATUS) # function imported from a_star that change PLAN_STATUS cells from OBSTACLES to False
class World():
def __init__(self):
self.screen_width = 800
self.screen_height = 600
self.screen = pygame.display.set_mode((self.screen_width, self.screen_height))
self.clock = pygame.time.Clock()
self.bg_color = (255,255,255)
self.tile_size = 48
self.map_status = PLAN_STATUS
self.tiles_list = tiles_list
self.scale_change = 0
self.run()
def scale_tiles(self):
print(self.tile_size)
self.new_list = []
for i in self.tiles_list:
i = pygame.transform.scale(i, (self.tile_size, self.tile_size))
self.new_list.append(i)
self.tiles_list = self.new_list
def render_map(self):
print(self.tile_size)
'''
self.screen.fill(self.bg_color)
for x in range(PLAN_H):
for y in range(PLAN_W):
if self.map_status[y][x]:
tile = self.tiles_list[0]
elif not self.map_status[y][x]:
tile = self.tiles_list[1]
self.screen.blit(tile, (x*self.tile_size, y*self.tile_size))
'''
self.screen.blit(self.tiles_list[0], (400, 300))
def run(self):
while True:
self.events = pygame.event.get()
self.keys = pygame.key.get_pressed()
self.mouse_pos = pygame.mouse.get_pos()
self.mouse_but = pygame.mouse.get_pressed()
#UPDATE
self.screen.fill(self.bg_color)
#self.scale_tiles()
self.render_map()
pygame.display.flip()
#EVENTS
if self.keys[ord('e')] == 1:
self.tile_size += 1
self.scale_tiles()
if self.keys[ord('q')] == 1:
self.tile_size -= 1
self.scale_tiles()
'''for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_q:
print('q')
self.scale_change = -1
if event.key == pygame.K_e:
print('e')
self.scale_change = 1
if event.type == pygame.KEYUP:
if event.key == pygame.K_q or event.key == pygame.K_e:
print('stopped key')
self.scale_change = 0'''
self.clock.tick(50)
if __name__=="__main__":
main = World()
Where's the problem in my code?
Thank you for the help
Floor.png
Use the images/surfaces in the original, global tiles_list to create the new_list not the already scaled images in self.tiles_list, otherwise it seems to increase the impreciseness with every scaling.
for i in tiles_list: not for i in self.tiles_list:.
Currently, I have small image that I move using the D and A keys on the keyboard. The code works just how I want it to, but it seems to be a bit unnecessarily complicated. Is there a more efficient way of doing this?
In the main loop, my code checks to see what key is pressed through the events, but after that, if a key is no longer pressed, it checks to see if another key is pressed, just in case a user pressed a key while another key was initially pressed.
Here is my code:
import pygame
import sys
from pygame.locals import *
SCREENX = 640
SCREENY = 480
LEFT = 'left'
RIGHT = 'right'
class Character(pygame.sprite.Sprite):
def __init__(self, image_file):
super().__init__()
temp_image = pygame.image.load(image_file)
self.image = pygame.transform.scale(temp_image, (100, 100))
self.rect = self.image.get_rect()
self.moving = False
self.direction = RIGHT
def start_move(self, direction):
self.moving = True
if direction != self.direction:
self.image = pygame.transform.flip(self.image, True, False)
self.direction = direction
def stop_move(self):
if self.moving:
self.moving = False
def move(self):
if self.direction == RIGHT:
self.rect.x += 5
if self.direction == LEFT:
self.rect.x -= 5
def update(self):
if self.moving:
self.move()
def main():
pygame.init()
surface = pygame.display.set_mode((SCREENX, SCREENY))
clock = pygame.time.Clock()
sprites = pygame.sprite.Group()
girl = Character("assets/girl.png")
girl.rect.x = SCREENX/2
girl.rect.y = SCREENY - girl.rect.height
sprites.add(girl)
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == KEYDOWN:
if not girl.moving:
if event.key == K_d:
girl.start_move(RIGHT)
if event.key == K_a:
girl.start_move(LEFT)
if event.type == KEYUP:
if event.key == K_a or event.key == K_d:
girl.stop_move()
keys_pressed = pygame.key.get_pressed()
if keys_pressed[K_d]:
girl.start_move(RIGHT)
if keys_pressed[K_a]:
girl.start_move(LEFT)
surface.fill((255, 255, 255))
sprites.update()
sprites.draw(surface)
pygame.display.update()
clock.tick(60)
if __name__ == '__main__':
main()
Yes, you can get rid of most of the event handling, you can generalize your main loop, and you can get rid of some fields of your Character class.
See my explanatory notes in the comments:
import pygame
import sys
from pygame.locals import *
SCREENX = 640
SCREENY = 480
class Character(pygame.sprite.Sprite):
def __init__(self, image_file):
pygame.sprite.Sprite.__init__(self)
temp_image = pygame.image.load(image_file)
# only to the flipping of the image once
self.image_r = pygame.transform.scale(temp_image, (100, 100))
self.image_l = pygame.transform.flip(self.image_r, True, False)
self.image = self.image_l
self.rect = self.image.get_rect()
def update(self, pressed_keys):
move = 0
if pressed_keys[K_d]: move += 1
if pressed_keys[K_a]: move -= 1
self.rect.move_ip(move*5, 0)
# check which direction we're facing and set the image
self.image = self.image_l if move < 0 else self.image_r
def main():
pygame.init()
surface = pygame.display.set_mode((SCREENX, SCREENY))
clock = pygame.time.Clock()
sprites = pygame.sprite.Group()
girl = Character("assets/girl.png")
girl.rect.x = SCREENX / 2
girl.rect.y = SCREENY - girl.rect.height
sprites.add(girl)
while True:
for event in pygame.event.get():
# since we are in a function, we can simply return.
# we don't care here what happens after we quit the main loop
if event.type == QUIT: return pygame.quit()
# get all pressed keys, and just let all sprites
# decide what they want to do with it
keys_pressed = pygame.key.get_pressed()
# you could wrap this information in a more
# general container represeting the game state
# See how the game loop does not care what a
# Sprite does with this information
sprites.update(keys_pressed)
surface.fill((255, 255, 255))
sprites.draw(surface)
pygame.display.update()
clock.tick(60)
if __name__ == '__main__':
main()
I am trying to animate a spritesheet when you press a button on the keyboard. My initial idea was to just blit the images on keypress. I moved the animation code from the main loop to the user input loop. That did not work because the game loop handles individual state for the entire game. What technique is used to trigger animations from user input? I want an animation system that can handle animations both by user input and also in the background on their own.
if __name__ == "__main__":
print "the game"
pygame.init()
screen = pygame.display.set_mode((800,600))
images = load_sliced_sprites(100, 71, "spinning_roundhouse_kick.png")
spritesheet = AnimatedSprite(images, 20)
#while pygame.event.poll().type != KEYDOWN:
while True:
screen.fill((255,255,255))
event = pygame.event.poll()
if event.type == KEYDOWN:
#print "keydown"
for image in images:
screen.blit(image, (0,0))
#time = pygame.time.get_ticks()
#spritesheet.update(time)
#screen.blit(spritesheet.image, (0,0))
pygame.display.update()
Looks like you should be doing something like this..
sprite = AnimatedSprite(images, 20)
if event.type == KEYDOWN:
sprite.update(pygame.time.get_ticks())
screen.blit(sprite.getimage())
What should happen, is your sprite class should get an update with the time delta, and count time before changing to a new frame. Draw your animation whenever the key is down.
I solved the issue by tracking the frames of animation and using another while true loop.
import os
import pygame
from pygame.locals import *
def load_sliced_sprites(w, h, filename):
'''
Specs :
Master can be any height.
Sprites frames width must be the same width
Master width must be len(frames)*frame.width
Assuming you ressources directory is named "resources"
'''
images = []
master_image = pygame.image.load(os.path.join('resources', filename)).convert_alpha()
master_width, master_height = master_image.get_size()
for i in xrange(int(master_width/w)):
images.append(master_image.subsurface((i*w,0,w,h)))
return images
class AnimatedSprite(pygame.sprite.Sprite):
def __init__(self, images, fps = 10):
pygame.sprite.Sprite.__init__(self)
self._images = images
# Track the time we started, and the time between updates.
# Then we can figure out when we have to switch the image.
self._start = pygame.time.get_ticks()
self._delay = 1000 / fps
self._last_update = 0
self._frame = 0
# Call update to set our first image.
self.update(pygame.time.get_ticks())
def update(self, t):
# Note that this doesn't work if it's been more that self._delay
# time between calls to update(); we only update the image once
# then, but it really should be updated twice.
if t - self._last_update > self._delay:
self._frame += 1
#if self._frame >= len(self._images): self._frame = 0
if self._frame < len(self._images):
self.image = self._images[self._frame]
self._last_update = t
def getimage(self):
return self.image
def isfinished(self):
if self._frame == len(self._images):
return True
else:
return False
def reset(self):
if self._frame >= len(self._images): self._frame = 0
if __name__ == "__main__":
print "the game"
pygame.init()
screen = pygame.display.set_mode((800,600))
images = load_sliced_sprites(100, 71, "spinning_roundhouse_kick.png")
sprite = AnimatedSprite(images, 20)
#while pygame.event.poll().type != KEYDOWN:
while True:
screen.fill((255,255,255))
event = pygame.event.poll()
if event.type == KEYDOWN and event.key == K_a:
#print "keydown"
#for image in images:
while True:
#print "frame"
#print sprite._frame
#print sprite.isfinished()
time = pygame.time.get_ticks()
sprite.update(time)
screen.blit(sprite.getimage(), (0,0))
pygame.display.update()
#sprite.reset()
if sprite.isfinished() == True:
sprite.reset()
break
#time = pygame.time.get_ticks()
#sprite.update(time)
#screen.blit(sprite.image, (0,0))
pygame.display.update()