Pygame Collision Detection (rectangles) [duplicate] - python

This question already has answers here:
How do I detect collision in pygame?
(5 answers)
Closed 1 year ago.
I was only just introduced to Python and I am having a bit of trouble on detecting collision between two rectangles. I want a one rectangle to bounce off the other when they collide
Here is my code so far:
import pygame
import sys
from pygame.locals import *
pygame.init()
ww = 400
wh = 300
w = pygame.display.set_mode((ww,wh))
pygame.display.set_caption('OFK')
s = pygame.display.get_surface()
green = pygame.Color(152,251,152)
blue = pygame.Color(135,206,250)
white = pygame.Color(255,255,240)
clock = pygame.time.Clock()
background_file = 'ice-in-water.jpg'
background_image = pygame.image.load(background_file).convert()
rx = ww/2
ry = wh/2
rw = 30
rh = 20
px = ww/2
py = wh - 40
pw = 60
ph = 10
vdirection = -1
hdirection = 1
while True:
clock.tick(60)
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
quit()
if event.type == KEYDOWN:
#print (event.key,event.unicode)
if event.key == 275:
px = px + 30
if px > ww - pw:
px = ww - pw
if event.key == 276:
px = px - 30
if px < 0:
px = 0
s.fill(green)
pygame.draw.rect(s,blue,(rx,ry,rw,rh),0)
pygame.draw.rect(s,white,(px,py,pw,ph),0)
#w.blit(background_image,(-80,0))
pygame.display.update()
#mx,my = pygame.mouse.get_pos()
#print ("x: ", mx, "y: ", my)
#print ("ry: ", ry, "wh: ", wh)
rx = rx + 1.5 * hdirection
if rx > ww - rw:
hdirection = -hdirection
if rx < 0:
hdirection = -hdirection
ry = ry + 1.5 * vdirection
if ry > ww:
rx = ww/2
ry = wh/2
vdirection = -vdirection
if ry < 0:
vdirection = -vdirection
I have tried finding tutorials on how to do this, but I can't understand very well. A little help would be appreciated.

In the Rect class of pygame there is a method called colliderect.
You can also refer to this previous answer for some hints.

I calculate the angle between the two center points of the circles. Also remember arctan returns an angle between -pi/2 and pi/2 meaning you need to account for the other side of the unit circle.
then I use the angle to determine the exact points of the circles which point toward the other circle if you were to draw a line from center point to center point.
finally, I'm casting them to int as an easy way of checking to see if the two points end up being equal.
def detectCollision(self, game):
for planet in game.planets:
directionX = self.x - planet.x
directionY = self.y - planet.y
theta = atan(directionY / directionX)
if directionX < 0:
theta += pi
if theta < 0:
theta += 2 * pi
# point on player circle
point1 = [self.x + self.radius*cos(theta+pi),self.y + self.radius*sin(theta+pi)]
# point on planet circle
point2 = [planet.x + planet.radius*cos(theta), planet.y + planet.radius*sin(theta)]
if int(point1[0]) == int(point2[0]) and int(point1[1]) == int(point2[1]):
print("collided!")
This technically works, but will probably have problems if the movement steps are greater than a unit of 1 integer, since we're only checking that they are equal, not whether they've overlapped. A better way might be to leave them as floats and check that the difference comes within a certain bound, possibly dependent on the player velocity to account for larger jumps between frames.

Related

Python 2d Raycaster not using proper depth [duplicate]

