pygame: Stopping movement behind line - python

I am attempting to create a game that lets a block move "jump" and land on a platform above it. Then, jump again to the next platform.
Unfortunately, my code currently just stops when the block touches the bottom of the platform and moves no further. I am unsure why as i believe it should only stop when the bottom of the block hits the line
Specifically am looking at this bit of code, but full code below for context:
#the floor landing code
def hasJumperLanded(rect1, rect2):
for a, b in [(rect1, rect2), (rect2, rect1)]:
if isFloorTouching(a.bottom, b):
return True
def isFloorTouching(y, rect):
if (y > rect.top) and (y < rect.bottom):
return True
else:
return False
snip
#stop when land on floor
for n in range(len(floors)):
if (hasJumperLanded(j['rect'], floors[n]['line'])):
j['jump'] = STILL
Full code context:
import pygame, sys, time
from pygame.locals import *
#the deadzone collision code
def doRectsOverlap(rect1, rect2):
for a, b in [(rect1, rect2), (rect2, rect1)]:
if ((isPointInsideRect(a.left, a.top, b)) or
(isPointInsideRect(a.left, a.bottom, b)) or
(isPointInsideRect(a.right, a.top, b)) or
(isPointInsideRect(a.right, a.bottom, b))):
return True
return False
def isPointInsideRect(x, y, rect):
if (x > rect.left) and (x < rect.right) and (y > rect.top) and (y < rect.bottom):
return True
else:
return False
#the floor landing code
def hasJumperLanded(rect1, rect2):
for a, b in [(rect1, rect2), (rect2, rect1)]:
if isFloorTouching(a.bottom, b):
return True
def isFloorTouching(y, rect):
if (y > rect.top) and (y < rect.bottom):
return True
else:
return False
# set up pygame
pygame.init()
mainClock = pygame.time.Clock()
# set up the window
WINDOWWIDTH = 480
WINDOWHEIGHT = 800
windowSurface = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT), 0, 32)
pygame.display.set_caption('Jumper')
#Directions
LEFT = 4
RIGHT = 6
UP = 8
DOWN = 2
STILL = 5
#blocks location for jumping
#BLOCKLOCY = 700
#Binary for stopping movement
#STOPPER = 0
MOVESPEED = 1
# set up the colors
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
j = {'rect':pygame.Rect(240, 700, 20, 20), 'color':GREEN, 'dir':LEFT, 'jump':STILL}
f1 = {'line':pygame.Rect(0,720,480,2), 'color':GREEN, 'dir':STILL}
f2 = {'line':pygame.Rect(0,650,480,2), 'color':GREEN, 'dir':STILL}
floors = [f1,f2]
# run the game loop
while True:
# check for the QUIT event
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
# draw the black background onto the surface
windowSurface.fill(BLACK)
# This way or that way. Speed Code
if j['dir'] == LEFT:
j['rect'].left -= MOVESPEED
if j['dir'] == RIGHT:
j['rect'].left += MOVESPEED
#JUST JUMP ALREADY!
if j['jump'] == UP:
j['rect'].bottom -= MOVESPEED
#BLOCKLOCY -= MOVESPEED
#Bouce when side hitting
if j['rect'].left < 0:
j['dir'] = RIGHT
if j['rect'].left > WINDOWWIDTH-j['rect'].width:
j['dir'] = LEFT
#Press to Jump
if event.type == KEYDOWN:
if event.key == K_SPACE:
j['jump'] = UP
#stop when land on floor
for n in range(len(floors)):
if (hasJumperLanded(j['rect'], floors[n]['line'])):
j['jump'] = STILL
#Floor controll code for moving level - not working currently
for f in floors:
#if f['dir'] == DOWN:
# f['line'].y += MOVESPEED
# if event.type == KEYDOWN:
# if event.key == K_SPACE:
# f['dir'] = DOWN
# if f['line'].top == BLOCKLOCY:
# f['dir'] = STILL
# STOPPER = 1
#if f['line'].bottom == BLOCKLOCY:
# f['dir'] = STILL
# STOPPER = 1
# draw the block onto the surface
pygame.draw.rect(windowSurface, j['color'], j['rect'])
pygame.draw.rect(windowSurface, f['color'], f['line'])
# draw the window onto the screen
pygame.display.update()
mainClock.tick(1000)

