Pygame animation with multiple states - python

I am trying to figure out the best way to create an animation with multiple states but I can't seem to find any examples on a clean way to achieve what I'm after.
I have a sprite sheet for a character that has two states of jumping animations, there are 6 frames where the character animation is essentially jumping off the ground.
There are another 6 frames where the character is in a "jump loop" so the character is already off the ground but the arms etc move slightly.
When I put all the images into a list and iterate through them the initial part of the jump looks fine because the character gets off the ground and then goes into the jump loop. But once the jump loop sequence of frames is done the animation goes back to the start mid air, so it looks as though the character is just jumping off something.
The code for the function I have thus far is as follows
def animate(self):
now = pg.time.get_ticks()
# Jumping
if self.jumping:
if now - self.last_update > 18:
self.last_update = now
if self.left:
self.current_frame = (self.current_frame + 1) % len(self.jump_l)
bottom = self.rect.midbottom
self.image = self.jump_l[self.current_frame]
self.rect = self.image.get_rect()
elif self.right:
self.current_frame = (self.current_frame + 1) % len(self.jump_r)
bottom = self.rect.midbottom
self.image = self.jump_r[self.current_frame]
self.rect = self.image.get_rect()
self.rect.midbottom = bottom
The animation works but essentially what I want to do is to only how the first 6 frames and then loop over the last 6 frames until the character lands.

I would recommend splitting up the animation Spritesheet so you have one row with the six frames for jumping up, and then another row for the other six falling frames.
Once you can do that, just check if your sprites velocity variable is positive or negative like this:
if self.vel > 0:
# Switch animation frames to jumping up
elif self.vel < 0:
# Switch animation frames to falling down

Thanks guys, I did split the animation into two (not the spritesheet) and I got it to work with the velocity (self.vel.x > 0) but after some consideration those extra 6 frames didn't really add much to the animation so I just took it out and used the loop part of the jump as the animation.
Looking closer at it there are only really 3 frames which are when the character lifts his leg slightly before he is in full jump and it isn't really noticeable and thus didn't really add anything to it so I scrapped the idea.

Related

Pygame moving image not appearing on moving background

So, i have a moving background for my pygame that is working fine. Now i wanted to add an obstacle (like a rock), that would be at the bottom of the screen and would move with the background. However, the image (obstacle) just disapears after appearing for a couple of seconds. I want the rock to appear over and over, however, it does not appear. Cant figure out whats wrong. Plz help, thanks!
background = pygame.image.load('background.png')
backgroundX = 0
backgroundX2 = background.get_width()
obstacle = pygame.image.load('obstacle.png')
obstacleX = 0
obstacleX2 = obstacle.get_width()
# use procedure for game window rather than using it within loop
def redrawGameWindow():
# background images for right to left moving screen
screen.blit(background, (backgroundX, 0))
screen.blit(background, (backgroundX2, 0))
man.draw(screen)
screen.blit(obstacle, (obstacleX, 380))
screen.blit(obstacle, (obstacleX2, 380))
pygame.display.flip()
pygame.display.update()
The main loop:
while run:
screen.fill(white)
clock.tick(30)
pygame.display.update()
redrawGameWindow() # call procedure
obstacleX -= 1.4
obstacleX2 -= 1.4
if obstacleX < obstacle.get_width() * -10:
obstacleX = obstacle.get_width
if obstacleX2 < obstacle.get_width() * -10:
obstacleX2 = obstacle.get_width()
The surface.blit() (i.e.: screen.blit) function takes an image and top-left co-ordinates of where to draw it.
In the code provided the two copies of obstacle are drawn at obstacleX and obstacleX2 - one of which is set to 0, and the other the width of the image. So this should result in the two images being drawn next to each other, on the left side of the window, at line 380.
If these images are no longer being drawn after a while, this could be caused by -
The variables obstacleX and obstacleX2 being changed to an off-screen position
The image obstacle being changed to a blank (or otherwise invisible) version
There is no evidence in the small code sample above, but since the question suggests the images move, it's my guess that the obstacleX and obstacleX2 co-ordinates of the drawing positions are being changed to become off-screen.
EDIT:
It's apparent that your objects are starting at position 0 (window left), and the position is being updated obstacleX -= 1.4 which is moving the obstacle further left. This is why they start on-screen, but soon disappear.
Put your screen dimensions into constants, e.g.:
WINDOW_WIDTH = 400
WINDOW_HEIGHT = 400
And use these instead of peppering your code with numbers. This reduces the number of changes needed if you decide to change window sizes, and it also allows calculations based on window width.
So start your obstacle just off the screen.
obstacleX = WINDOW_WIDTH # off-screen
obstacleX2 = WINDOW_WIDTH + 100 # Further away from first obstacle
In the main update-loop, when the position of the items change, check to see if they need to be re-cycled back to in-front of the player:
# Move the obstacles 1-pixel to the left
obstacleX -= 1.4
obstacleX2 -= 1.4 # probably just 1 would be better
# has the obstacle gone off-screen (to the left)
if ( obstacleX < 0 - obstacle.get_width() ):
# move it back to the right (off-screen)
obstacleX = WINDOW_WIDTH + random.randint( 10, 100 )
# TODO - handle obstacleX2 similarly

