GameMaker [Solved] Move Object Forward Relative to Current Angle With Keyboard

U

Unpopularstream

Guest
I've got an object rotating left and right using A and D with added friction. The rotation works great but I want the object to move forward based on where the front of the sprite is rotated, not just up on the y-axis. Here's my current code so far

Code:
Create Event:
rotation = 0

Step Event:
// Value of _Direction will be either -1, 0, or 1.
direction = keyboard_check(ord("A")) - keyboard_check(ord("D"));
// Lerp between current rotation value and direction by 1% (increase to make acceleration / deceleration faster).
rotation = lerp(rotation, direction, .02);
// Add to image angle
image_angle += 3*rotation;
//Move toward front of sprite
direction = image_angle
Edit: I want to tie moving forward to W
 
Last edited by a moderator:
O

Obrez

Guest
If I'm understanding correctly, you could just add "speed = <insert speed here>;" since you are already setting your direction based on the image angle.

If you don't want to use built in speed, you can use the lengthdir functions to achieve the same thing.
Code:
x = x+lengthdir_x(speed,direction);
y = y+lengthdir_y(speed,direction);
 
U

Unpopularstream

Guest
If I'm understanding correctly, you could just add "speed = <insert speed here>;" since you are already setting your direction based on the image angle.

If you don't want to use built in speed, you can use the lengthdir functions to achieve the same thing.
Code:
x = x+lengthdir_x(speed,direction);
y = y+lengthdir_y(speed,direction);
I forgot to mention I want to tie it to a keyboard_check(ord("W"))
 
O

Obrez

Guest
Code:
var spd = keyboard_check(ord("W")) * <insert desired speed here>;
x = x+lengthdir_x(spd,direction);
y = y+lengthdir_y(spd,direction);
That creates a variable named "spd" which will be your speed number. If I'm remembering correctly, the keyboard_check will return true or false (1 or 0 respectively). Multiply that by whatever amount you want for your speed and use that in the lengthdir functions. Lets pretend you want the speed to be 5. If you are not pressing W then 0 * 5 = 0 so you go nowhere. If you are pressing W then it will be 1 * 5 = 5 so you will move 5 units/pixels.

If you want to use built in speed then just do:
Code:
speed = keyboard_check(ord("W")) * <desired speed>;
 
U

Unpopularstream

Guest
That worked great! Now I just gotta figure out how to apply the same friction to forward movement
 
O

Obrez

Guest
You could probably repurpose that lerp function to do the same thing.

I usually do something like this:
Create event:
Code:
spd = 0;
accel = 1;
decel = 1;
max_spd = 5;
Step event:
Code:
//increase spd by amount: "accel"
spd += keyboard_check(ord("W")) * accel;
//if not holding W then decelerate
if not keyboard_check(ord("W")) then
     {spd -= decel;}
//don't allow spd to be greater than max_spd
if spd >= max_spd then
     {spd = max_spd;}
//don't allow spd to be less than 0
if spd <= 0 then
     {spd = 0;}
//move the x/y to the desired resulting x/y
x = x+lengthdir_x(spd,direction);
y = y+lengthdir_y(spd,direction);
You can play around with the values in the create event to change acceleration and deceleration. I'm just getting back into this and I haven't tested that and I only have GMS1 but it should work.
 
U

Unpopularstream

Guest
It almost works but it never stops decelerating and goes off the screen and when it accelerates it gets to max_spd too fast

I apologize for the stupidity, I'm super dumb when it comes to gml and this code is more or less a frankenstein of other helpful users code, but this is what I have

Code:
Create Event:

rotation = 0;
thrust = 0;
spd = 0;
accel = .01;
decel = 0;
max_spd = 5;


Step Event:

thrust = keyboard_check(ord("W")) * 5;
x = x+lengthdir_x(thrust,direction);
y = y+lengthdir_y(thrust,direction);
spd += keyboard_check(ord("W")) * accel;
if not keyboard_check(ord("W")) then
     {spd -= decel;}
