Speed of an object in pygame? - python

I am writing a simple pygame program that only consists of moving a box around the screen. The box moves very fast and I want to know how to control the speed. In my code the updated position is moved by 1 and not smaller because if the number is not an integer it makes things more complicated.
import os, sys
import pygame
from pygame.locals import *
pygame.init()
mainClock = pygame.time.Clock()
WINDOWWIDTH = 400
WINDOWHEIGHT = 400
windowSurface = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT), 0, 32)
pygame.display.set_caption("Box")
BLACK = (0, 0, 0)
RED = (255, 0, 0)
WHITE = (255, 255, 255)
size1 = 20
size2 = 2
#character = pygame.Rect(30, 30, 20, 30)
player = pygame.Surface((40,40))
pos1 = 100
pos2 = 100
MOVESPEED = 6
x = 1
while True:
if pos1 == WINDOWWIDTH - 40 and pos1 > 0:
pos1 -= 1
x += 1
elif pos1 < WINDOWWIDTH - 40 and x == 1:
pos1 += 1
elif x ==2:
pos1 -= 1
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == KEYDOWN:
if event.key == K_LEFT:
pos1 -= 5
if event.key == K_RIGHT:
pos1 += 4
windowSurface.fill(WHITE)
#screen.blit(character)
windowSurface.blit(player, (pos1, pos2))
pygame.display.update()

You should put the following bit of code in your "while True:" loop somewhere:
clock.tick([insert fps here])
and put this somewhere before the loop:
clock=pygame.time.Clock()
This will not allow the loop to run more than the number of times you enter per second, and hopefully slow the cube down.

Just don't alter the position in every iteration of your loop.
Instead of
while True:
if ... :
pos1 += 1
...
use somethinig like this:
tmp = 0
while True:
if ... :
tmp += 1
if tmp == 10:
pos1 += 1
tmp = 0
...
or
tmp = 0
while True:
if ... and not tmp % 10:
pos1 += 1
...
where you adjust 10 to a value that will suit you.
Also, you may want to limit the framerate of your programm to get a (more or less) constant framerate using a Clock.

You could use floats to store the positions after all. Change the update values in the while loop to something smaller, e.g. pos1 += 0.25. Then just make sure to blit integers: windowSurface.blit(player, (int(pos1), int(pos2))).

I normally also use integers for most positions but when I say pygame to draw my object/sprite/... to the screen I always divide the position by 10 so that I have 10 steps of values since the objects moves one step on the screen.
Organisation of this is not too hard.

The minimum velocity you can have is 1 but there is a way to slow it down more by controlling time.
For example, moving the box with velocity 1 every 100ms instead of every frame makes the movement appear significantly slower.
See implementation below:
# initialize clock object outside of main loop
clock = pygame.time.Clock()
time = 0
while 1:
time = time + clock.get_time()
if time >= 100:
# Your movement implementation here
time = 0 # Reset timer at the end

Related

ZX81 BASIC to Pygame Conversion of "Dropout" Game

