Skip to content

Instantly share code, notes, and snippets.

@TigerHix
Created July 18, 2023 19:05
Show Gist options
  • Save TigerHix/2cb8052b0e8aeeb7f9cb796dc7edc6a3 to your computer and use it in GitHub Desktop.
Save TigerHix/2cb8052b0e8aeeb7f9cb796dc7edc6a3 to your computer and use it in GitHub Desktop.
{
"categories": [
{
"title": "Attack",
"entries": [
{
"label": "Attack 01",
"value": "katana-animations://data/Assets/KatanaAnimations/attack_01.anim"
},
{
"label": "Attack 02",
"value": "katana-animations://data/Assets/KatanaAnimations/attack_02.anim"
},
{
"label": "Attack 03",
"value": "katana-animations://data/Assets/KatanaAnimations/attack_03.anim"
},
{
"label": "Jump Attack",
"value": "katana-animations://data/Assets/KatanaAnimations/attack_jump.anim"
}
]
},
{
"title": "Combo",
"entries": [
{
"label": "Combo 01",
"value": "katana-animations://data/Assets/KatanaAnimations/combo_01.anim"
},
{
"label": "Combo 02",
"value": "katana-animations://data/Assets/KatanaAnimations/combo_02.anim"
},
{
"label": "Combo 03",
"value": "katana-animations://data/Assets/KatanaAnimations/combo_03.anim"
},
{
"label": "Combo 04",
"value": "katana-animations://data/Assets/KatanaAnimations/combo_04.anim"
},
{
"label": "Combo 05",
"value": "katana-animations://data/Assets/KatanaAnimations/combo_05.anim"
},
{
"label": "Combo 06",
"value": "katana-animations://data/Assets/KatanaAnimations/combo_06.anim"
},
{
"label": "Combo 07",
"value": "katana-animations://data/Assets/KatanaAnimations/combo_07.anim"
},
{
"label": "Combo 08",
"value": "katana-animations://data/Assets/KatanaAnimations/combo_08.anim"
}
]
}
]
}
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using UMod;
using UnityEngine;
using Warudo.Core;
using Warudo.Core.Attributes;
using Warudo.Core.Data;
using Warudo.Core.Plugins;
using Warudo.Core.Resource;
namespace KatanaAnimations {
/**
** An example plugin that adds some katana animations to any character animation dropdown (such as Character -> Default Idle Animation).
**/
[PluginType(Id = "KatanaAnimations", Name = "Katana Animations", Description = "Provides katana animations.", Author = "TigerHix", Version = "1.0.0")]
public class KatanaAnimationsPlugin : Plugin {
protected override void OnCreate() {
// Load animations from a JSON file.
// ModHost.Assets.Load is similar to Unity's Resources.Load, but you use the former in Warudo Mods.
// The path here is the relative path (i.e. from Unity project root) of the asset you are trying to load. Must start with "Assets/".
var json = ModHost.Assets.Load<TextAsset>("Assets/KatanaAnimations/Animations.json");
if (json == null) {
Debug.LogError("[KatanaAnimations] Failed to load Animations.json");
return;
}
// Here we deserialize the JSON into an autocomplete list (that is used for providing data in Warudo's dropdown menus).
// You can of course deserialize into your own data format, but you will have to convert it to an AutoCompleteList to display it in the menu anyway, so formatting your data as AutoCompleteList is more straightforward.
var animationCatalog = JsonConvert.DeserializeObject<AutoCompleteList>(json.text);
// Register a resource provider and resource resolver.
// Resource URIs in Warudo are just... URIs. For example:
// A prop in the Props folder: prop://data/MyProp.warudo
// A character in a workshop item: character://workshop/123456789/Character.warudo (123456789 is workshop item ID)
// These URIs are internal to Warudo, so when creating/loading prop & character mods, you don't see them.
//
// However, since we are trying to provide animations from our mod folder, we need to define our custom URI format.
// Here, we define our URI as: katana-animations://data/animation_filename
// We register a provider that will provide a list of animations included in the mod, which is read from the JSON above.
// We also register a resolver, that will create the Unity AnimationClip based on a URI that matches our format.
var rm = Context.ResourceManager;
rm.RegisterProvider(new KatanaAnimationsAnimationResourceProvider(animationCatalog), this);
rm.RegisterUriResolver(new KatanaAnimationsAnimationResourceUriResolver(ModHost), this);
}
public class KatanaAnimationsAnimationResourceProvider : IResourceProvider {
// This is the section title that is shown in the dropdown menu
public string ResourceProviderName => "Katana Animations";
private readonly AutoCompleteList catalog;
internal KatanaAnimationsAnimationResourceProvider(AutoCompleteList catalog) {
this.catalog = catalog;
}
public List<Resource> ProvideResources(string query) {
// Query is the "resource type". For example, in the Character asset, the Default Idle Animation data input is defined as:
//
// [DataInput]
// [AutoCompleteResource("CharacterAnimation")]
// public string DefaultIdleAnimation;
//
// When the user opens the dropdown, Context.ResourceManager.ProvideResources("CharacterAnimation") is called. Then every provider is queried to check if they can provide resource URIs for the resource type "CharacterAnimation".
//
if (query == "CharacterAnimation") {
// Map JSON entries to a list of Resources.
return catalog.categories.SelectMany(category => {
return category.entries.Select(it => new Resource {
category = category.title,
label = it.label,
uri = new Uri(it.value)
});
}).ToList();
}
return null;
}
}
public class KatanaAnimationsAnimationResourceUriResolver : IResourceUriResolver {
private readonly ModHost modHost;
internal KatanaAnimationsAnimationResourceUriResolver(ModHost modHost) {
this.modHost = modHost;
}
public object Resolve(Uri uri) {
// Check if the URI is in the format: katana-animations://data/XXXX
if (uri.Scheme != "katana-animations" || uri.Authority != "data") return null;
// Parse filename from URI
var path = uri.LocalPath.TrimStart('/');
// Load that file from the mod
return modHost.Assets.Load<AnimationClip>(path);
}
}
}
}
@TigerHix
Copy link
Author

Built-in resource types and what URI resolvers are expected to return:

  • Character - expects GameObject
  • CharacterAnimation (e.g. Character, Play Character Idle Animation node) - expects AnimationClip
  • Environment - expects Scene or ValueTuple<ModHost, Scene>
  • Image (e.g. Screen) - expects Warudo.Plugins.Core.Utils.ImageResource
  • Music (e.g. Music Player) - expects string (absolute file path)
  • Particle (e.g. Throw Prop At Character node) - expects GameObject
  • Prop - expects GameObject
  • Sound (e.g. Play Sound node, Throw Prop At Character node) - expects AudioClip
  • Video (e.g. Screen) - expects string (absolute file path)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment