Move Outside Issue

R

robproctor83

Guest
Okay, this is a long outstanding issue that I have had in my project that I am trying to fix, but it seems like no matter what I try it has some other issue. I attached an image to help explain, but my problem is that my ammo can get stuck in the wall if the player is too close. The ammo start extended out from the player, and so I need some way to pull the ammo out of the wall.

I have tried a number of things, but essentially what I want is for the ammo to instantly teleport to the right location. The closest I have gotten is the code below, but the problem is it only works with walls to the left/right and not up/down because it's got the x/y separated and moves x first, so if it's a y wall it will try moving it x first and that isn't what I want.

Code:
        var _collide = instance_place(x, y, obj_Solid);
        if(move_x_sp != 0){
            var collide = instance_place(x+move_x_sp,y,obj_Solid);
            if(instance_exists(collide) && collide.solid){
                if(_collide){
                    var _pdir = point_direction( 0, 0, sign(owner.x-x), 0);
                    move_outside_solid(_pdir, -1);
                }
                move_x_sp = 0;
            }
            x += move_x_sp*scr_delta(time.Global);
        }
        else{
            move_x_sp = cos(move_direction) * move_speed;
        }

        if(move_y_sp != 0){
            var collide = instance_place(x,y+move_y_sp,obj_Solid);
            if(instance_exists(collide) && collide.solid){
                if(_collide){
                    var _pdir = point_direction( 0, 0, 0, sign(owner.y-y));
                    move_outside_solid(_pdir, -1);
                }
                move_y_sp = 0;
            }
            y += move_y_sp*scr_delta(time.Global);
        }
        else{
            move_y_sp = sin(move_direction) * move_speed;
        }
I think, what I need is to do something like check and see if _collide is y positioned or x positioned and then run one of the two chunks.. But, the problem there would be walls that are diagnal, I don't think it would work there.

Also, just to note, I have tried other things like reversing the direction towards the player, or move_outside_* over time instead of instantly, it's like no matter what I do there is some other issue.
 

Attachments

Nidoking

Member
It might be easier if you spawn the bullet inside the player, where you know there won't be a solid object, then move it in the correct direction and account for collisions with walls as you go. The bullet won't actually appear until you reach the final location at the desired radius, since you do all of these calculations within the same event where you spawn the bullet.
 
R

robproctor83

Guest
If at all possible I'd want to avoid that. You can't tell from my example image but the player weapon circles the player 360 deg and when the player attacks the ammo spawns on the weapon rather than the player. Also some ammo is quite larger than the player and I'd run into problems still.
 

Nidoking

Member
There is literally no difference between spawning the bullet at the weapon and spawning it at the player and then moving it to the weapon, except for a bit more processing that you're going to have to do anyway.
 
R

robproctor83

Guest
I see what you are saying, sorry I misunderstood initially. While I think that would probably work, I don't think that would work with ammo larger than the player as it would start already on top of everything.

My thought was to take the colliding object and the ammos owner (player) and figure out if the ammo should be spawning above or below, left or right of the colliding object. The problem I ran into there is I have up/down and left/right separate and so if it's left/right collision it works perfectly but if it's up/down it still triggers as left/right and so repositions the ammo incorrectly. I was thinking about setting up some condition to see which direction I should reposition the ammo but then that would not fix diagonal. In order for diagonal to work it seems like it would need to only move so far in a single step, like 16px let's say, so that it can move in both directions, but then are other problems with moving over time... Aghh
 

Joe Ellis

Member
If the ammo moves how you want when the player isn't next to a wall, couldn't you do a check when the ammo is first fired, where if it collides with a wall within 1 step or so, it just destroys it instantly? You could set the ammo to not visible so this wouldn't be seen, then set it to visible if it hasn't been destroyed instantly.
 
R

robproctor83

Guest
Joe, Thanks for your suggestion. That is what I was doing initially, but the problem with that is some of the ammo is rather large and in scenarios where the player is in a tight spot or a narrow hallway it's not always clear when the ammo will end up landing on a wall, especially considering the weapon is rotating 360 around the player extended about 16px from the sprite origin. So when the player is butted up against a wall they may be trying to place ammo right up next to the wall, but to do that they have to make sure they don't have the weapon to far into the wall area otherwise the ammo will never appear.... See what I mean?

