Rotating an object around another moving object
Monkey Forums/Monkey Programming/Rotating an object around another moving object
| ||
Hi guys, Since I'm (still) crap at maths, I was hoping someone can help kick me in the shin for my stupidity and still help me with my wee little problem. As the title suggests, I'm trying to rotate an object around another object. Only it is a moving object (basically imagine a ball rotating around a moving player image) I found this link quite useful for the math that works out the calculations, but for some reason, in my implementation, the "ball" moves further and further away the more you move the player ship. Here's a rough snippet of my code: This is the code that updates the ball's new X and Y position: Local rotatedBy:Float = 90 * dt.delta Local rotatedPosition:Float[] = rotatePoint(ballX, ballY, shipX, shipY, rotatedBy) ballX = rotatedPosition[0] ballY = rotatedPosition[1] And this is the code that is my converted method for the formula used in the link above: Method rotatePoint:Float[](pointX:Float, pointY:Float, centerX:Float, centerY:Float, angle:Float) Local result:Float[] Local radians:Float = angle * PI / 180 Local rotatedX:Float = Cos(radians) * (pointX - centerX) - Sin(radians) * (pointY - centerY) + centerX Local rotatedY:Float = Sin(radians) * (pointX - centerX) + Cos(radians) * (pointY - centerY) + centerY result = [rotatedX, rotatedY] Return result End So, if I don't move, it works perfectly, but as soon as I do move, the more I move, the further the ball moves away, ie the distance between the player ship and ball. I'm so close I can taste victory! I just need a little hint... :) |
| ||
You need to attach the ball to the player. Some frameworks refer to this as parenting. In your situation I'd do this by simply storing the ball offset from the player, rotating that and then using playerPosition + rotatedOffset as the final ball position. |
| ||
The drift you're experiencing is probably due to floating point precision with your frame interpolation. My suggestion would be to manually set the position based on its parent rather than trying to calculate from its previous position. Store the expected radius of the orbit and just calculate it from there.Local radius:Float = 100 Local rotatedBy:Float = 90 * dt.delta ballX = shipX + Cos(rotatedBy) * radius ballY = shipY + Sin(rotatedBy) * radius Edit: Updated for delta timing. Edit 2: Delta timing removed. |
| ||
Thanks for the update guys. Samah, that doesn't work. I keep getting jitters of the ball and it doesn't move/rotate. Here's an example of the ballX values spat out over say a few seconds: 55.939203232516554 59.03883077220927 61.80836696244423 59.03883077220927 61.80836696244423 61.80836696244423 55.939203232516554 64.22373055608624 59.03883077220927 55.939203232516554 59.03883077220927 64.22373055608624 55.939203232516554 61.80836696244423 61.80836696244423 55.939203232516554 61.80836696244423 55.939203232516554 61.80836696244423 61.80836696244423 61.80836696244423 59.03883077220927 55.939203232516554 61.80836696244423 61.80836696244423 61.80836696244423 55.939203232516554 I didn't move the ship, so its X position remained the same. Here's a simple quicky I made (erm).. Strict Import mojo Class Test Extends App Field shipX:Float = 30, shipY:Float = 100 Field ballX:Float = 30, ballY:Float = 50, ballRadius:Float = 50 Method OnCreate:Int() SetUpdateRate 60 Return(0) End Method OnUpdate:Int() Local rotatedBy:Float = 90 #Rem - Method 1 ballX = shipX + Cos(rotatedBy) * ballRadius ballY = shipY + Sin(rotatedBy) * ballRadius #End ' Method 2 Local rotatedPosition:Float[] = rotatePoint(ballX, ballY, shipX, shipY, rotatedBy) ballX = rotatedPosition[0] ballY = rotatedPosition[1] If shipX < 600 shipX += 1 Else shipX = 0 End Return(0) End Method OnRender:Int() Cls DrawRect(shipX, shipY, 20, 20) DrawCircle(ballX, ballY, 10) Return(0) End ' For method 2 Method rotatePoint:Float[](pointX:Float, pointY:Float, centerX:Float, centerY:Float, angle:Float) Local result:Float[] Local radians:Float = angle * PI / 180 Local rotatedX:Float = Cos(radians) * (pointX - centerX) - Sin(radians) * (pointY - centerY) + centerX Local rotatedY:Float = Sin(radians) * (pointX - centerX) + Cos(radians) * (pointY - centerY) + centerY result = [rotatedX, rotatedY] Return result End End Function Main:Int() New Test Return(0) End Note method 1 is your method and method 2 is the way I was doing it before. Weird things are happening :/ |
| ||
I think what you have is an unintended feedback loop. ballX/Y are fed into rotatePoint, but then are modified to the same. So you'll get all kinds of weird orbit behaviour as the ship moves. What I'd try... localPointX/Y is relative to the center/shipX/Y. So the ball should maintain a steady/consistent orbit around the ship regardless of it's movement. Also, Sin/Cos are degree based in Monkey, no need converting to radians. |
| ||
@Sensei: Samah, that doesn't work. Sorry, remove the delta timing from the last two lines and it should work. |
| ||
Nullterm, I think you've solved it, thanks! I'll adapt my stufff accordingly.. Really appreciate all your help guys! Hope someone else can make use of this too :) |
| ||
Just a couple of things: 1. You're creating a new array object every time you call rotatePoint. If this is in a loop, it's very bad. I'd suggest passing in a local array and reusing it on every call. 2. I don't see the need for a rotatePoint call anyway when you can just do it in two lines... :/ |
| ||
1. yeah, if you can get away from that, the garbage collector would be much happier. Ideally, ballPos is new'd only once when the object is created or at app start, so you re-use the same ballPos memory. 2. Makes life easier in the long run to keep rotatePoint a function/method. You can re-use the function elsewhere for other objects. And keep the calling function smaller/readable. |
| ||
Thanks you guys. I wasn't quite sure how to go about it until Nullterm's example above. I'll give it a try now! |
| ||
Ok I got it working great: The rotation parameters are actually part of the ship and so is the same as Nullterm's Class Point(): ship.x, ship.y, ship.rotateTime, ship.rotatorX, etc. In the looping code, all I do now is this: ship.rotateTime += 8 * dt.delta ' The rotation speed rotatePoint(ship) Then in the function: Function rotatePoint:Void(p:Ship) p.rotatorX = Cos(p.rotateTime) * (p.radius) - Sin(p.rotateTime) + p.x 'Removed the * (localBallY) as it wasn't being used at all p.rotatorY = Sin(p.rotateTime) * (p.radius) + Cos(p.rotateTime) + p.y End This is great as I've now learned how to improve code efficiency, thanks! @Samah: I tried your code as in the test.monkey I pasted earlier in this thread and it didn't move in there either. Grab that code and run it yourself and you will see. Thank you for your kind help though! Even though I've been using Monkey-X for the past 1.5 years, I'm still quite new to OO style coding, so my learning skills are slow.. |