/// <summary>Advances this instance by the given amount.</summary> public void update(float deltaTime) { if (!started || paused) { // Awaiting data, usually. return; } if (currentTime == 0f) { if (deltaTime > 0.5f) { // Block slow frames. // This is almost always only ever the very first one return; } // Clear running: firstRunning = null; lastRunning = null; // Establish duration: float rawDuration = appliedDuration; if (rawDuration < 0f) { if (duration == null) { // Infer from max end time, or '1' if there is none. rawDuration = maxDefinedDuration; if (rawDuration == 0f) { rawDuration = 1f; } } else { rawDuration = duration.GetDecimal(style == null? null : style.RenderData, null); } } actualDuration = rawDuration; // Starting backwards? backwards = (((int)direction & 1) == 1); // Update durations for each track: for (int i = 0; i < tracks.Length; i++) { Track track = tracks[i]; if (track == null) { continue; } track.onStart(); // Set start/duration: track.setStartAndDuration(rawDuration); // Reset current index: if (backwards) { track.currentSlide = track.slides.Length; } else { track.currentSlide = -1; } } // Dispatch start: dispatch("start"); // Instant? if (actualDuration == 0f) { stop(true); return; } } // If we have a timing leader, then current time is.. if (timingLeader != null) { currentTime = timingLeader.computedStart; if (timingLeader.timing != null) { // Get the leaders duration (just in case it has expired): float duration = timingLeader.timing.GetDuration(); float current = timingLeader.timing.GetCurrentTime(); currentTime += current; if (duration != -1f && current >= duration) { // It's finished! Quit the timing leader: // (This occurs if the lead time is shorter than the slide's duration). timingLeader.endTimingLead(); } } } else { currentTime += deltaTime; } if (style != null && !style.Element.isRooted) { // Immediately stop - the element was removed (don't call the finished event): stop(false); return; } // Set ActiveValue by sampling from the curve (if there is one): if (progressSampler != null) { // Map through the progression curve: progressSampler.Goto(currentTime / actualDuration, true); currentTime = progressSampler.CurrentValue * actualDuration; } // Advance all tracks to the frame at 'progress'. for (int i = 0; i < tracks.Length; i++) { // Get the track: Track track = tracks[i]; if (track == null || track.slides == null || track.slides.Length == 0) { continue; } int length = track.slides.Length; int index = track.currentSlide; if (backwards) { index--; } else { index++; } while ((backwards && index >= 0) || (!backwards && index < length)) { // Get the slide: Slide slideToStart = track.slides[index]; if (slideToStart.ignore) { // Skip: if (backwards) { index--; } else { index++; } continue; } // Startable? if (backwards) { if ((1f - slideToStart.computedEnd) >= currentTime) { // Nope! break; } } else if (slideToStart.computedStart >= currentTime) { // Nope! break; } // Add to queue: slideToStart.nextRunning = null; slideToStart.previousRunning = lastRunning; if (firstRunning == null) { firstRunning = lastRunning = slideToStart; } else { lastRunning.nextRunning = slideToStart; lastRunning = slideToStart; } // Start it now: slideToStart.start(); // Next: track.currentSlide = index; if (paused) { return; } if (backwards) { index--; } else { index++; } } } // Kill any slides which are now done: endDoneSlides(); if (currentTime >= actualDuration) { // Done! completedCycle(); } }
/// <summary>Loads a track from the given track data.</summary> public static Track loadFromJson(Timeline timeline, JSObject json) { // First, is it an indexed array? // ["Hello!","I'm Dave"] <- A basic dialogue track var trackData = json as Json.JSIndexedArray; Track track = null; if (trackData == null) { // Must explicitly define which type it is: // { "type":"style", "track":TheTrackData } string type = json.String("type"); if (string.IsNullOrEmpty(type)) { Timeline.loadFailed( "A track was an object but didn't contain a " + "'type' field to state what kind of track it is.", timeline ); } // How about track data: var intenalTrackData = json["track"]; if (intenalTrackData == null) { // Nope! Timeline.loadFailed("A track was an object but didn't contain a 'track' field.", timeline); } // Try and create a track of the specified type: type = type.Trim().ToLower(); if (type == "style" || type == "css") { // Style track. track = new StyleTrack(); } else if (type == "dialogue" || type == "speech" || type == "text") { // Dialogue track. track = new DialogueTrack(); } else { Timeline.loadFailed("Didn't recognise '" + type + "' as a track type.", timeline); } // Set it up: track.timeline = timeline; // Load the track data: track.load(intenalTrackData, trackData); return(track); } else if (trackData.length == 0) { // This is an empty track of unknown type. // It's a safe situation so we just return null: return(null); } // Detect what kind of track this is // based on the properties of the first entry: var firstEntry = trackData[0]; if (firstEntry == null) { // Invalid track. Timeline.loadFailed("invalid track", timeline); } // string, "markup", "speakers" => Dialogue track if (firstEntry is Json.JSValue || firstEntry["markup"] != null || firstEntry["goto"] != null || firstEntry["audio"] != null || firstEntry["speakers"] != null || firstEntry["speaker"] != null || firstEntry["options"] != null) { // If it contained goto, then we actually have a block of options in one slide. if (firstEntry["goto"] != null) { JSIndexedArray td = new JSIndexedArray(); JSObject firstSlide = new JSObject(); firstSlide["options"] = trackData; td.push(firstSlide); trackData = td; } // Dialogue track. track = new DialogueTrack(); // "selector","style" => Style track } else if (firstEntry["selector"] != null || firstEntry["style"] != null || firstEntry["animation"] != null) { // Style track. track = new StyleTrack(); } else if (firstEntry["wait-at"] != null) { // Cue track. track = new CueTrack(); } else { // Fail otherwise: Timeline.loadFailed("Unable to recognise this as a track.", timeline); } track.timeline = timeline; // Load it now: track.load(trackData, null); return(track); }