This question already has answers here:
How do I fix wall warping in my raycaster?
(1 answer)
Problem with recognising where a ray in raycaster intersects a wall along the horizontal axis
(1 answer)
cant get raycast to work from angles 90 to 270 pygame
(1 answer)
Why my raycasting keeps going through walls?
(1 answer)
Closed 3 months ago.
I am trying to create a raycast visualizer. The lines are supposed to shoot out and stop when they collide with a wall. Currently the length is entirely random and sometimes the rays point in directions that are even behind me. I am using an scale of 47 when i draw things to the screen for tiling purposes. I have tried for 10 or so hours every scale in the raycast code and I can't see what I am missing.
import pygame
import numpy
from PygameEngine import GameEngine
import sys
import math
class RayCasting:
FOV = numpy.pi/5
HALF_FOV = FOV/2
NUM_RAYS = GameEngine.WIDTH//2
HALF_NUM_RAYS = NUM_RAYS//2
DELTA_ANGLE = FOV/NUM_RAYS
MAX_DEPTH = 20
def __init__(self, game):
self.game = game
def rayCast(self):
ox, oy = self.game.wasd.pos
x_map = int(ox)
y_map = int(oy)
ray_angle = self.game.wasd.angle - self.HALF_FOV + 0.000001
for ray in range(self.NUM_RAYS):
sin_a = math.sin(ray_angle)
cos_a = math.cos(ray_angle)
# horizontals
y_hor, dy = (y_map + 1, 1) if sin_a > 0 else (y_map - 1e-6, -1)
depth_hor = (y_hor - oy) / sin_a
x_hor = ox + depth_hor * cos_a
delta_depth = dy / sin_a
dx = delta_depth * cos_a
print("YHor: ",y_hor, " DY:", dy, " Depth Hor: ", depth_hor, "X Hor: ", x_hor,
" Delta Depth: ", delta_depth, " DX: ", dx)
for i in range(self.MAX_DEPTH):
tile_hor = int(x_hor), int(y_hor)
if tile_hor in self.game.MAP.wallMap:
# print("INSIDE HOR")
break
x_hor += dx
y_hor += dy
depth_hor += delta_depth
# verticals
x_vert, dx = (x_map + 1, 1) if cos_a > 0 else (x_map - 1e-6, -1)
depth_vert = (x_vert - ox) / cos_a
y_vert = oy + depth_vert * sin_a
delta_depth = dx / cos_a
dy = delta_depth * sin_a
for i in range(self.MAX_DEPTH):
tile_vert = int(x_vert), int(y_vert)
if tile_vert in self.game.MAP.wallMap:
# print("INSIDE VERT")
break
x_vert += dx
y_vert += dy
depth_vert += delta_depth
# depth, texture offset
if depth_vert < depth_hor:
depth = depth_vert
#y_vert %= 1
#offset = y_vert if cos_a > 0 else (1 - y_vert)
else:
depth = depth_hor
#x_hor %= 1
#offset = (1 - x_hor) if sin_a > 0 else x_hor
# remove fishbowl effect
#depth *= math.cos(self.game.wasd.angle - ray_angle)
# projection
#proj_height = SCREEN_DIST / (depth + 0.0001)
# ray casting result
#self.ray_casting_result.append((depth, proj_height, texture, offset))
ray_angle += self.DELTA_ANGLE
pygame.draw.line(self.game.screen, "yellow", (ox*self.game.CELLSIZE,oy*self.game.CELLSIZE), (ox*self.game.CELLSIZE+depth*cos_a, oy*self.game.CELLSIZE+depth*sin_a), 1)
def update(self):
self.rayCast()
from PygameEngine import GameEngine
from Circle import Circle
import pygame
from pygame.locals import *
import sys
import numpy
from map import Map
from RaycastFunction import RayCasting
class RaycastGame(GameEngine):
# Space bar to place this circle which will connect to the WASD with a line
planet = Circle((0,0,0))
planet.keyX = 5
planet.keyY = 5
# Grid set up
def __init__(self):
super().__init__()
self.load()
self.MAP = Map()
self.CELLSIZE = self.MAP.CELLSIZE
# Circle controllable with WASD
self.wasd = Circle((123, 255, 123))
self.raycast = RayCasting(self)
def DDA(self):
# -
# * |
# Remember the Plane is - --m-- +
# * = target |
# m = mouse +
distX = self.wasd.keyX - self.planet.pos[0]
distY = self.wasd.keyY - self.planet.pos[1]
#hypotenuse = numpy.sqrt(distX**2+distY**2)
theta = numpy.arctan((distY/(distX+.0001)))
theta += numpy.deg2rad(90)
# print(numpy.rad2deg(theta), " THETA")
collisionPos = (0,0)
def draw(self):
# Draw MAP array
self.MAP.drawMap()
self.MAP.drawGrid()
# Draw mouse character
#pygame.draw.circle(self.screen, (0, 0, 0),
#(self.plane), Circle.radius)
# Draw planet
# self.planet.draw(self.screen)
# Draw wasd character
self.wasd.draw(self.screen)
# Connect mouse and wasd characters with a line
#pygame.draw.line(self.screen, (255, 255, 255), self.planet.pos, (self.wasd.keyX, self.wasd.keyY), 5)
def update(self):
self.planet.placePlanet()
self.wasd.move()
self.DDA()
self.raycast.update()
def run(self):
# Game loop.
while True:
#This gets written over. Only for clearing screen before each draw
self.screen.fill((0, 0, 0))
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
# Update.
self.update()
# Draw
self.draw()
pygame.display.flip()
self.fpsClock.tick(self.FPS)
I do not understand why the rays are not stopping in the proper area.

