20151206

UI progress

After a few months of doing nothing, I got motivation to resume working on the game. Thank you, /u/HappyGirlLucky!

My task is to migrate to Unity's new UI system, which lets me create a user interface that is not so hard on the eyes, and not a pain in the ass to use on mobile. First step was to flesh out some concepts in Inkscape - for details, see my earlier posts. Then, I exported all icons and the standard button shape to png files. In Unity, I made a Canvas object (Screen-space overlay), and I made a Game Object for each menu, and they all contain a Panel, and all the UI elements are on that panel. Not sure if this is the best practice, but the tutorial I was working from used this layout.

This is how the hierarchy looks:


And these are the corresponding menu screens:

 

For now, I hooked up some of the buttons - the Play button and the Map Editor button from the main menu, and the home button on the other two. Here is a gif in 1920x1080 landscape:


 For the portrait version, just watch the middle of the gif :D

20151031

UI design

Joel Spolsky wrote about good UI design here: http://www.ikoffice.de/downloads/Books/User_Interface_Design_For_Programmers.pdf (Yes, I know, I'm giving away someone's work for free. I hope that as the CEO of StackOverflow he doesn't mind this.)

I know I've read it at least twice since my boss suggested it at one of my jobs, and I really enjoyed reading it. Most of the points seemed straightforward and logical, served in an easy-to-understand and fun way. However, I can't remember a single point of the book. In spite of this, I'm sure the things I've learnt from it are influencing my UI design methodology to this day.

So here I am, developing a game that should've been finished months ago I want to get on the most types of devices possible, each of which has different input methods, different screen sizes, and different metaphors established. How do I deal with it?

Using an app on a PC is easier than using the same piece of app on a mobile device. You have bigger space on the screen, finer and more diverse input methods, and usually more horsepower. Probably these are the main reasons why apps made for a desktop computer are typically more complex, whereas apps on mobile devices usually focus on one thing.

How does it affect us as an app dev? If you are lazy like me, you don't want to think of different input methods on each platform - you would rather have one, and apply it to all platforms. In this case, it makes sense to customize the UI for the most restrictive platform, and then add convenience features on the others, where there aren't that much restrictions.

So I went ahead and started to design the UI for the smartphone. Key principles were:
  • Easy to use buttons (So they should be as big as possible)
  • As little text as possible (So far the only text is the title of the game)
  • Make the UI minimalistic, make it stand out and make it easy to understand
  • Structure everything in a way that the most used features are the most easy to reach
Currently I'm working on the main menu, which looks like this:


I have asked a few of my friends if they can figure out what each button means, provided this is a strategy game. Results were satisfying. (FWIW: New game, Settings, Map editor, Exit)

If you go to the map editor, you'll see this:


Top right side there is a button, which will contain a minimap. If you tap it, the minimap will expand. Tapping the minimap will minimize it. On the bottom, there is a Menu button, a Settings button and a paint mode. The paint mode button will change on each tap between paint, delete and move mode. (Move mode means you can drag the map around.)

Menu screen:



Self-explanatory. Settings screen:


This is only part of the settings screen. Here, terrain painting mode is selected, and the thinnest brush (one tile wide). You will be able to select town mode (where you can tell for which players will you place towns) and unit mode (same, but you can also change the type of unit being placed).

Please note that this isn't implemented yet, I just put together it in Inkscape. Also, if you are not worried about copyright things, you can google <icon name> icon (e.g. save icon), open one that you like, copy the image, paste it in Inkscape, then click on Path -> Trace bitmap, and you can have an SVG version of it. Then you can press Ctrl-L (or choose Path->Simplify) to have less points.

And now, if you excuse me, I'm going back to read this Joel Spolsky book.

20151004

Handling input

The problem: how to structure the input handling in a game that could run on PC and mobile devices? Each platform has different control schemes. In cases like this, I like to look for the common denominator. In this case, if we use only the left mouse button, the mouse wheel and the mouse itself, we get the same control actions as on the smartphone: tap (click), swipe (drag), pinch (scroll the mousewheel). Since I have experiences only with these two kind of devices, I won't talk about others, but I assume it isn't hard to fit these three actions over them.

Using these input methods, we can define events, like Tap, Drag and Zoom (because fuck naming consistency). However, we can zoom while we are dragging, so we will track the zoom level separately from the other events. We will also need a None event type, for the times nothing happens.

Let's see what kind of event transitions are available: take None, for example: when the user clicks (or touches the screen), it could mean either a start of a Tap, or the start of a drag. How to decide which one? I arbitrarily decided that if the touch (click) lasts no longer than 5 frames, and at the end of it the input device's position is not further than 10 pixels from the original place, I will consider it a tap, otherwise it's a drag. Until it isn't decided, I will call that state Hold.