How to prevent the position of the player to go outside screen bounds with pygame?

I want to grab the screen width of the x position. So, when I change the screen size I can have it where it already is configuration to the screen size and the player doesn't run off the screen. If that makes any sense.
windows = pygame.display.set_mode((1200,1000))
if player_x > pygame.display.get_surface().x-15:
player_x = pygame.display.get_surface().x-15
This isn't right because it crashes the game now.
Make Player disappear and appear in Pacman-Style
gameDisplay = pygame.display.set_mode((800,600))
x=gameDisplay.get_width()
y=gameDisplay.get_height()
if player.left > x-15:
player.left = 0
Maybe not the explicit solution, but you should be able to build from it, exactly what you want ;)

Subsurface rect outside of surface area when using get_clip() in Pygame?

I am currently working on a pygame, trying to animate my character so that as the player moves him the program will cycle through four sprite image subsurfaces. I already setup a class for this:
import pygame
from pygame.locaks import *
class Prota:
def __init__(self, sheet):
self.sheet = pygame.image.load(sheet).convert_alpha()
self.image_rect = self.sheet.get_rect()
self.image_rect_h = (self.image_rect.height) #num of rows
self.image_rect_w = (self.image_rect.width/16) #num of columns
self.image_reel = self.fetch()
self.image_cue = 0 #index: 0 - 3 (Right), 4 - 7 (Left), 8 - 11 (Front), 12 - 15 (Back)
self.clock = pygame.time.Clock()
def draw(self, screen):
self.clock.tick(60)
screen.blit(self.image_reel[self.image_cue], (400, 300))
def fetch(self):
sprites = []
for x in range(0, 15):
self.sheet.set_clip(pygame.Rect(self.image_rect_h*x, 0, self.image_rect_w, self.image_rect_h))
sprite = self.sheet.subsurface(self.sheet.get_clip())
sprites.append(sprite)
return sprites
And it worked perfectly when I used a dummy sprite sheet (just a simple 50 x 50 square sprite that changes colors), but when I tried to implement my (partially complete) actually character sheet, I got back
ValueError: subsurface rectangle outside surface area
I am not sure if it's the size of sheets (the dummy sheet was 832 x 52px, and the character sheet is 1008 x 79px), or what, and I can't seem to find any article that addresses this issue. (The closest I could find in a quick search was How to rotate images in pygame
Any ideas?
The mistake was to use self.image_rect_h*x as the first argument (the x-coord) of the pygame.Rect. Changing it to self.image_rect_w*x fixed it.
self.sheet.set_clip(pygame.Rect(self.image_rect_w*x, 0, self.image_rect_w, self.image_rect_h))
I'm not sure why the error occurs, because pygame.Surface.set_clip and .get_clip should be restricted to the surface area. You should post the full traceback (error message).
I actually think it would be better to let the program crash if something is wrong with the image loading and cutting, so that it's easier to find the error, otherwise you would get incorrect sprites anyway. So you could just use pygame.Surface.subsurface instead of set_clip andget_clip.
rect = (self.image_rect_w*x, 0, self.image_rect_w, self.image_rect_h)
sprite = self.sheet.subsurface(rect)

Pygame screen won't blit

I have an Agar.io like test game where the square player touches the "bit"s and grows. The problem is the bit generation. The bit generates every second and has its own surface. But, the program will not blit the bit (no pun intended) into the initial surface.
Here is the code:
import pygame
import random
pygame.init()
clock = pygame.time.Clock()
display = pygame.display
screen = display.set_mode([640,480])
rect_x = 295
rect_y = 215
display.set_caption("Agar")
d = False
bits = []
size = 50
steps = 0
class Bit:
cscreen = display.set_mode([640,480])
circ = pygame.draw.circle(cscreen, (65, 76, 150), (random.randrange(40, 600), random.randrange(40, 440)), 5)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
d = True
if d == True:
break
# Fill background
screen.fill((5, 58, 0))
# Create player object
player = pygame.draw.rect(screen, (250, 197, 255), [rect_x, rect_y, size, size])
# Moving
if pygame.key.get_pressed()[pygame.K_UP]:
rect_y -= 2
if pygame.key.get_pressed()[pygame.K_DOWN]:
rect_y += 2
if pygame.key.get_pressed()[pygame.K_RIGHT]:
rect_x += 2
if pygame.key.get_pressed()[pygame.K_LEFT]:
rect_x -= 2
# Bit generation
if steps == 60:
new_bit = Bit()
bits.append(new_bit.circ)
screen.blit(new_bit.cscreen, (0,0))
steps = 0
steps += 1
# Collision detection
collision = player.collidelist(bits)
if collision != -1:
bits[collision].cscreen.fill((0,0,0,0))
# Make player larger
size += 10
# FPS limit
clock.tick(60)
# Refresh
display.flip()
P.S. There is no error message.
Thanks in advance.
To have each Bit object have different locations and instance variables, you need to have an __init__ method in your bit class. By adding an __init__, and adding self. to the variables, the blitting works.
But a word of warning. When you make a separate cscreen for each object, and then blit that cscreen to your actual screen, you get rid of every other cscreen thats been blitted, making only 1 Bit viewable at a time. Even if you had cscreen outside of __init__, you would have an issue erasing older ones now, so I advise you take a different approach.
My recommendation is to find a small picture of a dot, and write these lines in the Bit's __init__ method.
self.image = pygame.image.load("reddot.png")
self.rect = self.image.get_rect()
Once you create your bit, add it to a pygame.sprite.Group(). These groups have very handy built in methods. One of them being .draw(). It will go through your Group of Bits and use each's self.image and self.rect to draw them in the correct place every time, without you having to worry about it. When a Bit is eaten, you can delete the Bit from the Group and it is now erased for you.
A random side note: I recommend you change your event loop to just break when it event.type == pygame.QUIT. That way, Python does not need to check the if d == True statement on every frame, while making the code slightly more readable. If it only breaks out of the for loop (for me it exited fine), you can use sys.exit() after importing sys

Positioning a moving image/rect with Pygame

I would like to position my image and rect, but they are already moving back and forth. Can I still manually position their start point at this stage, when they are already on the move?
Currently the Image bounces back and forth on the screen. I would like to position the start point of this motion to an arbitrary point, say (0,100) of my window. Is there a way to set this positioning of my image and rect so that it can keep it's movement but just start from another point? Currently it's stuck on the top, I've tried a few things that didn't work:
self.image = pygame.image.load('picture.jpg')
self.rect = self.image.get_rect()
self.screen.fill(self.bg_color)
self.rect.move_ip(5,0) #only moving on the x axis
if self.rect.left < 0 or self.rect.right > self.width:
self.speed[0] = -self.speed[0]
self.screen.blit(self.image,self.rect)
pygame.display.flip()
Is there a way where I can set a starting position for my Surface object and rectangle?
Sure. Just do
self.rect.move_ip(0, 100)
to move to (0,100).
See the pygame.Rect docs for more.

Categories

Resources