0
votes

I'm making a rogue-like and I trying to make a camera follow the player as he moves by the map.

I was able to make the Draw function only happens when the tiles are inside the camera viewport[show in gray], but I can't make the camera stay in the corner of the window.

This is how it is: enter image description here

And this is How it should be: enter image description here

Is there a way to 'crop' the screen Surface, or perhaps copy only whats inside the camera viewport and blit it in the screen again.

Probably I'm doing this the hard way. I'm iterating over the whole map, creating a rectangle for each tile, and checking if it's inside the Camera Viewport Rect using the '.contains()'.

EDIT: This is how I'm drawing the map:

for x in xrange(mymap.width):
    for y in xrange(mymap.height):
        lit = field_of_view.lit(x, y)
        visited = field_of_view.visited(x, y)
        graphic = mymap.tileAt(x, y).graphic
        if lit:
            color = mymap.tileAt(x, y).color
        elif visited:
            color = GRAY
        else:
            color = BLACK
        renderedgraphic = myfont.render(graphic, 1, color)
        screen.blit(renderedgraphic, (x*TILESIZE, y*TILESIZE))

I do the same thing for the player, monsters, items and etc, but everything in it's own classmethod.

My camera is set like this:

class Camera(Object):
    def __init__(self, x, y):
        graphic = ''
        Object.__init__(self, graphic, x, y)
        self.rect = pygame.Rect(x, y, CAMERA_WIDTH * 2 + 5, CAMERA_HEIGHT * 2 + 5)

    def update(self):
        self.x = PLAYER.x
        self.y = PLAYER.y

        startx = ((self.x * TILESIZE) - CAMERA_WIDTH) + 5
        starty = ((self.y * TILESIZE) - CAMERA_HEIGHT) + 5
        self.rect = pygame.Rect(startx, starty, CAMERA_WIDTH * 2 + 5, CAMERA_HEIGHT * 2 + 5)

So I tried what user3762084 said.

in short:

for x in xrange(mymap.width):
    for y in xrange(mymap.height):
        ... # do fov stuff        
        tile = Tile.At(x, y)  # get tile instance
        if tile:
            tile.update() # update it's relative position
            screen.blit(renderedgraphic, (tile.relX * TILESIZE, tile.relX * TILESIZE)) # blit the postion to it's relative position

This is what happens:

enter image description here

It's all squished in the side of the window. And if the player moves it all goes black.

2

2 Answers

2
votes

What I have done before to make a scrolling environment was to give each object its coordinates in the world, and then give your camera a position. When you are drawing, you then calculate the position of each object on the screen based on the object's actual coordinates and the camera's coordinates.

class Tile:
    def __init__(self, x, y, other_variables):
        self.x = x
        self.y = y

        # relative vars for later
        self.relX = None
        self.relY = None

# then your camera should have a position as well as its width and height:
class Camera:
    def __init__(self, x, y, width, height):
        # assign those variables here

# your drawing function:
for tile in tiles:
    tile.relX = tile.x - camera.x
    tile.relY = tile.y - camera.y
    # blit your tiles to the screen at the relative coordinates

In addition, you could also implement checks to see if the tile is completely outside of the camera space and not draw those tiles.

1
votes

Ok I got what I was looking for, I did a litte hack tho. So if anyone have a better way to do this.

I changede the way my map was drawing

  • I took the Camera Rect positions [topleft and bottomright];
  • Converted it to World Position;
  • Iterate over it, with a enumerator too;
  • Did any lit/visited FOG calcunations with X and Y;
  • And Blited in the screen using the enumerators 'i' and 'j'.

Here's the code:

topleft = Map.toWorld(camera.rect.topleft)
bottomright = Map.toWorld(camera.rect.bottomright)
for i, x in enumerate(xrange(topleft[0], bottomright[0])):
    for j, y in enumerate(xrange(topleft[1], bottomright[1])):
        tile = mymap.tileAt(x, y)
        object = [obj for obj in Object.OBJECTS if obj.pos == (x,y)]
        if tile:
            lit = field_of_view.lit(x, y)
            visited = field_of_view.visited(x, y)
            graphic = tile.graphic
            if lit:
                color = tile.color
            elif visited:
                color = GRAY
            else:
                color = BLACK
            renderedgraphic = myfont.render(ch, 1, graphic)
            screen.blit(renderedgraphic, Map.toScreen((i + 1, j)))
        if object:
            Draw.drawObject(object[0], Map.toScreen((i + 1, j)))

The only issue now is when the player is at the sides of the map it shows a black border.