Hey hey, back again with an update to my (still very much early-in-development) Unity project, the basic pitch for which is 'an Ace Attorney-style visual novel where the player takes the role of a psychotherapist in a fantasy world.' Today I'll be talking about a major gameplay mechanic I've been working on over the past month, the notepad.
(You'll have to excuse the weird flickering happening in the gif, this was about the best quality I could get with the tools at hand)
The idea with the notepad is to give the player another way to interact with the game and affect the outcome of the story, in addition to a more traditional visual novel dialogue system. The majority of the game takes place in a psychiatric office talking to clients, and whenever a client says something important or something that could potentially be expanded on, the player will automatically make a note of it in the notepad. Once a certain point in the conversation is reached, the player can bring up the notepad at will and individually select these notes to speak with the client about the note in more depth, potentially leading to further observations and notes. In this way it's meant to be a fairly deep system with the potential for different outcomes depending on which notes the player gathers and how he talks to the client about them. The notepad also serves as an important means of progressing through the game, as the player can use the notes they've gathered to point out inner conflicts in the client at times when the client is being contradictory, similar to presenting evidence in a Phoenix Wright game. Each time a breakthrough is made this way, the client's 'HP bar' (Seems kind of silly to use HP in a game like this, I know, but I figured it was in keeping with the fantasy RPG aesthetic) depletes by an amount proportional to the size of the revelation they've had, and when it falls to zero, a final gameplay segment (one that I haven't fully fleshed out the details on yet) will occur, after which the player 'wins' and the session ends.
So that's the theory behind it, onto the boring nitty-gritty of development. Never really having tackled anything like this before, I figured the best place to start was with the notepad asset itself. I had a few things I knew it needed it to accomplish-
- The player needed to be able to take any note in any order, as their path through the conversation would change depending on what they chose to do and say.
- The notes had to appear as though they were being written on the page, paired with an animation of the player's hands writing them that changed depending on how long the note was.
- The notepad needed to have multiple pages that the player could flip between, and the game logic needed to be able to automatically flip to the next page once the previous page was full.
- The notes needed two distinct 'selection' behaviors and associated dialogue, one for when the player is probing the client for more information about the note, and one for when the player is pointing out a conflict with the note.
With all this in mind, I thought for a while about how best to actually implement the notes on the page before settling on modeling, skinning, and animating all the elements of the page with bones in Maya. For a while I considered trying to use Unity's GUI system for the notes (As you can position UI text and other elements in 3D space to a limited extent), but ultimately decided against it as I needed the text elements to be skinned to the page mesh in order to keep them in place when animating the pages flipping back and forth. With this approach, I used an alpha texture sheet for the note text instead of an actual text element, with each line of text having its own mesh floating just above the base page mesh. The next challenge was how to get the words to appear left to right as though they were being written- the prospect of trying to dynamically resize the text mesh while preserving UVs seemed way more trouble than it was worth, so I used a little trick instead. Floating just above each note text mesh is yet another, slightly bigger, mesh with a texture identical to that of the base page, which is what is actually animating when the notes are written. I realize that may sound a bit confusing, so here's an image to help illustrate it-
There's one big caveat to the way I did this, which is that it wouldn't really work if the notepad had any sort of lighting or texture detail on it, as you'd be able to see the text cover meshes scaling down. However for my purposes (I'm planning to use unlit materials and basically fake/paint in all lighting in this game anyway), it ended up looking really good.
The next thing I had to figure out was how to update the text dynamically to display the correct text for whatever note the player was currently taking. To do this, in the main 'notepad manager' script that I created in Unity I read in information from a separate text file containing all of the relevant instructions for each note (similar to the dialogue text file from my last post), and then use the material.SetTextureOffset function to actually offset the UVs of the text mesh to the specified spot in the texture. The 1024 texture sheet I used for my text contains 3 columns of 26 lines each, so I divided it up into sections, specify the column and row in the instructions, and calculate the actual coordinates in the script. We have to make sure we do this for every line in the current note, so each line has a 'note index' integer value associated with it that is set when writing starts, and we run the texture offset function on every line with the same note index value. Also worth noting is that since I'm developing the game for both PC and mobile platforms, I created two such texture sheets, with the PC sheet having smaller text than the mobile sheet. This also means that notes don't take up the same number of lines between platforms, so I include two sets of instructions for each note, and Unity decides which to use depending on the platform. (Which is a snap to do with Unity's platform-dependent compilation directives)
The instructions for each line include not only its note index and texture offset as I've described, but also which writing animation to use depending on how long the text is (I made four different writing animations, from 1/4 line long to a full line long) and which dialogue to play if the note is selected by the player.
Once the actual mechanics of writing the notes were working, the next thing on the to-do list was to get page-turning working like I wanted it to. An integer value keeps track of what line the last note was written on, and if it detects that the next note will go past the end of the current page, the game automatically flips to a new page and starts writing back at the top. The player is unable to interact while writing a note, but during dialogue he is able to press a button on the screen to bring up the notepad manually, highlight/select individual notes, and flip pages at will. I also wanted the game to flip back to the first page when the player brought up the notepad, and back to the last page when he started writing again. I created a few functions to handle these situations- a TurnPageForward and TurnPageBack function for when the notepad is up and we want to see the animation of the page flipping, and a FlipToPage function for when the notepad is down that takes a page number as a parameter and instantly flips to that page. Both of these functions deactivate all the text objects and colliders on the current page, flip to the specified page, and turn the relevant objects on for that page- the main difference is that FlipToPage can flip through multiple pages until it reaches the right one, and TurnPageForward/Back only flips one page forward or backwards.
The PopulateArray calls that you can see there are populating certain arrays with relevant game objects from the page we just flipped to (For instance, I have an array that keeps track of all the text lines on the current page). One more note about page flipping- On PC I made little arrows at the bottom of each page for the player to use to flip between pages, but on mobile I found that to be too finicky, so instead I opted to use a touchscreen up/down 'swipe' gesture instead, which feels a lot better.
So with note-writing and page-turning out of the way, the last big hurdle was actually allowing the player to interact with these notes, and allowing for different behavior depending on the context in which the player is interacting with them. For any non-UI object that the player interacts with, it needs to have a collider so that a raycast can be made from the mouse position to see if it's over/clicking the object, so the first step was to add colliders to all my text and arrow graphics. From here I used Unity's Event Triggers to detect mouse interaction- specifically, OnPointerEnter (highlight text), OnPointerExit (Un-highlight text), and OnPointerClick (Call click function). The nice thing about these is that they work for touch input/mobile devices too- well, OnPointerClick does, anyway. (Mobile devices don't have hover) To highlight text, I downloaded a custom shader with a 'color' property so that I could change the text color from script (Unity's out-of-the-box mobile shaders don't have this). From here, I created two separate functions depending on whether the game has brought up the notepad (In which case the player is asking the client about a note) or the player has manually brought up the notepad (In which case the player is attempting to find a conflict with what the client is saying). In the former case, the dialogue jumps to that note's 'default' dialogue tree, and in the latter, the script checks the note instructions text file to see if the current dialogue line is a valid 'conflict' for the selected note, and if so, the conflict dialogue starts. If the player successfully presents a conflict, the note will eventually change into another note that represents the true nature of the client's feelings. (This is also where 'damage' will be done) For this transformation, I have the player circle the text, and an effect plays where the circle and text turn green and the circle animates away with a flash. The circle is animated with bones just like everything else on the page, and there are three different circle sizes to choose from depending on whether the note is 1, 2, or 3 lines long. The text 'transformation' is done by animating the alpha of the text to 0%, calling the previously mentioned texture offset function to change the text, then bringing the alpha back to 100 while also animating the texture color from red to green. The 'flash' effect is a particle effect that is triggered during the animation and is attached to the first bone in the circle, and therefore moves along with the circle as it is animating. The end result is a pretty snazzy-lookin' effect, if I may say so.
And there you have it, one month later and I've knocked out arguably the most robust gameplay element this game is going to have. As with the last post, these are all pretty broad strokes and there are a whole bunch of little things I didn't go into detail about, but I fear this post is too long and rambling as it is. In the unlikely case that anybody in the future reads this and has any questions or wants to know more about how I did anything just let me know. (I can't imagine there are too many people out there that are looking for online tutorials about creating notepads in Unity.) From here, I'm planning to get a few other features in like the whole damage/health thing I was talking about, some semblance of a main menu, dialogue options like text logs, auto-text, hiding the dialogue box and so on, and saving and loading the game. All of those (and more?) will probably be included in the next update, and after that we can start getting to the fun stuff- making art and writing the dialogue! Til next time!