Keyboard Building Simulator – Protoype Done!

Keyboard Building Simulator is a college project which was submitted for my year one FMP. It is a relatively simple game based around the assembly of Custom Mechanical Keyboards for clients based off of their briefs and was heavily inspired by the game Pc Building Simulator. This project was extremely technically challenging and featured various new concepts to me such as a binary save system, some of the features implemented are explained in this blog post.

I have built multiple Custom Mechanical Keyboards myself which can be read about on my other blog post here! https://alfiedev.co.uk/custom-keyboard-build/

Save System

Firstly I had to come up with an idea and how to save all the player data in Keyboard Building Simulator. Initially I looked at PlayerPrefs and Json files however decided against these due to their ease of editing by the player. This led me to making a binary save system. This system was based on the youtube tutorial by Brackeys linked below, and then modified to work with my needs. The main struggle with using this system was working out what I will actually need to save and their data types. 

Data Encoding

My solution to this issue was to use a sort of encoding, allowing me to store large values as 6 character strings. This system lets me easily save arrays for basically any information within Keyboard Building Simulator, excluding things like money and experience which will just be integers. Throughout the entire process of writing this save system I was focused on how I will save large amounts of information such as the components in a keyboard build. My methods on how to do this were thought up in the comments of my playerData file and shown below in figure 1.

JavaScript
    //build save system
    //every component is stored as an int between 12 and 99, with 11 meaning no component
    //there will then be an array with 3 entrys, this is for each placed keyboard case in the scene, each case is considered a keyboard
    //there can be a max of 2 placed at both the final station and at a workstation, there can simeltaneously be 1 in storage
    //each one of these will be saved as a long number of the component ids, so 261143 could mean case 26, no plate, and pcb 43

Figure 1-

Here you can see my initial ideas for how things will be saved, the components will all be stored as ints which will then be joined together to make up the properties of a keyboard build. This will likely be refined upon later when I come to the building system.

Experience And Levelling

Next I had to make the experience and money system for my game, this was very easy thanks to the save system I had just made, allowing me to do both of these things extremely simply, this code is shown in figure 2.

JavaScript
    public void SetMoney(int Tmoney)
    {
        money += Tmoney;
        Debug.Log(money);
    }

    public void AddExperience(int Texperience)
    {
        //each level is 1000exp
        experience += Texperience;
        Debug.Log(experience);

        //level calculation
        level = experience/1000;
        Debug.Log(level);
    }

Figure 2-

Here you can see the code used for my levelling and experience system, it simply adds the requested amount of experience and divides it by 1000 with no remainders to get the level number. I think this is the most efficient way of doing this for now as it uses very little memory and processing power to run.

Calendar Logic

First I had to come up with the actual logic for the calendar within Keyboard Building Simulator, this alone took nearly the entire week. The main issue is this isn't something you can just google how to do as it's so specific. This meant I had to take an entirely new outlook with coming up with whole new ideas and coming up with the logic myself. This was by far the most difficult and time consuming thing I've done yet with me spending about 15 hours this week just trying to figure out the logic of how to actually make this.

After multiple failed attempts at coming up with something I had the idea to make a sort of flow chart and use a kind of encoding system with my calendar. In theory this should let me actually have a working calendar. This flowchart is shown below in figure 3.

Figure 3-

This flowchart shows my planning split into sections along with the planning for data encoding

Flowchart Planning

This method of planning was immensely helpful allowing me to easily visualise the function of the calendar within Keyboard Building Simulator. I broke it down into simple sections of: Array logic where the calendar days would be saved, Data loading where the calendar arrays would be loaded and assigned to an array, Date checking which is where the calendar would check when things were meant to happen and whether or not they should have already happened, and finally displaying data where the calendar information would be decoded and put onto the calendar ui object.

Time Management

The complexity of this calendar system put me under immense time pressure forcing me to work late into the night and early hours of the morning to try and get it done without slowing down the rest of the project. Parts of the calendar were completed at around 2am putting large amounts of pressure on myself to have them done so that I could sleep. A large issue with working at times like this is that even though I worked well it would mean that the next day I would forget what I had done, leading to confusion and general chaos. Luckily I managed to push through this without much issue thanks to heavily commenting on all my code so that it was much easier for me and others to understand.

Email Logic

The email system was difficult to begin with, it appears logically simple at first with me just having to take an email, decode it, and make a button that loads it. However this was yet again much harder than anticipated. The main issues that I ran into while making this email system were centred around deleting and displaying emails, specifically deleting emails took me around 5 hours due to a simple mistake. The email code itself did not require a flow chart for planning at all due to its simplicity, it's essentially just a big array.

Email Displaying

Displaying emails in hindsight wasn't actually too bad, it just took a minute for me to understand. The main issue that I was having was being unsure how to edit the text on the email buttons, or how to let the game know what email was currently being displayed. The text issue was solved with the unity docs where I found that I could just change the text property of the text component. Letting the game know what email is open was imperative to the functionality of deleting and accepting emails and was done by assigning a variable to each email called its buttonNumber. Whatever the button number was is the position in the emails array that the current email is, this allows me to easily navigate through the miscEmails array to find the current one and get all the information needed for both displaying it and interacting with it.

https://docs.unity3d.com/2017.3/Documentation/ScriptReference/UI.Text-text.html

Delete Button

