Scrolling camera - python
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.
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 ] )
Jumping creates a distance between man and the floor Pygame [duplicate]
This question already has answers here: How to simulate Jumping in Pygame for this particular code (1 answer) jumping too fast? (1 answer) How to make a circular object jump using pygame? [duplicate] (2 answers) Pygame Bouncy Ball Sinks Through Floor (1 answer) Closed 2 years ago. The full code: import glob import pygame import os os.environ['SDL_VIDEO_WINDOW_POS'] = "%d,%d" % (100, 40) class Man(pygame.sprite.Sprite): # just taking random image to get height and width after trans scale to be able to crop later (see lines 23/36) idle_images_list = glob.glob(r"C:\Users\aviro\Desktop\מחשבים\python projects\platform game\animation\fighter\PNG\PNG Sequences\Firing"+'\*') random_image = pygame.image.load(idle_images_list[0]) new_img_width = int(random_image.get_width()/2.5) new_img_height = int(random_image.get_height()/2.5) random_image_after_crop = random_image.subsurface((70, 35, new_img_width-125, new_img_height-45)) # to be able to use needed images indexes # getting idle animations list right_idle_list = [] left_idle_list = [] for images in idle_images_list: img = pygame.image.load(images) wid = img.get_width() hei = img.get_height() img = pygame.transform.scale(img, (int(wid/2.5), int(hei/2.5))) img = img.subsurface((70, 35, new_img_width-125, new_img_height-45)) right_idle_list.append(img) left_idle_list.append(pygame.transform.flip(img, True, False)) # getting movement animations list walk_animation_list = glob.glob(r"C:\Users\aviro\Desktop\מחשבים\python projects\platform game\animation\fighter\PNG\PNG Sequences\Run Firing"+'\*') walk_right_list = [] walk_left_list = [] for files in walk_animation_list: # setting the animation list img = pygame.image.load(files) wid = img.get_width() hei = img.get_height() img = pygame.transform.scale(img, (int(wid/2.5), int(hei/2.5))) # chaging scale img = img.subsurface((70, 35, new_img_width-125, new_img_height-45)) walk_right_list.append(img) walk_left_list.append(pygame.transform.flip(img, True, False)) # mirror list def __init__(self, x, y,): super(Man, self).__init__() self.walk_left_list = Man.walk_left_list self.walk_right_list = Man.walk_right_list self.width = Man.new_img_width self.height = Man.new_img_height self.hitbox = (x+55, y+35, self.width-125, self.height-45) # nothing special on those num, just Trial and error self.rect = Man.random_image_after_crop.get_rect() self.rect.x = x self.rect.y = y def game_redraw(): # print things in end for main loop global right global left screen.blit(bg_image, (0, 0)) if right: screen.blit(man.walk_right_list[frame_count//3], (man.rect.x, man.rect.y)) elif left: screen.blit(man.walk_left_list[frame_count//3], (man.rect.x, man.rect.y)) else: if last_action == "right": screen.blit(man.right_idle_list[frame_count//13], (man.rect.x, man.rect.y)) if last_action == "left": screen.blit(man.left_idle_list[frame_count//13], (man.rect.x, man.rect.y)) else: # just for the first move screen.blit(man.right_idle_list[frame_count//13], (man.rect.x, man.rect.y)) pygame.draw.rect(screen, RED, man.rect, 4) pygame.display.flip() right = False left = False def input_process(key): global right global left global last_action global man global frame_count global is_jump global neg global jump_count # if is_jump: if jump_count >= -10: # check if jumping if jump_count < 0: neg = -1 man.rect.y -= (jump_count ** 2) * neg * 0.5 jump_count -= 1 else: neg = 1 is_jump = False # if key[pygame.K_RIGHT] and man.rect.right + speed < w: if left: frame_count = 0 right = True left = False last_action = "right" man.rect.x += speed # if key[pygame.K_LEFT] and man.rect.left + speed > 0: if right: frame_count = 0 right = False left = True last_action = "left" man.rect.x -= speed # if not is_jump: if key[pygame.K_UP]: jump_count = 10 is_jump = True w = 1728 h = 972 pygame.init() RED = (255, 0, 0) images_folder = r'C:\Users\aviro\Desktop\מחשבים\python projects\platform game\images'+'\\' bg_image = images_folder+"background.png" # setting background image ground_height = h-143 # the high of the image ground man = Man(200, ground_height) man.rect.bottom = ground_height clock = pygame.time.Clock() Refresh_Rate = 54 speed = 5 right = False left = False frame_count = 0 finish = False last_action = "" # jumping ver is_jump = False jump_count = 10 neg = 1 # screen = pygame.display.set_mode((w, h)) bg_image = pygame.image.load(bg_image).convert() # can do convert only after setting surface. # main loop while not finish: if frame_count >= 51: frame_count = 0 for events in pygame.event.get(): if events.type == pygame.QUIT: finish = True key = pygame.key.get_pressed() input_process(key) game_redraw() frame_count += 1 clock.tick(Refresh_Rate) I'm trying to create a simple shooting-platform game, using pygame module. Everything works fine except the jumping. See in line 88. The player is jumping like an x^2 parabola. I added the *0.5 at the end of the line to make the player jump slower and lower, but when I do it this is what happened. Before jumping: After jumping: Look in the second picture. There's a distance between the floor and the player. When I remove the *0.5 everything works fine. Why?
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
Python's list object do not allow index value change
I'm creating my first game using Python and Pygame. It consists on objects falling to the ground and an object controlled by the player catching them. However, It gives me an Exception when the falling object hits the ground: Traceback (most recent call last): File "/home/me/Chest/game.py", line 79, in <module> resetShirtPosition() File "/home/me/Chest/game.py", line 35, in resetShirtPosition shirtPosition[0] = random.randint(0, DISPLAY_WIDTH - shirt_size[0]) TypeError: 'tuple' object does not support item assignment Seems like it doesn't allow the vector to change by index. Which I find odd because I'm not assigning shirtPosition with a tuple but with a list. Besides that I'm changing the chestPosition the same way and it moves without crashing. Here is my code: import pygame, random, os pygame.init() DISPLAY_WIDTH, DISPLAY_HEIGHT = 1600, 900 clock = pygame.time.Clock() gamedisplay = pygame.display.set_mode((DISPLAY_WIDTH, DISPLAY_HEIGHT)) chest_imag = pygame.image.load(os.path.join("catcher.png")) shirt_red = pygame.image.load(os.path.join("shirt_red.png")) background = pygame.image.load(os.path.join("background.png")) shirt_blue = pygame.image.load(os.path.join("shirt_blue.png")) shirt_green = pygame.image.load(os.path.join("shirt_green.png")) shirt = shirt_blue chest_size = chest_imag.get_size() chestPosition = [DISPLAY_WIDTH / 2, DISPLAY_HEIGHT - chest_size[1]] shirt_blue_size = shirt_blue.get_size() shirt_size = shirt_blue_size shirtPosition = [random.randint(0, DISPLAY_WIDTH - shirt_size[0]), 0] dX, dY = 0, 20 score = 0 fallWidth = fallHeight = 100 COLLISION_LEVEL_LINE = DISPLAY_HEIGHT - chest_size[1] onGame = True collide = False # **************** Exception happens here ******************* def resetShirtPosition(): shirtPosition[0] = random.randint(0, DISPLAY_WIDTH - shirt_size[0]) shirtPosition[1] = 0 def hitCollisionLevelLine(): return (shirtPosition[1] + shirt_size[1]) >= COLLISION_LEVEL_LINE def hitGround(): return (shirtPosition[1] + shirt_size[1]) >= DISPLAY_HEIGHT def hitHorizontalEdge(): willTouchLeftEdge = (chest_size[0] + dX) < 0 willTouchRightEdge = (chest_size[0] + dX) > (DISPLAY_WIDTH - chest_size[0]) return willTouchLeftEdge or willTouchRightEdge def collides(): touchFromLeft = shirtPosition[0] > (chestPosition[0] - shirt_size[0]) touchFromRight = shirtPosition[0] < (chestPosition[0] + chest_size[0]) return touchFromLeft and touchFromRight while onGame: clock.tick(60) # action for event in pygame.event.get(): if event.type == pygame.KEYDOWN: if event.key == pygame.K_a: dX = -20 elif event.key == pygame.K_d: dX = 20 elif event.type == pygame.KEYUP: dX = 0 elif event.type == pygame.QUIT: onGame = false # logic if hitCollisionLevelLine(): hasCollided = collides() if hasCollided: collide = False score = score + 1 print score resetShirtPosition() if hitGround(): resetShirtPosition() if hitHorizontalEdge(): dX = 0 # update positions and draw chestPosition = (chestPosition[0] + dX, chestPosition[1]) shirtPosition = (shirtPosition[0], shirtPosition[1] + dY) gamedisplay.blit(background , (0, 0)) gamedisplay.blit(shirt_blue, shirtPosition) gamedisplay.blit(chest_imag, chestPosition) pygame.display.flip() I've posted all my code because I'd also like if you could hand me some tips to improve it, from code writting to improve-performance tricks. For example, I'm thinking about putting my images in a Dictionary and get them by its key (name). Thank you.
On the following line : shirtPosition = (shirtPosition[0], shirtPosition[1] + dY) you have reassigned shirtPosition as a tuple, this is causing your problem. Do convert this into a list object instead and your issue will be resolved.
pygame: Stopping movement behind line
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.