There has got to be some way to do this...
 
R

robproctor83

Guest
Alright, I have a solution that works "good enough" in that in 90% of the situations it reacts how I anticipate. On those few that didn't work exactly as I wanted they were still acceptable results. Even in tight situations and corners it seems to be working well enough, no more teleporting or flickering yay! It's not perfect though, in some corner cases or strange angles the ammo gets projected not exactly where I would want and also if the ammo can't be larger than 32px. Well, I would consider this solved, but if anyone has anymore suggestions/ideas I would love to hear them.

Code:
        if(move_x_sp != 0){
            var collide = instance_place(x+move_x_sp,y,obj_Solid);
            if(instance_exists(collide) && collide.solid){
                if(_collide && (owner.x > _collide.bbox_right || owner.x < _collide.bbox_left)){
                    var _cbx = (owner.x > _collide.bbox_right) ? _collide.bbox_right: _collide.bbox_left;
                    move_outside_solid(point_direction( 0, 0, sign(owner.x-_cbx), 0), 48);
                }
                move_x_sp = 0;
            }
            x += move_x_sp*scr_delta(time.Global);
        }
        else{
            move_x_sp = cos(move_direction) * move_speed;
        }

        if(move_y_sp != 0){
            var collide = instance_place(x,y+move_y_sp,obj_Solid);
            if(instance_exists(collide) && collide.solid){
                if(_collide){
                    var _cby = (owner.y > _collide.bbox_top) ? _collide.bbox_top: _collide.bbox_bottom;
                    move_outside_solid(point_direction( 0, 0, 0, sign(owner.y-_cby)), 48);
                }
                move_y_sp = 0;
            }
            y += move_y_sp*scr_delta(time.Global);
        }
        else{
            move_y_sp = sin(move_direction) * move_speed;
        }
 
R

robproctor83

Guest
Okay, dangit maybe I jumped the gun there... Play testing more this morning it seems that no, there are in fact still major problems. Bah... I'll have to rethink this. Preventing the ammo from ever starting outside the walls and spawning it on top of the player is probably the right way to go about doing this, but I can't comprehend how that would work without other issues because of the size of the ammo can be quite a bit larger than the player.
 

Joe Ellis

Member
What about using a collision line from the player to where the ammo first appears:

Code:
///weapon_shoot()

if !collision_line(x, y, x + lengthdir_x(ammo_size, aim_dir), y + lengthdir_y(ammo_size, aim_dir), obj_solid)
{
//create ammo
}
To get "ammo_size" it would probably be the width of the sprite, or height depending which direction the original image is facing.

Or maybe you could use collision_circle(x, y, ammo_radius, obj_solid)
 
Last edited:
R

robproctor83

Guest
Thank you Joe, but if I understand correctly what your saying would be the same as doing a normal collision check. What I am after is a way to move that object outside of the collision appropriately.
 

Joe Ellis

Member
Oh ok, in that case you could, after collision has been checked and a bullet is in collision, do a bbox check which will determine how it should bounce and also move it to contact with the wall's bbox:

Code:
if bbox_top < wall.bbox_bottom
&& bbox_bottom > wall.bbox_bottom
{
vsp = -vsp
y = wall.bbox_bottom - (bbox_top - y)
}
if bbox_bottom > wall.bbox_top
&& bbox_top < wall.bbox_top
{
vsp = -vsp
y = wall.bbox_top - (bbox_bottom - y)
}
if bbox_left < wall.bbox_right
&& bbox_right > wall.bbox_right
{
hsp = -hsp
x = wall.bbox_right - (bbox_left - x)
}
if bbox_right > wall.bbox_left
&& bbox_left < wall.bbox_left
{
hsp = -hsp
x = wall.bbox_left - (bbox_right - x)
}
It might be best to put that in a switch statement instead, to stop the speed being flipped twice, or use return false inside each statement.

Does this sound like the right kind of thing?
 
