private IEnumerator UpdateCoroutine() { // Update MovementComponent _mc.Update(); // Add Point to Trajectory, removing the oldest point if (_currentFrame % FramesPerPoint == 0) { this._trajectory.points.Add( new Trajectory.Point( new Vector2(this._model.position.x, this._model.position.z), this._model.rotation ) ); this._trajectory.points.RemoveAt(0); // Reset current Frame _currentFrame = 0; } _currentFrame++; // Load new Animation.Clip if (_ac.IsOver()) { // Find and load next Animation.Clip Trajectory.Snippet snippet = GetCurrentSnippet(); _ac.LoadClip(this._rc.QueryClip(snippet)); } // Play Animation.Frame _ac.Step(); yield return(null); }
public void DrawPath(Trajectory.Snippet snippet) { GameObject.Destroy(_path); _path = new GameObject(); _path.name = "Path"; _path.transform.position = new Vector3(this._model.position.x, 0, this._model.position.z); _path.transform.rotation = Quaternion.Euler(0, this._model.rotation.eulerAngles.y, 0); foreach (Trajectory.Point point in snippet.points) { CreateDot( new Vector3(point.position.x, 0, point.position.y), DotScale, _path.transform, Color.red ); } }
public void DrawAlternativePath(Trajectory.Snippet snippet, int offset, float weight) { GameObject.Destroy(_altPathArray[offset]); _altPathArray[offset] = new GameObject(); _altPathArray[offset].name = "Path " + offset; _altPathArray[offset].transform.parent = _altPaths.transform; _altPathArray[offset].transform.position = new Vector3(this._model.position.x, 0, this._model.position.z); _altPathArray[offset].transform.rotation = Quaternion.Euler(0, this._model.rotation.eulerAngles.y, 0); foreach (Trajectory.Point point in snippet.points) { CreateDot( new Vector3(point.position.x, 0, point.position.y), AlternativeDotScale, _altPathArray[offset].transform, Color.green ); } }
public Animation.Clip QueryClip(Trajectory.Snippet currentSnippet) { // 1. Reduce cooldowns (after the previous Clip has finished) ReduceCooldowns(); // 2. Check if the next Clip is fitting (or the first one, if we reach the end) // The next Clip is NOT necesserily the product of the next Feature this._currentFeature = (this._currentFeature + SalamanderController.FeatureStep) % this._anim[this._currentAnimation].featureList.Count; this._maxTrajectoryDiff = currentSnippet.CalcDiff(this._anim[this._currentAnimation].featureList[this._currentFeature].snippet); if (this._maxTrajectoryDiff > SalamanderController.RecalculationThreshold) { (this._currentAnimation, this._currentFeature) = QueryFeature(currentSnippet); //Debug.Log("Recalculating"); //Debug.Log("File: " + this._currentAnimation + " Clip: " + this._currentFeature); } else { //Debug.Log("Not Recalculating"); } // 3. Construct the Clip, blend it with the current one and return it Animation.Clip nextClip = new Animation.Clip( this._anim[this._currentAnimation].frameList.GetRange( this._anim[this._currentAnimation].featureList[this._currentFeature].frameNum + SalamanderController.FramesPerPoint, SalamanderController.FramesPerPoint * (SalamanderController.FeaturePoints + SalamanderController.ClipBlendPoints) ) ); nextClip.BlendWith(_currentClip); _currentClip = nextClip; // 4. Put the current Feature on cooldown PutOnCooldown(this._anim[this._currentAnimation].featureList[this._currentFeature]); return(nextClip); }
public Feature(int frameNum, Trajectory.Snippet snippet, Pose pose) { this.frameNum = frameNum; this.snippet = snippet; this.pose = pose; }
private (int, int) QueryFeature(Trajectory.Snippet currentSnippet) { List <CandidateFeature> candidateFeatures = new List <CandidateFeature>(); Tuple <float, CandidateFeature> winnerFeature = new Tuple <float, CandidateFeature>(Mathf.Infinity, null); Pose currentPose = _anim[_currentAnimation].featureList[_currentFeature].pose; float maxPosePositionDiff = 0; float maxPoseVelocityDiff = 0; // Draw current snippet _fc?.DrawPath(currentSnippet); // Initialize candidate features to the next clip for (int i = 0; i < SalamanderController.CandidateFramesSize; i++) { candidateFeatures.Add(new CandidateFeature( _anim[_currentAnimation].featureList[_currentFeature], _maxTrajectoryDiff, _currentAnimation, _currentFeature) ); } // 1. Find the Clips with the most fitting Trajectories for (int i = 0; i < _anim.Count; i++) { for (int j = 0; j < _anim[i].featureList.Count; j++) { Feature feature = _anim[i].featureList[j]; // Consider only active Frames (not on cooldown) if (feature.cooldownTimer == 0) { // A. Add candidate Feature to the best candidates list float diff = currentSnippet.CalcDiff(feature.snippet); // Experiment // If the Trajectory is good enough to consider if (diff <= _maxTrajectoryDiff) { // Remove worst candidate candidateFeatures.RemoveAt(candidateFeatures.Count - 1); _maxTrajectoryDiff = candidateFeatures[candidateFeatures.Count - 1].trajectoryDiff; // Add candidate to the right position for (int k = 0; k < candidateFeatures.Count; k++) { if (candidateFeatures[k].trajectoryDiff > diff) { candidateFeatures.Insert(k, new CandidateFeature(feature, diff, i, j)); break; } } } /* * if (diff <= this._maxTrajectoryDiff) * { * CandidateFeature candidateFeature = new CandidateFeature( * feature, diff, i, j * ); * candidateFeatures.Add(candidateFeature); * } * * // B. Sort candidates based on their diff * candidateFeatures.Sort( * (firstObj, secondObj) => * { * return firstObj.trajectoryDiff.CompareTo(secondObj.trajectoryDiff); * } * ); */ } } } //// C. Keep only a predefined number of best candidates //if (candidateFeatures.Count <= 0) //{ // Debug.LogError("Unable to find any Animation Frame to transition to"); // return (0, 0); //} //else if (candidateFeatures.Count > SalamanderController.CandidateFramesSize) //{ // candidateFeatures.RemoveRange(SalamanderController.CandidateFramesSize, candidateFeatures.Count - SalamanderController.CandidateFramesSize); //} // 2. Compute the difference in Pose for each Clip (position and velocity) for (int i = 0; i < candidateFeatures.Count; i++) { (float posePositionDiff, float poseVelocityDiff) = currentPose.CalcDiff(candidateFeatures[i].feature.pose); candidateFeatures[i].posePositionDiff = posePositionDiff; candidateFeatures[i].poseVelocityDiff = poseVelocityDiff; // Keep the maximum values of the differences, in order to normalise maxPosePositionDiff = posePositionDiff > maxPosePositionDiff ? posePositionDiff : maxPosePositionDiff; maxPoseVelocityDiff = poseVelocityDiff > maxPoseVelocityDiff ? poseVelocityDiff : maxPoseVelocityDiff; // Draw all alternative paths //this._fc.DrawAlternativePath(candidateFeatures[i].feature.snippet, i, candidateFeatures[i].trajectoryDiff); } // 3. Normalize and add differences for (int i = 0; i < candidateFeatures.Count; i++) { if (maxPosePositionDiff > 0) { candidateFeatures[i].posePositionDiff /= maxPosePositionDiff; } if (maxPoseVelocityDiff > 0) { candidateFeatures[i].poseVelocityDiff /= maxPoseVelocityDiff; } float totalPostDiff = candidateFeatures[i].posePositionDiff + candidateFeatures[i].poseVelocityDiff; winnerFeature = winnerFeature.Item1 > totalPostDiff ? new Tuple <float, CandidateFeature>(totalPostDiff, candidateFeatures[i]) : winnerFeature; } //Debug.Log("Playing animation from \"" + this._anim[winnerFeature.Item2.animationNum].animationName + "\""); // Draw picked animation's path this._fc?.DrawAlternativePath(winnerFeature.Item2.feature.snippet, 1, winnerFeature.Item2.trajectoryDiff); // 4. Return the Feature's index return(winnerFeature.Item2.animationNum, winnerFeature.Item2.clipNum); }
private (int, int) QueryFeature(Trajectory.Snippet currentSnippet) { List <CandidateFeature> candidateFeatures = new List <CandidateFeature>(); Tuple <float, CandidateFeature> winnerFeature = new Tuple <float, CandidateFeature>(Mathf.Infinity, null); Pose currentPose = this._anim[this._currentAnimation].featureList[this._currentFeature].pose; float maxPosePositionDiff = 0; float maxPoseVelocityDiff = 0; // TODO remove this._fc.DrawPath(currentSnippet); // 1. Find the Clips with the most fitting Trajectories for (int i = 0; i < this._anim.Count; i++) { for (int j = 0; j < this._anim[i].featureList.Count; j++) { Feature feature = this._anim[i].featureList[j]; // Consider only active Frames (not on cooldown) if (feature.cooldownTimer == 0) { // A. Add candidate Feature to the best candidates list float diff = currentSnippet.CalcDiff(feature.snippet); //Debug.Log("diff: " + diff); if (diff < CharacterController.MaxTrajectoryDiff) { CandidateFeature candidateFeature = new CandidateFeature( feature, diff, i, j ); candidateFeatures.Add(candidateFeature); } // B. Sort candidates based on their diff candidateFeatures.Sort( (firstObj, secondObj) => { return(firstObj.trajectoryDiff.CompareTo(secondObj.trajectoryDiff)); } ); // C. Keep only a predefined number of best candidates if (candidateFeatures.Count <= 0) { Debug.LogError("Unable to find any Animation Frame to transition to"); return(0, 0); } else if (candidateFeatures.Count > CharacterController.CandidateFramesSize) { candidateFeatures.RemoveRange(CharacterController.CandidateFramesSize, candidateFeatures.Count - CharacterController.CandidateFramesSize); } } } } // 2. Compute the difference in Pose for each Clip (position and velocity) for (int i = 0; i < candidateFeatures.Count; i++) { (float posePositionDiff, float poseVelocityDiff) = currentPose.CalcDiff(candidateFeatures[i].feature.pose); candidateFeatures[i].posePositionDiff = posePositionDiff; candidateFeatures[i].poseVelocityDiff = poseVelocityDiff; // Keep the maximum values of the differences, in order to normalise maxPosePositionDiff = posePositionDiff > maxPosePositionDiff ? posePositionDiff : maxPosePositionDiff; maxPoseVelocityDiff = poseVelocityDiff > maxPoseVelocityDiff ? poseVelocityDiff : maxPoseVelocityDiff; // TODO remove //this._fc.DrawAlternativePath(candidateFeatures[i].feature.snippet, i, candidateFeatures[i].trajectoryDiff); } // 3. Normalize and add differences for (int i = 0; i < candidateFeatures.Count; i++) { candidateFeatures[i].posePositionDiff /= maxPosePositionDiff; candidateFeatures[i].poseVelocityDiff /= maxPoseVelocityDiff; float totalPostDiff = candidateFeatures[i].posePositionDiff + candidateFeatures[i].poseVelocityDiff; winnerFeature = winnerFeature.Item1 > totalPostDiff ? new Tuple <float, CandidateFeature>(totalPostDiff, candidateFeatures[i]) : winnerFeature; } Debug.Log("Starting animation: " + this._anim[winnerFeature.Item2.animationNum].animationName); // TODO remove this._fc.DrawAlternativePath(winnerFeature.Item2.feature.snippet, 1, winnerFeature.Item2.trajectoryDiff); // 4. Return the Feature's index return(winnerFeature.Item2.animationNum, winnerFeature.Item2.clipNum); }