#the floor landing code
def hasJumperLanded(rect1, rect2):
for a, b in [(rect1, rect2), (rect2, rect1)]: ## **
if isFloorTouching(a.bottom, b):
return True
Take a look at the line I marked in this snippet.
Here You are checking the rects to collide both ways.
So you loose the meaning of which one is the floor and which one is moving.
If you just check (rect1, rect2) you will see the difference.
--
EDIT:
Check this out
def hasJumperLanded(rect1, rect2):
for a, b in [(rect1, rect2), (rect2, rect1)]:
if isFloorTouching(rect1, rect2):
return True
def isFloorTouching(y, rect):
if (y.bottom > rect.top) and (y.bottom < rect.bottom):
return True
else:
return False
It is more logical to handle the meaning of the floor inside the isFloorTouching() function.

Related

How to delete a single pygame drawing from the screen?

When the big circle touches the little circles I want the little circle that it touched to disappear from the screen. However, I can't figure out how exactly you delete an individual drawing in pygame. How do I fix this issue? does pygame have this feature built-in?
from pygame import *
import random as rd
import math as m
init()
screen = display.set_mode((800, 600))
p_1_x = 200
p_1_y = 200
p_1_change_x = 0
p_1_change_y = 0
def p_1(x, y):
player_1 = draw.circle(screen, (0, 0, 0), (x, y), 15)
def pick_up(x, y, xx, yy):
distance = m.sqrt(m.pow(xx - x, 2) + m.pow(yy - y, 2))
if distance < 19:
# I think the code to delete should go here
pass
dots = []
locations = []
for i in range(5):
x = rd.randint(100, 700)
y = rd.randint(100, 500)
locations.append((x, y))
while True:
screen.fill((255, 255, 255))
for events in event.get():
if events.type == QUIT:
quit()
if events.type == KEYDOWN:
if events.key == K_RIGHT:
p_1_change_x = 1
if events.key == K_LEFT:
p_1_change_x = -1
if events.key == K_UP:
p_1_change_y += 1
if events.key == K_DOWN:
p_1_change_y -= 1
if events.type == KEYUP:
if events.key == K_RIGHT or K_LEFT or K_UP or K_DOWN:
p_1_change_x = 0
p_1_change_y = 0
p_1_x += p_1_change_x
p_1_y -= p_1_change_y
for i, locate in enumerate(locations):
dot = draw.circle(screen, (0, 0, 0), locate, 5)
dots.append(dot)
for l in enumerate(locate):
pick_up(p_1_x, p_1_y, locate[0], locate[1])
p_1(p_1_x, p_1_y)
display.update()
Your code was so messy and hard to maintain, first I made 2 classes for Balls & Dots.
I detect collision by pygame.Rect.colliderect, first I make 2 rectangle then I check the collision like this:
def pick_up(ball, dot):
ball_rect = Rect( ball.x - ball.SIZE , ball.y - ball.SIZE , ball.SIZE*2, ball.SIZE*2)
dot_rect = Rect( dot.x - dot.SIZE , dot.y - dot.SIZE , dot.SIZE*2, dot.SIZE*2)
if ball_rect.colliderect(dot_rect):
return True
return False
If collision detects I remove it from dots array in the while loop:
for dot in dots:
if pick_up(ball, dot): # if dot in range ball
dots.remove(dot)
dot.draw()
Here is the whole source:
from pygame import *
import random as rd
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
NUMBER_OF_DOTS = 5
class Ball():
SIZE = 15
def __init__(self, x, y):
self.x = x
self.y = y
def draw(self):
draw.circle(screen, (0, 0, 0), (self.x, self.y), Ball.SIZE)
def move(self, vx, vy):
self.x += vx
self.y += vy
class Dot():
SIZE = 5
def __init__(self, x, y):
self.x = x
self.y = y
def draw(self):
draw.circle(screen, (0, 0, 0), (self.x, self.y), Dot.SIZE)
def pick_up(ball, dot):
ball_rect = Rect( ball.x - ball.SIZE , ball.y - ball.SIZE , ball.SIZE*2, ball.SIZE*2)
dot_rect = Rect( dot.x - dot.SIZE , dot.y - dot.SIZE , dot.SIZE*2, dot.SIZE*2)
if ball_rect.colliderect(dot_rect):
return True
return False
init()
screen = display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
dots = []
ball = Ball(200,200)
# generate dots
for i in range(NUMBER_OF_DOTS):
x = rd.randint(100, 700)
y = rd.randint(100, 500)
dots.append(Dot(x,y))
# the main game loop
while True:
screen.fill((255, 255, 255))
keys=key.get_pressed()
for events in event.get():
keys=key.get_pressed()
if events.type == QUIT:
quit()
if keys[K_RIGHT]:
ball.move(+1,0)
if keys[K_LEFT]:
ball.move(-1,0)
if keys[K_UP]:
ball.move(0,-1)
if keys[K_DOWN]:
ball.move(0,+1)
for dot in dots:
dot.draw()
if pick_up(ball, dot):
dots.remove(dot)
ball.draw()
display.update()
time.delay(1) # Speed down
Update1:
PyGame Rectangle Collision
http://www.pygame.org/docs/ref/rect.html#pygame.Rect.colliderect
Update2:
I make a repo in the github and did some changes,
Dots are colorful, new dot gets random color and the ball gets bigger whenever eats a dot.
https://github.com/peymanmajidi/Ball-And-Dots-Game__Pygame
The code should delete it from the locations list so that it's not re-drawn in the future. You clear the screen each frame, so clearing + not-redrawing is "deleting".
Say you modified pick_up() to simply return True or False:
def pick_up(x, y, xx, yy):
result = False
distance = m.sqrt(m.pow(xx - x, 2) + m.pow(yy - y, 2))
if distance < 19:
result = True # It was picked
return result
Then as you iterate through the locations list drawing & checking for being picked, save the index of the picked circles, then remove them from the locations in a second step. Using the 2-step form means you don't have to worry about accidentally skipping items if you delete from the list as you iterate over it.
p_1_x += p_1_change_x
p_1_y -= p_1_change_y
picked_up = [] # empty list to hold "picked" items
for i, locate in enumerate(locations):
dot = draw.circle(screen, (0, 0, 0), locate, 5)
dots.append(dot)
for l in enumerate(locate):
if ( pick_up(p_1_x, p_1_y, locate[0], locate[1]) ):
picked_up.append( i ) # save the index of anything "picked"
# remove any picked-up circles from the list
for index in sorted( picked_up, reverse=True ): # start with the highest index first
print( "Removing circle from location[%d]" % ( index ) ) # DEBUG
del( locations[ index ] )

Bouncing Ball doesn't come back pygame

import pygame
pygame.init()
width = 400
hight = 600
screen = pygame.display.set_mode((width, hight))
pygame.display.set_caption("Engine")
dot = pygame.image.load("KreisSchwarz.png")
clock = pygame.time.Clock()
running = True
WHITE = (255, 255, 255)
# Set (x, y) for Dot
def updateDot(x, y):
screen.blit(dot, (x, y))
# Display Dot at (x, y)
def update(fps=30):
screen.fill(WHITE)
updateDot(x, y)
pygame.display.flip()
return clock.tick(fps)
# Quit if User closes the window
def evHandler():
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
running = False
yKoords = []
(x, y) = (300, 200)
t = 1 # time variable
a = 2 # acceleration constant
tol = 40 # tolerance
i = 0 # just some iterator
# MAIN LOOP
while running:
evHandler()
update()
y += a * (t ^ 2)
t += 1
yKoords.append(int(y))
i += 1
if (y < (hight + tol)) and (y > (hight - tol)):
y = 580
yKoords.reverse()
update()
for q in range(i):
evHandler()
y = yKoords[q]
update()
if q == i - 1: # Because i didn't write the Part for the Dot coming back down
running = False
This is my Code for a Ball accelerating down and then jumping back up.
My Problem is, that the code works fine until the if statement. There the Programm just displays the Ball at the last position in yKoords and waits until the for loop finishes. If i remove the for loop the Ball gets displayed at y=580 and stops but thats fine.
Please help i have no idea whats wrong about this.
Don't do a separate process loop in the main loop.
It is sufficient to invert the direction, when the ball bounce on the ground (abs(y - hight)) or the ball reaches the top (t == 0).
direction = 1
while running:
evHandler()
update()
y += (a * (t ^ 2)) * direction
t += direction
if abs(y - hight) < tol:
y = 580
t -= 1
direction *= -1
elif t == 0:
direction *= -1