So None can transform to Hold, Hold can transform to Drag or Tap, and Drag / Tap can transform to None. Maybe introducing Hold isn't a good idea, but I can't see any problems with it yet. Time will tell.

Now, when I worked on other parts of the code, I just threw in some GetMouseButtonDown(0) to check if the mouse was pressed. But now, that I want to use the input, this kind of decentralized input checking is starting to get messy, harder to understand and error-prone. This is why I have created an InputObject, and attached an InputController script to it.

This script has three public variables (scrolling being the fourth, but I will implement that later): current input mode, event position and last position. The current mode can either be None, Hold, Tap or Drag. The event position will always tell where was the cursor when the action happened. In the case of Tap, it is where the action ended, and in the case of drag, where was the cursor dragged to in each frame, and also the last position will tell where the cursor was dragged from. (In all other cases, the last position tells nothing. I know it could be done more elegantly, but this is straightforward enough for now. If I'll make a library out of it, which I seriously doubt, I will do it elegantly.)

Then, in the Update() function, I just use a switch on currentMode to decide what to do next. The cases are bold here.

void Update () {
  leftButtonDown = Input.GetMouseButton(0);
  Vector2 currentPosition = Input.mousePosition;

  switch (currentInputMode) {
  case InputMode.None:
    if (leftButtonDown) {
      currentInputMode = InputMode.Hold;
      lastPosition = currentPosition;
    }
    break;
 
  case InputMode.Hold:
    if (GetFastDistance(currentPosition, lastPosition) < 10 &&
        framesSinceLastClick < 5) {

       if (!leftButtonDown) {
         currentInputMode = InputMode.Tap;
         eventPosition = currentPosition;

       }
    }
    else {
      currentInputMode = InputMode.Drag;
      eventPosition = currentPosition;
    }
    break;
 
  case InputMode.Tap:
    currentInputMode = InputMode.None;
    break;
 
  case InputMode.Drag:
    if (leftButtonDown) {
      framesSinceLastClick++;
      lastPosition = eventPosition;
      eventPosition = currentPosition;
    }
    else {
      framesSinceLastClick = 0;
      currentInputMode = InputMode.None;
    }
    break;
  }
}

Also, since I don't need a precise distance, just something like a pseudo-distance, here is a faster version, that defines a square-shaped area rotated by 45°:

int GetFastDistance(Vector2 p1, Vector2 p2) {
   return Mathf.RoundToInt(
    Mathf.Abs(p1.x - p2.x) + Mathf.Abs(p1.y - p2.y));
}

And this works surprisingly well. Now, the camera controller's update has a code snippet responsible for calculating the amount of displacement for the camera:

 if (inputMode == InputController.InputMode.Drag) {
  Vector3 difference =
    inputController.GetCurrentPosition() -
    inputController.GetPreviousPosition();
  Vector3 pos = myCamera.ScreenToViewportPoint(difference);
  currentMouseDragSpeed =
    new Vector2(-pos.x * mouseDragSpeed, -pos.y * mouseDragSpeed);
}
else {
  currentMouseDragSpeed = currentMouseDragSpeed * 0.8f;
}

(Don't worry, my code is commented, I just don't paste the comments here.)
 

20150915

Geohashing

Today, I solved a speed problem, related to coloring the tiles based on visibility.

My first, crude solution was this:
  • We have an incoming list of visible tiles, named visibleTiles.
  • We have a list of all tiles, named allTiles.
  • All tiles in allTiles is set to dark
  • For each visibleTile in visibleTiles:
    • for each tile in allTiles
      • if tile.coordinate == visibleTile.coordinate 
        • tile is set to bright
So if we have 'n' tiles and 'm' is visible of them, this will use 'n' times 'm' comparisons. Clearly not an effective method. It took half a minute to update a map containing 60 tiles, with about half of them visible.

So I thought maybe I should just hash the coordinates, and use them as an index key. Then, when I want to look for a tile with a specific coordinate, I'll hash that coordinate, and look for that hash in the list. (This requires of course, that each time I hash the same number, it will give the same result.)

Well, in C# there are two data containers for that: Hashtable and Dictionary. What is the difference?

You will declare a Hashtable this way:

Hashtable ht = new Hashtable();

That's all. No types, no anything. Dictionary is different:

Dictionary<Key, Value> dict = new Dictionary<Key, Value>();

So you can have type safety with Dictionary. But that's not all. Let's look at this example:

HexCoord hc1 = .new HexCoord(1, 1);
HexCoord hc2 = .new HexCoord(1, 1);

gameObject tile1 = new Tile(...);

// hc1 and hc2 are the same coordinate, but not the same
// object
Dictionary<HexCoord, GameObject> dict = new  Dictionary<HexCoord, GameObject();
dict.Add(hc1,  tile1);

// Now, if you just check if it is indeed in the list:
dict.ContainsKey(hc1); // True
// ...you will get true. But:
dict.Containskey(hc2); // False
// ...this is strange. What is the problem?

The problem is that internally, Dictionary uses ReferenceEqual for equality check. ReferenceEqual(o1, o2) only returns true, when o1 and o2 are pointing to the same location in memory. So in this case an IEqualityComparer class is needed. I won't go into details, google is your friend, and also I wanna go to sleep soon, have no energy to dive into this topic.

Point is, when you create a dictionary, you can feed it an IEqualityComparer, like this:

Dictionary<Key, Value> dict =
  new Dictionary<Key, Value>(new KeyComparer());

Also, if you would have used Hashtable instead of Dictionary, you would receive an error if you wrote:

Hashtable ht = new Hashtable(new KeyComparer());

...saying that the compiler couldn't typecast IEqualityComparer to int. I don't know why this happens, but it cost me at least half an hour thinking and googleing to realize that Dictionary doesn't have this porblem.

Now it works much faster then originally, in under a second for 60 tiles.

20150913

Visibility

I want this game to be impossible to cheat in without access to the server. (By cheating I mean maphacks and things like that. DDoS & co. are entirely other stories.) So I only send informations to the client that they have access to.

This means the server has to calculate, what can each client see, then - using this list - filter all the units and towns, and send them to each clients. Now, since I have to calculate all the tiles that are visible to a client, why would I choose not to send this info to the clients as well? This way, the client code doesn't need to calculate visible tiles.

So this is what I did. After the client receives the list of visible tiles, it sets their color to white (meaning they will be drawn in their original color), and the rest to grey (meaning they will be darker). The only problem so far is that I can't index the tiles by their coordinates (yet), so it takes half a minute to set each tile to the correct color. But at least now it works.

I've made a quick test to check if the visibility rules are calculated correctly on the server. Here is the result:




For the test, I used the Forest, Grass and Mountain tiles. The empty tile is opaque and invisible, so I didn't need to block out visibility with HighMountain tiles. Okay, let's go through all seven tests:

  1. You can't look into a group of forest tiles from another group of forest tiles.
  2. However, from a mountain tile, you can see other mountain tiles.
  3. You can't see forest or mountain tiles from outside.
  4. But you can look outside from mountains...
  5. ...and from forests.
  6. Mountains will block your vision through forests.
  7. Forests will block your vision from mountains.
One might argue that why can't one see the other mountains in test 7. - forests shouldn't block the sight from mountains, even if you can't look into them. While this argument is correct, the solution is not trivial - it would require another visibility type, or some height information, or something else.

Or maybe I just shouldn't allow mountains to be visible from other mountains. But playtests will tell. If it is counter-intuitive, I will change it.

20150906

Setting up Haskell with Atom on Windows

  1. Install MinGHC from https://github.com/fpco/minghc
  2. Install Atom from http://atom.io/
  3. Do your Haskell-configurations
    1. Open command line, then "cabal update"
    2. Modify the cabal config file in AppData\Roaming\cabal - enable profiling libraries and documentation
    3. Set up your sandboxes
  4. Install ghc-mod (cabal install ghc-mod)
  5. In atom, install the Language-haskell package
  6. In cmd, run this command: "apm install language-haskell haskell-ghc-mod ide-haskell autocomplete-haskell" (This will install the ide-haskell package.)
  7. ???
  8. Profit
The end result will be something like this:


20150904

The Best Programming Font Ever, and waving goodbye to Notepad++


Yes, it happened. I changed my default text editor from notepad++. Why?

Yesterday I have changed the default font used by npp. What happened after that, is npp had its colors mixed. Most notably, the background color went from a convenient grayish color to some bright orange. I don't know if I did something stupid (maybe pressed some buttons when I thought I'm writing somewhere else, probably in the browser's address bar), or it was npp's mistake, but I don't really care. I decided to look around in the world of not-so-IDE-but-more-advanced-than-notepad-kind-of text editors.

What I found is that there are mainly four contenders: SublimeText, Brackets, LightTable and Atom. SublimeText is propriety, LightTable is Clojure-oriented at the moment, so it leaves Brackets and Atom. Adfter eading this comparison I have decided to try my luck with Atom (which is developed by the devs of GitHub).

First, I saw their trailer video. I have to say I think it is hilariously well done. They seem to have a sense of style, which is good. Next, I have installed it, which wasn't hard (that is, after I finally found the download button on their site). Then I installed the "Language Haskell" package, which gave me syntax highlight.

When I used Notepad++, I had a little script, that ran "ghc -fno-code Main.hs" when I pressed Ctrl-F7. How should I do it in Atom? After reading about a few other packages, I settled at Process Palette, which is a little tool that enables me to create hotkeys and assign commands to them (among other things). The code snippet was easy to write:

{
   "action" : "Typecheck Haskell code",
   "command" : "ghc -fno-code Main.hs",
   "outputTarget" : "void",
   "keystroke" : "ctrl-f7",
   "successMessage" : "{stdout}",
   "errorMessage" : "{stderr}",
   "fatalMessage" : "{stderr}"

}

(I wanted to feed the output to the little message window instead of the panel on the bottom side of the window, because I like style. But that's just my personal thing.)

  • EDIT: It seems I don't need this process-palette extension and the code snippet, as ide-haskell also has a code-checking feature. Testing it.
  • EDIT 2: Much better. Wrote a new blog post about it.

But there was another problem: when I presset Alt Gr + B (which is the character '{' for me), it didn't write a bracket, but instead jumped to the beginning of the line. This is because Atom has a key binding on Ctrl + Alt + B. So I had to disable this hotkey - to do this, just go to Settings -> Keybindings, select Ctrl + Alt + B, click on copy, open the keymap file, paste the command you have just copied, and delete the string from the command part. This way, you will have an empty action assigned to the key combo, and it lets you write the bracket.

  • EDIT: instead of leaving it empty, you should write 'unset!', because otherwise weird things (like cryptic error messages) will happen. So this is the correct snippet:
 'atom-text-editor':
  'ctrl-alt-b': 'unset!'

You might also want to turn off the feature that automatically completes the brackets when you type in the opening part. it can be done by going to Settings -> Packages, searching for "Bracket-matcher", and clicking on Settings - you can turn off this feature there.

I also found The Best Programming Font Ever, namely "Input" - it finally has everything done right - it is readable, it distinguishes the il1O0o characters well, and what's more - it is configurable! You can set a few settings on its webpage (like character strength, line width, character width, the type of the letter 'a', 'g', etc...), and then you can download the customized version.

After I have done everything, it looked like this:




I'm excited about Atom at this moment. It is still a little rough around the edges, but looks very promising.

20150903

Units between tiles and attacking

Having the requirement that units travel between tiles for one or two turns is nice, but it poses a problem: when a unit is between two tiles, what happens when someone would arrive to one of those tiles?

Moreover, there is another problem: what if two units of the same owner want to move to the same tile at once? I made it a clear rule that one tile could be occupied by only one unit at a time.

Ok, let's tackle the first problem: say there is a unit (blue circle) trying to go from Tile A to Tile B. It's the start of the turn. Now let's imagine there is another unit (red circle), that is trying to move to Tile A.





Let's say their speed are 0.5. (I should've painted mountains instead of grasses. Whatever.) At the end of the turn, their positions are like this:






I quickly discarded the idea that we should use the border of tiles as places, too, because that would really complicate things - like calculating visibility, calculating speed, and so on.

But which tile should we take into account when a unit is between tiles? The target tile? The starting tile? Both?

If wou would take both into accounts, then the red unit would kill the blue. This is because the target of the red Infantry unit is the same as the origin of the blue Archer. But at the beginning of the turn, there were no overlapping tiles. So this idea is not consistent - it is possible to kill a unit when both of the attacker and defender are moving. Idea scrapped.

Okay, then let's look at the possibility when we use the target tile. Here, blue tries to escape by going to the mountains.




We know that going to the mountain halves a unit's speed. So going by common sense, red should capture blue. Let's see, what happens if we take the target tile into account:




Seems kind of counter-intuitive. Okay, red could capture blue in the next turn, but still, this is plain bad for a perfectionist like me. If we would take into account the original tile the unit was on, everything would work as they should.

The result is that we will take the original tile into account. But what about multiple units going to the same tile?

Since I don't allow more than one unit on any single tile, I would have to kill some units. But which one? It seems I would have to kill all of them. Which is kind of unfortunate if the user only misclicked, or was tired, and didn't think through where they want to place their units. One possibility would be to have the regular "move and attack" command, and there a simple "attack" command as well. The "move and attack" command would tell the unit to attack anything that is on the target tile, and also start moving towards it. The "attack" command would tell the unit to attack the target, but stay on the place.

But in this case, the unit's attacking tile would be one tile forward, but the tile it can be attacked on would be one tile backward (when the unit is between tiles). This is really uncomfortable if an infantry attacks an archer, since I also want every attack to result in exactly one winner. Would that mean that the defender could kill the attacker on a tile it can't even attack?

Also, it would be possible to set up a chain of attacks, where the outcome is not so clear at first glance:





The unit marked with read would die because it is attacked with a unit of the same type, but would also kill the blue unit. Resolving this in game logic would require a two-step system: in the first step, we check all encounters, and mark the dead units, then in the second run, we delete all those marked units.

If the units attaking tile would be the same that they could be attacked on, this wouldn't happen. But this would eliminate the possibility of a command that tells the unit to attack, but not move. (Although I don't really like the idea of attacking without moving, this is a medieval game, not a sniperfest.)

