Loops Occuring Per Frame?

For questions about using Classic.

Post » Thu Sep 18, 2014 10:19 am

I've been working on a custom platformer engine with someone, and we cannot get past this one simple problem. Supposedly, loops run within a single step instead of per step. However, the actions I've placed within these loops are running per step.
This problem has been driving me crazy for days. I've tried all kinds of tricks, workarounds, variations, and every type of loop. I've simplified the code as much as possible trying to defeat this.

This should be a basic, straightforward collision system. All it's doing is:
while object overlapping solid
shift the object by one pixel

I've even read elsewhere on the forum that this exact method should work, that the effect should be no overlapping visibly taking place. What's happening though is that, if the object phases into a solid, it's ejected one pixel per step as if there were no loop being used. This results in quick skips or "slides" when the player overlaps a solid.

I've reduced the engine to specifically the graphics and code needed to demonstrate this, so it's easy to examine. Most of it is commented. Basically here's how it works:
The player object is surrounded by three sensors for the sides and bottom. These are used for collision testing, the player object is not. The sensors are positioned to the player in a function. This function is called every step, and also called during the while loop that's supposed to eject the player during a collision.
A separate object holds the player's variables for movement, such as x and y speed. The player's position is updated every step based on these variables.
Input group: Uses variables as proxy for input
Character Setup group: Initializes movement variables and spawns the player.
Sensor group: Holds the while loops that eject the player in the event the sensors collide with solids.
Movement groups: Handles player movement variables such as speed, gravity, assigning rendered positions to the player, and controlling the player's movement through input.
Step group: Positions the sensors to the player via image points.
Temporary Code group: Debug stuff and placeholder code for later. Doesn't do anything significant. Draws X and Y positions on screen.

The cap file is attached to the post.
Arrow keys move, Z key is jump, shift boosts x speed. Holding shift and running into walls really shows the problem here.

I would appreciate it greatly if anyone can help with this. We're rather stuck until we get past this problem.
You do not have the required permissions to view the files attached to this post.
Last edited by Mr Lange on Fri Sep 19, 2014 12:08 am, edited 1 time in total.
B
6
S
1
Posts: 15
Reputation: 530

Post » Thu Sep 18, 2014 1:17 pm

You're making use of a non-standard "Dropshadow2" effect, so I can't open the file. I'll help you out if you can either remove this effect or include it with your CAP file.
Moderator
B
113
S
41
G
20
Posts: 1,586
Reputation: 19,106

Post » Fri Sep 19, 2014 12:09 am

Oh sorry about that. I thought I removed all the external stuff.
Updated. Also removed some pointless variables.
B
6
S
1
Posts: 15
Reputation: 530

Post » Fri Sep 19, 2014 1:24 am

It looks like While is simply broken in this case, although I doubt that's the answer you wanted to hear. If you place While in an event by itself and then do your collision checking as a sub-event to that -- using, of course, an else statement to break from the current loop so it doesn't loop forever -- then you'll achieve the results you want. Example:

While
--sensor overlaps solid: Do stuff
--else: Break current loop

Unfortunately, what you want this to do and what it actually does may differ, because as you have it setup now, if you run horizontally into a wall at high speed and the "down" sensor overlaps the ground, you're automatically pushed up through the obstacle, winding up on top. Horizontal and vertical speeds need to be accounted for when determining in which direction you want to push out.

As another tip on the matter -- I apologize if this has strayed too far from the original topic -- you generally want to be pushing out in the direction that requires the smallest number of steps, rather than arbitrarily pushing out in each direction systematically. Expanding on the previous point, this is because as you always push out up first, then you will never end up pushing out right or left.

One more thing to think about is how fast you want your player to go. At some point, your player may move so fast they might skip through obstacles. In this case, you need to move from a "move then push out" system to a "push to new position" system, whereby you incrementally push your object and check for collisions until you've either hit a non passable wall or have pushed as far as the objects speed allows it to move in one frame.

Going back to the original problem, it honestly sucks that While seems to be broken as it is, but at this point nobody is maintaining Classic, so it's unlikely to get fixed.

