Monday, July 15, 2019

rotation - Project rotated matrix to isometric coordinate


I have a group of soldiers, and each has a vector describing their position in the squad. When the group rotates, I do this math to calculate the desired position of each soldier.


var unitPositions : Array = []

let cosRadian = cos(rotation)
let sinRadian = sin(rotation)

for var index = 0; index < self.positions.count; ++index {


// Find the vector for this unit, translate it, then store it
let targetVector = self.getVectorAtPosition(index) - translate
let newX = targetVector.dx * cosRadian - targetVector.dy * sinRadian
let newY = targetVector.dx * sinRadian + targetVector.dy * cosRadian

unitPositions.append(CGPoint(x:newX, y:newY))
}

This produces the following result:


Units after normal matrix rotation



The problem with this is that units in an isometric world facing south east should match up with the reference grid. So the next step was to add a multiplier. In the loop above, I changed it to:


    let isoX = newX * 1.414213562373095;
let isoY = newY

unitPositions.append(CGPoint(x:isoX, y:isoY))

This produced closer results:


Positions after multiplier on X


But, it's still not 100% accurate. So, right now I'm going through and trying to get my fundamentals of math, matrices, vectors, etc all figured out in hopes of producing this desired result:


desired unit positions



I would love any pointers on what I should be searching for, as right now it is all a bit overwhelming. Thanks much!


UPDATE


Due to the awesome comment by @DMGregory, here are my findings:


If I want to "hack" this and continue to use screen coordinates, I can do so using 1.414213 (the magic number found here) combined with your true isometric projection.


let isoX = newX * 1.414213562373095
let isoY = newY * (1.414213562373095 * 1/sqrt(3))

That will produce:


enter image description here


That approach will work, but won’t solve everything else you mentioned (i.e: units will move faster when going certain directions). To handle that, creating the original formation with isometric vectors, and then just rotating that matrix will produce the same results. So, it looks like I have a lot of work converting my game calculations and coords to default as isometric coordinates. Thanks again!




Answer



The trick here is conversion between world space and screen space.


World space is the coordinate system you use for your game logic - calculations of pathing, movement, formations, etc. Your original formation code is correct for world space.


Screen space is the coordinate system in which items are displayed. Because you're using an axonometric projection here, your vertical/depth axis is foreshortened. So a distance of "one meter" forward spans fewer pixels on screen than "one meter" to the right. This also means that right angles in world space can become acute or obtuse on screen - which is why your right-angled unit formation (which is correct for world space) doesn't match your isometric grid.


You generally won't want to do game calculations in screen space - otherwise units moving "North" will have a higher effective speed relative to your isometric grid than units moving "East" (and units will be able to shoot further if they attack from North/South etc.)


3D games typically store only the world coordinates of each entity. They generate a "View-Projection" matrix, based on the position/orientation/FoV of a virtual camera, which converts world coordinates into screen coordinates on the GPU as the content is being rendered. A lot of "2D" games do this too, just with an orthographic matrix or constrained camera angle.


Judging by your example though, it looks like you're manually positioning your units with a formula, so I'll demonstrate that style.


The details will vary based on your coordinate setup, so I'll make some assumptions you might need to modify.


Let's say your world axes run like this, to align nicely with your isometric grid: coordinates


Here's some pseudocode (uv is just a temporary label for notational convenience)



// First, we recenter our worldspace position
// By changing center.xy, you can pan the "camera" around the map
uv.xy = worldPosition.xy - center.xy

// Then we convert to pixel offsets from the center of the screen
screenPosition.x = (uv.x - uv.y) * TileWidth * 0.5
screenPosition.y = (uv.x + uv.y) * TileHeight * 0.5

// Depending on your drawing environment, you might need to apply an offset to this
// eg. if your pixel coordinates are measured from bottom-left rather than from the center


Where TileWidth is the number of pixels across one grid square from left to right.


If you're using a true isometric projection,


TileHeight = TileWidth * 1/sqrt(3)

But note that many games use an "almost isometric" ratio of 1:2 to avoid irrationals, in which case


TileHeight = TileWidth * 0.5

No comments:

Post a Comment

Simple past, Present perfect Past perfect

Can you tell me which form of the following sentences is the correct one please? Imagine two friends discussing the gym... I was in a good s...