Rotating one game object around another
Page 1 of 111 Replies  2532 Views  Last Post: 01 December 2012  07:39 AM
#1
Rotating one game object around another
Posted 01 November 2012  06:03 PM
The use case for my first example involves player movement; when they move their ship forward, the pod rotates clockwise around the ship. If they move backwards, it rotates counterclockwise. In the second use case, I just want the option to put a slight curve on my bullets as they emerge.
Any insights, tips or thoughts? I'm really stumped on this one  trigonometry isn't my strong suit.
Replies To: Rotating one game object around another
#2
Re: Rotating one game object around another
Posted 02 November 2012  05:54 AM
public abstract class MultiBarrelWeapon : BaseWeapon { protected Matrix rotationMatrix; protected List<Vector2> bulletFirePoints; public MultiBarrelWeapon(Player owner) : base(owner) { bulletFirePoints = new List<Vector2>(); } protected Vector2 GetTransformedPoint(int i) { rotationMatrix = Matrix.CreateRotationZ(owner.Rotation); return Vector2.Transform(bulletFirePoints[i], rotationMatrix); } protected virtual void AddFirePoint(Vector2 firePoint) { bulletFirePoints.Add(firePoint); } }
Think of each firePoint as an offset from the players world origin. When you create them base the offset on the player with rotation 0. When you want to fire a bullet do something like this :
for (int i = 0; i < bulletFirePoints.Count; i++) { Vector2 position = owner.Position + GetTransformedPoint(i);// Most Important line accuracyVariant.X = random.NextFloat(accuracy, accuracy); //Not important accuracyVariant.Y = random.NextFloat(accuracy, accuracy);//Not important Vector2 direction = Vector2.Add(owner.AimDirection, accuracyVariant); float angle = (float)Math.Atan2(direction.Y, direction.X); direction.Normalize(); BulletManager.ActivateProjectile(data, ref position, angle, ref direction); }
Rotating an object around the player also uses a Matrix and Offset vector. Here is how I handle it :
rotation += rotRate * (float)gameTime.ElapsedGameTime.TotalSeconds; rotationMatrix = Matrix.CreateRotationZ(rotation); Vector2 trans = Vector2.Transform(offset, rotationMatrix); Position = (playerPos + trans);
The first line of code is only if you want a constantly orbiting object. To change the direction of the orbit you can multiply rotRate by 1 or create a flag and do rotation = rotRate * (float)gameTime.ElapsedGameTime.TotalSeconds.
For bullet arc, I really dont know any nifty math tricks for that, but I imagine you could find a direction vector (the direction you want the bullet to arc towards) based on where the bullet is firing from and alter the direction of the bullet alittle (opposite of the arc direction) and then when it is fired do something like this :
bulletPosition += (arcSpeed * arcDirection) * (float)gameTime.ElapsedGameTime.TotalSeconds;
... Kinda like a fake gravity effect.
Hope this helps you out!
#3
Re: Rotating one game object around another
Posted 05 November 2012  07:45 PM
I absolutely do appreciate your help on this one, but as I said in my post, I was hoping for an explanation of how to do things the hard way. As masochistic as that might sound, I figure rotation matrices aren't necessarily something I can rely upon in other programming languages, should the need arise to familiarize myself with them in future. I'm also a bit of an amateur games historian, and I'd love to try and understand how this was done in ye olden days.
#4
Re: Rotating one game object around another
Posted 08 November 2012  12:38 PM
You use several phrases that you do not define and are unique to your game. We don't know what you mean by "ring of bullets". I could guess, but be very wrong in guessing.
Pictures always are helpful.
Also, using matrices isn't the "easy" way vs the "hard" way. It's a way. Matrices aren't some magic shortcut, they're mathematical constructs that you can use to perform math with. Math to get the results you want.
What, do you want to do the math a matrix does with out using a matrix? Why don't you do the math of a number with out using numbers and sticking to just set theory. We don't do that... it's... a waste of time.
Is it that you don't understand what stuff a matrix represents and does? Well then you should go and study up on your math.
Study:
Trig
Geometry
Algebra (matrices)
Transform Matrices (the specific type of matrix we used in XNA)
It's hard for us to show you the "masocistic" and "hard" way if you don't know these things. I'd just sit here giving you a math lesson instead of helping you accomplish your job... which as far as I can tell LiberLogic did a fair enough job on doing with what data you gave him. You just don't get the math... not their fault.
Lastly, refer to my signature... math is important, especially if you want to make games.
This post has been edited by lordofduct: 08 November 2012  12:39 PM
#5
Re: Rotating one game object around another
Posted 09 November 2012  01:23 PM
x = X * (float)Math.Cos(TurnAngle)  Y * (float)Math.Sin(TurnAngle);
y = X * (float)Math.Sin(TurnAngle) + Y * (float)Math.Cos(TurnAngle);
Those formulas are the rotation formula. Understanding that is the key to understanding mathematical rotations. My advice: don't do it.
I would advise just learning to use these formulas rather than trying to understand them. Notice it takes two formulas to rotate a two dimensional point, one for x and another for y. Capital X and Y are the starting position and lowercase x and y are the result. Turn angle is the angle you want to rotate through in radians rather than in degrees. You could use degrees as long as you convert to radians before feeding it to the formulas. I think the (float) conversion was just because I wanted the end result in float rather than in double.
I discuss rotations a bit on my website at: http://xna3d101.co...s/Matrices.html
These are how the rotation matrices do their magic. The rotation matrices use the formulas above to do a 2D rotation in 3 dimensions (one for X, one for Y, and one for Z). You need to know a little matrix algebra before you can understand how to feed this formula into a matrix, but XNA does all that for you.
In 2D, I'm not sure I would even bother with the matrices (I use them for pretty much everything in 3D). The rotation formulas are pretty straight forward in their use. I notice that XNA doesn't really support 2D matrices (3x3, rather than 4x4). That makes me think they didn't intend for them to be used in 2D, although there's not really any reason not to use the Zrotation matrix for your 2D rotations.
It's important to understand that the formulas only rotate a point around the origin. In order to rotate around a different point, you have to translate the origin to that point. In other words, you move the point you want to rotate around to the origin and then move it back again when you're done. This is done between draw frames in a game and the user never even realizes that it's actually being mathematically moved to the origin and back.
So, to rotate a bunch of points around their center: move all the points the distance from the object's center to the origin. Then apply the formula on all points. Then move all points back by the same direction (but opposite) and distance you moved them before.
To make an object orbit another object: move the orbiting object the direction and distance from the orbited object's center to the origin. Then apply the rotation formulas like above. Then move the orbiting object back in the same direction (but opposite) and same distance.
That's how you rotate and orbit in 2D. In 3D, you have to do the exact same thing with the exact same formula. However, the formula is 2D. So to make it work in 3D, you have to apply it seperately along the X, Y, and Z planes.
Now I advised that you don't try and figure out the formula. I say that, because that's exactly what I did; I got the formula and it drove me crazy because I couldn't figure out why it worked even though I could see that it obviously worked.
The reason you don't want to try and understand it is that the rotation formula is going to be near the back of a college trig book. In order to understand it, you will pretty much need to know everything on the pages of that college trig book that come before that formula. In other words, you are ready to understand this formula when you are qualified to teach (or at least tutor) a college trig class.
Because of my personality, I had to actually figure it out. And I did. It took about a solid week of pouring through my college trig book and trying to remember it all. But after about a week, I had written a proof that explained how/why the rotation formula works. It was two pages of math and geometric equations to explain/prove it. So, I would not recomend that journey for anyone unless they can't take no for an answer on this and are willing to put the time in to master trig.
This post has been edited by BBeck: 09 November 2012  01:39 PM
#6
Re: Rotating one game object around another
Posted 09 November 2012  01:31 PM
In other words, I would have the bullet travel exactly as if the ship had of been not moving at all but was facing the direction that it was facing at the instant the bullet was fired. And the bullet would move in a straight line from that point as the ship continued to rotate to a new facing. That would make them appear to fire in a spiral pattern.
In physics, you learn that pretty much everything travels in a perfectly straight line unless something specifically causes it to deviate from its path. For example, the second you release a rock from a sling it will immediately proceed along a perfectly straight line from the point and direction of release. That's a tangent along the circle of rotation at the point of release.
So, there's never a slight curve on the projectile unless something is acting on it such as gravity pulling it downward. But then gravity is a specific force that pulls with a specific strength over time. Wind is another force that might cause it's path to curve, but that's again is a specific force in a specific direction. This is where learning vector math pays in dividends.
This post has been edited by BBeck: 09 November 2012  01:45 PM
#7
Re: Rotating one game object around another
Posted 10 November 2012  07:35 AM
And  as I suspect, anyway  it's application in bullet patterns (skip to 9:03):
I'm trying to build a comprehensive (well...sort of comprehensive?) bullet class, and so I'm curious as to how something like Danmakufu defines the curve value for a standard bullet. I sort of have the same bullet pattern figured out (minus the curve), so it would seem to me that curve, in this context, is rotating around the center point of the bullet pattern. Here's my code thus far (which I actually understand, believe it or not):
public void ringShot(GameTime gameTime) { int spread = 360; int degrees = 18; for (int i = 0; i < spread; i += degrees) { Vector2 direction = new Vector2((float)Math.Cos(MathHelper.ToRadians(i)), (float)Math.Sin(MathHelper.ToRadians(i))); createBullet(direction); } }
I convert degree to radians in the "for" loop. The method creates bullets every 18 degrees, according to an external timer. The end result is very similar to what you see in the video, only the bullets don't curve, of course.
It's important to me to understand how to rotate one object around another because I believe it will have more than one application in my game. As I said, I find some of the terminology you're using confusing: is the "point" the object I want to orbit (by the way, is orbit distinct from rotate, in this context? Is that generally accepted to be the case?), and is the "origin" the vector that the object orbits around?
Sorry for being so dense. I've said it before, but I'm still quite new to programming.
#8
Re: Rotating one game object around another
Posted 10 November 2012  08:39 AM
See transformations (position, rotation, scale) can be described as matrices.
Simple matrix multiplication can allow you to append matrices 1 after another.
If you pretend matrix A is the child of matrix B, where B is relative to A (like this red dot is to the black cube), and you append A to B, you'll get BOTH transformations combined.
You can then represent B relative to A at all times... ignoring the position and rotation of A. And when you go and render, you just multiply the matrices together to get the end result.
#9
Re: Rotating one game object around another
Posted 12 November 2012  08:01 AM
The origin is the point x,y=0,0. I believe it's the upper left corner of the screen in 2D graphics. I haven't done a lot of 2D in XNA, mostly just 3D. But I believe X goes from zero to positive screen width horizontally and Y goes from zero to positive screen height from top to bottom.
In 3D, no particular point on the screen is 0,0 (the origin). In 3D you still have an origin (0,0,0), but the "camera" can move anywhere, which makes the origin appear to move around.
Likewise, you could do some math to move the origin to the center of the screen, but start out by assuming that the screen is the area on an X,Y (Cartesian) graph. So, then there's and offscreen area above what's visible on the screen where Y is negative but X is still positive. And there's an area off the screen to the left where X is negative but Y is still positive. And then diagonal up and to the left is an area where both X and Y are negative.
So, the point where both x and y are zero is the upper left corner of the screen and that point is known as the "origin". It's a term from math class.
So to rotate the black box in your example: Well, this could be done in a couple of different ways. But I'm going to assume that this is done with sprites. So you probably have a black box sprite that has a vector that keeps track of it's position and another vector that keeps track of it's orientation.
This is real confusing because the position isn't really a vector at all, and the direction, or orientation, is a unit vector, which is a special type of vector. Think of the sprite's orientation vector as an arrow. It's tail is at 0,0 and it's head is a point that's exactly one unit away. That's kind of confusing because "units", in this case, are probably typically pixels. Don't let that throw you. Just realize this means that you can have decimal values in between pixels. In fact, 45 degrees would not reach all the way over to the next diagonal pixel. It would fall short. That's fine. With this orientation vector, all it represents is a direction and nothing more. Whatever direction that arrow points in will be the direction the sprite points in.
Now I assume that the sprite is drawn so that it's "point" is in the center of the sprite. And I assume that when the direction/orientation vector points towards the top of the screen the sprite is in it's "normal" orientation without any rotation.
To rotate it, (assuming it's attached to that direction/orientation vector) you have to rotate it's direction/orientation vector.
In this case, it's pretty easy. Vectors always have their tails at 0,0 (remember to think of a vector as an arrow where the position stored int he vector is the position of the vector's arrow head and 0,0 is always it's tail).
Just apply the formulas that I gave you for x and y to the head of the vector. That will rotate it and the sprite, being attached to it, will rotate with it. If for some reason, you think the math causes the vector's length to change from a length of one, you can "normalize" it to reset it's length to one.
I think that should get your black box sprite. You just have one vector for it's position and another for it's orientation. The arrow head of the orientation vector is "orbiting" the position of the sprite.
Now the red box is basically the same, but it's not built into XNA like the sprite rotation vector is.
So, we need another Vector2 for the position of the red box. In this case, it's always in orbit around the black box. It's distance is some radius (distance between a circle's center and it's edge).
Again, all vectors have their tail at 0,0 and their head at some point. Their arrow points from 0,0 to that point. But in the case of the red box, you want to think of that vector's tail as being at the center of the black box. And you want to think of the head as being the arrow head that points to the center of the red box. So, it's an "offset" from the black box to the red box.
The arrow points from the center of the black box to the center of the red box. The length of the vector's arrow is the distance between the center of the black box and the center of the red box. The direction of the vector is the direction from the center of the black box to the center of the red box.
To get the position of the red box, you add the position vector of the black box to our new position vector of the red box. That way, the position of the red box is always related to the position of the black box by the red box's vector.
To rotate (or orbit) the red box, you simply have to rotate it's vector.
Now this gets into vector math, but don't let that scare you. For most of this you can get away with changing the x and y values in the vectors. A better way would to be to actually handle these as vectors and add vectors together rather than adding their x and y values together. I think here in a minute you will have little choice in treating the vectors as vectors.
In order to rotate(orbit) the red box around the black box, just rotate it's vector (the offset from black box to red box). You can do that with the formulas I gave you by handling the x and y values inside the vector yourself.
Now in the example, the radius between the black box and the red box changes. This is where you really should probably handle this vector as if it's a vector.
I have a pretty thourough discussion on Vectors on my website.
You could do some semicomplex trigonometry and calculate new values for x and y based on their angle. But vector math makes this ridiculously simple.
In order to change the length of a vector (again, remember to think of vectors as arrows) you multiply the vector times a number. So, if a vector has a length of 3.5, you multiply it by the number 2 in order to set it's length to 7. Notice that this requires you to take into account what the original length is in order to know how to get it to a length of seven. If you just want to set it to a length of 7, you don't care what the original length was, it just makes the problem more complicated.
So, to make the math easy you normalize the vector. When you normalize a vector it sets it's length to one. One times any number is that number. So, if the vector is normalized, all you have to do is multiply it by 7 to set it's length to 7. You can set any normalized vector equal to whatever length you want it to be by multiplying that number by the normalized vector.
Now, I "think" you normalize the vector and multiply both the x and y by the number to do "vector" multiplication on it. But a better way would be to multiply the (normalized) vector object times the number. XNA will let you multiply the vector object by a number. It will also let you add to vector objects together without messing with their x and y values. That's the "prefered" way to do it.
You have to keep normalizing this vector over and over again, so I would suggest keeping the vector between the black box and the red box normalized all the time. What I would do is have the vector from the black box to the red box stay a normalized vector (length of one) at all times. Then it's just a vector that points in the direction of the red box without storing it's actual position. Then I would have an "offset" value that is the distance from the black box to the red box stored as a seperate number. This would allow me to easily and constantly change the distance.
Let's see if I can write some code:
Whoops. Just noticed that the Draw method handles the orientation as an angle in radians. I belive that you can convert the direction vector to an angle with the formula
DirectionAngle = atan(y,x)
But I'll just not use a vector for that to keep it simple.
//There's a button you can click in the upper right hand of the code box that makes this 1000 times easier to read. Vector2 BlackBoxPostion; float BlackBoxOrientation; Vector2 DirectionToRedBox; float DistanceOfRedBox; BlackBoxPostion = new Vector2(100,120); //BlackBoxOrientation = new Vector2(0,1); //Should be an arrow pointing straight up off the screen. BlackBoxOrientation = 0.0f; //Guess we'll go with an angle rather than a vector for this. DirectionToRedBox = new Vector(1,0); //Notice it's length is one, which makes it a "normalized" vector. DistanceOfRedBox = 10.5; //I threw in the .5 just to show that it can be done. DirectionToRedBox = RotateVector(DirectionToRedBox, MathHelper.ToRadians(20f)); //Rotate the red box 20 degrees around the black box. DirectionToRedBox.Normalize(); //Not necessary unless the math unnormalizes it. Which it shouldn't except through rounding errors. Included to emphasize that this is normalized. //I think you have to subtract half of the sprite width from the sprite's x and (height from) y to get the sprite centered on the position. But I'll leave that to you. BlackBox.Draw ( Texture2D texture, Rectangle destinationRectangle, Nullable<Rectangle> sourceRectangle, Color color, float BlackBoxOrientation, Vector2 BlackBoxPosition, SpriteEffects effects, float layerDepth ) //Likewise the position below is the center position and you'll have to offset the drawing position by half of the width and height of the sprite to get it to center on the position. RedBox.Draw ( Texture2D texture, Rectangle destinationRectangle, Nullable<Rectangle> sourceRectangle, Color color, float RedBoxOrientation, Vector2 BlackBoxPosition + (DirectionToRedBox*DistanceToRedBox), SpriteEffects effects, float layerDepth ) Vector2 RotateVector(Vector2 RotatingVector, float AngleOfRotationInRadians) { Vector2 NewRotationVector; NewRotationVector.x = RotatingVector.x * (float)Math.Cos(AngleOfRotationInRadians)  Y * (float)Math.Sin(AngleOfRotationInRadians); NewRotationVector.y = RotatingVector.y * (float)Math.Sin(AngleOfRotationInRadians) + Y * (float)Math.Cos(AngleOfRotationInRadians); return NewRotationVector; }
This post has been edited by BBeck: 12 November 2012  08:30 AM
#10
Re: Rotating one game object around another
Posted 17 November 2012  08:26 AM
HTH.
#11
Re: Rotating one game object around another
Posted 29 November 2012  07:04 PM
I used a transform matrix in the end. I vaguely understand the math, and that will have to do for the time being. I moved on to adapting the functionality to my own specification, and once again I've run into trouble based on my lack of knowledge. I've made a diagram:
diagram here
in fig A, the player is pressing right on the dpad, which means the player's velocity is (1,0). In this case, the sphere orbits in either a clockwise or counterclockwise direction, depending on whether it's above or below the player. Once it's at the rear of the ship, it locks into position until the player stops pressing right on the dpad. This is common functionality in shmups.
I've (somehow) managed to make this work (sort of) for the cardinal directions. Here's the code:
////////////POD/SPHERE CLASS////////////////////////// public float distance = 40f; //from player public float podOrbit = MathHelper.ToRadians(0); public float orbitSpeed = 360f; //////////////UPDATE METHOD/////////////// Matrix podMatrix = Matrix.CreateRotationZ(podOrbit) * player.GetPlayerWorldMatrix(); worldLocation = Vector2.Transform(offset, podMatrix); //up (clockwise) if (keyState.IsKeyDown(Keys.Up) && worldLocation.X > player.worldLocation.X) { podOrbit += MathHelper.ToRadians(orbitSpeed) * elapsed; } //up (counterclockwise) if (keyState.IsKeyDown(Keys.Up) && worldLocation.X <= player.worldLocation.X) { podOrbit += MathHelper.ToRadians(orbitSpeed) * elapsed; } //zero if (keyState.IsKeyDown(Keys.Up) && worldLocation.X == player.worldLocation.X) { //this part is giving me most trouble  can't figure out how to stop orbit podOrbit = 0; //this doesn't work either: //podOrbit += MathHelper.ToRadians(0) * elapsed; } //down (clockwise) if (keyState.IsKeyDown(Keys.Down) && worldLocation.X <= player.worldLocation.X) { podOrbit += MathHelper.ToRadians(orbitSpeed) * elapsed; } //down (counterclockwise) if (keyState.IsKeyDown(Keys.Down) && worldLocation.X > player.worldLocation.X) { podOrbit += MathHelper.ToRadians(orbitSpeed) * elapsed; } //downzero if (keyState.IsKeyDown(Keys.Down) && worldLocation.X == player.worldLocation.X) { worldLocation = upPoint; } //left (clock) if (keyState.IsKeyDown(Keys.Left) && worldLocation.Y <= player.worldLocation.Y) { podOrbit += MathHelper.ToRadians(orbitSpeed) * elapsed; } //left counterclock if (keyState.IsKeyDown(Keys.Left) && worldLocation.Y > player.worldLocation.Y) { podOrbit += MathHelper.ToRadians(orbitSpeed) * elapsed; } //leftzero if (keyState.IsKeyDown(Keys.Left) && worldLocation.Y == 0) { podOrbit = 0; } //right (clock) if (keyState.IsKeyDown(Keys.Right) && worldLocation.Y <= player.worldLocation.Y) { podOrbit += MathHelper.ToRadians(orbitSpeed) * elapsed; } //right counterclock if (keyState.IsKeyDown(Keys.Right) && worldLocation.Y >= player.worldLocation.Y) { podOrbit += MathHelper.ToRadians(orbitSpeed) * elapsed; } //rightzero if (keyState.IsKeyDown(Keys.Right) && worldLocation.Y == player.worldLocation.Y) { podOrbit = 0; }
It works, but only in a limited capacity: while the sphere/pod locks in place as expected for the 4 cardinal directions, it jitters, and I'm not sure how to stabilize it. I'm also having trouble getting the diagonal functionality (as seen in fig E  H) working. I've tried a number of solutions, none of which seem to work.
I realize at this point my lack of trig knowledge isn't helping, but I'm learning as I go. I'm just better at understanding stuff like trig in the context of something familiar, like video games, and I struggle with how to implement it in C#.
As usual, any thoughts would be very welcome.
This post has been edited by Geogaddy: 29 November 2012  07:05 PM
#12
Re: Rotating one game object around another
Posted 01 December 2012  07:39 AM
Geogaddy, on 10 November 2012  07:35 AM, said:
public void ringShot(GameTime gameTime) { int spread = 360; int degrees = 18; for (int i = 0; i < spread; i += degrees) { Vector2 direction = new Vector2((float)Math.Cos(MathHelper.ToRadians(i)), (float)Math.Sin(MathHelper.ToRadians(i))); createBullet(direction); } }
I convert degree to radians in the "for" loop. The method creates bullets every 18 degrees, according to an external timer. The end result is very similar to what you see in the video, only the bullets don't curve, of course.
I didn't read the entire posts after this, so the question may have been answered already.
...But to make the bullets curve slightly, just add a small angle to your loop, ie:
for (int i = curve_angle; i < spread + curve_angle; i += degrees)
The curveangle can be made dependant of time ofc, for instance if you want the bullets to rotate 1 degree for each x miliseconds, you could use something like:
(long)(gameTime.TotalGameTime.TotalMilliseconds / x) % degrees
(where degrees is the variable you already use a the angledelta).
Tom
