Rect.colliderect cannot detect specific collision - python-3.x

I am trying to make a very simple game, here is my full code:
import pygame
from pygame.locals import *
pygame.init()
#define variables
width, height = 940, 780
screen = pygame.display.set_mode((width, height))
grey = 87, 87, 87
white = 255, 255, 255
player = pygame.image.load("Pics\goodcar.jpeg")
keys = [False, False, False, False]
playerpos = [0,40]
green = 0,255,0
red = 255,0,0
color = red
x1 = 0
x2 = 40
y1 = 940
y2 = 100
#main program
while 1:
screen.fill(0)
road = pygame.draw.rect(screen, grey, (x1,x2,y1,y2), 0)
traffic_light = pygame.draw.circle(screen, white, (640,90), 40, 1)
screen.blit(player, playerpos)
car_rect = player.get_rect()
if traffic_light.colliderect(car_rect):
print("Its working")
for event in pygame.event.get():
if event.type==pygame.QUIT:
pygame.quit()
exit(0)
if event.type == pygame.KEYDOWN:
if event.key==K_RIGHT:
keys[0]=True
elif event.key==K_LEFT:
keys[1]=True
elif event.key==K_DOWN:
keys[2]=True
elif event.key==K_UP:
keys[3]=True
if event.type == pygame.KEYUP:
if event.key==pygame.K_RIGHT:
keys[0]=False
elif event.key==pygame.K_LEFT:
keys[1]=False
elif event.key==pygame.K_DOWN:
keys[2]=False
elif event.key==pygame.K_UP:
keys[3]=False
if keys[0]==True:
playerpos[0]+=3
elif keys[1]==True:
playerpos[0]-=3
elif keys[2]==True:
playerpos[1]+=3
elif keys[3]==True:
playerpos[1]-=3
pygame.display.update()
at the part where it checks if the car collided with the traffic light, it doesn't do anything.
Iv'e tried using a try statement, still doesn't work

Look at the documentation what Surface.get_rect() does:
Returns a new rectangle covering the entire surface. This rectangle will always start at 0, 0 with a width. and height the same size as the image.
So the Rects never collide, since the Rect for the car always starts at 0, 0. An easy fix is to simply set the starting position (top and left) of the car Rect while calling get_rect().
Change
car_rect = player.get_rect()
to:
car_rect = player.get_rect(left=playerpos[0], top=playerpos[1])
(Another way is to use the Sprite class, which basically combines a Surface and a Rect.)

Related

when adding new text it appears on bottom and rest of text goes up