Last edited:
R

robproctor83

Guest
Yes, Joe something like that may very well work, thank you! When I get home here in a bit I'll have another go at it and I think, yes, using the bbox of the collider (wall) and the ammo might be what I need.
 
R

robproctor83

Guest
It bothers me how much trouble this problem has given me. I've worked and reworked this code over and over for the past couple years and I've reworked my ammo generator again to start from the player, and for collisions with a single straight wall it works how I want, but on corners it falls apart because it's colliding with multiple walls. I'm not sure how I will go about figuring out corners... Argh... This is driving me crazy, I feel like no matter what I do there is some issue with it.

Code:
var _start_collide = instance_place(x,y,obj_Solid);
if(_start_collide){
    //top overlap
    if(bbox_top <= _start_collide.bbox_bottom && bbox_bottom > _start_collide.bbox_bottom){
        while(bbox_top <= _start_collide.bbox_bottom && bbox_bottom > _start_collide.bbox_bottom){
            y++;
        }
    }
    //right overlap
    if(bbox_right >= _start_collide.bbox_left && bbox_left < _start_collide.bbox_left){
        while(bbox_right >= _start_collide.bbox_left && bbox_left < _start_collide.bbox_left){
            x--
        }
    }
    //bottom overlap
    if(bbox_bottom >= _start_collide.bbox_top && bbox_top < _start_collide.bbox_top){
        while(bbox_bottom >= _start_collide.bbox_top && bbox_top < _start_collide.bbox_top){
            y--;
        }
    }
    //left overlap
    if(bbox_left <= _start_collide.bbox_right && bbox_right > _start_collide.bbox_right){
        while(bbox_left <= _start_collide.bbox_right && bbox_right > _start_collide.bbox_right){
            x++;
        }
    }
}
*edit - Okay, thinking about this for another 2 seconds I realize that obviously I should loop over all the collisions, but stil on the example image I attached how would I go about handling the collision with the wall object on the corner like that?

*edit 2 - Okay, more thought, maybe for wall corners I can check for each bbox side and not just opposite sides... Hmm... that's going to be a hell of a complicated conditional, so much so I feel like that can't be the right way... is it?

*EDIT 3 - OMG wait, maybe I don't need to worry about any of the bbox stuff! My ammo may be larger than my player, but it's not larger than a single gid cell (32px by 32px) SO I really only need to move the ammo to the grid cell that the player is in, not to the actual position of the player... YES that makes sense... Okay, updating now!

*EDIT 4 - Okay... Bummer that won't work because apart from walls I also have a few other solid objects and they are not all at least 32px, some are pretty small and so the player and a solid can both occupy a single cell at the same time... damnit...
 

Attachments

Last edited by a moderator:
R

robproctor83

Guest
Okay... Getting close, just about got it I think... As far as walls go, I think I have it figured out, it seems overwhelming but hey, it's working... Now, the problem I need to solve is the smaller solids, things that the ammo shouldn't spawn on but is smaller than a wall cell, some are even smaller than the player. the other problem that may arise is that the smaller solids can spawn next to a wall, so I have a feeling there will be some problems there... But for now, just walls, this seems to be working :)

Code:
var _bbox_top_left = collision_point(bbox_left, bbox_top, obj_Solid, false, true);
var _bbox_top_right = collision_point(bbox_right, bbox_top, obj_Solid, false, true);
var _bbox_bot_right = collision_point(bbox_right, bbox_bottom, obj_Solid, false, true);
var _bbox_bot_left = collision_point(bbox_left, bbox_bottom, obj_Solid, false, true);

