Verse, the new unreal scripting language

As someone who codes in C++ and C# daily, I can tell you there are numerous reasons to prefer a scripting language, but the biggest is probably the ability to quickly write queued code. Take this pseudo-code:

Wait(3.0)
DisplayMessage("Done!")

Ideally, we’d want the program to wait three seconds, then display a message. But in C++, Blueprints*, or C# Unity**, this function will immediately display the message. To get the desired result, your best bet is to use coroutines, which would be difficult-to-impossible using C++ (I’ve tried). It is for this reason that we use Lua in our C++ engine. It supports coroutines with nested yielding.

Based on what we’ve seen, it looks like Unreal Verse will natively support this feature, so that’s reason enough to use it.

*Blueprints have a delay function, but that is the extent of its current capabilities as far as I’m aware. You can’t, for example, make it wait until an object reaches a destination.
**Unity supports coroutines, but it requires special function calls to work correctly, and doesn’t support nested yielding.

1 Like

Hey, I am a noobie and only write code with blueprints so I am probably missing some context, but I dont understand problem you are describing.

If I want code to fire when something reaches a destination - or any event I’ve defined - I just fire off an event to trigger the code?

I’ve used the delay node here and there, but usually thats just for like, UI animations, stuff like that.

I dont know what coroutines and nested yielding means. Is there some practical exmaple you could describe?

Just asking for curiosity/learning.

I mean, if Wait() is calling sleep() behind the scenes in C++, it sure might wait 3 seconds before displaying the message, just as desired… but then you get into entire new fields of issues. Whatever thread you just ran that on isn’t gonna be doing anything for the next three seconds, so you better hope it wasn’t, say, the UI thread…

Which is honestly worse than the scenario where it displays immediately. (“Good job breaking it, hero.”)

Which is a thing you can do in other languages as well – callbacks are an ancient and oft-misused discipline – but it’s not what RealmRPGer means. An example of co-routines would be something like (to use imaginary and oversimplified code):

function FindNewPowerSource()
    ResearchParticlePhysics()
    TakeAWalk()
    HaveASnack()
    EndlessPower = FinishInventingColdFusion()
    yield EndlessPower

function StartingPoint()
    
    if (Battery.GetPercentage() < 20)
       Backup = await FindNewPowerSource()

    RenderPrettyGraphics()

In the case where the battery is too low, it will call that “FindNewPowerSource()” function. If we were just doing it all just calling it straight through, we would hang the entire process for quite a while inventing cold fusion (and pausing for a snack) whenever the battery got low.

“await” basically means “I’ll just stop here and chill until you have that ready for me”, and the FindNewPowerSource() serves as a co-routine. The StartingPoint() function thus stops running and the program can do other things. When FindNewPowerSource() has finished inventing cold fusion and yields endless power (*pause for obligatory maniacal laughter to accompany “ENDLESS POWER”*), now StartingPoint() gets resumed, and continues on with the value it was waiting for.

You can obviously do this with events/callbacks, but then you’re adding new handlers for those events or new functions that are the callbacks. Co-routines are both easier (since all the code necessary for the function to work in that manner is right there in that function), and also vastly easier to follow the flow of without opening six different files to follow how exactly the answer is coming back.

2 Likes

While we’re waiting for Verse to be invented, what is currently the best way to handle “coroutines” in UE?

Depends on what you want to do, I suppose?

Right now, I find myself using the task graph. I define a task to do the work I need, I queue it into the graph, and I tie an event-handler to the completion event.

To let me do this in a generic manner, I have a templated FLambdaTask I created, where I basically define a C++ lambda, pass the lambda into the task, and toss the task into the task graph. As an example (and forgive any mistakes, since I’m writing this right here in a thread posting box rather than, y’know, in a compiler):

// Assume we have an FRocketCalculationJob structure which contains
// the information we want to use in this calculation.
//
// There is also an FLambdaTaskDelegate defined, which takes an 
// FLambdaTask::Result, which is mostly just an FJsonObject (to allow receiving
// arbitrary results), along with a unique task ID.
//

FRocketCalculationJob Job();
Job.Rocket = MakeShareable(SomeRocket);
Job.Fuel = CurrentFuel;
Job.Destination = EPlanets::Neptune;

// QueueTask queues a task, and returns an FLambdaTask::TaskInfo.
auto TaskInfo = FLambdaTask::QueueTask<FRocketCalculationJob>(Job, 
	[] (FLambdaTask::State<FRocketCalculationJob>* TaskState) {

		// This lambda is basically the coroutine body.

		// Get back the Job we passed in, of type FRocketCalculationJob.
		auto JobData = TaskState->GetJobData();

		// Do something I didn't want to do in that main function...
		bool IsViable = UAerospaceMathLibrary::IsDestinationViable(JobData.Destination, 
			JobData.Rocket, JobData.Fuel);

		// Got back our answer. Let's add it to the result. 
		TaskState->Result.AddBoolValue("Viable", IsViable);

		// We exit the lambda. The Task's DoWork then executes the delegate,
		// if it's bound, passing the FLambdaResult
	});

