void Update() { RunState runState = runManager.runState; // do nothing if there is a tutorial if (tutorialManager.canvas.activeSelf) { return; } // adjust light beam width based on current energy float targetDisplayBeamWidth = BeamWidth(); beamWidth = Mathf.Lerp(beamWidth, targetDisplayBeamWidth, .02f); if (Mathf.Abs(beamWidth - targetDisplayBeamWidth) < .5f) { // extra adjustment so it coverges faster beamWidth = Mathf.Lerp(beamWidth, targetDisplayBeamWidth, .0f); } Light light = NoteLight.GetComponent <Light>(); light.cookieSize = beamWidth + .015f; // this is to avoid small slivers of black initially // destroy all notes falling above of the beam foreach (Note note in new List <Note>(notes)) { if (IsAboveBeam(note) && IsTouchingBeam(note)) { note.OnDeflect(); notes.Remove(note); } // only show arrows for active notes inside the beam note.arrow.enabled = !IsAboveBeam(note) && !note.isResolved && !note.IsSuppressed(); } // deal with suppressed notes foreach (Note n in new List <Note>(notes)) { if (n.IsSuppressed()) { n.isResolved = true; } if (activity.suppressedEmotions.Contains(n.emotionType) && n.isResolved && time >= n.arrivalTime) { notes.Remove(n); n.OnSuppress(runState); } } // update time - with current settings goes in increments of about .016 time += Time.deltaTime; // spawn the next preloaded note if the time has come if (notesToSpawn.Count > 0 && time >= notesToSpawn[0].spawnTime) { // spawn note, with time adjusted to be exact with intended pattern SpawnNote(notesToSpawn[0]); notesToSpawn.RemoveAt(0); if (notesToSpawn.Count == 0) { runState.CurrentActivityPlatform().isSongDone = true; } } // take inputs + trigger input animations bool up = Input.GetButtonDown("up"); bool left = Input.GetButtonDown("left"); bool down = Input.GetButtonDown("down"); bool right = Input.GetButtonDown("right"); GameObject inputAnimPreb = up ? upKey : down ? downKey : right ? rightKey : left ? leftKey : null; if (inputAnimPreb && Time.timeScale == 1) { Instantiate(inputAnimPreb, hitArea.transform.position, Quaternion.identity, hitArea.transform); } // detect rhythm hits/misses on the incoming group of notes if (notes.Count() > 0) { // deal with next wave of notes float epsilon = .01f; bool isAboutToSpawnNearest = (notesToSpawn.Count() > 0 && Mathf.Abs(notes[0].arrivalTime - (notesToSpawn[0].spawnTime + travelTime)) < epsilon); // contains all notes that are coming at the same time (only reset once all those notes have been resolved) if ((nearestNotes.Count() == 0 || nearestNotes.All(n => n.isResolved)) && !isAboutToSpawnNearest) { nearestNotes = notes.Where((n) => Mathf.Abs(n.arrivalTime - notes[0].arrivalTime) < epsilon).ToList(); } // unresolved list has all the notes of the current group that are still active List <Note> unResolvedNearestNotes = nearestNotes.Where(n => !n.isResolved).ToList(); // rhythm miss - too late if (nearestNotes.Count() > 0 && time > nearestNotes[0].arrivalTime + hitWindowLate) { // only trigger one miss of each type, to avoid drastic swings if song List <EmotionType> missTypes = new List <EmotionType>(); foreach (Note n in unResolvedNearestNotes) { notes.Remove(n); if (!missTypes.Contains(n.emotionType)) { missTypes.Add(n.emotionType); n.OnMiss(runManager.runState); // reactivate tutorials if miss many notes in a row if (runManager.runState.rhythmBreakCount > 7) { tutorialManager.OnMissingALot(); } } } // update late hit period so late hits do not affect future notes lateHitPeriodEnd = time + lateHitPeriod; } // otherwise, possible hit else if (up || left || down || right) { List <EmotionType> hitTypes = new List <EmotionType>(); if (up) { hitTypes.Add(EmotionType.None); } if (down) { hitTypes.Add(EmotionType.anxiety); } if (left) { hitTypes.Add(EmotionType.despair); } if (right) { hitTypes.Add(EmotionType.frustration); } if (time > nearestNotes[0].arrivalTime - hitWindowEarly) { List <EmotionType> noteTypes = nearestNotes.Select(note => note.emotionType).ToList(); // if an erroneous key was pressed, miss all these notes if (!hitTypes.All(noteTypes.Contains)) { foreach (Note n in unResolvedNearestNotes) { notes.Remove(n); n.OnMiss(runManager.runState); } } else { // hit the notes for which the key is pressed foreach (Note n in unResolvedNearestNotes) { if (hitTypes.Contains(n.emotionType)) { notes.Remove(n); n.OnHit(time, runManager.runState); } } // log hits for invisible suppressed notes! foreach (Note n in nearestNotes) { if (n.IsSuppressed() && hitTypes.Contains(n.emotionType)) { // be extra strict about hit window if (time > n.arrivalTime - hitWindowEarly / 2 && time < n.arrivalTime + hitWindowLate / 2) { n.isInvisibleHit = true; } } } } } else if (time > lateHitPeriodEnd && (time > nearestNotes[0].arrivalTime - earlyHitPeriod)) { // meaningful false hits cause miss next note foreach (Note n in unResolvedNearestNotes) { notes.Remove(n); n.OnMiss(runManager.runState); } } } } // no notes left - then spawn more to repeat the pattern // NOW WHOLE SONGS /* else if (activity != null && notesToSpawn.Count == 0) * { * LoadSong(); * // abort if the player is almost at the end of the platform * // (so no notes can spawn that reach player after they reach end) * float lastSpawnTime = notesToSpawn.Last().spawnTime; * ActivityPlatform ap = runState.CurrentActivityPlatform(); * float distLeft = ap.x + ap.length - player.transform.position.x; * if ((lastSpawnTime + travelTime - time) * player.PlatformMinForwardSpeed(runState) > distLeft) * { * notesToSpawn.Clear(); * notesToSpawn.Clear(); * } * } */ // activate appropriate tutorials // show rhythm tutorial once some notes appear on screen bool noteVisible = notes.Count > 0 && notes[0].transform.position.x < hitArea.transform.position.x + 5; if (gameManager.showRhythmTutorial && noteVisible) { tutorialManager.ActivateRhythmTutorial(); } // show emotion note tutorial once some emotion note seen bool emotionNoteVisible = notes.Count > 0 && notes[0].emotionType != EmotionType.None && !notes[0].IsSuppressed() && !IsAboveBeam(notes[0]); // bool emotionNoteArrived = emotionNoteVisible && (time > notes[0].arrivalTime - hitWindowEarly); if (gameManager.showEmotionNoteTutorial && emotionNoteVisible) { tutorialManager.ActivateEmotionNoteTutorial(); } }