1

Je suis en train de construire un coureur de haut en bas, mais je suis en train d'obtenir la détection de collision avec des objets précis carrés statiques.box-Rotating rects dans pygame

Quelle serait la meilleure méthode pour cela?

est ici une partie de mon code:

class VehicleSprite(Entity): 
    MAX_FORWARD_SPEED = 18 
    MAX_REVERSE_SPEED = 1 

    def __init__(self, images, position): 
     Entity.__init__(self) 
     self.src_images = images 
     self.images = images 
     self.rect = self.images.get_rect(center=position) 
     self.position = pygame.math.Vector2(position) 
     self.velocity = pygame.math.Vector2(0, 0) 
     self.speed = self.direction = 0 
     self.k_left = self.k_right = self.k_down = self.k_up = 0 
     self.width = 28 
     self.height = 64 
     self.numImages = 8 
     self.cImage = 0 

    def update(self, time): 
     self.speed += self.k_up + self.k_down 
     # To clamp the speed. 
     self.speed = max(-self.MAX_REVERSE_SPEED, 
         min(self.speed, self.MAX_FORWARD_SPEED)) 

     # Degrees sprite is facing (direction) 
     self.direction += (self.k_right + self.k_left) 
     rad = math.radians(self.direction) 
     self.velocity.x = -self.speed*math.sin(rad) 
     self.velocity.y = -self.speed*math.cos(rad) 
     self.position += self.velocity 
     if (self.cImage >= self.numImages - 1): 
      self.cImage = 0 
     else: 
      self.cImage += 1 
     self.images = pygame.transform.rotate(self.src_images, self.direction) 
     self.rect = self.images.get_rect(center=self.position) 

    def render(self, screen, camera): 
     screen.blit(self.images, (self.rect.topleft+camera), (self.cImage*self.width, 0, self.width, self.height)) 

Et voici le bit de détection de collision:

for sprite in all_sprites: 
    for crate in crates: 
     crate.update(time) 
    for crate in crates: 
     screen.blit(power_img, crate.position+camera) 
     # Collision with crate 
     if ((purple_bike.position.x >= crate.x) and purple_bike.position.x < (crate.x + crate.width) or (purple_bike.position.x + purple_bike.width) > crate.x and purple_bike.position.x + purple_bike.width < crate.x + crate.width) and ((purple_bike.position.y > crate.y) and purple_bike.position.y < (crate.y + crate.height) or (purple_bike.position.y + purple_bike.height) > crate.y and (purple_bike.position.y + purple_bike.height) < (crate.y + crate.height)): 
      print("Hit") 
      purple_bike.speed = 0 
+0

Veuillez décrire les problèmes en détail. La question est plutôt floue. – skrx

+0

Désolé! L'image tourne bien, mais je crois que le 'hit box' ne tourne pas avec, donc quand je heurte d'autres objets, la collision est désactivée. Je veux juste quelque chose qui puisse représenter avec précision une collision entre une image pivotée et une image statique. – Bubblehead333

+0

Ce problème n'est pas si facile à résoudre, car les 'pygame.Rect' ne peuvent pas être pivotés. Ainsi, chaque fois que vous faites pivoter une image et que vous en générez un nouveau, la taille du rect change, car elle doit contenir complètement l'image. Une solution très rudimentaire serait d'utiliser un plus petit rect, mais pour un jeu de course qui ne serait pas une bonne solution. Je pense qu'une bonne solution pour vous serait d'utiliser [masques pixellisés] (http://renesd.blogspot.de/2017/03/pixel-perfect-collision-detection-in.html) pour faire la détection de collision. – skrx

Répondre

0

Voici un exemple qui vous montre comment utiliser Pymunk conjointement avec pygame pour obtenir hitboxes/rects de collision en rotation. Les contours verts sont les bords des formes de Pymunk et les rects bleus sont les rects de pygame dont le seul but est de stocker la position de blit du sprite. Il faut du temps pour se familiariser avec Pymunk, mais c'est l'une des meilleures façons d'implémenter des hitboxes rotatives, de forme arbitraire, dans pygame.

