Felt Tip Circus

Felt Tip Circus

Not enough ratings
Creating New Acts for Felt Tip Circus
By railboy and 1 collaborators
A guide to creating & uploading your custom acts.
   
Award
Favorite
Favorited
Unfavorite
Introduction
Welcome! This guide assumes you're familiar with C# and the Unity engine. We've tried to cover all the basics, and we hope you find it useful. We strongly encourage you to download and experiment with our template project to learn more. If you have questions please post them to the Workshop discussion forum.
Download the Template Project
We've made the project files for Whack-a-Bear available so you can see how an FTC act is put together. This project also has the libraries you'll need to start a project from scratch. You can download everything here:

https://github.com/AADProductions/FTC-Act-Template

(You will need Unity 5.4.0b20 or later to open the Unity project.)

FTC.WhackABear Source
We've included the source for the FTC.WhackABear library as a Monodevelop project. You can use this as a template for your own Monodevelop or Visual Studio project. You can find it in the template project here: FTC.WhackABear/WhackABear.sln.

Free Unity Assets

In addition to the Whack-A-Bear assets, a set of FTC-themed assets are available as free DLC on Steam. (MIT license[opensource.org]). You can find these assets in VR-FTC-Assets.unitypackage under the local files of the Workshop Uploader DLC.
Important Project Files
Many of the project assets are used for the WhackABear act and can be deleted, but others are important and should be left alone. Assets stored in folders with the prefix 'FTC' should not be removed.

Libraries
  • Assets/Editor/FTC.Editor.dll - Bundle creation and custom inspectors. Don't remove.
  • Assets/Scripts/CoreScripts/FTC.Core.dll - Core functionality for FTC acts. Don't remove.
  • Assets/Scripts/CoreScripts/FTC.Interfaces.dll - Interfaces used for NewtonVR object interaction scripts. Don't remove.
  • Assets/Scripts/CoreScripts/FTC.WhackABear.dll - Custom code for the Whack-a-Bear act. You can remove this once you've created your own custom library.
Documentation for the FTC.Core & FTC.Editor libraries is available here. We'll cover how to use these libraries in a later step.

Important Scripts
  • Scripts/FTCScripts/ - Don't alter or remove these.
  • Assets/NewtonVR (FTC Custom)/ - A custom version of NewtonVR's interaction system. This is what you'll use to make objects interactive. Don't alter or remove these.
  • Assets/SteamVR/ - The version of SteamVR that FTC uses. Don't alter or remove these.

Important Assets
  • Assets/Resources/ - Don't alter or remove these. (Also, don't use the Resources folder to load assets. It will not work in-game.)
  • Assets/Sounds/FtcSounds/ - These are used by the tutorial prefab.
  • Assets/Textures/FTCTextures/ - Textures used by the game menu and lighting. Don't alter or remove these.
  • Assets/Fonts/FTCFonts/ - Fonts used for dialogs. Don't alter or remove these.
  • Assets/Prefabs/FTCPrefabs/ - Two prefabs, TutorialDialog & TutorialParticleSystem. Altering or removing the tutorial prefabs won't cause problems, but you'll need to keep them if you want to display tutorials.
  • Assets/Meshes/FTCMeshes/ - Meshes used by tutorials. You can safely alter these if you want to change the appearance of your tutorials.
  • Assets/Materials/FTCMaterials/ - Materials used by menus and for outlining objects. You can safely alter these if you want to change the appearance of your tutorials.
Converting Monobehaviors to FTC scripts
Starting out
We recommend that you ignore the FTC libraries during prototyping. You can assemble the basics of your act with regular Monobehavior scripts and test it as a standalone game to keep things simple & fast.

Once your act is ready you'll need to convert your scripts into FtcObject / FtcBehavior scripts. This is pretty simple and on average took us about 10-15 minutes per script.

What you'll use in place of Monobehaviors
FTC uses Unity AssetBundles to load custom acts, and custom Monobehavior scripts can't be loaded from an AssetBundle. To work around this limitation you'll be creating classes that are instantiated and managed by the following component scripts:
  • FTC.Core.Act - Manages an ActScript object.
  • FTC.Core.FtcObject - Manages an FtcObjectScript object. Used for basic objects that don't need an update function.
  • FTC.Core.FtcBehavior - Manages an FtcBehaviorScript object. Used for objects that need an Update function.
  • FTC.Core.FtcPhysicsBehavior - Manages an FtcBehaviorScript object. Used for objects that need to check for collisions / triggers or which need a FixedUpdate function.

These are the scripts which you will inherit from:
  • FTC.Core.ActScript - Runs the act. We'll get into this in detail later.
  • FTC.Core.FtcObjectScript - Use in place of a Monobehavior.
  • FTC.Core.FtcBehaviorScript - Use in place of a Monobehavior with an Update function or physics functions.

Creating your act library
Your custom scripts will need to be kept in a library which references the FTC.Core, FTC.Interfaces and UnityEngine libraries. (Use the FTC.WhackABear library as a template.)

The name of your library should be unique, descriptive, and relatively short because you're going to be using it everywhere. (See naming section for details.)

Import all of your monobehavior scripts into your library and change them to inherit from FtcObjectScript or FtcBehaviorScript. This will probably cause a flood of errors but most will be easy to resolve.
Name / Namespace Rules
FTC makes some assumptions about the names of certain scripts and assets. If your scripts aren't loading odds are it's a name or namespace issue. Here are the rules you'll need to follow for everything to work:
Script rules
  • Your library must have a unique, descriptive and relatively short name.
    Eg, FTC.WhackABear, FTC.ThrowingKnives, FTC.LionTamer.
  • All scripts must be in the FTC.[ActName] namespace.
    Eg, FTC.WhackABear.Hammer.
  • Your act script name must be FTC.[ActName].[ActName]Act.
    Eg, FTC.WhackABear.WhackABearAct.

Asset rules
  • Your scene name must share your library name.
    Eg, FTC.WhackABear / WhackABear.unity, FTC.ThrowingKnives / ThrowingKnives.unity.
  • Your scene must be in the Assets/Scenes/ directory.
  • Your library must be in the Assets/Scripts/CoreScripts/ directory.
