GML [Solved] Problem with Sticky Projectiles

Papa Doge

Member
Hello,

I'm working on a game that has a throwing projectile that the player tosses and sticks into walls, then the player can call the projectile back and throw it again.

I had the basic code setup and working fine, but my projectile sprite was going too far into the walls, so I added some collision detection to check if the projectile is about to hit a wall based on its movement speed using a while loop, and if it is, I decided to move the projectile 1 pixel closer to the wall it's about to hit until it is hitting the wall, then stop its movement and fix the projectile's image_angle to match the wall it's hitting.

The problem I'm having is that my player sprite is 32x32 and my projectile speed is 16. Every time the player is on the floor or climbing a wall and tries to throw a projectile, the projectile sticks in the surface of whatever they are currently touching because the speed of the projectile is half the height of the player sprite and the collision detection kicks in automatically.

The solution I've been hunting for is some way to only check the collision detection after the projectile has moved at least 1 pixel which would solve my immediate problem, but I'm sure there are more elegant ways to resolve the issue I'm seeing.

Code:
// COLLISION VARIABLES
onWall = place_meeting(x+projectileSpeed*2,y,par_obj_collisions) - place_meeting(x-projectileSpeed*2,y,par_obj_collisions);
onFloor = place_meeting(x,y+projectileSpeed*2,par_obj_collisions) - place_meeting(x,y-projectileSpeed*2,par_obj_collisions);
onSpike = place_meeting(x,y,obj_death);
onDoor = place_meeting(x,y,obj_door);

/* -------------------- */

// COLLISION CHECKS

// if the projectile is not touching a wall or a floor...
if (onWall == 0) && (onFloor == 0) {
    // set speed to default...
    speed = projectileSpeed;
} else {
    speed = 0;
    if (returning == false) {
        if (onWall == 1) {
            // make it stick into the tile...
            image_angle = 0;
            while (!place_meeting(x,y,par_obj_collisions)) {
                x += 1;
            }
        }
        if (onWall == -1) {
            // make it stick into the tile...
            image_angle = 180;
            while (!place_meeting(x,y,par_obj_collisions)) {
                x -= 1;
            }
        }
        if (onFloor == 1) {
            // make it stick into the tile...
            image_angle = 270;
            while (!place_meeting(x,y,par_obj_collisions)) {
                y += 1;
            }
        }
        if (onFloor == -1) {
            // make it stick into the tile...
            image_angle = 90;
            while (!place_meeting(x,y,par_obj_collisions)) {
                y -= 1;
            }
        }
    }
}
 
Last edited:

Papa Doge

Member
Whoops, sorry. I just realized I shared the older version of my code. Now it should make a bit more sense...
 
Last edited:

TheouAegis

Member
You could use an alarm, but your biggest issue is you're checking left and right of a projectile at the same time. You don't need to check both directions, just the direction that projectile's moving. And why are you checking for collisions 2 steps in advance?

(Edit: stupid phone lol)
 
Last edited:

3dgeminis

Member
Maybe this code will help you do it in a different way, simpler and shorter.
Code:
repeat(spd) ///spd=16
  {
   if !place_meeting(x+lengthdir_x(1, ang), y+lengthdir_y(1, ang), obj_wall) ///ang -> 0-90-180-270
      {
       x+=lengthdir_x(1, ang)
       y+=lengthdir_y(1, ang)
      }
  }
Also take into account the position in which you create the projectile.
 

NeZvers

Member
One way to check fast projectiles if the have passed through objects is collision_line(xprevious, yprevious, x, y, 0, 1)
If there's collision you can go and do while() collision method or bbox collision method.
 

Papa Doge

Member
Maybe this code will help you do it in a different way, simpler and shorter.
Code:
repeat(spd) ///spd=16
  {
   if !place_meeting(x+lengthdir_x(1, ang), y+lengthdir_y(1, ang), obj_wall) ///ang -> 0-90-180-270
      {
       x+=lengthdir_x(1, ang)
       y+=lengthdir_y(1, ang)
      }
  }
Also take into account the position in which you create the projectile.
Wow, this is so much better. Thanks for the suggestion! Here's what I ended up with...

Code:
// THROW

// if the projectile is being thrown...
if (returning == false) {
    // run this code once every unit of speed...
    repeat (projectileSpeed) {
        // if the projectile isn't about to touch an obstacle on its current path...
        if (!position_meeting(x+lengthdir_x(1,image_angle),y+lengthdir_y(1,image_angle),par_obj_collisions)) {
            // move it along it's path 1 pixel
            x += lengthdir_x(1,image_angle);
            y += lengthdir_y(1,image_angle);   
        }
    }
}
I needed to add that extra if statement because I'm using the mp_potential_step function to return the projectile to the player which was causing conflicts. Otherwise this code works perfectly. I decided to punt on the whole "rotate projectile to match the surface it's hitting" concept. Mainly because I'm still grayboxing and that feels like a some extra code I might not need if I do something clever with my future sprite animations.

You could use an alarm, but your biggest issue is you're checking left and right of a projectile at the same time. You don't need to check both directions, just the direction that projectile's moving. And why are you checking for collisions 2 steps in advance?

(Edit: stupid phone lol)
I liked this suggestion too and it was good to read up on alarms and how they work. That's definitely going to come in handy latter. Your comments on checking both directions where super valuable and ultimately why I ended up going with the code above that's only checking for collisions in the direction of movement. That checking 2 steps ahead thing was a mistake on my part. I threw that in when I was trying to figure out how speed was impacting my collisions. Pun intended. Thanks for the advice!

One way to check fast projectiles if the have passed through objects is collision_line(xprevious, yprevious, x, y, 0, 1)
If there's collision you can go and do while() collision method or bbox collision method.
I tried this one too, but couldn't figure out how to reposition the projectile correctly after the collision. It was randomly getting stuck in walls and my recall code wasn't working so I gave up. When I was trying to wrap my head around this one I saw a lot of examples of "enemy line of sight" that got me really exited to do some new experiments. Thanks for sharing!
 

Papa Doge

Member
I agree with TheouAegis. Simplest solution would be to use an alarm or just spawn the projectile a little farther out.
Spawning farther out was something I hadn't considered either and it worked just fine if the image angle was pointing upwards but if it was exactly 0 or 180 degrees it was sticking the the floor. Definitely easy to test and worth a try. Thanks for the suggestion!
 
Top