The other solution would be to just simply disable this possibility from the client. This is not a good idea, as I can't be sure that players use the official client, and they can issue commands that would make the gamestate illegal with respect to these rules.

The most likely scenario is that I will just resolve units of the same owner going to the same tile like I would do it if the units were of different teams. I don't want to disable this possibility in the client, because it could be useful in some very rare cases.

There is also the possibility that two units of different teams want to swap places. I don't want them to just swap places, because it would make it really hard to capture a single unit. But by these rules, this is what should happen.

It really needs some serious thoughts.

20150822

WHY U NO BLOG????

Well. This is my shot at looking like a normal indie developer - keeping track of things on blog, on twitter, on github, on yout...wait, not youtube. At least not yet.

I had a lot of things to write about until recently - design decisions, progressions and hiccups in the development process, things like that. Well, maybe it isn't considered much by indie standards, but I usually have very few things to talk about to random strangers of the internet. But for a few weeks I haven't written anything. It is because, for a few weeks, I haven't progressed anything. I wrote maybe 20-50 lines of code. What happened?

Work. More work. Heatwave. Heatwave while working. More work. Not enough sleep. Heatwave. Work.

The thing is, I'm a very low-energy person, and it can be depleted pretty fast. This month, there was always a colleague who was on vacation. (not me, mind you.) This left me with a workload bigger than I usually had to bear with. When I got home, I was dead tired. All I could do is to fire up some MOBA and anger myself because of the noob teammates.

