SOLVED Oscillation Physics, how to implement?

Petrik33

Member
So, hello everybody, I am currently trying to implement the tutorial budy for my game. The game is a classical platformer but to understand the things better I've attached the screenshot.
So the problem is that I want the dummy to swing like a pendulum if somebody pushes it. I tried using the cos and sin functions for the image_angle controlled by the main function of simple harmonic motion or how is it called in English(yeah, by the way I am sorry if I missuse any physical terms, I am studying physics in other language).
So as a few lines it looks like this:
GML:
oscillation_progress++;
var _phase_shift = angular_frequency * oscillation_progress + starting_shift;

image_angle = oscillation_amplitude * sin(_phase_shift);
Where the oscillation progress is an analogue of time. But it's all good if I want to show 1-time effect controlled by some variables which are preset manually.

But in the game I want to create the truly working physics so when you push the dummy the angular frequency and other parameters are changed depending on the power of push, the current speed of oscillations and the direction of the push force( just left-right, don't have to mess with accurate angles, just 1 or -1 for the direction value)
So, I can tell a lot about how I was trying to do this, but anyway the result is surely not perfect and even not working. So, anyone familiar with such things who can help me???
 

Attachments

This should be pretty straightforward to accomplish. All the code here is just pseudocode for explaining purposes. First set up a rotation and rotation speed.
GML:
angle_goal = 90;
image_angle = angle_goal;
angle_speed = 0;
Then have it always swing back to angle_goal;. You have to use angle_difference to work around the troubled you would have when between 0 and 360
GML:
angle_speed += sign(angle_difference(image_angle,angle_goal))
image_angle += angle_speed
Now for the collision. Instead of trying to workout the Newtonian physics using math like you would see in a textbook. I will use a more common approach to game physics that takes advantage of the computers strengths. This is exactly how most of box2D (the physics engine for gamemaker) works. Here's a stripped down simple explanation of it:

When your two objects collide, they will be overlapping by a given amount. Using iteration you will push the objects apart until they are separate, for each iteration, you will also apply a speed change in the same direction as the position change. So basically the faster your objects are going, the more they overlap, the more they have to iterate the separation code, the more the speed is going to change. It looks something like this
GML:
repeat(precision_amount)
{
    if(collision = true)
    {
        //for your swinging thing
        image_angle += sign(the_angle_that_moves_away)
        angle_speed += sign(the_angle_that_moves_away)
        //for the thing colliding with it (the ratio of movement between the self and the other will determine the mass E.G. Have the self move twice as fast, and it will look twice at light
        x-= the_direction_that_moves_away
        y-= the_direction_that_moves_away
        hspeed-= the_direction_that_moves_away
        vspeed-= the_direction_that_moves_away
    }
    else
    {
        break;
    }

}
The reason most physics engines use this iterative approach is because if you have multiple collision at the same time, trying to workout the math would be insane! But with iteration, you just iterate all the collisions at the same time and it works its self out.
 

Petrik33

Member
This should be pretty straightforward to accomplish. All the code here is just pseudocode for explaining purposes. First set up a rotation and rotation speed.
GML:
angle_goal = 90;
image_angle = angle_goal;
angle_speed = 0;
Then have it always swing back to angle_goal;. You have to use angle_difference to work around the troubled you would have when between 0 and 360
GML:
angle_speed += sign(angle_difference(image_angle,angle_goal))
image_angle += angle_speed
Now for the collision. Instead of trying to workout the Newtonian physics using math like you would see in a textbook. I will use a more common approach to game physics that takes advantage of the computers strengths. This is exactly how most of box2D (the physics engine for gamemaker) works. Here's a stripped down simple explanation of it:

When your two objects collide, they will be overlapping by a given amount. Using iteration you will push the objects apart until they are separate, for each iteration, you will also apply a speed change in the same direction as the position change. So basically the faster your objects are going, the more they overlap, the more they have to iterate the separation code, the more the speed is going to change. It looks something like this
GML:
repeat(precision_amount)
{
    if(collision = true)
    {
        //for your swinging thing
        image_angle += sign(the_angle_that_moves_away)
        angle_speed += sign(the_angle_that_moves_away)
        //for the thing colliding with it (the ratio of movement between the self and the other will determine the mass E.G. Have the self move twice as fast, and it will look twice at light
        x-= the_direction_that_moves_away
        y-= the_direction_that_moves_away
        hspeed-= the_direction_that_moves_away
        vspeed-= the_direction_that_moves_away
    }
    else
    {
        break;
    }

}
The reason most physics engines use this iterative approach is because if you have multiple collision at the same time, trying to workout the math would be insane! But with iteration, you just iterate all the collisions at the same time and it works its self out.
So, well, thank you very much for your help! Although I can't apply your solution probably because I use different collision system, and also the event when the oscillating values are updated has to be single and doesn't depend on the collision.
Still, I guess I haven't explained the problem clearly, so it's my bad. What I think I should add, is that I don't truly need the true working physics, but rather just realistic, so the player gets what he expects, so this is why the way you achieve it doesn't matter so much.
 
So you "don't truly need the true working physics, but rather just realistic" Realistic and physics mean the same thing in this context. What you're saying is you want the simplest solution for having an object push away from you in a realistic way. The solution I provided is actually the simplest solution for this. I have a lot of experience with physics, and If you don't want to use iteration it's not going to be as easy and it's not going to look as good.

The reasons you stated for this not working don't add up. For the collision, I was just using a generic pseudocode collision (if collision = true) So any type of collision check should work. What type of collision check are you using? And also the place where the "oscillating is updated" doesn't depend on the collision (if by single you mean happens once per step, then it meets that criteria as well.) Just set angle_goal to your oscillating value in the step event and you will have your effect.

I'm not married to my solution, but I'm not sure what you are looking for. You should narrow the parameters of what you want a bit more clearly.
 
Last edited:

Petrik33

Member
So you "don't truly need the true working physics, but rather just realistic" Realistic and physics mean the same thing in this context. What you're saying is you want the simplest solution for having an object push away from you in a realistic way. The solution I provided is actually the simplest solution for this. I have a lot of experience with physics, and If you don't want to use iteration it's not going to be as easy and it's not going to look as good.

The reasons you stated for this not working don't add up. For the collision, I was just using a generic pseudocode collision (if collision = true) So any type of collision check should work. What type of collision check are you using? And also the place where the "oscillating is updated" doesn't depend on the collision (if by single you mean happens once per step, then it meets that criteria as well.) Just set angle_goal to your oscillating value in the step event and you will have your effect.

I'm not married to my solution, but I'm not sure what you are looking for. You should narrow the parameters of what you want a bit more clearly.
So, I am sorry if you understood me wrong, but the problem is that the push event happens not even because of the collision, it happens in the PushEntity Function which takes 3 arguments: speed, left-right, direction. So for the regular entities, it gives the entity horizontal and vertical speed depending on those 3 arguments. But for the dummy we don't need the angle, just speed and left-right, to decide whether the oscillations should speed up or slow down. So it doesn't take collisions into account at all. Like the dummy can be pushed by the ranged skill. It doesn't require any collision to do that, for example the skill blows the wind in some direction, it doesn't create any entity pushing other entities, it just calls PushEntity Function for all entities in range.
Still, I believe I am really just a real newbie and this is the only reason why I don't understand you. But I hope you can help me, I am just not sure about how your code works, and I am currently not able to test it.
 

Petrik33

Member
Do you want the objects to be able to overlap? so when you pass by the oscillating object it has more of a wind effect?
Hey man, I am sorry if I seem strange, but I guess I start understanding how this code works, like I if get it right, the angle speed has the constant acceleration which changes the sign after reaching equilibrium point, which is angle goal in this case, and then the speed will start getting smaller and smaller, and turn backwards, and this will lead to the proper oscillations, this can be really helpful, because I will then be able just to lower or higher the angle speed inside the PushEntity event, but one question remains for me: how do I make the oscillations fade with time in this case??
 
Hey man, I am sorry if I seem strange, but I guess I start understanding how this code works, like I if get it right, the angle speed has the constant acceleration which changes the sign after reaching equilibrium point, which is angle goal in this case, and then the speed will start getting smaller and smaller, and turn backwards, and this will lead to the proper oscillations, this can be really helpful, because I will then be able just to lower or higher the angle speed inside the PushEntity event, but one question remains for me: how do I make the oscillations fade with time in this case??
That is correct. To have the oscillation fade there are a few things you can mess around with, but here's just one solution:

Simply reduce the angle speed.
GML:
angle_friction = 0.2
angle_speed -= min(sign(angle_speed)*angle_friction,angle_speed)
//using min prevents over shooting.
//In plane language "if angle_speed is already
//smaller then the friction amount just set it to 0"
//without the overshooting protection it would look like this:
angle_speed -= sign(angle_speed)*angle_friction
//but this is never going to actually completely stop the angle_speed
Alternatively or even additionally you can move the angle toward its goal without adjusting the angle speed. like this:
GML:
image_angle += sign(angle_difference(image_angle,angle_goal))*amount
//you can use the same min funtion here to prevent overshooting
This will actually slow the oscillation as well, the reason it works is that it moves the angle towards the goal without speed up (effectively giving the angle_speed function less to work with.)
 
Or for a different looking effect you can use the previously mentioned reducing angle speed function in collaboration with this:
GML:
image_angle += angle_difference(image_angle,angle_goal))/20
//no overshooting protection needed as the value is ALWAYS a fraction of what it could be
effectively the farther away from angle_goal image_angle is, the MORE influence this stabilizing function has. The result is if you REALLY push the swing object hard. It initially REALLY bounces back. And when it gets closer to its goal it has less influence.
 