/* STRAIGHT */
//top
var _ic = 0;
if(_bbox_top_left && _bbox_top_right && !_bbox_bot_right && !_bbox_bot_left){
    while(collision_line(bbox_left, bbox_top, bbox_right, bbox_top, obj_Solid, false, false)){
        y+=2;
        _ic++;
        if(_ic > 100) break;
    }
}
//right
if(!_bbox_top_left && _bbox_top_right && _bbox_bot_right && !_bbox_bot_left){
    _ic = 0;
    while(collision_line(bbox_right, bbox_top, bbox_right, bbox_bottom, obj_Solid, false, false)){
        x-=2;
        _ic++;
        if(_ic > 100) break;
    }
}
//bottom
if(!_bbox_top_left && !_bbox_top_right && _bbox_bot_right && _bbox_bot_left){
    _ic = 0;
    while(collision_line(bbox_left, bbox_bottom, bbox_right, bbox_bottom, obj_Solid, false, false)){
        y-=2;
        _ic++;
        if(_ic > 100) break;
    }
}
//left
if(_bbox_top_left && !_bbox_top_right && !_bbox_bot_right && _bbox_bot_left){
    _ic = 0;
    while(collision_line(bbox_left, bbox_top, bbox_left, bbox_bottom, obj_Solid, false, false)){
        x+=2;
        _ic++;
        if(_ic > 100) break;
    }
}
/* CORNER || INSET */
//top left
if((_bbox_top_left && !_bbox_top_right && !_bbox_bot_right && !_bbox_bot_left) || (_bbox_top_left && _bbox_top_right && !_bbox_bot_right && _bbox_bot_left)){
    _ic = 0;
    while(collision_line(bbox_left, bbox_top, bbox_right, bbox_top, obj_Solid, false, false) && collision_line(bbox_left, bbox_top, bbox_left, bbox_bottom, obj_Solid, false, false)){
        y+=2;
        x+=2;
        _ic++;
        if(_ic > 100) break;
    }
}
//top right
if((!_bbox_top_left && _bbox_top_right && !_bbox_bot_right && !_bbox_bot_left) || (_bbox_top_left && _bbox_top_right && _bbox_bot_right && !_bbox_bot_left)){
    _ic = 0;
    while(collision_line(bbox_left, bbox_top, bbox_right, bbox_top, obj_Solid, false, false) && collision_line(bbox_right, bbox_top, bbox_right, bbox_bottom, obj_Solid, false, false)){
        y+=2;
        x-=2;
        _ic++;
        if(_ic > 100) break;
    }
}
//bot right
if((!_bbox_top_left && !_bbox_top_right && _bbox_bot_right && !_bbox_bot_left) || (!_bbox_top_left && _bbox_top_right && _bbox_bot_right && _bbox_bot_left)){
    _ic = 0;
    while(collision_line(bbox_left, bbox_bottom, bbox_right, bbox_bottom, obj_Solid, false, false) && collision_line(bbox_right, bbox_top, bbox_right, bbox_bottom, obj_Solid, false, false)){
        y-=2;
        x-=2;
        _ic++;
        if(_ic > 100) break;
    }
}
//bot left
if((!_bbox_top_left && !_bbox_top_right && !_bbox_bot_right && _bbox_bot_left) || (_bbox_top_left && !_bbox_top_right && _bbox_bot_right && _bbox_bot_left)){
    _ic = 0;
    while(collision_line(bbox_left, bbox_bottom, bbox_right, bbox_bottom, obj_Solid, false, false) && collision_line(bbox_left, bbox_top, bbox_left, bbox_bottom, obj_Solid, false, false)){
        y-=2;
        x+=2;
        _ic++;
        if(_ic > 100) break;
    }
}
*edit 1 - I also need to fix that +/-2 it should only be 1 at a time but for some reason when only moving 1 some other code I have that stop ammo from moving when it hits a wall is being triggered incorrectly.
 
Last edited by a moderator:
R

robproctor83

Guest
Progress! I'm very close, thank you guys for your help and suggestions because it has helped a lot. I have it working "almost" perfect, the only remaining problem is tight situations where a small solid spawn near a wall or a corner and the player positions themselves between the two and fires an ammo that is larger than the distance between the two solids. It's not a super common issue, but there are usually a handful of those positions throughout the map. As a temporary work around I am destroying the ammo if after re-positioning everything it still has a solid collision.

I would LOVE to fix that problem if possible, so if anyone has any suggestions please, it would be much appreciated. It can be confusing for the player to nudged up in that corner attacking and then go to shoot a particular ammo that is just a few pixels too large and nothing appears... But how do I figure out how to reposition ammo in this obscure circumstance? I need to somehow figure out the closest open space, in the direction of the player, and move it there...