I based the code below on this article: http://kevman3d.blogspot.com/2015/07/basic-games-in-python-1982-would-be.html
and on the ZX BASIC in this image:
10 LET P=0
20 LET T=P
30 FOR Z=1 T0 10
35 CLS
37 PRINT AT 12,0;T
40 LET R=INT (RND*17)
50 FOR Y=0 TO 10
60 PRINT AT Y,R;"O"
70 LET N=P(INKEY$="4")-(INKEY$="1")
80 IF N<0 OR N>15 THEN LET N=P
100 PRINT AT 11,P;" ";AT 11,N;"┗┛";AT Y,R;" "
110 LET P=N
120 NEXT Y
130 LET T=T+(P=R OR P+1=R)
150 NEXT Z
160 PRINT AT 12,0;"YOU SCORED ";T;"/10"
170 PAUSE 4E4
180 RUN
I also shared it on Code Review Stack Exchange, and got a very helpful response refactoring it into high quality Python code complete with type hints.
However, for my purposes I'm wanting to keep the level of knowledge required to make this work a little less advanced, including avoiding the use of OOP. I basically want to maintain the "spirit of ZX BASIC" but make the code "not awful." The use of functions is fine, as we were allowed GOSUB back in the day.
I'm pretty dubious about the approach of using nested FOR loops inside the main game loop to make the game work, but at the same time I'm curious to see how well the BASIC paradigm maps onto the more event driven approach of Pygame, so I'd welcome any comments on the pros and cons of this approach.
More specifically,
Is there somewhere I can put the exit code if event.type == pygame.QUIT where it will work during game rounds, without having to repeat the code elsewhere?
How would this game be implemented if I were to avoid the use of FOR loops / nested FOR loops?
Are there any points of best practice for pygame/Python which I have violated?
What improvements can you suggest, bearing in mind my purpose is to write good Pygame code while maintaining the "spirit" of the ZX81 games?
Any input much appreciated. I'm also curious to see a full listing implementing some of the ideas arising from my initial attempt if anyone is willing to provide one.
import pygame
import random
import sys
# Define colors and other global constants
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
TEXT_SIZE = 16
SCREEN_SIZE = (16 * TEXT_SIZE, 13 * TEXT_SIZE)
NUM_ROUNDS = 5
def print_at_pos(row_num, col_num, item):
"""Blits text to row, col position."""
screen.blit(item, (col_num * TEXT_SIZE, row_num * TEXT_SIZE))
# Set up stuff
pygame.init()
screen = pygame.display.set_mode(SCREEN_SIZE)
pygame.display.set_caption("Dropout")
game_font = pygame.font.SysFont('consolas', TEXT_SIZE)
# Create clock to manage how fast the screen updates
clock = pygame.time.Clock()
# initialize some game variables
player_pos, new_player_pos, coin_row, score = 0, 0, 0, 0
# -------- Main Program Loop -----------
while True:
score = 0
# Each value of i represents 1 round
for i in range(NUM_ROUNDS):
coin_col = random.randint(0, 15)
# Each value of j represents one step in the coin's fall
for j in range(11):
pygame.event.get()
pressed = pygame.key.get_pressed()
if pressed[pygame.K_RIGHT]:
new_player_pos = player_pos + 1
elif pressed[pygame.K_LEFT]:
new_player_pos = player_pos - 1
if new_player_pos < 0 or new_player_pos > 15:
new_player_pos = player_pos
# --- Game logic
player_pos = new_player_pos
coin_row = j
if player_pos + 1 == coin_col and j == 10:
score += 1
# --- Drawing code
# First clear screen
screen.fill(WHITE)
player_icon = game_font.render("|__|", True, BLACK, WHITE)
print_at_pos(10, new_player_pos, player_icon)
coin_text = game_font.render("O", True, BLACK, WHITE)
print_at_pos(coin_row, coin_col, coin_text)
score_text = game_font.render(f"SCORE: {score}", True, BLACK, WHITE)
print_at_pos(12, 0, score_text)
# --- Update the screen.
pygame.display.flip()
# --- Limit to 6 frames/sec maximum. Adjust to taste.
clock.tick(8)
msg_text = game_font.render("PRESS ANY KEY TO PLAY AGAIN", True, BLACK, WHITE)
print_at_pos(5, 0, msg_text)
pygame.display.flip()
waiting = True
while waiting:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit(0)
if event.type == pygame.KEYDOWN:
waiting = False
Here's my reorganisation of your code:
import pygame
import random
# Define global constants
TEXT_SIZE = 16
SCREEN_SIZE = (16 * TEXT_SIZE, 13 * TEXT_SIZE)
NUM_ROUNDS = 5
def print_at_pos(row_num, col_num, item):
"""Blits text to row, col position."""
screen.blit(item, (col_num * TEXT_SIZE, row_num * TEXT_SIZE))
# Set up stuff
pygame.init()
screen = pygame.display.set_mode(SCREEN_SIZE)
pygame.display.set_caption("Dropout")
game_font = pygame.font.SysFont("consolas", TEXT_SIZE)
# Create clock to manage how fast the screen updates
clock = pygame.time.Clock()
# draw the images
player_icon = game_font.render("|__|", True, "black", "white")
# if we don't specify a background color, it'll be transparent
coin_text = game_font.render("O", True, "black")
msg_text = game_font.render("PRESS ANY KEY TO PLAY AGAIN", True, "black", "white")
# initialize some game variables
waiting = False # start in game
player_pos = 0
score = 0
game_round = 0
coin_row = 0
coin_col = random.randint(0, 15)
running = True # For program exit
# -------- Main Program Loop -----------
while running:
# event handling
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
if waiting:
waiting = False
score = 0 # reset score
elif event.key == pygame.K_LEFT:
player_pos -= 1
elif event.key == pygame.K_RIGHT:
player_pos += 1
# --- Game logic
if waiting:
# don't update the game state or redraw screen
print_at_pos(5, 0, msg_text)
else:
coin_row += 1 # TODO: decouple from frame rate
if -1 > player_pos:
player_pos = -1 # so we can catch a coin at zero
elif 15 < player_pos:
player_pos = 15
# coin is in scoring position
if coin_row == 10:
if player_pos + 1 == coin_col:
score += 1
elif coin_row > 10: # round is over
coin_col = random.randint(0, 15)
coin_row = 0
game_round+= 1
if game_round >= NUM_ROUNDS:
waiting = True
game_round = 0 # reset round counter
# --- Drawing code
screen.fill("white") # clear screen
print_at_pos(10, player_pos, player_icon)
print_at_pos(coin_row, coin_col, coin_text)
score_text = game_font.render(f"SCORE: {score}", True, "black", "white")
print_at_pos(12, 0, score_text)
# --- Update the screen.
pygame.display.flip()
# --- Limit to 6 frames/sec maximum. Adjust to taste.
clock.tick(6)
pygame.quit()
I've used a boolean waiting to allow for common event and game state handling that only moves during gameplay. For more complex interactions, you'll want a state machine.
The coin movement is currently coupled to the frame rate, which is easy, but ideally you'd specify a rate/time interval, e.g. 200ms between row drops and then you could have a refresh rate similar to the monitor refresh rate.