And then there were two heatwaves. My room was a sauna. If the air is 30 °C then I can't think. (We don't have air conditioner.) So in addition of being tired, I could barely get enough cool. I couldn't do anything productive. But now the heat is gone, and all of my colleagues are finally back.

Next week, I won't be home, so probably I won't code. After that... well, that depends. Since I am in a different department in my work starting from September (it doesn't ahve anything to do with this month, just a coincidence), I'll have to learn a lot of new things. (I'm learning SAP, ABAP, and things like that.). If it doesn't sap (hehe, get it?) too much energy from me, then I will be able to progress. Otherwise, not so much.

I also angered myself so much on Heroes of the Storm, that I deleted all games from my PC. I considered myself pretty good, I had a nearly 50% win rate. And then it broke. For example, the last 60 matches I played, I lost 40 of them. (I lost all of my 10 most recent matches.) I don't know if I suddenly became lame, or just RNG thought I should play with noobs, but I had enough. Team games suck, because doesn't matter how good you are, you won't be able to carry a whole team.

Fuck them.

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).

20150630

Lobby for more mental energy

Last few weeks passed by leaving me feel like a zombie. When I got home, I could only play and watch series. (On a more uplifting note, I bought Abathur in Heroes of the Storm, and I love his playstyle.)

What little energy I had I used to rewrite the lobby, because the lobby code was a mess. Now I have a Lobby class that is responsible for communication with the server, and the Multiplayer GUI just communicates with this lobby class. It works just like the old one, but now I can understand my own code.


Now I don't know what should I work on. Either the Level editor, which seems simple (just click on a tile, and then click on a place on the map, and then overwrite that place with the selected tile in memory). However, I could just create a map by hand, or using Hexographer and just convert it. It would be good enough for testing.

Well, I guess I'll have another day to decide that, after I'm done feeling guilty of the lacking updates. Also, I'd like to thank Introversion software the inspiration to get my working energy back. (Prison Architect Alpha 34 came out today, and in the video, they talked about how the obligation of getting a new build out at the end of the month makes them able to work even when they really don't feel like it. I envy them.)

