Rotating/Tilting Platforms?

SQGTDev

Member
My platform engine is almost complete and I've been working on one last feature to add: rotating platforms.


My platform engine is an imitation of the one used in the New Super Mario Bros. games. To handle slopes, the player object uses a single pixel (in Game Maker, a collision_point at the player's bottom center) to collide with them. All platforms move in their Begin Step, and the player gets the platform's ID, stores it as "floorID," and moves with it in the player's Step. I have all sorts of moving and sloped grounds working properly, but spinning platforms are giving me a lot of trouble.
My theory right now is to make a circle using lengthdir functions based on how far the player's slope collider (which is just at objPlayer.x/objPlayer.y) is from the platform's pivot point but I'm just lost.

Code:
///Moving Platforms
if (floorID != 0)
{
    if instance_exists(floorID)
    {
        var floorIDXPos, floorIDYPos, floorIDAnglePos, pivotPointDistance;
        floorIDXPos = floorID.x - floorID.xprevious;
        floorIDYPos = floorID.y - floorID.yprevious;
        floorIDAnglePos = floorID.anglePrevious;
        pivotPointDistance = point_distance(x, y, floorID.x, floorID.y);
        
        repeat(abs(floorIDYPos))
            y += sign(floorIDYPos);
        repeat(abs(floorIDXPos))
            x += sign(floorIDXPos);
}
 

Attachments

TheouAegis

Member
If precision isn't really a concern, why not use a collision_line from the platform's left edge to the right edge. The left edge is at (lengthdir_x(sprite_width/2,image_angle+180), lengthdir_y(sprite_width/2,image_angle+180). The right edge is the same, but without the +180s.
 

SQGTDev

Member
If precision isn't really a concern, why not use a collision_line from the platform's left edge to the right edge. The left edge is at (lengthdir_x(sprite_width/2,image_angle+180), lengthdir_y(sprite_width/2,image_angle+180). The right edge is the same, but without the +180s.
It took me a second to process that but I get what you're saying. I think I would need precision to be able to have any kind of surface be rotatable so that I can have stuff like circle platforms, long tilting platforms, etc.
What I want to do is basically move the character around a hypothetical circle. The radius of said circle would be the point_distance from the character's bottom center to the platform's pivot point, and I would also need to somehow get the angle of the character from the pivot point since they usually won't be standing directly over the center.
 

TheouAegis

Member
For the circle, take the point of the player's intersection (mid-bottom), calculate the angle from the circle's focal point to the player's collision. Calculate the lengthdir_x and lengthdir_y values for that angle. Then do the same for the angle+rotation speed. Subtract the two values and add that to the player's x and y coordinates.
 

SQGTDev

Member
For the circle, take the point of the player's intersection (mid-bottom), calculate the angle from the circle's focal point to the player's collision. Calculate the lengthdir_x and lengthdir_y values for that angle. Then do the same for the angle+rotation speed. Subtract the two values and add that to the player's x and y coordinates.
I tried this method and after a lot of fiddling around, it almost works. Unfortunately the player character becomes detached from the object a little bit meaning he jitters around a lot.

This is it what it looks like right now.

Code:
///Moving Platforms
if (floorID != 0)
{
    if instance_exists(floorID)
    {
        //Init Variables
        var floorIDXPos, floorIDYPos, floorIDAnglePos, pivotPointDistance, playerAngleFromPivotPoint, angleX, angleY, angleWRotationX, angleWRotationY;
        floorIDXPos = floorID.x - floorID.xprevious;
        floorIDYPos = floorID.y - floorID.yprevious;
        floorIDAnglePos = floorID.anglePrevious;
        
        //Get distance from collision point to platform pivot point
        pivotPointDistance = point_distance(x + floorSensorX, y + floorSensorY, floorID.x, floorID.y);
        //Get angle of collision point to pivot point
        playerAngleFromPivotPoint = point_direction(x + floorSensorX, y + floorSensorY, floorID.x, floorID.y);
        //Get lengthdir_x and y values from angle
        angleX = lengthdir_x(pivotPointDistance, playerAngleFromPivotPoint);
        angleY = lengthdir_y(pivotPointDistance, playerAngleFromPivotPoint);
        //Get lengthdir_x and y values from angle + rotation speed
        angleWRotationX = angleX - lengthdir_x(pivotPointDistance, playerAngleFromPivotPoint + floorIDAnglePos);
        angleWRotationY = angleY - lengthdir_y(pivotPointDistance, playerAngleFromPivotPoint + floorIDAnglePos);
        floorIDXPos += angleWRotationX;
        floorIDYPos += angleWRotationY;
        
        //Debug to draw circle
        floorDebugX = floorID.x;
        floorDebugY = floorID.y;
        testCircle = pivotPointDistance;
        
        //Movement
        repeat(abs(floorIDYPos))
            y += sign(floorIDYPos);
        repeat(abs(floorIDXPos))
            x += sign(floorIDXPos);
    }
}
And here's my moving platform code. The player calls it in the step event and the platforms all move/rotate in their Begin Step event.
 

TheouAegis

Member
A circle might not be the best way if it's a square block spinning around. We'll have to think about that some more. I had an idea but then i watched the video again (glad u had two guys in the vid) and my first idea wasn't going to work.
 

SQGTDev

Member
Ah, that explains everything. I'm hoping to try to have a method that works for all sorts of different shapes, since I can use a method to push the player out of the ground to the left/right when the ground gets steep enough, another thing I took from NSMB. Would it be possible to detect the player's position around the edge of the platform and move them that way instead of using a circle?
 

TheouAegis

Member
Without precise collisions, not that i know. I think they typically treat different shapes with different mechanics.
 

TheouAegis

Member
I was thinking the edges of the platform would just be a set of vectors, since it's easier to rotate 4 lines than to rotate one object and collide with it without using sloppy collision mechanics. But I still think it's just a matter of

p1 --> p1'
p2 --> p2'
player.(x,y) --> player.(x,y)'

So each vertex is translated based on the rotation that step and so is the player's coordinates when the player is in contact with it. But maybe NSMB used a cruder method than that, I dunno. I'm just looking at it from my own experience with a couple games myself.
 

SQGTDev

Member
I wouldn't want to swap over to Box2d right now, it looks useful but my engine right now already works pretty well.

I was thinking the edges of the platform would just be a set of vectors, since it's easier to rotate 4 lines than to rotate one object and collide with it without using sloppy collision mechanics. But I still think it's just a matter of

p1 --> p1'
p2 --> p2'
player.(x,y) --> player.(x,y)'

So each vertex is translated based on the rotation that step and so is the player's coordinates when the player is in contact with it. But maybe NSMB used a cruder method than that, I dunno. I'm just looking at it from my own experience with a couple games myself.
So basically all I need to do now is figure out how to track the player's position along the edge of the platform instead of using a circle? I think one way to accomplish that would be to make a path for the platform's outline and use that to detect the positioning. NSMB appears to have separate invisible line platforms for each edge which is a bit crude but it works.
 

SQGTDev

Member
I'm close to getting a new method to work:

I have a platform object that is a single pixel, but can be stretched into a line and rotated. It moves and rotates in its Begin Step.
Now I try to measure the player's position along the line, and snap it to the same position along the line at its new angle. I feel like I'm close and this is a better solution anyways but it still doesn't work great.

Code:
///Moving Platforms
if (floorID != 0)
{
    if instance_exists(floorID)
    {
        //Init Variables
        var floorIDXPos, floorIDYPos, floorIDAnglePos, pivotPointDistance, playerAngleFromPivotPoint, angleX, angleY, angleWRotationX, angleWRotationY;
        floorIDXPos = floorID.x - floorID.xprevious;
        floorIDYPos = floorID.y - floorID.yprevious;
        floorIDAnglePos = floorID.anglePrevious;
        
        //Get distance from collision point to platform pivot point
        pivotPointDistance = point_distance(x + floorSensorX, y + floorSensorY, floorID.x, floorID.y);
        //Use distance to get player point on line
        angleX = lengthdir_x(pivotPointDistance, floorID.angle);
        angleY = lengthdir_y(pivotPointDistance, floorID.angle);
        //Find same point on rotated line
        angleWRotationX = lengthdir_x(pivotPointDistance, floorID.anglePrevious);
        angleWRotationY = lengthdir_y(pivotPointDistance, floorID.anglePrevious);
        //Subtract
        floorIDXPos += angleX - angleWRotationX;
        floorIDYPos += angleY - angleWRotationY;
        
        //Movement
        repeat(abs(floorIDYPos))
            y += sign(floorIDYPos);
        repeat(abs(floorIDXPos))
            x += sign(floorIDXPos);
    }
}
 
Top