1

I have a rotating square that moves around the screen and am tasked with (1) Setting boundaries to keep it within the screen and (2) Collision detection with a similar rotating square. I require help with the first issue.

P.S. To my teacher (if you're reading this), I have been going at this specific problem for over 6 hours already and must resort to consulting others for help. Some problems can sustain the assault of sustained thinking, failure is not delayed success - it's just failure, skill is not 99% effort + 1% talent - it's the other way around, etc. etc.

Note: Sorry for the poor formatting. It's my first time using Stack Overflow; was using Y!A before but there aren't many people in the programming section there

import pygame 
import random

pygame.init()  # Initialize py-game
screen = pygame.display.set_mode((800, 500))  # Create screen
color = (255, 240, 245)

# Initial Coordinates
objectX, objectY = random.randint(100, 700), random.randint(100, 400)

fps = pygame.time.Clock()
velocity = [8, 6]  # speed
velocity2 = [6, 8]
size = 0  # size
drag = False

# Rotating the object
sat_orig = pygame.Surface((70, 70))
sat_orig.set_colorkey((0, 0, 0))
sat_orig.fill(color)
sat = sat_orig.copy()
sat.set_colorkey((0, 0, 0))
rect = sat.get_rect()
rect.center = (800 // 2, 500 // 2)
rotation = 0
rotation_speed = 2


# --------------------------------------------------------------------------


running = True
while running:  # Game Loop

    screen.fill((0, 0, 0))
    pygame.display.set_caption('Collision Interactions ' + str(pygame.mouse.get_pos()))  # caption

    # exit button
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

        elif event.type == pygame.MOUSEBUTTONUP:
            if event.button == 1:
                drag = False
                rotation_speed = 2

        elif event.type == pygame.MOUSEBUTTONDOWN:
            if event.button == 1:
                drag = True
                rotation_speed = 0

    size = 70

    old_center = rect.center
    rotation = (rotation + rotation_speed) % 360
    new_image = pygame.transform.rotate(sat_orig, rotation)
    rect = new_image.get_rect()
    rect.center = old_center
    screen.blit(new_image, (objectX, objectY))
    objectArea = rect

    ############
    # Movement #
    ############
    if drag:  # Manual
        if objectArea.collidepoint(event.pos):
            objectX, objectY = pygame.mouse.get_pos()
            objectX -= size // 2
            objectY -= size // 2
    else:  # Automatic
        objectX += velocity[0]
        objectY += velocity[1]

        ##############
        # Boundaries #
        ##############
        if objectX + size > 800 or objectX + size < 0:
            velocity[0] = -velocity[0]
        if objectY + size > 500 or objectY + size < 0:
            velocity[1] = -velocity[1]

    fps.tick(80)

    pygame.display.update()  # Displays updates
Benjamin Luo
  • 131
  • 8

1 Answers1

1

The axis aligned width and height of the rotated rectangle is larger than the size of the original recatngle. The boundaries of the rotated rectangle are stored in objectArea. Use the rotated rectangle for the collision test:

while running:  # Game Loop
    # [...]

    if drag:  # Manual
        # [...]

    else:  # Automatic

        objectX += velocity[0]
        objectY += velocity[1]

        # set new location of rotated rectangle
        objectArea.topleft = (objectX, objectY)

        # bounce on bounds
        if objectArea.right > 800 or objectArea.left < 0:
            velocity[0] = -velocity[0]
        if objectArea.bottom > 500 or objectArea.top < 0:
            velocity[1] = -velocity[1]#

Note, since the movement of the rectangle is more than 1, it may exceed the bound slightly. To prevent that the rectangle can be adjusting to the bounds:

while running:  # Game Loop
    # [...]

    if drag:  # Manual
        # [...]

        objectX += velocity[0]
        objectY += velocity[1]
        objectArea.topleft = (objectX, objectY)

        if objectArea.left < 0:
            velocity[0] *= -1
            objectX = 0
        elif objectArea.right > screen.get_width():
            velocity[0] *= -1
            objectX = screen.get_width() - objectArea.width 

        if objectArea.top < 0:
            velocity[1] *= -1
            objectY = 0
        elif objectArea.bottom > screen.get_height():
            velocity[1] *= -1
            objectY = screen.get_height() - objectArea.height

I recommend to draw the rectangle (and the entire) scene at the end of the main application loop. Thus the current position of the rectangle is visualized, rather than the position before the movement:

while running:

    # handle the events
    for event in pygame.event.get():
        # [...]


    # do all the computations
    # [...]

    # draw the scene
    screen.fill((0, 0, 0))
    screen.blit(new_image, (objectX, objectY))
    pygame.display.update()  # Displays updates
    fps.tick(80)

Complete example:

import pygame 
import random

pygame.init()  # Initialize py-game
screen = pygame.display.set_mode((800, 500))  # Create screen
color = (255, 240, 245)

# Initial Coordinates
objectX, objectY = random.randint(100, screen.get_width()-100), random.randint(100, screen.get_height()-100)

fps = pygame.time.Clock()
velocity = [8, 6]  # speed
velocity2 = [6, 8]
size = 0  # size
drag = False

# Rotating the object
sat_orig = pygame.Surface((70, 70))
sat_orig.set_colorkey((0, 0, 0))
sat_orig.fill(color)
sat = sat_orig.copy()
sat.set_colorkey((0, 0, 0))
rect = sat.get_rect()
rect.center = (screen.get_width() // 2, screen.get_height() // 2)
rotation = 0
rotation_speed = 2


# --------------------------------------------------------------------------

pygame.display.set_caption('Collision Interactions ' + str(pygame.mouse.get_pos()))  # caption

running = True
while running:  # Game Loop

    # exit button
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

        elif event.type == pygame.MOUSEBUTTONUP:
            if event.button == 1:
                drag = False
                rotation_speed = 2

        elif event.type == pygame.MOUSEBUTTONDOWN:
            if event.button == 1:
                drag = True
                rotation_speed = 0

    size = 70
    old_center = rect.center
    rotation = (rotation + rotation_speed) % 360
    new_image = pygame.transform.rotate(sat_orig, rotation)
    rect = new_image.get_rect()
    rect.center = old_center
    objectArea = rect

    ############
    # Movement #
    ############
    if drag:  # Manual
        if objectArea.collidepoint(event.pos):
            objectX, objectY = pygame.mouse.get_pos()
            objectX -= size // 2
            objectY -= size // 2
    else:  # Automatic

        objectX += velocity[0]
        objectY += velocity[1]
        objectArea.topleft = (objectX, objectY)

        if objectArea.left < 0:
            velocity[0] *= -1
            objectX = 0
        elif objectArea.right > screen.get_width():
            velocity[0] *= -1
            objectX = screen.get_width() - objectArea.width 

        if objectArea.top < 0:
            velocity[1] *= -1
            objectY = 0
        elif objectArea.bottom > screen.get_height():
            velocity[1] *= -1
            objectY = screen.get_height() - objectArea.height 

    screen.fill((0, 0, 0))
    screen.blit(new_image, (objectX, objectY))
    pygame.display.update()  # Displays updates
    fps.tick(80)

For the collision of 2 rotated rectangles, I recommend to use pygame.mask.Mask objects and pygame.mask.Mask.overlap() or pygame.sprite.Sprite and pygame.sprite.collide_mask().

See further:
Collision between masks in pygame
How can I made a collision mask?
Pygame mask collision

Rabbid76
  • 202,892
  • 27
  • 131
  • 174
  • 1
    Thank you so much! (I would upvote but I'm restricted since my reputation < 15). You explained it perfectly and pointed out the exact issue with the velocity that I encountered. I struggled with the masks and sprites collisions but will look into the links you kindly provided – Benjamin Luo Apr 08 '20 at 11:27