20150616

Sleep deprivation and server architecture

If I sleep about 4 hours for a few days, I become more concentrated (can get work done more easily), more creative (less energy for my mind to filter out ideas bubbling up from the unconscious), and less able to create mental constructions (so I won't come up with an elegant architecture). If I sleep less then 6 hours, and then I work 8 and a half hours, then I will be absolutely useless.

I also learned that my circadian rythm does everything it can to stop me from getting up in the morning and going to bed in the evening. When I'm working in night shift, I usually get up around noon, and go to bed around 4 AM. (Also, if I'm left to myself, I will usually sleep ~10 hours.) The best thing is that at night everything is quiet, so I can think. But when I have to work from 6 am to 14:30 pm, this tendency to not sleep enough takes its toll on my ability to work on my game. This is why I didn't do much in the past week.

Before that, a first draft of the game server was finished. The first problem I ran into was this: when a client connects, it sends its name and intended color to the server, then asks for the player list. If this was done in a quick succession, then it received a list of players without its own data updated. Why?

Let's take a look at how the server architecture looked like:



In a nutshell: clients connect to the server. The Listener thread listens to them, and spawns threads for each client (not depicted on the picture, think them as part of the Listener box). Those threads send and receive messages to and from MessageRouter, which forwards the messages to either CommandInterpreter (if it is a server-administrating command, starting with the "recon" prefix), or LogicDaemon (if it is a message to the game server itself). Then they both send requests to the ServerManager, containing modifications to the actual server data.

