How to detect collision between two objects in turtle python?

Question:

So I am making a clone for chrome’s dino game in python turtle . I have done the randomisation for the cactus (which is very hackish , and yes if something is better than my aaproach , please help !)and the jumping is working.But as the heading says , I am unable to detect collisions properly. I have tried this much :

import turtle
import time
import random
print("dino game in turtle")

wn = turtle.Screen()
wn.bgcolor("white")
wn.setup(width=650, height=400)
wn.tracer(0)
delay = 0.1
#scoring system
score = 0
w = 1
h = 1
#dino
dino = turtle.Turtle()
dino.shape("square")
dino.shapesize(w, h)
dino.color("black")
dino.penup()
dino.goto(-200, -50)

#ground
g = turtle.Turtle()
g.penup()
g.goto(0, -60)
g.pendown()
g.lt(180)
g.fd(500)
g.rt(180)
g.fd(1000)
g.hideturtle()

#cactus
#y = random.randint(2, 4)
cactus = turtle.Turtle()
cactus.penup()
cactus.shape("square")
cactus.color("green")
cactus.shapesize(3, 0.5)
cactus.goto(-50, -30)

cactus2 = turtle.Turtle()
cactus2.penup()
cactus2.shape("square")
cactus2.color("green")
cactus2.shapesize(3, 0.5)
cactus2.goto(600, -30)

cactus3 = turtle.Turtle()
cactus3.penup()
cactus3.shape("square")
cactus3.color("green")
cactus3.shapesize(3, 0.5)
cactus3.goto(600, -30)

#score_pen
pen = turtle.Turtle()
pen.speed(0)
pen.shape("square")
pen.color("white")
pen.penup()
pen.hideturtle()
pen.goto(0, 260)
pen.write("Score: 0  High Score: 0", align="center", font=("Courier", 24, "normal"))





max_height = 150



def steady():
    y = dino.ycor() 
    if y == -50:
        return True
        
    else:
        return False
def jump():
    y = dino.ycor()
    if y != -50:
        return True
    else:
        return False
def jumping():
    y = dino.ycor()
    y += 200
    dino.sety(y)

def cactus_move():
    x = cactus.xcor()
    x -= 20
    cactus.setx(x)

def cactus_move2():
    x = cactus2.xcor()
    x -= 20
    cactus2.setx(x)

def cactus_move3():
    x = cactus3.xcor()
    x -= 20
    cactus3.setx(x)
x = dino.xcor()
y = dino.ycor()

def check_rect_collision(dino, x, y, w, h): 
    
    if p.x >= x and p.x <= x+w and p.y >= y and p.y <= y+h:
        # collision between p and rectangle
        return True
    return False







wn.listen()
wn.onkeypress(jumping, "space")

while True:
    score += 1
    check_rect_collision()
    #print(score)
    #pen.clear()
    #pen.clear()
    #pen.write("Score: {} ".format(score), align="center", font=("Courier", 24, "normal")) 
    x = random.randint(200, 300)
    xm = random.randint(300, 500)
    xms = random.randint(500, 550)
    
    cactus_move()
    cactus_move2()
    cactus_move3()
    y = dino.ycor()

    if steady() == True and dino.distance(cactus) < 25:
        print("Hello")
        break
    if steady() == True and dino.distance(cactus2) < 25:
        print("Hello")
        break
    if steady() == True and dino.distance(cactus3) < 25:
        print("Hello")
        break
    if jump() == True and dino.ycor() <= cactus.ycor():
        print("Ycor")
        break 
    if jump() == True and dino.ycor() <= cactus2.ycor():
        print("Ycor")
        break 
    if jump() == True and dino.ycor() <= cactus3.ycor():
        print("Ycor")
        break 

    if steady() == False:
        y -= 25
        dino.sety(y)
        
    if y >= max_height:
        y -= 200
        dino.sety(y) 

    if cactus.xcor() < -400:
        cactus.goto(x, -30)

    if cactus2.xcor() < -400:
        cactus2.goto(xm, -30)

    if cactus3.xcor() < -400:
        cactus3.goto(xms, -30)

    
    
        

    time.sleep(delay)
    wn.update()
wn.mainloop()
Asked By: Dev

||

Answers:

Normally, I’d say use the turtle.distance() function. But since your two turtles are different shapes, you need a custom collision function that takes into account the shape of both. I’ve reworked your code below incorporating such as well as fixing it to use turtle timer events instead of while True and sleep() and other changes:

