• Hey Guest! Ever feel like entering a Game Jam, but the time limit is always too much pressure? We get it... You lead a hectic life and dedicating 3 whole days to make a game just doesn't work for you! So, why not enter the GMC SLOW JAM? Take your time! Kick back and make your game over 4 months! Interested? Then just click here!

Turning the image_angle with steering

U

Undead Hero

Guest
This is kinda hard to describe, but I'm completely stumped and could use some direction.

Essentially, I have an object moving to the right shmup style. You use the mouse to direct this object up and down on the y axis. Currently when you click inside the play area, this object matches the mouse_y and sticks to it.

What I'm trying to do, is make the object angle up and down as it travels up and down, to look like steering. The angle would gradually increase based on the speed, so it would angle more sharply if you quickly drag up or down. I tried making it point towards the mouse, but since it sits on the mouse's y position that doesn't work. I guess I just don't know how to make the game recognize whether it's traveling up or down, and at what speed.
 
S

skeer

Guest
Depending on what method you want to use, you can go presice but slow by using arctangent, or if you have patience you can tweak a function til you get results you like. I'll explain the second method, because calculating arctangent every frame sounds awful.

Let xspeed be the movement forward, and yspeed the movement up or down. What we want isn't an exact angle, but something close enough to looking correct. We can use an array containing values marking the boundaries of each animation change. To keep it simple, we will mark every ten degrees of turning. The formula we will use is:

tan(angle)=yspeed/xspeed

I'm guessing the ship never stops moving forward, so no divide by 0 error. Our example table will look like this:

80 degrees = 5.67
70 = 2.75
60 = 1.73
50 = 1.19
40 = 0.84
30 = 0.58
20 = 0.36
10 = 0.18
0 = 0
-10 = -0.18
Etc

So if the player moves forward 10 pixels and up 24, 24/10= 2.4, so angle the ship somewhere between 60 and 70 degrees.

You can use if statements with something like:

If(1.73<=yoverx&&yoverx<=2.75){
//insert rotation code for rotating 65 degrees maybe
}

Hopefully this helps and makes sense. Maybe someone can verify that this works :)
 
U

Undead Hero

Guest
Oh god math. Okay, I think I follow that. Figures that something mostly cosmetic would be more complex than anything else. I'll plug it in tomorrow and report back.
 
U

Undead Hero

Guest
Alright, the problem I'm having is I don't know how to store the yspeed. When you're moving the object, it's basically y = mouse_y. This is meant to be used on a touch screen, and this feels the best. How then do I call on however many pixels it's moving up or down to determine the angle needed?
 
S

skeer

Guest
yspeed is the new y minus the old y if I remember correctly, so:

oldy=y;
y=mouse_y;
yspeed=y-oldy;

Keep me updated, and feel free to ask more questions :)
 
U

Undead Hero

Guest
Alright, bear with me... I have some quick and dirty code just to try and get the basic concept working, but I'm running into a problem which you'll see. And just to be clear, I'm using a global variable as a lazy way to store yspeed so that I can access it in the draw event (I drew the yspeed on the screen for debug purposes) and I'm not using xspeed, because I decided to make the player stay still on the x axis and scroll the background (bites the issue of room transitions in the butt). Just to be safe, I toyed with moving the player on the x axis and using the formula to make sure that wasn't the issue, and I had the same problem.

Code:
if CarGrabbed = true {
    oldy = y;
    y = median(RoadTopY, mouse_y, RoadBottomY)
    global.yspeed = y - oldy;
  
    if global.yspeed > -1 && global.yspeed < 1 {
        image_angle = 0;
    } else if global.yspeed <= -1 && global.yspeed > -5 {
        image_angle = 10;
    } else if global.yspeed <= -5 && global.yspeed > -10 {
        image_angle = 20;
    } else if global.yspeed <= -10 && global.yspeed > -15 {
        image_angle = 30;
    } else if global.yspeed <= -15 && global.yspeed > -20 {
        image_angle = 40;
    } else if global.yspeed <= -20 {
        image_angle = 50;
    } else if global.yspeed >= 1 && global.yspeed < 5 {
        image_angle = -10;
    } else if global.yspeed >= 5 && global.yspeed < 10 {
        image_angle = -20;
    } else if global.yspeed >= 10 && global.yspeed < 15 {
        image_angle = -30;
    } else if global.yspeed >= 15 && global.yspeed < 20 {
        image_angle = -40;
    } else if global.yspeed >= 20 {
        image_angle = -50;
    }
  
} else if CarGrabbed = false {
    global.yspeed = 0;
    image_angle = 0;
}
http://imgur.com/a/G9b1U