The main issue that I really struggled with for this entire system was the delete button thanks to me writing code whilst being very tired again. The emails for some reason wouldnt delete no matter what I did and I spent around 4 hours redoing the delete system and email update system, just to realise that I forgot to update the emails array after i’d deleted one, this was extremely frustrating as I had just wasted hours on such a menial task but there was nothing I could do and I was done with the week. The problematic code for this is shown in figure 4.

JavaScript
    public void DeleteEmail(int buttonNumber)
    {
        //for the email content section
        GameObject subjectContent = GameObject.Find("SubjectContent");
        GameObject senderContent = GameObject.Find("SenderContent");
        GameObject bodyContent = GameObject.Find("BodyContent");

        Player playerData = GameObject.Find("GameManagement").GetComponent<Player>();

        //not yet implemented
        //GameObject dueDate = GameObject.Find("DueContent");
        //GameObject cost = GameObject.Find("CostContent");
        subjectContent.GetComponent<TMPro.TextMeshProUGUI>().text = "";
        senderContent.GetComponent<TMPro.TextMeshProUGUI>().text = "";
        bodyContent.GetComponent<TMPro.TextMeshProUGUI>().text = "";

        playerData.inboxMisc[playerData.currentEmail] = "";


        // subjectContent.GetComponent<TMPro.TextMeshProUGUI>().text = Decoder.DecodeEmail(email, 1);
        // subjectContent.GetComponent<TMPro.TextMeshProUGUI>().text = Decoder.DecodeEmail(email, 1);

        UpdateEmails();
        playerData.SavePlayer();
    }

Figure 4-

The bottom line of the DeleteEmail function where it saves the player was originally missing. This meant that the arrays holding the current emails were never updated so the delete button didn't do anything.

Shop Logic

Finally I made the shop for Keyboard Building Simulator, yet again the logic was extremely simple for this so only a brief flowchart was required, however as always, there were some complications. The main issue I had was splitting the various components into categories along with the data system for these components. I decided I would use a similar system to that of the emails for storing component information so I added a new method to my Decoder script shown below in figure 5

JavaScript
    public static string DecodeComponent(string entry, int part)
    {
        //splitted aray of component strings
        string[] splittedComponent = {"", "", ""};
        if (entry != null)
        {
            //splits into parts. Type, Item
            splittedComponent = entry.Split('@');

            if(part == 1)
            {
                return splittedComponent[0];
            }
            else if(part == 2)
            {
                return splittedComponent[1];
            }
            else if(part == 3)
            {
                return splittedComponent[2];
            }
            else
            {
                return "tf is this";
            }
        }
        else
        {
            return "tf is this";
        }
    }

Figure 5-

The DecodeComponent method of my Decoder script. It takes two inputs, one is the full string of all the information, and the other is an int saying what section is needed.

Decoder

Using this decoder script I could find out the type of the item as this was stored in the first two bits of the string. The next two were the item id, and the final two are the price. This was something I added as an afterthought for the shop system when I remembered about items costing money. Thanks to this system the rest of the shop code was written without issue, all I had to do was compare the type, add it to an array, and make a button. The code for this is shown below in figure 6.

JavaScript
    //parse in the type, if its cases it will instantiate the case ones etc, parse as number
    public void CreateButtons(int type)
    {
        //clears buttons
        ResetButtons();

        //creates buttons
        //cases
        if(type == 0)
        {
            int buttonsAmnt = cases.Length;
            for(int i = 0; i < buttonsAmnt; i++)
            {
                //makes enough buttons for all cases in list
                GameObject tButton;
                tButton = Instantiate(buttonPrefab);
                //sets parent to the button list for scrolling
                tButton.transform.SetParent(itemGrid.transform);
                //sets the scale of the button to 1 because this was an issue for whatever reason that broke all the scaling of the list
                tButton.transform.localScale = new Vector3(1f,1f,1f);

                //changes text to placeholder text
                tButton.GetComponentInChildren<TMPro.TextMeshProUGUI>().text = GenerateText(cases[i]);
                //sets its buttonNumber int accordingly
                tButton.GetComponent<shopButtons>().buttonNumber = i;
            }
        }

Figure 6- The creation of buttons for the shop using items in the plates array.

Keyboard Building

Next I had to make the Custom Keyboard Building system. Initially I had been concerned about this but then I realised that a large amount of it would be identical to the shop. The main thing that had to be changed was removing items from the players inventory instead of adding them, and instantiating an object into the scene whenever you buy an item. The code for this creation of items is shown below in figure 7. I also made a flowchart so as to help myself understand the processes in building, this is also below in figure 8.

Unity docs:

https://docs.unity3d.com/ScriptReference/Object.Instantiate.html

JavaScript
    public void PlaceComponent(int buttonNum, int type)
    {
        LoadItems();

        if(type == 0)
        {
            string component = cases[buttonNum];
            GameObject tObject;
            currentBuild[0] = component;
            foreach(Transform child in casesPoint.transform)
            {
                Destroy(child.gameObject);                                 
            } 
            tObject = Instantiate(Resources.Load(component), casesPoint.transform.position, casesPoint.transform.rotation) as GameObject;
            tObject.transform.SetParent(casesPoint.transform);
            // tObject.transform.TransformPoint(0f,0f,0f);
            // tObject.transform.position = new Vector3(0f,0f,0f);
        }

Figure 7-

This is the code for instantiating the keyboard components into the scene so that the player can actually visualise the custom keyboard which they are creating.

Figure 8-

This is the flowchart for planning my build system with how it will be shown to the player, saved, and actually function. This also includes the saving format for ongoing keyboard builds.