That was a lot more long winded than I anticipated. If you need clarification on any of the points, I'll be happy to help out.
Moderator
B
113
S
41
G
20
Posts: 1,586
Reputation: 19,106

Post » Fri Sep 19, 2014 1:53 am

Thank you for your help so far. I've attached a new cap to this post with the current progress.
linkman2004 wrote:It looks like While is simply broken in this case, although I doubt that's the answer you wanted to hear. If you place While in an event by itself and then do your collision checking as a sub-event to that -- using, of course, an else statement to break from the current loop so it doesn't loop forever -- then you'll achieve the results you want. Example:

While
--sensor overlaps solid: Do stuff
--else: Break current loop

Okay I did that, and it DID fix the per frame issue, so that's out of the way. However, there's still often a one frame jump that occurs when the loop is executed.

linkman2004 wrote:Unfortunately, what you want this to do and what it actually does may differ, because as you have it setup now, if you run horizontally into a wall at high speed and the "down" sensor overlaps the ground, you're automatically pushed up through the obstacle, winding up on top. Horizontal and vertical speeds need to be accounted for when determining in which direction you want to push out.

I'm aware of this. I setup conditions that check which speed is faster as to which loop to execute, but it's very faulty. I toggled those conditions for now but left them in so you can see what I did.
I reordered the loops so that x ejection runs first, and what happens is, every frame that the player is inside the solid, it runs the y ejection loop once, so holding shift while running into a wall causes the player to climb one pixel every other frame. You'll see what I mean. The goal is to remove that one frame skip altogether so this wouldn't happen at all.

linkman2004 wrote:As another tip on the matter -- I apologize if this has strayed too far from the original topic -- you generally want to be pushing out in the direction that requires the smallest number of steps, rather than arbitrarily pushing out in each direction systematically. Expanding on the previous point, this is because as you always push out up first, then you will never end up pushing out right or left.

Well, if the player gets stuck in a wall, I don't know how to find which direction requires the least number of steps before picking which ejection routine to run.

linkman2004 wrote:One more thing to think about is how fast you want your player to go. At some point, your player may move so fast they might skip through obstacles. In this case, you need to move from a "move then push out" system to a "push to new position" system, whereby you incrementally push your object and check for collisions until you've either hit a non passable wall or have pushed as far as the objects speed allows it to move in one frame.

I'm not sure I understand this... do you mean a check AHEAD prediction system instead of a check BEHIND ejection system? If so, I'm not sure how to accomplish that in Construct.
You do not have the required permissions to view the files attached to this post.
B
6
S
1
Posts: 15
Reputation: 530

Post » Fri Sep 19, 2014 2:44 am

Mr Lange wrote:Okay I did that, and it DID fix the per frame issue, so that's out of the way. However, there's still often a one frame jump that occurs when the loop is executed.

If you're referring to the shaking, this is because you're pushing out before moving the object and handling it's controls, so it's essentially pushing out of the wall only to move right back in in the same frame. You should push out after movement and controls -- also remember to update your sensor positions before pushing out if you do this.

Mr Lange wrote:I'm aware of this. I setup conditions that check which speed is faster as to which loop to execute, but it's very faulty. I toggled those conditions for now but left them in so you can see what I did.

This looks about right, although I advise pushing out in the path of least resistance, which I'll explain in a minute.

Mr Lange wrote:I reordered the loops so that x ejection runs first, and what happens is, every frame that the player is inside the solid, it runs the y ejection loop once, so holding shift while running into a wall causes the player to climb one pixel every other frame. You'll see what I mean. The goal is to remove that one frame skip altogether so this wouldn't happen at all.

The climbing is because whenever you push your player and then call "PlayerReposition", you're only repositioning the colliding sensor. So when going fast, all three sensors could be in the wall. The side ones are checked first, pushing the object -- and side sensors -- out of the wall horizontally. Then you check the down one, which is still inside the wall. This sensor sees itself in the wall, so it pushes up once and is then repositioned based on the position of the object, where it's no longer overlapping. Try calling the function while forgetting picked objects so they all reposition rather than just the one that was picked by the event from which the function is called.

