Search Unity

[Released] OneJS: Typescript + React (JSX) for Unity

Discussion in 'Assets and Asset Store' started by Singtaa, May 16, 2022.

  1. Singtaa

    Singtaa

    Joined:
    Dec 14, 2010
    Posts:
    492


    Asset Store Link

    OneJS is a Javascript Engine specifically designed for Unity. It is lightweight, performant, pure C# based, has first-class Typescript support, and works everywhere.

    Feature Highlights
    • Use Preact for UI Development in Unity
    • Fast Iteration speed with Live Reload
    • Awesome Performance due to 1-to-1 interop between Preact and UI Toolkit
    • TS Definitions for tons of UnityEngine and UIElements types. We also provide an C#-to-TS Type Converter that will make your Typing life much easier.
    • Works Everywhere (Mac, Windows, iOS, Android, Editor, Standalone, Mono, Il2cpp)
    • Built-in Security when you need it. (Should you choose to give your players scripting capabilities, you can set many security settings such as memory limit, call depth, script timeout, among many others, courtesy of Jint)
    Website & DocumentationDiscordYoutubeTwitter
     
  2. Prodigga

    Prodigga

    Joined:
    Apr 13, 2011
    Posts:
    1,123
    Looks insane mate well done
     
    Singtaa likes this.
  3. HuginnRaven

    HuginnRaven

    Joined:
    Dec 20, 2015
    Posts:
    33
    Looks great! I've toyed with this very concept in my mind, and if I had a better understanding of both Unity and Javascript I would do it myself.

    But I don't, and here you are.
     
  4. Singtaa

    Singtaa

    Joined:
    Dec 14, 2010
    Posts:
    492
    Oh yeah. I pin it on the years of frustration that pushed me to this point. :D
     
    The_Swiss_Guy and HuginnRaven like this.
  5. Nanomid

    Nanomid

    Joined:
    Nov 8, 2014
    Posts:
    15
    Trying to use OneJS in 2022.2.0b1
    Is there an option for the new Input?

    "UI Toolkit is currently relying on the legacy Input Manager for its active input source, but the legacy Input Manager is not available using your current Project Settings. Some UI Toolkit functionality might be missing or not working properly as a result. To fix this problem, you can enable "Input Manager (old)" or "Both" in the Active Input Source setting of the Player section. UI Toolkit is using its internal default event system to process input. Alternatively, you may activate new Input System support with UI Toolkit by adding an EventSystem component to your active scene.
    UnityEngine.UIElements.UIElementsRuntimeUtilityNative:UpdateRuntimePanels ()"

    FYI Simply enabling "Both" fixed this issue.
    Also had to remove UIElementsNativeModule from "Assemblies" as not found.
     
    Last edited: Jul 18, 2022
  6. Singtaa

    Singtaa

    Joined:
    Dec 14, 2010
    Posts:
    492
    I'm not sure if things are different for 2022.2 (I'm downloading it right now to test). But in general yes, to use UI Toolkit with the new Input System, you'd need to:

    1) Enable Input System in Player settings
    2) Import latest Input System package from Package Manager
    3) Have the appropriate Event System in scene (the old Event System will prompt you to convert once UI Toolkit and Input System are detected I think; and the conversion is automatic)

    Right now, Unity is definitely prioritizing the new Input System over the legacy one in terms of features and bug fixes. So may be a good choice to just use the new one for future-proofing.

    ------

    Okay I just finished setting up 2022.2, and it does seem like there are some changes to UI Toolkit. UIElementsNativeModule is no more. I'll add some pre-processors to check for this for 2022.2 editors. Will play around with 2022.2.0b1 a bit more to see if there are other changes. (Thanks for the heads-up!)
     
  7. Nanomid

    Nanomid

    Joined:
    Nov 8, 2014
    Posts:
    15
    Great. Most exciting asset in Unity, from my POV. (I don't know how much $ I've spent on assets, more than $2k for sure).
    I really hope it works out for me. UI Toolkit is kind of busted and this asset is a great buffer.
    Only feature missing is an ALine,Linefy like GPU lines interface for canvas drawing.
    Perhaps an upgrade version? OneJS + Canvas? SVG?

    Thanks for this asset.
     
    Singtaa likes this.
  8. Singtaa

    Singtaa

    Joined:
    Dec 14, 2010
    Posts:
    492
    Thanks a lot! Right now UI Toolkit's Vector API should be a good stand-in for Canvas API (usage is very similar). Doing it in C# can be a bit pain though as you have to wait for domain reload for every code change. With OneJS, you can just do it in Preact like below and have Live Reload on:

    Code (JavaScript):
    1. // Part of {ProjectDir}/OneJS/Samples/sample.tsx
    2. const App = () => {
    3.     const ref = useRef<Dom>();
    4.     const [pos1, setPos1] = useState({ x: 0, y: 0 })
    5.     const [pos2, setPos2] = useState({ x: 0, y: 0 })
    6.  
    7.     useEffect(() => {
    8.         ref.current.ve.generateVisualContent = onGenerateVisualContent
    9.         ref.current.ve.MarkDirtyRepaint();
    10.     }, [pos1, pos2])
    11.  
    12.     function onGenerateVisualContent(mgc: MeshGenerationContext) {
    13.         var paint2D = mgc.painter2D
    14.  
    15.         let { x: x1, y: y1 } = pos1
    16.         let { x: x2, y: y2 } = pos2
    17.  
    18.         paint2D.strokeColor = Color.white
    19.         paint2D.lineWidth = 10;
    20.         paint2D.BeginPath()
    21.         paint2D.MoveTo(new Vector2(x1, y1))
    22.         paint2D.BezierCurveTo(new Vector2(x1 + 180, y1), new Vector2(x2 - 180, y2), new Vector2(x2, y2))
    23.         paint2D.Stroke()
    24.     }
    25.  
    26.     return <div ref={ref}></div>
    27. }
    And I think SVG support is on UI Toolkit's roadmap too. Future should be bright!
     
    Last edited: Jul 19, 2022
    Nanomid likes this.
  9. achimmihca

    achimmihca

    Joined:
    Feb 13, 2016
    Posts:
    283
    Holy wombat, I am deeply impressed with OneJS!!!

    I was wondering whether it is possible to create a React-like development experience with UIToolkit. You proved it is!

    I think, creating VisualElements from JSX is a very smart move because you primarily need a JavaScript engine. Afterwards, TypeScript can be used for TSX compilation and type system (which also leverages existing editor support).

    Also I was thinking that responsive design like in Bootstrap should be possible with UIToolkit because one can check screen size and load style sheets accordingly. However, integrating Tailwind is an even more sophisticated solution. Nice!

    Seriously, kudos to you and the team behind this!

    This re-use of established and proven concepts and technology from web development is also why I think Unity did well with the direction of UIToolkit.

    ---
    However, I suggest to stay with Unity's element names, e.g. VisualElement instead of div.

    From the library point of view, I think you bridge from TSX to UIToolkit, not to HTML.
    Introducing different names may introduce misconceptions (which "HTML elements" are supported) and complexity (Unity devs need to know VisualElement AND that it corresponds to a div in OneJS).

    It should be easy enough for users of OneJS to create a VisualElement named div if they want.
     
    cyhtan and Singtaa like this.
  10. achimmihca

    achimmihca

    Joined:
    Feb 13, 2016
    Posts:
    283
    Any plans to publish OneJS under an open source license at some point (MIT license or similar)?
     
  11. Singtaa

    Singtaa

    Joined:
    Dec 14, 2010
    Posts:
    492
    Yes, but may still be 1+ years away. So we plan to eventually grow OneJS like Deno, build a central repository of OneJS-centric packages, services, and game addons for a community of devs, modders, and players. I think when we start building that repository, that's when we'll open source OneJS. We understand open sourcing it will be crucial to its growth later.

    At that time, it'll still remain a paid package on the Asset Store for earlier access to new features, priority support, and Discord/community perks, etc. At least, that's the plan.

    Yeah, there's definitely some consideration here. So we decided to match stock UI Toolkit controls to base (lowercase) html elements because we wanted to differentiate the C#/built-in ones from Preact components. VisualElement is the base of all controls and div is the most ubiquitous html element. Plus, div is much easier to type.

    There are Pros and Cons to this decision for sure.

    Agreed. That's the major downside of this decision. But I think we can alleviate most of the confusion with good TS definitions for IntrinsicElements.

     
  12. SpindizzyGames

    SpindizzyGames

    Joined:
    Jun 29, 2017
    Posts:
    108
    WebGL support? Will OneJS work in Web GL?
     
  13. Singtaa

    Singtaa

    Joined:
    Dec 14, 2010
    Posts:
    492
    You can build for WebGL with OneJS, yes. The JS interpreter (Jint) and Preact work fine in WebGL per my testings. But I find that UI Toolkit itself is not very stable with WebGL yet. You will encounter weird bugs with some built-in controls and the input system.
     
  14. philip368320

    philip368320

    Joined:
    Dec 8, 2016
    Posts:
    8
    Can this script other components, for example if I buy a component library in the asset store, can it access its apis from javascript?
     
  15. philip368320

    philip368320

    Joined:
    Dec 8, 2016
    Posts:
    8
    In unity 2021.3.6f1 Personal , the benzier curve white line is not showing for me
     
  16. Singtaa

    Singtaa

    Joined:
    Dec 14, 2010
    Posts:
    492
    Hello!

    What do you mean by component library? You can access all .Net classes/namespace/assemblies from Javescript via Jint: https://github.com/sebastienros/jint#accessing-net-assemblies-and-classes

    Example:
    Code (JavaScript):
    1. // From {ProjectDir}/OneJs/Samples/sample.tsx
    2. import { namedColor } from "onejs/utils/color-parser"
    3. import { GameObject, PrimitiveType, MeshRenderer, Vector3 } from "UnityEngine"
    4.  
    5. let plane = GameObject.CreatePrimitive(PrimitiveType.Plane)
    6. plane.GetComp(MeshRenderer).material.color = namedColor("maroon")
    7. plane.transform.localScale = new Vector3(10, 1, 10)
    8.  
    9. var cam = GameObject.Find("Main Camera")
    10. cam.transform.position = new Vector3(0, 30, -60)
    11. cam.transform.LookAt(new Vector3(0, 10, 0))
    The Vector API would require at least Unity 2022.1
     
    Last edited: Sep 24, 2022
  17. philip368320

    philip368320

    Joined:
    Dec 8, 2016
    Posts:
    8
    Hi Singtaa, yes thanks upgrade unity fixed that problem.

    About a component library, I have a library which I can use from Unity which is called Shapes.

    https://assetstore.unity.com/packages/tools/particles-effects/shapes-173167

    Now I wish to use it from within this OneJS, but I'm not sure how to import ... how to use?

    Could I do System.GetType to get the class as I know the class I want is Draw in namespace Shapes, digging deeper I see some interop and security features to allow for reflection

    Thanks, Phil
     
    Last edited: Sep 24, 2022
  18. Singtaa

    Singtaa

    Joined:
    Dec 14, 2010
    Posts:
    492
    @philip368320 Yup, the INTEROP options on the ScriptEngine component will allow you to expose any .Net assemblies/namespaces/classes/objects to be used from JS.
     
  19. Singtaa

    Singtaa

    Joined:
    Dec 14, 2010
    Posts:
    492
    Just gave it a whirl myself. Shapes seems to work pretty well from JS:

    Code (CSharp):
    1. // C#
    2. using System;
    3. using Shapes;
    4. using UnityEngine;
    5.  
    6. public class ShapesDrawer : ImmediateModeShapeDrawer {
    7.     public event Action OnDraw;
    8.  
    9.     public override void DrawShapes(Camera cam) {
    10.         using (Draw.Command(cam)) {
    11.             OnDraw?.Invoke();
    12.         }
    13.     }
    14. }
    Code (JavaScript):
    1. // Typescript
    2. import { Vector3, Color } from "UnityEngine"
    3. import { Draw, LineGeometry, ThicknessSpace } from "Shapes"
    4.  
    5. var drawer = require("drawer")
    6.  
    7. drawer.add_OnDraw(() => {
    8.     Draw.LineGeometry = LineGeometry.Volumetric3D;
    9.     Draw.ThicknessSpace = ThicknessSpace.Pixels;
    10.     Draw.Thickness = 4;
    11.     Draw.Matrix = drawer.transform.localToWorldMatrix;
    12.  
    13.     Draw.Line(Vector3.zero, Vector3.right, Color.red);
    14.     Draw.Line(Vector3.zero, Vector3.up, Color.green);
    15.     Draw.Line(Vector3.zero, Vector3.forward, Color.blue);
    16. })
    Code (JavaScript):
    1. // TS Definition
    2. // Can use the included C# to TS Def Converter to extract all of Shapes.Draw typings if you want
    3. declare module "Shapes" {
    4.     export const Draw: any
    5.  
    6.     export enum LineGeometry {
    7.         Flat2D,
    8.         Billboard,
    9.         Volumetric3D
    10.     }
    11.  
    12.     export enum ThicknessSpace {
    13.         Meters,
    14.         Pixels,
    15.         Noots
    16.     }
    17. }
    Please see onejs.com for docs, workflows, and samples.



     
    Last edited: Sep 25, 2022
  20. philip368320

    philip368320

    Joined:
    Dec 8, 2016
    Posts:
    8
    Wow! it worked for me, thanks
     
    Singtaa likes this.
  21. GamePowerNetwork

    GamePowerNetwork

    Joined:
    Sep 23, 2012
    Posts:
    257
    As a react developer... I have never hit purchase on an asset as quickly as I did here! Thank you so much to your team for sharing this with us!

    I was wondering if there was an issue with your discord link? I tried to join but the link doesn't seem to be working.
     
    Singtaa likes this.
  22. Singtaa

    Singtaa

    Joined:
    Dec 14, 2010
    Posts:
    492
    Tyvm! The Discord link https://discord.gg/Nm5VdgMywa is working well on my side (just tried with a new browser session). Let me know if the problem persists for you. Hopefully it's just a temporary/local issue.
     
    GamePowerNetwork likes this.
  23. GamePowerNetwork

    GamePowerNetwork

    Joined:
    Sep 23, 2012
    Posts:
    257
    Ahh yes it works now. It was a local issue!
     
    Singtaa likes this.
  24. Rytif

    Rytif

    Joined:
    Dec 28, 2012
    Posts:
    8
    Does this have debugging support for the js files? Is there anyway I can hookup some breakpoints?
     
  25. Singtaa

    Singtaa

    Joined:
    Dec 14, 2010
    Posts:
    492
    Not right now. But debugger support based on Jither's work (https://github.com/sebastienros/jint/pull/1113) is in the works. No specific ETA at this point yet, but some time in 2023 is safe to assume.
     
  26. Silly_Rollo

    Silly_Rollo

    Joined:
    Dec 21, 2012
    Posts:
    501
    Have you done any testing on performance when heavily using the ts scripting? This actually looks like a pretty nice alternative to lua for a game built around modding. Typescript is productive enough (imo) you could move a lot of gameplay code to user accessible ts files if the performance is there.
     
  27. Singtaa

    Singtaa

    Joined:
    Dec 14, 2010
    Posts:
    492
    Most of the testing so far has been on the UI side of things. OneJS works pretty well with UI Toolkit since it's retained-mode UI, and you are not forced to do things every frame. I have some general notes about performance considerations here: https://github.com/DragonGround/Ove...9bc9afe886130232ff2037/CharacterStats.tsx#L12

    You can also refer here for benchmarks about Jint, the JS interpreter that OneJS uses.
     
  28. Archi_16

    Archi_16

    Joined:
    Apr 7, 2017
    Posts:
    87
    Hello. Can the script packed into assetbundle which will be loaded from cloud runtime, and will it work on IOS in that case. ?
    As for example other similar plugin(such as Bolt) not supporting this case, as IOS blocks not compiled scripts.
     
  29. Singtaa

    Singtaa

    Joined:
    Dec 14, 2010
    Posts:
    492
    Hi @Archi_16 ! For completeness, I'm just pasting here some Discord messages we already exchanged. Jint is the JS interpreter that's used under-the-hood, and it works on mobile and AOT platforms. OneJS has a built-in bundler that basically bundles your working TS directory and extracts it to persistentDataPath for standalone app runtime. However you want to bundle your scripts is up to you! At the end of the day, they are just text files.
     
    Archi_16 likes this.
  30. jonshan

    jonshan

    Joined:
    Jul 30, 2019
    Posts:
    1
    Thanks for this fantastic asset. I spent lots of time designing complex UI in Unity's UGUI solution. It was a terrible experience most of the time. As a former front-end developer, after I saw your UI solution, I immediately bought it, and I think it has a bright future.

    I like that the separation between UI and logic works great in Unity and Onejs. Also, I believe the most productivity of using OneJs + Preact will be seen in rendering repetitive elements such as lists with 1000+ elements, and 100+ cards... in Unity.

    Although I worked for some time on it, I couldn't quite know how to approach some parts of the architecture. For example, If I want to spawn a popup element that I designed using OneJs, how can I do it as this in the logic part?

    Example:

    Code (JavaScript):
    1.  
    2. ...
    3.  
    4. return (
    5.     <Popup ref={ref} title={popupTitle} desc={popupDesc} />
    6. )
    and in a MonoBehaviour script:
    Code (CSharp):
    1. CreatePopup("title here", "description here");
    In this case, the logic part will be sending the props to the UI, and with that, We'll be able to create as many popups as we want. To implement that, do I need to spawn a UIDocument and set its elements manually in C# since I couldn't find a way to interact with TSX in C#?

    Another example is, after designing a shop panel using OneJs, if I want to create multiple item elements inside it dynamically, how can I do it in C#?

    Example:
    Code (CSharp):
    1. SpawnPanel(cards: new Card[100] { new Card("Sword"), new Card("Pistol") ... });
    This way, the UI components will be mostly modular and will allow me to use the same component in different areas such as one for the shop panel, one for the collections panel, etc.
     
  31. Singtaa

    Singtaa

    Joined:
    Dec 14, 2010
    Posts:
    492
    Thanks for the kind words. And Happy Turkey Day!

    In general, I always separate game logic from UI. With OneJS, I put 100% of UI code in JS and never call UI code from C#. In other words, C# is used for game logic only and, when state changes, events are fired. JS code subscribe to these events and changes the UI accordingly.

    The act of opening a Popup can be kept in JS-land as it's pure UI logic. (Btw, A simple Modal example can be found on the community wiki: https://github.com/DragonGround/ScriptLib/wiki/Simple-Modal-Example )

    (I'm pasting some past conversations from our Discord):

    UI is a way to present game state to the player and also a way for them to make some input to change the game state. In OneJS context, a good (simplified) architecture is like this:



    A good way to think about this is that if you replace UI and Input with AI or Test Actors, everything will still work out very cleanly. Game Logic sits at the top of the dependency chain (it depends on nothing) while UI is always at the bottom (it depends on almost everything: core logic, input, rendering, physics, audio, etc...)

    Changing public APIs in the Game Logic will have a cascading effect on everything that depends on it. But changing UI code will not have any effect on others since nothing depends on it. (This is also why UI code can have the fastest iteration speed, especially as your project grows)

    Anyhow, let me know if this helps. And hop in or search around in our Discord if you haven't already.
     
    xucian, piotrekwitek and jonshan like this.
  32. achimmihca

    achimmihca

    Joined:
    Feb 13, 2016
    Posts:
    283
    I know OneJS implemented loading style sheets from string, which UI Toolkit still does not support.

    Question: Can I use this style sheet loader independently from OneJS?
    Use case: I have my UI already in UI Toolkit, but would like runtime loaded style sheets for user defined themes.

    I am considering to buy OneJS specifically for this feature.
     
  33. Singtaa

    Singtaa

    Joined:
    Dec 14, 2010
    Posts:
    492
    Yes, in OneJS there's a public class and method you can use to build the StyleSheet at runtime. Here's the actual code for OneJS's
    document.addRuntimeUSS(string uss)
    :

    Code (CSharp):
    1. public void addRuntimeUSS(string uss) {
    2.     var ss = ScriptableObject.CreateInstance<StyleSheet>();
    3.     var builder = new OneJS.CustomStyleSheets.CustomStyleSheetImporterImpl();
    4.     builder.BuildStyleSheet(ss, uss);
    5.     if (builder.importErrors.hasErrors) {
    6.         Debug.LogError($"Runtime USS Error(s)");
    7.         foreach (var error in builder.importErrors) {
    8.             Debug.LogError(error);
    9.         }
    10.         return;
    11.     }
    12.     _runtimeStyleSheets.Add(ss); // List<StyleSheet>
    13.     _root.styleSheets.Add(ss); // VisualElement
    14. }
    There is limitation around the use of paths/urls/resources which you can refer to here: https://onejs.com/docs/runtimeuss#limitations
     
    achimmihca likes this.
  34. Sholms

    Sholms

    Joined:
    Nov 15, 2014
    Posts:
    85
    it is possible to use it on world position? for like vr UI?
     
  35. Slashbot64

    Slashbot64

    Joined:
    Jun 15, 2020
    Posts:
    326
    I have this addon but failing to see how I can use it effectively maybe not enough examples, I use ugui and extensions for that like new widgets ui etc.. which add a lot of useful rich components to utilize.. also hate react so that doesn't help but I suppose maybe there is a combination of using other react libraries, suppose I want to see something more than jsut the basics of buttons and sliders.. like a real rich interface with scrollviews and datatables nothing really sells it to me on spending my time with it.