// FLambdaTask::TaskInfo contains a 'Completed' field, which is
// an FLambdaTaskDelegate that I can bind to. Let's do that for
// the one we just set up...
//
// It also contains a unique task ID, so if I was queuing multiple
// jobs bound to the same incoming function, I could store contextual
// data based on the task ID, and then look up the contextual data
// when I got the result. 
TaskInfo.Completed.Bind(this, &UExampleClass::OnViabilityCalculationComplete);

I don’t know if that’s best; truthfully, it’s probably not, because I half-suspect I did something horrifying with the delegate behind the scenes that’s going to bite me someday. And since some of this was written in a fit of insomniac pique, I honestly don’t remember why the task state is a pointer, but…

¯\_(ツ)_/¯

2 Likes

SkookumScript is available as a plugin for UE4, and supports this kind of coroutine handling. It was purchased by Epic, but hasn’t been updated in quite a while. Most people assume that’s because Unreal Verse is meant to replace it.

To summarize a coroutine: A coroutine is a function that doesn’t necessarily complete on a single call. The function may “yield.” This saves a bookmark into the function, and the next time the function is called it will resume at that bookmark. For games this is very useful, as it allows us to complete a task over time by calling the same function each frame.

On nested yielding: There are two ways to support Coroutines. The primary method that most languages use requires a call chain, meaning all outer functions must know/request the yield:

function DoStuff()
	yield TakeABreak()

function TakeABreak()
	yield Wait(3.0)

function Wait(time)
	elapsed = 0
	while elapsed < time
		elapsed += delta_time
		yield false

The second, smarter method, doesn’t have this requirement. Any yield within a coroutine call chain is picked up. This is nested yielding.

function DoStuff()
	TakeABreak()

function TakeABreak()
	Wait(3.0)

function Wait(time)
	elapsed = 0
	while elapsed < time
		elapsed += delta_time
		yield false
1 Like

Translating your message to what i readed:

You want the unreal engine team to work for years on a scripting language because you don’t know how to use delegates or haven’t came up with anything creative?

Besides all that, if you are using hard coded timers to do something, you may not be doing the thing in a nice way

Would I be wrong in thinking that it would be close to as performant as lua? Verse will def not be as fast as c++ but I think that it should be faster than bp.

It’s the second half of 2022, and I’m dying to hear anything new about Verse.Hope the posts don’t disappear and there are more people to discuss

Well there is nothing to discuss right now. The release of Fortnite Creative 2.0 might include Verse, which would be the first significant data point we are likely to get about Verse.

All these recent posts on this thread kept getting me excited that there was some news! Guess not. :frowning:
Unfortunately, our game is in a holding pattern right now. We really need Verse, as what we’re trying to do is far too complex for Blueprints, and too design-heavy for C++.

Are we even sure Verse will be capable of replacing blueprints and C++ in most cases? It may be limited especially early on, as what it’s really being built for currently is level and game mode scripting. First release or two may not be stable or documented enough for production usage, as well…

Chances are C++ would still be required for good performance, in which case you could be better off finding good abstractions to allow C++ behaviour to be driven by Blueprint or data-driven configuration.

1 Like

The goal for us is to use Verse for its intended purpose: Scripting. We previously got a UE4 prototype up and running that used Lua, but it was cumbersome. If Verse is even half as good as SkookumScript, it will be an upgrade. It doesn’t make sense for us to spend the time it would take to better integrate Lua, especially when there are other projects for us to work on in the meantime. Lua has amazing coroutine support and is very efficient, but it’s really starting to show its age. I’d rather not continue to work with it if there’s an alternative.

The SkookumScript developers have heavily implied that they expect Verse to a be a full replacement for Skookum, so it also doesn’t make sense to try to roll our own SS integration into UE5. At a bare minimum, “wait and see” seems like the best approach. Again, we have other stuff we can work on in the interim.

1 Like

We are still working away on Verse. Would love to say more than any hype though all that can be said at the moment without any other public announcements is that we are not idle. :grin:

9 Likes

thanks for the update!

Hopefully there will be recursive structs, first class functions and ADTs, and good interfacing with C++ for that. Blueprint doesn’t support a lot fundamental things for some reason. You can’t even create an array of arrays.

Just use FSharp

Immutable data structures, curried by default, discriminated unions, records, full type inference, clean syntax, great performance, good tooling and a heckton more.

https://fsharp.org/testimonials/

Much fun

cho choo ! incoming news about Verse:

I’m also posting new findings about Verse on my twitter and dedicated subreddit:
r/uefn

2 Likes

Personally wouldn’t be much worried. There are many experienced folks there and company soaked deep in gamedev which should suggest us that gamedev developers would find language coherent with domain it steems from.
I’d rather argue that end user, meaning non code exposed creators just might have steeper learning curve cause of inherited complexity of sth trying to be less ‘generic’ and more of an DSL language. I guess though this also was taken into consideration.
I’d argue we might get visual scripting for UEFN/Fortnite to lessen a burden, plus it might make sense to expose scripting functionality for other platforms. There are some very early signs of this coming with leaks like console/mobile key bindings, ScriptDevice, Epic hiring posts etc.

1 Like