import sys 
import math 

import pygame as pg 
import pymunk as pm 
from pymunk import Vec2d 


def flipy(p): 
    """Convert chipmunk coordinates to pygame coordinates.""" 
    return Vec2d(p[0], -p[1]+600) 


class Player(pg.sprite.Sprite): 

    def __init__(self, pos, space, mass=0.3): 
     super().__init__() 
     self.image = pg.Surface((52, 72), pg.SRCALPHA) 
     pg.draw.polygon(self.image, pg.Color('steelblue2'), 
         [(1, 72), (26, 1), (51, 72)]) 
     self.rect = self.image.get_rect(center=pos) 
     self.orig_image = self.image 
     # The verts for the Pymunk shape in relation 
     # to the sprite's center. 
     vertices = [(0, 36), (26, -36), (-26, -36)] 
     # Create the physics body and shape of this object. 
     moment = pm.moment_for_poly(mass, vertices) 
     self.body = pm.Body(mass, moment) 
     self.shape = pm.Poly(self.body, vertices, radius=3) 
     self.shape.friction = .8 
     self.shape.elasticity = .2 
     self.body.position = pos 
     # Add them to the Pymunk space. 
     self.space = space 
     self.space.add(self.body, self.shape) 

     self.accel_forw = False 
     self.accel_back = False 
     self.turn_left = False 
     self.turn_right = False 
     self.topspeed = 1790 
     self.angle = 0 

    def handle_event(self, event): 
     if event.type == pg.KEYDOWN: 
      if event.key == pg.K_w: 
       self.accel_forw = True 
      if event.key == pg.K_a: 
       self.turn_left = True 
      if event.key == pg.K_d: 
       self.turn_right = True 
      if event.key == pg.K_s: 
       self.accel_back = True 
     if event.type == pg.KEYUP: 
      if event.key == pg.K_w: 
       self.accel_forw = False 
      if event.key == pg.K_a: 
       self.turn_left = False 
      if event.key == pg.K_d: 
       self.turn_right = False 
      if event.key == pg.K_s: 
       self.accel_back = False 

    def update(self, dt): 
     # Accelerate the pymunk body of this sprite. 
     if self.accel_forw and self.body.velocity.length < self.topspeed: 
      self.body.apply_force_at_local_point(Vec2d(0, 624), Vec2d(0, 0)) 
     if self.accel_back and self.body.velocity.length < self.topspeed: 
      self.body.apply_force_at_local_point(Vec2d(0, -514), Vec2d(0, 0)) 
     if self.turn_left and self.body.velocity.length < self.topspeed: 
      self.body.angle += .1 
      self.body.angular_velocity = 0 
     if self.turn_right and self.body.velocity.length < self.topspeed: 
      self.body.angle -= .1 
      self.body.angular_velocity = 0 
     # Rotate the image of the sprite. 
     self.angle = self.body.angle 
     self.rect.center = flipy(self.body.position) 
     self.image = pg.transform.rotozoom(
      self.orig_image, math.degrees(self.body.angle), 1) 
     self.rect = self.image.get_rect(center=self.rect.center) 


class Wall(pg.sprite.Sprite): 

    def __init__(self, pos, verts, space, mass, *sprite_groups): 
     super().__init__(*sprite_groups) 
     # Determine the width and height of the surface. 
     width = max(v[0] for v in verts) 
     height = max(v[1] for v in verts) 
     self.image = pg.Surface((width, height), pg.SRCALPHA) 
     pg.draw.polygon(self.image, pg.Color('sienna1'), verts) 
     self.rect = self.image.get_rect(topleft=pos) 

     moment = pm.moment_for_poly(mass, verts) 
     self.body = pm.Body(mass, moment, pm.Body.STATIC) 
     # Need to transform the vertices for the pymunk poly shape, 
     # so that they fit to the image vertices. 
     verts2 = [(x, -y) for x, y in verts] 
     self.shape = pm.Poly(self.body, verts2, radius=2) 
     self.shape.friction = 1.0 
     self.shape.elasticity = .52 
     self.body.position = flipy(pos) 
     self.space = space 
     self.space.add(self.shape) 


