Page 1 of 2

2D Parralax Scrolling

Posted: Wed Sep 09, 2009 12:25 pm
by Big Grizzle
Feeling a tad inspired. I uploaded my first youtube video that didn't involve me playing Tetris.

It's a proof of concept bit of code for creating 2d parralax scrolling.

<object width="425" height="344"><param name="movie" value="http://www.youtube.com/v/mVdMCNE_8xA&hl ... ram><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/mVdMCNE_8xA&hl=en&fs=1&" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="344"></embed></object>[/youtube]

For those interested the code is here.

Code: Select all

import pygame

class BG_Item(pygame.sprite.Sprite):
    def __init__(self,size=(64,64),position=(0,0),colour=(0,0,0),velocity=(0,0)):
        ## Call the __init__ function from the inherited class
        pygame.sprite.Sprite.__init__(self)

        ## Save a rect of the screen.
        self.screen = pygame.display.get_surface().get_rect()

        ## Create a surface to draw upon.
        self.obj = pygame.Surface(size)
        ## Fill in the surface with a colour
        self.obj.fill(colour)

        ## Create a rect from the size of our surface.
        self.rect = self.obj.get_rect()
        
        ## Set the position of our rect on the display
        self.rect.x = position[0]
        self.rect.y = position[1]

        ## Create a variable for the old position of our rect.
        ## We will use this to fill it in with the background colour/details.
        self.old_rect = (0,0,0,0)
        
        ##Set the speed at which the Surface scrolls
        self.velocity = velocity

    def update(self,amount):
        self.old = self.rect       
	self.rect = self.rect.move(amount)
        if (self.rect.x + self.rect.width) <= 0:
        	self.rect.x = self.rect.width + 400
				
RESOLUTION = (400,300)

pygame.init()
screen = pygame.display.set_mode(RESOLUTION,pygame.DOUBLEBUF)
pygame.key.set_repeat(1,1)
sky = BG_Item(RESOLUTION,(0,0),(0,0,200))
sun = BG_Item((48,48),(320,16),(200,200,0),(-1,0))
ground = BG_Item((400,64),(0,236),(0,230,0),(0,0))
cloud1 = BG_Item((32,16),(368,32),(240,240,240),(-3,0))
cloud2 = BG_Item((64,32),(232,64),(240,240,240),(-4,0))
cloud3 = BG_Item((128,64),(96,128),(240,240,240),(-5,0))
back_building1 = BG_Item((128,64),(0,180),(180,180,180),(-2,0))
back_building2 = BG_Item((128,96),(142,180-32),(180,180,180),(-2,0))
building1 = BG_Item((160,96),(0,204),(50,50,50),(-7,0))
building2 = BG_Item((160,128),(200,172),(50,50,50),(-7,0))
building3 = BG_Item((160,160),(380 ,300-160),(50,50,50),(-7,0))
objects = [
    sky,sun,
    ground,
    back_building1,
    back_building2,
    
    cloud1,
    cloud2,
    cloud3,
    
    building1,
    building2,
    building3,
    ]

def blit_to_screen():
	for object in objects:
		screen.blit(object.obj,object.rect)

def move_all():
	for object in objects:
		object.update(object.velocity)
		
blit_to_screen()
pygame.display.update()
clock = pygame.time.Clock()
while True:
	for event in pygame.event.get():
		if event.type == pygame.KEYDOWN:
			if pygame.key.get_pressed()[pygame.K_LEFT]:
				move_all()
				blit_to_screen()
			
			clock.tick(30)
			pygame.display.update()    
I welcome any constructive criticism on how I could have done it better.

Re: 2D Parralax Scrolling

Posted: Wed Sep 09, 2009 1:42 pm
by hurstshifter
I was actually considering messing around with some parallax scrolling after stumbling across a similar video earlier this week. Well done.

Re: 2D Parralax Scrolling

Posted: Wed Sep 09, 2009 2:02 pm
by Big Grizzle
Cheers dude.

Just been tinkering with adding a sprite.

<object width="425" height="344"><param name="movie" value="http://www.youtube.com/v/5iqmTNyefkQ&hl ... ram><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/5iqmTNyefkQ&hl=en&fs=1&" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="344"></embed></object>[/youtube]

Having trouble with keeping the player on screen. At the moment it checks the sprites (x,y) coordinates against the main screen's and changes the velocity to push the sprite back onto the screen. This makes it very jumpy when right up against the edges.

Code is here for anyone who gives a crap...

Code: Select all

import pygame

