16. Generating Stage Unlock Values

What’s new this week?

Yay! I noted my scheduled DevBlog WordPress post worked a charm! Great to see. Quite a handy tool which I will definitely utilise in the future.

So anyway, before I could work on my next big task, I needed to make some additional code alterations regarding the Lobber Target. I previously overlooked the need to include the Lobbers into my Targets Spawned calculations. I realised that I had not yet added Lobbers into the Level Complete Score, so this oversight was a necessary fix before other tasks like Generating Stage Unlock Values.

When a player hits a Lobber they earn a score, but hitting a projectile does not. The rationale is, if the player is fast, their goal should be to prevent the Lobber projectile from firing, and if they are unable to prevent the firing of the projectile, then a consequence is that they are required to hit an additional target, the projectile. Just FYI.

So, I started working on generating values for my Score Unlock function. This function gathers information about the stage, then estimates the value the player needed to earn to unlock the next stage. The value was generated by a conditional equation, derived from targets spawned, including those all important Lobber targets.

My “Generating Stage Unlock Values” function

I got the script to a point, where the values were generating as required, but I noticed it needed to allow for higher target values, Ie: when the targets missed were also the big scoring targets. So I added some fairness to allow for player error, so a player could miss a couple of high scoring targets and still have relative success on unlocking stages. It was a delicate balance. So currently I am pretty happy with the current state of the Stage Unlock values, spending some time testing and tweaking the Stage Unlock Percentage values for balancing the difficulty of each stage. I can always enable an Override setting, to change these balanced values to smaller values, for debugging, and disable the Override to switch back to the balanced game settings.

Below is the updated GamePlay Progression Matrix, with my current Stage Unlock Percentages.

Updated Stage Unlock Percentages in GamePlay Progression Matrix

Next, I fixed a bug found during playtesting, noting that the Lobber Targets were not consistently registering a hit when shot. Investigating this issue, I discovered the Flash plane, which emulates the turret’s muzzle flash, had an unnecessary collider component attached, so my raycast was intermittently contacting this plane, instead of my target’s collider. This was an easy fix, removing the collider component in the prefab, and a quick playtest to confirm it works as designed. Flawless Victory!

Another bug I noticed during playtesting was an inconsistent scaling of a score tweening animation. This was a familiar issue, that I was pretty sure related to LeanTween, when a curve controlling the animation, went below zero on the Y axis (Ie: Y < 0). I tweaked font size and edited the curve to get the same visual effect, without drawing the curve beneath the zero value. With Y always > 0, problem was solved immediately.

Below is a better view of the code I used to generate the Score Unlock Score. I left all of the Debug.Log() code in there, as it comes in handy, not just for creating the function, but for debugging purposes later.

