コード例 #1
0
    void Update()
    {
        float audioTime = mediaSource.getTime();// -playStartOffset;//TODO necessary to add playStartOffset? let's go with no, but it does affect the boxes that were spawned before clip start, so should add the offset once available, to existing boxes

        //Debug.Log("... " + frameSeekOffset + " " + seekOffset + " " + state);
        if (state != State.MODELLING)
        {
            //we check user lane pos drag in all states, for different use cases (eg. while idle just switch custom layouts, for recording save the new pos, etc.)
            bool lanePosChanged = false;
            //Vector3[] changedLanes = new Vector3[lanesEnd.Length];
            for (int lane = 0; lane < lanesEnd.Length; lane++)
            {
                HitChecker.RegisteredMove move = lanesEnd[lane].getRegisteredMove();
                if (move != null)
                {
                    lanePosChanged = true;
                    if (playMode == Settings.PlayMode.RECORD_PATTERN)
                    {
                        lanePosChanges.Add(new SongRow(getLanesPosition(), audioTime));
                        //Debug.Log("Recorded lane pos change at " + audioTime);
                    }
                    //changedLanes[lane] = move.position;
                }
                //else//non changing lanes we set to current position (this is saved as custom positions in UIManager)
                //    changedLanes[lane] = lanesEnd[lane].transform.position;
            }
            if (lanePosChanged && (state != State.PLAYING && state != State.SEEK_TO_PLAY))   //only broadcast lane change in idle states, this changes the ui and settings, no need to do while playing/recording
            {
                EventManager.TriggerEvent(EventManager.EVENT_USER_LANE_LAYOUT_CHANGE, null); //new LayoutParam(changedLanes));
            }
        }

        if (state != State.SEEK_TO_PLAY && state != State.PLAYING)
        {
            return;
        }


        if (state == State.SEEK_TO_PLAY) //spawn the beats that have timestamp shorter than timeToLaneEnd
        {
            if (recordingStartFlag && playMode == Settings.PlayMode.RECORD_PATTERN)
            {//spawn colored boxes when the song begins (well, seekOffset time before it begins)
                spawn(-1);
                recordingStartFlag = false;

                //TODO setting this initial position messes up the resulting pattern lanes pos?!?!

                //Vector3[] lanePos = Settings.instance.getLayout();//we also save current layout as starting one for the pattern
                lanePosChanges.Add(new SongRow(getLanesPosition(), 0));
            }
            frameSeekOffset += Time.deltaTime;
            //Debug.Log("... " + frameSeekOffset + " " + seekOffset);
            if (seekOffset <= frameSeekOffset)
            {
                Debug.Log("TimeToEnd offset is complete, starting clip. Offset " + seekOffset + " " + playMode);
                mediaSource.setVolume(0.9f);
                mediaSource.play();
                //state = State.READY_TO_PLAY;
                state = State.PLAYING;
                //TODO to keep things simple, make sure all lanes in a layout always have same distance
                frameSeekOffset = seekOffset;// timeToLaneEnd[0];//this works, as long as all lanes have same distance
            }
        }
        if (state == State.PLAYING)
        {
            if (!mediaSource.isPlaying())
            {
                Debug.Log("Song end, stopping");
                EventManager.TriggerEvent(EventManager.EVENT_END_OF_SONG, null);
                stop();
                return;
            }
            //if (audioSource.time == 0)
            //    elapsedPlayTime += Time.deltaTime;
        }


        if (playMode == Settings.PlayMode.RECORD_PATTERN)
        {
            //TODO spawn line instead of ghostboxes

            /*
             * int index = getNextRowIndex(SongRow.Type.BEAT, audioTime + frameSeekOffset, beatRowIndex);
             * while (index >= 0)
             * {
             *  //Debug.Log("BEAT at " + songRows[SongRow.Type.BEAT][index].timestamp + " " + time + " clip time " + audioSource.time);
             *  spawn(songRows[SongRow.Type.BEAT][index].timestamp);
             *  beatRowIndex = index + 1;
             *  index = getNextRowIndex(SongRow.Type.BEAT, audioTime + frameSeekOffset, beatRowIndex);
             *
             * }
             * */

            //lane pos change caused by switch button
            //TODO let's deactivate this, it's enough with drag?
            if (InputUtils.isCustomLayoutSwitchDown())
            {
                //Settings.instance.setCustomLayouts(!Settings.instance.isCustomLayouts());//already done in uimanager.update()
                //setLanesPosition(lanePos);//UIManager already takes care of position changes, just need to save the new pos for the recording
                Vector3[] lanePos = Settings.instance.getLayout();
                lanePosChanges.Add(new SongRow(lanePos, audioTime));
            }

            int activeLaneChanges = 0;

            //take hitcheckers registered beats and save/spawn
            float laneBoxAverageTime = 0;
            for (int lane = 0; lane < lanesEnd.Length; lane++)
            {
                HitChecker.RegisteredBeat beat = lanesEnd[lane].getRegisteredBeat();

                if (beat != null && beat.type != SongRow.BoxType.NONE)// && (prevRegisteredBeats[lane] == null || prevRegisteredBeats[lane].type == SongRow.BoxType.NONE))// || beat.timestamp > prevRegisteredBeats[lane].timestamp + Settings.instance.minTimeBetweenRegisteredBeats))//avoid duplicates
                {
                    //if (prevRegisteredBeats[lane] != null)
                    //    Debug.Log(beat.timestamp + " " + prevRegisteredBeats[lane].timestamp + " " + prevRegisteredBeats[lane].type);
                    //if (prevRegisteredBeats[lane].type != SongRow.BoxType.NONE && beat.timestamp < prevRegisteredBeats[lane].timestamp + Settings.instance.minTimeBetweenRegisteredBeats)
                    if (beat.timestamp < prevRegisteredBeats[lane].timestamp + Settings.instance.minTimeBetweenRegisteredBeats)
                    {
                        Debug.Log("Ignoring box!!!");
                        continue;
                    }

                    laneBoxAverageTime                 += beat.timestamp;
                    prevRegisteredBeats[lane].type      = beat.type;
                    prevRegisteredBeats[lane].timestamp = beat.timestamp;
                    activeLaneChanges++;

                    int direction = SongRow.getDirection(beat.type);

                    if (direction >= 0)
                    {
                        spawn(lane, false, true, direction);
                    }
                    else
                    {
                        switch (beat.type)
                        {
                        case SongRow.BoxType.REGULAR:
                            spawn(lane, false, true);
                            break;

                        case SongRow.BoxType.HOLD:
                            //TODO keep holds array to extend trail
                            //use lastHoldBox[lane]?
                            BeatBox holdBeatBox = spawn(lane, false, true);//TODO spawn recording trails and stuff (currently just ghostbox + regular box)
                            holdBeatBox.holdTrail(1.5f, Color.grey);
                            break;
                        }
                    }
                }
                else
                {
                    prevRegisteredBeats[lane].type = SongRow.BoxType.NONE;
                    //prevRegisteredBeats[lane].timestamp = beat.timestamp;
                }
            }
            if (activeLaneChanges > 0)
            {
                //if (laneBoxAverageTime == 0)
                //    Debug.Log("0 TIMESTAMP!!!!!!!!");
                if (laneBoxAverageTime != 0)//ignore timestamp zeros, those are from... release? TODO, wtf?
                {
                    processedRecordedBeats.Add(new SongRow(laneBoxAverageTime / activeLaneChanges, prevRegisteredBeats));
                }
            }
        }

        lanePosChangeProgressTime += Time.deltaTime;
        if (mediaSource.isPlaying() && playMode == Settings.PlayMode.PLAY && mediaSource.getTime() <= 0)
        {
            playStartOffset += Time.deltaTime;
            Debug.Log("not playing yet?!?!?!?!?!?!?!?!? " + playStartOffset);
        }
        else
        if (playMode == Settings.PlayMode.PLAY || playMode == Settings.PlayMode.TEST_PATTERN_PLAY)
        {
            //adjust lane position smoothly
            if (lanePosChangeProgressTime <= totalDuration)
            {
                setLanesPosition(currentLaneDestination, lanePosChangeProgressTime / totalDuration);
            }

            //check updated lane pos to set smooth change
            int index = getNextRowIndex(SongRow.Type.LANE_POS, audioTime, lanPosRowIndex);
            if (index >= 0)
            {
                if (songData.rows[SongRow.Type.LANE_POS].Count > index + 1)
                {
                    totalDuration = songData.rows[SongRow.Type.LANE_POS][index + 1].timestamp - songData.rows[SongRow.Type.LANE_POS][index].timestamp;
                    //Debug.Log("Total duration " + totalDuration);
                    lanePosChangeProgressTime = 0;
                    currentLaneDestination    = songData.rows[SongRow.Type.LANE_POS][index].lanePositions;
                }
                else
                {
                    setLanesPosition(songData.rows[SongRow.Type.LANE_POS][index].lanePositions, totalDuration);
                }

                lanPosRowIndex = index + 1;
            }

            //beat BPM change checks. using just for event notification to animators
            //TODO songRow types for animations (new segment?)...
            index = getNextRowIndex(SongRow.Type.BPM, audioTime + frameSeekOffset, bpmRowIndex);
            if (index >= 0)
            {
                float bpm = songData.rows[SongRow.Type.BPM][index].bpm;
                //calculateTimeToLaneEnd(Math.Max(bpm, 120));//doing this messes up the pattern (fast beats overtaking slow ones)
                EventManager.TriggerEvent(EventManager.EVENT_BPM_CHANGE, new BeatData(-1, -1, bpm));
                bpmRowIndex = index + 1;
            }

            //beat with chroma check
            int   activatedLaneCount  = 0;
            float searchAheadBeatTime = (audioTime + frameSeekOffset);

            index = getNextRowIndex(SongRow.Type.BOX, searchAheadBeatTime, boxRowIndex);
            if (index >= 0)
            {
                //EventManager.TriggerEvent(EventManager.EVENT_BEAT, new BeatData(-1, -1, bpm));

                SongRow songRow = songData.rows[SongRow.Type.BOX][index];
                boxRowIndex = index + 1;
                //int[] activeLanes = songRow.activeLanes;
                for (int lane = 0; lane < timeToLaneEnd.Length; lane++)
                {
                    if (songRow.activeLanes[lane] > 0)
                    {
                        //finds out if the lane will be at a different location when beat arrives, which should be songRow.timestamp + timeToLaneEnd
                        //TODO review the timing for this
                        //look at where the lane end might be at the time the box arrives (now + timeToLaneEnd)
                        int validFutureLanePosIndex = -1;
                        //int futureLanePosIndex = getNextRowIndex(SongRow.Type.LANE_POS, time - seekOffset + timeToLaneEnd[lane], lanPosRowIndex);
                        int futureLanePosIndex = getNextRowIndex(SongRow.Type.LANE_POS, songRow.timestamp, lanPosRowIndex);
                        while (futureLanePosIndex >= 0)
                        {
                            validFutureLanePosIndex = futureLanePosIndex;
                            //futureLanePosIndex = getNextRowIndex(SongRow.Type.LANE_POS, time - seekOffset + timeToLaneEnd[lane], futureLanePosIndex + 1);
                            futureLanePosIndex = getNextRowIndex(SongRow.Type.LANE_POS, songRow.timestamp, futureLanePosIndex + 1);
                        }

                        if (validFutureLanePosIndex >= 0)
                        {
                            Debug.Log("Beat should arrive at " + (songRow.timestamp + timeToLaneEnd[lane]));
                            Debug.Log("Closest position update  " + songData.rows[SongRow.Type.LANE_POS][validFutureLanePosIndex] + " index " + validFutureLanePosIndex + " at " + songData.rows[SongRow.Type.LANE_POS][validFutureLanePosIndex].timestamp);
                        }
                        Vector3 futureLanePos = Vector3.zero;
                        if (validFutureLanePosIndex >= 0)
                        {
                            futureLanePos = songData.rows[SongRow.Type.LANE_POS][validFutureLanePosIndex].lanePositions[lane];
                        }

                        //validFutureLanePosIndex = -1;
                        //validFutureLanePosIndex = -1;
                        //spawning a hold or regular note, with lane position at time of arrival (if it changes)
                        //notTODO it's not a bug, it's a feature: hold lanes get restarted at init of other lane changes. fixed (it was a timestamping, index fetching, nut sucking problem)
                        bool isHold = Settings.instance.difficultyTrailBoxes && songRow.activeLanes[lane] == 2;
                        if (isHold)
                        {
                            //we create new hold
                            if (lastHoldBox[lane] == null || !lastHoldBox[lane].isTrailActive())
                            {    //don't feed trails to boxes that are already done!
                                if (validFutureLanePosIndex >= 0)
                                {
                                    lastHoldBox[lane] = spawn(lane, futureLanePos);
                                }
                                else
                                {
                                    lastHoldBox[lane] = spawn(lane, false, false);
                                }
                                activatedLaneIndex += 2;

                                Color laneColor = AssetManager.instance.getLaneColor(lane);
                                laneColor.a = 0.2f;
                                //we add the initial trail to the box
                                if (songData.rows[SongRow.Type.BOX].Count > index + 1 && songData.rows[SongRow.Type.BOX][index + 1].activeLanes[lane] == (int)SongRow.BoxType.HOLD)
                                {
                                    lastHoldBox[lane].holdTrail(songData.rows[SongRow.Type.BOX][index + 1].timestamp - songRow.timestamp, laneColor);
                                }
                                //else
                                //    lastHoldBox[lane].holdTrail(audioSource.clip.length - songRow.timestamp, laneColor);//last hold with no other box to set finish time
                            }
                            else //we add trail to existing hold
                            {    //index-1 should always be inside bounds, since it is a trail for previous row
                                //if (songRow.timestamp - songRows[SongRow.Type.BOX][index - 1].timestamp > 0)//hmm...
                                //{
                                //lastHoldBox[lane].holdTrail(songRow.timestamp - songRows[SongRow.Type.BOX][index - 1].timestamp, AssetManager.instance.getLaneColor(lane));
                                if (songData.rows[SongRow.Type.BOX].Count > index + 1 && songData.rows[SongRow.Type.BOX][index + 1].activeLanes[lane] == (int)SongRow.BoxType.HOLD)
                                {
                                    //problem here is that last segment of trail addition addst time after trail should have endend
                                    lastHoldBox[lane].holdTrail(songData.rows[SongRow.Type.BOX][index + 1].timestamp - songRow.timestamp, Color.white);        //AssetManager.instance.getLaneColor(lane) - new Color(0,0,0,0.6f));
                                    //lastHoldBox[lane].holdTrail(songRow.timestamp + songRows[SongRow.Type.BOX][index - 1].timestamp, Color.white);//AssetManager.instance.getLaneColor(lane) - new Color(0,0,0,0.6f));
                                    activatedLaneIndex += 3;
                                }
                                //else
                                //    lastHoldBox[lane] = null;//we can finish the trail here?
                                //}
                            }
                            //TODO current//problem is that trail finishes at the right time, but beatbox still stays in place... should disable trail
                        }
                        else
                        {    //non hold box
                            if (lastHoldBox[lane] != null)
                            {
                                activatedLaneIndex += 4;
                            }
                            lastHoldBox[lane] = null;

                            int direction = SongRow.getDirection(songRow.activeLanes[lane]);

                            if (validFutureLanePosIndex >= 0)
                            {
                                lastRegularBox[lane] = spawn(lane, futureLanePos, direction);
                            }
                            else
                            {
                                lastRegularBox[lane] = spawn(lane, false, false, direction);
                            }

                            //Debug.Log("Spawning note (test note at 3.5s) note.ts" + songRow.timestamp + " audiotime+offset" + time + " audiotime" + audioSource.time + " audiotime+offset+seekoffset " + (time + seekOffset) + "  with playOffset " + (audioSource.time + frameSeekOffset + playStartOffset));
                            //Debug.Log("Spawning note frameOffset " + frameSeekOffset + " seekOffset" + seekOffset);// + " audiotime" + audioSource.time + " audiotime+offset+seekoffset " + (time + seekOffset) + " -playOffset " + (audioSource.time - seekOffset + playStartOffset));

                            EventManager.TriggerEvent(EventManager.EVENT_BEAT_NOTE, new BeatData(lane, -1, bpm));

                            activatedLaneIndex += lane;
                            activatedLaneCount++;
                            //activatedTimestamp = songRow.timestamp;
                        }
                    }
                    else
                    {
                        if (lastHoldBox[lane] != null)
                        {    //this lane is currently inactive, so no more trail to add to current trail box, next trail will be added to new box
                            lastHoldBox[lane]   = null;
                            activatedLaneIndex += 5;
                        }
                        else
                        {
                            activatedLaneIndex += 6;
                        }
                    }
                }

                //index = getNextRowIndex(SongRow.Type.BOX, searchAheadBeatTime, boxRowIndex);
            }

            index = getNextRowIndex(SongRow.Type.BEAT, searchAheadBeatTime, beatRowIndex);
            if (index >= 0)
            {
                EventManager.TriggerEvent(EventManager.EVENT_BEAT, new BeatData(-1, -1, bpm));
                //difficulty +1 code
                if (playMode == Settings.PlayMode.PLAY && Settings.instance.difficultyPlusOneBox)    //ignore the option +1 when testing
                {
                    //if (activatedLaneCount < Settings.instance.patternMaxActiveLanesAtOnce)
                    //if (activatedLaneCount == 0)
                    //{
                    int plusOneLane = (activatedLaneIndex + activatedLaneCount) % lanesEnd.Length;
                    //while (lastHoldBox[plusOneLane] != null)
                    //    plusOneLane = (++activatedLaneIndex + activatedLaneCount) % lanesEnd.Length;

                    if (lastRegularBox[plusOneLane] != null && searchAheadBeatTime - lastRegularBox[plusOneLane].timeToLaneEnd > 0.5f)
                    {
                        plusOneLane++;
                    }

                    if (plusOneLane < lanesEnd.Length)
                    {
                        spawn(plusOneLane, !Settings.instance.ghostBoxWithLaneColor);
                    }
                    //}
                }
                //EventManager.TriggerEvent(EventManager.EVENT_BEAT, new BeatData(-1, -1, bpm));
                beatRowIndex = index + 1;
                //index = getNextRowIndex(SongRow.Type.BEAT, audioTime, beatRowIndex);
            }
        }
    }