class Player(pygame.sprite.Sprite):
    def __init__(self,position):
        pygame.sprite.Sprite.__init__(self)
        self.screen = pygame.display.get_surface().get_rect()
        self.surface = pygame.image.load("ship1.png").convert()
        self.surface.set_colorkey((255,0,255))
        self.rect = self.surface.get_rect()
        self.old = self.rect
        self.position = position
        self.rect.x = position[0]
        self.rect.y = position[1]
        self.speed = [0,0]
        print self.rect

    def fly(self):
        self.old = self.rect
        self.rect = self.rect.move(self.speed)
        ## Standard 4-way
        if pygame.key.get_pressed()[pygame.K_LEFT]:
            self.speed = [-7,0]
        if pygame.key.get_pressed()[pygame.K_RIGHT]:
            self.speed = [7,0]
        if pygame.key.get_pressed()[pygame.K_DOWN]:
            self.speed = [0,7]
        if pygame.key.get_pressed()[pygame.K_UP]:
            self.speed = [0,-7]
        ## Diagonal left
        if pygame.key.get_pressed()[pygame.K_LEFT] and pygame.key.get_pressed()[pygame.K_DOWN]:
            self.speed = [-7,7]
        if pygame.key.get_pressed()[pygame.K_LEFT] and pygame.key.get_pressed()[pygame.K_UP]:
            self.speed = [-7,-7]
        ## Diagonal right
        if pygame.key.get_pressed()[pygame.K_RIGHT] and pygame.key.get_pressed()[pygame.K_DOWN]:
            self.speed = [7,7]
        if pygame.key.get_pressed()[pygame.K_RIGHT] and pygame.key.get_pressed()[pygame.K_UP]:
            self.speed = [7,-7]
        print "Position = ", self.rect.x,self.rect.y
        print "Speed = ", self.speed

    def onScreen(self):
        ## NEEDS TUNING ##
        ## Check we are on the screen 
        if self.rect.x < 1:
            self.speed[0] = 6 
            print "Off left side of screen"
        if self.rect.y < 1:
            self.speed[1] = 6
            print "Off top side of screen"
        if self.rect.x + self.rect.width > self.screen.width:
            self.speed[0] = -6
            print "Off right side of screen"
        if self.rect.y + self.rect.height > self.screen.height:
            self.speed[1] = -6
            print "Off bottom side of screen"
        
    def resetSpeed(self):
        """Called from main game loop on KEYUP event to stop other keys moving the ship"""
        self.speed = [0,0]

    def shoot(self):
        """Called from main game loop on KEYDOWN event to fire guns"""
        if pygame.key.get_pressed()[pygame.K_x]:
            print "SHOOTING AT YA!"
    def missile(self):
        """Called from main game loop on KEYDOWN event to fire missiles"""
        if pygame.key.get_pressed()[pygame.K_z]:
            print "HERE COME THE MISSILES!"
    def deathRay(self):
        """Called from main game loop on KEYDOWN event to fire death ray"""
        if pygame.key.get_pressed()[pygame.K_d]:
            print "DEATH RAY! ZZZzzzZZZzzzZZZzzz DEATH RAY!"
            
class BG_Item(pygame.sprite.Sprite):
    def __init__(self,size=(64,64),position=(0,0),colour=(0,0,0),velocity=(0,0)):
        ## Call the __init__ function from the inherited class
        pygame.sprite.Sprite.__init__(self)

        ## Save a rect of the screen.
        self.screen = pygame.display.get_surface().get_rect()

        ## Create a surface to draw upon.
        self.surface = pygame.Surface(size)
        ## Fill in the surface with a colour
        self.surface.fill(colour)

        ## Create a rect from the size of our surface.
        self.rect = self.surface.get_rect()
        
        ## Set the position of our rect on the display
        self.rect.x = position[0]
        self.rect.y = position[1]

        ## Create a variable for the old position of our rect.
        ## We will use this to fill it in with the background colour/details.
        self.old_rect = (0,0,0,0)
        
        ##Set the speed at which the Surface scrolls
        self.speed = velocity
        print self, "BG_Item initialised - OK"

    def update(self,amount):
        self.old = self.rect       
        self.rect = self.rect.move(amount)
        if (self.rect.x + self.rect.width) <= 0:
            self.rect.x = self.rect.width + 400
                
RESOLUTION = (400,300)

pygame.init()
screen = pygame.display.set_mode(RESOLUTION,pygame.DOUBLEBUF)
pygame.key.set_repeat(1,1)

sky = BG_Item(RESOLUTION,(0,0),(0,0,200))
sun = BG_Item((48,48),(320,16),(200,200,0),(-1,0))
ground = BG_Item((400,64),(0,236),(0,230,0),(0,0))
cloud1 = BG_Item((32,16),(368,32),(240,240,240),(-3,0))
cloud2 = BG_Item((64,32),(232,64),(240,240,240),(-4,0))
cloud3 = BG_Item((128,64),(96,128),(240,240,240),(-5,0))
back_building1 = BG_Item((128,64),(0,180),(180,180,180),(-2,0))
back_building2 = BG_Item((128,96),(142,180-32),(180,180,180),(-2,0))
back_building3 = BG_Item((128,96),(142+128+16,180-32),(180,180,180),(-2,0))
building1 = BG_Item((160,96),(0,204),(50,50,50),(-7,0))
building2 = BG_Item((160,128),(200,172),(50,50,50),(-7,0))
building3 = BG_Item((160,160),(380 ,300-160),(50,50,50),(-7,0))

ship = Player((1,120))