Last edited:

Petrik33

Member
Or for a different looking effect you can use the previously mentioned reducing angle speed function in collaboration with this:
GML:
image_angle += angle_difference(image_angle,angle_goal))/20
//no overshooting protection needed as the value is ALWAYS a fraction of what it could be
effectively the farther away from angle_goal image_angle is, the MORE influence this stabilizing function has. The result is if you REALLY push the swing object hard. It initially REALLY bounces back. And when it gets closer to its goal it has less influence.
Hey, this solution is perfect, although I had to switch the angles in the angle difference everything is working just great, although it's quite hard to determine values controlling the oscillations, but it is my problem and I have almost solved it.
Though, there is one thing: the
image_angle += angle_difference(image_angle,angle_goal))/20
doesn't lead to the full stop of the oscillation and I can't find the solution for that myself. I am very grateful for the amount of help you have already given, but solving this little problem would be just perfect!
 
That's good to hear! The image_angle += angle_difference(image_angle,angle_goal))/20 wouldn't lead to a full stop. It only has influence in the extremes. It's almost like a soft bounding box, that only kicks in when things get too crazy. To achieve a full stop I think you could use a combination of the first two functions.

Here I added a "min" gate to the second option to get a full stop.
GML:
var amount = 2//how many degrees to move toward goal. Try large value if your not getting that full stop. 
var angle_dif = angle_difference(angle_goal,image_angle)//not sure if I got it right this time
image_angle += min(sign(angle_dif)*amount,angle_dif)
//the "min" funtion is used here to say: if "angleDif" is larger then "amount" (2) only move it
//an "amount" (2) degrees, otherwise move it angleDif degrees (Wich effectivly
//just sets image_angle = angle_goal
I'm glad you were able to switch angles in the angle difference effectively. You might have to do the same again I don't know. Let me know how this pans out...
 