from turtle import Screen, Turtle
from random import randint

WIDTH, HEIGHT = 650, 400
MAX_HEIGHT = 180
BASELINE = -60

NUMBER_CACTI = 3

FONT = ('Courier', 24, 'normal')

CURSOR_SIZE = 20
CACTUS_WIDTH, CACTUS_HEIGHT = 10, 60

def steady():
    return dino.ycor() == BASELINE + CURSOR_SIZE/2

def jump():
    y = dino.ycor()

    if steady():
        dino.sety(y + 7 * CURSOR_SIZE)
    elif y < MAX_HEIGHT:
        dino.sety(y + 2 * CURSOR_SIZE)

def cactus_move(cactus):
    cactus.setx(cactus.xcor() - CACTUS_WIDTH)

def check_rect_collision(a, b):
    return abs(a.xcor() - b.xcor()) < CURSOR_SIZE/2 + CACTUS_WIDTH/2 and abs(a.ycor() - b.ycor()) < CURSOR_SIZE/2 + CACTUS_HEIGHT/2

def place_cactus(cactus):
    cactus.setx(randint(WIDTH//2, WIDTH))

    while True:
        for other in cacti:
            if other is cactus:
                continue

            if other.distance(cactus) < 5 * CACTUS_WIDTH:
                cactus.setx(randint(0, WIDTH//2))
                break  # for
        else:  # no break
            break  # while

# scoring system
score = 0

def run():
    global score

    score += 1

    pen.clear()
    pen.write("Score: {} ".format(score), align='center', font=FONT)

    for cactus in cacti:
        cactus_move(cactus)

        if check_rect_collision(dino, cactus):
            screen.onkeypress(None, 'space')  # Game Over
            screen.tracer(True)
            return

        if cactus.xcor() < -WIDTH/2:
            place_cactus(cactus)

    if not steady():
        dino.sety(dino.ycor() - CURSOR_SIZE)

    screen.update()
    screen.ontimer(run, 50)  # repeat in 50 milliseconds

screen = Screen()
screen.title("Dino game in turtle")
screen.bgcolor('white')
screen.setup(width=WIDTH, height=HEIGHT)
screen.tracer(False)

# ground
ground = Turtle()
ground.hideturtle()

ground.penup()
ground.sety(BASELINE)
ground.pendown()

ground.forward(WIDTH/2)
ground.backward(WIDTH)

# dino

dino = Turtle()
dino.shape('square')
dino.penup()
dino.goto(-WIDTH/3, BASELINE + CURSOR_SIZE/2)

# cacti

cacti = []

for _ in range(NUMBER_CACTI):

    cactus = Turtle()
    cactus.shape('square')
    cactus.shapesize(CACTUS_HEIGHT / CURSOR_SIZE, CACTUS_WIDTH / CURSOR_SIZE)
    cactus.color('green')

    cactus.penup()
    cactus.sety(BASELINE + CACTUS_HEIGHT/2)
    place_cactus(cactus)

    cacti.append(cactus)

# score pen
pen = Turtle()
pen.hideturtle()
pen.penup()
pen.sety(175)

pen.write("Score: 0  High Score: 0", align='center', font=FONT)

screen.onkeypress(jump, 'space')
screen.listen()

run()

screen.mainloop()

I believe it is bascially playable now but you might want to tweak some of the constants to taste.

Answered By: cdlane

You could do the same as game engines, put shapes over the objects and apply algorithms for collision detection, there are some algorithms that very simple, for example sphere vs sphere, this is how it works.

  1. Put a circle over the objects that you want detect colision, this can be done making a class CircleCollider for example and make it property of your turtle. This collider have an ID, radius and a position, the same as the turtle

  2. Make another class to manage the colliders and collisions, saving the collisions in an array or a list

  3. Every iteration (i suppose you have your game code in a loop), apply the sphere vs sphere algorithm to detect a colision. You have to do this between all the objects. (do not forget update the collider position as well)

Sphere vs sphere algorithm (between 2 spheres): just calculate the distance between the two colliders, it must be minor than the radius, you can do this with the formula d=sqrt((x1-x2)^2 + (y1-y2) ^2), if not, there is a collision.

I known this solution can be a little long, but it worth it, also you must consider if the circle is the perfect shape for your character, you can also use boxes and capsules, but the algorithms are a little more complex. Finally if you only have 2 types of objects (character and obstacles) instead using an id you can use tags (character or obstacle) instead a different ID for every object.

Answered By: Farengar