Bottom Page

Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
 [PyGame] Making Player Sprite Ricochet of walls
I have vector ("Asteroids") type movement and I want my player sprite to bounce of things they bump into.

It is best to use 2 .spritecollide checks, one for vertical walls and one for horizontal? That won't work for a tile that can be hit from both angles.

I'm sure it's been done a million times, but I could sure use help finding where to start. Can I somehow check what side of a tile is hit?
You can easily adapt a pong game to use. As the ball is meant to bounce of the top and bottom, and two paddles on right and left.

The part that actually changes the velocity is
 self.vel[1] *= -1
 self.vel[0] *= -1
which reverse the velocity of the objects direction once it hits another object, making it appear to bounce off objects like a ball.

By tile; do you mean tilemaps or do you mean a 4 sided object?

It doesnt matter what the angle the object that is being hit at. The angle of the object in which is initiating the hit, is. Unless you want to take into account of that angle too.
Use clipping rect size. Determine which is smaller. Width or Height. If even then you can says it hit both angles.
Then you can compare rect.centerx or rect.centery to collision rect.centerx or rect.centery.

I like displacement the best. It the easiest to start with.
import os
import pygame
import random
from pygame.sprite import Sprite, Group, spritecollide

class Block(Sprite):
    def __init__(self, image, position, group):
        # Reference the image. Doesn't make new one
        self.image = image
        # Collision group reference = group
        self.rect = image.get_rect()
        self.rect.topleft = position
        # Hold the floats position. Since rect only does int
        self.position = pygame.Vector2(position)
        self.direction = pygame.Vector2(
            (random.randint(0, 600) - 300) / 100 / Game.fps,
            (random.randint(0, 600) - 300) / 100 / Game.fps)

    def update(self):
        self.position += self.direction *
        self.rect.topleft = self.position

    def collision(self):
        blocks = spritecollide(self,, False)
        for block in blocks:
            if block != self:
                # Simple displacement
                w, h = self.rect.clip(block.rect).size
                if w < h:
                    if self.rect.centerx > block.rect.centerx:
                        self.rect.left = block.rect.right
                        self.position.x = self.rect.x
                        self.direction.x = abs(self.direction.x)
                        block.direction.x = -abs(block.direction.x)
                        self.rect.right = block.rect.left
                        self.position.x = self.rect.x
                        self.direction.x = -abs(self.direction.x)
                        block.direction.x = abs(block.direction.x)
                    if self.rect.centery > block.rect.centery:
               = block.rect.bottom
                        self.position.y = self.rect.y
                        self.direction.y = abs(self.direction.y)
                        block.direction.y = -abs(block.direction.y)
                        self.rect.bottom =
                        self.position.y = self.rect.y
                        self.direction.y = -abs(self.direction.y)
                        block.direction.y = abs(block.direction.y)

    def wall_bounce(self):
        clamp = self.rect.clamp(Game.rect)

        if clamp.x != self.rect.x:
            self.rect.x = clamp.x
            self.position.x = clamp.x
            self.direction.x = -self.direction.x

        if clamp.y != self.rect.y:
            self.rect.y = clamp.y
            self.position.y = clamp.y
            self.direction.y = -self.direction.y

class Scene:
    def __init__(self):
        self.sprites = Group()

    def add_blocks(self, number):
        for n in range(number):
            image = random.choice(self.block_images)
            position = (random.randint(30, Game.rect.width - 30),
                        random.randint(30, Game.rect.height - 30))

            block = Block(image, position, self.sprites)

    def create_block_images(self):
        self.block_images = []

        for x in range(20):
            color = pygame.Color('black')
            while color.r < 100 and color.g < 100 and color.b < 100:
                color.r = random.randint(0, 255)
                color.g = random.randint(0, 255)
                color.b = random.randint(0, 255)

            low, high = 20, 30
            size = random.randint(low, high), random.randint(low, high)
            surface = pygame.Surface(size)

    def draw(self, surface):

# Namespace for pygame game loop
class Game:
    def setup(cls, title, width, height):
        # Basic pygame setup
        cls.surface = pygame.display.set_mode((width, height))
        cls.rect = cls.surface.get_rect()
        cls.clock = pygame.time.Clock() = 0
        cls.fps = 30

        cls.scene = Scene()

    def mainloop(cls):
        cls.running = True
        while cls.running:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    cls.running = False
                elif event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_ESCAPE:
                        cls.running = False

   = cls.clock.tick(cls.fps)

if __name__ == '__main__':
    os.environ['SDL_VIDEO_CENTERED'] = '1'
    Game.setup('Block Bouncing', 800, 600)
99 percent of computer problems exists between chair and keyboard.
(Jun-03-2019, 12:28 PM)metulburr Wrote: By tile; do you mean tilemaps or do you mean a 4 sided object?

It's txt file that is read and translated into a map of 32X32 tile sprites. I'll look at the pong code and see what it is, but if there are sprite groups for walls and paddles, that won't be useful if there is a single tile in the room that can be hit from all sides. Will it??

(Jun-03-2019, 02:03 PM)Windspar Wrote: Use clipping rect size. Determine which is smaller. Width or Height. If even then you can says it hit both angles.
Then you can compare rect.centerx or rect.centery to collision rect.centerx or rect.centery.

ok... I think i sort of get it. Long story short, if the smallest of the remainders of the rect.centery or rect.centerx comparison will determine if it is a vertical surface or a horizontal surface?

I'll look into all of this and see where I get.

Thanks guys
Here. I try to explain it better.
Displacement collision. Moving Object hit a Stationary Object.
1. get the clipping rect size
width, height = object.rect.clip(collision.rect).size
2. Determine the Axis.
if width < height:
    # X Axis
    # Y Axis
3. Determine which side. Displace object and Change direction.
# Determine Axis X or Y
if width < height:
    # X Axis
    # Determine which side
    if object.rect.centerx > collision.rect.centerx:
        # Displace object
        object.rect.left = collision.rect.right
        # Displace object
        object.rect.right = collision.rect.left

    # Change direction
    object.direction.x = -object.direction.x
    # Y Axis
    # ...
99 percent of computer problems exists between chair and keyboard.

Top Page

Possibly Related Threads...
Thread Author Replies Views Last Post
  [PyGame] How to create a sprite in a different module I_like_pepperjack 1 254 Aug-15-2019, 10:23 AM
Last Post: metulburr
  Need help making a sprite GalaxyCoyote 4 298 Aug-11-2019, 09:12 PM
Last Post: metulburr
  creating sprite mask pfaber11 5 490 Jun-12-2019, 09:39 PM
Last Post: pfaber11
  [PyGame] assigning rect to sprite pfaber11 1 257 May-18-2019, 05:39 PM
Last Post: metulburr
  moving a sprite pfaber11 3 478 May-15-2019, 12:52 PM
Last Post: pfaber11
  Raycasting(again) walls with different heights robie972003 0 282 Mar-23-2019, 01:18 AM
Last Post: robie972003
  [PyGame] Need Help With Sprite ghost0fkarma 2 883 Jan-09-2018, 02:14 PM
Last Post: ghost0fkarma

Forum Jump:

Users browsing this thread: 1 Guest(s)