Adding infinite number of obstacles in pygame

i am trying to make a flappy bird game in pygame for school but i am struggling with adding the obstacles. I can add one obstacle but i want to add infinite obstacles and i already have an infinite loop to run my game so when i add an extra infinite, loop my game just crashes. I am using a time.sleep() function when add the obstacle in my infinite loop which makes the game crash.
This is my code so far:
import time
import pygame
pygame.init()
xSpeler = 450
ySpeler = 100
widthSpeler = 40
heightSpeler = 40
vel = 10
vel1 = 10
#obstacle 1
xo1 = 900
yo1 = 0
ho1 = 200
wo1 = 50
xo2 = 900
yo2 = 350
ho2 = 200
wo2 = 50
#obstacle 2
xo3 = 900
yo3 = 0
ho3 = 250
wo3 = 50
xo4 = 900
yo4 = 350
ho4 = 150
wo4 = 50
win = pygame.display.set_mode((1000, 500))
bi = pygame.image.load('bada.png')
pygame.display.flip()
run = True
while run:
win.blit(bi, (0, 0))
pygame.time.delay(1)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
keys = pygame.key.get_pressed()
ySpeler += vel
if ySpeler >= 500 - heightSpeler:
ySpeler = 500 - heightSpeler
xo1 -= vel1
xo2 -= vel1
if keys[pygame.K_SPACE] and xSpeler> vel and ySpeler > 0:
ySpeler -= jump
pygame.draw.rect(win, (255, 0, 0), (xSpeler, ySpeler, widthSpeler, heightSpeler))
for x in range(100):
pygame.draw.rect(win, (0, 255, 0), (xo1, yo1, wo1, ho1)) or pygame.draw.rect(win, (0, 255, 0),(xo1, yo1, wo1, ho1))
pygame.draw.rect(win, (0, 255, 0), (xo2, yo2, wo2, ho2)) or pygame.draw.rect(win, (0, 255, 0),(xo2, yo2, wo2, ho2))
time.sleep(7)
pygame.display.update()
im came from netherland so my inglish is not so good and my variables are in dutch so sorry for it
i realy hope someone can help me.
Your screen stays black because of time.sleep(7). It is in a for loop that iterate 100 times so that's your program sleeping 700 seconds every frame. To make an obstacle, make a function that generates and returns 2 obstacles, one for top and the other one for bottom.
def genObstacle():
# generate and return
#1. pygame surface for top and bottom rects
#2. initial position for top rect and bottom rect
topHeight = random.randint(10, 200) # height for bottom obstacle
botHeight = random.randint(10, 200) # height for top obstacle
top = pygame.Surface((10, topHeight)).convert()
bot = pygame.Surface((10, botHeight)).convert()
# return: top rect, bottom rect, top rect's position, bottom rect's position
return [top, bot, [800, 0], [800, 500-botHeight]]
Return the initial positions for both of the obstacles as well, so it is easier to work with. I would really recommend using a class for obstacle, so working with attributes of obstacles like position is easier.
Now that we have a function to generate obstacle, we can make a list to hold those obstacles obstacles = []. Now, we can generate the obstacles every 3 seconds like this:
#other code
start = time.time()
while True:
#other code
now = time.time()
if now - start > 3:
obstacles.append(genObstacle())
start = now
Now that we have obstacles, we can change draw them and change their values like this:
for i in range(len(obstacles)):
# remember, third item in list is position for top and
# fourth item is the position for bottom
# draw the obstacles
win.blit(obstacles[i][0], (obstacles[i][2][0], obstacles[i][3][1]))
win.blit(obstacles[i][1], (obstacles[i][2][0], obstacles[i][3][1]))
# change the x values for it to move to the right
obstacles[i][2][0] -= 1
obstacles[i][2][0] -= 1

