Objective: Mixed Reality CCG: Collect cards by scanning QR codes
Engine / Languages: Unity / C#, XML
Team Size: 6
Duration: ~18 work days
Q® (“QR”) is a Collectible Card Game / Outdoor Card Hunting Experience for android. It features an original card game with a corporate theme, faction VS faction metagame, and physical-space card hunts. You are recruited by one of the eight corporations as an area manager to increase your corporation’s profits and public appearance. Win card games against players from other corporations to increase your corporation’s power. Team up with other players of your faction to find powerful cards to add to your decks, and become the megacorp to rule the world.
Q® was the third Semester Project I did in the CGL (Nov 30th 2016 – Feb 2nd 2017). I worked with Andreas Gefken, Caterina Böhm, Matthias Siegel, Owen Franke, and Sarah Engelhardt on this.
I made the Unity Prototype except for the QR-Scanning and collecting new cards. This included the entire card-game mechanics and deck-builder, as well as the menus. I also acted as the project leader for the second half of the project.
You can download the android apk, a PC and Mac build HERE. You can read about the development process HERE.
The final stretch, there were nine days until the presentation, and another day until the playtesting day, so I had to hurry and get everything to work! I had never in my life spent so much time working on something, I must have accumulated about 120 work hours in those days, in addition to the presentation itself, and discussions with teammates. I don’t think calling it a death march would be an overstatement.
I knew that death marches can happen in studios even if managers try to avoid it, so I’m actually glad I had this experience. I would like to never experience this again though, so I absolutely plan on starting on a prototype sooner from now on! To be honest, I did still enjoy it though, I wrote code, fought with UI, made sure the Art assets were all properly aligned to fit the game and it was a real blast.
I also had to keep working on prototype after the presentation, since I still had to implement the deckbuilder and staff cards, as well as all of the card effects…
I managed to implement the first two, and got started on the card effects, but I could not stay awake any longer and only managed to finish about 7 card effects. Implementing the deckbuilder however was well worth it, it felt really good to look at your card collection, and being able to change the cards in your deck!
GUI and Game Mechanics
I had finally started to work on the deckbuilder and the help overlay in the main menu. With the experience I gained with the UI Canvases from the game scene, it took me about 5 hours to implement both of them, although the deckbuilder only visually. I spent another 2-3 hours on the functionality on the deckbuilder later.
I talked with Andreas and Owen about implementation of the card collecting, and we got to the conclusion that implementing it online, with the website and user logins, would not be feasible in the remaining time. Instead we went with Emergency contingency plan Beta23.05f that we would scan codes with our in-app scanner, and save the collected cards locally on the device, through player prefs.
I also had an intense discussion with matthias about our core game mechanic, profit = cashflow or profit = accumulation of cashflow (check this post for more info). I felt that the game at its current state simply wasn’t fun enough, so Matthias and I discussed whether it was still possible time-wise to change it, and whether it would improve fun!
Matthias slept over it and weighed the decision as well, and eventually agreed that it would probably improve the game to change, so we pitched it to the rest of the group, and they agreed.
I realized that people sometimes have very wrong ideas of how long it would take to make something. People suggest a mechanic or idea, with the intention to save you work, but not realizing that another (possibly even better) option takes just as long, or even less time to program!
Things like this were why I had decided to be extremely blunt about suggestions. Especially with artists, because I honestly didn’t have much of an idea about their work process. I would also try to present all options I could think of, with an added explanation of what I would personally prefer for Game Design reasons, and what would be less work for me to implement.
I sometimes felt that this was not received well but in this group, I had the feeling that the others appreciated it!
Assets and Production
I made a new Dropbox folder for Final Art Assets and a google doc folder for playtesting, documents for known bugs and expected bugs, with color coding for status.
I also made a final “Template/Example” XML file for Matthias, so he knew exactly to write in the XML file.
I once again realized that I often find problems, or something no one has thought of before, while working on the protoype, and wonder it was my responsibility to think of that beforehand… I try to plan a lot before I start working, but I’ve found that it doesn’t really help me so much, but maybe I would have to plan even more?
I had to update two of the Testing-Android phones for them to work with the QR scanner Owen implemented. The oldest one took about 2.5 hours to update, with at least 6 reboots!!
I realized how important it is to make a prototype ASAP, to make a build and to make EVERYONE test it, so everyone is on the same page about how the game works! In this case, someone had a different idea of how the menu structure worked. Luckily it wasn’t problematic, but it could have been!
Sometimes during coding I have to concentrate a lot, and need to turn off my music, lest it distract me from thinking. That was the case while I was implementing the multiplayer aspects, because it was new and unfamiliar.
When I was finally back on familiar territory, working on gamelogic, I could finally put on headphones, turn on noisy music and really crank it up!
Writing bad code was… liberating. I always build some errorcatching functionality, to check for human (my) error, but while I had so little time left, and the goal was only to make it work somehow, I learned a lot of new things!
And it was relaxing somehow, not having to think of how a certain case would be handled. If the player does something totally unexpected, the game crashes, or some undefined behaviour happens, I didn’t know, but it didn’t matter because it was only a prototype!
Also, here is a quick guide on how to finish a game for a tight deadline:
We held a Mini Gamejam at my house on the 29th featuring Owen, Andreas and myself. Unfortunately didn’t get a lot done on my end because there were a lot of uncertainties about how we would implement the mixed reality and how we would compile it, in order to present it at CGL! I thought the time was worth it though, also to make sure the QR scanner is working as it should! After Andreas left and Owen crashed at my place because he missed the last train, I could implement the discard phase from start to finish, which took up only around an hour! After that I went back into the matchmaking scene and made that a little better but annoyingly, the final button the player had to press was still too small…
This was a long week (10 days apparently!). On our weekly Monday meeting, Jan. 16, we decided on a milestone for me. Until the next meeting, I would make the game “playable”. That meant the multiplayer synching had to work. I needed to implement UI, and update it. Playing cards should only be possible if they can legally be played. Turn order had to be implemented, and the different phases of the turn be programmed. Cards you play should have an impact on the player’s resources.
I was confident to reach all of these goals. Maybe I was too confident.
I was already neck-deep into the multiplayer aspect before the meeting, and since the game is pointless without it, I kept working at it before anything else. At this point let me explain the term Equivision.
In our game, akin to the way Hearthstone did it, each player sees the game from the same camera.
The player is at the bottom of the screen, the opponent at the top. On one hand, the players see something different – they don’t look at the same table from opposing ends like you would in a physical game. You can see this fact in the shape of the board, it is not symmetrical. On the other hand, the players have the same point of view. There is one camera in the unity scene and both players are looking through the camera. However they see their own resources and Board-Cards on their side of the field and the opponent’s on their side. This is what I refer to as Equivision.
Part of the Equivision aspect -code wise- is that I refer to the two players as “host” or “guest”. The cardgame is always one-on-one so I can make use of that. Every action that a player takes tells the server the either “host” or “guest” as an argument, and the game handles it based on that.
Implementing Equivision, along with the other multiplayer aspects, took a lot of time from me. I had never made a multiplayer game before, not even a local one, so I came into this project with a lot of things to learn. I definitely underestimated this aspect of the programming!
To best explain how I implemented Equivision, and other multiplayer aspects, I should explain the main components of it. The most important Scripts for this are ServerAuthority, ClientController and PlayerController as well as ResourceContainer.
ServerAuthority is the script that handles the turn-order. In its Update function, it rotates through the two players’ phases and sets relevant variables. It is server-only and therefore does not exist on the client, which is why I needed this many scripts to communicate between the hosting and the remote client. Surely there had to be a better way to solve this, but I had already given up on writing pretty code. The ServerAuthority script does hold each player’s deck, but it does not hold their resources (Profit&Budget&Public Opinion), nor does it hold the “currentPhase” variable. The reason for this is the aforementioned, that the guest client cannot access the ServerAuthority script to ask for the values. These are instead held by the ResourceContainer.
ResourceContainer is exactly what the name implies – a container. It contains only variables and Set&Get methods, it does not execute any actions on its own.
ClientController contains all of the [ClientRpc] functions. ClientRpc functions are called from the host client and executed on both clients. I use this for everything part of the Equivision. In fact only for that. It updates the UI to display the current resources, it assigns newly drawn cards to the players’ hands, and it places cards on the board on the correct positions.
PlayerController is attached to the Player Prefab. It is therefore set to local player authority, which means that only the person who owns this player can call functions in this script. PlayerController contains all [Command] functions. Command functions are the counterpart to the ClientRpc functions, they are called by a client, and executed only on the host client. The Script handles most of the gamelogic, those parts which require the player to take action. It contains the functions that check whether a card can be played, and resolves playing them. It also contains the function that reacts to the player pressing the “next phase” button.
At the same time, I was working on the UI. I plonked the mockup assets I took from Andreas’ files on a Canvas and set it to scale with screensize. There is really not much else to say about it, but I didn’t have the final assets yet, so I had to come back to it later anyways.
The worst part of week 7 was attempting to make an automatic matchmaking scene. The Unity-provided UI for this were absolutely disgusting OnGUI generated buttons and I wanted the matchmaking to happen automatically anyways, instead of asking the player to click through a bunch of menus. Here is an impression of the work I put into it.
And after a total of around 10 hours, I had no working result. I hoped to get back to it in the next week, but I doubted I could spend even more time on it. After around 9 hours I was at the point where it worked as I wanted, but only if the hosting client is the Unity editor. I decided to stop trying to fix it about an hour later, and re-implemented the ugly Unity UI.
The worst thing about it is that the UI is pixelsize, and does not scale with screensize. This means that on a modern phone, you literally could not tap the buttons without equipping finger hands.
Funny Programming Fails Number 2 will blow your mind!!
This is how tired me thinks encapsulation works:
I DO have a tendency for long names:
This is how I comment my code after a long session:
When I try to code, IF is written in capitals:
Ups and downs
While fiddling with the “Check if you can play the card” implementation I finally learned how to use delegates! I had seen them before, and they appeared to be extremely useful, but they also seemed very complicated at the time. However thanks to this video, I understood them within 10 minutes. And my mind was blown. Thankfully, someone re-enacted what was happening during that moment, so I can share it with you here.
But then, disaster struck. Delegates could not be used with networking Commands or ClientRpcs.
Ultimately, I had to funnel it through 4 different scripts (sound familiar) instead. But in the end it worked out, so all was well!
All of the multiplayer stuff I needed was done. The UI was rigged up and synched. Cards could only be played if there were free slots for them, and if the player had the resources, and the resources were affected by the cards. All I had left to do was implement the turn order. It was already past midnight of monday, my deadline, and I my code started to look bad.
I wrote code for about 90 minutes straight without compiling and testing inbetween and in what seems like nothing short of a miracle to me, the code worked as intended, instantly! (I forgot to assign a reference but that doesn’t count!)
I made builds for PC and Android and loaded them on the testing devices and checked if they worked. Then I collapsed into bed – tired, but satisfied.
I had reached all of my critical goals, and did some extra bits, but unfortunately not as much as I had hoped to, due to the matchmaking fiasko.
The fruits of my hard work were the reactions of my team members. They had fun playing around with the bugs I had left in, enough that we made one of them a feature! During the meeting I found that there was yet a bit more for me to do than I was aware of before, but I was still confident that I could implement a lot of features. The main workload now was implementing each and every card effect, and I hoped I could implement at least most of them.
Now that I started really working on the App, it was time to look into how I could implement multiplayer. On Sunday I started researching different ways of how I could do multiplayer in Unity. The best options were Photon and Unity’s own networking services. I eventually decided to use Unity’s service, because it seemed easy to implement and Unity provided a matchmaking service for up to 20 concurrent users, which would be enough for our prototype phase. I spent the rest of the day working with the Unity tutorial on Networking. Annoyingly, the tutorial didn’t go as far as I needed it to. I didn’t explain the Unity Services part of multiplayer networking, which is necessary to take the game from LAN-only to internet multiplayer. So I had to play around with that for a while to get it work, and bother some of my friends to test if I could match them until I could get it to work. I met with Markus the day after to ask for his opinion on my approach, and he didn’t have any objections, so I went ahead with it. Also I tested the tutorial game on android, to verify that networking works the same, as Unity claimed.
Motivating myself was always rather difficult for me at the start of a new project, and there was really not trick to it. I had been training my willpower, and slowly but surely I could motivate myself better.
After doing a lot of research and setting up some of the basics for multiplayer, I started implementing bits of the interactive parts of the game. Until the end of the week I made responsible scripts and classes for drawing cards from the deck and adding them to the hand, dragging cards from the hand to play them, placing structures on the board and some more.
Our intermediate presentation was on January 11, so on our weekly meeting on Monday 9, we started working on the presentation. We outlined what we wanted to talk about in a googledoc presentation file. Sarah then took that and made the slides in some other program to make them look good. Everyone told her what they wanted on their slides, but I really had no idea what to talk about, however Sarah solved that very gracefully:
The presentation went very well in my opinion, the Profs commented on some things like the huge scope we were planning for, but we had a good answer for everything (we had already cut down the scope a huge amount for the prototype).
I started this week by looking at the game files for the game that is our main inspiration – Hearthstone. The only relevant files that were not encrypted were the XML files. I had hoped to find the main files for cards, but it seemed as if those were either not saved as XML, or split between so many files that I could not puzzle them together. I did however find out that they don’t have different elements for the different properties of the cards, but instead differ between them with different values for the “field” attribute.
… Monkey do.
I thought about changing my XML to the way Hearthstone did it, but decided against it, because i saw no reason to do it. I did however spend some more time on changing the names of the attributes of each card, to better reflect, and more clearly explain their function.
I got the core XML functionalities running to the point where I could load cards from the XML document and display the values on a card-mockup.
The importance of examples
During our meeting on Monday we found out that we had different concepts of one of our Resources, “Profit”. Some of us understood it as an accumulation of the player’s income, and the others as cash-flow. This explained why we had such different ideas of how it would be used. After I explained to the others how I imagined Profit as cashflow to work, we decided on using that.
I learned that whenever you talk about a game mechanic like this, you should make some kind of example, perhaps do a few turns of mock-play to see if everyone is on the same page.
For this project I decided to write every class in their own Script, like Card and CardSlot for clarity and neatness.
During programming I often noticed things that we hadn’t talked about before. I was wondering whether this was fine, or whether we should have planned ahead more. One of these things was showing buffs or debuffs on certain cards, for example if you were to use an event card that increases profit of a structure, that change has to be somehow shown on the card.
I also get to make small gameplay decisions all the time.
For example, I decided that the chance for a card that is randomly inserted into a deck would be a tiny bit (like 0.000001%) higher to be inserted at the top than anywhere else. This is due to the way probabilities are calculated in programming. I could have chosen any location to be this tiny bit higher, but the top position was the most fun imo.
One of my favourite things in programming are named and optional attributes. They allow you to keep method calls short if you do not need all parameters.
The Week following Christmas was – expectedly – accompanied with little progress on the game. We had agreed not to meet that week, as we were all going to be busy celebrating Christmas with our families. We did however decide to hold our skype call on the 29th. We had planned to revise the GDD Matthias had written to finally nail down a lot of the rules to the card game and to get ahead on the mixed reality aspect as well. However some of us didn’t make it to the call, partly without any excuse or prior notice so we couldn’t get as far as we had planned. We discussed the topics at hand among those of us who were present, and decided that we would absolutely have to decide on the rules on Monday, the next meeting. We would build a Pen&Paper Prototype, and start playing the game, and get a feel of it and see if it works at all like we thought, and to test around numbers and limitations.
During our Thursday SkypeCall, we realized that we had differing views on how the mixed-reality aspect of the game would work, and what kind of scale it would have. We discussed it a lot, and came out with something everybody seemed to like. I still wasn’t sure whether we were all on the same page now, but Andreas would flesh out his different approaches of the mixed-reality game, and we would see how it worked out then.
Preparation and Programming
I had started the week before already, but that week I focused even more on preparing for programming the app. I wrote down a list of what the app needs and in more detail, what the actual game would need to work.
I wrote down a lot of thoughts and approaches, ideas on how I would code the game. Mainly that I would use a single class “Card” which would exist from the beginning of the game until its end. Looking back, this was quite an obvious choice, but I had thought of making different objects for the card depending on their state; in the deck, in your hand, on the field, or destroyed.
I also decided on using XML again, to store the standard values for every card we had (“template”). When the game started, the program would read through the xml file and instantiate the cards in each players’ decks. I took the XML code I had written for The Toddler Connection, and modified it to fit our current needs. I wasn’t entirely sure what values every card would need yet, but I could adjust that later.
My main challenge were the special effects, and how the game would handle them. Since we still hadn’t finalized the game mechanics, and we had no finished cards, I couldn’t really start working on it yet, but I thought I could start with the most simple effects like drawing a new card, or modifying current values on a card.
After the pitches on St Nicholas Day we decided to give everyone a role in order to facilitate the decision-making progress and general workflow. We had named Owen as project leader before the pitch, and he was now also in charge of the narrative. Matthias was put in charge of the card game mechanics, and Andreas would be most concerned with the meta-game and the mixed-reality aspect. Sarah was appointed Lead artist and in charge of logos, and I took the position of lead programmer.
We decided that the one in charge will have the final say on a decision concerning their field, but everyone could always make suggestions and give criticism to anyone.
We decided to meet in person every monday, and over skype every thursday.
We had been using Google docs already and they had proven effective, so we decided to keep any written text on those, so we had some nice and dynamic documents. We used whatsapp as main communication channel, and dropbox as space for larger files. We also decided to use Slack as a sort of hub to connect everything, but at the time, it hadn’t been used much. We used skype for the Thursday calls, but not otherwise.
Developing the core mechanics for the card game had proven challenging. Given that we needed to nail those down ASAP, we had spent most of our efforts that week coming up with ideas, and then revising and revising those. We hadn’t quite finalized them, but we had a strong bunch of foundations, and just needed to shave off some of the rough edges.
The narrative had been developed alongside the mechanics. We’ve had the 8 factions from the start, and wanted to stick with them. We had decided that we could not possibly finish all the necessary cards for the 8 factions until February, so we chose three factions that were different enough to show the possibilities the game would offer.
The meta-game had changed as well. We had found some flaws and found ways to make them into mechanics, but we still needed more work on that.
On the day of the Kickoff meeting I had a few ideas about what I would like to do and wouldn’t like to do. I knew I didn’t want to do VR like in the previous semester, and I was interested in creating a board game, perhaps with QR codes.
After the kick-off meeting I went to the open space and started bouncing ideas around with the others who were there. Owen and I started talking about posting masses of recognizable QR codes around the city, so many that people would become interested and just scan them out of curiosity. The idea was that there would be this corporation whose only product is its logo which they post everywhere.
We met again on Friday, along with Andreas and a few other people, to bounce ideas around some more. We had the idea of making a card game out of it, where you need to go outside and scan QR codes in order to earn new cards. The card game would feature a corporate theme, to cover the ideas we had before.
After the brainstorm meeting at CGL Owen and I spent the weekend thinking about different aspects of the game, I was mostly concerned with game mechanics and the meta-game of collecting QR codes, as well as legal consequences of posting stickers on public places. I also spent a lot of time researching the stock market, to see what kind of game mechanics we could make out of it.
Adding Team Members
During the weekend Andreas decided to join us. I was very happy to get Andreas on the team, as I had wanted to work with him before, and he seemed very interested in the Mixed Reality aspect, which I didn’t want to concern myself with too much.
We met on Monday to polish the ideas we had and start working on the pitch.
During our pitch Sarah joined, and afterwards Matthias joined as well, which made us a pretty strong team. Owen and I had thought about asking Sarah to join anyways, as we were both impressed with her previous work, so it was great that she joined out of her own motivation. Matthias has a lot of experience with Card games and was excited to create one from scratch, so he also was a much needed addition to the team!
Later that day Caterina, who wasn’t there for the pitches, asked to join the team. After some discussion we invited her on the next day, as we realised how much artwork will need to be done for the cards, and the app, and the corporate logos etcetera. From what I could tell, Caterina’s artstyle was quite similar to Sarah’s, so they should be able to work together well!