Pygame: Moving an image around another image in a circular path

I know that there are already some solutions here but unfortunately they did not help me with my problem. I want to move a satellite image around a planet image in a circular path. I know how to move it in a line from left to right but I don't get it how to make the circle. This is my code so far
import pygame
from pygame.locals import *
from sys import exit
from math import sin
from math import cos
player_image = 'dragon-500px.png'
pygame.init()
screen = pygame.display.set_mode((1080, 720), 0, 32)
pygame.display.set_caption("Animate X!")
mouse_cursor = pygame.image.load(player_image).convert_alpha()
image_earth = pygame.image.load('Erde.jpg').convert()
image_earth = pygame.transform.scale(image_earth, (300,300))
image_earth_rect = image_earth.get_rect()
image_earth_rect.center = (540, 360)
radius = 100
center = (540, 360)
direction = pygame.math.Vector2(1,0)
clock = pygame.time.Clock()
speed = 300.0
x = 0 - mouse_cursor.get_width()
y = 10
while True:
time_passed = clock.tick() / 1000.0
moved_distance = time_passed * speed
for event in pygame.event.get():
if event.type == QUIT:
exit()
direction.rotate_ip(4)
direction.normalize_ip()
Satellite_pos = [int(i) for i in center + direction*radius]
screen.fill((255,255,255))
if x > screen.get_width():
x = 0 - mouse_cursor.get_width()
elif y > screen.get_height():
y = 10
screen.blit(mouse_cursor, (x, y))
screen.blit(image_earth, image_earth_rect)
x+=moved_distance
y+= moved_distance
pygame.display.update() ```
Compute the position of the satellite dependent on an angle:
angle = 0
# [...]
while True:
# [...]
center = image_earth_rect.center
Satellite_pos = [center[0] + radius * cos(angle), center[1] + radius * sin(angle)]
angle += 0.01
# [...]

Why won't my Spaceship point toward the planet? [duplicate]

This question already has answers here:
How to know the angle between two vectors?
(7 answers)
How to rotate an image(player) to the mouse direction?
(2 answers)
How do I rotate an image around its center using Pygame?
(6 answers)
Closed 2 years ago.
Small gif showing problem but I don't have 10 reputation yet (this is only my 2nd question) and thus have to use link
I have a simple test program with a Spaceship which is supposed to point toward a planet, but it points in odd directions instead.
The purpose of the program is to test whether my test program can get the angle from the spaceship to the planet, so an alternate method won't work, since I need the angle to determine which direction to apply "gravity" in (which is what this test is for).
This is also why the script repeats multiple times but only the final angle will be graphically displayed.
My program uses code gotten from SO question Calculate angle (clockwise) between two points
The answer by "Chris St Pierre" just gave me an error for attempting to divide a float by zero (?).
The answer by "ali_m" just gave me a problem like this one.
I'm using the answer by "Colin Basnett", which doesn't work for me either, but it's my favorite method so far because it doesn't require plugins (and because it's short and doesn't just straight-away throw an error at me).
I adapted it into the function below:
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def get_angle_between(x0,y0,x1,y1):
v1 = Vector(x0, y0)
v2 = Vector(x1, y1)
v1_theta = math.atan2(v1.y, v1.x)
v2_theta = math.atan2(v2.y, v2.x)
r = (v2_theta - v1_theta) * (180.0 / math.pi)
return r
It is called by this script in the Spaceship sprite's "move" function:
if gravityJsonQ:
for item in planets:
centreOfGravityX = planets[item]["x"] + (planets[item]["s"] / 2)
centreOfGravityY = planets[item]["y"] + (planets[item]["s"] / 2)
centreOfGravityGravity = float(planets[item]["g"])
pendingUtil = get_points(prevSubPositionX,prevSubPositionY,subPositionX,subPositionY)
for item2 in pendingUtil:
cfx,cfy = item2
circular_percentage = get_angle_between(cfx,cfy,centreOfGravityX,centreOfGravityY) / 3.6
circular_percentage (cp) is essentially degrees / 3.6 (anticlockwise, which, although the link is for clockwise angles, I tried subtracting it from 100cp (360deg) to no avail, so I doubt that's the problem)
get_points() is "Bresenham's line algorithm", and it works fine.
planets is the following dictionary:
{"Earthlike": {"x": 375, "y": 375, "s": 200, "i": "earthlike_1_beveled.png", "g": 11}}
I've tried fiddling with it a bit to see if it would start working, but the main problem is I don't understand any of the math involved, so the linked Wikipedia article(s) went right over my head.
I have (frankly) not a clue to what's causing the problem or how to solve it.
Here's a link to download all 194KB (it's actually 10KB smaller when unzipped) of the program and it's textures. (Use WASD/arrow keys to move, the problem is in either lines 49 to 63 or 100 to 108 (the first line is #1 not #0)):
https://www.filehosting.org/file/details/920907/SOQ.zip
There might be some unnecessary code since I just got my main program and cut out most of the bits that weren't needed.
Just in case, here's the code (it's in the zip, but I figured I'm probably supposed to put it here anyway even though it is unrunable (real word?) without the textures):
#See lines (this line is #1) - 49 to 63 - and - 100 to 108
import json, math, os, pygame, sys, time
from pygame.locals import *
pygame.init()
baseFolder = __file__[:-10]
FPS = 30
FramePerSec = pygame.time.Clock()
xVelocity = yVelocity = rVelocity = float(0)
def get_points(x0,y0,x1,y1):
pointlist = []
x0,y0 = int(x0),int(y0)
x1,y1 = int(x1),int(y1)
dx = abs(x1-x0)
dy = abs(y1-y0)
if x0 < x1: sx = 1
else: sx = -1
if y0 < y1: sy = 1
else: sy = -1
err = dx-dy
while True:
pointlist.append((x0,y0))
if x0 == x1 and y0 == y1: return pointlist
e2 = 2 * err
if e2 > -dy:
err = err - dy
x0 += sx
if e2 < dx:
err = err + dx
y0 += sy
screen_size = 750
spaceship_texture = "spaceship.png"
spaceship_texture = spaceship_texture.replace("\n","")
spaceship_size = 60
gravityJsonQ = True
planets = {"Earthlike": {"x": 375, "y": 375, "s": 200, "i": "earthlike_1_beveled.png", "g": 11}}
displaySurf = pygame.display.set_mode((screen_size,screen_size))
displaySurf.fill((0,0,0))
subPositionX = subPositionY = float(screen_size / 2)
circular_percentage = 0
#Problem:
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def get_angle_between(x0,y0,x1,y1):
v1 = Vector(x0, y0)
v2 = Vector(x1, y1)
v1_theta = math.atan2(v1.y, v1.x)
v2_theta = math.atan2(v2.y, v2.x)
r = (v2_theta - v1_theta) * (180.0 / math.pi)
return r
#or mabye...
class Spaceship(pygame.sprite.Sprite):
def __init__(self):
global baseFolder, screen_size, spaceship_images, spaceship_size, spaceship_texture
super().__init__()
spaceship_images = {}
for pendingUtil in range(0,100): spaceship_images[str(pendingUtil)] = pygame.image.load(baseFolder + "\\" + spaceship_texture + ".texture_map\\" + str(pendingUtil) + ".png")
self.image = spaceship_images["0"]
self.surf = pygame.Surface((int(spaceship_size), int(spaceship_size)))
self.rect = self.surf.get_rect(center = (int(screen_size / 2),int(screen_size / 2)))
self.image = pygame.transform.scale(self.image,(spaceship_size,spaceship_size))
def move(self):
global circular_percentage, rVelocity, prevSubPositionX, prevSubPositionY, subPositionX, subPositionY, xVelocity, yVelocity
pressed_keys = pygame.key.get_pressed()
if pressed_keys[K_UP] or pressed_keys[K_w]:
yVelocity -= 0.1
if pressed_keys[K_DOWN] or pressed_keys[K_s]:
yVelocity += 0.1
if pressed_keys[K_LEFT] or pressed_keys[K_a]:
xVelocity -= 0.1
if pressed_keys[K_RIGHT] or pressed_keys[K_d]:
xVelocity += 0.1
prevSubPositionX,prevSubPositionY = subPositionX,subPositionY
subPositionX += xVelocity
subPositionY += yVelocity
#Problem:
if gravityJsonQ:
for item in planets:
centreOfGravityX = planets[item]["x"] + (planets[item]["s"] / 2)
centreOfGravityY = planets[item]["y"] + (planets[item]["s"] / 2)
centreOfGravityGravity = float(planets[item]["g"])
pendingUtil = get_points(prevSubPositionX,prevSubPositionY,subPositionX,subPositionY)
for item2 in pendingUtil:
cfx,cfy = item2
circular_percentage = get_angle_between(cfx,cfy,centreOfGravityX,centreOfGravityY) / 3.6
#Problem will (very likely) be either here or noted area above
while circular_percentage < 0: circular_percentage += 100
while circular_percentage > 99: circular_percentage -= 100
self.rect = self.surf.get_rect(center = (int(subPositionX),int(subPositionY)))
self.image = spaceship_images[str(int(circular_percentage))]
self.image = pygame.transform.scale(self.image,(spaceship_size,spaceship_size))
Player = Spaceship()
all_sprites = pygame.sprite.Group()
all_sprites.add(Player)
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
displaySurf.fill((0,0,0))
for item in planets:
current_planet_image = pygame.image.load(planets[item]["i"])
current_planet_image = pygame.transform.scale(current_planet_image,(planets[item]["s"],planets[item]["s"]))
displaySurf.blit(current_planet_image,(planets[item]["x"],planets[item]["y"]))
for entity in all_sprites:
displaySurf.blit(entity.image,entity.rect)
entity.move()
pygame.display.update()
FramePerSec.tick(FPS)
Note, Pygame provides the pygame.math.Vector2 class. It is not necessary to paint 100 images of the space ship with different angles. You can rotate an image with pygame.transform.rotate. See How do I rotate an image around its center using PyGame?.
The vector from the point (x0, y0) to the point (x1, y1) is:
v = Vector(x1-x0, y1-y0)
The angle of the vector is (see How to know the angle between two points?):
math.atan2(y1-y0, x1-x0)
The top left of the pygame coordinate system is (0, 0). Therefore the y-axis points downwards. Hence you have to invert the y-axis for the calculation of the angle.
get_angle_between function:
def get_angle_between(x0, y0, x1, y1):
v = Vector(x1-x0, y1-y0)
return math.degrees(math.atan2(-v.y, v.x))
In the above formula, an angle of 0 means that the spaceship is pointing to the right. If your spaceship is pointing up at a 0 angle, you'll need to add a correction angle (see How to rotate an image(player) to the mouse direction?):
def get_angle_between(x0, y0, x1, y1):
v = Vector(x1-x0, y1-y0)
return math.degrees(math.atan2(-v.y, v.x)) - 90
In this answer I want to explain the steps for the calculation. I want to keep it comprehensible. Of course, you can skip constructing the Vector object and put everything in one line of code:
def get_angle_between(x0, y0, x1, y1):
return math.degrees(math.atan2(y0-y1, x1-x0)) - 90
However, the bottleneck in the calculation is the function math.atan2.

my picture should show in the direction of my mouse but it does not work [duplicate]

This question already has answers here:
How do I make my player rotate towards mouse position?
(1 answer)
How to rotate an image(player) to the mouse direction?
(2 answers)
Closed 2 years ago.
My picture should point in the direction of my mouse but it doesn't work because I am enlarging my screen (because I make pixel art).
I think the coordinates are influenced by the magnifying of my screen
can you help me with fixing that issue??
here is my code:
import pygame
import sys
import random
import math
from pygame.locals import *
pygame.init()
clock = pygame.time.Clock()
screen_width = 1440
screen_height = 900
screen = pygame.display.set_mode((screen_width,screen_height))
display = pygame.Surface((int(1440/2),int(900/2)))
player = pygame.Rect(50,50,50,50)
bow_path = "data/bow_1.png"
bow = pygame.image.load(bow_path).convert()
bow.set_colorkey((255,255,255))
#---movement---#
right = False
left = False
down = False
up = False
movement = [0,0]
while True:
display.fill((0,0,0))
press_timer = 0
mx,my = pygame.mouse.get_pos()
movement = [0,0]
if right == True:
movement[0] += 2.5
if left == True:
movement[0] -= 2.5
if up == True:
movement[1] -= 2.5
if down == True:
movement[1] += 2.5
player.x += int(movement[0])
player.y += int(movement[1])
bow_rect = bow.get_rect()
angle = math.degrees(math.atan2(player.centery - my, mx - player.centerx)) + 180
rotated_bow = pygame.transform.rotate(bow,angle)
pygame.draw.rect(display,(255,255,255),player)
display.blit(rotated_bow,(int(player.x+25)-int(rotated_bow.get_width()/2),int(player.y+25)-int(rotated_bow.get_height()/2)))
here I change the size of my screen:
screen.blit(pygame.transform.scale(display,(screen_width,screen_height)),(0,0))
pygame.display.update()
clock.tick(60)
The mouse coordinates are always relative to the native resolution. When you scale a surface to the screen, you must descale the mouse coordinates to match the surface.
Here is the updated code:
bow_rect = bow.get_rect()
# reverse scale mouse coordinates (1/2x)
angle = math.degrees(math.atan2(player.centery - my/2, mx/2 - player.centerx)) + 180
rotated_bow = pygame.transform.rotate(bow,angle)
pygame.draw.rect(display,(255,255,255),player)
display.blit(rotated_bow, (int(player.x+25)-int(rotated_bow.get_width()/2),
int(player.y+25)-int(rotated_bow.get_height()/2)))
# scale surface to screen (2x)
screen.blit(pygame.transform.scale(display,(screen_width,screen_height)),(0,0))
pygame.display.update()
clock.tick(60)

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

Categories