Petrik33

Member
That's good to hear! The image_angle += angle_difference(image_angle,angle_goal))/20 wouldn't lead to a full stop. It only has influence in the extremes. It's almost like a soft bounding box, that only kicks in when things get too crazy. To achieve a full stop I think you could use a combination of the first two functions.

Here I added a "min" gate to the second option to get a full stop.
GML:
var amount = 2//how many degrees to move toward goal. Try large value if your not getting that full stop.
var angle_dif = angle_difference(angle_goal,image_angle)//not sure if I got it right this time
image_angle += min(sign(angle_dif)*amount,angle_dif)
//the "min" funtion is used here to say: if "angleDif" is larger then "amount" (2) only move it
//an "amount" (2) degrees, otherwise move it angleDif degrees (Wich effectivly
//just sets image_angle = angle_goal
I'm glad you were able to switch angles in the angle difference effectively. You might have to do the same again I don't know. Let me know how this pans out...
GML:
image_angle += angle_speed;
    
var _angles_difference = angle_difference(image_angle,angle_goal);
angle_speed -= sign(_angles_difference) * angle_acceleration;

if(abs(_angles_difference)>1.5)
{
    image_angle -= _angles_difference/friction_K;
}
else
{
    image_angle -= _angles_difference
}
So, this is actually what I ended up with, works pretty fine, just like I wanted it to work. Thank you very much for your help, guess I can now finally mark this thread as SOLVED
 
Top