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:
- x and y, the co-ordinates of the object, relative to the upper left corner of the room.
- vx and vy, the co-ordinates of the top left corner of the view
- theta, the rotation of the view, measured in degrees from horizontal, going anti-clockwise.
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:
- y and vy are not the same, it’s just coincidence that they’re close here. No values mentioned are the same, related, or constrained by each other.
- theta can range from 0 to 360, meaning the whole view could be upside down relative to the room.
- vx and vy are the ‘top left’ of the view when it’s at theta 0.
It rotates around that point as theta increases, meaning it could end up not being the top left. I am mistaken about this! See below.
- The object can be completely outside the view, we still want to know rx and ry even if they’re negative or larger than the view size.
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)