GMS1.4.1804: Trying to do "move_outside_object" in quickest direction. Totally stumped.

O

Obrez

Guest
Hi all,

I'm messing around working on a wanna-be doom clone. My 2.5D movement, mouselook, wall sliding, crouching, jumping, gravity, and ramps are all working wonderfully. I wrote a "collision_circle_z" script to use for the 3D collision detection and wall slide and it works great. However, I am using a similar "collision_point_z" concept to check if the player is standing on top of a floor with their "physical" feet on that floor. Because of this, when I walk off a ledge, I fall to the correct floor z height since the "collision_point_z" is reading that the center of the player collision circle had moved off of the previous floor z height. I also get stuck immediately as far as x/y movement is concerned because the "collision_circle_z" is reading (correctly) that roughly half the player character collision circle is well inside the wall and I am prevented from moving in any direction because the collision detection will detect collision with the wall I am already stuck in. This in itself is fine and is technically working as intended because I do want z collisions to be determined by that center point and x/y collisions to be determined by the circle.

What I'm trying to achieve is a smooth "pushing" of the player outside of the object that they are stuck in in the direction that would most quickly lead to the player not being stuck. I'm familiar with the gamemaker functions for "move_outside" for all and solid and I know that can be mimicked for specific objects but my problem is that I cannot for the life of me figure out how to have a function like that automatically determine which direction would be quickest. I know I've seen this before and I think I've even done it but my searches are turning up empty and I purged all my old projects.

So far I've tried a number of scripts that use a 360 degree sonar-esque sweep with the lengthdir functions in a loop to determine a point from which to do a rangefinder check from that point back in the direction of the player's x/y. The intent was to record the angle at which the range to the object that I am stuck in is the greatest, return that, and then move the player in that direction. I've tried modified versions as well using a half-circle sweep in each direction and averaging those to find correct angles and another that did an expanding orbit of collision circle check. That one was a hoot. Every iteration has been a total failure and to be honest, I get the impression that it's an overly-complex and sloppy idea.

At this point I am at a total loss and could really use a hand from the gurus. My brain is pretty much fried from hours of failure and I'm sure the answer is something simple that I'm glossing over; it usually is. what I wouldn't give for built-in normal checking.

Hopefully this ms paint masterpiece will help explain. The green circle is the player collision cylinder. Imagine that the black boxes are elevated platforms. This would be a top down view. In each example, the player's x/y coordinate stepped off the edge while moving so the player fell to the white level below. x/y collision with obstacles is checked against their 2D sprite so any 2D based concepts should work fine.
stuck.png

I really hope I explained that well enough. I foolishly deleted my attempts to code this but I will try to recreate them and copy it here if it would help. There has to be a better method though. I'm kind of embarrassed that I could get everything else working fine but this one has really humbled me.
 
Last edited by a moderator:

Joe Ellis

Member
If the player is using a circle mask, that lets you do the simplest technique. Basically the nearest point from the wall to the edge of the circle will be exactly the same direction as the wall is facing. So all you need to do is use the wall's normal, get the dot product of the vector from the circle\player's center position to one of the wall's vertices(start or end coordinate) and the wall's normal, ei. dot(player_pos - wall_vertex1, wall_normal). This dot product gives the distance between the player's center position and the nearest point on the wall. So then all you have to do is add the wall's normal to the player's position multiplied by the player's circle radius - the distance.

Code:
var dist = dot_product(x - wall_x1, y - wall_y1, wall_nx, wall_ny);
if dist < mask_radius
{
dist = mask_radius - dist
x += wall_nx * dist
y += wall_ny * dist
}
 
O

Obrez

Guest
Thanks, I'll take a look into that and tinker with it a bit, I never knew about dot_product.

Going into it, wouldn't this be problematic if the player is stuck inside multiple walls at once like in a corner?
 

Joe Ellis

Member
Thanks, I'll take a look into that and tinker with it a bit, I never knew about dot_product.

Going into it, wouldn't this be problematic if the player is stuck inside multiple walls at once like in a corner?
I think it usually sorts it out by itself, cus collision with one of the walls will push it out of collision with it, then the other one will straight after.
For instance if the floor was checked first, it would move the player upwards a few pixels but not change the x coord.
If the walls and floors always have 90 degrees or more difference between their normals there shouldn't be any problem.
The only case that could end up pushing the player into another wall when it collides with one is if there's an acute angle between their normals,
Then it might kind of flicker between the two positions. It's hard to know without testing it though
 
Top