Attaching Custom Scripts to Objects
Here's a (simplified) example of a script I want to attach to my hammer object:
namespace FTC.WhackABear { public class Hammer : FtcBehaviourScript { public INVRInteractable Interactable; public ParticleSystem Splinters; public int NumCracks = 0; public Mesh CrackedMesh; public Mesh BrokenMesh; public Transform ForwardTransform; public Transform BackTransform; public AudioClip CrackClip; public AudioClip BreakClip; public Vector3 CenterOfMass = new Vector3 (0f, 0.75f, 0f); Material [] hammerMats; float lastTimeHit; float minTimeHit = 0.25f; public override void OnEnable () { //initialize, etc } } }
Once the FTC.WhackABear library is compiled and imported into Unity, I can attach this script to an object by first attaching an FtcObject component, then selecting FTC.WhackABear.Hammer from the Class Name dropdown menu:
Custom Script Asset Dependencies
FtcObject, FtcBehavior & FtcPhysicsBehavior have custom inspectors that let you view the scripts they manage. One unfortunate limitation is that you can't manually set values or references to other objects in the inspector, because your custom classes will not be serialized in the AssetBundle and this information would be lost. (Fields marked YELLOW indicate that they will be NULL on startup.)

To work around this I've created a helper class called FtcDependencies, which lets you drag-and-drop references to other assets in the project. These references will be preserved, so you can look them up at runtime in an OnEnable function. Here's what it looks like in the inspector:

And here is how you would use it on startup:
namespace FTC.WhackABear { public class Hammer : FtcBehaviourScript { public INVRInteractable Interactable; public ParticleSystem Splinters; public int NumCracks = 0; public Mesh CrackedMesh; public Mesh BrokenMesh; public Transform ForwardTransform; public Transform BackTransform; public AudioClip CrackClip; public AudioClip BreakClip; public Vector3 CenterOfMass = new Vector3 (0f, 0.75f, 0f); Material [] hammerMats; float lastTimeHit; float minTimeHit = 0.25f; public override void OnEnable () { FtcDependencies d = gameObject.GetComponent <FtcDependencies> (); ForwardTransform = d.GetDependency <GameObject> ("Forward").transform; BackTransform = d.GetDependency <GameObject> ("Back").transform; Splinters = d.GetDependency <GameObject> ("SplinterParticles").GetComponent <ParticleSystem> (); CrackedMesh = d.GetDependency <Mesh> ("ScoreHammerCracked1"); BrokenMesh = d.GetDependency <Mesh> ("ScoreHammerCracked2"); BreakClip = d.GetDependency <AudioClip> ("HammerBreakClip"); CrackClip = d.GetDependency <AudioClip> ("HammerCrackClip"); Interactable = gameObject.GetComponent (typeof(INVRInteractable)) as INVRInteractable; hammerMats = gameObject.GetComponent <Renderer> ().materials; } } }

At runtime, any failed requests are logged and displayed in the inspector so you can easily see where things went wrong. A script whose assets have all been successfully loaded will look like GREEN like this:
Picking Up Objects
FTC uses a customized version of the NewtonVR object interaction system[github.com]. If you want the player to be able to pick up an object, here's what to do:
  • Add the NVRInteractableItem script
  • Add a rigidbody
  • Set the gameObject layer to Tool (or any layer that collides with the Player layer.)
  • Add at least one collider

Your custom scripts will have to use the INVRInteractable interface to talk to NVRInteractableItem scripts.
public interface INVRInteractable { bool IsAttached { get; } INVRHand AttachedHandInterface { get; } void EndInteraction (); Action OnBeginInteraction { get; set; } Action OnEndInteraction { get; set; } Transform transform { get; } GameObject gameObject { get; } }

You can also get information about the hand holding the item with the INVRHand interface:
public interface INVRHand { bool HoldButtonDown { get; set; } bool HoldButtonUp { get; set; } bool HoldButtonPressed { get; set; } bool UseButtonDown { get; set; } bool UseButtonUp { get; set; } bool UseButtonPressed { get; set; } bool MenuButtonDown { get; set; } bool MenuButtonUp { get; set; } bool MenuButtonPressed { get; set; } bool IsInteracting { get; } void TriggerHapticPulse (ushort pulseLength); void EndInteraction (); Vector3 Velocity { get; } }

You can get an interface from an object like this:
void Start () { INVRHand h = gameObject.GetComponent (typeof(INVRHand)) as INVRHand; }

For more information check out the NewtonVR scripts included in the project.
Custom Script Interactions
Converting scripts usually doesn't require you to change much code. One exception is object interaction. Something like this:
namespace FTC.WhackABear { public class Hammer : FtcBehaviourScript { public override void OnTriggerEnter (Collider other) { Hammer h otherHammer = other.GetComponent <Hammer> (); if (h != null) { //do something } } } }
Must be converted to this:
namespace FTC.WhackABear { public class Hammer : FtcBehaviourScript { public override void OnTriggerEnter (Collider other) { FtcPhysicsBehavior fpb = other.GetComponent <FtcPhysicsBehavior> (); if (fpb != null) { Hammer h = fpb.script as Hammer; if (h != null) { //do something } } } } }
Custom Act Script
You'll need to add an act script to the scene before FTC will be able to use it. Start by inheriting from FtcAct. Make sure your namespace and class name follow the rules:
namespace FTC.WhackABear { public class WhackABearAct : FtcAct { ... } }

Required functions
You must implement two static functions. The first must return a display-friendly name:
public static string GetName () { return "Whack-A-Bear"; }
The second must return an array of 5 bonus descriptions. (All acts have 5 possible bonuses that are revealed by the slot machine.) Here's a stub you can use until your bonuses are fully defined.
public static ActBonus [] GetBonusDefinitions () { ActBonus [] bonuses = new ActBonus [5]; ActClassInfo actInfo = new ActClassInfo ("FTC.WhackABear.WhackABearAct", "FTC.WhackABear"); bonuses [0] = new ActBonus ("Bonus1", "Desc1", ActDifficulty.None, actInfo.ActKey, 0); bonuses [1] = new ActBonus ("Bonus2", "Desc2", ActDifficulty.None, actInfo.ActKey, 1); bonuses [2] = new ActBonus ("Bonus3", "Desc3", ActDifficulty.None, actInfo.ActKey, 2); bonuses [3] = new ActBonus ("Bonus4", "Desc4", ActDifficulty.None, actInfo.ActKey, 3); bonuses [4] = new ActBonus ("Bonus5", "Desc5", ActDifficulty.None, actInfo.ActKey, 4); return bonuses; }
You must also override PrepareForScoring. This is where you can judge the player's performance and award bonuses. Here is how bonuses are awarded in the ThrowingKnives act:
public override void PrepareForScoring () { base.PrepareForScoring (); int numKnives = knives.Length; int numBalloons = balloons.Length; int numKnivesThrown = 0; int numBalloonsPopped = 0; float averageAccuracy = 0f; for (int i = 0; i < knives.Length; i++) { if (knives [ i ].Thrown) { numKnivesThrown++; } } for (int i = 0; i < balloons.Length; i++) { if (balloons [ i ].Popped) { numBalloonsPopped++; averageAccuracy += balloons [ i ].Accuracy; } } if (act.FinalTime / act.TimeToComplete < 0.25f) { act.AchieveBonus ("EfficiencyBonus"); } if (Olive != null && Olive.Hit) { act.AchieveBonus ("PoppedOlive"); } if (numBalloonsPopped == numBalloons) { act.AchieveBonus ("PoppedAllBalloons"); } if (numKnivesThrown > 1 && numKnivesThrown == numBalloonsPopped) { act.AchieveBonus ("EveryThrowHit"); } if (Knife.NumAcrobatsHit == 0) { act.AchieveBonus ("NoCasualties"); } }

Required coroutines
You must override two coroutines at a minimum. The first is ActIntroduceOverTime. This is called when your act becomes visible and is where you'll want to adjust things based on difficulty level. Setup can take as long as you like. Before exiting this function, act.ActBegin must be called.
public override IEnumerator ActIntroduceOverTime () { act.ActBegin (); yield break; }

The second is ActUpdateOverTime. This should include a loop that continues until the timer runs out. (You don't have to wait for the timer, of course - if the player screws up badly enough you can end the act prematurely.) Before exiting this function, act.ActScore must be called.
public override IEnumerator ActUpdateOverTime () { while (act.TimeSoFar < act.TimeToComplete) { yield return null; } act.ActScore (); yield break; }
You can also override ActFinishOverTime and ActWaitForTutorials if you want to do something unique during those phases, but this is almost never necessary.

Once you've done all this, Your act will function on a basic level.

Adding an act to the scene
Just add the 'Act' component to any gameObject and it will automatically detect the FtcAct script in your library. If it fails to find your act class you'll see a warning. (It's most likely a naming problem.)

Be sure to follow the heirarchy shown in the WhackABear example scene. The act object must be the root object, and all of your act content must be kept under the ActivationObject transform.
Act Asset Dependencies
Acts have a built-in dependency array that you can use just like the FtcDependencies script. In the inspector, any fields marked YELLOW will be NULL on startup:



If you need to look for dependencies override the LookForObjects function:
public override void LookForObjects () { GameObject Thing = act.GetDependency <GameObject> ("Thing"); }
Failed requests are logged and displayed in the inspector. At runtime you'll want to see GREEN:

Scoring
Cheering & Booing
When the player does something well, call Act.Current.Cheer ().
When the player does something badly, call Act.Current.Boo ().

Scoring details
The act keeps a running Score from 0-1. This is reflected on the scorekeeper as amusement. Over time, this value is reduced to zero at a rate defined by ScoreLoseSpeed. Each time the player earns a cheer, ScorePerCheer is added to this value. When Score exceeds 1, one point is added to NumCaptivated. When the player earns a boo, Score is automatically halved.

namespace FTC { public class Act { ... public float Score = 0.5f; public float ScorePerCheer = 0.2f; public float ScoreLoseSpeed = 0.05f; public int NumCaptivated = 0; ... } }

If you want to change the ScoreLoseSpeed and ScorePerCheer values depending on the difficulty level, we recommend you do so in ActIntroduceOverTime.
Exporting Your Act & Uploading to Workshop
Building The Asset Bundles
To export your act, select the act object in your scene and click the Export Scene + Library to Workshop Uploader button. (If you've made changes to the library but not the scene, you can click Export Library to Workshop Uploader button to save some time.)

This will open a folder selection dialog. Navigate to the WorkshopContent folder of the FTC Act Uploader, which will probably be here:
SteamApps\common\Felt Tip Circus\FTC Act Uploader\WorkshopContent

An [ActName] folder will be created and two asset bundles will be copied into it, [actname]_assembly and [actname]_scene.

If no thumbnail exists, a default thumbnail will be copied to the WorkshopContent folder. You'll want to replace this with a custom thumbnail, obviously.


Don't add text to your thumbnails - the title of your act will be overlaid in-game like so:


Uploading to Workshop
Once your act is exported, launch FTC from Steam and choose the Game Editor launch option. If you don't have this launch option, make sure you've downloaded the free FTC Workshop Tool in your DLC section:



Select your act from the list on the left:



Once you've entered in a description, etc, click the Upload button:



If you've set the act visibility to public it should appear in the Workshop immediately.
Questions & Comments
That should get you started - if you have questions or comments please post them to the Workshop discussion forums. (You're welcome to make comments below but we may not notice them right away.)
1 Comments
zl_god 27 Feb, 2018 @ 2:44am 
66666666