I want to display text on the text box that says if I hit and how much damage I do and vice versa for the enemy, but I just can't figure out how to make the text to display in this manner.
Here is the code I'm working on:
def textBox(textv):
lText = []
text = font.render(str(textv),True,(1,1,1))
lText.append(text)
if len(lText) >= 10:
lText.pop(9)
screen.blit(lText[0],(20,400))
screen.blit(lText[1],(20,380))
while True:
battle_screen()
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
screen.blit(enemy_background,(20,20))
player.drawPlayer()
enemy.newEnemy()
textBox("Daniel")
textBox("Jenny")
pygame.display.update()
Rendere the text an add the rendered text into a list:
text_surf_list = []
text_surf = font.render("Daniel", True, (255, 255, 0))
text_surf_list.append(text_surf)
text_surf = font.render("Jenny", True, (255, 255, 0))
text_surf_list.append(text_surf)
Define a rectangular area for the text box:
text_box_rect = pygame.Rect(20, 20, 250, 360)
Iterate through list in reversed order. Draw the text from the bottom up. Remove the text from the list if it is not in the box (see How to remove items from a list while iterating?):
text_y = box_rect.bottom - font.get_height()
for text_surf in reversed(text_surf_list[:]):
if text_y <= box_rect.top:
text_surf_list.remove(text_surf)
else:
surf.blit(text_surf, (box_rect.left + 10, text_y))
text_y -= font.get_height()
Set a temporary clipping area while drawing the text box withpygame.Surface.set_clip:
# set clipping
surf.set_clip(box_rect)
# draw text box
# [...]
# disable clipping
surf.set_clip(None)
Minimal example:
import pygame, random
pygame.init()
window = pygame.display.set_mode((400, 400))
clock = pygame.time.Clock()
def draw_scroll_text_box(surf, font, box_rect, text_surf_list):
surf.set_clip(box_rect)
text_y = box_rect.bottom - font.get_height() - 10
for text_surf in reversed(text_surf_list[:]):
if text_y <= box_rect.top:
text_surf_list.remove(text_surf)
else:
surf.blit(text_surf, (box_rect.left + 10, text_y))
text_y -= font.get_height()
pygame.draw.rect(surf, (164, 164, 164), box_rect, 5)
surf.set_clip(None)
font = pygame.font.SysFont(None, 50)
text_box_rect = pygame.Rect(20, 20, 250, 360)
text_surf_list = []
timer_event = pygame.USEREVENT+1
pygame.time.set_timer(timer_event, 200)
background = pygame.Surface(window.get_size())
ts, w, h, c1, c2 = 50, *background.get_size(), (32, 32, 32), (64, 64, 64)
tiles = [((x*ts, y*ts, ts, ts), c1 if (x+y) % 2 == 0 else c2) for x in range((w+ts-1)//ts) for y in range((h+ts-1)//ts)]
[pygame.draw.rect(background, color, rect) for rect, color in tiles]
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == timer_event:
names = ["Daniel", "Jenny", "Patrick", "Sandy", "Bob"]
text_surf = font.render(random.choice(names), True, (255, 255, 0))
text_surf_list.append(text_surf)
window.blit(background, (0, 0))
draw_scroll_text_box(window, font, text_box_rect, text_surf_list)
pygame.display.flip()
clock.tick(60)
pygame.quit()
exit()

moving a square in a grid

making a square move in a grid
import pygame
from pygame.locals import *
pygame.init()
clock = pygame.time.Clock()
w = 1008
h = 640
left = 16
top = 16
width = 16
height = 16
YELLOW = (255, 255, 55)
BLUE = (0, 0, 255)
BLACK = (0, 0, 0)
bg = (255, 255, 255)
x = 0
y = 0
screen = pygame.display.set_mode((w, h))
gameExit = False
while not gameExit:
for event in pygame.event.get():
if event.type == pygame.QUIT:
gameExit = True
if event.type == KEYDOWN:
if event.key == K_DOWN:
top += 16
if event.key == K_UP:
top -= 16
if event.key == K_RIGHT:
left += 16
if event.key == K_LEFT:
left -= 16
x += 16
y += 16
screen.fill(BLACK)
pygame.draw.rect(screen, [255, 255, 55], [left, top, width, height], 0)
pygame.draw.line(screen, bg, (w - 16, y), (16, y), 1)
pygame.draw.line(screen, bg, (x, h - 16), (y, 16), 1)
pygame.display.flip()
pygame.quit()
at the moment if I # out screen.fill() grid appears on the screen and the rect moves with a tail behind, if I take away the # from the screen.fill() grid disappears but rect moves correct with no tail I want both to happen.
The issue with your code is that it isn't doing what you might expect. Every time you iterate through your while loop, you increment the x and y values by 16 and draw a single horizontal and vertical line. This appears to draw a grid, because the screen is NOT cleared automatically between your while loop iterations. However, as a result, after a while, your x and y values increase to the point that you start drawing more and more grid lines that are off the screen (try to print out the start and end points for these lines in your loop if this is unclear)!
When you add the screen.fill(BLACK) call before you start drawing grid lines, you basically clear the entire screen. Since you do not redraw your old grid lines, only the next pair of grid lines are drawn and the rest are lost. Since the new pair of grid lines eventually goes off the screen (as described in the first paragraph), your code does not appear to draw any grid lines.
When you comment out screen.fill(BLACK) instead, previous drawings are not cleared. All of the grid is drawn over multiple while loop iterations (in addition to wastefully drawing new grid lines off screen), as is all of the previously positions your player is drawn at.
To solve this, your game drawing loop should be structured like this:
Clear the screen.
Draw the entire grid, not just two lines at a time. Do this in a function (e.g. draw_grid).
Draw the rectangle at the new player position. Do this in a function as well (e.g. draw_player).
Rinse and repeat next iteration!
Implementing these small changes, your code could look like this:
import pygame
from pygame.locals import *
def draw_grid():
screen.fill(BLACK)
for y in range(height, h, height):#horizontal lines
pygame.draw.line(screen, bg, (width, y), (w - width, y), 1)
for x in range(width, w, width):#vertical lines
pygame.draw.line(screen, bg, (x, height), (x, h - height), 1)
def draw_player():
pygame.draw.rect(screen, [255, 255, 55], [left, top, width, height], 0)
pygame.init()
clock = pygame.time.Clock()
w = 1008
h = 640
left = 16
top = 16
width = 16
height = 16
YELLOW = (255, 255, 55)
BLUE = (0, 0, 255)
BLACK = (0, 0, 0)
bg = (255, 255, 255)
x = 0
y = 0
screen = pygame.display.set_mode((w, h))
gameExit = False
while not gameExit:
for event in pygame.event.get():
if event.type == pygame.QUIT:
gameExit = True
if event.type == KEYDOWN:
if event.key == K_DOWN:
top += 16
if event.key == K_UP:
top -= 16
if event.key == K_RIGHT:
left += 16
if event.key == K_LEFT:
left -= 16
draw_grid()
draw_player()
pygame.display.flip()
pygame.quit()
That being said, there is plenty of room for improvement for this code, which I will leave up to you. The big issue is that you are using a bunch of global variables. These are basically all of the variables listed before your while loop. Use of such global variables is "evil" when they are mutable. As your code gets bigger, it will become more difficult to reason what functions are changing these variables, since every function could potentially have access to these variables.

Mouse click detection in pygame

I have a task, where I have a square 3X3 and for every click in the small square, this square will paint in red. Here is my code yet. I think I did something wrong in my first while loop, but I'm not sure. Please help me.
import pygame
pygame.init()
#create a screen
screen = pygame.display.set_mode((400, 400))
#colors
white = [255, 255, 255]
red = [255, 0, 0]
x = 0
y = 0
#create my square
for j in range(3):
for i in range(3):
pygame.draw.rect(screen, white, (x, y, 30, 30), 1)
x += 30
if x == 90:
x = 0
y += 30
pygame.display.flip()
running = 1
while running:
event = pygame.event.poll()
#found in what position my mouse is
if event.type == pygame.QUIT:
running = 0
elif event.type == pygame.MOUSEMOTION:
print("mouse at (%d, %d)" % event.pos)
mouse = pygame.mouse.get_pos()
click = pygame.mouse.get_pressed()
#mouse click
if click[0] == 1 and x in range(30) and y in range (30):
pygame.draw.rect(screen, red, (30, 30 , 29 ,29))
while pygame.event.wait().type != pygame.QUIT:
pygame.display.change()
You have to update your screen each time when you draw or do something in screen. So, put this line under your first while loop.
pygame.display.flip()
In your condition you was checking x and y and which are not mouse position.
if click[0] == 1 and x in range(30) and y in range (30):
Check your mouse position in range(90) because of you have three rectangular and the are 30x30.
if click[0] == 1 and mouse[0] in range(90) and mouse[1] in range (90):
Then, set your rectangular start position to fill the mouse point.
rect_x = 30*(mouse[0]//30) # set start x position of rectangular
rect_y = 30*(mouse[1]//30) # set start y position of rectangular
You can edit your code with this.
while running:
pygame.display.flip()
event = pygame.event.poll()
#found in what position my mouse is
if event.type == pygame.QUIT:
running = 0
elif event.type == pygame.MOUSEMOTION:
print("mouse at (%d, %d)" % event.pos)
mouse = pygame.mouse.get_pos()
click = pygame.mouse.get_pressed()
#mouse click
if click[0] == 1 and mouse[0] in range(90) and mouse[1] in range (90):
'''
rect_x = 30*(0//30) = 0
rect_y = 30*(70//30) = 60
'''
rect_x = 30*(mouse[0]//30) # set start x position of rectangular
rect_y = 30*(mouse[1]//30) # set start y position of rectangular
pygame.draw.rect(screen, red, (rect_x, rect_y , 30 , 30)) # rectangular (height, width), (30, 30)

Animation of a projection object use pygame

I'm trying to make a animation of projection movement but it does't work.
Just a black window appear.
import math
import time
import pygame
V0 = int(input("please enter initial speed(m/s)"))
angle = int(input("please enter throwing angle"))
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
pygame.init()
screen = pygame.display.set_mode((400, 300))
done = False
clock = pygame.time.Clock()
while not done:
T = time.clock()
x = int(math.cos(angle)*V0*T)
y = int((math.sin(angle)*V0*T)-1/2*(9.8*T*T))
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
screen.fill(BLACK)
pygame.draw.circle(screen, WHITE, (x, y), 5, 0)
pygame.display.flip()
clock.tick(10)
pygame.quit()
I think the problem is my formula statement, but I don't know how to fix it.
Read the comments. I've changed the way you calculated time and angle.
import math
import time
import pygame
V0 = int(input("please enter initial speed(m/s)"))
angle = int(input("please enter throwing angle"))
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
pygame.init()
screen = pygame.display.set_mode((400, 300))
done = False
clock = pygame.time.Clock()
T = 0 #initialize
while not done:
#T = time.clock() #don't use
x = int(math.cos(math.radians(angle))*V0*T) #use math.radians(degree). Don't just provide degrees. cos() and sin() needs input in radians
y = 300 - int((math.sin(math.radians(angle))*V0*T)-(0.5*9.8*T*T)) #looks better. projectile starts from the ground.
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
screen.fill(BLACK)
pygame.draw.circle(screen, WHITE, (x,y), 5, 0)
milliseconds = clock.tick(60) #try this. count ms. add to s. calculate total time passed.
seconds = milliseconds / 1000.0
T += seconds
pygame.display.flip()
pygame.quit()

Bullet to Enemy Collision Detection

After a few hours of looking for code to reference. I failed i tried to re-write the collision code but it failed. Now my Question is
How can i fix my code to make it delete the Enemy ship when the bullet hits him
Note: Im not asking for you to do it, i just need it further explained what the problem or where im making a mistake
The Code Segment
for bullet in Bullets:
Hit_list = pygame.sprite.Group()
if pygame.sprite.collide_rect(bullet, Enemy_list):
Hit_list.add(hit)
for hit in Hit_list:
Bullets.remove(bullet)
All.remove(bullet)
All.remove(hit)
Enemy_list.remove(hit)
# If the bullet goes off the screen it deletes
if bullet.rect.y < 0:
Bullets.remove(bullet)
All.remove(bullet)
The Error
Traceback (most recent call last):
File "C:\Users\jb127996\Desktop\Invaders in Space\Space Shooter.py", line 92, in <module>
if pygame.sprite.collide_rect(bullet, Enemy_list):
File "C:\Python32\lib\site-packages\pygame\sprite.py", line 1290, in collide_rect
return left.rect.colliderect(right.rect)
AttributeError: 'Group' object has no attribute 'rect'
Entire Code
import pygame
import Player_func
import AI
from pygame.locals import *
#Background
Background = pygame.image.load('Tempback.png')
Background_size = Background.get_size()
Background_rect = Background.get_rect()
# Screens Resoultion
Width = 640
Height = 480
Scr = pygame.display.set_mode((Width,Height))
# Fps Clock Vars( Clock.tick(FPS) )
Clock = pygame.time.Clock()
FPS = 60
# Colors
White = (255, 255, 255)
Black = (0, 0, 0)
Green = (0, 255, 0)
Red = (255, 0, 0)
Blue = (0, 0, 255)
Orange = (255, 140, 0)
Default = White
# Sprite Lists
Bullets = pygame.sprite.Group()
Enemy_list = pygame.sprite.Group()
All = pygame.sprite.Group()
# Shorting the Class files
AI = AI.Enemy()
Ply = Player_func.Player()
# CORDS for Player
AI.rect.x, AI.rect.y = 100, 50
AI_pos = (AI.rect.x, AI.rect.y)
Ply.rect.x, Ply.rect.y = 580, 425
P_pos = (Ply.rect.x, Ply.rect.y)
# True false statements and Vars
running = True
Gun_Overload = 0
Fire = 20
while running:
Enemy_list.add(AI)
All.add(AI)
All.add(Ply)
# Collision Detection for walls on the spaceship
if Ply.rect.x <= 0:
Ply.rect.x += 5.5
elif Ply.rect.x >= 590:
Ply.rect.x -= 5.5
# Events
for event in pygame.event.get():
if event.type == QUIT:
running = False
elif event.type == KEYDOWN:
if event.key == K_SPACE and Gun_Overload == Fire:
bullet = Player_func.Bullet()
bullet.rect.x = (Ply.rect.x +15)
bullet.rect.y = Ply.rect.y
Bullets.add(bullet)
All.add(bullet)
Gun_Overload = 0
#Cool Down
if Gun_Overload < Fire:
Gun_Overload += 1
AI.movement()
Ply.Controls()
# Updates the Sprite lists so they can all be drawn
All.update()
# Checks if a bullet hits the enemy on the list
for bullet in Bullets:
Hit_list = pygame.sprite.Group()
if pygame.sprite.collide_rect(bullet, Enemy_list):
Hit_list.add(hit)
for hit in Hit_list:
Bullets.remove(bullet)
All.remove(bullet)
All.remove(hit)
Enemy_list.remove(hit)
# If the bullet goes off the screen it deletes
if bullet.rect.y < 0:
Bullets.remove(bullet)
All.remove(bullet)
Scr.blit(Background, Background_rect)
All.draw(Scr)
pygame.display.flip()
# FPS Clock Loaded in
Clock.tick(FPS)
pygame.quit()
Your If condition uses collide_rect:
pygame.sprite.collide_rect()
Collision detection between two sprites, using rects.
collide_rect(left, right) -> bool
You pass 2 parameters - a Bullet, and a SpriteGroup. The error is telling you that a SpriteGroup does not have a Rect attribute.
I think you wanted to use:
pygame.sprite.spritecollide()
Find sprites in a group that intersect another sprite.
spritecollide(sprite, group, dokill, collided = None) -> Sprite_list
Apart from this, I would change all the variable names to lowercase. I have also noticed that your if bullet.rect.y < 0: is under the collide test. I think you want to remove a Bullet from the screen even if there is no collision.

Resources