//don't allow spd to be greater than max_spd
if spd >= max_spd then
     {spd = max_spd;}
//move the x/y to the desired resulting x/y
x = x+lengthdir_x(spd,direction);
y = y+lengthdir_y(spd,direction);
// Value of _Direction will be either -1, 0, or 1.
direction = keyboard_check(ord("A")) - keyboard_check(ord("D"));
// Lerp between current rotation value and direction by 1% (increase to make acceleration / deceleration faster).
rotation = lerp(rotation, direction, .02);
// Add to image angle
image_angle += 3*rotation;
// Move toward front of sprite
direction = image_angle
Also I changed the "spd" variable to "thrust"
 
O

Obrez

Guest
Taking a quick glance at it I can see a few things.
1. You are moving twice per step. Once with thrust, which will always be 5 or 0, hence why you are accelerating to max speed instantly when using thrust, then you are moving again after that with spd. Thrust and accel appear to be the same idea, just implemented differently but at the same time.
2. Your decel variable is 0 so it will never slow down if you stop pressing W. You're just doing spd - 0 for deceleration so it has no affect.

Let me tinker a bit and come back.

Edit:
Ok, try this:

Create event:
Code:
rotation = 0;
thrust = 0.01;
spd = 0;
decel = 1;
max_spd = 5;
Step event:
Code:
// Value of _Direction will be either -1, 0, or 1.
direction = keyboard_check(ord("A")) - keyboard_check(ord("D"));
// Lerp between current rotation value and direction by 1% (increase to make acceleration / deceleration faster).
rotation = lerp(rotation, direction, .02);
// Add to image angle
image_angle += 3*rotation;
// Move toward front of sprite
direction = image_angle

//Increase speed by amount of <thrust>
spd += keyboard_check(ord("W")) * thrust;
//If you are not holding W then decrease speed by amount of <decel>
if not keyboard_check(ord("W")) then
     {spd -= decel;}
//don't allow spd to be greater than max_spd
if spd > max_spd then
     {spd = max_spd;}
//don't allow spd to be less than 0
if spd < 0 then
     {spd = 0;}
//move the x/y to the desired resulting x/y
x = x+lengthdir_x(spd,direction);
y = y+lengthdir_y(spd,direction);
I replaced my "accel" variable to your "thrust" variable. I also put the forward movement after the angle so you are always moving directly forward. You can switch it back if you need.

So when you are holding W, your "spd" will increase by "thrust" (.01). If it ever goes over "max_spd" (5), you automatically set it back to "max_spd" to limit top speed.

Similarly, if you are not holding W, then spd will decrease by "decel" (1). If speed ever goes below 0 then it will be set back to 0 for a complete stop.

Once the thrust or deceleration are determined, you now have a final "spd". You add that with the lengthdir functions to actually move by whatever "spd" is after adding thrust/deceleration.

If you want to change the thrust or deceleration amount, do so by changing the value in the create event only.
 
Last edited by a moderator:
U

Unpopularstream

Guest
Taking a quick glance at it I can see a few things.
1. You are moving twice per step. Once with thrust, which will always be 5 or 0, hence why you are accelerating to max speed instantly when using thrust, then you are moving again after that with spd.
2. Your decel variable is 0 so it will never slow down if you stop pressing W. You're just doing spd - 0 for deceleration so it has no affect.

Let me tinker a bit and come back.
The decel was at 1 but I put it to 0 till I could figure out why it didnt stop. And I forgot to replace all the spd for thrust, but that's all it is, just a replacement name. But thank you for helping
 
O

Obrez

Guest
I finished some edits to that post while you were typing. You were also moving twice per step. Try the new code. I didn't actually test it but it should work.
 
U

Unpopularstream

Guest
Alright that was amazing. I do have to ask tho how to switch the rotation priority tho. It's going to be a spaceship so I'd prefer the other way
 
O

Obrez

