Sunday 31 March 2019

Pygame : Working with Sprites and animations




We already know how to load and display images in pygame now. But those are only images. You can do nothing more than displaying those images using a surface object. For making games we need more than simply displaying. And here comes the Sprite module.

By using this module to its full potential, you can easily manage and draw your game objects. The sprite classes are very optimized, so it's likely your game will run faster with the sprite module than without.
This module not only allow you to draw the images, but also do many operations on them like animations, grouping etc.. Also it provides collision detection power to your game development process. You need not write the whole collision detection code by yourself.

The sprite module provides two classes:
  1. Sprite
  2. Group
The Sprite class is designed to be a base class for all your game objects while the Group class is container for the sprites. For example if you are creating a menu for your game, all the buttons in the menu are sprites. And the menu itself is a sprite group.

In this post let’s see an example of sprite class:
Let’s animate the ship in our previous example with some water ripples around it. Like in following video



In the above video what we see our ship is not moving right now but having the water around it is moving. To create this animation we’ll use the sprite module of pygame. Till now our ship was only an image but now we’ll turn it into a game object. To do so we’ll have to create a class Ship extending class pygame.sprite.Sprite as follows:

class Ship(pygame.sprite.Sprite):
def __init__(self):
super(Ship, self).__init__()


In sprite class we need to assign the image of sprite as “self.image = image_to_assign”
now, we have to display a ship, and a set of water ripple frames around it. That means our main image is ship and we have to blit the water ripples along with the ship.
So we’ll assign self.image=ship and create an array for animation frames as “self.animFrames” and store the ripple images in this array. In this way we have what we need to create our animation.

Now we have to display this animation. Here, if we display all the ripple frames at a time, we can’t call it animation; we want to animate it show frame by frame. That means we’ll need a timer and an index to store current frame. And also a position to display our sprite and its animation. So let’s modify our sprite class:

class Ship(pygame.sprite.Sprite):
    index = 0

    def __init__(self, texture, animFrames, x=0, y=0, width=1, height=1):
        super(Ship, self).__init__()
        self.image = texture
        self.animFrames = animFrames
        self.currentFrame = self.animFrames[0]
        self.x = x
        self.y = y
        self.rect = self.image.get_rect()
        self.width = self.rect.width
        self.height = self.rect.height

Here we are providing texture of ship and animation frames as well as the rect details to sprite from parameters.

To keep the animation updating let’s write the update method:

def update(self):
    if self.isAnimating:
        self.animate()

and to implement the animation functionality, let’s add the following methods:

def startAnimation(self):
    self.isAnimating = True

def animate(self):
    self.index += 1
    if self.index >= len(self.animFrames):
        self.index = 0
    self.currentFrame = self.animFrames[self.index]
    time.sleep(0.01)

def stopAnimation(self):
    self.isAnimating = False


Here the time.sleep provides us the delay of displaying the next animation frame. Now we have done everything our update method is properly updating the sprite according to animation but to display the animation on the screen, we’ll have to use the draw method of sprite class:

def draw(self, surface):
    surface.blit(self.currentFrame, (self.x-self.width, self.y-self.height*0.2))
    surface.blit(self.image, ((self.x), (self.y)))


Here we are drawing the animation frame first and on top of the animation frame we are drawing the ship. Because we want the water ripple under the ship.

Now we have successfully created the Ship class we only have to instantiate it on our previous window and run the code

so as usual first load images:

We already have loaded the ship. We only need to load animation frames

shipAnimFrames_str = ["ripple0.png", "ripple1.png", "ripple2.png", "ripple3.png", "ripple4.png"]

shipAnimFrames = []
i = 0
while i < len(shipAnimFrames_str):
    shipAnimFrames.append((pygame.image.load(shipAnimFrames_str[i])))
    i += 1

ShipSprite = Ship(ship, shipAnimFrames, winWidth*0.45, winHeight*0.75)
ShipSprite.startAnimation()

and inside the game loop
call the update() and draw() of ShipSprite
as
ShipSprite.update()
ShipSprite.draw()


save the code and run it to get output like above.


The complete code for the above is as follows:



Similarly you can use the background image also as sprite rather than a surface. By making it sprite you can convert this whole code into an endless travel for your ship as shown in following video.


You can get the complete source code for this endless travel in the following repo. In the coming post let’s see what more we can do with sprites like collision detection, grouping, other animations etc.. so stay tuned.

Tuesday 5 March 2019

Pygame Images; a little More.....

A Little More About Pygame Images!


In previous post, we loaded and displayed images. Since we now know, how to load images, let's check out some more things that we can do with images...

In first post, we set a text caption of the window now let's set an icon of the window.

copy and paste these lines before the Game_Window= pygame.display.set_mode(winWidth,winHeight)

ship= pygame.image.load("ship.png")
pygame.display.set_icon(ship)

 
Here point to remember is you need to set the icon before creating the Game_Window. A 32X32 icon is generally recommended.

Our background is a river with banks, lets add some bushes on both the sides of bank. We first need to load the image of bush.

bush=pygame.transform.scale((pygame.image.load("bush.png")),(50,50))

the bush image i'm having with me is little bigger so im going to scale it down to 50X50
now let's create an array of bushes to show many bushes on the bank of river. Here we do not need to create so many images by loading images again and again. We have a method called as copy() that will create copy of surface bush. so let's create 10 bushes here add the following lines below bush definition.
bushes=[]
i=0
while i < 10:
     bushes.append(bush.copy())
     i+=1

now we have created 10 bushes we have to display those on the river bank.
Let's reassign i to 0 and
just below blitting the background, add the following lines
while i < len(bushes)
    if i%2==0:
        Game_Window.blit(bushes[i],(winWidth*0.03,50*i))
     else:
        Game_Window.blit(bushes[i],(winWidth*0.83,50*i))
     i+=1
   
So now our window is going to look like


Now Let's add a ship in our river. As we already have loaded the image, we don't need to load it again.
To display the ship on the window, we'll have to blit it on the window. Add the following line before pygame.display.update().
Game_Window.blit(ship,(winWidth*0.45,winHeight*0.75))
This will show the ship on the bottom of our window. We can also see the icon in the dock in the following image that is going to show the output of whatever we have done above.

Now Let's see how to move this ship forward till the other end of river...
we only have to update the position of the ship. so before our game loop starts, define starting "y" Position of ship as
y=winHeight*0.75
 
update the y value as soon as you enter the game loop as follows:
if y>0:
     y-=1

 And after you have blit background on the game window, now blit the ship as
Game_Window.blit(ship,(winWidth*0.45,y))


At the end of game loop instead of
pygame.display.update(),call pygame.display.flip() to update the entire screen so you'll get the ship moving upwards.

You can also play with transparency of images. All you need to do is set the alpha of the surface. The alpha values range from 0 to 255. 0 is a total transparent while 255 is completely opaque.

Following is the complete code we worked till now. In this code we have a background, aset of some bushes, a moving ship an icon to window and a semitransparent image drawn on the background.

The images required are in the same gist's attachments

Now We can display images why not to use all those to create a simple game using these assets. In next post let's create some animations and have a look on events in pygame.