forked from cgvr/DeltaVR
complete animating mouth scale based on amplitude timeline
This commit is contained in:
@@ -196,15 +196,20 @@ MonoBehaviour:
|
|||||||
m_Script: {fileID: 11500000, guid: 8a215290e2c2e1d43983b61cb160e241, type: 3}
|
m_Script: {fileID: 11500000, guid: 8a215290e2c2e1d43983b61cb160e241, type: 3}
|
||||||
m_Name:
|
m_Name:
|
||||||
m_EditorClassIdentifier:
|
m_EditorClassIdentifier:
|
||||||
mouthTransform: {fileID: 5759406807219530703}
|
voicelinesFolder: CharacterVoicelines
|
||||||
mouthScalingMultiplier: 2.5
|
characterSpecificFolder: Chef
|
||||||
mouthMovementDuration: 0.25
|
|
||||||
voiceLineKeys:
|
voiceLineKeys:
|
||||||
- Chef/Ulrich_Serve
|
- Ulrich_Serve
|
||||||
- Chef/Ulrich_Get_Correctly
|
- Ulrich_Get_Correctly
|
||||||
- Chef/Ulrich_Excellent
|
- Ulrich_Excellent
|
||||||
- Chef/Ulrich_Terribly_Sorry_1
|
- Ulrich_Terribly_Sorry_1
|
||||||
- Chef/Ulrich_Enjoy
|
- Ulrich_Enjoy
|
||||||
|
mouth: {fileID: 5759406807219530703}
|
||||||
|
minScaleY: 0.3
|
||||||
|
maxScaleY: 1
|
||||||
|
gain: 30
|
||||||
|
attack: 0.6
|
||||||
|
release: 0.2
|
||||||
fmodWhisperBridge: {fileID: 0}
|
fmodWhisperBridge: {fileID: 0}
|
||||||
notepadText: {fileID: 0}
|
notepadText: {fileID: 0}
|
||||||
notepad: {fileID: 0}
|
notepad: {fileID: 0}
|
||||||
|
|||||||
Binary file not shown.
@@ -335,7 +335,7 @@ public class AudioManager : MonoBehaviour
|
|||||||
}
|
}
|
||||||
|
|
||||||
instance.start();
|
instance.start();
|
||||||
// instance.release();
|
//instance.release();
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,9 @@ public abstract class NPCController : MonoBehaviour
|
|||||||
{
|
{
|
||||||
protected Transform playerTransform;
|
protected Transform playerTransform;
|
||||||
|
|
||||||
|
[Header("Voiceline Amplitude Timeline Config")]
|
||||||
|
public string voicelinesFolder = "CharacterVoicelines";
|
||||||
|
public string characterSpecificFolder;
|
||||||
public string[] voiceLineKeys;
|
public string[] voiceLineKeys;
|
||||||
|
|
||||||
|
|
||||||
@@ -20,17 +23,14 @@ public abstract class NPCController : MonoBehaviour
|
|||||||
public float attack = 0.6f; // faster opening
|
public float attack = 0.6f; // faster opening
|
||||||
public float release = 0.2f; // slower closing
|
public float release = 0.2f; // slower closing
|
||||||
|
|
||||||
[Header("Timeline Folder (StreamingAssets recommended)")]
|
|
||||||
public string timelineFolder = "CharacterVoicelines";
|
|
||||||
|
|
||||||
private float[] rmsCurve;
|
private float[] rmsCurve;
|
||||||
private EventInstance currentVoicelineEvent;
|
private EventInstance currentVoicelineEvent;
|
||||||
private bool isSpeaking;
|
private bool isSpeaking;
|
||||||
private float smoothed;
|
private float smoothed;
|
||||||
|
|
||||||
private int sampleRate = 50; // RMS samples per second (20ms windows)
|
|
||||||
// If you change RMS window in Python, update this
|
// If you change RMS window in Python, update this
|
||||||
|
private const float FRAME_DURATION = 0.02f;
|
||||||
|
private float sampleRate = 1f / FRAME_DURATION; // RMS samples per second (20ms windows)
|
||||||
|
|
||||||
|
|
||||||
// Start is called before the first frame update
|
// Start is called before the first frame update
|
||||||
@@ -62,7 +62,6 @@ public abstract class NPCController : MonoBehaviour
|
|||||||
|
|
||||||
private void AnimateMouth()
|
private void AnimateMouth()
|
||||||
{
|
{
|
||||||
|
|
||||||
// get FMOD timeline position
|
// get FMOD timeline position
|
||||||
currentVoicelineEvent.getTimelinePosition(out int ms);
|
currentVoicelineEvent.getTimelinePosition(out int ms);
|
||||||
float time = ms / 1000f;
|
float time = ms / 1000f;
|
||||||
@@ -88,6 +87,23 @@ public abstract class NPCController : MonoBehaviour
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void StopSpeaking()
|
||||||
|
{
|
||||||
|
isSpeaking = false;
|
||||||
|
smoothed = minScaleY;
|
||||||
|
|
||||||
|
if (mouth != null)
|
||||||
|
{
|
||||||
|
var scale = mouth.localScale;
|
||||||
|
scale.y = minScaleY;
|
||||||
|
mouth.localScale = scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentVoicelineEvent.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private void OnTriggerEnter(Collider other)
|
private void OnTriggerEnter(Collider other)
|
||||||
{
|
{
|
||||||
if (other.gameObject.tag == "Player Head")
|
if (other.gameObject.tag == "Player Head")
|
||||||
@@ -120,45 +136,28 @@ public abstract class NPCController : MonoBehaviour
|
|||||||
}
|
}
|
||||||
|
|
||||||
string key = voiceLineKeys[voiceLineId];
|
string key = voiceLineKeys[voiceLineId];
|
||||||
|
|
||||||
LoadCurve(key); // load RMS data
|
LoadCurve(key); // load RMS data
|
||||||
Debug.Log("loaded timeline curve");
|
|
||||||
|
|
||||||
currentVoicelineEvent = AudioManager.Instance.PlayDialogue(key, gameObject);
|
|
||||||
|
|
||||||
|
currentVoicelineEvent = AudioManager.Instance.PlayDialogue(characterSpecificFolder + "/" + key, gameObject);
|
||||||
if (!currentVoicelineEvent.isValid())
|
if (!currentVoicelineEvent.isValid())
|
||||||
{
|
{
|
||||||
Debug.LogError("Failed to start dialogue event.");
|
Debug.LogError("Failed to start dialogue event.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
isSpeaking = true;
|
||||||
//isSpeaking = true;
|
|
||||||
|
|
||||||
// Stop mouth on end
|
// Stop mouth on end
|
||||||
currentVoicelineEvent.setCallback((type, inst, param) =>
|
float voicelineDuration = rmsCurve.Length * FRAME_DURATION;
|
||||||
{
|
Invoke(nameof(StopSpeaking), voicelineDuration);
|
||||||
if (type == EVENT_CALLBACK_TYPE.STOPPED)
|
|
||||||
{
|
|
||||||
isSpeaking = false;
|
|
||||||
smoothed = minScaleY;
|
|
||||||
|
|
||||||
if (mouth != null)
|
|
||||||
{
|
|
||||||
Vector3 s = mouth.localScale;
|
|
||||||
s.y = minScaleY;
|
|
||||||
mouth.localScale = s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return FMOD.RESULT.OK;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------
|
|
||||||
// Load RMS Timeline (.txt)
|
// ---------------------------
|
||||||
// ---------------------------
|
// Load RMS Timeline (.txt)
|
||||||
|
// ---------------------------
|
||||||
private void LoadCurve(string key)
|
private void LoadCurve(string key)
|
||||||
{
|
{
|
||||||
string folderPath = Path.Combine(Application.streamingAssetsPath, timelineFolder);
|
string folderPath = Path.Combine(Application.streamingAssetsPath, voicelinesFolder, characterSpecificFolder);
|
||||||
string filePath = Path.Combine(folderPath, key + ".txt");
|
string filePath = Path.Combine(folderPath, key + ".txt");
|
||||||
|
|
||||||
if (!File.Exists(filePath))
|
if (!File.Exists(filePath))
|
||||||
|
|||||||
Reference in New Issue
Block a user