Skip to content

Commit

Permalink
Merge pull request #10 from HakuyaLabs/scripting
Browse files Browse the repository at this point in the history
Scripting section
  • Loading branch information
TigerHix authored Jun 15, 2024
2 parents 34d4919 + fbad57a commit 7de6d81
Show file tree
Hide file tree
Showing 64 changed files with 2,776 additions and 175 deletions.
2 changes: 1 addition & 1 deletion docs/blueprints/templates/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ sidebar_position: 0

# Overview

In this section, we provide some ready-to-use blueprints that you can just import into your scene and use right away! Technically, you don't need to read any of the other sections to use these blueprints, but we still recommend you to read [Understanding Blueprints](../understanding-blueprints) to get a better understanding of how blueprints work.
In this section, we provide some ready-to-use blueprints that you can just import into your scene and use right away! Technically, you don't need to read any of the other sections to use these blueprints, but we still recommend you to read [Creating Your First Blueprint](../understanding-blueprints) to get a better understanding of how blueprints work.

## Setup

Expand Down
2 changes: 1 addition & 1 deletion docs/blueprints/tutorials/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ sidebar_position: 0
# Overview

:::info
If you have not read the [Understanding Blueprints](../understanding-blueprints.md) section, we recommend that you do so before continuing.
If you have not read the [Creating Your First Blueprint](../understanding-blueprints.md) section, we recommend that you do so before continuing.
:::

Creating a blueprint is really just a two-step process:
Expand Down
2 changes: 1 addition & 1 deletion docs/blueprints/tutorials/toggle-meshes.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Often character models will have multiple meshes for different clothing and acce

## Toggling Meshes

Without further ado, let's get started! First, let's create a new blueprint. Just like in the [Understanding Blueprints](../understanding-blueprints) tutorial, we need to add a **On Keystroke Pressed** node. I will set the hotkey to **Ctrl+Shift+J** (J for "jacket"), but you can use whatever you want.
Without further ado, let's get started! First, let's create a new blueprint. Just like in the [Creating Your First Blueprint](../understanding-blueprints.md) tutorial, we need to add a **On Keystroke Pressed** node. I will set the hotkey to **Ctrl+Shift+J** (J for "jacket"), but you can use whatever you want.

![](/doc-img/en-blueprint-toggle-meshes-1.png)

Expand Down
6 changes: 6 additions & 0 deletions docs/scripting/api/_category_.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"position": 10,
"label": "Scripting API",
"collapsible": true,
"collapsed": false
}
173 changes: 173 additions & 0 deletions docs/scripting/api/assets.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
---
sidebar_position: 40
---

# Assets

Assets are self-contained objects that implement a feature or a behavior in the scene. Different from nodes, an asset usually encapsulates a more complex logic. They can be thought of classes in a program and are more similar to Unity's `MonoBehaviour`.

## Type Definition

You can create asset types that can be instantiated and stored in the scene. An asset type inherits from the `Asset` type and is decorated with the `[AssetType]` attribute, like below:

```csharp
[AssetType(
Id = "c6500f41-45be-4cbe-9a13-37b5ff60d057",
Title = "Hello World",
Category = "CATEGORY_DEBUG",
Singleton = false
)]
public class HelloWorldAsset : Asset {
// Asset implementation
}
```

Here's a summary of the parameters:

