[pygame] scrolling the map relitive to characters position?

[pygame] scrolling the map relitive to characters position?

Postby spamynator_1 » Sat Aug 03, 2013 4:27 am

I would like to know if possible a suggested manor of making it so, a character sprite, in a 2d game when walks toward off screen that the screen follows him. so if i had a map that was 1000x1000 that the 640x480 windows was always semi-focused on the character, but that if you got soo close to the edge of the screen that the screen would move up if you continued in that direction? any ideas on how i could do this or how i could achieve something simular.
Last edited by Yoriz on Sun Aug 04, 2013 1:00 pm, edited 1 time in total.
Reason: changed title
spamynator_1
 
Posts: 63
Joined: Sun Mar 03, 2013 12:45 am

Re: Map scrolling.

Postby Mekire » Sun Aug 04, 2013 12:44 pm

Here is an example using a solid image map (as opposed to a tiled map). The map stays centered on the player unless he is near an edge, in which case he moves off center. Example uses pixel perfect collision techniques (masks).

(Code below loads the images from my photobucket so it might take a second.)
Code: Select all
import os
import sys
import pygame as pg


CAPTION = "Scrolling Background"


DIRECT_DICT = {pg.K_UP   : ( 0,-1),
               pg.K_DOWN : ( 0, 1),
               pg.K_RIGHT: ( 1, 0),
               pg.K_LEFT : (-1, 0)}


class Player(object):
    """Our user controllable character."""
    def __init__(self,location,speed):
        """The location is an (x,y) coordinate; speed is in pixels per frame.
        The location of the player is with respect to the map he is in, not the
        display screen."""
        self.speed = speed
        self.image = PLAY_IMG
        self.mask = pg.mask.from_surface(self.image)
        self.rect = self.image.get_rect(center=location)

    def update(self,level_mask,keys):
        """Check pressed keys to find initial movement vector.  Then call
        collision detection methods and adjust the vector appropriately."""
        move = self.check_keys(keys)
        self.check_collisions(move,level_mask)

    def draw(self,surface):
        """Basic draw function."""
        surface.blit(self.image,self.rect)

    def check_keys(self,keys):
        """Find the players movement vector from key presses."""
        move = [0,0]
        for key in DIRECT_DICT:
            if keys[key]:
                for i in (0,1):
                    move[i] += DIRECT_DICT[key][i]*self.speed
        return move

    def check_collisions(self,move,level_mask):
        """Call collision_detail for the x and y components of our movement
        vector."""
        x_change = self.collision_detail(move,level_mask,0)
        self.rect.move_ip((x_change,0))
        y_change = self.collision_detail(move,level_mask,1)
        self.rect.move_ip((0,y_change))

    def collision_detail(self,move,level_mask,index):
        """Check for collision and if found decrement vector by single pixels
        until clear."""
        test_offset = list(self.rect.topleft)
        test_offset[index] += move[index]
        while level_mask.overlap_area(self.mask,test_offset):
            move[index] += (1 if move[index]<0 else -1)
            test_offset = list(self.rect.topleft)
            test_offset[index] += move[index]
        return move[index]


class Level(object):
    """A class for our map.  Maps in this implementation are one image; not
    tile based.  This makes collision detection simpler but can have performance
    implications."""
    def __init__(self,map_image,viewport,player):
        """Requires an image from which to make a mask, and a player
        instance."""
        self.image = map_image
        self.mask = pg.mask.from_surface(self.image)
        self.rect = self.image.get_rect()
        self.player = player
        self.player.rect.center = self.rect.center  #Start at center of map.
        self.viewport = viewport

    def update(self,keys):
        """Updates the player and then adjust the viewport with respect to the
        player's new position."""
        self.player.update(self.mask,keys)
        self.update_viewport()

    def update_viewport(self):
        """The viewport will stay centered on the player unless the player
        approaches the edge of the map."""
        width,height = self.viewport.size
        if self.player.rect.centerx <= width//2:
            self.viewport.x = 0
        elif self.player.rect.centerx >= self.rect.width-width//2:
            self.viewport.x = self.rect.width-width
        else:
            self.viewport.centerx = self.player.rect.centerx
        if self.player.rect.centery <= height//2:
            self.viewport.y = 0
        elif self.player.rect.centery >= self.rect.height-height//2:
            self.viewport.y = self.rect.height-height
        else:
            self.viewport.centery = self.player.rect.centery

    def draw(self,surface):
        """Blit actors onto a copy of the map image; then blit the viewport
        portion of that map onto the display surface."""
        new_image = self.image.copy()
        self.player.draw(new_image)
        surface.fill((50,255,50))
        surface.blit(new_image,(0,0),self.viewport)


