Python Forum
[PyGame] Making Player Sprite Ricochet of walls
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
[PyGame] Making Player Sprite Ricochet of walls
#1
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?
Reply
#2
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.
https://github.com/metulburr/pong/blob/m...all.py#L53

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.
Recommended Tutorials:
Reply
#3
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.
Example.
import os
import pygame
import random
from pygame.sprite import Sprite, Group, spritecollide

class Block(Sprite):
    def __init__(self, image, position, group):
        Sprite.__init__(self)
        # Reference the image. Doesn't make new one
        self.image = image
        # Collision group reference
        self.group = 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 * Game.delta
        self.rect.topleft = self.position
        self.collision()

    def collision(self):
        self.wall_bounce()
        blocks = spritecollide(self, self.group, 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)
                    else:
                        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)
                else:
                    if self.rect.centery > block.rect.centery:
                        self.rect.top = block.rect.bottom
                        self.position.y = self.rect.y
                        self.direction.y = abs(self.direction.y)
                        block.direction.y = -abs(block.direction.y)
                    else:
                        self.rect.bottom = block.rect.top
                        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()
        self.create_block_images()
        self.add_blocks(15)

    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)
            block.add(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)
            surface.fill(color)
            self.block_images.append(surface)

    def draw(self, surface):
        self.sprites.draw(surface)
        self.sprites.update()

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

        cls.scene = Scene()

    @classmethod
    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.surface.fill(pygame.Color('black'))
            cls.scene.draw(cls.surface)
            pygame.display.flip()
            cls.delta = cls.clock.tick(cls.fps)

if __name__ == '__main__':
    pygame.init()
    os.environ['SDL_VIDEO_CENTERED'] = '1'
    Game.setup('Block Bouncing', 800, 600)
    Game.mainloop()
    pygame.quit()
99 percent of computer problems exists between chair and keyboard.
Reply
#4
(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
Reply
#5
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
else:
    # 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
    else:
        # Displace object
        object.rect.right = collision.rect.left

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


Possibly Related Threads…
Thread Author Replies Views Last Post
  [PyGame] pygame, help with making a function to detect collision between player and enemy. Kris1996 3 3,282 Mar-07-2020, 12:32 PM
Last Post: Kris1996
  [PyGame] Sprite image.get_rect() moves sprite to 0, 0 michael1789 2 4,551 Dec-13-2019, 08:37 PM
Last Post: michael1789
  How to place walls in random dungeon ? michael1789 5 3,957 Nov-11-2019, 12:12 AM
Last Post: michael1789
  Sprite not rendering Clunk_Head 2 2,095 Oct-03-2019, 11:27 AM
Last Post: Clunk_Head
  Need help making a sprite GalaxyCoyote 4 3,184 Aug-11-2019, 09:12 PM
Last Post: metulburr
  moving a sprite pfaber11 3 2,545 May-15-2019, 12:52 PM
Last Post: pfaber11
  Raycasting(again) walls with different heights robie972003 0 2,433 Mar-23-2019, 01:18 AM
Last Post: robie972003
  [PyGame] Need Help With Sprite ghost0fkarma 2 3,251 Jan-09-2018, 02:14 PM
Last Post: ghost0fkarma

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020