The image capture is a little weird, but it basically shows the problem. The rotations are very jerky, and the culprit seems to be that the yspeed doesn't scale smoothly. It's constantly jumping around. As a test, I tried using larger numbers for the yspeed checks, and even though that smoothed it out considerably, at lower speeds the yspeed just kept jumping back and forth from 0. Any ideas?

Thank you very much for all the help, by the way.
 
S

skeer

Guest
The video isn't loading for some reason. Even if it's the background scrolling, you will still need to divide yspeed by the speed the background scrolls, otherwise the turning will be massively off.

The other possible problem is that the angle does a split second jerk, and then goes straight again. To fix this, we can make the angle transistion instead of jump. instead of the degrees being stored to image_angle, we can store it in desired_image_angle. Then we pick a method to transistion from our current angle to the desired angle. One of these might work:

if(image_angle>desired_image_angle) image_angle=image_angle-1;
if(image_angle<desired_image_angle) image_angle=image_angle+1;

(IDK if image_angle++ and image_angle-- exist in gamemaker) Or:

image_angle=image_angle+(desired_image_angle-image_angle)*factor;

Where factor is a decimal number between 0 and 1. 0 means don't turn at all, and 1 means jerk. You would probably want to start with 0.5 and start tweaking it. Sorry that I don't have a better method that's tried and true, guessing is the best I can do sometimes.
 
T

TheTrophieStars

Guest
You could set the object to be behind the mouse.

If its supposed to be to the left of it:
x = mouse_x - 30; //Or some other value

if under:
y = mouse_y + 30; //Or some other value

then

image_angle = point_direction(x, y, mouse_x, mosue_y);
 
check this out. This is just normal movement except setting hspeed to zero. You can clamp image_angle as well if you don't want the vehicle to be able to turn outside of a certain range of angles.

Code:
//get direction to mouse
var _m = point_direction(x,y,mouse_x,mouse_y);

//change angle of vehicle
//clamp angle_difference to simulate max turn ammount (i.e. how much image_angle can change in 1 step)
image_angle += clamp(angle_difference(_m,image_angle),-2,2);
direction = image_angle;

//movement
speed = 10;
hspeed = 0;
Here's a gfycat of the result:
https://gfycat.com/LikelySilkyHadrosaurus

The above code, is the same as doing this (where "road_speed" would be the same as "speed" in the code above). Only in this case we are not setting the built-in variable "speed" and then cancelling the hspeed component, instead we are bypassing all that and just setting the vspeed directly.
Code:
var _m = point_direction(x,y,mouse_x,mouse_y);
image_angle += clamp(angle_difference(_m,image_angle),-1,1);
vspeed = -dsin(image_angle)*road_speed;
 
Last edited:
U

Undead Hero

Guest
Unfortunately, that code doesn't work in this specific scenario because the car is attached to the mouse's y axis. I tried experimenting before with having the car "move" to the mouse, but it didn't feel right for the type of game I'm trying to make (speed and reflexes are key). However, I wasn't aware of the "clamp" function and godammit is that going to be useful.

We're so close that it's painful. Your modified code, skeer, feels great and makes the turning very smooth. The problem I'm still running into, which is evident from drawing the yspeed on the screen, is with slower mouse movements the yspeed keeps jumping between 0 and 4. This makes the sprite start jiggling back and forth quickly.
 
Okay, how about this then?

Code:
image_angle = darcsin(clamp(y-mouse_y,-road_speed,road_speed)/road_speed);
y = mouse_y;
the vehicle is free to move any amount along the y axis. the difference between y and mouse_y is clamped to avoid having darcsin throw an error. If you move the car really fast along the y axis it will apear to turn at 90 degree angles. If you don't like the way that looks, you could try this instead:

Code:
image_angle = point_direction(0,y,road_speed,mouse_y);
y = mouse_y;
In that second example, "road_speed" is the horizontal speed of the car, but not it's total speed, like in the first example.