Mr Lange wrote:Well, if the player gets stuck in a wall, I don't know how to find which direction requires the least number of steps before picking which ejection routine to run.

This is a matter of trial and error. You need to push out in a direction, counting the amount of steps taken, then reset to the original position and check push out in another direction, repeating the pattern. At the end, pick the direction with the lowest amount of steps necessary and push out in that direction. Since you already know how many steps it takes, you don't need to check overlap anymore.

Mr Lange wrote:I'm not sure I understand this... do you mean a check AHEAD prediction system instead of a check BEHIND ejection system? If so, I'm not sure how to accomplish that in Construct.

Yes, the concept boils down to this:

1. Calculate based on velocity how many pixels the object will move that frame(for X and Y separately)
2. Using a loop, move the object in increments of one each loop
3. If the object hits an obstacle, move back one pixel and break from the loop; else, break from the loop when you've moved the amount of pixels calculated in step 1
Moderator
B
113
S
41
G
20
Posts: 1,586
Reputation: 19,106

Post » Fri Sep 19, 2014 3:31 am

linkman2004 wrote:
Mr Lange wrote:Okay I did that, and it DID fix the per frame issue, so that's out of the way. However, there's still often a one frame jump that occurs when the loop is executed.

If you're referring to the shaking, this is because you're pushing out before moving the object and handling it's controls, so it's essentially pushing out of the wall only to move right back in in the same frame. You should push out after movement and controls -- also remember to update your sensor positions before pushing out if you do this.

Moved SF Sensors after SF Movement, and moved SF Step between them. This nearly solved it...

linkman2004 wrote:
Mr Lange wrote:I'm aware of this. I setup conditions that check which speed is faster as to which loop to execute, but it's very faulty. I toggled those conditions for now but left them in so you can see what I did.

This looks about right, although I advise pushing out in the path of least resistance, which I'll explain in a minute.

Try enabling the conditions, then holding shift while running off of a platform onto the ground, you'll see what I mean.

linkman2004 wrote:
Mr Lange wrote:Try calling the function while forgetting picked objects so they all reposition rather than just the one that was picked by the event from which the function is called.

*facepalm* I feel like an idiot for missing this. After doing this along with reordering the code, it was completely solved. Attached cap with updated code demonstrates.

linkman2004 wrote:
Mr Lange wrote:Well, if the player gets stuck in a wall, I don't know how to find which direction requires the least number of steps before picking which ejection routine to run.

This is a matter of trial and error. You need to push out in a direction, counting the amount of steps taken, then reset to the original position and check push out in another direction, repeating the pattern. At the end, pick the direction with the lowest amount of steps necessary and push out in that direction. Since you already know how many steps it takes, you don't need to check overlap anymore.

Mr Lange wrote:I'm not sure I understand this... do you mean a check AHEAD prediction system instead of a check BEHIND ejection system? If so, I'm not sure how to accomplish that in Construct.

Yes, the concept boils down to this:

1. Calculate based on velocity how many pixels the object will move that frame(for X and Y separately)
2. Using a loop, move the object in increments of one each loop
3. If the object hits an obstacle, move back one pixel and break from the loop; else, break from the loop when you've moved the amount of pixels calculated in step 1

Sounds tricky but doable. If at some point this becomes necessary, I'll give it a try.

Alright, thank you so much linkman, I'm going to continue with this setup. If anything goes wrong I'll come back to this thread, but for now it's good.
You do not have the required permissions to view the files attached to this post.
B
6
S
1
Posts: 15
Reputation: 530

Post » Sat Sep 20, 2014 10:02 pm

For loop would work just as well, and you should just use a for loop anyway since its not buggy. While is dangerous because it has a really high likely hood of crashing everything, and a like 1000 iteration for loop will work just as well, except has a fail safe if the termination isn't early.
B
75
S
13
G
8
Posts: 1,973
Reputation: 9,841


Return to Help & Support using Construct Classic

Who is online

Users browsing this forum: No registered users and 1 guest