I am currently making a 2d game in Pygame and have run into a roadblock trying to make a scrolling camera (follows the character). I have seen some other answers to similar questions, but they have helped very little. Also, I am purposely doing this without the use of Pygame's Sprite
class, so that makes things a little more complex. How should I approach this?
display_width = 800
display_height = 640
size = (display_width, display_height)
half_width = int(display_width / 2)
half_height = int(display_height / 2)
black = (0,0,0)
white = (255,255,255)
clock = pygame.time.Clock()
pygame.display.set_caption("My game")
screen = pygame.display.set_mode(size)
background = Surface((32,32))
background.convert()
background.fill(Color('#783131'))
x = 0
y = 0
platformlst = []
players = []
level = [
"PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP",
"P P",
"P P",
"P P",
"P PPPPPPPPPPP P",
"P P",
"P P",
"P P",
"P PPPPPPPP P",
"P P",
"P PPPPPPP P",
"P PPPPPP P",
"P P",
"P PPPPPPP P",
"P P",
"P PPPPPP P",
"P P",
"P PPPPPPPPPPP P",
"P P",
"P PPPPPPPPPPP P",
"P P",
"P P",
"P P",
"P P",
"PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP",]
total_level_width = len(level[0])*32
total_level_height = len(level)*32
map_size = (total_level_width, total_level_height)
virtualwindow = pygame.Surface.get_rect(screen)
class Camera:
def __init__(self, players):
self.left_viewbox = display_width/2 - display_width/8
self.right_viewbox = display_width/2 + display_width/10
def follow(self, shift_x):
virtualwindow.x += shift_x
print virtualwindow.x
for i in players:
i.rect.x += shift_x
def viewbox(self):
if player.x <= self.left_viewbox:
view_difference = self.left_viewbox - player.x
player.x = self.left_viewbox
self.follow(view_difference)
if player.x >= self.right_viewbox:
view_difference = self.right_viewbox - player.x
player.x = self.right_viewbox
self.follow(view_difference)
Basically, the player
objects are added to a list, which is called out in the follow()
function, which is called by the viewbox()
function. Then the follow()
function should be adding the view_difference
as shift_x
to the virtualwindow.x
. Shouldn't this therefore scroll the map along its x
axis relative with the characters movement?
Here is the full code: https://github.com/tear727/Netse/blob/master/game.py
Answer
Code:
import pygame
pygame.init()
#
###
class Player:
def __init__(self):
self.image = pygame.Surface((16,16)) # Create Player Image
self.image.fill(colors["RED"]) # Fill Player Red
self.rect = pygame.Rect((50,50),(16,16)) # Create Player Rect
def move(self,camera_pos):
pos_x,pos_y = camera_pos # Split camara_pos
#
key = pygame.key.get_pressed() # Get Keyboard Input
if key[pygame.K_w]: # Check Key
self.rect.y -= 8 # Move Player Rect Coord
pos_y += 8 # Move Camara Coord Against Player Rect
if key[pygame.K_a]:
self.rect.x -= 8
pos_x += 8
if key[pygame.K_s]:
self.rect.y += 8
pos_y -= 8
if key[pygame.K_d]:
self.rect.x += 8
pos_x -= 8
#
if self.rect.x < 0: # Simple Sides Collision
self.rect.x = 0 # Reset Player Rect Coord
pos_x = camera_pos[0] #Reset Camera Pos Coord
elif self.rect.x > 984:
self.rect.x = 984
pos_x = camera_pos[0]
if self.rect.y < 0:
self.rect.y = 0
pos_y = camera_pos[1]
elif self.rect.y > 984:
self.rect.y = 984
pos_y = camera_pos[1]
#
return (pos_x,pos_y) # Return New Camera Pos
def render(self,display):
display.blit(self.image,(self.rect.x,self.rect.y))
###
#
#
###
def Main(display,clock):
world = pygame.Surface((1000,1000)) # Create Map Surface
world.fill(colors["BLACK"]) # Fill Map Surface Black
for x in range(10):
pygame.draw.rect(world,colors["BLUE"],((x*100,x*100),(20,20))) # Put Blue Rectagles On Map Surface
#
player = Player() # Initialize Player Class
camera_pos = (192,192) # Create Camara Starting Position
#
while True:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
return
#
camera_pos = player.move(camera_pos) # Run Player Move Function And Return New Camera Pos
#
display.fill(colors["WHITE"]) # Fill The Background White To Avoid Smearing
world.fill(colors["BLACK"]) # Refresh The World So The Player Doesn't Smear
for x in range(10):
pygame.draw.rect(world,colors["BLUE"],((x*100,x*100),(20,20)))
player.render(world) # Render The Player
display.blit(world,camera_pos) # Render Map To The Display
#
pygame.display.flip()
###
#
if __name__ in "__main__":
display = pygame.display.set_mode((500,500))
pygame.display.set_caption("Scrolling Camera")
clock = pygame.time.Clock()
#
global colors # Difign Colors
colors = {
"WHITE":(255,255,255),
"RED" :(255,0,0),
"GREEN":(0,255,0),
"BLUE" :(0,0,255),
"BLACK":(0,0,0)
}
Main(display,clock) # Run Main Loop
I remember when I had this same issue. The above code is an example I wrote up quick showing how handled the issue. Feel free to take it and reverse engineer it to your hearts content. :)
The way I figured it out is that since the actual window is itself a surface, if all the games world assets, the player, scenery, goals, etc., were placed on a player made surface instead of the display surface. This surface can then be moved against the player movement to create the allusion that the player remains in the same place.
This is shown in my example code here within the players move function:
pos_x,pos_y = camera_pos # Split camara_pos
#
key = pygame.key.get_pressed() # Get Keyboard Input
if key[pygame.K_w]: # Check Key
self.rect.y -= 8 # Move Player Rect Coord
pos_y += 8 # Move Camara Coord Against Player Rect
if key[pygame.K_a]:
self.rect.x -= 8
pos_x += 8
if key[pygame.K_s]:
self.rect.y += 8
pos_y -= 8
if key[pygame.K_d]:
self.rect.x += 8
pos_x -= 8
By moving the blitting coods of the camera the an opposite direction and at the same velocity of the player, it cancels out the momentum of the player object, and creates the illusion that the player is standing still while the game world moves around him/her/it. (I actually figured this all out in Physics class during a lecture on momentum)
Make sure to blit the player to the world surface and not the display surface as shown in the code:
display.fill(colors["WHITE"]) # Fill The Background White To Avoid Smearing
world.fill(colors["BLACK"]) # Refresh The World So The Player Doesn't Smear
for x in range(10):
pygame.draw.rect(world,colors["BLUE"],((x*100,x*100),(20,20)))
player.render(world) # Render The Player
display.blit(world,camera_pos) # Render Map To The Display
As if you mistakenly blit the player to the display instead of the world, they won't work off of each other and the player will instead have the illusion of moving twice as fast as the world. If you want, you can switch out:
player.render(world) # Render The Player
with:
player.render(display) # Render The Player
And see what I mean by that.
I hope all this helps, and best of luck with your future coding projects. :)
No comments:
Post a Comment