So I began my wargame this past week. I know, I’m kicking around two different projects (Shining Hope, and my Wargame). Shining Hope unfortunately requires some art that is beyond my current capabilities and hiring an artist and paying up front isn’t happening right now.
I’ve laid out a pretty sparse skeleton over the weekend which allows me to specify an outside tabletop texture and I can now draw models on the table. Unfortunately at the moment my models are kind of weak little squares but for testing they work.
I spent the better part of an hour this afternoon getting my zoom scaling to work properly. Essentially I have a 2D world matrix that keeps track of the location of all objects sitting on it. I assume that 32×32 pixels = 1″ x 1″ on the table. I can zoom in 64 or 128×128, and opposite I can come out to 16×16 or 8×8 or the whole table.
The problem I ran into was the world matrix defaulting at 32×32 needed adjusted to accommodate my new positioning at various zoom levels.
Right now my table is a massive 1024 x 768 pixels or 32″ across by 24″. (A standard gaming table is about 6 or 8′ across by 4′ wide). Future updates will let me work with a bigger table. Right now I have bigger fish to fry, like getting units to move around, act as units, etc…
Anyhow, so I draw three models on the table and I want them sitting next to each other in a line. Should be pretty easy right? For shits and grins I tell my program that on the world matrix they are sitting at 100,100 || 132,100||164,100. What that means is that if you break the screen down into its X,Y coordinates, model 1 is sitting at X = 100, Y = 100, model 2 is at X=132, Y = 100, and model 3 is at X=164, Y = 100: a horizontal line of three models.
At default zoom level it was rendering the line properly and life was good. However, when I went to zoom in (mouse wheel up zooms in, mouse wheel down zooms out) it was drawing the three models at odd places. Zooming in stacked the models on top of themselves, and zooming out put spaces between the models.
After about 45 minutes of pounding my head on the desk trying to figure it out, I went for a run and lo about 5 minutes into the run the conclusion came to me.
I have to scale the world coordinates with my images as well!!
Sure enough, if I go in and apply multipliers to the coordinates based on my zoom levels (32×32 = multiplier of 1, 64×64 was a multiplier of 2, 128×128 a multiplier of 4, and then 16×16 = 0.5f and 8×8 = 0.25f) then I get an adjust translation of my world matrix and the relative positioning of my icons.
To put that into engrish:
If I have a world matrix that is 1000 pixels wide by 1000 pixels tall, and by default my models are sitting at (100,100), (132,100), and (164,100), then when I display, they will display correctly.
If I mouse up and zoom in by 1 level so that my icons are now 64×64, I have to adjust the world that they live on to accommodate this mathematical shift. Meaning, I went from 32×32 in size to 64×64. I doubled my size (multiplier of 2) so I have to multiply my position on the matrix accordingly.
My table has an Update method which scans the objects to see if they are visible:
foreach (GamePiece piece in GamePieces)
Vector2 worldPositionAdjusted = GraphicsUtil.TranslateCameraOrigin(piece.WorldPosition, CameraZoom);
//if item’s worldPosition falls within the camera’s view, set its visible to true. Otherwise set it to false
if (worldPositionAdjusted.X < CameraOrigin.X ||
worldPositionAdjusted.Y < CameraOrigin.Y ||
worldPositionAdjusted.X > CameraOrigin.X + clientScreen.Width ||
worldPositionAdjusted.Y > CameraOrigin.Y + clientScreen.Height)
piece.Visible = false;
piece.Visible = true;
The translation code above (TranslateCameraOrigin) is simply a switch statement that figures out the multiplier and then does this:
return new Vector2(point.X * zoomMultiplication, point.Y * zoomMultiplication);
My pieces draw as such:
//TODO: note that this zoom only works on squares right now. cav bases are rectangles and need to be factored in later
spriteDimensions.X = (float)Math.Pow(2, pixelTranslation);
spriteDimensions.Y = (float)Math.Pow(2, pixelTranslation);
//if i’m at 32, 64, and 86 for three objects and i move up to 64 pixels in size, i need to calc this out
//Final Screen Position (FSP) = (currentX – screenX), (currentY – screenY)
//In this case we would draw the object at 100, 200 if it started at 500,500 and camera was at 400,300
//scale the location on the screen
//the world view is set at 2^5 (32×32)
//origin.x = 500 – 400 = 100
//origin.y = 500 – 300 = 200
spriteOrigin.X = ((worldPosition.X * zoomMultiplication) + (screenOrigin.X));
spriteOrigin.Y = ((worldPosition.Y * zoomMultiplication) + (screenOrigin.Y));
new Rectangle((int)spriteOrigin.X, (int)spriteOrigin.Y, (int)spriteDimensions.X, (int)spriteDimensions.Y), //destination rectangle
Color.White, //hue color
0, //rotation value
0.1f); //layer depth
Again what does this mean?
This means my 32×32 icon sitting at 100,100 is a 64×64 icon sitting at position 200,200, and at 128,128 is sitting at 400,400. As I zoom into the table, the size of the table gets larger to accommodate the larger pieces. A model is a model. If I stick a model down on the table and move closer to it, the model appears to get large and so does the table to my perspective even though the model itself does not move.
So I got that to work correctly. I also added code to add and remove the USER INTERFACE PANEL at the bottom with the TAB key. This will eventually contain a mini map, unit card information, and command panel. Right now it is a whole rectangle of awesome nothing.
The last thing I did tonight was add some gamepad support which actually turned into a lot more work then I had bargained. That was due to my own poor planning.
The XBox Gamepad now will zoom in and out with the DPad. The Left stick moves the mouse pointer. This was the part that was getting me. It’s fairly easy on the surface until you have a moment of ineptitude as I did.
Moment of Ineptness
So basically up to this point I draw the neat little hand by figuring out where the mouse is and then drawing the hand icon based on the X,Y of the mouse. I had to add a new variable to keep track of the pointer and then move it with the mouse OR with the gamepad.
Seems easy. Except that my mouse was being looked at first. So this was what was happening:
Mousecode says (pointerLocation.X & Y is where the mouse is)
GamePadCode says (move mousepointer based on stick direction * speed variable)
Can you see what the problem is here?
I sat there moving the gamepad around, and the mouse would just move six pixels or so and then stop (my speed variable was six). I sat there going “WTF”…
Well yeah genius. The mouse code was always running first and resetting my position to where the mouse was sitting idle. I’d move it with the gamepad, then the mouse would run and say “you told me to set the pointer to where I am and i haven’t moved so back you go”
So in the mouse code I capture previous state and basically say “if you haven’t moved since last time I looked at you… do not reset the position. Only set the position if you move”
Had something similar happen when implementing my click with the “A” button. I would click the mouse and nothing happened anymore. That was because my gamepad was constantly setting the click status to not clicked since it ran after the mouse. I told the gamepad “if the mouse has clicked, do not reset the mouse because the “A” button is not clicked”. I don’t see anyone using a gamepad AND a mouse at the exact same time…
So now I get to have some fun. Have a few tasks this week I need to do:
- Be able to click a unit which “Activates” it. An activated unit can move and display some information about itself. This will require adding an initial data structure to the object and then a data area in the UI panel to display it. I will also need to make the object aware of when it is active or not.
- Need to write code to move units. There are several ways to do this. I want the game pad to be able to move units. Haven’t figured out how yet though as L.Stick moves the mouse pointer. Perhaps R.Stick can move it up and back and if right or left it can rotate the unit… I will have the keyboard able to move a unit as well with arrow keys (forward, back, side to side, and then a couple rotate keys) and last the hard one will be the ability to point at an area on the battlefield and have the unit animate itself up to it.
- Along with mouse point click movement I need to be able to be able to use the mouse to realign the model (rotate it) so that if I want it to face right I click on the spot and then drag the mouse to the right, causing it to face that way.
This is some serious work. Doesn’t sound like a lot but the rotation algorithms require some trig. My next update will occur after this works. =)