Player moves correct speed across the screen, walk cycle is too fast, hoping for suggestions to do it properly

I'm jumping around tutorials trying to successfully complete my first game. I was taking a heavy object oriented approach for managing resources, but I felt that was clouding my understanding a bit, so I restarted. I currently have my character moving smoothly across the screen at a speed I like, my problem is my "walk cycle" of three images goes way to fast, and I get a persistence of vision effect on my little chicken's legs. I need to slow it down, so far I was able to achieve a desired effect by popping in a call to the clock after each decision loop, but I am worried that will slow down the entire game logic, as I am not sure if calling clock more than once is "freezing" my game update in time while the character decides to move. I had considered maybe making some type of equation comparing the time on the clock to the time on the clock before, to slow down the walk cycle key frames. Any help or suggestions, is there an easier method? Thanks a bunch.
import pygame, sys
from pygame.locals import *
pygame.init()
#Contstants
BLACK = (0, 0, 0)
SCREENWIDTH = 300
SCREENHEIGHT = 300
game_running = True
clock = pygame.time.Clock()
#variables
current = 0
class Player(object):
def __init__(self):
self.avatar_front = pygame.image.load("chicken_front.png")
self.avatar_back = pygame.image.load("chicken_back.png")
self.avatar_right = pygame.image.load("chicken_right_stand.png")
self.avatar_left = pygame.image.load("chicken_left_stand.png")
self.avatar_left_walk = pygame.image.load("chicken_left_walk1.png")
self.avatar_left_walk2 = pygame.image.load("chicken_left_walk2.png")
self.avatar_right = pygame.image.load("chicken_right_stand.png")
self.avatar_right_walk = pygame.image.load("chicken_right_walk1.png")
self.avatar_right_walk2 = pygame.image.load("chicken_right_walk2.png")
self.position = [0, 0]
self.current_direction = self.avatar_front
#SetUp
myScreen = pygame.display.set_mode((SCREENWIDTH, SCREENHEIGHT))
pygame.display.set_caption("Chicken Rush!")
pygame.display.set_icon(pygame.image.load("chicken_front.png"))
myPlayer = Player()
while game_running:
myScreen.fill(BLACK)
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
#RIGHT HERE DETERMINES WHICH IMAGE TO DISPLAY FOR WALKING LEFT->This is the part I need to # slow down
keys_pressed = pygame.key.get_pressed()
if keys_pressed[K_LEFT] and myPlayer.position[0] >= 0:
if current == 0:
myPlayer.current_direction = myPlayer.avatar_left
current += 1
elif current == 1:
myPlayer.current_direction = myPlayer.avatar_left_walk
current += 1
elif current == 2:
myPlayer.current_direction = myPlayer.avatar_left_walk2
current = 0
myPlayer.position[0] -= 3
if keys_pressed[K_RIGHT] and myPlayer.position[0] < SCREENWIDTH - 32:
myPlayer.position[0] += 3
if keys_pressed[K_UP] and myPlayer.position[1] >= 0:
myPlayer.position[1] -= 3
if keys_pressed[K_DOWN] and myPlayer.position[1] < SCREENHEIGHT - 35:
myPlayer.position[1] += 3
myScreen.blit(myPlayer.current_direction, (myPlayer.position[0], myPlayer.position[1]))
pygame.display.update()
clock.tick(28)
It seems like you could use a longer interval to slow down the image transitions. Keep in mind you'll need to play with the value of imageTransitionSpeed to get it to your liking, but something like this should help slow the transition down:
#RIGHT HERE DETERMINES WHICH IMAGE TO DISPLAY FOR WALKING LEFT->This is the part I need to # slow down
imageTransitionSpeed = 10
keys_pressed = pygame.key.get_pressed()
if keys_pressed[K_LEFT] and myPlayer.position[0] >= 0:
if current == 0:
myPlayer.current_direction = myPlayer.avatar_left
current += 1
elif current == imageTransitionSpeed:
myPlayer.current_direction = myPlayer.avatar_left_walk
current += 1
elif current == (imageTransitionSpeed * 2):
myPlayer.current_direction = myPlayer.avatar_left_walk2
current = 0
else:
current += 1
myPlayer.position[0] -= 3
if keys_pressed[K_RIGHT] and myPlayer.position[0] < SCREENWIDTH - 32:
myPlayer.position[0] += 3
if keys_pressed[K_UP] and myPlayer.position[1] >= 0:
myPlayer.position[1] -= 3
if keys_pressed[K_DOWN] and myPlayer.position[1] < SCREENHEIGHT - 35:
myPlayer.position[1] += 3
myScreen.blit(myPlayer.current_direction, (myPlayer.position[0], myPlayer.position[1]))
pygame.display.update()
clock.tick(28)