Pygame: colliding rectangles with other rectangles in the same list

I have a list of 10 drawn rectangles (referenced as cubes in my script) that are affected by gravity. I made a simple collision system for them to stop when they hit the ground. How can I make it so when 2 cubes collide they stop falling like they do with the ground?
import pygame
import time
import random
pygame.init()
clock = pygame.time.Clock()
wnx = 800
wny = 600
black = (0,0,0)
grey = (75,75,75)
white = (255,255,255)
orange = (255,100,30)
wn = pygame.display.set_mode((wnx, wny))
wn.fill(white)
def cube(cx,cy,cw,ch):
pygame.draw.rect(wn, orange, [cx, cy, cw, ch])
def floor(fx,fy,fw,fh):
pygame.draw.rect(wn, grey, [fx, fy, fw, fh])
def main():
floory = 550
number = 30
cubex = [0] * number
cubey = [0] * number
cubew = 10
cubeh = 10
for i in range(len(cubex)):
cubex[i] = (random.randrange(0, 80)*10)
cubey[i] = (random.randrange(2, 5)*10)
gravity = -10
exit = False
while not exit:
for event in pygame.event.get():
if event.type == pygame.QUIT:
exit = True
for i in range(len(cubex)): #i want to check here if it collides with an other cube
if not (cubey[i] + 10) >= floory:
cubey[i] -= gravity
wn.fill(white)
floor(0,floory,800,50)
for i in range(len(cubex)):
cube(cubex[i], cubey[i], cubew, cubeh)
pygame.display.update()
clock.tick(5)
main()
pygame.quit()
quit()
Use pygame.Rect.colliderect to check if to rectangles are intersecting.
Create an rectangle (pygame.Rect) which defines the next position (area) of the cube:
cubeR = pygame.Rect(cubex[i], cubey[i] + 10, cubew, cubeh)
Find all intersecting rectangles
cl = [j for j in range(len(cubey)) if j != i and cubeR.colliderect(pygame.Rect(cubex[j], cubey[j], cubew, cubeh))]
And don't move (let further "fall") the cube if there is any() collision:
if not any(cl):
# [...]
The check may look like this:
for i in range(len(cubex)):
cubeR = pygame.Rect(cubex[i], cubey[i] + 10, cubew, cubeh)
cisect = [j for j in range(len(cubey)) if j != i and cubeR.colliderect(pygame.Rect(cubex[j], cubey[j], cubew, cubeh))]
if not any(cisect) and not (cubey[i] + 10) >= floory:
cubey[i] -= gravity
Note, since all the cubes are aligned to an 10*10 raster, it is sufficient to check if the origins of the cubes are equal:
for i in range(len(cubex)):
cisect = [j for j in range(len(cubey)) if j != i and cubex[i] == cubex[j] and cubey[i]+10 == cubey[j]]
if not any(cisect) and not (cubey[i] + 10) >= floory:
cubey[i] -= gravity

Python Pygame randomly draw non overlapping circles

