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 Innequin.
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 causes the Rhyme Innequin to make up a rhyme with the given phrase.
For example, if you input the phrase "apples"
Rhyme Innequin comes 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 a 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 Innequin, this activates the greeting
intent which enables 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 Innequin 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 Innequin to walk right activates the corresponding goal, which in turn disables the right
goal and enable the left
goal. The Goals Innequin then walks toward the right.
Similarly asking Goals Innequin to walk left disables the left
goal and enables 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 Innequin 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 Innequin listens for the player to ask what the population is in a particular city. This activates the get_population_goal
goal which returns 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 there is a listener for these triggers sent by the server.
When a trigger is received by the client, this listener first checks 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) it is possible to attempt to fetch the data on the client. In this case the data is being fetched 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 Retrieval Innequin 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
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 Innequin who can detect motion in two separate areas. If they notice movement in one of those zones, they walk to that zone to investigate.
Motion detection for Patrol Innequin 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 Innequin 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 activates 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 Innequin. 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 Innequin 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 sends a Goal Complete trigger, which is 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.