Guest
Well, you can just move the second chunk of that step code above the first. However, that will provide very little difference, so little you might not even notice it since you are always either moving exactly in the current direction you are facing or the direction you were facing one step previously, which, assuming your game speed isn't VERY low, will be fractions of a second.

I'm assuming you want the spaceship to basically so some sick drifts. I'd have to tinker more. I guess theoretically you could just do a second lerp rotation that is slower. One lerp rotation to actually turn the angle/direction of the ship and the other to change the direction of the movement. Not sure if that's what you are going for.
 
U

Unpopularstream

Guest
Well sick drifts wasn't the plan but that does sound enticing. Just so that it accurately portrays what the actual behavior would be like in space. Thrust in a direction, release the thrust, and be able to rotate while going in the original direction instead of continuously following the front of the ship
 
O

Obrez

Guest
Ok, then you need to remove your "direction = image_angle" bit since direction is for movement and image_angle is for which way the ship is facing.
Try this for the step event.

EDIT: WON'T WORK, just ignore this post

Code:
<fail>
Now, technically that's still not correct, since you will immediately move in the direction you are facing instead of continuing to slide a bit, but it should show you the difference that would make. I think the second lerp for the direction variable will be the way to go.

You can also add speed in certain directions to mimic actual "friction" like what you are describing but I'd have to do some refreshing before I could do that with any real efficiency. I've been out of the game for a while.
 
Last edited by a moderator:
U

Unpopularstream

Guest
I'll have to take some time and figure out how to add that in but thank you so much for getting me closer and taking the time in general to help me out, it means a lot <3
 
O

Obrez

Guest
Create event:
Code:
rotation = 0;
rotate = 0;
thrust = 0.01;
spd = 0;
decel = 0.01;
max_spd = 5;
turn_lag = 2;
Step event:
Code:
// Value of _Direction will be either -1, 0, or 1.
rotate = keyboard_check(ord("A")) - keyboard_check(ord("D"));
// Lerp between current rotation value and direction by 1% (increase to make acceleration / deceleration faster).
rotation = lerp(rotation, rotate, .02);
// Add to image angle
image_angle += 3*rotation;

//If you are holding W then increase speed by amount of <thrust> and rotate direction of movement toward direction you are facing by amount of <turn_lag> or 
//the difference between current ship facing direction and current movement direction, whichever is smaller.
if keyboard_check(ord("W")) then
     {
     spd += thrust;
     var ang_dif = angle_difference(image_angle, direction);
     direction += min(abs(ang_dif), turn_lag) * sign(ang_dif);
     }
else
//otherwise, if you are not holding W, then decrease speed by amount of <decel>
     {spd -= decel;}
//don't allow spd to be greater than max_spd
if spd > max_spd then
     {spd = max_spd;}
//don't allow spd to be less than 0
if spd < 0 then
     {spd = 0;}
//move the x/y to the desired resulting x/y
x = x+lengthdir_x(spd,direction);
y = y+lengthdir_y(spd,direction);
"Direction" is a built in variable best used for movement. I added another "rotate" variable to take it's place in determining which way the ship should face. The function I was looking for is angle_difference, which happen to have a perfect example of a "rotate towards" code which I implemented. Plug this all in, I think you'll be happy with the result. Change the value of "turn_lag" in the create event so make things more or less drifty.
 
U

Unpopularstream

Guest
I gotta say I honestly didn't expect another reply. Like it's all so crisp omg thank you so much. I added a brake as well with spd -= 2*decel for full freedom. If this thing ever gets finished you'll be in the credits for sure. Thank you so much
 
O

Obrez

Guest
My pleasure. Happy coding!

You might also need to add "if spd = 0 then {direction = image_angle;}"

in between your ship rotation and movement chunks of code otherwise it might be a little wonky if you try to accelerate from a complete stop while rotating. Technically, it might be best to change the turn_lag based on current speed. Not sure, I already closed out my test project without saving.
 
Last edited by a moderator:
Top