class Control(object):
    """We meet again."""
    def __init__(self):
        """Initialize things; create a Player; create a Level."""
        self.screen = pg.display.get_surface()
        self.screen_rect = self.screen.get_rect()
        self.clock = pg.time.Clock()
        self.fps = 60.0
        self.keys = pg.key.get_pressed()
        self.done = False
        self.player = Player((0,0),7)
        self.level = Level(POND_IMG,self.screen_rect,self.player)

    def event_loop(self):
        """A quiet day in the neighborhood here."""
        for event in pg.event.get():
            self.keys = pg.key.get_pressed()
            if event.type == pg.QUIT or self.keys[pg.K_ESCAPE]:
                self.done = True

    def update(self):
        """Update the level.  In this implementation player updateing is taken
        care of by the level update function."""
        self.screen.fill((0))
        self.level.update(self.keys)
        self.level.draw(self.screen)
        caption = "{} - FPS: {:.2f}".format(CAPTION,self.clock.get_fps())
        pg.display.set_caption(caption)

    def main_loop(self):
        """...and we run in circles."""
        while not self.done:
            self.event_loop()
            self.update()
            pg.display.update()
            self.clock.tick(self.fps)


def image_from_url(url):
    """Load an image from an url.  The return can be loaded by
    pygame.image.load.  Python 2 and 3 compatible."""
    try:
        from urllib2 import urlopen
        from cStringIO import StringIO as inout
    except ImportError:
        from urllib.request import urlopen
        from io import BytesIO as inout
    myurl = urlopen(url)
    return inout(myurl.read())


if __name__ == "__main__":
    os.environ['SDL_VIDEO_CENTERED'] = '1'
    pg.init()
    pg.display.set_mode((700,500))
    face_url  = "http://i1192.photobucket.com/albums/aa340/Mekire/smallface.png"
    pond_url = "http://i1192.photobucket.com/albums/aa340/Mekire/pond-1.png~original"
    PLAY_IMG  = pg.image.load(image_from_url(face_url)).convert_alpha()
    POND_IMG  = pg.image.load(image_from_url(pond_url)).convert_alpha()
    run_it = Control()
    run_it.main_loop()
    pg.quit()
    sys.exit()

Alternatively you can find the code and images in my repo:
https://github.com/Mekire/meks-pygame-samples/tree/master/topdown_scrolling

-Mek
User avatar
Mekire
 
Posts: 987
Joined: Thu Feb 07, 2013 11:33 pm
Location: Amakusa, Japan

Re: [pygame] scrolling the map relitive to characters positi

Postby spamynator_1 » Mon Aug 05, 2013 8:20 pm

If i wanted to in a seperate class communicate with my control class, and wanted to change self.state in the control class via different class how would i do that?
spamynator_1
 
Posts: 63
Joined: Sun Mar 03, 2013 12:45 am

Re: [pygame] scrolling the map relitive to characters positi

Postby spamynator_1 » Mon Aug 05, 2013 8:26 pm

Also i cannot read code i didnt write so well, would it be possible for you to demonstrate what happens here in non class format? as in like loops and if statements so i can read it.
spamynator_1
 
Posts: 63
Joined: Sun Mar 03, 2013 12:45 am

Re: [pygame] scrolling the map relitive to characters positi

Postby Mekire » Mon Aug 05, 2013 11:47 pm

spamynator_1 wrote:If i wanted to in a separate class communicate with my control class, and wanted to change self.state in the control class via different class how would i do that?
If you are making a large program with multiple states, the control class will just manage logic for switching between them. I only put everything inside it for concise examples.

You can see the control class for a more complicated program here:
https://github.com/metulburr/Plants-VS-Zombies/blob/master/data/tools.py#L36
and the states are added to the control class's dictionary of states in the main module:
https://github.com/metulburr/Plants-VS-Zombies/blob/master/data/main.py#L15


spamynator_1 wrote:Also i cannot read code i didnt write so well, would it be possible for you to demonstrate what happens here in non class format? as in like loops and if statements so i can read it.
I'm afraid learning classes is something you will have to do to move forward with game programming. If I were to rewrite that last program without classes it would be a mess. Also, all of the objects we deal with in pygame are classes so knowing how they work is not something one can avoid. Basically the program works like this:
Code: Select all
while our program is running:
    Check events on the event queue.
    Run the current levels update function:
        Run the player's update method and check for collisions.
        Based on the player's confirmed position, update the level's viewport.
        Draw the updated level to the screen.
    Update the display.
    Limit fps using clock.
    Loop.

I think you should start with some basic movement and collision detection on a non-scrolling map before trying to move on.

Check out this one:
https://github.com/Mekire/meks-pygame-samples/blob/master/eight_dir_move.py

If you understand everything that is going on there, move on to the ones here:
https://github.com/Mekire/meks-pygame-samples/tree/master/four_direction_movement

If you have any specific questions about how things are working, I would be glad to answer them.

-Mek
User avatar
Mekire
 
Posts: 987
Joined: Thu Feb 07, 2013 11:33 pm
Location: Amakusa, Japan


Return to Game Development

Who is online

Users browsing this forum: No registered users and 2 guests

cron