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

https://vimeo.com/708468718

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 & Documentation • Discord • Youtube • Twitter

8 Likes

Looks insane mate well done

1 Like

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.

Oh yeah. I pin it on the years of frustration that pushed me to this point. :smile:

2 Likes

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.

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!)

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.

1 Like

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:

// Part of {ProjectDir}/OneJS/Samples/sample.tsx
const App = () => {
    const ref = useRef<Dom>();
    const [pos1, setPos1] = useState({ x: 0, y: 0 })
    const [pos2, setPos2] = useState({ x: 0, y: 0 })

    useEffect(() => {
        ref.current.ve.generateVisualContent = onGenerateVisualContent
        ref.current.ve.MarkDirtyRepaint();
    }, [pos1, pos2])

    function onGenerateVisualContent(mgc: MeshGenerationContext) {
        var paint2D = mgc.painter2D

        let { x: x1, y: y1 } = pos1
        let { x: x2, y: y2 } = pos2

        paint2D.strokeColor = Color.white
        paint2D.lineWidth = 10;
        paint2D.BeginPath()
        paint2D.MoveTo(new Vector2(x1, y1))
        paint2D.BezierCurveTo(new Vector2(x1 + 180, y1), new Vector2(x2 - 180, y2), new Vector2(x2, y2))
        paint2D.Stroke()
    }

    return <div ref={ref}></div>
}

And I think SVG support is on UI Toolkit’s roadmap too. Future should be bright!

1 Like

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.

2 Likes

Any plans to publish OneJS under an open source license at some point (MIT license or similar)?

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.

WebGL support? Will OneJS work in Web GL?

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.

Can this script other components, for example if I buy a component library in the asset store, can it access its apis from javascript?

In unity 2021.3.6f1 Personal , the benzier curve white line is not showing for me

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:

// From {ProjectDir}/OneJs/Samples/sample.tsx
import { namedColor } from "onejs/utils/color-parser"
import { GameObject, PrimitiveType, MeshRenderer, Vector3 } from "UnityEngine"

let plane = GameObject.CreatePrimitive(PrimitiveType.Plane)
plane.GetComp(MeshRenderer).material.color = namedColor("maroon")
plane.transform.localScale = new Vector3(10, 1, 10)

var cam = GameObject.Find("Main Camera")
cam.transform.position = new Vector3(0, 30, -60)
cam.transform.LookAt(new Vector3(0, 10, 0))

The Vector API would require at least Unity 2022.1

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.

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

@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.

Just gave it a whirl myself. Shapes seems to work pretty well from JS:

// C#
using System;
using Shapes;
using UnityEngine;

public class ShapesDrawer : ImmediateModeShapeDrawer {
    public event Action OnDraw;

    public override void DrawShapes(Camera cam) {
        using (Draw.Command(cam)) {
            OnDraw?.Invoke();
        }
    }
}
// Typescript
import { Vector3, Color } from "UnityEngine"
import { Draw, LineGeometry, ThicknessSpace } from "Shapes"

var drawer = require("drawer")

drawer.add_OnDraw(() => {
    Draw.LineGeometry = LineGeometry.Volumetric3D;
    Draw.ThicknessSpace = ThicknessSpace.Pixels;
    Draw.Thickness = 4;
    Draw.Matrix = drawer.transform.localToWorldMatrix;

    Draw.Line(Vector3.zero, Vector3.right, Color.red);
    Draw.Line(Vector3.zero, Vector3.up, Color.green);
    Draw.Line(Vector3.zero, Vector3.forward, Color.blue);
})
// TS Definition
// Can use the included C# to TS Def Converter to extract all of Shapes.Draw typings if you want
declare module "Shapes" {
    export const Draw: any

    export enum LineGeometry {
        Flat2D,
        Billboard,
        Volumetric3D
    }
 
    export enum ThicknessSpace {
        Meters,
        Pixels,
        Noots
    }
}

Please see onejs.com for docs, workflows, and samples.

Wow! it worked for me, thanks

1 Like