Goals Showcase
This room showcases various ways in which Goals can be utilized to create a variety of unique and interactive experiences with Inworld characters in Unity.
Parameters
The Parameters showcase is an example of how trigger parameters can be utilized to inject realtime information from the Unity client to an Inworld character action at runtime.
In this case the client sends a phrase input by the player in the Unity Playground as a trigger parameter to the Rhyme Bot.
public void SendTrigger()
{
m_InworldCharacter.SendTrigger("rhyme", true, new Dictionary<string, string>()
{
{"phrase", m_InputField.text}
});
}
This will activate the "rhyme"
goal and trigger the "make a rhyme with {{p.phrase}}"
instruction.
goals:
- name: "rhyme"
repeatable: True
activation:
trigger: "rhyme"
actions:
- instruction: "make a rhyme with {{p.phrase}}"
This will cause the Rhyme Bot to make up a rhyme with the given phrase. For example, if you input the phrase "apples"
Rhyme Bot will come up with a rhyme about apples:
For more information on utilizing Parameters see Customizable parameters changing action in runtime.
Enable Goals with Intents
The Enable Goals with Intents showcase is multi-feature example showing how goals can be enabled/disabled with Intents by using Character Mutations.
The demo requires that you first say 'hello' to Goals Bot, this will activate the greeting
intent which will enable the left
and right
goals.
intents:
- name: "greeting"
training_phrases:
- "Hello"
- "Hi"
- "Hey"
- "Hi there"
- "Greetings"
- "Howdy"
- "Hey there"
- "Hiya"
- "Sup"
goals:
- name: "greeting"
activation:
intent: "greeting"
actions:
- instruction: "greet {player} and ask if they would like you to walk left or right."
character_changes:
enable_goals:
- "left"
- "right"
disable_goals:
- "left_disabled"
- "right_disabled"
From here you can instruct Goals Bot to walk left or right utilizing the left
and right
intents.
intents:
- name: "left"
training_phrases:
- "left"
- "walk left"
- "go left"
- "head left"
- name: "right"
training_phrases:
- "right"
- "walk right"
- "go right"
- "head right"
goals:
- name: "left"
enabled_by_default: false
repeatable: true
activation:
intent: "left"
actions:
- say_verbatim: "I will head left."
character_changes:
enable_goals:
- "right"
- "left_disabled"
disable_goals:
- "left"
- "right_disabled"
- name: "right"
enabled_by_default: false
repeatable: true
activation:
intent: "right"
actions:
- say_verbatim: "I will head right."
character_changes:
enable_goals:
- "left"
- "right_disabled"
disable_goals:
- "right"
- "left_disabled"
- name: "left_disabled"
enabled_by_default: true
repeatable: true
activation:
intent: "left"
actions:
- say_verbatim: "I'm sorry I can't walk left."
- name: "right_disabled"
enabled_by_default: true
repeatable: true
activation:
intent: "right"
actions:
- say_verbatim: "I'm sorry I can't walk right."
Asking Goals Bot to walk right will activate the corresponding goal, which in turn will disable the right
goal and enable the left
goal. The Goals Bot will then walk toward the right.
Similarly asking Goals Bot to walk left will disable the left
goal and enable the right
goal.
For more information on utilizing intent recognition for activating goals see the Activation section of Concept Definitions. For more information on enabling and disabling goals and other character mutations checkout Character Mutations.
Data Retrieval
The Data Retrieval showcase is a multi-feature example of utilizing Intents, Entities and Parameters to have an Inworld character dynamically fetch and relay data requested by the player.
In this case, our character Data Retrieval Bot provides population data for either New York City or San Francisco.
This works first by utilizing Intents and Entities to interpret a players request. Data Retrieval Bot listens for the player to ask what the population is in a particular city. This will activate the get_population_goal
goal which will return any matching city
entity to the client as a trigger parameter.
intents:
- name: "population_in_city"
training_phrases:
- "What's the population of "
- "What is the population of "
- "population in"
- "population of"
entities:
- name: "city"
map_entries:
- value: "san_francisco"
display_name: "San Francisco"
synonyms:
- "sf"
- "san francisco"
- value: "new_york"
display_name: "New York City"
synonyms:
- "nyc"
- "new york"
- "new york city"
goals:
- name: "get_population_goal"
repeatable: true
activation:
intent: "population_in_city"
actions:
- send_trigger: "get_population"
trigger_params: [city]
On the client side we have a listener for these triggers sent by the server. When a trigger is received by the client, we first check for the city
parameter which indicates if the player asked for the population of a supported city.
void OnDataRetrievalBotServerTrigger(string triggerName, Dictionary<string, string> parameters)
{
switch (triggerName)
{
case "get_population":
if (!parameters.TryGetValue("city", out var city) || string.IsNullOrEmpty(city))
{
InworldController.Instance.SendTrigger("unsupported_city", m_InworldPlaygroundCharacter.ID, new Dictionary<string, string>
{
{ "city", city }
});
break;
}
GetPopulation(city);
break;
}
}
If the city
parameter does exist (and is not null) we can attempt to fetch the data on the client. In this case we are fetching the data from a JSON file.
{
"Cities": [
{
"Name": "San Francisco",
"Population": "808437",
"Year": "2022"
},
{
"Name": "New York City",
"Population": "8335897",
"Year": "2022"
}
],
"Source": "https://www2.census.gov/programs-surveys/popest/tables/2020-2022/cities/totals/"
}
void GetPopulation(string cityName)
{
Debug.Log("Getting population data for: " + cityName);
try
{
TextAsset populationDataTextAsset = Resources.Load<TextAsset>("PopulationData");
var populationData = JsonUtility.FromJson<PopulationData>(populationDataTextAsset.text);
foreach (City city in populationData.Cities)
{
if (city.Name == cityName)
{
GivePopulationInfo(cityName, city.Population.ToString());
break;
}
}
}
catch (Exception e)
{
Debug.LogWarning($"Failed to get population data for: {cityName}, error: {e}");
}
}
If the data is successfully fetched, we can then relay that information to Data Retreival Bot utilizing trigger Parameters.
void GivePopulationInfo(string cityName, string population)
{
var parameters = new Dictionary<string, string>
{
{ "city", cityName },
{ "population", population }
};
InworldController.Instance.SendTrigger("provide_population", m_InworldPlaygroundCharacter.ID, parameters);
}
goals:
- name: "provide_population"
repeatable: true
activation:
trigger: "provide_population"
actions:
- instruction: "tell {player} the population of {{p.city}} is {{p.population}}."
emotion_change: JOY
- name: "unsupported_city"
repeatable: true
activation:
trigger: "unsupported_city"
actions:
- instruction: "apologize to {player} for not knowing the population of {{p.city}}."
emotion_change: SADNESS
For more information on utilizing Intents, Entities and/or Parameters, please see the corresponding links:
- Intents: Concept Definitions
- Entities: Entity Extraction
- Parameters: Customizable parameters changing action in runtime
Interactable Items
The Interactable Items showcase is an example of how you can have an Inworld character react to the player interacting with objects in a scene.
This showcase utilizes Intents, Parameters and Character Mutations.
Before you can interact with the items in the showcase you must first enable the interactable goals by greeting Mr. Toyman.
intents:
- name: "greeting"
training_phrases:
- "hi"
- "hello"
- "what's up"
- "yo"
goals:
- name: "greeting"
enabled_by_default: true
repeatable: false
activation:
intent: "greeting"
motivation: "player says hello"
actions:
- instruction: "Ask the player which item they are interested in"
send_trigger: enable_interactables
character_changes:
set_emotion: INTEREST
enable_goals:
- "item_interacted"
- "doll_interacted"
- "angry_interacted"
Once you have greeted Mr. Toyman you can interact with the Toy Rocket or Innequin Miniature by hovering over the object (the crosshair will turn green) and left-clicking with the mouse.
Interacting with the Innequin Miniature will change its skin and trigger the item_interacted
goal.
goals:
- name: "item_interacted"
enabled_by_default: false
repeatable: true
activation:
trigger: "item_interacted"
actions:
- instruction: "Acknowledge {player} {{p.action}} the {{p.item}} and tell them something about that {{p.item}}."
This object has the Material Change Interactable
component attached to it which defines the action and item parameters in the item_interacted
trigger sent from the client.
Interacting with the Toy Rocket will cause it to fly into the air and fall off the platform.
This will trigger the angry_interacted
goal which will cause Mr. Toyman to get angry at the player.
goals:
- name: "angry_interacted"
enabled_by_default: false
repeatable: false
activation:
trigger: "angry_interacted"
actions:
- instruction: "React to {player} for {{p.action}} the {{p.item}} worth {{p.price}} dollars."
character_changes:
set_emotion: ANGER
set_custom_dialogue_style:
set_adjectives:
- "annoyed"
- "1 sentence response"
The Toy Rocket object has the Apply Force Interactable
component attached to it which defines the action, item and cost parameters in the angry_interacted
trigger sent from the client.
For more information on utilizing Intents, Parameters, and/or Character Mutations please see the corresponding links:
- Intents: Concept Definitions
- Parameters: Customizable parameters changing action in runtime
- Character Mutations: Character Mutations
Environmental Triggers
The Environmental Triggers showcase is an example of how you can utilize Goals and Intents to make an Inworld character interact with and/or react to their environment.
In this example we have Patrol Bot who can detect motion in two seperate areas. If they notice movement in one of those zones they will walk to that zone to check it out.
Motion detection for Patrol Bot is handled by the client, in Unity we have an object with a Sphere Collider component as well as a custom On Trigger component on each platform.
The On Trigger component sends either a motion_area_a
or motion_area_b
trigger to Patrol Bot when the player collides with one of these objects as they walk onto a platform.
goals:
- name: "motion_area_a"
repeatable: true
activation:
trigger: "motion_area_a"
actions:
- send_trigger: "motion_area_a"
say_verbatim: "Motion detected in Area alpha. Moving out!"
- name: "motion_area_b"
repeatable: true
activation:
trigger: "motion_area_b"
actions:
- say_verbatim: "Motion detected in Area beta. On my way!"
This will activate the corresponding goal and send a trigger back to the client. On the client side we listen for these server triggers to determine when and where to move Patrol Bot. Movement is handled using Unity's AI Navigation system.
protected override void OnInworldCharacterGoalCompleted(string triggerName)
{
switch (triggerName)
{
case "motion_area_a":
case "request_area_a":
MoveTo(m_AreaATransform.position);
break;
case "motion_area_b":
case "request_area_b":
MoveTo(m_AreaBTransform.position);
break;
}
}
protected void MoveTo(Vector3 position)
{
if (NavMesh.SamplePosition(position, out NavMeshHit hit, 10, NavMesh.AllAreas))
{
m_CurrentDestination = new Vector2(hit.position.x, hit.position.z);
if (!IsAgentWithinStoppingDistance(m_CurrentDestination))
m_NavMeshAgent.SetDestination(hit.position);
}
}
Patrol Bot can also be commanded by the player to move to one of those two areas. This is handled through the request_area_a
and request_area_b
Intents.
goals:
- name: "request_area_a"
repeatable: true
activation:
intent: "request_area_a"
actions:
- say_verbatim: "Request to patrol Area alpha received."
- name: "request_area_b"
repeatable: true
activation:
intent: "request_area_b"
actions:
- say_verbatim: "Request to patrol Area beta received."
intents:
- name: "request_area_a"
training_phrases:
- "I need you to patrol area a"
- "Patrol area a"
- "I need you to patrol area alpha"
- "Patrol area alpha"
- "Go to a"
- "Go to alpha"
- name: "request_area_b"
training_phrases:
- "I need you to patrol area b"
- "Patrol area b"
- "I need you to patrol area beta"
- "Patrol area beta"
- "Go to b"
- "Go to beta"
When an intent is activated, the server will send a Goal Complete trigger, which will be handled by the client in the same way as triggers for motion detection are handled.
For more general information on using Goals with Inworld see here, for more information on using Goals with the Inworld Unity SDK, see the SampleGoals_Actions demo scene.