20150714

PaaS and Compiler bugbashing

In the past few days, I thought I should start to have my server code running on some online server, so I decided to give OpenShift a shot - this is a Platform-as-a-Service (PaaS) solution, which - in layman's terms - means I can host my server without paying anything. Of course, the free plan can't handle big traffic, and there are other limitations, too. But it's fine for testing.

Since I wrote my server in Haskell, and OpenShift supports custom cartridges (Haskell, for example), OpenShift was a logical choice. However, it requires that my code is buildable in Cabal (Haskell's "package management" system), which shouldn't be a problem - I just write a .cabal file defining the name of the project, the author, files that are used, etc. Well, it did cause problems.

If I compile my server using `ghc main`, everything works as it should. However, if I use cabal to build it, it doesn't work quite as it should - it hangs at some point. I couldn't uncover the causes, so I wrote a post to reddit (2nd time I ask the internet's help in something), where a commenter gave me some pointers on how I can continue uncovering the secrets of this case. Finally, I was able to find the cause of the problem: cabal has optimization level one on by default. (Cabal invokes GHC with a ton of flags. I took and replicated this command in the comand line, and then tried to delete some flags, trying to figure out which of them causes this problem. Finally I could isolate the -O flag.) No problem, I could simply turn it off in the .cabal file, so the problem is essentially solved. However, there was something still bothering me.

I suspected a bug in the haskell compiler (GHC 7.10.1), so I tried to reproduce the problem with a minimal and complete code. My method was deleting more and more code, furiously recompiling to check if the problem is still there, until there was only one file left, with a code that couldn't be simplified further. I checked a few times more, to be sure that if I made any changes to this code, the problem would go away, and then filed a ticket on GHC's trac.

Since this is my first bugreport, I'm a little nervous, and also I have a feeling I could have done more to help their problem (provided that this actually *is* a compiler bug, and not a result of some silly mistake I made), but we'll see what happens next.

All this was done while I was sleep-deprived and finished a 7-hour shift. It took me about 6 hours to do all this stuff. And I don't have much time to sleep, so I probably should go to sleep now, so I maybe have another 4 hours of sleep. But I think I'll pat myself on the back.

20150710

Towns

Map editor now supports towns. A couple of points:
  • Had to change Physics2D.Raycast to Physics2D.RaycastAll in the code responsible for changing tiles, due to towns being over terrain tiles - probably would have been more elegant to use layers, instead of just checking for the name (either "HexTile" or "TownTile"), but I'm following the 3-step development process (I'm currently at step 1):
    1. Make it work.
    2. Make it beautiful.
    3. Make it fast.
  •  This GUI is like my backpack: there is always more room for things. But it starts to look cluttered. I probably should do a redesign at some point in the future.
  • Original plan was to add the ability to place units, but then I would needed another GUI feature to change the amount of food each unit had. So I scrapped it. Along with the GUI redesign, I'll need to find a convenient way to add this feature.
  • You don't need undo and redo... *waves hand like a jedi* You really don't... Okay, you do, and I'm a lazy sloth. Someday... someday...

20150709

Saving and loading maps

I am lazy. I wanted to figure out a file structure for storing maps, but then I thought it would be easier to just serialize the whole Map object to a file, and then load it back. It isn't programmer-friendly, meaning it is really hard to write a map file parser, if you don't use C# and you don't know the layout of the Map object. I promise, if the need ever arises, I will rewrite the whole map saving/loading part.

So currently, I'm using System.Serializable for saving and loading maps. There was a little problem, though (other than figuring out how serializing actually works): Unity's Vector2 is not Serializable. I am in a fortunate situation, as I don't use Unity's types extensively in my Map object, so it was easy and fast to write an ad-hoc replacement for Vector2, but if you ever want to use C#'s internal serializer, know about this problem: Unity's types are not serializable by the default C# Serializer.

Anyway, a gif from the Save / New / Load dialog:


20150707

Minimap

Today, I finished up some code from yesterday, simplified the painting algorithm, and added a minimap.

The painting algorithm was a little bit pathetic: at first, I only supported 3 brush sizes. Writing the painting algorith wsas kind of trivial for these three sizes: I told Unity which tiles should we overwrite. Yes, by hand. I know, I should kill myself. But today I re-discovered some older code (it was from the map generator I wrote for a tech-demo), which was a method to create hexagon rings (a hexagon ring, in this sense, consists of every tile that has the same distance from some origin). Now I just tell how many rings (of what sizes) do I want to make, and voilá, I can have as many brush sizes I want (I made buttons up until 6, I think that's more than enough).

For the minimap, I originally thought I would have a Texture2D, and update it at every painting event, but then I did a quick google search to see how others do it. Well, it turned out, creating a second camera is way more easier, and why-the-f***-didn't-I-think-of-it-OMG... So I made a second camera, and I'm dynamically changing it's viewport size as the map gets bigger. For this, I used a bounding rectangle calculator method, which I wrote earlier today - it simply calculates every added hex's coordinates, and updates the min and max variables accordingly. It doesn't update when erasing, though - that would be a little more resource-heavy. But if you completely erase the map, it will reset to default. Hmm....probably I should have a Clear button for that...

Also, I have noticed that the tile outlines are kind of visible in the minimap. This gave me the idea to make a layer for a rectangle encompassing the area the user can see, and it would be only visible to the minimap, and then place the outlines to a different layer, which would be only visible to the main camera. Also, clicking in the minimap should reposition the main camera - I think I can read the coordinates from the minimap's camera, and set the main camera to that position. But this is not a priority right now.


20150706

Map painting

The map editor has a few things it should be able to do:
  • Create a new (empty) map
  • Save a map
  • Load a map
  • Show a minimap of the whole map
  • Set brush tile type 
  • Set brush size
  • Set tiles
  • Erase tiles
Today, I did all painting-related things: setting tiles, changing brush tile types / brush size, and erasing tiles. Basically, you have a brush that is either 1, 3 or 5 cells wide. There are eight terrain types, plus an "empty" terrain type, which is effectively the eraser.

How did I make this work?

I made a HexTile prefab, which is a GameObject with a SpriteRenderer component (and also a child that would be the outline, but this will be only used in Game mode). This is what gets put here and there. At each frame, I check if the mouse button is down. If yes, I calculate which hex is the mouse pointing at, and then check if there is already a tile at that hex's center (with Physics2D.RayCast) - if yes, I delete it. Then I check which terrain type is selected (the buttons on the sidebar are a toolbar) and if it is not the empty terrain, I put a tile of said type to that hex. If the brush size is bigger, I do the same for the other hexes.

The result is here:


20150704

Menu in Unity and Pixel to Hex

So I have decided to start to work on the level editor. Gotta delay the actual playable game a few months somehow, and wasting time on a level editor is the bast way to do so. But I am better at delaying things than I thought - it took me 8 hours to make hex-to-pixel conversion working.

Before venturing into details, a few words on how I did the menu: There is a MonoBehaviour class for the Main Menu, and for every menu item in that. Each one uses OnGUI() for the GUI stuff. Also, the lobby uses a Lobby class (which doesn't inherit from MonoBehaviour), which is responsible for the network connection and communication with the server. The editor itself is also a MonoBehaviour. When I want to go from one MonoBehaviour to another, I simply disable the current one and enable the next one. All these behaviours are attached onto a gameobject named "_SCRIPTS", but by default, only MainMenu is enabled.

Now, how will the MapEditor work? Each frame, it will draw a few hex outlines onto the visible part of the scene - if you click into one, the currently selected hextile will get placed there. There will be a GUI part, where you can select what kind of tile do you want to place on the map. This is the basic idea. Here is a gif about the current state - please note, that the outlines are static at the moment, and the tile being placed is just a cyan circle. Next step will be to fix these.



If you want to know how to convert between Hex and World coordinates, contact this page: http://www.redblobgames.com/grids/hexagons/

But for my purposes, this is how I did it (with flat-top hexes):
//sq3 is sqrt(3) and sq3_3over2 is 3 / (sqrt(3)  * 2)

Vector2 HexToWorld(Map.HexCoord coord) {
    float x = coord.x * sq3_3over2;
    float y = coord.x * 0.5f + coord.y;
    return new Vector2(x, y);
}

Map.HexCoord WorldToHex(Vector2 coord) {
    float x = coord.x / sq3_3over2;
    float y = coord.y - (coord.x / sq3);
    float z = -x - y;

    float rx = (float)Math.Round(x);
    float ry = (float)Math.Round(y);
    float rz = (float)Math.Round(z);

    float x_diff = Math.Abs(rx - x);
    float y_diff = Math.Abs(ry - y);
    float z_diff = Math.Abs(rz - z);

    if (x_diff > y_diff && x_diff > z_diff) {
        rx = -ry-rz;
    }
    else if (y_diff > z_diff) {
        ry = -rx-rz;
    }
    else {
        rz = -rx-ry;
    }

    return Map.HexCoord.HexCoordXY((int)Math.Round(rx), (int)Math.Round(ry));
}

Map.HexCoord is a class, which has three coordinates: x, y and z. Their sum is always 0. HexCoordXY is a method of creating a HexCoord , where you fed the x and y coordinates, and z is calculated from these two (z = -x - y).