コード例 #2
0
    public void mediaPrepared()
    {
        //TODO Settings.instance.isSongPatternOverridesLanePositions() ignore lane_POS if not doing override
        //TODO if overrides, who handles number of lanes... should be user set, but if set to 4 and lane_pos has 6 pos, should we switch numlanes... mmm, yeah, if override, check first lane_pos length to change ui set
        Debug.Log("Media prepared");
        if (playMode == Settings.PlayMode.RECORD_PATTERN)
        {
            processedRecordedBeats.Clear();
            lanePosChanges.Clear();
        }

        if (playMode == Settings.PlayMode.TEST_PATTERN_PLAY)
        {//only thing needed to play test is replace loaded box type items to recordedBeats, then act as regular play
            Debug.Log("Skipping loaded BOXes (" + songData.rows[SongRow.Type.BOX].Count + ") to use previously recorded pattern (" + processedRecordedBeats.Count + ")");
            songData.rows[SongRow.Type.BOX]      = processedRecordedBeats;
            songData.rows[SongRow.Type.LANE_POS] = lanePosChanges;//THIS IS RELATED TO THE FUTURE POS ISSUE
            playMode = Settings.PlayMode.PLAY;
        }

        Debug.Log("Spawner.play " + mediaSource.getName());
        Debug.Log(songData.rows[SongRow.Type.BEAT].Count + " beats, " + songData.rows[SongRow.Type.BOX].Count + " boxes, " + songData.rows[SongRow.Type.BPM].Count + " bpms, " + songData.rows[SongRow.Type.LANE_POS].Count + " lanePositions");

        foreach (HitChecker hc in lanesEnd)
        {
            hc.reset();
        }

        int firstBPMIndex  = 0; // getFirstRowIndex(SongRow.Type.BPM);
        int firstBeatIndex = 0; // getFirstRowIndex(SongRow.Type.BOX);

        if (songData.rows[SongRow.Type.BPM].Count == 0)
        {
            songData.rows[SongRow.Type.BPM].Add(new SongRow(0, 160));//set default bpm if there's none in the pattern (should not happen but...)
        }
        //if (firstBPMIndex < 0 || firstBeatIndex < 0){
        if (songData.rows[SongRow.Type.BPM].Count == 0 || songData.rows[SongRow.Type.BOX].Count == 0)
        //if (songRows[SongRow.Type.BOX].Count == 0)
        {
            Debug.LogError("Can't play song, BOX types missing " + firstBPMIndex + " " + firstBeatIndex);
            state = State.STOPPED;
        }
        else
        {
            //SongRow firstBpm = songRows[firstBPMIndex];
            //SongRow firstBpm = songRows[SongRow.Type.BPM][0];

            float maxBpm = 80;
            foreach (SongRow row in songData.rows[SongRow.Type.BPM])
            {
                if (row.bpm > maxBpm)
                {
                    maxBpm = row.bpm;
                }
            }
            calculateTimeToLaneEnd(Mathf.Min(maxBpm, 240));
            //working assumption that first beat will reach lane end first (it won't if other lanes timeToEnd + second beat timestamp < first beat)
            //TODO a thourough check to find which songRow and lane is the first to end

            float soonestTimeToEnd = 100000;
            //SongRow firstBeat = songRows[firstBeatIndex];
            SongRow firstBeat = songData.rows[SongRow.Type.BOX][0];
            firstBeatTimestamp = firstBeat.timestamp;

            //TODO problem here is a beat on L8 is not registering on a layout 4 config
            for (int lane = 0; lane < timeToLaneEnd.Length; lane++)
            {
                //if (firstBeat.activeLanes[lane] > 0 && soonestTimeToEnd > timeToLaneEnd[lane])
                //if (firstBeat.activeLanes[lane] > 0 && soonestTimeToEnd > timeToLaneEnd[lane])
                if (soonestTimeToEnd > timeToLaneEnd[lane])
                {
                    soonestTimeToEnd = timeToLaneEnd[lane];
                }
            }

            Debug.Log("Soonest time to end " + soonestTimeToEnd + " " + firstBeat + " frameSeekOffset: " + frameSeekOffset);

            seekOffset = soonestTimeToEnd;
            state      = State.SEEK_TO_PLAY;
        }

        if (state == State.SEEK_TO_PLAY)
        {
            if (songData.rows[SongRow.Type.LANE_POS].Count > 0 && Settings.instance.isSongPatternOverridesLanePositions())
            {//if first lane_pos row exists and has timestamp 0, and song pattern overrides lane pos, we init lane positions with its parameter
                SongRow firstLanePos = songData.rows[SongRow.Type.LANE_POS][0];
                if (firstLanePos.timestamp <= 0)
                {
                    //Debug.Log("7777777777777777777777777777777777 TO " + firstLanePos.lanePositions);
                    setLanesPosition(firstLanePos.lanePositions, true);
                }
            }
        }

        prevRegisteredBeats = new HitChecker.RegisteredBeat[lanesEnd.Length];
        for (int i = 0; i < lanesEnd.Length; i++)
        {
            prevRegisteredBeats[i] = new HitChecker.RegisteredBeat(0, SongRow.BoxType.NONE);
        }

        //audioSource.PlayDelayed(seekOffset);

        //return state == State.SEEK_TO_PLAY;
    }