public void GeneratingStageUnlockValues() 
    {
        if (!UseStageUnlockOverride) //if NOT Overriding Generated Data.
        {
            //Gathering TargetValues 
            int targetRegularValue = target_Regular;
            Debug.Log("TARGET REGULAR VALUE = " + targetRegularValue);
            int targetBonusValue = target_Bonus;
            Debug.Log("TARGET BONUS VALUE = " + targetBonusValue);
            int targetLobberValue = target_Lobber;
            Debug.Log("TARGET Lobber VALUE = " + targetLobberValue);
            int targetBossValue = target_Boss;
            Debug.Log("TARGET BOSS VALUE = " + targetBossValue);

            //Currently using 3 Speeds, which require 3 different Target 'Pop' Values (number of instantiations per stage)
            //Ie: Smaller the Idle time, the more targets instantiated
            if (NumberOfGameStages[stageIndex].target_idle == 1.1f)
            {
                debugPopValue = 34;
            }
            else if (NumberOfGameStages[stageIndex].target_idle == 1.0f)
            {
                debugPopValue = 37;
            }
            else if (NumberOfGameStages[stageIndex].target_idle == 0.9f)
            {
                debugPopValue = 41;
            }
            else
            {
                Debug.LogWarning("TargetIdle is not set to ANY of the preset speeds. Check StageMmanager for recognised speed settings");
            }

            //initialising values
            numRegRemaining = debugPopValue;
            totalScorePossible = 0;

            //Note: How many Targets have popped/instantiations.
            //This is a MEAN value, derived through multiple play sessions, hardcoded above.
            Debug.Log("DEBUG POP VALUE = " + debugPopValue);

            //Determine "Number of Multiple Targets" on Target2 array (ie These are always Regular Targets) 
            if (NumberOfGameStages[stageIndex].spawnType == SpawnType.Multiple)
            {
                //How many Multiples are ratio-d?
                int spRatio = NumberOfGameStages[stageIndex].spawnRatio;
                Debug.Log("SPAWN RATIO = " + spRatio);

                //Get the Number of Multiples
                int numMults = debugPopValue / spRatio;
                Debug.Log("Number of Regular Target MULTIPLES = " + numMults);

                //What is the Value of these MULTIPLES? (ie: regular target value x number of multiples)
                int valueMultRatio = targetRegularValue * numMults;

                //ADD Value to TOTAL SCORE POSSIBLE.
                totalScorePossible = totalScorePossible + valueMultRatio;
                Debug.Log("TOTAL SCORE POSSIBLE = " + totalScorePossible.ToString());

                //These MULTIPLES Add to score, but do not effect NumRegTargetsRemaining, as they are calculating MULTIPLES in 2nd array, and
                //while NumRegTargetsRemaining calculates the 1st array data. 
                Debug.Log("NumRegTargetsRemaining = " + numRegRemaining.ToString());
            }

            //Determine "Number of BONUS Targets"
            if (NumberOfGameStages[stageIndex].bonus)
            {
                //How many Bonus's are ratio'd? 
                int boRatio = NumberOfGameStages[stageIndex].bonusRatio;
                Debug.Log("BONUS RATIO = " + boRatio);

                //Get the Number of Bonus Targets
                int numBonuses = debugPopValue / boRatio;
                Debug.Log("DEBUG NUMBONUSES = " + numBonuses);

                //What is the Value of these BONUSES? (ie: bonus target value x number of bonuses)
                int valueMultRatio = targetBonusValue * numBonuses;
                Debug.Log("INITIAL BONUS VALUE = " + valueMultRatio);

                //Allow Player to miss at least half the bonuses, so divide the value by 2
                valueMultRatio = valueMultRatio / 2;
                Debug.Log("AMMENDED BONUS VALUE DIVIDED BY 2 = " + valueMultRatio);

                //ADD Value to TOTAL SCORE POSSIBLE.
                totalScorePossible = totalScorePossible + valueMultRatio;
                Debug.Log("TOTAL SCORE POSSIBLE = " + totalScorePossible.ToString());

                //DEDUCT number of Bonus Targets from Number of Regular targets remaining 
                numRegRemaining = numRegRemaining - numBonuses;
                Debug.Log("NumRegTargetsRemaining = " + numRegRemaining.ToString());
            }

            //Determine "Number of AVOID Targets"
            if (NumberOfGameStages[stageIndex].avoid)
            {
                //How many avoids are ratio'd?
                int avRatio = NumberOfGameStages[stageIndex].avoidRatio;
                Debug.Log("AVOID RATIO = " + avRatio);

                //Get the Number of Avoid Targets
                int numAvoids = debugPopValue / avRatio;
                Debug.Log("DEBUG NUMAVOIDS = " + numAvoids);

                //DEDUCT number of Avoid Targets from Number of Regular targets remaining 
                numRegRemaining = numRegRemaining - numAvoids;
                Debug.Log("NumRegTargetsRemaining = " + numRegRemaining.ToString());
            }

            //Determine "Number of LOBBER Targets"
            if (NumberOfGameStages[stageIndex].lobber)
            {
                //How many Lobbers are ratio'd?
                int loRatio = NumberOfGameStages[stageIndex].lobberRatio;
                Debug.Log("LOBBER RATIO = " + loRatio);

                //Get the Number of Lobber Targets
                int numLobbers = debugPopValue / loRatio;
                Debug.Log("DEBUG NUMLOBBERS = " + numLobbers);

                //What is the Value of these LOBBERS? (ie: lobber target value x number of lobbers)
                int valueMultRatio = targetLobberValue * numLobbers;

                //ADD Value to TOTAL SCORE POSSIBLE.
                totalScorePossible = totalScorePossible + valueMultRatio;
                Debug.Log("TOTAL SCORE POSSIBLE = " + totalScorePossible.ToString());

                //DEDUCT number of Lobber Targets from Number of Regular targets remaining 
                numRegRemaining = numRegRemaining - numLobbers;
                Debug.Log("NumRegTargetsRemaining = " + numRegRemaining.ToString());
            }

            //Determine "Number of BOSS Targets"
            if (NumberOfGameStages[stageIndex].boss)
            {
                //How many Bosses are ratio'd?
                int bossesRatio = NumberOfGameStages[stageIndex].bossRatio;
                Debug.Log("BOSS RATIO = " + bossesRatio);

                //Get the Number of Boss Targets
                int numBosses = debugPopValue / bossesRatio;
                Debug.Log("DEBUG NUMBOSSES = " + numBosses);

                //What is the Value of these BOSSES? (ie: boss target value x number of bosses)
                int valueMultRatio = targetBossValue * numBosses;

                //Allow Player to miss at least half the Bosses, so divide the value by 2
                valueMultRatio = valueMultRatio / 2;
                Debug.Log("AMMENDED BOSS VALUE DIVIDED BY 2 = " + valueMultRatio);

                //ADD Value to TOTAL SCORE POSSIBLE.
                totalScorePossible = totalScorePossible + valueMultRatio;
                Debug.Log("TOTAL SCORE POSSIBLE = " + totalScorePossible.ToString());

                //DEDUCT number of Boss Targets from Number of Regular targets remaining 
                numRegRemaining = numRegRemaining - numBosses;
                Debug.Log("NumRegTargetsRemaining = " + numRegRemaining.ToString());
            }

            //Determine Value of Regular Targets Remaining, after special targets are deducted.
            int valueMultNumberOfRegTargetsRemaining = targetRegularValue * numRegRemaining;
            Debug.Log("VALUE of NumRegTargetsRemaining = " + valueMultNumberOfRegTargetsRemaining.ToString());

            //ADDING this value to Total Score Possible, to derive a MAXIMUM Total ScorePossible,
            //with ALL Scoring targets considered (Ie: Multiple, Bonus, Lobber, Boss and Regular targets).
            totalScorePossible = totalScorePossible + valueMultNumberOfRegTargetsRemaining;
            Debug.Log("MAXIMUM TOTAL SCORE POSSIBLE = " + totalScorePossible.ToString());

            // TODO: Now let's Get percentage of the Total Score - thie is the Stage Unlock Score
            Debug.Log("STAGE UNLOCK PERCENTAGE = " + NumberOfGameStages[stageIndex].stageUnlockPercentage.ToString() + "%");

            //GET % of totalScorePossible, convert to a decimal (divide by 100)
            float percentage = NumberOfGameStages[stageIndex].stageUnlockPercentage / 100;
            Debug.Log("PERCENTAGE as a decimal = " + percentage.ToString());

            //Get Percentage of Total Score Possible
            float percentageValueAsFloat = totalScorePossible * percentage;
            //Cast float as Int
            int percentageValueAsInt = (int)percentageValueAsFloat;
            Debug.Log("PERCENTAGE VALUE as an Integer = " + percentageValueAsInt.ToString());

            //Round value to nearest 100
            //convert value to a double
            double dValue = (double)percentageValueAsInt;
            Debug.Log("DOUBLE VALUE = " + dValue.ToString());

            //round value and convert double to int
            double roundedValue = Math.Round(dValue / 100d, 0, MidpointRounding.AwayFromZero) * 100d;
            int roundedInt = (int)roundedValue;
            Debug.Log("Rounded VALUE = " + roundedInt.ToString());

            //FINALLY Save Percentage Value to Stage Unlock Value.
            NumberOfGameStages[stageIndex].stageUnlockScore = roundedInt;
            Debug.Log("STAGE UNLOCK SCORE = " + NumberOfGameStages[stageIndex].stageUnlockScore.ToString());
        }
        else
        {
            Debug.LogWarning("Stage Unlock Score Override is CURRENTLY set. Deselect in Inspector, on StageManager");
        }
    }