It's out.
Throw a wrench, change the galaxy.
Mostly nebulae.
Windows, dunno about others
John Winder
Chris Harvey
Tom Francis
John Roberts
Get on our mailing list so we can tell you when we release a new game.
We're @HeatSig on there.
Update: This was originally a post to ask for help, but now that we’ve solved the problem I’m posting the solution for anyone who needs it, and changing the title to make it more searchable. It’s a function that lets you find where an object appears on-screen, so that you can use the DrawGUI event to draw interface elements over it or annotate it, useful for tutorials. Original post follows, updates and working script at the end!
I have a maths problem in Heat Signature that I can’t quite get my head around, so if you’re into trigonometry, read on and see if you can help me! I think I know all the rules I need to solve this, but I can never quite reframe the problem into something I recognise.
In Game Maker, everything the player can see on the screen at one time is called ‘the view’. The whole game world is called ‘the room’. In my game, the view moves around the room and also rotates, to follow the action. In the diagram, the whole image is the room, and the rotated black rectangle is the view: what the player sees.
What I’m trying to do is find rx and ry: an object’s x and y co-ordinates relative to the screen, at its current position and rotation. I have every other piece of information:
So, how do I combine those things to find rx and ry?
My usual method is to bash my head against these problems for a day or two, drawing endless diagrams and running tests and simulations in code. But lately I’ve realised how quickly some problems can be solved by just checking in with someone who already knows or finds it easy, so I’m trying that! Any help much appreciated.
Update: Thanks for all the suggestions so far! It’s possible that my diagram implies some assumptions I didn’t intend. To be clear:
To illustrate the full shittiness of the problem, here’s another diagram this also has to solve:
Update 2: The plot thickens! The reason nothing has worked for me so far is that I have misunderstood how the view rotates. vx and vy do stay the same as it rotates, BUT the rectangle itself rotates around its center! So for most angles, vx,vy is well outside the screen area! Bizarre! We still need rx,ry relative to what the player will see as the top left of the screen.
Of your many clever and much appreciated suggestions, the two I’ve had most success understanding and implementing are, roughly:
The Andrew/Andrey ‘cos-sin’ version
xDifference = x – view_xview[0]
yDifference = y – view_yview[0]
Angle = view_angle[0] * -1
GUIx = (xDifference * dcos(Angle)) – (yDifference * dsin(Angle))
GUIy = (xDifference * dsin(Angle)) + (yDifference * dcos(Angle))
The Puzey/Varanas ‘arctan’ version
xDifference = x – view_xview[0]
yDifference = y – view_yview[0]
Distance = point_distance(0,0,xDifference,yDifference)
Angle = darctan(yDifference/xDifference) – view_angle[0]
GUIx = Distance * dcos(Angle)
GUIy = Distance * dsin(Angle)
These both work at view_angle[0] = 0, but drift in a circular way when the view is rotated, because it’s not rotating around the point I thought it was. The point it’s rotating around must be something like:
view_xview[0] + (view_wview[0]/2)
view_yview[0] + (view_hview[0]/2)
But we still need co-ord relative to ‘the top left of the screen’, which is something I no longer even have a variable name for.
Now that I know this I might have a way of figuring it out, will update if I do so.
Update 3: Got it! For anyone who needs it, here’s a rotation-proof function to find an object’s position in screen co-ordinates, for us in the Game Maker Studio’s DrawGUI function so you can draw HUD elements over it and annotate it and stuff. Now accounts for zoom too!
Thanks so much to everyone who helped!
var ViewCenterX = view_xview[0] + (view_wview[0]/2);
var ViewCenterY = view_yview[0] + (view_hview[0]/2);
var Zoom = view_wport[0] / view_wview[0];
var MyDistanceFromCenter = point_distance(ViewCenterX,ViewCenterY,x,y) * Zoom;
var MyDirectionFromCenter = point_direction(ViewCenterX,ViewCenterY,x,y) + view_angle[0];
GUIx = (view_wport[0]/2) + lengthdir_x(MyDistanceFromCenter,MyDirectionFromCenter)
GUIy = (view_hport[0]/2) + lengthdir_y(MyDistanceFromCenter,MyDirectionFromCenter)
Update 4: One year later, I needed to do the reverse – find world co-ordinates from GUI co-ords. So here’s that, slightly more verbose to explain what’s happening:
var ScreenViewCenterX = view_wport / 2;
var ScreenViewCenterY = view_hport / 2;
var Zoom = view_wport[0] / view_wview[0];
var MyDistanceFromScreenViewCenter = point_distance(ScreenViewCenterX,ScreenViewCenterY,GUIx,GUIy);
var MyDirectionFromScreenViewCenter = point_direction(ScreenViewCenterX,ScreenViewCenterY,GUIx,GUIy);
var MyDistanceFromWorldViewCenter = MyDistanceFromScreenViewCenter / Zoom;
var MyDirectionFromWorldViewCenter = MyDirectionFromScreenViewCenter – view_angle;
var WorldViewCenterX = view_xview + (view_wview / 2);
var WorldViewCenterY = view_yview + (view_hview / 2);
x = WorldViewCenterX + lengthdir_x(MyDistanceFromWorldViewCenter,MyDirectionFromWorldViewCenter)
y = WorldViewCenterY + lengthdir_y(MyDistanceFromWorldViewCenter,MyDirectionFromWorldViewCenter)
Hey Tom,
It’s quite simple.
All you need to do is to rotate both points (x,y) and (vx,vy) by a -theta so the box will be aligned with the world axes
I assume you the rotation goes around (x,y) point. That means that (x,y) will still be (x,y) point after reverse rotation.
So the only change will be with (vx,vy) point.
vx_new = vx * cos(-theta) – vy * sin(-theta);
vy_new = vx * sin(-theta) + vx * cos(-theta);
And, finally:
rx = abs(vx_new – x);
ry = abs(vy_new – y);
Andrew:
Let’s call the vector from object to the view corner ox & oy – this is calculated as ox = vx – x & oy = vy – y. Simply rotate this around the object by theta, so rx = ox * cos(theta) – oy * sin(theta) & ry = ox * sin(theta) + oy * cos(theta)
Oh my god, forget about that comment, it’s even easier!
1) We need to transform all our points (vx,vy) and (x,y) relative to (x,y) so we’ll have (vx – x,vy – y) and (0,0) points;
2) Do a reverse rotation to (vx – x, vy – y) point using formula I mentioned above.
vx_rotated = (vx – x) * cos(-theta) – (vy – y) * sin(-theta);
vy_rotated = (vx – x) * sin(-theta) + (vy – y) * cos(-theta);
3) Funny thing, but (vx_rotated, vy_rotated) is what we need as they’re distances from (x,y) point to rect bounds in local coordinates.
rx = vx_rotated;
ry = vy_rotated;
Tom Whitney:
I think another way is:
rx = sin(90-theta)*(x-vx)
ry = sin(theta)*(x-vx)
Just using the little right triangles made in your diagram there. But I have no idea if GML supports sines…
aiusepsi:
The best way to consider this is to use rotation matrices. http://en.wikipedia.org/wiki/Rotation_matrix
If you initially consider the view and room to be at the same origin and theta to be 0, the first step is to do the rotation.
(x) = ( cos(theta) sin(theta) ) (rx)
(y) = ( -sin(theta) cos(theta) ) (ry)
then you’re translating the whole thing by v..
(x) = ( cos(theta) sin(theta) ) (rx) + (vx)
(y) ( -sin(theta) cos(theta) ) (ry) + (vy)
Then you want to invert that.
So it’d be:
(x- vx) = ( cos(theta) sin(theta) ) (rx)
(y – vy) ( -sin(theta) cos(theta) ) (ry)
(rx) = ( cos(theta) -sin(theta) ) (x- vx)
(ry) ( sin(theta) cos(theta) ) (y – vy)
Then you can multiply that out and get your answer. The toolkit should have stuff already in it for doing matrix maths, so taking advantage of that would make your life easier.
aiusepsi:
Urgh, I forgot that my whitespace would get eaten so my formatting has gone totally awry, Apologies.
James:
Easiest way for me to think about it:
https://www.dropbox.com/s/o5i7x04a1vqus4n/IMG_20150127_110651.jpg?dl=0
This is a bit rough in that I’ve done it on my whiteboard and haven’t tested, but maybe it’ll give you a decent starter. Also, excuse the horrible typed-maths syntax…
Okay, so, assumptions first: this assumes the view is in the approximate rotation of your diagram. For each 90-degree quadrant of rotation of theta, the numbers will change signs and you’ll have to adjust the math to compensate (there’s probably a general form that a proper mathematician would come up with).
First, get rid of the offset: let’s say dx = x – vx and dy = vy – y (NOTE that this will produce negative numbers for some rotations; you may
Call the diagonal from the view’s origin to the target point D. Length of D = sqrt(dx * dx + dy * dy)
We have an angle theta between a horizontal line, and the top of the screen. We need the angle between D and the top of the screen.
Call the angle between a horizontal line and D “S”; S = inverse-tan dy/dx
Call the angle between D and the top of the screen “T”. T = theta – S
We now have the angle T, and the length of hypotenuse D, which means you can do:
rx = D * cos T
ry = D * sin T
Hope that gets you in the right direction…!
@James: you’ve done the same as I have, but you’ve assumed that the target point is in a straight horizontal line from the corner of the view (we can blame Tom’s diagram for making that look confusing ;-)).
For the general case, that won’t be true, so you have to work out the angle/length of that diagonal in full to work the general case.
nesis:
I’m typing this on a phone, so hopefully I’m not slow to the punch!
p = vector (x,y)
v = vector (vx,vy)
offsetVector = (p-v), a vector from v to p
Convert theta to a vector pointing in its direction
localXAxis = vectorFromAngle(theta)
Project offsetVector onto localXAxis:
rx = project(offsetVector, localXAxis)
ry = offsetVector – rx
Pseudocode functions if GameMaker has no suitable ones:
vector project (vector a, vector normal)
{
return normal * ( dot(a, normal) )/(dot (normal, normal) )
}
float dot(vector a, vector b)
{
return a.x*b.x + a.y*b.y
}
vector vectorFromAngle(float thetaAsRadians)
{
return vector(Cos(thetaAsRadians), Sin(thetaAsRadians))
}
James:
@Dan Puzey
Yup, you’re totally right.
Please disregard my earlier post.
I’ll blame it on just having woken up rather than Tom’s diagram.
Mark:
//The View’s position vector
v = (vx, vy)
//The absolute position you’re trying to convert
a = (x, y)
//The vector pointing from the top-left of the View to the point
r0 = a – v
//Rotate the above vector by -theta to get its position within the View’s coordinate system
r = r0 * rotationMatrix(-theta)
Reference: http://en.wikipedia.org/wiki/Rotation_matrix
Mark:
When I say “top-left of the View,” what I really mean is “the View’s origin.” The subtraction moves the point (x,y) so it’s relative relative to the View’s position (vx,vy), and the rotation by -theta makes it relative to the View’s orientation.
I also had my vector math backwards. Should be:
r = rotationMatrix(-theta) * r0
I don’t know if GameMaker has vector/matrix types with overloaded operators or what, but the actual calculation is relatively straightforward:
r0x = (x – vx);
r0y = (y – vy);
rx = r0x * cos(-theta) – r0y * sin(-theta);
ry = r0x * sin(-theta) + r0y + cos(-theta);
Surein:
Hi, I think most of the theory here is right but there might be a few sign errors here and there.
What I came up with is:
rx = (x – vx)*cos(theta) – (y – vy)*sin(theta)
ry = (x – vx)*sin(theta) + (y – vy)*cos(theta)
You should maybe put this up on the GameMaker Marketplace as a free asset for people to use. That way it will be held centrally with other resources and more likely to be found.
Miguel:
I would recommend you to go in the direction Mark points to, using transformation matrices. Even better than using a separate translation and rotation matrix separately, try to learn how to use homogeneous coordinates and affine transforms. It will pay in the long run, believe me.
http://en.wikipedia.org/wiki/Transformation_matrix#Affine_transformations
isaac:
I noticed that your ship/character always stays centered in the view, despite the fact that you’re zooming in and out and using a fixed room size.
Is there a tutorial you could recommend that would show me how to keep my room size fixed, but have the view zoom out without shifting the centered object into a corner of the view?
Thanks for the code! I felt like trying it out, so I made a small demo where I applied HUD elements to some enemies. I’ve got a description and a dropbox link to the zipped source code here (https://dcgamesmith.wordpress.com/2015/02/12/hud-example/) if anyone wants to use it.
[…] The tricky part is accounting for view movement and rotation. He posted code you can use in a blog entry, but I thought it might be a good exercise to try it out in a little example. In case anyone would […]
Spot The Bug - a post on the Heat Signature site:
[…] in case you think I’m Tom Sawyering you into solving it for me, I have no compunction about just asking for help when I really am stuck. This one was kind of fun to figure out, for a certain definition of […]
I just wanted to thank you for this blog post! It saved my butt and I ended up with a really cool game effect! If you have some spare time check it out at RocketBoy.space Thanks!