Week 8: Prototyping Abilities in Unity / by Valzorra

Having crafted a basic system for the camera and movement of this prototype, I thought it was now time to introduce some of the fun abilities that would help players maneuver through the environment. At this stage, I had a very rough idea of what I wanted those abilities to be based on some initial idea generation in Project Proposal 4, As It Lies. I felt the strongest about Teleportation as it would give players mobility and flexibility and about Electrocution as it is a damage-dealing ability that would help players confront any threats. I would like to note that these have not yet been set in stone and could be amended or removed as the design and development process progresses. Additionally, I would also like to reiterate that the work described below has been a collaborative effort between James and myself and that this prototype would not be possible without his help and guidance.

Range Indication and Teleportation

The very first ability I wanted to try my hand at was the Teleportation Ability. The exact methodology of how this power would work has been described in detail in Project Proposal 4 in one of my storyboards within that post. However, to briefly summarize, once the player has selected that ability, they will be presented with an indicator of their range in the form of a circle around them. The player would then be able to click anywhere within the Range Circle to select a target location, and after they have made their selection, they would be instantly teleported there. There were a few problems to solve within this description, the first of which was to figure out how to best implement the Range Circle Indicator, which would be used for a series of other abilities as well. I knew what I was going for, which was ideally a large circle around the player, which would give them a clear indication of where their abilities stretch up to. Additionally, the Range Indicator needed to appear only as players are about to perform an Ability and had to disappear and reset as soon as the ability has been executed. That way, players would not have circles all over their screens without necessity. A terrific example of the type of indicator I wanted is constantly used throughout League of Legends as shown below.

League of Legends Range Indicator

For my own prototype, the Range Indicator was handled through the use of a very basic cylinder, scaled to X: 1, Y:0.0001, and Z: 1, making it as close to a two-dimensional circle as possible. That Cylinder was then placed straight on top of the Player, keeping them in the center. The Cylinder was then parented to the Player, which meant that as the Player moved, so would the Ranged Indicator. Additionally, in order to see the scene clearly, a Semi-Transparent Material was added to the Range Indicator with the transparency set to 50 out of 255. At this stage, I had a very simple environment and a Player character with a large semi-transparent circle on top of their head, meaning I was ready to actually make the Range Indicator work. The system for it functions in a similar but slightly more intricate manner to the way Player Movement is handled as described in Week 8: Prototyping the Camera and Movement in Unity.

This is how the Range Indicator appears in the prototype for Teleportation.

This is how the Range Indicator appears in the prototype for Teleportation.

The first step in the process was to indicate what the maximum range of Teleportation would actually be, which was determined by a simple float. For the purposes of this prototype, the value has been arbitrarily set to 15. The next step was to ensure that the Range Indicator would only be displayed when the Teleportation was actually used. As the Range Indicator would only really be needed if an ability has been activated, its Mesh Renderer was disabled in Unity for all other general purposes, making it invisible to the user. The only occasion on which the Mesh Renderer is enabled through code, is when the player presses down any keys associated with the activation of powers. For the purposes of this prototype, I have set it up so that pressing T activates Teleportation. The dice rolling mechanic will be implemented at a later point. Once the Mesh Renderer is enabled, the other important aspect of displaying the Range Indicator is ensuring that the Circle resizes itself according to the range of each ability as indicated by the associated variables. To do that, a line of code has been implemented, which states that if the Current Power is Teleportation, then the Range Indicator will be scaled in accordance with the maxTeleportRange variable. This is basically how the Range Indicator is handled within this prototype not only for Teleportation, but for all of the abilities that would need it. Therefore, the entire system for displaying the Range Indicator has been compacted in a switch statement as showcased below.

