I'm learning pygame and I have an issue with detection of rect's collisions. I'm using colliderect() function now but it works only when rects overlap, and the question is "How to detect even edges collisions?". General comments about the whole are welcome. First post btw.
Here is my code:
import pygame
#####SETTINGS#####
HEIGHT = 1080
WIDTH = 1920
BLOCK_SIZE = 60
##################
class Level():
def __init__(self, file):
self.file = file
self.blocks = []
self.map = self.load_from_file()
def load_from_file(self):
map = []
file = open(self.file + '.txt', 'r')
data = file.read()
file.close()
data = data.split('\n')
for x in data:
map.append(list(x))
return map
def render(self, screen):
self.blocks = []
y = 0
for row in self.map:
x = 0
for block in row:
if block != '0':
self.blocks.append(pygame.Rect(x * BLOCK_SIZE, y * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE))
if block == '1':
pygame.draw.rect(screen, (56,24,0), (x * BLOCK_SIZE, y * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE))
elif block == '2':
pygame.draw.rect(screen, (18,115,81), (x * BLOCK_SIZE, y * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE))
x += 1
y += 1
class Player():
def __init__(self, x, y, color):
self.x = x
self.y = y
self.color = color
self.rect = pygame.Rect(x, y, BLOCK_SIZE, BLOCK_SIZE)
self.go_left = False
self.go_right = False
self.go_up = False
self.go_down = False
self.collisions = {'left' : False, 'right' : False, 'top' : False, 'bottom' : False}
def move(self):
self.collisions = test_collisions(self.rect, level.blocks)
if self.go_left and not self.collisions['left']:
self.x -= 10
self.go_left = False
if self.go_right and not self.collisions['right']:
self.x += 10
self.go_right = False
if self.go_up and not self.collisions['top']:
self.y -= 10
self.go_up = False
if self.go_down and not self.collisions['bottom']:
self.y += 10
self.go_down = False
self.rect = pygame.Rect(self.x, self.y, BLOCK_SIZE, BLOCK_SIZE)
def render(self, screen):
pygame.draw.rect(screen, self.color, self.rect)
def render(screen, player, level):
screen.fill((49, 113, 181))
level.render(screen)
player.render(screen)
pygame.display.update()
def handle_events(player):
for event in pygame.event.get():
if event.type == pygame.QUIT or event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
run = False
pygame.quit()
keys = pygame.key.get_pressed()
if keys[pygame.K_a]:
player.go_left = True
if keys[pygame.K_d]:
player.go_right = True
if keys[pygame.K_w]:
player.go_up = True
if keys[pygame.K_s]:
player.go_down = True
def test_collisions(object, rects):
collisions = {'left' : False, 'right' : False, 'top' : False, 'bottom' : False}
for rect in rects:
if object.colliderect(rect):
if object.x <= rect.x:
collisions['right'] = True
if object.x >= rect.x:
collisions['left'] = True
if object.y >= rect.y:
collisions['top'] = True
if object.y <= rect.y:
collisions['bottom'] = True
return(collisions)
def main_loop():
clock = pygame.time.Clock()
while run:
clock.tick(60)
handle_events(player)
player.move()
render(screen, player, level)
if __name__ == "__main__":
run = True
screen = pygame.display.set_mode((WIDTH, HEIGHT))
level = Level('assets/level_one')
player = Player(0,0,(255,255,0))
main_loop()
And here is level_one.txt file content:
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000002222222222200000
00000000000000000000000000000000
00000000000000000000000000000000
00022222222200000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
22222222222222222222222222222222
11111111111111111111111111111111
11111111111111111111111111111111
11111111111111111111111111111111
11111111111111111111111111111111
11111111111111111111111111111111
You can enlarge the rectangles for the collision detection with [pygame.Rect.inflate`](builtins.TypeError: argument 1 must be pygame.Surface, not function). e.g.:
if object.colliderect(rect.inflate(1, 1)):
# [...]
Related
My code:
import pgzrun, pgzero, pygame, math, time, random, os
from pgzero.builtins import Actor, animate, keyboard
screen = pgzero.screen.Screen
# screen
WIDTH = 800
HEIGHT = 600
pygame.init()
pygame.mouse.set_visible(False)
os.chdir("c:/Users/carter.breshears/Documents/CSPVSC/Final/Images")
font = pygame.font.Font("Minecraft.ttf", 30)
# player images
playerIdle = pygame.image.load("playerIdle.png")
playerWalkImages = [pygame.image.load("playerRun1.png"), pygame.image.load("playerRun2.png"), pygame.image.load("playerRun3.png"), pygame.image.load("playerRun4.png")]
#variables
# gameloop
gameover = False
# classes
class Player:
def __init__(self, x, y):
self.x = x
self.y = y
self.animationCount = 0
self.movingRight = False
self.movingLeft = False
self.movingUp = False
self.movingDown = False
def main(self, screen):
if self.animationCount + 1 >= 24:
self.animationCount = 0
self.animationCount += 1
if self.movingRight or self.movingUp or self.movingDown:
screen.blit(pygame.transform.scale(playerWalkImages[self.animationCount//6], (40,74)), (self.x, self.y))
elif self.movingLeft:
screen.blit(pygame.transform.scale(pygame.transform.flip(playerWalkImages[self.animationCount//6], True, False), (40,74)), (self.x, self.y))
else:
screen.blit(pygame.transform.scale(playerIdle, (40,74)), (self.x, self.y)) ##### This line!
self.movingRight = False
self.movingLeft = False
self.movingUp = False
self.movingDown = False
class PlayerBullet:
def __init__(self, x, y, mouseX, mouseY):
self.x = x
self.y = y
self.mouseX = mouseX
self.mouseY = mouseY
speed = 6
self.angle = math.atan2(y - mouseY, x - mouseX)
self.x_vel = math.cos(self.angle) * speed
self.y_vel = math.sin(self.angle) * speed
def main(self, screen):
self.x -= int(self.x_vel)
self.y -= int(self.y_vel)
print("bang")
pygame.draw.circle(screen, "yellow", (self.x+16, self.y+16), 5)
class InvaderEnemy:
def __init__(self, x, y):
self.x = x
self.y = y
self.greenAnimationImages = [pygame.image.load("virus g.png"), pygame.image.load("virus g2.png")]
self.yellowAnimationImages = [pygame.image.load("virus y.png"), pygame.image.load("virus y2.png")]
self.blueAnimationImages = [pygame.image.load("virus b.png"), pygame.image.load("virus b2.png")]
self.redAnimationImages = [pygame.image.load("virus r.png"), pygame.image.load("virus r2.png")]
self.animationCount = 0
self.velocity = 1
self.lerpFactor = 0.05
def main(self, screen):
if self.animationCount + 1 == 8:
self.animationCount = 0
self.animationCount += 1
spawnPos = (random.randint(-1000, 1000), random.randint(-1000, 1000))
targetVector = pygame.math.Vector2(player.x, player.y)
enemyVector = pygame.math.Vector2(spawnPos.x, spawnPos.y)
newEnemyVector = pygame.math.Vector2(spawnPos.x, spawnPos.y)
distance = enemyVector.distance_to(targetVector)
minDistance = 25
maxDistance = 1000
if distance > minDistance:
directionVector = (targetVector - enemyVector)/2
self.minStep = max(0, distance - maxDistance)
self.maxStep = distance - minDistance
self.stepDistance = self.minStep + (self.maxStep - self.minStep) * self.lerpFactor
newEnemyVector = enemyVector + directionVector * self.stepDistance
return(newEnemyVector.x, newEnemyVector.y)
# player
player = Player(400,300)
displayScroll = [0,0]
# shooting
playerBullets = []
# invader
invaders = []
invaderEnemy = InvaderEnemy(100,100)
invaderSpawnRate = 1
# functions
def update():
# movement
keys = pygame.key.get_pressed()
if keys[pygame.K_a]:
displayScroll[0] -= 2
player.movingLeft = True
for bullet in playerBullets:
bullet.x += 2
if keys[pygame.K_d]:
displayScroll[0] += 2
player.movingRight = True
for bullet in playerBullets:
bullet.x -= 2
if keys[pygame.K_w]:
displayScroll[1] -= 2
player.movingUp = True
for bullet in playerBullets:
bullet.y += 2
if keys[pygame.K_s]:
displayScroll[1] += 2
player.movingDown = True
for bullet in playerBullets:
bullet.y -= 2
player.main(screen)
# shooting
mouseX, mouseY = pygame.mouse.get_pos()
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1:
playerBullets.append(PlayerBullet(player.x, player.y, mouseX, mouseY))
for bullet in playerBullets:
PlayerBullet.main(screen)
#enemies
global spawnPos
if time.perf_counter() - invaderSpawnRate > 1:
invaders.append(InvaderEnemy(spawnPos.x, spawnPos.y))
for invader in invaders:
InvaderEnemy.main(screen)
# run
pgzrun.go()
I've tried everything I know of, Im new to this. I know that it worked before because I tested this on my home computer but whenever I downloaded the file on my school computer it stopped working so something must have changed between python versions.
I am learning pygame. I wanted to make the camera follows the player so that the player is in the middle of the screen all the time. Even though I move the camera about the same vector, the player moves much faster and runs off the screen. Thanks in advance for any attempts to help. I am posting the whole code, but the problem probably lies in one of the functions: player.move(), level.move_camera() or level.render().
import pygame
from pygame.math import Vector2
#####SETTINGS#####
HEIGHT = 1080
WIDTH = 1920
TICKRATE = 60
BLOCK_SIZE = 60
GRAVITY = 18
##################
class Level():
def __init__(self, file):
self.file = file
self.blocks = [] # list of tills
self.map = self.load_from_file()
self.camera_position = Vector2(WIDTH / 2, 0)
# loading map
def load_from_file(self):
map = []
file = open(self.file + '.txt', 'r')
data = file.read()
file.close()
data = data.split('\n')
for x in data:
map.append(list(x))
return map
def move_camera(self, player):
self.camera_position.x += player.shift.x
def render(self, screen):
self.blocks = []
y = 0
for row in self.map:
x = 0
for block in row:
if block != '0':
self.blocks.append(pygame.Rect(x * BLOCK_SIZE - self.camera_position.x, y * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE))
if block == '1':
pygame.draw.rect(screen, (56,24,0), (x * BLOCK_SIZE - self.camera_position.x, y * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE))
elif block == '2':
pygame.draw.rect(screen, (18,115,81), (x * BLOCK_SIZE - self.camera_position.x, y * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE))
x += 1
y += 1
class Player():
def __init__(self, x, y, color):
self.position = Vector2(x, y)
self.shift = Vector2(0, 0)
self.jump = Vector2(0, 0)
self.color = color
self.rect = pygame.Rect(x, y, BLOCK_SIZE, BLOCK_SIZE)
self.go_left = False
self.go_right = False
self.go_up = False
self.collisions = {'left' : False, 'right' : False, 'top' : False, 'bottom' : False}
# moving the player object
def move(self, level):
self.shift = Vector2(0, GRAVITY)
self.position.y = int(self.position.y)
# left / right section
if self.go_left:
self.go_left = False
self.collisions = test_collisions(pygame.Rect(self.position.x - 1, self.position.y, BLOCK_SIZE, BLOCK_SIZE), level.blocks)
if not self.collisions['left']:
self.shift += Vector2(-10, 0)
if self.go_right:
self.go_right = False
self.collisions = test_collisions(pygame.Rect(self.position.x + 1, self.position.y, BLOCK_SIZE, BLOCK_SIZE), level.blocks)
if not self.collisions['right']:
self.shift += Vector2(10, 0)
# gravity section
self.collisions = test_collisions(pygame.Rect(self.position.x, self.position.y + GRAVITY, BLOCK_SIZE, BLOCK_SIZE), level.blocks)
if self.collisions['bottom']:
self.shift -= Vector2(0, GRAVITY)
if self.position.y % BLOCK_SIZE > 0:
self.position.y += BLOCK_SIZE - (self.position.y % BLOCK_SIZE)
# jump section
if self.go_up:
self.go_up = False
self.collisions = test_collisions(pygame.Rect(self.position.x, self.position.y + GRAVITY, BLOCK_SIZE, BLOCK_SIZE), level.blocks)
if self.collisions['bottom']:
self.jump = Vector2(0, -80)
self.collisions = test_collisions(pygame.Rect(self.position.x, self.position.y - 1, BLOCK_SIZE, BLOCK_SIZE), level.blocks)
if self.jump.y > GRAVITY or self.collisions['top']:
self.jump = Vector2(0, 0)
else:
self.jump *= 0.9
self.shift += self.jump
# new position
self.position += self.shift
self.rect = pygame.Rect(self.position.x, self.position.y, BLOCK_SIZE, BLOCK_SIZE)
# checking if player is death
def check_death(self):
if self.position.y > HEIGHT:
new_game()
# render player
def render(self, screen):
pygame.draw.rect(screen, self.color, self.rect)
# render section
def render(screen, player, level):
screen.fill((49, 113, 181))
level.render(screen)
player.render(screen)
pygame.display.update()
# keyboard handling
def handle_events(player):
for event in pygame.event.get():
if event.type == pygame.QUIT or event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
run = False
pygame.quit()
keys = pygame.key.get_pressed()
if keys[pygame.K_a] or keys[pygame.K_LEFT]:
player.go_left = True
if keys[pygame.K_d] or keys[pygame.K_RIGHT]:
player.go_right = True
if keys[pygame.K_w] or keys[pygame.K_UP] or keys[pygame.K_SPACE]:
player.go_up = True
# test direction of collison (rects have to overlap)
def test_collisions(object, rects):
collisions = {'left' : False, 'right' : False, 'top' : False, 'bottom' : False}
for rect in rects:
if object.colliderect(rect):
if object.x <= rect.x:
collisions['right'] = True
if object.x >= rect.x:
collisions['left'] = True
if object.y >= rect.y:
collisions['top'] = True
if object.y <= rect.y:
collisions['bottom'] = True
return(collisions)
# game loop
def main_loop(run, screen, level, player):
clock = pygame.time.Clock()
while run:
clock.tick(TICKRATE)
handle_events(player)
player.move(level)
player.check_death()
level.move_camera(player)
render(screen, player, level)
# init new game
def new_game():
run = True
screen = pygame.display.set_mode((WIDTH, HEIGHT))
level = Level('assets/level_two')
level.camera_position = Vector2(0, 0)
player = Player(WIDTH / 2, 0, (255,255,0))
main_loop(run, screen, level, player)
# launch new game on start
if __name__ == "__main__":
new_game()
You are not taking into account the players movement on itself, you only apply it to the blocks. For it to always be in the centre, create a direction vector towards the centre from the player and add that to the player position like this:
class Player():
#...
def move(self, level):
#...
#either this
self.position.x += ((WIDTH / 2) - self.position.x)
#or if you want a camera following with a delay effect
delay = 5
self.position.x += ((WIDTH / 2) - self.position.x) / delay
I would like to create a game in python but I need a thought provoking impulse, how I can draw a point, which draws a line behind him/a trail, so far, so good, I have no idea how to make my point not just moving in the 4 directions, I want him to move forward on his own and the user should steer left and right.
Missing is:
• The trail of my point (later I have to check, if another sprite touches it)
• The "curvy" moving
My current code:
import pygame
import os
pygame.init()
width, height = 970, 970
screen = pygame.display.set_mode((width, height))
h_center = ((height / 2) - 4)
w_center = ((width / 2) - 4)
class Point(pygame.sprite.Sprite):
def __init__(self):
self.image = pygame.image.load(os.path.join("assets", "point.png"))
self.x = (width / 2)
self.y = (height / 2)
self.speed = 5
self.direction = 3 # 1:north ; 2:east ; 3:south ; 4:west
def handle_keys(self):
key = pygame.key.get_pressed()
dist = 1
if key[pygame.K_DOWN]:
self.direction = 3
elif key[pygame.K_UP]:
self.direction = 1
if key[pygame.K_RIGHT]:
self.direction = 2
elif key[pygame.K_LEFT]:
self.direction = 4
def move(self):
if self.direction == 1:
self.y -= self.speed
if self.direction == 2:
self.x += self.speed
if self.direction == 3:
self.y += self.speed
if self.direction == 4:
self.x -= self.speed
def draw(self, surface):
surface.blit(self.image, (self.x, self.y))
def main():
point = Point()
clock = pygame.time.Clock()
background = pygame.image.load('backgroundborder.png').convert()
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
running = False
point.handle_keys()
point.move()
screen.fill((0, 0, 0))
screen.blit(background, (0, 0))
point.draw(screen)
pygame.display.update()
clock.tick(40)
if __name__ == '__main__':
main()
Please assist
Two things:
Use a list to store the trail. It's just a list of previous positions.
Use the arrow keys to adjust speed, not direction
I also added a few lines of code so the dot stays on the screen. The dot.png is just a black dot 20x20 pixels.
Here's the updated code:
import pygame
import os
pygame.init()
width, height = 970, 970
screen = pygame.display.set_mode((width, height))
h_center = ((height/2) - 4)
w_center = ((width/2) - 4)
trail=[None]*50 # trail has 50 dots
trailimage = pygame.image.load('dot.png')
class Point(pygame.sprite.Sprite):
def __init__(self):
self.image = pygame.image.load('dot.png')
self.x = (width/2)
self.y = (height/2)
self.speed = {'x':0, 'y':0}
self.direction = 3 # 1north ; 2east ; 3south ; 4west
def handle_keys(self):
key = pygame.key.get_pressed()
dist = 1
if key[pygame.K_DOWN]:
self.speed['y']+=0.25
elif key[pygame.K_UP]:
self.speed['y']-=0.25
if key[pygame.K_RIGHT]:
self.speed['x']+=0.25
elif key[pygame.K_LEFT]:
self.speed['x']-=0.25
def move(self):
self.y += self.speed['y']
self.x += self.speed['x']
# wrap to other side of screen
if self.x > width: self.x = (self.x - width)
elif self.x < 0: self.x = (width + self.x)
if self.y > height: self.y = (self.y - height)
elif self.y < 0: self.y = (height + self.y)
def draw(self, surface):
surface.blit(self.image, (self.x, self.y))
def main():
point = Point()
clock = pygame.time.Clock()
#background = pygame.image.load('backgroundborder.png').convert()
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
running = False
point.handle_keys()
point.move()
screen.fill((200, 200, 200))
#screen.blit(background, (0, 0))
for d in trail:
if d: screen.blit(trailimage, d)
del trail[0] # remove last point in trail
trail.append((point.x,point.y)) # append this position
point.draw(screen)
pygame.display.update()
clock.tick(40)
if __name__ == '__main__':
main()
To make the controls more like CurveFever, I updated the code so the left \ right keys adjust the travel direction (in degrees). The speed is constant.
Here is the updated code:
import pygame
import os
import math
pygame.init()
width, height = 970, 970
screen = pygame.display.set_mode((width, height))
h_center = ((height/2) - 4)
w_center = ((width/2) - 4)
trail=[None]*50 # trail has 50 dots
trailimage = pygame.image.load('dot.png')
speed = 8 # constant
class Point(pygame.sprite.Sprite):
def __init__(self):
self.image = pygame.image.load('dot.png')
self.x = (width/2)
self.y = (height/2)
self.speed = {'x':0, 'y':0}
self.deg = -90 # up, direction in degrees
def handle_keys(self):
key = pygame.key.get_pressed()
dist = 1
if key[pygame.K_RIGHT]:
self.deg+=2
elif key[pygame.K_LEFT]:
self.deg-=2
self.speed['x'] = speed*math.cos(math.radians(self.deg))
self.speed['y'] = speed*math.sin(math.radians(self.deg))
def move(self):
self.y += self.speed['y']
self.x += self.speed['x']
# wrap to other side of screen
if self.x > width: self.x = (self.x - width)
elif self.x < 0: self.x = (width + self.x)
if self.y > height: self.y = (self.y - height)
elif self.y < 0: self.y = (height + self.y)
def draw(self, surface):
surface.blit(self.image, (self.x, self.y))
TrailTrim = False # set True for constant trail length
def main():
point = Point()
clock = pygame.time.Clock()
#background = pygame.image.load('backgroundborder.png').convert()
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
running = False
point.handle_keys()
point.move()
screen.fill((200, 200, 200)) # clear screen
#screen.blit(background, (0, 0))
for d in trail:
if d: screen.blit(trailimage, d)
if (TrailTrim): del trail[0] # delete trail end
trail.append((point.x,point.y)) # add current postiion
point.draw(screen) # draw current point
pygame.display.update()
clock.tick(40) # 40 FPS
if __name__ == '__main__':
main()
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'm writing a basic Snake game in Pygame, but the body of the snake is not extending after its eaten 4 pieces of food, nor does it extend after the first bite. I'm creating a new body object after each bite, and storing it in a list. x and y variables are used to store the position of the head prior to its movement, which in turn I use as the coordinates for the first segment of the snake's body. I'm then trying to iterate over this list, creating new objects with the x and y coordinates of the previous body segment to create the tail effect. Why is it stopping short?
import pygame
from pygame.locals import *
import random
import sys
pygame.init()
FPS = 30
fpsClock = pygame.time.Clock()
WIN_WIDTH = 680 #width of window
WIN_HEIGHT = 500 #height of window
DISPLAY = (WIN_WIDTH, WIN_HEIGHT) #variable for screen display
DEPTH = 32 #standard
FLAGS = 0 #standard
BLACK = (0, 0, 0) #black
RED = (255, 0, 0) #red
GOLD = (255, 215, 0)
IDK = (178, 154, 96)
screen = pygame.display.set_mode(DISPLAY, FLAGS, DEPTH)
pygame.display.set_caption('Snake')
snake_parts = [1]
Score = 0
speed = 10
snakex = 15
snakey = 70
size = 20
Snakes = 1
# --- classes ---
class Snake(pygame.Rect):
def __init__(self, x, y, screen, size, colour):
pygame.Rect.__init__(self, x, y, size, 20)
self.screen = screen
self.colour = colour
self.x = x
self.y = y
def draw(self, screen):
pygame.draw.rect(self.screen, self.colour, self)
def coordinates(self):
return self.x, self.y
class Food(pygame.Rect):
def __init__(self, x, y, screen):
pygame.Rect.__init__(self, x, y, 20, 20)
self.screen = screen
def draw(self, screen):
pygame.draw.rect(self.screen, GOLD, self)
# --- functions ---
def get_food_pos(WIN_WIDTH, WIN_HEIGHT):
WIN_WIDTH = random.randint(1, WIN_WIDTH)
WIN_HEIGHT = random.randint(1, WIN_HEIGHT)
return WIN_WIDTH, WIN_HEIGHT
eaten = True
pressed_right = True
pressed_left = False
pressed_up = False
pressed_down = False
pygame.key.set_repeat(10,10)
while True:
screen.fill(BLACK)
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
elif event.type == pygame.KEYDOWN: # check for key presses
if event.key == pygame.K_LEFT: # left arrow turns left
pressed_left = True
pressed_right = False
pressed_up = False
pressed_down = False
elif event.key == pygame.K_RIGHT: # right arrow turns right
pressed_right = True
pressed_left = False
pressed_up = False
pressed_down = False
elif event.key == pygame.K_UP: # up arrow goes up
pressed_up = True
pressed_right = False
pressed_left = False
pressed_down = False
elif event.key == pygame.K_DOWN: # down arrow goes down
pressed_down = True
pressed_right = False
pressed_up = False
pressed_left = False
x = snakex
y = snakey
if pressed_left:
snakex -= speed
elif pressed_right:
snakex += speed
elif pressed_up:
snakey -= speed
elif pressed_down:
snakey += speed
snake_parts[0] = Snake(snakex, snakey, screen, int(size), IDK)
snake_parts[0].draw(screen)
if eaten:
foodx, foody = get_food_pos(WIN_WIDTH, WIN_HEIGHT)
eaten = False
my_food = Food(foodx, foody, screen)
my_food.draw(screen)
if snake_parts[0].colliderect(my_food):
eaten = True
screen.fill(BLACK)
a_snake = Snake(snakex, snakey, screen, int(size), RED)
snake_parts.append(a_snake)
if len(snake_parts) >= 1:
for i in range(1, len(snake_parts)-1):
tempx, tempy = snake_parts[i].coordinates()
snake_parts[i] = Snake(x, y, screen, int(size), RED)
snake_parts[i].draw(screen)
snake_parts[i+1] = Snake(tempx, tempy, screen, int(size), RED)
x, y = tempx, tempy
Figured it out, the issue was this line:
snake_parts[i+1] = Snake(tempx, tempy, screen, int(size), RED)
removed it and it's working fine.