The problem is that I'm using Software Transactional Memory (STM), so any transactions I make to an object cannot be seen from outside until it is finished. It means that while these requests are not finished, one can still access the old state of said object. This is why the client got the message that - in spite of the client has told its name - it doesn't have name assigned to it. If it were to ask for the player data, say, half a second later, it would have correctly seen its name in the player list.

What I did was to merge four of the threads into one: MessageRouter, LogicDaemon, CommandInterpreter and ServerManager.



Results: now if the client sends the message too fast, the server doesn't even get it. Fail. But everything else works as it should, so I hope it's just a minor something that I have overlooked. (EDIT: Figured out the reason, it indeed was a problem with how the server parsed the incoming messages. Was a fault of my own laziness.)

This is a thesis on writing a game server in Erlang. it seems, most of the components of the server can be found in my server architecture as well (with some differencies). I guess I can be moderately proud of myself.

20150611

Unity and spies

So Unity 5.1.0f3 just came out. I updated. Wish I hadn't. Because now it crashes while it tries to show the splash screen. Reinstall, registry cleaning, removing every Unity-related folders I know of, installing in a different place (what if bad sectors are the problem) - nothing has helped. Maybe I'll just give up and revert to 5.0.1.

So while there are no updates code-wise, I thought I'd share my struggle with the spy unit. Initially, I thought the spy could be a scout-like unit and that's all. But it wasn't as exciting as I think a spy unit should be. Sun Tzu in Art of War distinguished five types of spies:

"Hence the use of spies, of whom there are five classes: (1) Local spies; (2) inward spies; (3) converted spies; (4) doomed spies; (5) surviving spies"

So what are these?

"Having local spies means employing the services of the inhabitants of a district."

The spy "system" in its current form doesn't let us do this. So how should we incorporate it in the game design? Bribe enemy towns, maybe? What should that result in? Could we see a few tiles around the town? Should the cost be defined per turns? What would deter somebody from just buying all towns? Is it a life-like scenario? (While we're at it, is this game life-like at all?)

"Having inward spies, making use of officials of the enemy."

 Bribe enemy spies? What would deter a player from just hiring all enemy spies?

"Having converted spies, getting hold of the enemy's spies and using them for our own purposes."

When we would kill a spy, should the game ask if we want to kill or convert into a double agent?

"Having doomed spies, doing certain things openly for purposes of deception, and allowing our spies to know of them and report them to the enemy."

This would be fairly straightforward: assuming one of our spies is a double agent, we could move our units in a way that makes the enemy do incorrect assumptions. This only requires the game mechanics that lets a spy to be a double agent. Which is the harder part.

"Surviving spies, finally, are those who bring back news from the enemy's camp."

It's just sending a scout into the enemy lines. The kind of spy the current system has.


The concept of bribing seems to come up regulary at the spy types. If I were to implement it, how should it work?
  • Should the player capture a spy before it can be bribed? Not a good idea: the other player - seeing the spy had been captured - could just kill the spy and hire a new one.
  • Let the player bribe any spy anytime? (At least those who are visible to them.) Not a lot of players would use spies then - they could be seen accidentally, and then they are better off dead.
  • Hire the spies with some user-given cost, and let the other player bribe any visible spy at any time, with a custom cost, and if the cost is higher then it is converted? In this case, the players should not know if the spy is still theirs, because then they just raise the cost, and then they are OK. But how we make sure none of the players know if it is actually still their spy or not?
    • Place random units instead of what's actually there. I don't like this idea, as I'm not a huge fan of randomness. This game is all about a minimalistic warfare simulator, without any random chance whatsoever. It would also be somewhat hard to implement.
    • Doesn't update the fields that only the spy can see. It might actually be a usable idea. For some reason it still doesn't feel like it is, but I can't tell what is my problem with it. Just as with the spy-betting system: I sense there is something wrong with it, but can't tell what
 These are the ideas that I had so far. I'm not sold on either of them so far. It might be a decision that needs playtesting to figure out which is right, or maybe I got some other idea. For now, I'm going to go with the "spy-as-a-scout" plan, and then work out the details when it's time.

