Quick note. This is for my A-Level NEA Programming Project. There are two main sections - One where a maze is generated and the user must navigate through it in a given time period, the time period is not currently implemented, and a second section where the user has to answer educational physics questions in order to get the best score. Questions are imported from a text file stored locally on my system. The user's score is then exported to a local text file along with the date completed.
So far my program generates the maze and the user can move freely. The educational aspect works as intended.
# Imports
import pygame
import sys
import csv
import random
from datetime import date
# Initialising pygame
pygame.init()
# Setting parameters for commonly used colours
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
BLUE = (0, 0, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
# Creating a variable set to 'False' for the generation of the maze
done = False
# Setting variables for the size of the maze, how many columns and rows there will be
cols = 10
rows = 10
# Setting variables for the size of the window and the size of each individual part of the walls
width = 600
height = 600
wr = width/cols
hr = height/rows
# Initialising the 'screen' this is the surface within pygame that everything will be displayed upon
screen = pygame.display.set_mode([width, height])
screen_rect = screen.get_rect()
pygame.display.set_caption("Maze Generator")
# Creating a clock for the pygame module to run off of
clock = pygame.time.Clock()
# This is a class for the pathfinder section of the maze, it will allow the program to spot where needs to be visited
class Spot:
def __init__(self, x, y):
self.x = x
self.y = y
self.f = 0
self.g = 0
self.h = 0
self.neighbors = []
self.visited = False
self.walls = [True, True, True, True]
def show(self, color=BLACK):
if self.walls[0]:
pygame.draw.line(screen, color, [self.x*hr, self.y*wr], [self.x*hr+hr, self.y*wr], 2)
if self.walls[1]:
pygame.draw.line(screen, color, [self.x*hr+hr, self.y*wr], [self.x*hr+hr, self.y*wr + wr], 2)
if self.walls[2]:
pygame.draw.line(screen, color, [self.x*hr+hr, self.y*wr+wr], [self.x*hr, self.y*wr+wr], 2)
if self.walls[3]:
pygame.draw.line(screen, color, [self.x*hr, self.y*wr+wr], [self.x*hr, self.y*wr], 2)
def show_block(self, color):
if self.visited:
pygame.draw.rect(screen, color, [self.x*hr+2, self.y*wr+2, hr-2, wr-2])
def add_neighbors(self):
if self.x > 0:
self.neighbors.append(grid[self.x - 1][self.y])
if self.y > 0:
self.neighbors.append(grid[self.x][self.y - 1])
if self.x < rows - 1:
self.neighbors.append(grid[self.x + 1][self.y])
if self.y < cols - 1:
self.neighbors.append(grid[self.x][self.y + 1])
grid = [[Spot(i, j) for j in range(cols)] for i in range(rows)]
for i in range(rows):
for j in range(cols):
grid[i][j].add_neighbors()
current = grid[0][0]
visited = [current]
completed = False
def breakwalls(a, b):
if a.y == b.y and a.x > b.x:
grid[b.x][b.y].walls[1] = False
grid[a.x][a.y].walls[3] = False
if a.y == b.y and a.x < b.x:
grid[a.x][a.y].walls[1] = False
grid[b.x][b.y].walls[3] = False
if a.x == b.x and a.y < b.y:
grid[b.x][b.y].walls[0] = False
grid[a.x][a.y].walls[2] = False
if a.x == b.x and a.y > b.y:
grid[a.x][a.y].walls[0] = False
grid[b.x][b.y].walls[2] = False
class Player:
def __init__(self, x, y):
self.rect = pygame.Rect(x, y, hr-2, wr-2)
self.x = int(x)
self.y = int(y)
self.colour = (255, 0, 0)
self.velX = 0
self.velY = 0
self.left_pressed = False
self.right_pressed = False
self.up_pressed = False
self.down_pressed = False
self.speed = 5
def draw(self, win):
pygame.draw.rect(win, self.colour, self.rect)
def update(self):
self.velX = 0
self.velY = 0
if self.left_pressed and not self.right_pressed:
self.velX = -self.speed
if self.right_pressed and not self.left_pressed:
self.velX = self.speed
if self.up_pressed and not self.down_pressed:
self.velY = -self.speed
if self.down_pressed and not self.up_pressed:
self.velY = self.speed
self.x += self.velX
self.y += self.velY
self.rect = pygame.Rect(self.x, self.y, hr-2, wr-2)
def readMyFiles():
questionsAndAnswers = []
correctAnswers = []
with open('questions.txt', newline='') as f:
reader = csv.reader(f, delimiter='\t')
for row in reader:
questionsAndAnswers.append(row)
return questionsAndAnswers
def game(questions, answers, correctAnswers):
score = 0
counter = 0
numberOfQuestions = len(questions)
while not counter == numberOfQuestions:
print(questions[counter])
print(answers[counter])
userAnswer = input('\nWhat is the correct answer?\n')
if userAnswer == correctAnswers[counter]:
print('Well done! That is correct.')
score += 1
else:
print('Better luck next time, that is not correct.')
counter += 1
return score
def shuffleSplit(qna):
random.shuffle(qna)
questions = []
answers = []
correctAnswers = []
for q in qna:
questions.append(q[0])
correctAnswers.append(q[1])
del q[0]
random.shuffle(q)
answers.append(q)
return (questions, answers, correctAnswers)
def exportScores(score, ):
with open('scores.txt', mode='a') as scores:
scores = csv.writer(scores, delimiter='\t')
today = date.today()
dateFormat = today.strftime("%d/%m/%Y")
scores.writerow([dateFormat, score])
player = Player(2, 2)
while not done:
clock.tick(60)
screen.fill(BLACK)
if not completed:
grid[current.x][current.y].visited = True
got_new = False
temp = 10
while not got_new and not completed:
r = random.randint(0, len(current.neighbors)-1)
Tempcurrent = current.neighbors[r]
if not Tempcurrent.visited:
visited.append(current)
current = Tempcurrent
got_new = True
if temp == 0:
temp = 10
if len(visited) == 0:
completed = True
break
else:
current = visited.pop()
temp = temp - 1
if not completed:
breakwalls(current, visited[len(visited)-1])
current.visited = True
current.show_block(WHITE)
for i in range(rows):
for j in range(cols):
grid[i][j].show(WHITE)
# grid[i][j].show_block(BLUE)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
questionsAndAnswers = readMyFiles()
questions, answers, correctAnswers = shuffleSplit(questionsAndAnswers)
score = game(questions, answers, correctAnswers)
exportScores(score)
print('\nYour score is', str(score))
sys.exit()
if event.type == pygame.KEYDOWN and completed:
if event.key == pygame.K_LEFT:
player.left_pressed = True
if event.key == pygame.K_RIGHT:
player.right_pressed = True
if event.key == pygame.K_UP:
player.up_pressed = True
if event.key == pygame.K_DOWN:
player.down_pressed = True
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT:
player.left_pressed = False
if event.key == pygame.K_RIGHT:
player.right_pressed = False
if event.key == pygame.K_UP:
player.up_pressed = False
if event.key == pygame.K_DOWN:
player.down_pressed = False
player.rect.clamp_ip(screen_rect)
if player.x <= 2:
player.left_pressed = False
player.x = 2
if player.y <= 2:
player.up_pressed = False
player.y = 2
if player.x >= width-(wr-2):
player.right_pressed = False
player.x = width-(wr-2)
if player.y >= height-(wr-2):
player.down_pressed = False
player.y = height-(wr-2)
player.draw(screen)
player.update()
pygame.display.flip()
Where do I go from here to get to a point so that the walls act as physical barriers rather than just visual barriers? Currently, the maze is generated and the user can move throughout the screen. It always stays on the screen however I am not sure what the best way to implement the walls would be.
Compute the bounding rectangle of the player and compute the grid indices of the corner points and and center point:
player_rect = pygame.Rect(player.x, player.y, wr-3, hr-3)
xC, yC = int(player_rect.centerx / wr), int(player_rect.centery / hr)
x0, y0 = int(player_rect.left / wr), int(player_rect.top / hr)
x1, y1 = int(player_rect.right / wr), int(player_rect.bottom / hr)
Restrict the movement dependent on the direction and walls. For instance:
if player.left_pressed and player_rect.x < xC*wr+2:
if grid[xC][y0].walls[3] or grid[xC][y1].walls[3]:
player.x = xC*wr+2
player.left_pressed = False
Complete collision test:
while not done:
# [...]
player_rect = pygame.Rect(player.x, player.y, wr-3, hr-3)
xC, yC = int(player_rect.centerx / wr), int(player_rect.centery / hr)
x0, y0 = int(player_rect.left / wr), int(player_rect.top / hr)
x1, y1 = int(player_rect.right / wr), int(player_rect.bottom / hr)
if player.left_pressed and player_rect.x < xC*wr+2:
if grid[xC][y0].walls[3] or grid[xC][y1].walls[3]:
player.x = xC*wr+2
player.left_pressed = False
if player.y != yC*hr+2 and grid[x0][y0].walls[2]:
player.x = xC*wr+2
player.left_pressed = False
if player.right_pressed and player_rect.x > xC*wr+2:
if grid[xC][y0].walls[1] or grid[xC][y1].walls[1]:
player.x = xC*wr+2
player.right_pressed = False
if player.y != yC*hr+2 and grid[x0+1][y0].walls[2]:
player.x = xC*wr+2
player.right_pressed = False
if player.up_pressed and player_rect.y < yC*hr+2:
if grid[x0][yC].walls[0] or grid[x1][yC].walls[0]:
player.y = yC*hr+2
player.up_pressed = False
if player.x != xC*wr+2 and grid[x0][y0].walls[3]:
player.y = yC*hr+2
player.up_pressed = False
if player.down_pressed and player_rect.y > yC*hr+2:
if grid[x0][yC].walls[2] or grid[x1][yC].walls[2]:
player.y = yC*hr+2
player.down_pressed = False
if player.x != xC*wr+2 and grid[x0][y0+1].walls[3]:
player.y = yC*hr+2
player.down_pressed = False
See also How do I prevent the player from moving through the walls in a maze?
Related
I am working on creating a donkey kong like game in which I will have Mario try to reach the top platform. To do this, I need Mario to have the ability to jump and fall with gravity.
I have been working on implementing gravity, but when I tried something that I thought would work, the gravity element worked, but the player avatar began to glitch erratically. I believe this is because he is moving up and down several times per second. Any help as to how resolve this issue, and get my gravity mechanism functioning would be greatly appreciated.
Here is my code so far:
from pygame.locals import *
import itertools
global moves
pygame.init()
screen_height = 800
screen_width = 600
screen = pygame.display.set_mode((screen_width, screen_height))
pygame.display.set_caption('Donkey Kong')
FPS = 30
player = pygame.image.load('mario.bmp') #facing right
player_rect = player.get_rect()
player_rect.center = (80, 700)
move_rate = 3
move_left = False
move_right = False
move_up = False
move_down = False
touch_ladder = True
ladder_move_up = False
ladder_move_down = False
gravity = 4
gravity_check = True
jump = False
jump_moves = 0
platform = pygame.image.load('platform.bmp')
platforms = []
platform_x = [60,120,180,240,300,360,420,480]
platform_y = [120,240,360,480,600,720]
ladders = []
ladder_x = [300, 480, 240, 300, 180, 420, 240, 120, 60, 420, 300, 480]
ladder_y = []
class Platform():
def __init__(self, y, x, x_index, y_index):
self.platform = platform
self.rect = self.platform.get_rect()
self.x_index = x_index
self.y_index = y_index
if (self.y_index % 2) != 0 :
self.rect.y = y + (2 * self.x_index)
else:
self.rect.y = y - (2 * self.x_index)
self.rect.x = x
def draw_platform(self):
if (self.rect.y % 240) != 0:
screen.blit(self.platform, (self.rect.x, self.rect.y )) #up
else:
screen.blit(self.platform, (self.rect.x, self.rect.y)) #down
class Ladder():
def __init__(self, y, x, y_index, x_index):
self.y_index = y_index
self.x_index = x_index
if (y % 240) != 0:
self.height = abs(((platform_y[self.y_index - 1] + (2 *(2 * self.x_index)))) - y)
self.rect = pygame.Rect(x, y - self.height, 20, self.height)
self.rect.y -= (2 * self.x_index)
else:
self.height = abs(((platform_y[self.y_index - 1] - (2 * (2 * self.x_index)))) - y)
self.rect = pygame.Rect(x, y - self.height, 20, self.height)
self.rect.y += (2 * self.x_index)
self.rungs = 0
def draw_ladder(self):
#pygame.draw.rect(screen, (255,0,0), self.rect)
pygame.draw.rect(screen, (255, 255, 255), (self.rect.x, self.rect.y , 4, self.height))
pygame.draw.rect(screen, (255, 255, 255), (self.rect.x + 16, self.rect.y, 4, self.height))
def draw_rungs(self):
pygame.draw.line(screen, (255, 255, 255), (self.rect.x, self.rect.y+self.rungs),(self.rect.x + 19, self.rect.y+self.rungs), 4)
self.rungs += 14
y_multiplied = platform_y.copy()
y_multiplied.extend(platform_y)
y_multiplied.sort()
print(y_multiplied)
print(ladder_x)
for y in platform_y:
if platform_y.index(y) < 6:
for x in platform_x:
platforms.append(Platform(y, x, platform_x.index(x), platform_y.index(y)))
for y, x in zip(y_multiplied, ladder_x):
ladders.append(Ladder(y, x, platform_y.index(y), platform_x.index(x)))
while True:
gravity_check = True
screen.fill((105, 105, 255))
screen.blit(player, player_rect)
for p in platforms:
p.draw_platform()
if player_rect.colliderect(p.rect):
player_rect.bottom = p.rect.top
gravity_check = False
for l in ladders:
if player_rect.colliderect(l.rect):
touch_ladder = True
gravity_check = False
if gravity_check == True:
player_rect.y += gravity
for l in ladders:
if l.y_index > 0:
l.draw_ladder()
for i in range(l.height // 14):
l.draw_rungs()
l.rungs = 0
for event in pygame.event.get():
if event.type == QUIT:
sys.exit()
pygame.quit()
if event.type == KEYDOWN:
if event.key == K_LEFT:
player = pygame.transform.flip(player, True, False)
move_left = True
if event.key == K_RIGHT:
move_right = True
player = pygame.image.load('mario.bmp')
if event.key == K_UP:
jump = True
if event.key == K_DOWN:
player_rect.y += move_rate
if event.type == KEYUP:
if event.key == K_LEFT:
move_left = False
if event.key == K_RIGHT:
move_right = False
if move_left == True:
player_rect.x -= move_rate
if move_right == True:
player_rect.x += move_rate
if ladder_move_up == True:
player_rect.y -= move_rate
if jump == True:
gravity_check = True
if jump_moves <= 60:
player_rect.y -= 10
jump_moves += 10
else:
jump_moves = 0
jump = False
pygame.display.update()
pygame.time.Clock().tick(FPS)```
This line causes problems:
if gravity_check == True:
player_rect.y += gravity
If you put print():
if gravity_check == True:
print(player_rect.y)
player_rect.y += gravity
print(player_rect.y)
You'll see that player_rect.y constantly changes between 56 and 60 (for my random image) at the start. So you need to prevent change of the player_rect.y on more than one place simultaneously to avoid this behaviour. Also as I mentioned in the comment try to avoid loading images inside of the loop because it will consume much resources.
EDIT:
for p in platforms:
p.draw_platform()
if player_rect.colliderect(p.rect):
player_rect.bottom = p.rect.top + gravity
gravity_check = False
player_rect.bottom = p.rect.top + gravity adding gravity value will solve the problem. And this is the line of code that was causing changes to player_rect.y as well as one mentioned in the original post. Hope this solves your problem.
I want to make it so when my character touches the "v" block the game quits. What I tried was to find all blocks that are "v" and put them in the list. If player colliderect with any in list of "v" game will quit.
I think this should work although it does not seem to be working. Whenever I run it when I touch the "v" block nothing happens.
Here is my code
import pygame
from pygame.locals import *
pygame.init()
clock = pygame.time.Clock()
WINDOW_SIZE = (600, 400)
pygame.display.set_caption("Game")
screen = pygame.display.set_mode(WINDOW_SIZE, 0, 32)
display = pygame.Surface((300, 200))
player_image = pygame.image.load("Jacques clone-1.png (1).png").convert()
player_image.set_colorkey((255,255,255))
location = [50, 50]
#boolean for movement
moving_right = False
moving_left = False
scroll = [0, 0]
Stay_right = True
game_map1 = """
-----------------------------------------------------
-----------------------------------------------------
-----------------------------------------------------
-----------------------------------------------------
-----------------------------------------------------
xx----------x----------------------------------------
----------vvvv---------------------xxx----------------
---------xooo----------------------------------------
xxxxxxxxxooooxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
ooooooooooooooooooooooooooooooooooooooooooooooooooooo
ooooooooooooooooooooooooooooooooooooooooooooooooooooo
ooooooooooooooooooooooooooooooooooooooooooooooooooooo
""".splitlines()
game_map = [list(lst) for lst in game_map1]
tl = {}
tl["v"] = spike_img = pygame.image.load('dirt.png')
tl["o"] = dirt_img = pygame.image.load('grass.png')
tl["x"] = grass_img = pygame.image.load('grass.png')
player_rect = pygame.Rect(50, 50, 25, 25)
momentum = 0
air_timer = 0
#adding tiles list that are hit for movement
def collision_test(rect, tiles):
hit_list = []
for tile in tiles:
if rect.colliderect(tile):
hit_list.append(tile)
#print(hit_list)
return hit_list
def move(rect, movement, tiles):
collision_types = {'top': False, 'bottom': False, 'right': False, 'left': False}
rect.x += movement[0]
hit_list = collision_test(rect, tiles)
for tile in hit_list:
if movement[0] > 0:
rect.right = tile.left
collision_types['right'] = True
elif movement[0] < 0:
rect.left = tile.right
collision_types['left'] = True
rect.y += movement[1]
hit_list = collision_test(rect, tiles)
for tile in hit_list:
if movement[1] > 0:
rect.bottom = tile.top
collision_types['bottom'] = True
elif movement[1] < 0:
rect.top = tile.bottom
collision_types['top'] = True
return rect, collision_types
run = True
while run:
display.fill((146, 244, 255))
scroll[0] += (player_rect.x - scroll[0] - 130)
tile_rects = []
y = 0
for line_of_symbols in game_map:
x = 0
for symbol in line_of_symbols:
if symbol in tl:
display.blit(tl[symbol], (x * 16 - scroll[0], y * 16 - scroll[1]))
if symbol != "-":
tile_rects.append(pygame.Rect(x * 16, y * 16, 16, 16))
x += 1
y += 1
list_ofspike = []
y2 = 0
for lineofsymbols in game_map:
x2 = 0
for symbols in lineofsymbols:
if symbols == "v":
list_ofspike.append(pygame.Rect(x2 * 16, y2 * 16, 16, 16))
x2 += 1
y2 += 1
for spike in list_ofspike:
if player_rect.colliderect(spike):
pygame.quit()
player_movement = [0, 0]
if moving_right:
player_movement[0] += 2
if moving_left:
player_movement[0] -= 2
player_movement[1] += momentum
momentum += 0.3
if momentum > 3:
momentum = 3
player_rect, collisions = move(player_rect, player_movement, tile_rects)
if collisions['bottom']:
air_timer = 0
momentum = 0
else:
air_timer += 1
if Stay_right:
display.blit(player_image, (player_rect.x - scroll[0], player_rect.y - scroll[1]))
else:
display.blit(pygame.transform.flip(player_image, 1, 0 ),(player_rect.x - scroll[0], player_rect.y - scroll[1]))
for event in pygame.event.get():
if event.type == QUIT:
run = False
if event.type == KEYDOWN:
if event.key == K_RIGHT:
moving_right = True
Stay_right = True
if event.key == K_LEFT:
moving_left = True
Stay_right = False
if event.key == K_SPACE:
if air_timer < 6:
momentum = -5
if event.type == KEYUP:
if event.key == K_RIGHT:
moving_right = False
if event.key == K_LEFT:
moving_left = False
screen.blit(pygame.transform.scale(display, (WINDOW_SIZE)), (0, 0))
pygame.display.update()
clock.tick(60)
pygame.quit()
You do not detect a collision with the spikes, because your collision detection works too perfectly.
When you move the player then you ensure, that the player does not intersect any object, thus the player does not intersect a spike, too.
You have to test if the player touches a spike. Increase the player rectangle by 1 in direction and use the increased rectangle to do the collision test with the spikes:
while run:
# [...]
test_rect = pygame.Rect(player_rect.left-1, player_rect.top-1,
player_rect.width+2, player_rect.height+2)
for spike in list_ofspike:
if test_rect.colliderect(spike):
pygame.quit()
# [...]
Hi I am currently writing a snake game code and I am nearly finished however I am having difficulty writing a code which will cause the game to end if the head of the snake collides with its body, I thought I could create a collide function similar to the collide function for the snake and the apple:
pygame.sprite.collide_rect(h, a)
however all the separate parts of the snake act in the same way so the snake will always be constantly colliding with itself. Are there any ways around this.
here is my full snake code:
import pygame
import random
BLACK = (0, 0, 0)
GREEN = (0, 250, 0)
RED = (250, 0, 0)
Width = 15
Space = 3
Xspeed = 18
Yspeed = 0
Factor = 18
clock = pygame.time.Clock()
segments = 2
HitLoop = 0
ScreenWidth = 800
AppleCount = 1
#creating initial snake
class HEAD(pygame.sprite.Sprite):
def __init__(self, x, y, colour = GREEN):
super().__init__()
self.image = pygame.Surface([Width, Width])
self.image.fill(colour)
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
class APPLE(pygame.sprite.Sprite):
def __init__(self, z, q):
super().__init__()
self.image = pygame.Surface([Width, Width])
self.image.fill(RED)
self.rect = self.image.get_rect()
self.rect.x = z
self.rect.y = q
pygame.init()
screen = pygame.display.set_mode([ScreenWidth, ScreenWidth])
pygame.display.set_caption('Snake')
allspriteslist = pygame.sprite.Group()
SnakeSegments = []
for i in range(segments):
x = 250 - (Width + Space) * i
y = 30
h = HEAD(x, y)
SnakeSegments.append(h)
allspriteslist.add(h)
AppleList = []
for i in range(0,AppleCount):
z = random.randint(10,ScreenWidth-25)
q = random.randint(10,ScreenWidth-25)
a = APPLE(z, q)
AppleList.append(a)
allspriteslist.add(a)
#main loop
done = False
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
if Xspeed == -Factor:
Xspeed = 0
if Xspeed == Factor:
Xspeed = Factor
else:
Xspeed = Xspeed - Factor
Yspeed = 0
elif event.key == pygame.K_RIGHT:
if Xspeed == Factor:
Xspeed = 0
if Xspeed == -Factor:
Xspeed = -Factor
else:
Xspeed = Xspeed + Factor
Yspeed = 0
elif event.key == pygame.K_UP:
if Yspeed == -Factor:
Yspeed = 0
if Yspeed == Factor:
Yspeed = Factor
else:
Yspeed = Yspeed - Factor
Xspeed = 0
elif event.key == pygame.K_DOWN:
if Yspeed == Factor:
Yspeed = 0
if Yspeed == -Factor:
Yspeed = -Factor
else:
Yspeed = Yspeed + Factor
Xspeed = 0
clock.tick(10)
#snake builder
OldSegment = SnakeSegments.pop(-1)
allspriteslist.remove(OldSegment)
x = SnakeSegments[0].rect.x + Xspeed
y = SnakeSegments[0].rect.y + Yspeed
h = HEAD(x, y)
SnakeSegments.insert(0, h)
allspriteslist.add(h,a)
allspriteslist.update()
# collision had to create apples own list for respawn
if pygame.sprite.collide_rect(h, a) == True and HitLoop == 0:
SnakeSegments.append(h)
AppleList.append(a)
HitLoop = HitLoop + 1
z = random.randint(10, ScreenWidth - 25)
q = random.randint(10, ScreenWidth - 25)
OldApple = AppleList.pop()
allspriteslist.remove(OldApple)
a = APPLE(z, q)
allspriteslist.update()
# collision had to create a new class
if pygame.sprite.collide_rect(h, h) == True:
pass
# hit timer
if HitLoop > 0:
HitLoop += 1
if HitLoop > 4:
HitLoop = 0
screen.fill(BLACK)
#game walls
pygame.draw.rect(screen, GREEN, [0, 0, ScreenWidth, 10])
pygame.draw.rect(screen, GREEN, [0, 0, 10, ScreenWidth])
pygame.draw.rect(screen, GREEN, [0, ScreenWidth - 10, ScreenWidth, 10])
pygame.draw.rect(screen, GREEN, [ScreenWidth - 10, 0, 10, ScreenWidth])
if x <= 10:
done = True
if x >= ScreenWidth - Width:
done = True
if y <= 10:
done = True
if y >= ScreenWidth - Width:
done = True
allspriteslist.draw(screen)
pygame.display.flip()
In your code it looks as though you are checking if the head of the snake collide with itself, which will always return True. You need to individually check that the head of snake does not collide with any of it's trailing segments:
for segment in SnakeSegments[2:]:
if pygame.sprite.collide_rect(h, segment):
pass # collision detected, game-over
The head of the snake is expected to collide with the segment directly behind it, which is why we need to start with the 3rd element in the list.
I am making a space invader/dodging game in pygame. I have items that you can touch to e.g. increase health. When the sprite touches the health item, I want the background to be green momentarily.
Below is what I have at the moment. Since I have more than 1 item on the screen at a time, I check for each time using (line 1). You can see that when touching potion, the fillcolor is set to green
The problem is if the sprite is only touching 1 of the 2 items in the screen, the background will be set to black when checking for the second one.
How do I make it so that if at least 1 of the 2 items is being touched, the background becomes green?
EDIT:
I had made the question too vague in fear of making it too long, so here are more details.
fillcolor is the variable that sets the background color of the window. There are three types of items that you can touch, and a maximum of 2 items can appear at once, regardless of the type of item.
This might be a bit long, but what is going wrong is:
By using the for loop, I am checking the 2 items on the screen, and checking if the item you hit is a potion, ammunition or "fever mode"(powerup item). As you can see if it is "potion", your health is increased and if it is "ammo" your ammo count is being increased. For example if there are two items on the screen and you are touching one of them, which is a potion. Then the background becomes green, however in the next round of the for loop when checking the second item, fillcolor instantly becomes black because you are not touching both of the items, only one of them. What is want to do is to make the background green if you are touching one of them, even if the second one is not touched.
for e in items:
ship.checkItemCollision(e, ship)
if ship.checkItemCollision(e, ship) == 'potion':
print('potion')
ship.health += 0.5
fillcolor = (0, 255, 0)
touchDatItem = True
elif ship.checkItemCollision(e, ship) == 'ammo':
print('ammo')
ammoCount += 1
fillcolor = (255, 255, 0)
touchDatItem = True
elif ship.checkItemCollision(e, ship) == 'fever':
print('fever')
feverMode = True
fillcolor = (255, 0, 0)
touchDatItem = True
elif not touchDatItem:
fillcolor = black
Here's the whole code:
import pygame as pg
import time
import random
import math
pg.init()
display_width = 800
display_height = 600
black = (0, 0, 0)
white = (255, 255, 255)
red = (200, 0, 0)
bllue = (0, 0, 255)
green = (0, 200, 0)
bright_red =(255, 0, 20)
bright_green = (0, 255, 0)
yellow = (255,255,0)
dark_yellow = (150, 150, 0)
clock = pg.time.Clock()
potion = pg.image.load('revive.png')
ammo = pg.image.load('ammo.png')
fever = pg.image.load('fever.png')
gameDisplay = pg.display.set_mode((display_width, display_height))
pg.display.set_caption('Object Oriented')
class Item:
def __init__(self):
self.items = [potion, potion, potion, ammo, ammo,ammo, ammo, ammo, fever, fever, fever, fever, fever, fever, fever, fever, fever, fever, fever, fever, fever, ]
self.images = potion
self.speed = 3
self.width = 30
self.height = 30
self.x = 30
self.y = random.randrange(-1000, -300)
def move(self):
self.y += self.speed
if self.y > display_height:
self.x = random.randrange(0, (display_width - self.width))
self.y = random.randrange(-5000, -1000)
self.images = random.choice(self.items)
def draw(self):
gameDisplay.blit(self.images, (self.x, self.y))
class Thing:
def __init__(self):
self.width = 20
self.height = 20
self.x = random.randrange(0, (display_width - self.width))
self.y = random.randrange(-500, 0)
self.speedY = 3
self.speedX = 3
self.color = bright_red
self.ratio = random.randrange(-3, 3)
def move(self, count):
if self.ratio == 0:
self.y += self.speedY
else:
self.y += self.speedY
## self.x += random.randint(-5, 5)
self.x += self.ratio
if self.y > display_height:
self.x = random.randrange(0, (display_width - self.width))
self.y = random.randrange(-500, 0)
self.ratio = random.randrange(-3, 3)
return True
def draw(self):
pg.draw.rect(gameDisplay, self.color, [self.x, self.y, self.height, self.width])
## def randomizeX(self):
## self.x = random.randrange(0, (display_width - self.width))
## def resetY(self):
## self.y = 05
##def checkQuit():
## for event in pg.event.get():
## if event.type == pg.QUIT:
## pg.quit()
## quit()
class Ship:
def __init__(self):
self.x = display_width / 2
self.y = display_height / 2
self.speed = 10
self.height = 20
self.width = 20
self.color = yellow
self.changeX = 0
self.changeY = 0
self.health = 100
## def move(self, event):
##
## if event.type == pg.KEYDOWN:
## print(event.key)
## if event.key == pg.K_LEFT:
## self.change = -(self.speed)
## if event.key == pg.K_RIGHT:
## self.change = self.speed
## if event.type == pg.KEYUP:
## if event.key == pg.K_LEFT or event.key == pg.K_RIGHT:
## self.change = 0
## self.x += self.change
def draw(self):
pg.draw.rect(gameDisplay, self.color, [self.x, self.y, self.height, self.width])
def moveShip(self, event):
if event.type == pg.KEYDOWN:
## print(self.changeY)
## print(self.changeX)
if event.key == pg.K_LEFT:
self.changeX = -(self.speed)
if event.key == pg.K_RIGHT:
self.changeX = self.speed
if event.key == pg.K_UP:
self.changeY = -(self.speed)
if event.key == pg.K_DOWN:
self.changeY = self.speed
if event.type == pg.KEYUP:
if event.key == pg.K_LEFT or event.key == pg.K_RIGHT or event.key == pg.K_UP or event.key == pg.K_DOWN:
self.changeX = 0
self.changeY = 0
def testWallCollision(self):
if self.x > (display_width - self.width) or self.x < 0:
self.health = self.health/2
def checkThingCollision(self, t, ship, fillcolor, red, count):
# if thing_starty < (y + car_height) and y < (thing_starty+thing_height):
if (t.y - (t.height/2)) < (ship.y + ship.height) and ship.y < ((t.y - (t.height/2)) + t.height):
if (self.x > t.x and self.x < (t.x + t.width) or ((self.x + t.width) > t.x and (self.x + t.width) < t.x + t.width)):
self.health -= 0.5
t.x = random.randrange(0, (display_width - t.width))
t.y = random.randrange(-500, 0)
t.ratio = random.randrange(-10, 10)
def checkItemCollision(self, e, ship):
if e.y < (ship.y + ship.height) and ship.y < (e.y + e.height):
if (self.x > e.x and self.x < (e.x + e.width) or ((self.x + e.width) > e.x and (self.x + e.width) < e.x + e.width)):
if e.images == potion:
return 'potion'
elif e.images == ammo:
return 'ammo'
elif e.images == fever:
return 'fever'
class Bullet:
def __init__(self, ship):
self.speed = 20
self.color = white
self.x = ship.x + (ship.width / 2)
self.y = ship.y + (ship.width / 2)
self.height = 5
self.width = 5
def draw(self):
## print('IN DRAAAAAW')
## if event.key == pg.K_SPACE:
pg.draw.rect(gameDisplay, self.color, [self.x, self.y, self.height, self.width])
def move(self, ship):
self.y -= self.speed
## if self.y < 0:
## self.x = ship.x + (ship.width / 2)
## self.y = ship.y + (ship.width / 2)
def checkCollision(self, t, ship, count):
if t.y < (self.y + self.height) and self.y < (t.y + t.height):
if (self.x > t.x and self.x < (t.x + t.width) or ((self.x + t.width) > t.x and (self.x + t.width) < t.x + t.width)):
t.x = random.randrange(0, (display_width - t.width))
t.y = random.randrange(-500, 0)
t.ratio = random.randrange(-10, 10)
self.y = -self.height
return True
def healthNum(health, color):
font = pg.font.SysFont(None, 25)
text = font.render('health:' + str(health) + '/100', True, color)
gameDisplay.blit(text, (500, 0))
def ammoNum(ammoCount, color):
font = pg.font.SysFont(None, 25)
text = font.render('ammo:' + str(ammoCount), True, color)
gameDisplay.blit(text, (300, 0))
def things_dodged(count):
font = pg.font.SysFont(None, 25)
text = font.render('score: ' + str(count), True, white)
gameDisplay.blit(text, (0, 0))
def main_loop():
touchDatItem = False
feverTimer = 0
gameExit = False
allItems = [potion, ammo]
things = []
ship = Ship()
bullets = []
fillcolor = black
count = 0
items = []
ammoCount = 20
FEVER = False
LIST = []
feverMode = False
for t in range (30):
things.append(Thing())
for e in range(2):
items.append(Item())
while not gameExit:
print(fillcolor)
for event in pg.event.get():
if event.type == pg.QUIT:
pg.quit()
quit()
ship.moveShip(event)
if event.type == pg.KEYDOWN:
if event.key == pg.K_SPACE:
## FEVER = True
if ammoCount > 0:
bullets.append(Bullet(ship))
if not feverMode:
ammoCount -= 1
if feverMode:
FEVER = True
else:
FEVER = False
if event.type == pg.KEYUP:
if event.key == pg.K_SPACE:
FEVER = False
if FEVER == True:
feverTimer += 1
if not feverTimer > 100:
if ammoCount > 0:
bullets.append(Bullet(ship))
else:
print('STAAAAAAAAAP')
FEVER = False
feverTimer = 0
feverMode = False
ship.x += ship.changeX
ship.y += ship.changeY
ship.testWallCollision()
gameDisplay.fill(fillcolor)
LIST = []
healthNum(ship.health, white)
ammoNum(ammoCount, white)
for t in things:
ship.checkThingCollision(t, ship, fillcolor, red, count)
if ship.checkThingCollision(t, ship, fillcolor, red, count) == True:
print('###########################')
ship.color = red
else:
ship.color = yellow
t.draw()
ship.draw()
t.move(count)
if t.move(count) == True:
count+= 1
for b in bullets:
b.draw()
for t in things:
b.checkCollision(t, ship, count)
if b.checkCollision(t, ship, count) == True:
count += 10
b.move(ship)
for e in items:
ship.checkItemCollision(e, ship)
if ship.checkItemCollision(e, ship) == 'potion':
LIST.append('potion')
print('potion')
ship.health += 0.5
touchDatItem = True
elif ship.checkItemCollision(e, ship) == 'ammo':
LIST.append('ammo')
print('ammo')
ammoCount += 1
touchDatItem = True
elif ship.checkItemCollision(e, ship) == 'fever':
LIST.append('fever')
print('fever')
feverMode = True
touchDatItem = True
if 'potion' in LIST:
fillcolor = (0, 255, 0)
elif 'ammo' in LIST:
fillcolor = (255, 255, 0)
elif 'fever' in LIST:
fillcolor = (255, 0, 0)
else:
fillcolor = black
e.draw()
e.move()
print('fillcolor = ' + str(fillcolor))
if ship.health < 1:
ship.health = 0
pg.quit()
quit()
things_dodged(count)
pg.display.update()
clock.tick(60)
pg.quit()
quit()
main_loop()
That happens when you invent your own collision detection function instead of using pygame's collision detection methods. ;) Your checkItemCollision method is incorrect.
Change this line ...
if (self.x > e.x and self.x < (e.x + e.width) or ((self.x + e.width) > e.x and (self.x + e.width) < e.x + e.width)):
to this:
if (self.x > e.x and self.x < e.x + e.width or self.x + self.width > e.x and self.x + self.width < e.x + e.width):
I'm still not 100% sure if everything is correct now. I'd give your objects a self.rect attribute and use it for the collision detection instead, e.g.:
# In __init__:
self.rect = pg.Rect(self.x, self.y, self.width, self.height)
# Then check if they collide with the `colliderect` method.
self.rect.colliderect(e.rect)
The rect needs to be moved as well when you update the positions.
i am currentely making my final project for my exam and i'm making a platform game. Actually i'm made the collisions and it works. But the actual problem is that if i jump on a platform and i keep my player static for 1 second the player disappears which is weird. Could you help me to solve this issue? I have a hint that this issue is related to the velocity (calles vitesse_x and vitesse_y).
import pygame
from pygame.locals import *
pygame.init()
fenetre = pygame.display.set_mode((1024,768))
pygame.display.set_caption("Portal Escape")
class Perso(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.Surface((50,50))
self.rect = self.image.get_rect(bottomleft=(0,740))
self.image.fill((255, 0, 0))
self.doit_monter = False
self.vitesse_x = 0
self.vitesse_y = 0
def update(self):
self.rect.x += self.vitesse_x
self.rect.y += self.vitesse_y
touche = pygame.key.get_pressed()
if touche[pygame.K_RIGHT] and self.rect.x < 920:
self.vitesse_x += 1
if touche[pygame.K_LEFT] and self.rect.x > 0:
self.vitesse_x -= 1
collision = pygame.sprite.spritecollide(self, blocs, False)
for bloc in collision:
if self.vitesse_y >= 0:
self.rect.bottom = bloc.rect.top
elif self.vitesse_y < 0:
self.rect.top = bloc.rect.bottom
perso = Perso()
class Bloc(pygame.sprite.Sprite): #pour creer un obstacle et éléments du jeu
def __init__(self, x, y, w, h):
super().__init__()
self.image = pygame.Surface((w,h))
self.image.fill((0, 255, 0))
self.rect = self.image.get_rect(topleft=(x, y))
all_sprites = pygame.sprite.Group()
blocs = pygame.sprite.Group()
all_sprites.add(perso)
b1 = Bloc(200,500,250,50)
b2 = Bloc(500,600,200,50)
b3 = Bloc(0,740,1024,30)
blocs.add(b1,b2,b3)
all_sprites.add(b1,b2,b3)
new_y = 0
continuer = True
while continuer:
pygame.time.Clock().tick(60)
for event in pygame.event.get():
if event.type == QUIT:
continuer = False
if event.type == KEYDOWN:
if event.key == pygame.K_SPACE:
perso.doit_monter = True
new_y = perso.rect.y - 100
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT and perso.vitesse_x < 0:
perso.vitesse_x = 0
perso.vitesse_y = 0
if event.key == pygame.K_RIGHT and perso.vitesse_x > 0:
perso.vitesse_x = 0
perso.vitesse_y = 0
if event.key == pygame.K_SPACE and perso.vitesse_y < 0:
perso.vitesse_y = 0
if perso.doit_monter == True:
if perso.rect.y > new_y and perso.rect.y > 0:
perso.vitesse_y -= 5
else:
perso.doit_monter = False
else:
perso.vitesse_y += 1
all_sprites.update()
fenetre.fill((0,0,0))
all_sprites.draw(fenetre)
pygame.display.flip()
pygame.quit()
Ok simple, problem, simple solution. You have to reset the y velocity if you are on the ground.
In Perso.update:
for bloc in collision:
if self.vitesse_y >= 0:
self.vitesse_y = 0 # reset velocity, so it isn't increasing forever
self.rect.bottom = bloc.rect.top
elif self.vitesse_y < 0:
self.rect.top = bloc.rect.bottom
If you don't do that, the velocity will increase, till it is high enough to jump throug the floor in one go without colliding with stopping box