GML spawning a bullet while rotating

J

jobel

Guest
when my player is at angles 0, 90, 180 or 270 the bullets look correct.

if my player is between those angles then the bullet spawns off to the right/left of where it should be.

Code:
var _xx = x + lengthdir_x(20, image_angle);
var _yy = y + lengthdir_y(0, image_angle);
  
instance_create_layer(_xx,_yy,"Instances",obj_bullet);
this makes no sense to me... what's the point of lengthdir taking image_angle as an argument???
 

Binsk

Member
I don't know how it could be spawning correctly at 90 and 270 if your lengthdir_y distance is set to 0. Also, if you don't know what a function does either don't use it or look it up in the manual.

The lengthdir functions convert values in Polar coordinates into Cartesian coordinates (aka, circular into rectangular). Cartesian coordinates require an x-axis and a y-axis to determine position. Polar coordinates require theta (the angle around the origin) and a distance (from the origin along the angle) to determine position.

Your length value in those function is acting as the "distance" parameter and the image_angle is acting as the "theta" parameter. You need the image_angle because that is an essential value for defining a position in polar coordinates (it's like asking why you need an x-value when defining the position in a room).

The general use-case for lengthdir_x/y is to make things orbit things or be positioned around something in a circular range. Giving different distance values between the x and y will make it more oval instead of circular and the image_angle defines which part of the edge of the circle you want (starting at the right-most point and moving counter-clockwise).

In your code above, you are basically defining an oval with zero height and 20px radius (which is essentially just a 40px long line). So your bullet will be spawned somewhere on that line. If you want your bullet to spawn in a circular radius around your character, change the 0 in the lengthdir_y to match the value in lengthdir_x. If you aren't trying to spawn in a circular radius at all then you don't need the lengthdir functions at all.
 
J

jobel

Guest
Also, if you don't know what a function does either don't use it or look it up in the manual.
of course I looked it up in the manual.. how else would I learn to use such a strange function? I searched everything, watched tutorials on it and it still doesn't make sense. In the manual they show a little guy where you calculate the x/y from his origin. You also use the image_angle in the calc but it doesn't show how you would make something spawn always to the right of a rotating player - that was the missing piece I wasn't understanding.

In your code above, you are basically defining an oval with zero height and 20px radius (which is essentially just a 40px long line). So your bullet will be spawned somewhere on that line. If you want your bullet to spawn in a circular radius around your character, change the 0 in the lengthdir_y to match the value in lengthdir_x. If you aren't trying to spawn in a circular radius at all then you don't need the lengthdir functions at all.
I changed the xx/yy to both be 20 and now it works perfectly. However, that still makes no sense to me. lengthdir_x() and lengthdir_y() should be consolidated into 1 function lengthdir() since you aren't actually calculating the x/y of the position you want relative to the origin. Instead you have to calculate the angle when you are already using x/y! the result is it's extremely confusing and not intuitive. Why would I give a 20/20 x/y coordinate of something I want to always to appear 20 pixels in front of my sprite? it should be 20/0 x/y and I should not have to give the angle! x/y is already an angle as well as distance!
 
Last edited by a moderator:

TsukaYuriko

☄️
Forum Staff
Moderator
It's not a 20/20 x/y coordinate. It's a 20/20 length in the given direction, first on the x axis, then on the y axis. Note that these two functions are merely wrappers around the trigonometric functions sin and cos that can be used to calculate a point that is at an offset of len pixels in direction dir.

You could also think of them like this: The len argument specifies the maximum distance of the offset on that specific axis (depending on the direction, it may be anywhere between len and -len).
If you were to plot points on your screen while continuously incrementing dir, using equal len for both x and y would plot a circle.
Using half the value of x for y would draw a long oval. Half the value of y for x would draw a tall oval.
Insert 0 for y (equivalent to omitting any lengthdir calculation) and you'd get a horizontal line. 0 for x results in a vertical line.
 
J

jobel

Guest
You could also think of them like this: The len argument specifies the maximum distance of the offset on that specific axis (depending on the direction, it may be anywhere between len and -len).
If you were to plot points on your screen while continuously incrementing dir, using equal len for both x and y would plot a circle.
Using half the value of x for y would draw a long oval. Half the value of y for x would draw a tall oval.
Insert 0 for y (equivalent to omitting any lengthdir calculation) and you'd get a horizontal line. 0 for x results in a vertical line.
yes the only reason to make it this confusing would be to give the ability to make irregular shapes in orbit around the origin - which I guess could be interesting - but way less useful than a relative x/y coordinate.

Let's say I have a steam engine train and I want smoke to puff out the top.. the train is drawn facing right and the origin is x/y 16x16. I want the smoke to spawn at the smoke stack which is at x/y 26/3 - and the train goes up and down hills - so the angle does change. So now I have to use lengthdir 10/10 and figure out what that angle is from the origin. That's not a very useful function since I still have to do a lot of the work.

do you know of any function that deals in relative x/y values instead of lengthdir?
 

Binsk

Member
If you are handling rotations then you generally want Polar coordinates (which would be using lengthdir) as Cartesian coordinates (x/y) are TERRIBLE for circular things.
However, that still makes no sense to me. lengthdir_x() and lengthdir_y() should be consolidated into 1 function lengthdir() since you aren't actually calculating the x/y of the position you want relative to the origin. Instead you have to calculate the angle when you are already using x/y!
Lengthdir isn't FOR calculating the angle. It is for taking an angle/length pair and converting them into x/y values (aka, converting FROM Polar). If you want to calculate an angle/distance pair from x/y (aka, TO Polar) then use the functions point_distance and point_direction to get the values.

In the case of your train, take a look at what is going on from a coordinate perspective. You want to find a linear offset (aka, the offset from the origin initially). Cartesian coordinates are great for this, polar is a pain. However, then you want to rotate the train which is a circular function. Polar coordinates are great for this, Cartesian is a pain. The goal here is to use Cartesian for the offset, convert it to Polar when you need to rotate, then convert it back to Cartesian to draw it. Whether you use lengthdir or trig functions, you are going to have to do it in some way or another which requires some math.

So to solve the train problem, you can offset easily in Cartesian w/ x/y to get the position of the smoke. You can calculate the Polar representation very easily via point_distance and point_direction between the origin and the point of smoke when not rotated. When you rotate the train, all you have to do is grab these already-calculated Polar versions, add to the angle the rotation of the train then calculate the new position back into Cartesian w/ lengthdir.

So yes, you will still have to do a lot of the math work, there isn't a simple way to do it, sadly. Does this make more sense as to why you would need lengthdir? I mean, you could pre-calculate all the values and hard-code them in, but I feel like that is a pretty tacky way of doing things. Other than that you will have to calculate them on the fly via lengthdir. Lengthdir is just hiding basic trigonometry functions which, if you know trig, you will realize it is required for anything circular.
 

TsukaYuriko

☄️
Forum Staff
Moderator
You can use vectors if you're more comfortable with linear algebra. That's pure math, not a function.

You don't have to calculate any angles if you're trying to position things in directions other than straight ahead, there's point_direction and point_distance to do that for you.

If this is still not to your liking, I suggest creating a script that takes origin coordinates and target coordinates as arguments, calculates the corresponding direction and distance between the origin and target, adds the origin coordinates and the lengthdir'd direction and distance together and returns a two-element array. That should be the function you're looking for, if I understood you correctly?
 
J

jobel

Guest
@Binsk I now understand it, I'm merely saying it's not that helpful. Other game engines I've used let you deal with relative x/y or by using "image points" which are in addition to the origin. It's WAY faster and a lot more user friendly with less trial and error. Whenever I'm placing something that is relative to my sprite I don't want to calculate the angle/len of a location. 2D art is all x/y. If my 64/64 player has a gun that is at 50/20 I'd rather not have to figure out what the angle 32/32 to 50/20 is...nevermind if that sprite had 15 frames of animation and the gun was moving with each frame.
 
J

jobel

Guest
@YsukaYuriko seems like you could combine length_dir and point_direction to make a useful function!
The amount of time I've spent (since I figured out what was happening) placing things is incredibly time consuming compared to other engines I've used.
 
Last edited by a moderator:

TsukaYuriko

☄️
Forum Staff
Moderator
Now that you mention that term, attachment points can already be used with sprites based on skeletal animation (for example Spine). Attachment points for non-skeletal sprites are under the planned features - until then, those two functions can indeed be combined to create a function possessing greater power and user-friendliness for your case.

There are a few tricks to make this work seamlessly with multiple subimages with different attachment points, as well as with multiple sprites. Setting up the attachment points is manual, but it would be manual in one way or another, either way. The actual setup to automate the process is minimal.

For subimages, instead of hard-coding an attachment point, store it in an array, one array index per subimage. You can then automatically reference the correct coordinates by referencing array[floor(image_index)].

It's similar with sprites, except with a map. The key is the sprite resource, the value is the array that stores its subimages' attachment point. The correct map to use can then be found via map(? sprite_index).

Chain finding the right sprite and subimage together, and this setup becomes fully automated by using the mechanisms that are already provided with the engine by default - sprite and subimage.
 
J

jobel

Guest
@TsukaYurika when you say 'subimage' are you referring to an animation frame? I don't see anything in the doc when seraching on 'subimage'.
 

woods

Member
obj_player step event to fire bullet
Code:
if keyboard_check_pressed(vk_space)
{
instance_create(x,y,obj_bullet)
}
obj_bullet create event
Code:
speed = 8;
direction = obj_player.image_angle;
image_angle = obj_player.image_angle;
 

TsukaYuriko

☄️
Forum Staff
Moderator
@TsukaYurika when you say 'subimage' are you referring to an animation frame? I don't see anything in the doc when seraching on 'subimage'.
Yes. That's either referred to as a subimage (taken from draw_sprite's subimg argument) or frame in GM.
 
J

jobel

Guest
obj_player step event to fire bullet
Code:
if keyboard_check_pressed(vk_space)
{
instance_create(x,y,obj_bullet)
}
the problem is this creates the bullet at the origin of the player, not where the player's gun is in the sprite itself. I've since figured out the problem is just that GML is not great at helping you determine where these attach points are located.
 

woods

Member
instance_create(x,y,obj_bullet)

would be nice if we could go....
instance_create(rotation,obj_bullet)
 
J

jobel

Guest
I would rather:

player_image_point[0,0] = 40
player_image_point[0,1] = 32
player_image_point[1,0] = 50
player_image_point[1,1] = 20

instance_create_at_image_point(obj_player, obj_bullet_shell, player_image_point[0])
instance_create_at_image_point(obj_player, obj_bullet, player_image_point[1])
 
Top