Back to homepage of
Replacing the traditional RPG battle system are dance-offs, programmed in sequencer for each song. The rhythm functionality is managed primarily by an invisible object within the game world: obj_beat.
OBJ_BEAT EVENT GRAPH
At the highest level, the event graph handles the timing of a note to be Good, Great, or Perfect, as well as Early or Late. The timing of these can be easily tweaked via float variables.
If there is no input detected, the object will register the note as a miss and run animations accordingly. Each interval of timing runs a function named fnc_beatrange.
Each time the beat reaches a new time interval, it checks this code. This looks for several features of the note to make sure the right one is being hit: including direction, its place in the queue of notes, etc.
Additionally, for debugging purposes, it prints a string at the exact beat point so the timing can be arranged precisely.
Each direction is checked via a function named fnc_checkdirection.
Like the function above it, this function checks to make sure the proper note, direction, and the like is being hit. If it passes all these tests, it removes any extraneous inputs, plays animations and draws text corresponding to the direction and timing, adds the player's respective points, and removes the note from both the UI and queue.
It also checks the player's streak, via a function named fnc_streak.
This function checks the player's streak, via a temporary array. If the three indexes of the array are equal to each other, and the indexes are Perfects, it will run a "Killin' it!" text across the screen and add extra points. If they are instead all equal to misses, "Ouch..." will play instead, and points (along with health) will be reduced.
Using Unreal's audio visualizer plugin, the dance battles also trigger another invisible object: obj_vis. Within this script is a loop which creates a ring of bars at each whole number interval through 360. In real time, these bars change height in correspondence to the volume of their particular frequency.
In RPGs such as this, a high fidelity dialogue system is key for encouraging meaningful interaction with characters and providing them with personality and charm. The dialogue system for Project EIGHT accomplishes this by a combination of traditional blueprint interaction and a repurposing of Unreal's behavior trees.
MAIN INTERACTION INPUT
Within the player character is the input for triggering a dialogue. This checks multiple things, such as if the dialogue is already open, and if there is something interactable that the player can talk to.
If it passes these, the code toggles between three states: starting, continuing, and ending the dialogue. There are custom events peppered around to skip certain sections as needed, for cutscenes and special occurrences.
For the dialogue to draw the text, the code includes a macro named mcr_scrolltxt.
This code is a loop with branching paths, which draws the text on the screen character by character. These branching paths check for things such as the name of the character speaking, the sound to play when a letter is drawn, and special characters (the space character skips the sound, the ` character skips being drawn and adds a delay in its place, etc.)
Once the length of the string being drawn matches the length of the desired result string, the loop finishes.
Obj_itr is an object to which the player can interact. Upon overlapping with the object's trigger box, this is selected as the object to interact with and sets variables accordingly. These variables include text speed, the character (if applicable), and of course the interaction text itself.
There are also public variables if these objects save the game, or give the player an item.
If the object has something in the "dlg" variable slot, however, it ignores these and instead runs the dialogue behavior trees.
DIALOGUE BEHAVIOR TREES
More complex cutscenes and dialogue options repurpose Unreal's behavior tree into dialogue trees. This allows for player choice and branching dialogue paths, as well as cutscene functions such as moving cameras and characters.
These cutscene functions come in the form of normal behavior tree tasks. Some include tsk_dlg, tsk_nextdialogue, tsk_answers, and tsk_cameramove.
Tsk_dlg is the primary task in the dialogue trees. It stores the dialogue information such text speed, the character (if applicable), and the interaction text itself, and transfers them to the player character, just like obj_itr does for non-cutscenes.
There are a few new variables in here, such as "walkable", which if set, allows the player to continue walking while a conversation is going on in the background. "name_unknown" allows for the proper character voice to play while the name is shown as "???" instead.
Almost always following tsk_dlg is tsk_nextdialogue, a task which waits for the player's input once the text is finished drawing. Within this is a float variable named "automatic" which, when not set to 0, will disable the player input and move to the next dialogue after the set amount of seconds. This is great for arguments, walkable cutscenes, and more!
Usually once an input is read, it will remove the text and dialogue widget, unless the "question" variable is set to true. In this case, the tsk_answers task will run instead.
If the player is given a choice, tsk_answers will draw buttons on the screen with their respective names as public variables. The inputted answer is set as a blackboard key selector variable, and the dialogue tree picks the corresponding branch.
These dialogue trees also have functionality to move the camera among other cutscene programming.
Within this task are options for directly moving the current camera, as well as using a preset camera location via the Set View Target With Blend node. To move the camera back to the game's default at any point, another tsk_cameramove task can be placed in the tree and setting "reverse" to true.