objects = [
    sky,sun,
    ground,
    back_building1,
    back_building2,
    back_building3,    
    cloud1,
    cloud2,
    cloud3,
    
    building1,
    building2,
    building3,
    ship
    ]

def blit_to_screen():
    for obj in objects:
        screen.blit(obj.surface,obj.rect)

def move_all():
    for obj in objects:
        obj.update(obj.speed)
        
blit_to_screen()
pygame.display.update()
clock = pygame.time.Clock()
while True:

    for event in pygame.event.get():
        if event.type == pygame.KEYDOWN:
            ship.fly()
            ship.onScreen()
            ship.shoot()
            ship.missile()
            ship.deathRay()
        if event.type == pygame.KEYUP:
            ship.resetSpeed()
    move_all()
    blit_to_screen()
    clock.tick(30)
    pygame.display.update()    
It could do with a bit of cleaning up as it was just a copy, paste and tweak job from two seperate source files.

Re: 2D Parralax Scrolling

Posted: Mon Sep 14, 2009 12:29 pm
by dandymcgee
Big Grizzle wrote: Having trouble with keeping the player on screen. At the moment it checks the sprites (x,y) coordinates against the main screen's and changes the velocity to push the sprite back onto the screen. This makes it very jumpy when right up against the edges.
Just check for edge of screen collision before you update the screen, if you collide change it back before you draw anything and you should never see the jumping effect.

Re: 2D Parralax Scrolling

Posted: Mon Sep 14, 2009 12:49 pm
by Bakkon
Yeah, directly modify the position to snap it back into the screen bounds. If you only change the velocity, if leaves a game tick to draw before it updates it back onto the screen.

Re: 2D Parralax Scrolling

Posted: Tue Sep 15, 2009 9:18 am
by Big Grizzle
Thanks Bakkon. I figured it out and that is exactly what I did. However I have introduced a new feature(BUG!) when the ship is travelling diagonally to the corner of the screen. If I release one of the direction keys the ship zooms off the screen until a keyup/keydown event. It is probably the flow of my game loop or something. I'll work it out eventually though.

Making a game is much more fun than when I made an income tax/national insurance calculator with Python and wxPython.

Re: 2D Parralax Scrolling

Posted: Mon Oct 05, 2009 7:13 pm
by Big Grizzle
Slowly, but surely!

Unfortunately the combination of CamStudio, a couple thousand sprites being updated and then a Youtube conversion completely butt fucked any quality out of the video.

<object width="425" height="344"><param name="movie" value="http://www.youtube.com/v/44W3z5lU8rI&hl ... ram><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/44W3z5lU8rI&hl=en&fs=1&" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="344"></embed></object>[/youtube]

Re: 2D Parralax Scrolling

Posted: Mon Oct 05, 2009 9:09 pm
by trufun202
Dude, nice! The gameplay looks really smooth, and I like how you already have weapon upgrades in there.

Mowing down all of those enemies is just asking for a badass particle effect. ;)

Re: 2D Parralax Scrolling

Posted: Mon Oct 05, 2009 10:24 pm
by dandymcgee
Lookin' good!

Re: 2D Parralax Scrolling

Posted: Tue Oct 06, 2009 11:10 am
by MarauderIIC
Neat. I liked the city theme better though :D

Re: 2D Parralax Scrolling

Posted: Wed Oct 07, 2009 3:27 pm
by Big Grizzle
Thanks for the kind words. Still so much to do.

A particle effect?....Hmmmmm. Gives me an idea.

BTW...the city scape is still being used.

Re: 2D Parralax Scrolling

Posted: Wed Oct 07, 2009 6:08 pm
by Bakkon
Big Grizzle wrote: A particle effect?....Hmmmmm. Gives me an idea.
The way you're firing bullets is already a variant of a particle effect. Just have to modify the way they move and die off. ;)

Re: 2D Parralax Scrolling

Posted: Thu Oct 08, 2009 11:01 am
by MarauderIIC
So, using the cityscape, can you have stuff scroll by in front of the player? That way you can have the player fly through a building (this would be awesome), as well as have him fly between buildings.

Re: 2D Parralax Scrolling

Posted: Thu Oct 08, 2009 2:06 pm
by Big Grizzle
Yes. That is entirely possible. I could also have clouds rendered with an alpha value that will be rendered over the player to give the impression that you are flying through them.

Each star is basically a sprite. I have at the moment 4 layers of them scrolling at diffferent speeds to give the illusion of depth. Just like the cityscape. I can also do a fake rotating effect by moving the bottom layers in the opposite direction of the closer layers.

My plan at the moment is to start in space at the edge of the solar system and progress towards Earth using the planets as background sprites. Upon reaching Earth the player is going to go into the atmosphere and end up over a city. This way I also get to have an asteroid segement. :)

That said I have just spent the last two days trying to get the frame rate back up above 30 frames due to a combination of too many stars (I had about 1600) and a weird bug in my collision detection that was allowing certain bullets to pass through the enemies.

Re: 2D Parralax Scrolling

Posted: Sun Oct 18, 2009 9:54 am
by eatcomics
This game sounds great but I can't watch the videos, I'm on an iPod but I will definitely watch later :)