Im very new to python and seem to be missing something.
I want to randomly draw circles on a pygame display but only if the circles don't overlap each other.
I believe I must find the distance between all circle centers and only draw it if the distance is bigger than circle radius * 2.
I've tried many different things but all without success, I always get the same result - circles drawn overlapping.
#!/usr/bin/env python
import pygame, random, math
red = (255, 0, 0)
width = 800
height = 600
circle_num = 10
tick = 2
speed = 5
pygame.init()
screen = pygame.display.set_mode((width, height))
class circle():
def __init__(self):
self.x = random.randint(0,width)
self.y = random.randint(0,height)
self.r = 100
def new(self):
pygame.draw.circle(screen, red, (self.x,self.y), self.r, tick)
c = []
for i in range(circle_num):
c.append('c'+str(i))
c[i] = circle()
for j in range(len(c)):
dist = int(math.hypot(c[i].x - c[j].x, c[i].y - c[j].y))
if dist > int(c[i].r*2 + c[j].r*2):
c[j].new()
pygame.display.update()
else:
continue
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
You did not check against all other circles. I added a variable shouldprint which gets set to false if any other circle is too close.
import pygame, random, math
red = (255, 0, 0)
width = 800
height = 600
circle_num = 20
tick = 2
speed = 5
pygame.init()
screen = pygame.display.set_mode((width, height))
class circle():
def __init__(self):
self.x = random.randint(0,width)
self.y = random.randint(0,height)
self.r = 100
def new(self):
pygame.draw.circle(screen, red, (self.x,self.y), self.r, tick)
c = []
for i in range(circle_num):
c.append('c'+str(i))
c[i] = circle()
shouldprint = True
for j in range(len(c)):
if i != j:
dist = int(math.hypot(c[i].x - c[j].x, c[i].y - c[j].y))
if dist < int(c[i].r*2):
shouldprint = False
if shouldprint:
c[i].new()
pygame.display.update()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
The for loop has been changed to a while loop. It will keep trying to generate circles until the target number is reached. A circle is first generated. Then, it checks if it intersects with any existing circle using the formula from this answer.
It iterates through every existing circle (store in the list circles) and performs the check using the formula. any() returns True if the formula evaluates to True for any iteration. If it's True, it means it found an intersection. Thus, it continues to the next iteration to try again with a new circle.
circles = []
while len(circles) < circle_num:
new = circle()
if any(pow(c.r - new.r, 2) <=
pow(c.x - new.x, 2) + pow(c.y - new.y, 2) <=
pow(c.r + new.r, 2)
for c in circles):
continue
circles.append(new)
new.new()
pygame.display.update()

Scrolling camera

I've been tinkering with a scrolling camera that follows the player around and have got it to follow the player. The problem is that it moves slower than the player and because of this the player wont stay in the middle of the screen.
I believe the problem is in the offset (cameraX) and have tried a few different values but haven't found any that work.
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
cameraX = player.x - WINDOWW/2
# 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-cameraX, blockListDisp[i].y)) #blit the wall-block graphics
pygame.draw.rect(screen, (0, 0, 0), player)
pygame.display.update()
mainClock.tick(FPS)
You don't apply the camera-offset to the player itself, only to the wallblocks.
So change
pygame.draw.rect(screen, (0, 0, 0), player)
to
pygame.draw.rect(screen, (0, 0, 0), player.move(-cameraX, 0))
Some more notes:
Using three lists (blockList, blockListDisp, blockTypeList) to keep track of your level is way to complex, use a single list :-)
Change your add_level to:
# use a dict to keep track of possible level blocks, so adding new ones becomes simple
types = {0: "air", 1: "solid"}
def add_level(lvl, bSize): # Creates the level based on a map (lvl) and the size of blocks
for y in range(len(lvl)):
for x in range(len(lvl[0])):
# no more if/elif
yield types[lvl[y][x]], pygame.Rect((bSize * x), (bSize * y), bSize, bSize)
your lists to:
blocks = list(add_level(currLevel, BLOCKSIZE)) # a single list which holds the type and rect for each block of the level
Then your collision detection can look like this:
while True:
"""COLLISION"""
collision = False
for type, rect in blocks: # list contains a tuple of type, rect
if type == "solid":
if player.colliderect(rect):
collision = True
if vx > 0 and not falling:
player.right = rect.left # now you can always use the rect directly instead of accesing other lists
vx = 0
print('Collide Right')
...
Also, the drawing code becomes simpler:
for type, rect in blocks:
if type == "solid":
screen.blit(wallblock, rect.move(-cameraX, 0)) #blit the wall-block graphics
pygame.draw.rect(screen, (0, 0, 0), player.move(-cameraX, 0))
In the long run, you may want to use a class instead of a tuple, but that's another topic.

Categories