20150603

Static typing vs dynamic typing

Static typing all day every day!

I'm working on the server for the game.Previously I have mentioned Google App Engine, and the fact I'm using Python. Now that has changed. I am not using Google App Engine for now. (I'm rather thinking about getting a free plan on OpenShift.)

I could use Frege (Haskell-like language on the JVM) on the JVM with GAE, but why limit myself to GAE's restrictions, when I can have a free OpenShift account that doesn't have them? (I haven't researched the topic extensively, so I might stumble upon some factor that makes me rethink this, but I will eventually need an offline, local server, when I want to support LAN, so either way, I have to write it.)

What was my problem with Python?

When I wrote a few lines of code, then I fired up the client to test it, it caught some error - maybe I wanted to call a method on the None object, because I didn't check for the object being None (this was the most common type of error). I fixed it, ran the client again, and oops, another error. This time I tried to concatenate a string and a number together. Fixed that. Next run, I forgot to write the "else" part of an "if" statement - since that I always write out the "else" part explicitly, and then have a "pass" in it, if nothing else. I have avoided a few problems thanks to this habit. Ran the client, and it went fine.

Three runs for a few lines of code, half an hour wasted, and have no guarantee whatsoever about my code correctness. That would require unit tests, static code-analysis tools, etc.

What about Haskell?

After I switched back to Haskell (which I've been coding in for a few years now), I started by laying out the types of the functions I knew I wanted to have. Most functions' body was empty. It gave me a structure that could be filled with code - most times the solution was straightforward, thanks to the types, I hardly had to do any thinking. Of course, the initial, upfront design had to be thought out carefully, but after the types were in place, it was just compiling for typechecking, and fixing the type errors.

I coded for 4 days without ever running the code - mostly because there were missing body functions that I haven't written yet. But I had confidence that once I'll run the code, it will Just Work(TM). After I wrote every necessary functions, I have started the code, and it ran...

...with hitches. It turns out, that once the main thread finishes execution, all the other threads die instantly. So I just inserted a placeholder thread that just runs without doing anything, and the server is now working correctly (at least the connection handling, but I don't expect any big problems with the other parts). I could quickly add a logging capability as well.

What causes this difference?

Types. Let me explain.

What kind of troubles I ran into with Python?
  • - Null types - with Haskell, you can make any type into a nullable type by using Maybe. (Or you can just roll up your own Nullable datatype.) You want a nullable Int? You can have Maybe Int. You want a nullable EmployerStruct? Maybe EmployerStruct. You want a nullable function with side effect giving you back some value? Maybe (IO a). If you are a masochistic mad scientist, you can make nulable types nullable: Maybe (Maybe Int) (though this usually has no usability, so don't bother). The catch is: it is stated explicitly. If you are eypecting a nullable type, the type checker makes sure you are aware of it. If you are not expecting nullable type, the type checker makes sure you can't give that function a nullable type.
  • - Missing "else" caluses - in Haskell, an "if" MUST have an "else" clause. You have to return something. If your function returns a number, you have to return a number. (And "undefined" is a number. In fact, "undefined" is kind of a Null pointer. Although you are better off with some other error-handling, if you really need to halt and catch fire. If you just don't always want to return something, make the return type a Maybe, or some other kind of multi-value type.) Most of the time, however, this is not even uncomfortable. Sometimes it is, but you should know that it is for your own good.
  • - Concatenate a string with a number - if Python would have a compiler with types, that would've caught this. Without type-checking, that particular code branch has to run to catch this error. There are no tests in the world that can guarantee 100% coverage. (Of course, static typing could not get 100% coverage, either. But silly things like this? It does catch 'em quite well.)

As was written in Why haskell just works, there are three types of mistakes, and a static type system can catch two of them. Dynamic type systems are not so good at it. All in all, types are there to protect the programmer from shooting itself in the foot.

And, as Carmack said: "Anything that is semantically legal will end up in your codebase." - static types will make that "semantically legal" space smaller - which filters out a huge number of bugs.

Of course, the drawback is that it is less comfortable to adhere to the type system. But in the long run, the benefits are so much bigger than this discomfort. Think about it.

20150530

PaaS and static typing

I have mentioned Google App Engine - "Google App Engine is a Platform as a Service (PaaS) offering that lets you build and run applications on Google’s infrastructure."

You can build apps in Python, Java, PHP or Go. PHP sucks, Go is not that popular as the other three candidates, Java has a runtime penalty, and Python is a dynamic language.

What do I mean by runtime penalty? First, we need to know how GAE works: each time a client sends an HTTP request to the app server, an instance of the code is spawned to provide a response. For Python, this doesn't require a lot: just fire up the interpreter, interpret the script, and you are good. But for Java, this means you have to fire up the VM, and then run the Java code. The catch is, if there were no requests in the last 30 seconds, the VM shuts down, so next time it has to be started again. (Also, there is no way a free app could have a background process running indefinately. Task Queue and Cron is the closest you can have - or Background processes in the Module API, but I forgot why I ruled it out, and I'm lazy to check it again.)

In itself, this wouldn't be a big problem for my game, but combined with my hatred for Java, this is enought to not choose this. So this leaves my Python. I thought that maybe I just oversensitively annoyed by dynamic typing - it might not be that bad. I was wrong.

When I create a module in Haskell, the compiler spits out a thesis on why I should feel bad about myself - in the form of error messages, of course. Two points to mention: a) it is fast, b) if I fix those errors, I can be 90% sure my code works as it is intended. This is very different from working with Python: you write a syntactically correct code, and that's it - nobody's there to defend you from yourself, so you have to test everything in order to be sure that you will not shoot yourself in the foot. I usually write a few lines of Python code, then start the game, while watching the logs from Google App Engine Launcher, only to find my code just tried to call some method on the None object, because I forgot to check if the object is None. (Fore the record: there is no Null object in Haskell - if you want a Nullable type, you have Maybe - but it has to be said explicitly in the type signature (you use type signatures, right? RIGHT????), so when you work with a function that can have a Nullable object, you are aware of it, because the type checker makes sure. Bothersome in the short run, but you will thank for it every day after you wrote the script.)

I was patient with myself, kept telling "this is not so wrong, you just have to have discipline, and everything is good". Well, after years coding in Haskell, I have everything but discipline. It is so comfortable to know that there is a typechecker to cover my back that I'm happily rummaging through the codebase and creating a mess that I hope will work. (And I have the courage to call myself a "programmer".)

After a week, I gave up. I can't do it. I'm a lousy coder, I need my typechecker, I need my functional language, and I need my Haskell. So I'm now searching for something like GAE but for Haskell. I know that at FPCenter, I have a Haskell IDE on the cloud, and a free server that is ideal for testing, I just need to find some PaaS that is sized for my wallet. (I don't have $100 for a server at Amazon EC2.) I love GAE, but I hate the languages it lets me use.

On the bright side, I have learnt a lot from GAE: I learn how to use the NDB/Datastore, how to use the Tast Queue, how to use POST and GET requests, learnt about what the cron is good for, and I'm thankful for all of these.

20150526

Networking

Being multiplayer, a game needs a server hosting. The question arises: where and how to host a server?

To answer the question, we have to know what our needs are.
- Lobby
- Persistence (ranking and matchmaking, last few matches, statistics, etc.)
- Medium speed / latency, no realtime is required (hooray for turn based games)
- >99% uptime

Since I am a wannabe indie developer, I am tight on budget. I could pay, however I rather wouldn't until this game has at least some success. But later I might have to rent a server (due to traffic), so ideally I would like to find some host which grants a free plan that can later be upgraded.

First, look at the options:
  • Find a free webhost that lets you use PHP or some kind of scripting language.
    • + Free
    • - PHP!!!
    • - Not really meant to be a game server
  • Rent a server
    • + Use your favourite language
    • + Uptime is usually guaranteed
    • - Money
  • Cloud / Google App Engine (Did not consider Amazon or any other possibility)
    • + No need to use PHP, you can use Python (or Java or Go)
    • + Have free plans
    • + Scalable
    • - Very complexity, so features, wow, much buzzwords
There are other solutions, however this area is so new to me I wanted to cry from all the informations. These are the ones I still can remember. I chose Google app engine, since users praised it. I have downloaded Python 2.7, the Google App SDK, and created a simple web server locally, that replied "Hello world" to any incoming connection. Then I went to Unity, and added this little chunk of code:

void StartMultiplayer() {
  string url = "localhost:8080";
  WWW www = new WWW(url);
  StartCoroutine(GetResponse(www));
}

IEnumerator GetResponse(WWW www) {
  yield return www;
  if (www.error == null) {
    Debug.Log ("WWW Ok: " + www.text.ToString());
  } else {
    Debug.Log ("WWW error: " + www.error.ToString());
  }
}

I have added a "Multiplayer" button to the GUI, and tested it. It worked. (It took only 3 hours until I found this code snippet on the net and things finally started to work. I don't claim I understand what is happening, though.)

As I understood, I can store data in SQL table, so persistence is no problem. Lot of people on forums said that HTTP requests will be enough for turn based games (HTTP requests usually take about 1-2 seconds), so I'm optimistic about it.