Anyways, here is what I have now... I somehow still feel like I am not going about this the right way! But, I'm just not sure.
Code:
///AMMO CREATE
//Start the ammo from the player position, this will be the base position from which the ammo can spawn
//Check each corner to see what section is overlapped
var _bbox_top_left = collision_point(bbox_left, bbox_top, obj_Solid, false, true);
var _bbox_top_right = collision_point(bbox_right, bbox_top, obj_Solid, false, true);
var _bbox_bot_right = collision_point(bbox_right, bbox_bottom, obj_Solid, false, true);
var _bbox_bot_left = collision_point(bbox_left, bbox_bottom, obj_Solid, false, true);

/* STRAIGHT collisions, directly up,down,left,right*/
//top
var _ic = 0;
if(_bbox_top_left && _bbox_top_right && !_bbox_bot_right && !_bbox_bot_left){
    while(collision_line(bbox_left, bbox_top, bbox_right, bbox_top, obj_Solid, false, false)){
        y+=2;
        _ic++;
        if(_ic > 100) break;
    }
}
//right
if(!_bbox_top_left && _bbox_top_right && _bbox_bot_right && !_bbox_bot_left){
    _ic = 0;
    while(collision_line(bbox_right, bbox_top, bbox_right, bbox_bottom, obj_Solid, false, false)){
        x-=2;
        _ic++;
        if(_ic > 100) break;
    }
}
//bottom
if(!_bbox_top_left && !_bbox_top_right && _bbox_bot_right && _bbox_bot_left){
    _ic = 0;
    while(collision_line(bbox_left, bbox_bottom, bbox_right, bbox_bottom, obj_Solid, false, false)){
        y-=2;
        _ic++;
        if(_ic > 100) break;
    }
}
//left
if(_bbox_top_left && !_bbox_top_right && !_bbox_bot_right && _bbox_bot_left){
    _ic = 0;
    while(collision_line(bbox_left, bbox_top, bbox_left, bbox_bottom, obj_Solid, false, false)){
        x+=2;
        _ic++;
        if(_ic > 100) break;
    }
}
/* CORNER || INSET */
//top left
if((_bbox_top_left && !_bbox_top_right && !_bbox_bot_right && !_bbox_bot_left) || (_bbox_top_left && _bbox_top_right && !_bbox_bot_right && _bbox_bot_left)){
    _ic = 0;
    while(collision_line(bbox_left, bbox_top, bbox_right, bbox_top, obj_Solid, false, false) && collision_line(bbox_left, bbox_top, bbox_left, bbox_bottom, obj_Solid, false, false)){
        y+=2;
        x+=2;
        _ic++;
        if(_ic > 100) break;
    }
}
//top right
if((!_bbox_top_left && _bbox_top_right && !_bbox_bot_right && !_bbox_bot_left) || (_bbox_top_left && _bbox_top_right && _bbox_bot_right && !_bbox_bot_left)){
    _ic = 0;
    while(collision_line(bbox_left, bbox_top, bbox_right, bbox_top, obj_Solid, false, false) && collision_line(bbox_right, bbox_top, bbox_right, bbox_bottom, obj_Solid, false, false)){
        y+=2;
        x-=2;
        _ic++;
        if(_ic > 100) break;
    }
}
//bot right
if((!_bbox_top_left && !_bbox_top_right && _bbox_bot_right && !_bbox_bot_left) || (!_bbox_top_left && _bbox_top_right && _bbox_bot_right && _bbox_bot_left)){
    _ic = 0;
    while(collision_line(bbox_left, bbox_bottom, bbox_right, bbox_bottom, obj_Solid, false, false) && collision_line(bbox_right, bbox_top, bbox_right, bbox_bottom, obj_Solid, false, false)){
        y-=2;
        x-=2;
        _ic++;
        if(_ic > 100) break;
    }
}
//bot left
if((!_bbox_top_left && !_bbox_top_right && !_bbox_bot_right && _bbox_bot_left) || (_bbox_top_left && !_bbox_top_right && _bbox_bot_right && _bbox_bot_left)){
    _ic = 0;
    while(collision_line(bbox_left, bbox_bottom, bbox_right, bbox_bottom, obj_Solid, false, false) && collision_line(bbox_left, bbox_top, bbox_left, bbox_bottom, obj_Solid, false, false)){
        y-=2;
        x+=2;
        _ic++;
        if(_ic > 100) break;
    }
}