I like the way the second one looks better, even though the first one makes more logical sense to me.

BTW, using trig a few times every frame is no problem, the biggest bottleneck in GML is the actual language itself. You'll want to do whatever you can using built-in functions rather than using extra lines of GML.
 
Last edited:
S

skeer

Guest
So slight mouse motions cause too big of turns? If I had to guess, I'd say you need either need to make the range for 0 degrees bigger, such as -5 to 5 instead of -1 to 1. If that doesn't work, try making a an else if statement between 0 and 10 degrees for a 5 degree rotation. Don't forget to repeat this for -5.

Glad to hear that it's helping so far, guessing ftw I guess ;)
 
U

Undead Hero

Guest
I'm going to plug this stuff in tomorrow and report back. Thanks a lot to everyone for the help. As silly as it is, I think this will wind up being the most complicated part of this project.
 
U

Undead Hero

Guest
Sorry for not updating yesterday, wound up having family over.

I think the case is closed for the time being. It isn't perfect, but I'm confident I can tweak it from here to make it so. Using smaller rotations at lower yspeeds helped to smooth out the twitches, and the turning looks great at higher speeds. The code is a mess right now though, and super excessive. I'm going to have to see if I can clean it up with an array and loop statement, but that's not something I'm very experienced with, so I'll just have to dig in and figure it out later. Using pure math would certainly be a lot easier and less resource intensive, but I couldn't get it to look quite how I want it to. And I'm a dummy and don't understand trigonometry.

Code:
if CarGrabbed = true {

    oldy = y;
    y = median(RoadTopY, mouse_y, RoadBottomY)
    global.yspeed = y - oldy;
   
    desired_image_angle = 0
    TurnFactor = .2
   
    if global.yspeed > -1 && global.yspeed < 1 {
        desired_image_angle = 0
        image_angle = image_angle+(desired_image_angle-image_angle)*.4;
       
    } else if global.yspeed <= -1 && global.yspeed > -10 {
        desired_image_angle = 5;
        image_angle = image_angle+(desired_image_angle-image_angle)*TurnFactor;
    } else if global.yspeed <= -10 && global.yspeed > -40 {
        desired_image_angle = 10;
        image_angle = image_angle+(desired_image_angle-image_angle)*TurnFactor;
    } else if global.yspeed <= -40 && global.yspeed > -60 {
        desired_image_angle = 20;
        image_angle = image_angle+(desired_image_angle-image_angle)*TurnFactor;
    } else if global.yspeed <= -60 && global.yspeed > -80 {
        desired_image_angle = 30;
        image_angle = image_angle+(desired_image_angle-image_angle)*TurnFactor;
    } else if global.yspeed <= -80 && global.yspeed > -100 {
        desired_image_angle = 40;
        image_angle = image_angle+(desired_image_angle-image_angle)*TurnFactor;
    } else if global.yspeed <= -100 {
        desired_image_angle = 50;
        image_angle = image_angle+(desired_image_angle-image_angle)*TurnFactor;

    } else if global.yspeed >= 1 && global.yspeed < 10 {
        desired_image_angle = -5;
        image_angle = image_angle+(desired_image_angle-image_angle)*TurnFactor;
    } else if global.yspeed <= -10 && global.yspeed > -40 {
        desired_image_angle = 10;
        image_angle = image_angle+(desired_image_angle-image_angle)*TurnFactor;
    } else if global.yspeed >= 40 && global.yspeed < 60 {
        desired_image_angle = -20;
        image_angle = image_angle+(desired_image_angle-image_angle)*TurnFactor;
    } else if global.yspeed >= 60 && global.yspeed < 80 {
        desired_image_angle = -30;
        image_angle = image_angle+(desired_image_angle-image_angle)*TurnFactor;
    } else if global.yspeed >= 80 && global.yspeed < 100 {
        desired_image_angle = -40;
        image_angle = image_angle+(desired_image_angle-image_angle)*TurnFactor;
    } else if global.yspeed >= 100 {
        desired_image_angle = -50;
        image_angle = image_angle+(desired_image_angle-image_angle)*TurnFactor;
    }
   
} else if CarGrabbed = false {
    global.yspeed = 0;
    image_angle = 0;
}
Thanks everyone for the assistance, I learned a lot and I don't think I could have gotten here on my own! Love this community.
 
Top