June 15, 2023

Virtuosos

More than just a rhythm game

Play on Itch.io

Summary

Left Text

Left Text

Middle Text

Middle Text

  • Left Text
  • Left Text
  • Middle Text
  • Middle Text
  • By Brett Hickman, Lucas Li, Alex Hartford, Coby Chuang, William Yi, Kaylen Wan, Tiffany Vu, Kevin Yan, Nancy Chen

    Gameplay

    As a JRPG rhythm game, 「Virtuosos」 combines the style of Japanese role-playing games with elements of rhythm games. Within different levels of the game, players will discover the background stories of various characters and their environments, gradually exploring new gameplay mechanics. And of course, they will be able to indulge in the joy of music!

    Music Performance

    The heart of 「Virtuosos」 lies in its immersive music performance gameplay. Players step into the role of talented musicians and embark on a quest to master rhythms and melodies. Through carefully timed inputs and precise execution, players must perform musical sequences to progress through levels and overcome challenges.

    Visual Novel

    On the other side, players are invited to embark on an immersive journey through a rich and captivating story. Dialogues and narratives will provide deeper insights into the world of Virtuosos, allowing players to develop connections with the characters and become fully immersed.

    Technical Details

    I worked as a programmer along side two others programmers, three artists, and two musicians. In order to improve maintainability, collaboration, and performance, we implemented modular and structured code to lay the foundation for a high-quality and enjoyable gaming experience.

    Sound Management

    All the original sound tracks in the game are handled by the Conductor class. The main functionality of the class is to keep track of the current song position, and schedule the next piece of the music when needed. Here is a simplified version of the class, showing some of the values needed per song:

    									public class Conductor : MonoBehaviour
    {
    	// constants info for a song
    	// for example, 4/4 Time with a resolution of 16th notes
    	private const int SPOTS_PER_BEAT = 4;
    	private const int BEATS_PER_BAR = 4;
    
    	// other constants info for a song
    	public float bpm;
    	public float beatLength;
    	public float spotLength;
    
    	// use to keep track of song position
    	public int spotNumber;
    	public int beatNumber;
    	public int barNumber;
    
    	// number of beats until the first beat of the beatmap
    	public int beats_till_first_note;
    	// constant offset for a song if not on beat
    	public double offset;
    	// player set offset based on monitor/speaker used
    	public float latency_offset;
    
    	// times for play scheduled
    	private double startTime;
    	private double nextStartTime;
    	private double lastStartTime;
    
    	// load drummer section as an example
    	public bool playDrumsNextBar = false;
    
    	void Start()
    	{
    		// initialize all the constants
    		initConstants();
    	}
    
    	public double GetSongPosition()
    	{
    		// Returns the unequivocal position in the current song.
    		return AudioSettings.dspTime - startTime 
    			- (beats_till_first_note * beatLength) 
    			- offset + latency_offset;
    	}
    }
    								

    Notice that the GetSongPosition() function uses dspTime instead of normal Unity time. This is because, when dealing with audio playback, scheduling events, or syncing audio with other gameplay elements, the audio system in Unity operates on a separate timeline. DspTime provides a more accurate representation of time in the audio engine.

    The following code is also part of the Conductor class, showing how we manage to schedule the next piece of the music when needed:

    									public void Update()
    {
    	// if this is level 1, having 2 of the same BGM loaded
    	if(PlayerPrefs.GetInt("level_number") == 1) 
    	{
    		// if the first BGM is playing
    		if(current_background == 1) {
    			// scheduled the second BGM to play at the next in-coming bar
    			background2.PlayScheduled(nextStartTime);
    			current_background = 2;
    		}
    		// if the second BGM is playing
    		else {
    			// scheduled the first BGM to play at the next in-coming bar
    			background.PlayScheduled(nextStartTime);
    			current_background = 1;
    		}
    	}
    	// Check if the drummer section want to start
    	if(playDrumsNextBar) {
    		// scheduled the drummer song to play at the next in-coming bar
    		playDrumsNextBar = false;
    		drums.PlayScheduled(nextStartTime);
    	}
    
    	// update the current timers for play scheduled
    	lastStartTime = nextStartTime;
    	nextStartTime = nextStartTime + backgroundDuration;
    										}
    								

    Visualization

    Based on the beatmap structure above, we can simply spawn a note in the SpawnMaster class on a given track based on its type, length, and position. Updating note position can be divided into 2 pieces: move towards and bounce off. First, we move the note towards the player using linear interpolations:

    The start point of the interpolation is at the track spawner (out of the view of the camera), and the end point is at the note trigger in front of the player, with the interpolation factor based on current song position and current spot position.

    Next, if the player hit the note at the correct time, the note changes its movement and bounces off. In this case, we change the movement calculation to use cosine interpolations:

    Art and Animation

    We were fortunate to have an amazing art team for this project. Collaborating with them was one of the highlights of this project because of their consistently amazing output.

    The first task we had them take on was designing the map for our game. With very little information they created the entire look and feel of the game:

    The map was meticulously divided into distinct layers, carefully separating elements such as sprites, particles, and lights. This approach ensured that each layer contributed to the overall visual richness and depth, resulting in a captivating and visually immersive gaming environment.

    Character Design

    The integration of characters was challenging and engaging, requiring collaboration with various team members including concept artists, lead artists, and the writer. Together, we embarked on an extensive creative process, aligning the character design with the game’s story, art style, and gameplay mechanics. This intricate task demanded careful consideration of every detail, resulting in the development of compelling and well-integrated characters.

    The PlayerController class takes the reins in controlling the characters within「Virtuosos」. Serving as a central hub, it manages essential player information such as health, position, and velocity. Additionally, the PlayerController facilitates character switching and handles the intricacies of animation, which will be discussed next.

    2D Animation

    In「Virtuosos」, all the playable characters come to life through the magic of skeletal animation, offering us customization and reusability. When we initially dove into 2D animation in Unity, we sought inspiration and reference from other games’ animations. However, we put our unique spin on things when it came to the frame ratio for attack animations.

    Now, here’s where we did things a bit differently compared to some other games out there. Attack animations often have stand-by, lead-in, attack, and follow-through phases. However, in our game, we made a bold decision to shake things up. we completely ditched the lead-in phase to give our rhythm game a distinctive and unparalleled game feel.

    By doing so, we created a sense of immediacy and responsiveness that perfectly aligns with the rhythm-based mechanics. It’s all about hitting those beats and maintaining that flow without any unnecessary delays.

    Sprite Animation

    When it came to the other in-game elements like notes and obstacles, we opted for sprite animation. Sprite animation provided us with a simpler and more efficient solution.

    It was a breeze to implement, and whenever edits or adjustments were needed, we could tackle them with lightning speed. After all, when it comes to making quick changes or improvements, efficiency is key.

    Note Detection

    Detection recognition was the primary and most challenging task we undertook in the development of this game.

    The range of each note is tied to the threshold system. Accuracy is defined by a floating point number, with smaller thresholds indicating more demanding timing. A “good” action has a threshold of 1.0, offering a modest window, while achieving “perfect” requires remarkable precision with a threshold of only 0.3.

    A actual hit always happened in between 2 spots

    Input Detection

    To address the challenges of supporting multiple input systems during development, we transitioned from the chaotic input detection approach to a more organized solution. Instead of relying solely on the standard Unity Input API, we adopted the use of Action Maps.

    									if (checkHit(KeyCode.Joystick1Button0, 
    	KeyCode.Joystick1Button2, 
    	KeyCode.Joystick1Button1, 
    	/* potentially needed for other 
    		controller or platform support,
    		but is overlapping with other
    		tracks.
    	KeyCode.Joystick1Button7,
    	KeyCode.Joystick1Button6,
    	KeyCode.Joystick1Button4,
    	*/
    	InputSystem.GetButton("leftShoulder"),
    	KeyCode.K, 2, low))
    hasBeenPressed[2] = true;Old System
    								
    									public InputActionReference lowControls;
    
    if (checkHit(lowControls, 2, low))
    	hasBeenPressed[2] = true;New System
    								

    The implementation of the Action Map not only facilitated support for multiple controllers and platforms, but it also offered increased flexibility for making simultaneous changes when necessary.

    Latency Detection

    Detecting and adjusting for latency is of utmost importance in「Virtuosos」. Players rely on precise timing to hit notes, and even the slightest delay can throw off their rhythm and disrupt the gameplay flow. To address the variability in latency between different monitors and speakers, we have implemented a user-friendly interface that allows players to manually adjust the latency while listening to the background music from the start page.

    By providing this interface, players can fine-tune the synchronization between the visual cues and the corresponding audio feedback according to their specific setup. Each time the user presses the interaction keys (such as space or a button on the controller), the game system calculates the latency by recording the current offset from the correct beat using the Unity audio engine. The system then displays the average offset of the 10 most recent presses from the user.

    Retrospective

    There are many more elements to this game that weren't discussed. In the end,「Virtuosos」is the culmination of our collective efforts to create a unique fusion of rhythm and JRPG elements. As a developer of「Virtuosos」, I am incredibly proud to present this game to you. It has been a journey filled with passion, creativity, and countless hours of hard work from an amazing team of friends.