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()
Related
I'm creating a small game in pygame with obstacles that fall from the top of the screen to the bottom. At each event tick, an obstacle is created. However, at each new tick (1500milliseconds) the current obstacle is removed before it can reach the bottom and a new one is created. I need the obstacles to stay on the screen while new ones are generated.
I'm trying to get this done with classes and functions only.
So I want to create an obstacle_movement() function within the obstacle class.
Can you help please?
My code is below.
import pygame
import sys
from random import randint
from pygame import surface
import time
import os
class obstacle(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
Obstacle_directory = r'C:\\Users\\ctcar\\Documents\\CompSci\\GameDev\\selfgame\\Graphics\\Obstacles'
obstacle_lst = []
self.obstacle_frames = []
for filename in sorted(os.listdir(Obstacle_directory), key = len):
if filename.endswith('.png'):
obstacle_lst.append('Graphics/Obstacles/' + filename)
for sprite in obstacle_lst:
alpha_sprite = pygame.image.load(sprite).convert_alpha()
self.obstacle_frames.append(alpha_sprite)
y_pos = -20
self.obstacle_idx = 0
self.frames = self.obstacle_frames
self.image = self.frames[self.obstacle_idx]
self.rect = self.image.get_rect(midbottom = (randint(50, 750), y_pos))
def obstacle_animation(self):
self.obstacle_idx += 0.1
if self.obstacle_idx >= len(self.frames):
self.obstacle_idx = 0
self.image = self.frames[int(self.obstacle_idx)]
def update(self):
self.obstacle_animation()
self.rect.y += 4
obstacle_group = pygame.sprite.GroupSingle()
obstacle_timer = pygame.USEREVENT + 1
pygame.time.set_timer(obstacle_timer, randint(1000, 1100))
game_active = True
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if game_active:
screen.blit(sky_surface,(0,0))
screen.blit(ground_surface,(100,710))
if event.type == obstacle_timer:
obstacle_group.add(obstacle())
obstacle_group.draw(screen)
obstacle_group.update()
pygame.display.update()
clock.tick(60)
You need to use a pygame.sprite.Group insterad of a pygame.sprite.GroupSingle:
obstacle_group = pygame.sprite.GroupSingle()
obstacle_group = pygame.sprite.Group()
See obstacle_group = pygame.sprite.GroupSingle():
The GroupSingle container only holds a single Sprite. When a new Sprite is added, the old one is removed.
Furthermore, the events must be handled in the event loop:
game_active = True
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if game_active:
if event.type == obstacle_timer:
obstacle_group.add(obstacle())
if game_active:
screen.blit(sky_surface,(0,0))
screen.blit(ground_surface,(100,710))
obstacle_group.draw(screen)
obstacle_group.update()
pygame.display.update()
clock.tick(60)
pygame.quit()
sys.exit()
I have written a code which aims to move a horse when pressing the right arrow key, but when I press it, it doesnt move. I can't seem notice where the problem is. I have typed print(a.locx) in def char() to see a.locx is increasing or not but its not and also in class Horse()'s method def location() when I press right arrow key self.locx is increasing and then instantly decreasing.
import pygame
from pygame import locals
def main():
global window,width,height
pygame.init()
width ,height = 500,500
window = pygame.display.set_mode((width,height))
while True:
window.fill((0,0,0))
for event in pygame.event.get():
if pygame.event == pygame.QUIT:
pygame.quit()
char()
pygame.display.update()
def char():
a = Horse()
window.blit(a.horse1,(a.locx,a.locy))
print(a.locx)
a.location()
class Horse():
def __init__(self):
self.horse1 = pygame.image.load("C:/Users/niimet/Desktop/pygeym/blitz/Horse_Walk3.png")
self.horse2 = []
for horse in range(0,8):
self.horse2.append(pygame.image.load(("C:/Users/niimet/Desktop/pygeym/blitz/Horse_Walk{}.png").format(horse+1)))
self.horse3 = []
for horse in self.horse2:
self.horse3.append(pygame.transform.flip(horse,True,False))
self.locx = 0
self.locy = width - self.horse1.get_size()[1]
def location(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_RIGHT]:
print(self.locx,"1")
self.locx += 200
print(self.locx,"2")
main()
The issue is that you crate a new Horse object in every frame and so the horse continuously "starts" at its initial position.
def char():
a = Horse() # <--- creates new Hors object with "self.locx = 0"
# [...]
Create a Horse in global name space and use this object:
def main():
global window, width, height, a
pygame.init()
width, height = 500,500
window = pygame.display.set_mode((width,height))
a = Horse()
while True:
window.fill((0,0,0))
for event in pygame.event.get():
if pygame.event == pygame.QUIT:
pygame.quit()
char()
pygame.display.update()
def char():
window.blit(a.horse1,(a.locx,a.locy))
print(a.locx)
a.location()
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:.
I'm working on making a game for a project at my university using pygame. All I'm trying to get done right now is create a ball that can be controlled to go back and forth on the screen when the user presses the left and right arrow keys. Honestly I don't really know what I'm doing, so I'm using the code in the pygame documentation that was used for pong as the base for my game. My code is below, and if someone knows why I'm getting the error that's in the title, please let me know.
try:
import sys
import random
import math
import os
import getopt
import pygame
from socket import *
from pygame.locals import *
except ImportError, err:
print "couldn't load module. %s" % (err)
sys.exit(2)
def load_png(name):
""" Load image and return image object"""
fullname = name
try:
image = pygame.image.load(fullname)
if image.get_alpha() is None:
image = image.convert()
else:
image = image.convert_alpha()
except pygame.error, message:
print 'Cannot load image:', fullname
raise SystemExit, message
return image, image.get_rect()
class Ball(pygame.sprite.Sprite):
def __init__(self, (xy)):
pygame.sprite.Sprite.__init__(self)
self.image, self.rect = load_png('ball.png')
screen = pygame.display.get_surface()
self.area = screen.get_rect()
self.hit = 0
self.speed = 10
self.state = "still"
def reinit(self):
self.state = "still"
self.movepos = [0,0]
if self.side == "left":
self.rect.midleft = self.area.midleft
def update(self):
newpos = self.rect.move(self.movepos)
if self.area.contains(newpos):
self.rect = newpos
pygame.event.pump()
def moveleft(self):
self.movepos[1] = self.movepos[1] - (self.speed)
self.state = "moveleft"
def moveright(self):
self.movepos[1] = self.movepos[1] + (self.speed)
self.state = "moveright"
def main():
running = True
pygame.init()
(width, height) = (800, 600)
screen = pygame.display.set_mode((width, height))
# Fill background
background = pygame.Surface(screen.get_size())
background = background.convert()
background.fill((0, 0, 0))
screen.blit(background, (0, 0))
pygame.display.flip()
global player
player = Ball("left")
playersprite = pygame.sprite.RenderPlain(player)
playersprite.draw(screen)
player.update()
while running:
for event in pygame.event.get():
if event.type == KEYDOWN:
if event.key == K_q:
running = False
if event.key == K_LEFT:
player.moveleft()
if event.key == K_RIGHT:
player.moveright()
elif event.type == KEYUP:
if event.key == K_UP or event.key == K_DOWN:
player.movepos = [0,0]
player.state = "still"
#screen.blit(background, ball.rect, ball.rect)
screen.blit(background, player.rect, player.rect)
#screen.blit(background, player2.rect, player2.rect)
#ballsprite.update()
playersprite.update()
#ballsprite.draw(screen)
playersprite.draw(screen)
if __name__ == '__main__': main()
All I'm trying to get done right now is create a ball that can be
controlled to go back and forth on the screen when the user presses
the left and right arrow keys.
You are massively over complicating this, you don't need 102 lines to move a ball around. I wrote and commented a simple example that I think could help you a lot. All you really need to do is detect key presses and then update an x and y variable then draw the image using the x and y variables.
import pygame
screen_width = 1280
screen_height = 720
pygame.init()
screen = pygame.display.set_mode((screen_width,screen_height))
BLACK = (0,0,0)
ballImg = pygame.image.load("ball.jpg")
ballPosition = [0,0]
speed = 1
def game_loop():
while 1:
for event in pygame.event.get():
if event.type == pygame.QUIT:
exit()
#get all the keys being pressed
keys = pygame.key.get_pressed()
#depending on what key the user presses, update ball x and y position accordingly
if keys[pygame.K_UP]:
ballPosition[1] -= speed
if keys[pygame.K_DOWN]:
ballPosition[1] += speed
if keys[pygame.K_LEFT]:
ballPosition[0] -= speed
if keys[pygame.K_RIGHT]:
ballPosition[0] += speed
screen.fill(BLACK) #fill the screen with black
screen.blit(ballImg, ballPosition) #draw the ball
pygame.display.update() #update the screen
game_loop()
What versions of python/pygame are you running as when I tested your code with python 3.4, I first got syntax errors with your try: except statements, but this may be due to different syntax over different versions. After fixing that I ran into the issue of movepos not being defined when pressing left or right. Adding self.movepos = [0, 0] to the __init__() of the Ball class fixed this.
I never ran into the error you described, however the game did give a constant black screen no matter what I do.
What I'm trying to say is that errors can sometimes be caused by other mistakes earlier on in the code that don't get picked up. One of the answers here sums it up nicely: error: video system not initialized; Is there a solution?
Also, what variables store the balls x and y position? I couldn't seem to make out how you were controlling that?
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()