It is another unity watson sdk question.
I solved the first talk issue by just make fake object again..
Here is another thing.
In my chatbot I can see sequential text if there are same intents.
What line do I have to change or add to make it happen?
(another question: What line do I have to change or add to bring 'jump to' method in my unity?
using IBM.Watson.DeveloperCloud.Services.Conversation.v1;
using IBM.Watson.DeveloperCloud.Utilities;
using System;
using System.Collections.Generic;
using UnityEngine;
class Watson : MonoBehaviour{
static Credentials credentials;
static Conversation _conversation;
void Start()
{
credentials = new Credentials("xx-xx-xx-xx-xx", "xx", "https://gateway.watsonplatform.net/conversation/api");
// credentials.Url = "";
_conversation = new Conversation(credentials);
}
static Action<string, ManagerChat.Feel, bool> Act;
public static void GoMessage(string _str,Action<string, ManagerChat.Feel,bool> _act)
{
if (!_conversation.Message(OnMessage, "xx-xx-xx-xx-xx", _str))
Debug.Log("ExampleConversation Failed to message!");
Act = _act;
}
static bool GetIntent(Dictionary<string, object> respDict)
{
object intents;
respDict.TryGetValue("intents", out intents);
object intentString = new object();
object confidenceString = new object();
foreach (var intentObj in (intents as List<object>))
{
Dictionary<string, object> intentDict = intentObj as Dictionary<string, object>;
intentDict.TryGetValue("intent", out intentString);
intentDict.TryGetValue("confidence", out confidenceString);
}
string str = intentString as string;
if (str == "6사용자_마무리")
return true;
return false;
}
static string GetOutput(Dictionary<string, object> respDict)
{
object outputs;
respDict.TryGetValue("output", out outputs);
object output;
(outputs as Dictionary<string, object>).TryGetValue("text", out output);
string var = (output as List<object>)[0] as string;
return var;
}
static ManagerChat.Feel GetEntities(Dictionary<string, object> respDict)
{
object entities;
respDict.TryGetValue("entities", out entities);
List<object> entitieList = (entities as List<object>);
if(entitieList.Count == 0)
{
return ManagerChat.Feel.Normal;
}
else
{
object entitie;
(entitieList[0] as Dictionary<string, object>).TryGetValue("value", out entitie);
ManagerChat.Feel feel = ManagerChat.Feel.NONE;
string str = entitie as string;
switch (str)
{
case "Happy":
feel = ManagerChat.Feel.Happy;
break;
case "Expect":
feel = ManagerChat.Feel.Expect;
break;
case "Born":
feel = ManagerChat.Feel.Born;
break;
case "Sad":
feel = ManagerChat.Feel.Sad;
break;
case "Surprise":
feel = ManagerChat.Feel.Surprise;
break;
case "Normal":
feel = ManagerChat.Feel.Normal;
break;
default:
break;
}
return feel;
}
}
static void OnMessage(object resp, string data)
{
Dictionary<string, object> respDict = resp as Dictionary<string, object>;
bool flag = (GetIntent(respDict));
string output = (GetOutput(respDict));
ManagerChat.Feel feel = GetEntities(respDict);
// Debug.Log(resp);
// Debug.Log(data);
Act(output,feel, flag);
}
}
I don't quite follow your question about sequential texts, but I think you may be trying to do more in your application code than is needed.
I'll just give a high level pattern about how to use the sdk and see if that clears things up. Im no Unity dev, but the pattern is the same no matter the language.
You only need to give Watson the user's input text, and the existing context variables, most importantly system context, but any custom context you have created is valuable as well.
Then, Watson will return an output.text object, which you post to the user, and Watson also returns an updated system context.
Next, the user types something new, your app grabs that text, passes it to Watson along with the context object he returned last time, and the process repeats.
You should not need to do anything in your app code for jump tos, sequential text, etc., as thats all handled by Watson.
The only feature I can think of for the sequential piece is the response variations for a single dialog node. This feature means that if a user visits a specific dialog node multiple times, you will give different reponses, either in seuqential order, or random order, if there are multiple. It does require more than one input from the user, navigating to the same node multiple times. This is to give your bot some variation, most valuable for common inputs like 'hello', 'googbye', 'thanks', etc.
Related
I am currently using Azure notification hub(FCM) to send one-one notification to user as well as notification to group of users by using tags(5000 - 10000 users at a time) .
Now while sending notification to group , I want some personalization like:
Hi ABC<$(firstname of user1)>, here is new AAAAA for you today.
Hi XYZ<$(firstname of user2)>, here is new AAAAA for you today.
.
.
Hi ZZZ<$(firstname of user5000)>, here is new AAAAA for you today.
I read that this is possible by using push variables with native registartion /installation sdk.
Ref:https://azure.microsoft.com/en-in/blog/updates-from-notification-hubs-independent-nuget-installation-model-pmt-and-more/
But I could not find any option in registration/installation Java SDK to set these values .
Registration registration = new FcmRegistration(id, token);
registration.getTags().add(tagname);
hub.createRegistration(registration);
Installation installation = new Installation(name);
installation.setPushChannel(token);
installation.setPlatform(NotificationPlatform.Gcm);
installation.addTag(tagname);
hub.createOrUpdateInstallation(installation);
Any help is really appreciated , otherwise for group notification I have to send notification for each user via iteration and that defeats benefit of using tags and getting the job done in just 1 hub API call.
You are correct - this is exactly what ANH templates are for. You can read this blog post about them for some background knowledge. Essentially, once you've created a template you can do a template send operation that provides just the values that need to be injected. i.e. Your Installation record will have set the appropriate body:
"Hi $(firstname), here is new $(subject) for you today."
and your send operation provides the values to inject:
{
"firstname": "ABC",
"subject": "AAAAA"
}
Also, make sure to specify the correct tags to scope the audience, in this case something like "ABC" to specify the user, and "new-daily" to specify which templates should be used.
Another trick, you can skip a layer of tag management and send fewer requests by embedding the $(firstname) in the template itself.
"Hi ABC, here is new $(subject) for you today."
Because templates are stored per device, each device can have a separate name embedded in it, reducing the number of tags you need to tinker with. This would make the body you send just:
{
"subject": "AAAAA"
}
and you only need to scope with the tag "new-daily".
Looks like you're on the right track with templating. When you embed an expression into surrounding text, you're effectively doing concatenation, which requires the expression to be surrounded in { }. See documentation about template expression language using Azure Notification Hubs where it states "when using concatenation, expressions must be wrapped in curly brackets."
In your case, I think you want something along the lines of:
...
{"title":"{'Seattle Kraken vs. ' + $(opponent) + ' starting soon'}",...}
...
Thanks a lot I got it working by extending the API classes on my own in following manner as per the blog suggested.
package com.springbootazure.controller;
import java.util.Map;
import org.apache.commons.collections.map.HashedMap;
import com.google.gson.GsonBuilder;
import com.windowsazure.messaging.FcmRegistration;
public class PushRegistration extends FcmRegistration {
private static final String FCM_NATIVE_REGISTRATION1 = "<?xml version=\"1.0\" encoding=\"utf-8\"?><entry xmlns=\"http://www.w3.org/2005/Atom\"><content type=\"application/xml\"><GcmRegistrationDescription xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"http://schemas.microsoft.com/netservices/2010/10/servicebus/connect\">";
private static final String FCM_NATIVE_REGISTRATION2 = "<GcmRegistrationId>";
private static final String FCM_NATIVE_REGISTRATION3 = "</GcmRegistrationId></GcmRegistrationDescription></content></entry>";
private Map<String, String> pushVariables = new HashedMap();
public PushRegistration() {
super();
}
public PushRegistration(String registrationId,
String fcmRegistrationId, Map<String, String> pushVariables) {
super(registrationId, fcmRegistrationId);
this.pushVariables = pushVariables;
}
public PushRegistration(String fcmRegistrationId, Map<String, String> pushVariables) {
super(fcmRegistrationId);
this.pushVariables = pushVariables;
}
#Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result
+ ((pushVariables == null) ? 0 : pushVariables.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!super.equals(obj)) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
PushRegistration other = (PushRegistration) obj;
if (pushVariables == null) {
if (other.pushVariables != null) {
return false;
}
} else if (!pushVariables.equals(other.pushVariables)) {
return false;
}
return true;
}
protected String getPushVariablesXml() {
StringBuilder buf = new StringBuilder();
if (!tags.isEmpty()) {
buf.append("<PushVariables>");
buf.append(new GsonBuilder().disableHtmlEscaping().create().toJson(pushVariables));
buf.append("</PushVariables>");
}
return buf.toString();
}
#Override
public String getXml() {
return FCM_NATIVE_REGISTRATION1 +
getTagsXml() +
getPushVariablesXml() +
FCM_NATIVE_REGISTRATION2 +
fcmRegistrationId +
FCM_NATIVE_REGISTRATION3;
}
}
And afterwards , register a token using :
Map<String, String> pushVariables = new HashMap<>();
pushVariables.put("firstname", "Gaurav");
pushVariables.put("lastname", "Aggarwal");
Registration registration = new PushRegistration(name, token, pushVariables);
if (registration == null) {
registration = new FcmRegistration(name, token);
}
registration.getTags().add(tagname);
registration.getTags().add(category);
hub.createRegistration(registration);
And then send notification like:
Notification n = Notification.createFcmNotifiation("{\n" +
" \"notification\" : {\n" +
" \"body\" : \"{ $(firstname) + ' starting soon'}\",\n" +
" \"title\" : \"test title\"\n" +
" }\n" +
"}");
hub.sendNotification(n, tagname);
So when writing UI in GTK it's generally preferrable to handle reading of files, etc. in an Async Method. things such as listboxes, are generally bound to a ListModel, the items in the ListBox updated in accordance with the items_changed signal.
So if I have some class, that implements ListModel, and has an add function, and some FileReader that holds a reference to said ListModel, and call add from an async function, how do i make that in essence triggering the items_changed and having GTK update accordingly?
I've tried list.items_changed.connect(message("Items changed!")); but it never triggers.
I saw this: How can one update GTK+ UI in Vala from a long operation without blocking the UI
but in this example, it's just the button label that is changed, no signal is actually triggered.
EDIT: (Codesample added at the request of #Michael Gratton
//Disclaimer: everything here is still very much a work in progress, and will, as soon as I'm confident that what I have is not total crap, be released under some GPL or other open license.
//Note: for the sake of readability, I adopted the C# naming convention for interfaces, namely, putting a capital 'I' in front of them, a decision i do not feel quite as confident in as I did earlier.
//Note: the calls to message(..) was put in here to help debugging
public class AsyncFileContext : Object{
private int64 offset;
private bool start_read;
private bool read_to_end;
private Factories.IVCardFactory factory;
private File file;
private FileMonitor monitor;
private Gee.Set<IVCard> vcard_buffer;
private IObservableSet<IVCard> _vCards;
public IObservableSet<IVCard> vCards {
owned get{
return this._vCards;
}
}
construct{
//We want to start fileops at the beginning of the file
this.offset = (int64)0;
this.start_read = true;
this.read_to_end = false;
this.vcard_buffer = new Gee.HashSet<IVCard>();
this.factory = new Factories.GenericVCardFactory();
}
public void add_vcard(IVCard card){
//TODO: implement
}
public AsyncFileContext(IObservableSet<IVCard> vcards, string path){
this._vCards = vcards;
this._vCards = IObservableSet.wrap_set<IVCard>(new Gee.HashSet<IVCard>());
this.file = File.new_for_path(path);
this.monitor = file.monitor_file(FileMonitorFlags.NONE, null);
message("1");
//TODO: add connect
this.monitor.changed.connect((file, otherfile, event) => {
if(event != FileMonitorEvent.DELETED){
bool changes_done = event == FileMonitorEvent.CHANGES_DONE_HINT;
Idle.add(() => {
read_file_async.begin(changes_done);
return false;
});
}
});
message("2");
//We don't know that changes are done yet
//TODO: Consider carefully how you want this to work when it is NOT called from an event
Idle.add(() => {
read_file_async.begin(false);
return false;
});
}
//Changes done should only be true if the FileMonitorEvent that triggers the call was CHANGES_DONE_HINT
private async void read_file_async(bool changes_done) throws IOError{
if(!this.start_read){
return;
}
this.start_read = false;
var dis = new DataInputStream(yield file.read_async());
message("3");
//If we've been reading this file, and there's then a change, we assume we need to continue where we let off
//TODO: assert that the offset isn't at the very end of the file, if so reset to 0 so we can reread the file
if(offset > 0){
dis.seek(offset, SeekType.SET);
}
string line;
int vcards_added = 0;
while((line = yield dis.read_line_async()) != null){
message("position: %s".printf(dis.tell().to_string()));
this.offset = dis.tell();
message("4");
message(line);
//if the line is empty, we want to jump to next line, and ignore the input here entirely
if(line.chomp().chug() == ""){
continue;
}
this.factory.add_line(line);
if(factory.vcard_ready){
message("creating...");
this.vcard_buffer.add(factory.create());
vcards_added++;
//If we've read-in and created an entire vcard, it's time to yield
message("Yielding...");
Idle.add(() => {
_vCards.add_all(vcard_buffer);
vcard_buffer.remove_all(_vCards);
return false;
});
Idle.add(read_file_async.callback);
yield;
message("Resuming");
}
}
//IF we expect there will be no more writing, or if we expect that we read ALL the vcards, and did not add any, it's time to go back and read through the whole thing again.
if(changes_done){ //|| vcards_added == 0){
this.offset = 0;
}
this.start_read = true;
}
}
//The main idea in this class is to just bind the IObservableCollection's item_added, item_removed and cleared signals to the items_changed of the ListModel. IObservableCollection is a class I have implemented that merely wraps Gee.Collection, it is unittested, and works as intended
public class VCardListModel : ListModel, Object{
private Gee.List<IVCard> vcard_list;
private IObservableCollection<IVCard> vcard_collection;
public VCardListModel(IObservableCollection<IVCard> vcard_collection){
this.vcard_collection = vcard_collection;
this.vcard_list = new Gee.ArrayList<IVCard>.wrap(vcard_collection.to_array());
this.vcard_collection.item_added.connect((vcard) => {
vcard_list.add(vcard);
int pos = vcard_list.index_of(vcard);
items_changed(pos, 0, 1);
});
this.vcard_collection.item_removed.connect((vcard) => {
int pos = vcard_list.index_of(vcard);
vcard_list.remove(vcard);
items_changed(pos, 1, 0);
});
this.vcard_collection.cleared.connect(() => {
items_changed(0, vcard_list.size, 0);
vcard_list.clear();
});
}
public Object? get_item(uint position){
if((vcard_list.size - 1) < position){
return null;
}
return this.vcard_list.get((int)position);
}
public Type get_item_type(){
return Type.from_name("VikingvCardIVCard");
}
public uint get_n_items(){
return (uint)this.vcard_list.size;
}
public Object? get_object(uint position){
return this.get_item((int)position);
}
}
//The IObservableCollection parsed to this classes constructor, is the one from the AsyncFileContext
public class ContactList : Gtk.ListBox{
private ListModel list_model;
public ContactList(IObservableCollection<IVCard> ivcards){
this.list_model = new VCardListModel(ivcards);
bind_model(this.list_model, create_row_func);
list_model.items_changed.connect(() => {
message("Items Changed!");
base.show_all();
});
}
private Gtk.Widget create_row_func(Object item){
return new ContactRow((IVCard)item);
}
}
Heres the way i 'solved' it.
I'm not particularly proud of this solution, but there are a couple of awful things about the Gtk ListBox, one of them being (and this might really be more of a ListModel issue) if the ListBox is bound to a ListModel, the ListBox will NOT be sortable by using the sort method, and to me at least, that is a dealbreaker. I've solved it by making a class which is basically a List wrapper, which has an 'added' signal and a 'remove' signal. Upon adding an element to the list, the added signal is then wired, so it will create a new Row object and add it to the list box. That way, data is control in a manner Similar to ListModel binding. I can not make it work without calling the ShowAll method though.
private IObservableCollection<IVCard> _ivcards;
public IObservableCollection<IVCard> ivcards {
get{
return _ivcards;
}
set{
this._ivcards = value;
foreach(var card in this._ivcards){
base.prepend(new ContactRow(card));
}
this._ivcards.item_added.connect((item) => {
base.add(new ContactRow(item));
base.show_all();
});
base.show_all();
}
}
Even though this is by no means the best code I've come up with, it works very well.
I'm working on a small program that can modify the animation at run time(Such as when you run faster the animation not only play faster but also with larger movement). So i need to get the existing animation, change its value, then send it back.
I found it is interesting that i can set a new curve to the animation, but i can't get access to what i already have. So I either write a file to store my animation curve (as text file for example), or i find someway to read the animation on start up.
I tried to use
AnimationUtility.GetCurveBindings(AnimationCurve);
It worked in my testing, but in some page it says this is a "Editor code", that if i build the project into a standalone program it will not work anymore. Is that true? If so, is there any way to get the curve at run time?
Thanks to the clearify from Benjamin Zach and suggestion from TehMightyPotato
I'd like to keep the idea about modifying the animation at runtime. Because it could adapt to more situations imo.
My idea for now is to write a piece of editor code that can read from the curve in Editor and output all necesseary information about the curve (keyframes) into a text file. Then read that file at runtime and create new curve to overwrite the existing one. I will leave this question open for a few days and check it to see if anyone has a better idea about it.
As said already AnimationUtility belongs to the UnityEditor namespace. This entire namespace is completely stripped of in a build and nothing in it will be available in the final app but only within the Unity Editor.
Store AnimationCurves to file
In order to store all needed information to a file you could have a script for once serializing your specific animation curve(s) in the editor before building using e.g. BinaryFormatter.Serialize. Then later on runtime you can use BinaryFormatter.Deserialize for returning the info list again.
If you wanted it more editable you could as well use e.g. JSON or XML of course
UPDATE: In general Stop using BinaryFormatter!
In the newest Unity versions the Newtonsoft Json.NET package comes already preinstalled so simply rather use JSON
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Unity.Plastic.Newtonsoft.Json;
using UnityEditor;
using UnityEngine;
using Object = UnityEngine.Object;
public class AnimationCurveManager : MonoBehaviour
{
[Serializable]
public sealed class ClipInfo
{
public int ClipInstanceID;
public List<CurveInfo> CurveInfos = new List<CurveInfo>();
// default constructor is sometimes required for (de)serialization
public ClipInfo() { }
public ClipInfo(Object clip, List<CurveInfo> curveInfos)
{
ClipInstanceID = clip.GetInstanceID();
CurveInfos = curveInfos;
}
}
[Serializable]
public sealed class CurveInfo
{
public string PathKey;
public List<KeyFrameInfo> Keys = new List<KeyFrameInfo>();
public WrapMode PreWrapMode;
public WrapMode PostWrapMode;
// default constructor is sometimes required for (de)serialization
public CurveInfo() { }
public CurveInfo(string pathKey, AnimationCurve curve)
{
PathKey = pathKey;
foreach (var keyframe in curve.keys)
{
Keys.Add(new KeyFrameInfo(keyframe));
}
PreWrapMode = curve.preWrapMode;
PostWrapMode = curve.postWrapMode;
}
}
[Serializable]
public sealed class KeyFrameInfo
{
public float Value;
public float InTangent;
public float InWeight;
public float OutTangent;
public float OutWeight;
public float Time;
public WeightedMode WeightedMode;
// default constructor is sometimes required for (de)serialization
public KeyFrameInfo() { }
public KeyFrameInfo(Keyframe keyframe)
{
Value = keyframe.value;
InTangent = keyframe.inTangent;
InWeight = keyframe.inWeight;
OutTangent = keyframe.outTangent;
OutWeight = keyframe.outWeight;
Time = keyframe.time;
WeightedMode = keyframe.weightedMode;
}
}
// I know ... singleton .. but what choices do we have? ;)
private static AnimationCurveManager _instance;
public static AnimationCurveManager Instance
{
get
{
// lazy initialization/instantiation
if (_instance) return _instance;
_instance = FindObjectOfType<AnimationCurveManager>();
if (_instance) return _instance;
_instance = new GameObject("AnimationCurveManager").AddComponent<AnimationCurveManager>();
return _instance;
}
}
// Clips to manage e.g. reference these via the Inspector
public List<AnimationClip> clips = new List<AnimationClip>();
// every animation curve belongs to a specific clip and
// a specific property of a specific component on a specific object
// for making this easier lets simply use a combined string as key
private string CurveKey(string pathToObject, Type type, string propertyName)
{
return $"{pathToObject}:{type.FullName}:{propertyName}";
}
public List<ClipInfo> ClipCurves = new List<ClipInfo>();
private string filePath = Path.Combine(Application.streamingAssetsPath, "AnimationCurves.dat");
private void Awake()
{
if (_instance && _instance != this)
{
Debug.LogWarning("Multiple Instances of AnimationCurveManager! Will ignore this one!", this);
return;
}
_instance = this;
DontDestroyOnLoad(gameObject);
// load infos on runtime
LoadClipCurves();
}
#if UNITY_EDITOR
// Call this from the ContextMenu (or later via editor script)
[ContextMenu("Save Animation Curves")]
private void SaveAnimationCurves()
{
ClipCurves.Clear();
foreach (var clip in clips)
{
var curveInfos = new List<CurveInfo>();
ClipCurves.Add(new ClipInfo(clip, curveInfos));
foreach (var binding in AnimationUtility.GetCurveBindings(clip))
{
var key = CurveKey(binding.path, binding.type, binding.propertyName);
var curve = AnimationUtility.GetEditorCurve(clip, binding);
curveInfos.Add(new CurveInfo(key, curve));
}
}
// create the StreamingAssets folder if it does not exist
try
{
if (!Directory.Exists(Application.streamingAssetsPath))
{
Directory.CreateDirectory(Application.streamingAssetsPath);
}
}
catch (IOException ex)
{
Debug.LogError(ex.Message);
}
// create a new file e.g. AnimationCurves.dat in the StreamingAssets folder
var json = JsonConvert.SerializeObject(ClipCurves);
File.WriteAllText(filePath, json);
AssetDatabase.Refresh();
}
#endif
private void LoadClipCurves()
{
if (!File.Exists(filePath))
{
Debug.LogErrorFormat(this, "File \"{0}\" not found!", filePath);
return;
}
var fileStream = new FileStream(filePath, FileMode.Open);
var json = File.ReadAllText(filePath);
ClipCurves = JsonConvert.DeserializeObject<List<ClipInfo>>(json);
}
// now for getting a specific clip's curves
public AnimationCurve GetCurve(AnimationClip clip, string pathToObject, Type type, string propertyName)
{
// either not loaded yet or error -> try again
if (ClipCurves == null || ClipCurves.Count == 0) LoadClipCurves();
// still null? -> error
if (ClipCurves == null || ClipCurves.Count == 0)
{
Debug.LogError("Apparantly no clipCurves loaded!");
return null;
}
var clipInfo = ClipCurves.FirstOrDefault(ci => ci.ClipInstanceID == clip.GetInstanceID());
// does this clip exist in the dictionary?
if (clipInfo == null)
{
Debug.LogErrorFormat(this, "The clip \"{0}\" was not found in clipCurves!", clip.name);
return null;
}
var key = CurveKey(pathToObject, type, propertyName);
var curveInfo = clipInfo.CurveInfos.FirstOrDefault(c => string.Equals(c.PathKey, key));
// does the curve key exist for the clip?
if (curveInfo == null)
{
Debug.LogErrorFormat(this, "The key \"{0}\" was not found for clip \"{1}\"", key, clip.name);
return null;
}
var keyframes = new Keyframe[curveInfo.Keys.Count];
for (var i = 0; i < curveInfo.Keys.Count; i++)
{
var keyframe = curveInfo.Keys[i];
keyframes[i] = new Keyframe(keyframe.Time, keyframe.Value, keyframe.InTangent, keyframe.OutTangent, keyframe.InWeight, keyframe.OutWeight)
{
weightedMode = keyframe.WeightedMode
};
}
var curve = new AnimationCurve(keyframes)
{
postWrapMode = curveInfo.PostWrapMode,
preWrapMode = curveInfo.PreWrapMode
};
// otherwise finally return the AnimationCurve
return curve;
}
}
Then you can do something like e.e.
AnimationCurve originalCurve = AnimationCurvesManager.Instance.GetCurve(
clip,
"some/relative/GameObject",
typeof<SomeComponnet>,
"somePropertyName"
);
the second parameter pathToObject is an empty string if the property/component is attached to the root object itself. Otherwise it is given in the hierachy path as usual for Unity like e.g. "ChildName/FurtherChildName".
Now you can change the values and assign a new curve on runtime.
Assigning new curve on runtime
On runtime you can use animator.runtimeanimatorController in order to retrieve a RuntimeAnimatorController reference.
It has a property animationClips which returns all AnimationClips assigned to this controller.
You could then use e.g. Linq FirstOrDefault in order to find a specific AnimationClip by name and finally use AnimationClip.SetCurve to assign a new animation curve to a certain component and property.
E.g. something like
// you need those of course
string clipName;
AnimationCurve originalCurve = AnimationCurvesManager.Instance.GetCurve(
clip,
"some/relative/GameObject",
typeof<SomeComponnet>,
"somePropertyName"
);
// TODO
AnimationCurve newCurve = SomeMagic(originalCurve);
// get the animator reference
var animator = animatorObject.GetComponent<Animator>();
// get the runtime Animation controller
var controller = animator.runtimeAnimatorController;
// get all clips
var clips = controller.animationClips;
// find the specific clip by name
// alternatively you could also get this as before using a field and
// reference the according script via the Inspector
var someClip = clips.FirstOrDefault(clip => string.Equals(clipName, clip.name));
// was found?
if(!someClip)
{
Debug.LogWarningFormat(this, "There is no clip called {0}!", clipName);
return;
}
// assign a new curve
someClip.SetCurve("relative/path/to/some/GameObject", typeof(SomeComponnet), "somePropertyName", newCurve);
Note: Typed on smartphone so no warranty! But I hope the idea gets clear...
Also checkout the example in AnimationClip.SetCurve → You might want to use the Animation component instead of an Animator in your specific use case.
I have downloaded the Watson unity SDK and set it up like show in the picture and it works.
My question is how do I add keyword spotting?
I have read this question For Watson's Speech-To-Text Unity SDK, how can you specify keywords?
But I cant for example locate the SendStart function.
The Speech to Text service does not find keywords. To find keywords you would need to take the final text output and send it to the Alchemy Language service. Natural Language Understanding service is still being abstracted into the Watson Unity SDK but will eventually replace Alchemy Language.
private AlchemyAPI m_AlchemyAPI = new AlchemyAPI();
private void FindKeywords(string speechToTextFinalResponse)
{
if (!m_AlchemyAPI.ExtractKeywords(OnExtractKeywords, speechToTextFinalResponse))
Log.Debug("ExampleAlchemyLanguage", "Failed to get keywords.");
}
void OnExtractKeywords(KeywordData keywordData, string data)
{
Log.Debug("ExampleAlchemyLanguage", "GetKeywordsResult: {0}", JsonUtility.ToJson(resp));
}
EDIT 1
Natural Language Understanding has been abstracted in tot he Watson Unity SDK.
NaturalLanguageUnderstanding m_NaturalLanguageUnderstanding = new NaturalLanguageUnderstanding();
private static fsSerializer sm_Serializer = new fsSerializer();
private void FindKeywords(string speechToTextFinalResponse)
{
Parameters parameters = new Parameters()
{
text = speechToTextFinalResponse,
return_analyzed_text = true,
language = "en",
features = new Features()
{
entities = new EntitiesOptions()
{
limit = 50,
sentiment = true,
emotion = true,
},
keywords = new KeywordsOptions()
{
limit = 50,
sentiment = true,
emotion = true
}
}
if (!m_NaturalLanguageUnderstanding.Analyze(OnAnalyze, parameters))
Log.Debug("ExampleNaturalLanguageUnderstanding", "Failed to analyze.");
}
private void OnAnalyze(AnalysisResults resp, string customData)
{
fsData data = null;
sm_Serializer.TrySerialize(resp, out data).AssertSuccess();
Log.Debug("ExampleNaturalLanguageUnderstanding", "AnalysisResults: {0}", data.ToString());
}
EDIT 2
Sorry, I didn't realize Speech To Text had the ability to do keyword spotting. Thanks to Nathan for pointing that out to me! I added this functionality into a future release of Speech to Text in the Unity SDK. It will look like this for the Watson Unity SDK 1.0.0:
void Start()
{
// Create credential and instantiate service
Credentials credentials = new Credentials(_username, _password, _url);
_speechToText = new SpeechToText(credentials);
// Add keywords
List<string> keywords = new List<string>();
keywords.Add("speech");
_speechToText.KeywordsThreshold = 0.5f;
_speechToText.Keywords = keywords.ToArray();
_speechToText.Recognize(_audioClip, HandleOnRecognize);
}
private void HandleOnRecognize(SpeechRecognitionEvent result)
{
if (result != null && result.results.Length > 0)
{
foreach (var res in result.results)
{
foreach (var alt in res.alternatives)
{
string text = alt.transcript;
Log.Debug("ExampleSpeechToText", string.Format("{0} ({1}, {2:0.00})\n", text, res.final ? "Final" : "Interim", alt.confidence));
if (res.final)
_recognizeTested = true;
}
if (res.keywords_result != null && res.keywords_result.keyword != null)
{
foreach (var keyword in res.keywords_result.keyword)
{
Log.Debug("ExampleSpeechToText", "keyword: {0}, confidence: {1}, start time: {2}, end time: {3}", keyword.normalized_text, keyword.confidence, keyword.start_time, keyword.end_time);
}
}
}
}
}
Currently you can find the refactor branch here. This release is a breaking change and has all of the higher level (widgets, config, etc) functionality removed.
I need to recollect some data calling to a method is connecting to a webservice.
problem: Imagine I need to update the content text of a label control according to this remote gathered information. Until all this data is recollected I'm not going to be able to show the label.
desired: I'd like to first show the label with a default text, and as I'm receiving this information I want to update the label content (please, don't take this description as a sucked code, I'm trying to brief my real situation).
I'd like to create an observable sequence of these methods. Nevertheless, these method have not the same signature. For example:
int GetInt() {
return service.GetInt();
}
string GetString() {
return service.GetString();
}
string GetString2 {
return service.GetString2();
}
These methods are not async.
Is it possible to create an observable sequence of these methods?
How could I create it?
Nevertheless, which's the best alternative to achieve my goal?
Creating custom observable sequences can be achieved with the Observable.Create. An example using your requirements is shown below:
private int GetInt()
{
Thread.Sleep(1000);
return 1;
}
private string GetString()
{
Thread.Sleep(1000);
return "Hello";
}
private string GetString2()
{
Thread.Sleep(2000);
return "World!";
}
private IObservable<string> RetrieveContent()
{
return Observable.Create<string>(
observer =>
{
observer.OnNext("Default Text");
int value = GetInt();
observer.OnNext($"Got value {value}. Getting string...");
string string1 = GetString();
observer.OnNext($"Got string {string1}. Getting second string...");
string string2 = GetString2();
observer.OnNext(string2);
observer.OnCompleted();
return Disposable.Empty;
}
);
}
Note how I have emulated network delay by introducing a Thread.Sleep call into each of the GetXXX methods. In order to ensure your UI doesn't hang when subscribing to this observable, you should subscribe as follows:
IDisposable subscription = RetrieveContent()
.SubscribeOn(TaskPoolScheduler.Default)
.ObserveOn(DispatcherScheduler.Current)
.Subscribe(text => Label = text);
This code uses the .SubscribeOn(TaskPoolScheduler.Default) extension method to use a TaskPool thread to start the observable sequence and will be blocked by the calls the Thread.Sleep but, as this is not the UI thread, your UI will remain responsive. Then, to ensure we update the UI on the UI thread, we use the ".ObserveOn(DispatcherScheduler.Current)" to invoke the updates onto the UI thread before setting the (data bound) Label property.
Hope this is what you were looking for, but leave a comment if not and I'll try to help further.
I would look at creating a wrapper class for your service to expose the values as separate observables.
So, start with a service interface:
public interface IService
{
int GetInt();
string GetString();
string GetString2();
}
...and then you write ServiceWrapper:
public class ServiceWrapper : IService
{
private IService service;
private Subject<int> subjectGetInt = new Subject<int>();
private Subject<string> subjectGetString = new Subject<string>();
private Subject<string> subjectGetString2 = new Subject<string>();
public ServiceWrapper(IService service)
{
this.service = service;
}
public int GetInt()
{
var value = service.GetInt();
this.subjectGetInt.OnNext(value);
return value;
}
public IObservable<int> GetInts()
{
return this.subjectGetInt.AsObservable();
}
public string GetString()
{
var value = service.GetString();
this.subjectGetString.OnNext(value);
return value;
}
public IObservable<string> GetStrings()
{
return this.subjectGetString.AsObservable();
}
public string GetString2()
{
var value = service.GetString2();
this.subjectGetString2.OnNext(value);
return value;
}
public IObservable<string> GetString2s()
{
return this.subjectGetString2.AsObservable();
}
}
Now, assuming that you current service is called Service, you would write this code to set things up:
IService service = new Service();
ServiceWrapper wrapped = new ServiceWrapper(service); // Still an `IService`
var subscription =
Observable
.Merge(
wrapped.GetInts().Select(x => x.ToString()),
wrapped.GetStrings(),
wrapped.GetString2s())
.Subscribe(x => label.Text = x);
IService wrappedService = wrapped;
Now pass wrappedService instead of service to your code. It's still calling the underlying service code so no need for a re-write, yet you still are getting the observables that you want.
This is effectively a gang of four decorator pattern.