#15 Syncing TheFatRat - Unity with Dynamic Cube Spawns (Version 1)
Introduction:
Hello rhythm architects! In this 15th edition of our Beat Saber VR series, we're introducing a musical twist with the iconic "FatRatUnity" song. Version 1 of our CubeSpawner script is tailored to synchronize dynamic cube spawns with the beats of this electrifying track. Join us as we dissect the code, step by step, and infuse our virtual realm with the pulsating rhythm of "FatRatUnity."
Step 1: Integrating FatRatUnity into the Scene:
Import the Song:
- Download the "FatRatUnity" song and import it into your Unity project.
Create an Audio Source:
Add an Audio Source component to the GameObject responsible for managing the audio in your scene.
Assign the "FatRatUnity" song to the AudioClip property.
Adjust Audio Settings:
- Fine-tune audio settings such as volume and spatial blend to achieve the desired auditory experience.
Step 2: Understanding CubeSpawner Script (Version 1):
Initialization:
- The script initializes variables for cube prefabs, spawn parameters, and timing.
Update Method:
- In the Update method, a timer counts beats and triggers cube spawning when a beat is detected.
CanSpawnCube Method:
- Determines if enough time has passed since the last cube spawn based on random delays.
SpawnCube Method:
Spawns blue and red cubes with randomized positions, rotations, and initial velocities.
Adjusts cube scale for visual variety.
ToggleSpawn Method:
- Toggles the ability to spawn cubes on and off, controlled by the 'Q' key or Oculus button 'B.'
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CubeSpawner : MonoBehaviour
{
public GameObject cube;
public GameObject RedCube;
public Vector3 spawnOffset = new Vector3(0.0f, 1.5f, 1.0f);
public float xRange = 0.8f;
public float yRange = 0.3f;
public float minDistance = 12f; // Minimum distance from the player
public float minSpawnDelay = 1.5f;
public float maxSpawnDelay = 2.0f;
public float beat = 60 / 105;
private float timer;
private float lastSpawnTime;
private bool canSpawn = true; // Variable to control spawning
// Start is called before the first frame update
void Start()
{
lastSpawnTime = Time.time;
}
// Update is called once per frame
void Update()
{
if (timer > beat)
{
if (canSpawn && CanSpawnCube())
{
SpawnCube();
}
timer -= beat;
}
// Check if the 'Q' key is pressed or 'B' key is pressed
if (Input.GetKeyDown(KeyCode.Q) || OVRInput.GetDown(OVRInput.RawButton.B))
{
// Toggle spawning when the 'Q' key is pressed
ToggleSpawn();
}
timer += Time.deltaTime;
}
bool CanSpawnCube()
{
float elapsedTimeSinceLastSpawn = Time.time - lastSpawnTime;
float randomSpawnDelay = UnityEngine.Random.Range(minSpawnDelay, maxSpawnDelay);
return elapsedTimeSinceLastSpawn > randomSpawnDelay;
}
void SpawnCube()
{
float x = UnityEngine.Random.Range(-xRange, xRange);
float y = UnityEngine.Random.Range(-yRange / 2.0f, yRange);
float xr = UnityEngine.Random.Range(-xRange, xRange);
float yr = UnityEngine.Random.Range(-yRange / 2.0f, yRange);
Vector3 spawnPos = transform.position - new Vector3(0.0f, 0.0f, spawnOffset.z);
Vector3 directionToPlayer = (transform.position - spawnPos).normalized;
Vector3 initialPos = new Vector3(x, y, 0.0f) + directionToPlayer * minDistance;
Vector3 initialPosRed = new Vector3(xr, yr, 0.0f) + directionToPlayer * minDistance;
// BlueCube is Cube
GameObject blue = Instantiate(cube, initialPos, Quaternion.identity);
blue = blue.transform.Find("Cube").gameObject;
blue.transform.Rotate(transform.forward, 90 * UnityEngine.Random.Range(0, 4));
Rigidbody rbBlue = blue.GetComponent<Rigidbody>();
rbBlue.useGravity = false;
// RedCube is Cube
GameObject red = Instantiate(RedCube, initialPosRed, Quaternion.identity);
red = red.transform.Find("Cube").gameObject;
red.transform.Rotate(transform.forward, 90 * UnityEngine.Random.Range(0, 4));
Rigidbody rbRed = red.GetComponent<Rigidbody>();
rbRed.useGravity = false;
float theta = UnityEngine.Random.Range((float)(Math.PI / 4.0f), (float)(3.0f * Math.PI / 4.0f));
float mag = UnityEngine.Random.Range(1.0f, 3.0f);
Vector3 addedVelocity = -directionToPlayer * mag;
rbBlue.AddForce(addedVelocity, ForceMode.VelocityChange);
rbRed.AddForce(addedVelocity, ForceMode.VelocityChange);
float scale = UnityEngine.Random.Range(0.1f, 0.25f);
blue.transform.localScale = new Vector3(scale, scale, scale);
red.transform.localScale = new Vector3(scale, scale, scale);
lastSpawnTime = Time.time;
}
// Method to toggle spawning on/off
public void ToggleSpawn()
{
canSpawn = !canSpawn;
}
}
Step 3: Synchronizing with FatRatUnity Beats:
Beat Detection:
- The timer in the Update method corresponds to the beats of "FatRatUnity," triggering cube spawns in sync.
Fine-Tuning Spawn Logic:
Adjust spawn rates and positions to align with the rhythm of the song.
Experiment with parameters for optimal synchronization.
Step 4: Testing and Refinement:
Playtest the Scene:
- Enter Play mode and experience the dynamic cube spawns synced with "FatRatUnity."
Refine as Needed:
Fine-tune parameters based on playtesting feedback.
Ensure a harmonious blend of visual and auditory elements.
GitHub Link - For Version-1 branch: https://github.com/Akash3121/Beat-Saber-Game-VR/tree/Version-1
Conclusion:
With "FatRatUnity" setting the beat, our virtual realm is now a vibrant symphony of rhythm and visuals. Stay tuned for future updates as we evolve our Beat Saber VR experience.
Happy Coding :)