//Reposition ammo to target position
var xx = x;
var yy = y;
x = move_x;
y = move_y;
_ic = 0;
//Step BACK from target, less collision checks hopefully
while(instance_place(x, y, obj_Solid)){
    var _pdir = point_direction(x,y,xx,yy);
    x += lengthdir_x(1,_pdir);
    y += lengthdir_y(1,_pdir);
    _ic++;
    if(_ic > 100) break;
}
//One last collision to prevent player hiding behind solid and shooting
//This happens because of stepping back from target
//One extra collision, may still be cheaper than moving forward
if(collision_line(xx, yy, x, y, obj_Solid, false, false)){
    x = xx;
    y = yy;
}
Code:
///AMMO STEP
var _collide = instance_place(x, y, obj_Solid);
//... do some other stuff with collisions... 
//...
if(move_x_sp != 0){
    var _collide_x = instance_place(x+move_x_sp,y,obj_Solid);
    if(instance_exists(_collide_x)){
        if(_collide && (owner.x > _collide.bbox_right || owner.x < _collide.bbox_left)){
            var _cbx = (owner.x > _collide.bbox_right) ? _collide.bbox_right: _collide.bbox_left;
            move_outside_solid(point_direction( 0, 0, sign(owner.x-_cbx), 0), 48);
            var _collide_destroy = instance_place(x,y,obj_Solid);
            if(_collide_destroy) instance_destroy();
        }
        move_x_sp = 0;
    }
    x += move_x_sp*scr_delta(time.Global);
}
else{
    move_x_sp = cos(move_direction) * move_speed;
}

if(move_y_sp != 0){
    var _collide_y = instance_place(x,y+move_y_sp,obj_Solid);
    if(instance_exists(_collide_y)){
        if(_collide){
            var _cby = (owner.y > _collide.bbox_top) ? _collide.bbox_top: _collide.bbox_bottom;
            move_outside_solid(point_direction( 0, 0, 0, sign(owner.y-_cby)), 48);
            var _collide_destroy = instance_place(x,y,obj_Solid);
            if(_collide_destroy) instance_destroy();
        }
        move_y_sp = 0;
    }
    y += move_y_sp*scr_delta(time.Global);
}
else{
    move_y_sp = sin(move_direction) * move_speed;
}
 
R

robproctor83

Guest
I've attached a graphic that details the different collisions situations and the one that I am having problems with, maybe this will help visualize what is going on.
 

Attachments

R

robproctor83

Guest
Thanks Joe, and no worries about responding, I'm just updating my progress... So close, but I have a feeling this last issue will be a real challenge, hopefully I don't have to redo everything again lol.
 
A

Alaska Minds

Guest
Hello robproctor83,

The code below may help you. Add the code to the Ammo object and in a Collision Event with the Wall object. The code will cause the Ammo object to move to a random location 32 pixels away from the Wall and Character object. You can use this same code on all Collision Events (e.g. another Ammo object...)

Code:
// Moves instance away from wall (Provides Object Overlap Protection)
var dir;
var move_dis = 32;  // pixels to move away from other object in collision

// If both instances are in the same location, set direction random
if (x == other.x && y == other.y)
    dir = random(360);

// Move in opposite direction of object in collision
else
    dir = point_direction(other.x,other.y,x,y);

// Move to new location but away from wall and character

var dx = lengthdir_x(move_dis,dir);
var dy = lengthdir_y(move_dis,dir);

if (!place_meeting(x+dx,y,obj_character)) x += dx;
if (!place_meeting(x,y+dy,obj_character)) y += dy;
 
Last edited by a moderator:
Top