- **`Id`**: A unique identifier for the asset type; you should [generate a new GUID](https://www.guidgenerator.com/online-guid-generator.aspx) for each new asset type. Note that this is different from the asset instance's UUID (`asset.Id`).
- **`Title`**: The name of the asset type that will be displayed in the *Add Asset* menu.
- **`Category`**: Optional. The group of the asset in the *Add Asset* menu.
- **`Singleton`**: Optional. If set to `true`, only one instance of the asset can exist in the scene. Default is `false`.

:::info
Here are some common asset categories you can use: `CATEGORY_INPUT`, `CATEGORY_CHARACTERS`, `CATEGORY_PROP`, `CATEGORY_ACCESSORY`, `CATEGORY_ENVIRONMENT`, `CATEGORY_CINEMATOGRAPHY`, `CATEGORY_EXTERNAL_INTERACTION`, `CATEGORY_MOTION_CAPTURE`.
:::

## Components

An asset type can define data inputs and triggers. Unlike nodes, assets do not have data outputs or flow inputs/outputs.

![](/doc-img/en-scripting-concepts-4.png)

## Lifecycle

Assets have the lifecycle stages listed on the [Entities](entities#lifecycle) page. You can override these methods to perform various tasks; for example, `OnUpdate()` is called every frame, similar to Unity's `Update()` method.

## Active State {#active-state}

Different from nodes, assets have an active state that inform whether the asset is "active", or ready to use. For example, when a character asset does not have a `Source` selected, it is shown as inactive in the editor.

![](/doc-img/en-custom-asset-1.png)

By default, assets are **NOT** active when they are created. You can set the active state of an asset by calling `SetActive(bool state)`. For example, if your asset is always ready to use, you can set it to active in the `OnCreate` method:

```csharp
public override void OnCreate() {
base.OnCreate();
SetActive(true);
}
```

If your asset only works when connected to an external server, e.g., a remote tracking device, you can set it to active only if the connection is successfully established:

```csharp
[DataInput]
public string RemoteIP = "127.0.0.1";

[DataInput]
public string RemotePort = "12345";

public override void OnCreate() {
base.OnCreate();
WatchAll(new [] { nameof(RemoteIP), nameof(RemotePort) }, ResetConnection); // When RemoteIP or RemotePort changes, reset the connection
}

protected void ResetConnection() {
SetActive(false); // Inactive until connection is established
if (ConnectToRemoteServer(RemoteIP, RemotePort)) {
SetActive(true);
}
}
```

:::tip
Determining whether your asset is "ready to use" is entirely up to you. The convention that Warudo's internal assets use is that an asset is active when all data inputs required for the asset to function properly are set.
:::

## Creating GameObjects

You can create GameObjects in the (Unity) scene anytime you want. For example, the following asset creates a cube GameObject when the asset is created, and destroys it when the asset is destroyed:

```csharp
private GameObject gameObject;

public override void OnCreate() {
base.OnCreate();
gameObject = GameObject.CreatePrimitive(PrimitiveType.Cube);
}

public override void OnDestroy() {
base.OnDestroy();
Object.Destroy(gameObject);
}
```

However, the user cannot move this cube around, since there are no data inputs that control the cube! You can add data inputs to control the cube's position, scale, etc., but an easier way is to inherit from the `GameObjectAsset` type:

```csharp
using UnityEngine;
using Warudo.Core.Attributes;
using Warudo.Plugins.Core.Assets;

[AssetType(
Id = "4c00b14a-aed5-423e-abe6-6921032439c5",
Title = "My Awesome Cube",
Category = "CATEGORY_DEBUG"
)]
public class MyAwesomeCubeAsset : GameObjectAsset {
protected override GameObject CreateGameObject() {
return GameObject.CreatePrimitive(PrimitiveType.Cube);
}
}
```

The `GameObjectAsset` handles creating and destroying the GameObject for you, and it comes with a `Transform` data input that allows the user to control the GameObject's position, rotation, and scale:

![](/doc-img/en-custom-asset-2.png)

:::tip
When to use `GameObjectAsset`? If your asset is "something that can be moved by the user in the (Unity) scene", then it's probably a good idea to inherit from `GameObjectAsset`.
:::

## Events

The `Asset` type invokes the following events that you can listen to:

- **`OnActiveStateChange`**: Called when the active state of the asset changes.
- **`OnSelectedStateChange`**: Called when the asset is selected or deselected in the editor.
- **`OnNameChange`**: Called when the name of the asset changes.

For example, the built-in [Leap Motion tracking](../../mocap/leap-motion) asset listens to the `OnSelectedStateChange` event to display a Leap Motion controller model in the (Unity) scene when the asset is selected.

```csharp
public override void OnCreate() {
base.OnCreate();
OnSelectedStateChange.AddListener(selected => {
if (selected) {
// Show the model
} else {
// Hide the model
}
});
}
```

## Code Examples

### Basic

- [AnchorAsset.cs](https://gist.github.com/TigerHix/c549e984df0be34cfd6f8f50e741aab2)
Attachable / GameObjectAsset example.

### Advanced

- [CharacterPoserAsset.cs](https://gist.github.com/TigerHix/8413f8e10e508f37bb946d8802ee4e0b)
Custom asset to pose your character with IK anchors.

<AuthorBar authors={{
creators: [
{name: 'HakuyaTira', github: 'TigerHix'},
],
translators: [
],
}} />
64 changes: 64 additions & 0 deletions docs/scripting/api/entities.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
---
sidebar_position: 2
---

# Entities

An entity is an object that is persistent in a [scene](scene) (e.g., nodes, assets) or across scenes (e.g., plugins). Scripting in Warudo is all about defining your own entity types and interacting with the Warudo runtime. You can define your own entity type by inheriting a base class provided by Warudo:

- **Node:** Inherit from `Node` to create a new node type. A node type can define data inputs, data outputs, flow inputs, flow outputs, and triggers. A node is instantiated by dragging it from the node palette to the blueprint editor; the node instance is saved along with the blueprint.
- **Asset:** Inherit from `Asset` to create a new asset type. An asset type can define data inputs and triggers. An asset is instantiated by using the *Add Asset* menu; the asset instance is saved along with the scene.
- **Plugin:** Inherit from `Plugin` to create a new plugin type. A plugin type can define data inputs and triggers. A plugin is loaded when Warudo starts, or when the plugin is hot-reloaded. There is only one plugin instance per plugin type, and data inputs are saved across scenes.
- **Structured Data:** Inherit from `StructuredData` to create a new [structured data](structured-data) type. Structured data is a complex data type that can be used as an "embedded" data input in nodes, assets, or plugins.

:::info
In the [Creating Your First Script](../creating-your-first-script.md) tutorial, we created a custom node type called `HelloWorldNode` and a custom asset type called `CookieClickerAsset`.
:::

:::tip
The concept of entities is loosely similar to Unity's MonoBehaviour, but with a more structured approach.
:::

After an entity is instantiated, it is automatically assigned a UUID (unique identifier) by Warudo. You can access it via the `Id` property. This UUID is then used to identify the entity in the scene, and is saved along with the scene.

An entity type can contain regular C# fields and methods. However, to interface with the Warudo runtime, you need to create [**ports and triggers**](ports-and-triggers) in the entity. All entities can have [data input ports](ports-and-triggers#data-input-ports), [data output ports](ports-and-triggers#data-output-ports) and [triggers](ports-and-triggers#triggers), while nodes can additionally have [data output ports](ports-and-triggers#data-output-ports), [flow input ports](ports-and-triggers#flow-input-ports), and [flow output ports](ports-and-triggers#flow-output-ports).

![](/doc-img/en-custom-node-1.png)

![](/doc-img/en-scripting-concepts-4.png)

## Lifecycle {#lifecycle}

An entity has a lifecycle that consists of several stages:

- **`OnCreate()`:** Called when the entity is created. This is where you should initialize the instance fields, subscribe to events, etc.
- **Assets:** Called when a new asset is added to the scene by the user, or when the scene is loaded.
- **Nodes:** Called when a new node is added to the blueprint by the user, or when the scene is loaded. Nodes are always created _after_ assets.
- **Plugins:** Called when the plugin is loaded by Warudo on startup, or when the plugin is hot-reloaded.
- **Structured Data:** Called when the parent entity is created, or when the user adds a new structured data to an array of structured data.
- **`OnDestroy()`:** Called when the entity is destroyed. This is where you should release resources, etc.
- **Assets:** Called when the asset is removed from the scene by the user, or when the scene is unloaded.
- **Nodes:** Called when the node is removed from the blueprint by the user, or when the scene is unloaded. Nodes are always destroyed _after_ assets.
- **`Deserialize(TSerialized data)`:** Called when the entity is deserialized from a saved state. For example, this method is called on an asset when the scene is just opened and loaded. _You usually do not need to override this method._
- **`Serialize()`:** Called when the entity needs to be serialized to a saved state. For example, this method is called on an asset when the scene is about to be saved, or need to be sent to the editor. _You usually do not need to override this method._

Assets, nodes, and plugins have extra lifecycle stages:

- **`OnUpdate()`:** Called every frame when the entity is not destroyed. This is similar to Unity's `Update()` method.
- `OnPreUpdate()` and `OnPostUpdate()` are also provided. They are called before and after `OnUpdate()`, respectively.
- **`OnLateUpdate()`:** Called every frame after `OnUpdate()`. This is similar to Unity's `LateUpdate()` method.
- **`OnFixedUpdate()`:** Called every fixed frame. This is similar to Unity's `FixedUpdate()` method.
- **`OnEndOfFrame()`:** Called at the end of the frame. This is similar to using Unity's [`WaitForEndOfFrame` coroutine](https://docs.unity3d.com/ScriptReference/WaitForEndOfFrame.html).
- **`OnDrawGizmos()`:** Called when the entity should draw gizmos. This is similar to Unity's `OnDrawGizmos()` method.

:::info
The update order is as follows: plugins → assets → nodes.
:::

<AuthorBar authors={{
creators: [
{name: 'HakuyaTira', github: 'TigerHix'},
],
translators: [
],
}} />
61 changes: 61 additions & 0 deletions docs/scripting/api/events.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
---
sidebar_position: 90
---

# Global Events {#events}

Warudo contains a `EventBus` class that allows you to subscribe to and broadcast global events. To define a custom event class, inherit from the `Warudo.Core.Events.Event` class. For example:

```csharp
public class MyEvent : Event {
public string Message { get; }
public MyEvent(string message) {
Message = message;
}
}
```

To subscribe (listen) to an event:

```csharp
Context.EventBus.Subscribe<MyEvent>(e => {
Debug.Log(e.Message);
});
```

To broadcast (fire) an event:

```csharp
Context.EventBus.Broadcast(new MyEvent("Hello, world!"));
```

You should unsubscribe from events when you no longer need to listen to them:

```csharp
var subscriptionId = Context.EventBus.Subscribe<MyEvent>(e => {
Debug.Log(e.Message);
});
// Later
Context.EventBus.Unsubscribe<MyEvent>(subscriptionId);
```

### Subscribing Events In Entities

If you are writing code inside an entity type, you can use the `Subscribe` method directly to avoid handling event subscription IDs:

```csharp
// Inside an entity type, i.e., asset, node, plugin, structured data
Subscribe<MyEvent>(e => {
Debug.Log(e.Message);
});
```

The events are automatically unsubscribed when the entity is destroyed.

<AuthorBar authors={{
creators: [
{name: 'HakuyaTira', github: 'TigerHix'},
],
translators: [
],
}} />
Loading

0 comments on commit 7de6d81

Please sign in to comment.