3

So I have this obstacle that I want my sprite to collide with and it is at a particular angle.In this case, we are measuring from the positive x-axis to the top of the rectangle and in this instance it is 333.02 degrees with respect to the positive x-axis or 63.02 degrees with respect to the negative y-axis. So my issue is that how do I set up my pygame sprite to properly collide with the angle rectangle obstacle? Pygame sprite rectangles have no rotation attribute (to my knowledge) and I can't just say, "Hey when the right corner of my sprite collides with top, etc" because of this lack of rotation. My collisions work great for horizontal and even vertical surfaces but I am stuck on how to collide with angled obstacles.

Here is my collision code right now. It uses vectors and checks both x and y independently to see if anything is there. And below is a picture of the object I want to collide with created in the Tile Map Editor. It is at an angle of 333.02 degrees like I mentioned before. I also included a rough sketch of the axis in case that is relevant.

    def update(self):
        self.animate()
        self.acc = vec(0, PLAYER_MASS * GRAVITY)
        self.move()

        # Equations of Motion
        self.acc.x += self.vel.x * PLAYER_FRICTION
        self.vel += self.acc

        # Collision check in all 4 directions
        self.pos.x += (
            self.vel.x + 0.5 * self.acc.x * self.game.dt
        )  # Update x component (Frame-independent motion)
        if abs(self.vel.x) < PLAYER_VELX_EPSILON:
            self.vel.x = 0

        self.rect.x = self.pos.x
        hits = pg.sprite.spritecollide(self, self.game.platforms, False)
        for hit in hits:  # Horizontal collision
            if self.vel.x > 0:  # Rightward motion
                self.rect.right = hit.rect.left
            elif self.vel.y < 0:  # Leftward motion
                self.rect.left = hit.rect.right
            self.pos.x = self.rect.x  # Update true postion

        self.pos.y += self.vel.y + 0.5 * self.acc.y * self.game.dt  # Update y component
        self.rect.y = self.pos.y + 5

        # This prevents double jumping
        if self.vel.y > 0:
            self.onGnd = False

        hits = pg.sprite.spritecollide(self, self.game.platforms, False)
        for hit in hits:  # Vertical Collision
            if self.vel.y > 0:  # Downward motion
                self.rect.bottom = hit.rect.top
                self.vel.y = 0
                self.onGnd = True
            elif self.vel.y < 0:  # Upward motion
                self.rect.top = hit.rect.bottom
                self.vel.y = 0
            self.pos.y = self.rect.y  # Update true postion

        # Limit Player's movement
        if self.rect.bottom > HEIGHT:
            self.vel.y = 0
            self.rect.bottom = HEIGHT
            self.pos.y = self.rect.y

This is the invisible object in the Tiled Map Editor that I want to collide with so I can go up this slope. The angle says 333.02 with respect to the positive x axis (to the right when facing the screen).

Rough Drawing of the axis and angles associated.

Any help on this problem would be greatly appreciated!

MBA20
  • 73
  • 5

2 Answers2

1

The answer is coordinate translation. Imagine that the rotated object had its own coordinate system, where x runs along the bottom of the rectangle, and y up the side on the left. Then, if you could find the position of your sprite in that coordinate system, you could check for collisions the way you normally would with an unrotated rectangle, i.e., if x >=0 and x <= width and y >=0 and y <= height then there's a collision.

But how do you get the translated coordinates? The answer is matrices. You can use 2d transformation matrices to rotate, scale and translate vectors and coordinates. Unfortunately my experience with these types of transformations is in C#, not python, but this page for instance provides examples and explanations in python using numpy.

Note that this is quite simply the way 2d (and 3d) games work - matrix transformations are everywhere, and are the way to do collision detection of rotated, scaled and translated objects. They are also how sprites are moved, rotated etc: the pygame transform module is a matrix transformation module. So if the code and explanations looks scary at first glance, it is worth investing the time to understand it, since it's hard to write games without it beyond a certain point.

I'm aware this is not a full answer to your question, since I haven't given you the code, but it's too long for a comment, and hopefully points you in the right direction. There's also this answer on SO, which provides some simple code.

EDIT: just to add some further information, a full collision detection routine would check each collidable pixel's position against the object. This may be the required approach in your game, depending on how accurate you need the collision detection to be. That's what I do in my game Magnetar (https://www.youtube.com/watch?v=mbgr2XiIR7w), which collides multiple irregularly shaped sprites at arbitrary positions, scales and rotations.

I note however that in your game there's a way you could possibly "cheat" if all you need is collision detection with some angled slopes. That is you could have a data structure which records the 'corners' of the ground (points it change angle), and then use simple geometry to determine if an x,y point is below or above ground. That is, you would take the x value of a point and check which segment of the ground it is over. If it is over the sloped ground, work out how far along the x axis of the sloped ground it is, then use the sin of the angle times this value to work out the y value of the slope at that position, and if that is greater than the y value of the point you are checking, you have a collision.

see sharper
  • 11,505
  • 8
  • 46
  • 65
  • Thanks for the suggestion! So I got out some scratch paper and worked out the rotation matrix for this case. I have [cos(theta),-sin(theta),0;sin(theta),cos(theta),0;0,0,1] for this obstacles ref frame. Now my follow up question is when do I know to transform my coordinates to a local frame? If I am on a horizontal surface well the local coord. of the floor is the same as the sprite global coor. But how does my sprite "know" a ramp is coming up, let me transform my coord to it's local and compare. Sorry if that's a naive question, I didn't know how else to word it – MBA20 Jul 16 '20 at 05:03
  • You should have a list of objects on the screen that you want to check for collisions against. Note that working out your own transformation matrices is probably unnecessary. I am sure there are python libraries for 2d transforms. And you will need to take into account translation (offset) as well as rotation. – see sharper Jul 16 '20 at 05:13
0

The answer from seesharper may be the right way to go. I have no experience with that but it looks interesting and I will have to go read the links and learn something about this approach. I will still provide my immediate reaction to the question before I saw his answer though.

You could use pygames mask and mask collision detection routines. Create a mask that was the shape of the angled rectangle/platform and use the methods to detect collisions with that.

If you add a self.mask attribute to your Sprite subclass, the sprites will automatically use that mask in the collision detection.

Glenn Mackintosh
  • 2,765
  • 1
  • 10
  • 18
  • Yes I was considering just using a mask but I was worried about performance especially since ramps will be common on the screen. I think I will reserve mask collisions when precision is paramount like an enemy attacking the sprite for example. – MBA20 Jul 16 '20 at 05:06