pygame - Real time 2x scale low resolution

I'm writing an arcade game with traditional resolution 240 x 320 (vertical screen)
I need to render that to a modern display in real time.
This means that I need it to double (1 pixel = 4 on output) or even triple (1 pixel = 9)
I can't simply double scale the sprites because the game movement won't scale with them. (movement won't "snap" to visual scale)
Currently I have a game window that is 480 x 640 pixels.
I'm blitting all game sprites to a 240 x 320 surface, double scaling it and outputting that surface to the window with pygame. The game has slowed down far too much now.
How can all these emulators do nice double scale and triple scales with big clean pixels and pygame not? I thought SDL would be better at 2D rasterization.
Here is the code I currently have:
import pygame
import sys
import random
from Bullet import Bullet
bullets = []
pygame.init()
fps_clock = pygame.time.Clock()
# Our final window layer
window = pygame.display.set_mode((480, 640))
# This is the layer that gets scaled
render_layer = pygame.Surface((240, 320))
red = (255, 0, 0)
white = (255, 255, 255)
dkred =(127, 0, 0)
counter = 0;
# Sprite resources
bullet_sprite = pygame.image.load("shot1.png")
bullet_sprite2 = pygame.image.load("shot2.png")
while True:
render_layer.fill(dkred)
for i in bullets:
i.tick()
if i.sprite == "bullet_sprite1":
render_layer.blit(bullet_sprite, (i.x - 12, i.y -12))
else:
render_layer.blit(bullet_sprite2, (i.x - 12, i.y -12))
pygame.transform.scale2x(render_layer, window)
if i.x < 0 or i.y < 0 or i.x > 240 or i.y > 320:
i.dead = True
bullets = [x for x in bullets if x.dead == False]
counter += 3.33
for i in range(10):
if i % 2 == 0:
bullets.append(Bullet(120,120,360.0/10*i - counter, 3, -1,
sprite = "bullet_sprite1"))
else:
bullets.append(Bullet(120,120,360.0/10*i - counter, 3, -1,
sprite = "bullet_sprite2"))
for e in pygame.event.get():
if e.type == pygame.QUIT:
pygame.quit()
sys.exit()
if e.type == pygame.KEYDOWN:
if e.key == pygame.K_ESCAPE:
pygame.event.post(pygame.event.Event(pygame.QUIT))
pygame.display.update()
fps_clock.tick(60)
I found that pygame.transform.scale2x() is in a for loop. Try just using it before pygame.display.update(). If there is more than one bullet, then I see how it could get laggy quickly.

Scrolling in 2D game?

I'm trying to add a scrolling "camera" that follows the player when it moves but can't figure out how to do this. I know that you can just move the level in the opposite direction when you press one of the movement keys but I'd rather not do that as I plan on adding enemies later on and don't want have to keep update their coordinates as the player moves.
I've added my code with a sample level below.
Code:
import pygame, sys, time, random, math
from pygame.locals import *
BACKGROUNDCOLOR = (255, 255, 255)
WINDOWW = 800
WINDOWH = 600
PLAYERW = 66
PLAYERH = 22
FPS = 60
MOVESPEED = 3
YACCEL = 0.13
GRAVITY = 2
BLOCKSIZE = 30
pygame.init()
screen = pygame.display.set_mode((WINDOWW, WINDOWH), 0, 32)
mainClock = pygame.time.Clock()
testLevel = [
(1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,),
(1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,),
(1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,),
(1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,),
(1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,),
(1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,),
(1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,),
(1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,),
(1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,),
(1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,),
(1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,),
(1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,),
(1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,),
(1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,),
(1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,),
(1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,),
(1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,),
(1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,),
(1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,),
(1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,)]
def createblock(length, height, color):
tmpblock = pygame.Surface((length, height))
tmpblock.fill(color)
tmpblock.convert()
return tmpblock
def terminate(): # Used to shut down the software
pygame.quit()
sys.exit()
def add_level(lvl, bSize): # Creates the level based on a map (lvl) and the size of blocks
bList = [] # List of every block
bListDisp = [] # List of every block to display
bTypeList = [] # List with corresponding type of block(wall, air, etc.)
for y in range(len(lvl)):
for x in range(len(lvl[0])):
if lvl[y][x] == 0: # If the block type on lvl[y][x] is '0', write "air" down in the type list
bTypeList.append("air")
elif lvl[y][x] == 1: # If the block type on lvl[y][x] is '1', write "wall" down in the type list
bTypeList.append("solid")
bList.append(pygame.Rect((bSize * x), (bSize * y), bSize, bSize)) #Append every block that is registered
bListDisp.append(pygame.Rect((bSize * x), (bSize * y), bSize, bSize)) #Append every block to display that is registered
return bList, bListDisp, bTypeList
player = pygame.Rect((WINDOWW/2), (WINDOWH - BLOCKSIZE*3), PLAYERW, PLAYERH)
wallblock = createblock(BLOCKSIZE, BLOCKSIZE,(20,0,50))
lastTime = pygame.time.get_ticks()
isGrounded = False
vx = 0
vy = 0
allLevels = [testLevel] # A list containing all lvls(only one for now)
maxLevel = len(allLevels) # Checks which level is the last
currLevel = allLevels[0] # Current level(start with the first lvl)
blockList, blockListDisp, blockTypeList = add_level(currLevel, BLOCKSIZE) # A list with every block and another list with the blocks types
thrusters = True
jumping = False
falling = True
while True:
"""COLLISION"""
collision = False
for i in range(len(blockTypeList)):
if blockTypeList[i] == "solid":
if player.colliderect(blockList[i]):
collision = True
if vx > 0 and not falling:
player.right = blockListDisp[i].left
vx = 0
print('Collide Right')
if vx < 0 and not falling:
player.left = blockListDisp[i].right
vx = 0
print('Collide Left')
if vy > 0:
player.bottom = blockListDisp[i].top
isGrounded = True
falling = False
vy = 0
print('Collide Bottom')
if vy < 0:
player.top = blockListDisp[i].bottom
vy = 0
print('Collide Top')
else:
player.bottom += 1
if player.colliderect(blockList[i]):
collision = True
#isGrounded = True
#falling = False
player.bottom -= 1
if not collision:
falling = True
isGrounded = False
# Input
pressedKeys = pygame.key.get_pressed() # Checks which keys are being pressed
timeDiff = pygame.time.get_ticks() - lastTime # Calculates time difference
lastTime += timeDiff # Last time checked reset to current time
# Shut-down if the ESC-key is pressed or the window is "crossed down"
for event in pygame.event.get():
if event.type == QUIT or event.type == KEYDOWN and event.key == K_ESCAPE:
terminate()
"""X-axis control"""
if pressedKeys[ord('a')]:
vx = -MOVESPEED
if pressedKeys[ord('d')]:
vx = MOVESPEED
if not pressedKeys[ord('d')] and not pressedKeys[ord('a')]:
vx = 0
"""Y-axis control"""
# Controls for jumping
if pressedKeys[ord('w')] and thrusters == True:
vy -= YACCEL * timeDiff; # Accelerate along the y-xis when "jumping", but not above/below max speed
if vy <= -4:
vy = -4
isGrounded = False # You are airborne
jumping = True # You are jumping
if event.type == KEYUP: # If you let go of the "jump"-button, stop jumping
if event.key == ord('w') and vy < 0 and not isGrounded:
jumping = False
falling = True
player.x += vx
player.y += vy
# Gravity
if not isGrounded or falling:
vy += 0.3
if vy > 80:
vy = 80
screen.fill(BACKGROUNDCOLOR)
for i in range(len(blockTypeList)):
if blockTypeList[i] == "solid":
screen.blit(wallblock, (blockListDisp[i].x, blockListDisp[i].y)) #blit the wall-block graphics
pygame.draw.rect(screen, (0, 0, 0), player)
pygame.display.update()
mainClock.tick(FPS)
The trick is to keep track of camera coordinates and use these as an offset in your rendering code. It looks like you're doing you're rendering right at the end of the code you've posted, drawing each block with coord x,y to pixel x,y on the screen.
As you say, shifting the level around isn't great. Instead, have your key inputs (or other camera moving device) change cameraX and cameraY variables, and then add (or subtract, depending which direction you want to go) these values from the block x and y values to change which pixels map to which blocks. I.e. change your rendering to:
screen.blit(wallblock, (blockListDisp[i].x + cameraX, blockListDisp[i].y + cameraY))
This means if your camera moves to (10, 20) then you map your block at (5, 5) to (15, 25) on the screen, shifting your whole level across while your underlying model of the level stays the same. Make sense?
You can also take this slightly further; if your camera is only being moved to follow your character you can make swap cameraX and cameraY in the above for some function of the character position, and have the whole thing just managed directly there.

Categories

Resources