class Game: 

    def __init__(self): 
     self.done = False 
     self.screen = pg.display.set_mode((800, 600)) 
     self.clock = pg.time.Clock() 
     self.bg_color = pg.Color(60, 60, 60) 

     self.space = pm.Space() 
     self.space.gravity = Vec2d(0.0, 0.0) 
     self.space.damping = .4 

     self.all_sprites = pg.sprite.Group() 

     self.player = Player((100, 300), self.space) 
     self.all_sprites.add(self.player) 
     # Position-vertices tuples for the walls. 
     vertices = [ 
      ([80, 120], ((0, 0), (100, 0), (70, 100), (0, 100))), 
      ([400, 250], ((20, 40), (100, 0), (80, 80), (10, 100))), 
      ([200, 450], ((20, 40), (300, 0), (300, 120), (10, 100))), 
      ([760, 10], ((0, 0), (30, 0), (30, 420), (0, 400))), 
      ] 

     for pos, verts in vertices: 
      Wall(pos, verts, self.space, 1, self.all_sprites) 

    def run(self): 
     while not self.done: 
      self.dt = self.clock.tick(30)/1000 
      self.handle_events() 
      self.run_logic() 
      self.draw() 

    def handle_events(self): 
     for event in pg.event.get(): 
      if event.type == pg.QUIT: 
       self.done = True 

      self.player.handle_event(event) 

    def run_logic(self): 
     self.space.step(1/60) 
     self.all_sprites.update(self.dt) 

    def draw(self): 
     self.screen.fill(self.bg_color) 
     self.all_sprites.draw(self.screen) 
     # Debug draw - Pymunk shapes are green, pygame rects are blue. 
     for obj in self.all_sprites: 
      shape = obj.shape 
      ps = [flipy(pos.rotated(shape.body.angle) + shape.body.position) 
        for pos in shape.get_vertices()] 
      ps.append(ps[0]) 
      pg.draw.rect(self.screen, pg.Color('blue'), obj.rect, 2) 
      pg.draw.lines(self.screen, (90, 200, 50), False, ps, 2) 

     pg.display.flip() 


if __name__ == '__main__': 
    pg.init() 
    Game().run() 
    pg.quit() 
    sys.exit() 
+1

Brilliant merci skrx qui est très concis et serviable, je vais essayer! – Bubblehead333

0

Puisqu'ils sont sprites, vous pouvez vérifier les collisions en utilisant pygame.sprite.collide_rect(sprite1, sprite2). Si les deux sprites sont en collision (plus précisément, leurs rect), l'instruction est True. Alors:

if pygame.sprite.collide_rect(sprite1, sprite2): 
    print("Hit") 
    purple_bike.speed = 0 

Si elles sont dans le même groupe, vous pouvez utiliser pygame.sprite.spritecollide(). Dans les docs, ses paramètres sont:

spritecollide(sprite, group, dokill, collided = None) 

Si vous voulez que le sprite à enlever, définissez la troisième paarameter (doKill) à True. Sinon, réglez-le sur False.

+0

Merci pour votre réponse, cela fonctionne mieux qu'avant, mais la boîte à succès semble être encore un peu loin, je pense qu'il peut avoir quelque chose à voir avec la rotation, peut-être la boîte à succès du joueur ne tourne pas alors que l'image Est-ce que? Je semble frapper des 'boîtes imaginaires' au-dessus du sprite de caisse! – Bubblehead333

+0

@ Bubblehead333 Cela pourrait être la rotation, mais avez-vous assigné 'self.rect' à vos sprites? –

+0

J'ai, je pense que ma question a été répondue, moi-même.rect apparemment ne tourne pas avec l'image du sprite, donc la collision est désactivée. J'ai été informé d'une bibliothèque à vérifier pour créer une physique précise pour ma scène. Merci pour l'aide! – Bubblehead333