void ShowRangeOfPower() {
        switch(currentPower) {
            case "TELEPORT":
                //Enabling the Mesh Renderer
                rangeIndicator.GetComponent<MeshRenderer>().enabled = true;
                //Scaling the Range Indicator accoring to predetermined variables
                rangeIndicator.transform.localScale =
                new Vector3(maxTeleportRange, 0.01f, maxTeleportRange);
                break;
            case "ELECTROCUTE":
                rangeIndicator.GetComponent<MeshRenderer>().enabled = true;
                rangeIndicator.transform.localScale =
                new Vector3(maxElectrocuteRange, 0.01f, maxElectrocuteRange);
                break;
            //If no powers are selected, the Meah Renderer is Disabled
            default:
                rangeIndicator.GetComponent<MeshRenderer>().enabled = false;
                rangeIndicator.transform.localScale =
                new Vector3(1f, 0.01f, 1f);
                break;
        }

After the algorithm for the Range Indication had been sorted, it was time to code the Teleportation itself, which was a relatively straightforward process. The function works by casting a Ray from the Main Camera directly onto a location in the environment selected by the player. Just as with Walking, the Player can select a target location by pressing the Left Mouse Button. The function then checks whether the the target location is within the predetermined Teleportation Range and whether it is on a Walkable surface as indicated by our NavMesh. All floors within this testing environment have been marked as Walkable and if the player were to click anywhere else to move, such as on a building or outside of the environment, then the Player Character will be taken to the closest point to their selection. If the target location answers to both of those conditions, then the Player Game Object will be Warped to that point. The last bit of that function simply states that once the Teleportation is completed, then currentPower variable goes back to Null, thus resetting the whole process. The Teleportation function used in the prototype is displayed below.

 void Teleport() {
        RaycastHit h; 
        if(Physics.Raycast(Camera.main.ScreenPointToRay
                          (Input.mousePosition),out h, 100f)) {
            //If the Player Selects a location in Range and on a Walkable area
            if(Mathf.Abs((h.point - transform.position).magnitude)
              <= maxTeleportRange && h.collider.gameObject.layer == 9) {
                nAgent.Warp(h.point);
            }
        }
        currentPower = "NULL";
    }

That’s the basic premise of how both the Range Indicator and the Teleportation Ability work. The player can move freely within the environment and once the press T, a large circle will appear around them. If the player clicks on a surface within the environment that is also within the Range indication with the Left Mouse Button, then the player will be instantly teleported to that location. Currently there is no cool-down of any kind on the Teleportation, which does make it kind of fun to play around with. Below I have attached a few screenshots to display how the Teleportation Mechanic works in the prototype, and a video will be available in future blog posts.

Initial Phase of Teleportation: The Range Indicator appears in front of the player, allowing them to select a location.

Initial Phase of Teleportation: The Range Indicator appears in front of the player, allowing them to select a location.

Final Phase of Teleportation: Once a selection has been made, the player is transported there instantly.

Final Phase of Teleportation: Once a selection has been made, the player is transported there instantly.

Electrocution

Now that the Teleportation mechanic had been created and fully functional, James and I thought we would move on to Electrocution as it is the only damage-dealing ability in the game, making it rather significant. The Electrocution mechanic works by allowing the player to select an enemy within a certain range. Once an enemy has been selected they will take a certain amount of damage. If that initial enemy has any number of other enemies surrounding it within a certain range, then those secondary enemies will also take damage. In a sense, it is a strategic chaining mechanic meant to represent electricity going through enemies. The first step in this process was to create a list that would contain all of the Enemies, which would be affected by the Electrocution. Then, the algorithm finds the first enemy that would be affected by checking whether the player has selected a target marked as Enemy and whether that target is in range. If the selected enemy answers to both of those conditions, then it is added to the newly created list of Enemies Affected. The player selects the first target by clicking on it with the Left Mouse Button, and a Ray from the camera locates the selection much like with Teleportation.

After the first enemy has been located and added to the List, then the script searches for other enemies in range that would also be affected by the Electrocution. To begin with, a check is performed to see whether the List of Enemies Hit is empty or not. If the List is not empty, then an Overlap Sphere Collider is placed over the existing enemy within that List. The sphere’s radius is equal to a predetermined maximum range for the Electrocution. The next check performed is to see whether or not there are any other enemies within the Overlap Sphere Collider. If there are, then those enemies are also added to the List of Enemies Hit. Through this method, we can get all of the enemies that would be affected by the Electrocution. After this list has been filled, Damage must be applied to the Affected Targets. For the purposes of this prototype, a very simple check is executes, which simply states that if an enemy is within the Enemies Hit List, then that enemy will be destroyed. That’s the overall logic behind how the Electrocution Script works and the code for it can be reviewed below. Images of the visual process within Unity have also been included.

 void Electrocute() {
        GetComponent<PlayerMove> ().enabled = false;
        nAgent.SetDestination (transform.position);
        RaycastHit h;
        List<GameObject> enemiesHit = new List<GameObject>();
        //Get Initial Victim
        if (Physics.Raycast(Camera.main.ScreenPointToRay
                           (Input.mousePosition), out h, 100f)) {
            if (Mathf.Abs((h.point - transform.position).magnitude) 
            <= maxElectrocuteRange && h.collider.gameObject.layer == 10) {
                enemiesHit.Add(h.collider.gameObject);
            }
        }
        //Find next victims
        if(enemiesHit.Count > 0) {
            Collider[] nextVictims = 
            Physics.OverlapSphere(enemiesHit[0].transform.position,
                                 maxElectrocuteRange/2f);
            foreach (Collider c in nextVictims) {
                if(c.gameObject.layer == 10) {
                    enemiesHit.Add(c.gameObject);
                }
            }
        }
        //Visual Effect
        for(int i = enemiesHit.Count - 1; i >= 0; i--) {
            if (enemiesHit [i].GetComponent<LineRenderer> ().Equals (null)) {
                enemiesHit[i].AddComponent<LineRenderer>();
            }
            enemiesHit [i].GetComponent<LineRenderer> ().positionCount = 2;
            enemiesHit [i].GetComponent<LineRenderer> ().material = 
            Resources.Load<Material> ("Materials/lightning");
            enemiesHit [i].GetComponent<LineRenderer> ().startWidth = 0.2f;
            enemiesHit [i].GetComponent<LineRenderer> ().endWidth = 0.2f;
            enemiesHit [i].GetComponent<LineRenderer> ().startColor = Color.cyan;
            enemiesHit [i].GetComponent<LineRenderer> ().endColor = Color.cyan;
            if (i == 0) {
                enemiesHit [i].GetComponent<LineRenderer> ().SetPosition 
                (0, enemiesHit [0].transform.position);
                enemiesHit [i].GetComponent<LineRenderer> ().SetPosition 
                (1, transform.position);
            } else {
                enemiesHit [i].GetComponent<LineRenderer> ().SetPosition 
                (0, enemiesHit [i].transform.position);
                enemiesHit [i].GetComponent<LineRenderer> ().SetPosition 
                (1, enemiesHit [0].transform.position);
            }
        }
        //Apply damage
        foreach (GameObject enemy in enemiesHit) {
            enemy.GetComponent<Renderer>().material.color = Color.blue;
            Destroy(enemy, 1f);
        }
        currentPower = "NULL";
        GetComponent<PlayerMove> ().enabled = true;
    }
Initial state of Electrocution: The Player has activated the ability and can now choose a target.

Initial state of Electrocution: The Player has activated the ability and can now choose a target.

Middle state of Electrocution: The Player has selected the only target in range and the damage has been distributed to the enemies surrounding the initial target.

Middle state of Electrocution: The Player has selected the only target in range and the damage has been distributed to the enemies surrounding the initial target.

Final state of Electrocution: The damage has now been dealt and all enemies are destroyed.

Final state of Electrocution: The damage has now been dealt and all enemies are destroyed.

Reflection and Feedback

Overall, I really enjoyed working on these mechanics with James, because of how fun they seemed to me even at the prototyping stage. Because there were no cool-downs to these abilities at this stage, they could be used in combination with each other. For example, I found it quite entertaining to use Teleportation to get in range of the enemies and to then use Electrocution to defeat them, all in a manner of seconds. I think that with the addition of new abilities and their refinement, even more fun combinations will be possible. In terms of challenges, I thought that the Electrocution mechanic was rather tricky, especially because it was quite difficult to actually visualize the effect. James and I tied to apply some sort of texture as opposed to a single colored Line Renderer, however, we did not have much success. This indicates that I would have to do some more research on how one achieves effects of Lightning that can be controlled. And although this is just a simple prototype, I would like to refine the Range Indicator and make it a bit more intuitive. The main problem with it for now is that it has to stay on top of the Player’s head, because any other position results in it colliding with the environment, which can look quite broken. However, these problems may end up finding their solutions in Semester 2, because there is still quite a bit of design work to do for this Semester. Nonetheless, I am really excited about what we have so far, and I look forward to developing it further.

IMG_2269.JPG

After a long week of prototyping we had our Formative Feedback Session with Adam on the Friday of Week 8. At that point, I was relatively up to date on what I had been doing so far, so thankfully he was able to see most of my work. We primarily talked about Project Proposal 4 or As It Lies and I briefly went over the main mechanics, the abilities, the dice rolling, and some elements of the world. Overall, Adam’s feedback was positive and he had some really good recommendations on where to take the project forward. For example, he recommended that I translate the game into a paper prototype, specifically for the dice-rolling mechanic and to ensure that it would actually be fun. Additionally, after seeing a 3D Model I was working on, he suggested that I create somewhat of a style guide to help narrow down the specific style of models and game overall. Adam also suggested that I should carefully consider what abilities to include in the game, as they shouldn’t seem random or boring. That’s why none of them have been set in stone yet, and I will be reworking some of the powers later on, possibly through testing with the paper prototype. Quite reasonably, Adam also expressed concerns about the size of the project as I am working alone. That’s why we agreed that I would create a timetable for the second semester in order to ensure that the game will be fully completed by the time it needs to be presented. I intend to start working on Adam’s recommendations as soon as next week, so hopefully I will have even more clarity and information by next Friday.