Search Unity

MiniScript: lightweight scripting language for your game

Discussion in 'Works In Progress' started by JoeStrout, Dec 14, 2015.

  1. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    8,126
    Brilliant — in your case, there was no need to make a map to map function names to functions, because the built-in globals map was already doing the job!
     
  2. mf_andreich

    mf_andreich

    Joined:
    Jul 7, 2017
    Posts:
    23
    Hi. I look at documents and dont find information about how I can call c# methods/delegates from MiniScript... Its possible? (dont have your asset - just thinking about scripting language for mods)
     
  3. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    8,126
    Yes, you do that by adding new "intrinsic functions" to the MiniScript environment. See chapter 2 of the MiniScript Integration Guide.
     
  4. deab

    deab

    Joined:
    Aug 11, 2013
    Posts:
    83
    Hi Joe

    Currently looking at MoonSharp for lua, but that may be overkill for my intended use so interested in MiniScript.

    Using MiniScript can I access Unity structs (Vector2Int for example), my custom classes and structs from within the script?

    Can I pass a reference to a class in to a script, then call methods on it? Or does it need to be static?
     
  5. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    8,126
    After learning Lua pretty deeply, I would consider it "underkill" rather than "overkill," at least in terms of language features. :p

    But to answer your questions: you can't directly access any C# objects or data from the MiniScript side; MiniScript runs in a sandbox. What you do instead is expose access to just the parts you want MiniScript to have access to, by one (or more) of these approaches:
    1. Define new intrinsic functions, which call from MiniScript code into your C# code, and return data back. (These calls can even block the MiniScript code for an extended time if you like, e.g. a "MoveTo" method that doesn't return until the object has taken the specified amount of time to move to the target position.) Or,
    2. Stuff values into, or read values from, any MiniScript map (including the global namespace).
    These can be used in combination, for example, you could define intrinsics that are only intended to be used as methods on a class, and stuff the class itself (which is really just a map in MiniScript) into the global namespace. Then users would instantiate your class and interact with it that way.

    So it boils down to: you define exactly how you want MiniScript code to interact with your code.
     
  6. deab

    deab

    Joined:
    Aug 11, 2013
    Posts:
    83
    Thanks for the prompt response. So a Vector3 would need to be split to 3 floats in a map/array? I think it's going to be far too much wrapper code required for my use case (proc gen using a number of classes, structs and enums).

    I will however be keeping an eye on your progress, maybe an option in the future.
     
  7. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    8,126
    Yes, you'd probably want to make a map containing x, y, and z, very similar to the ship map in the demo.

    Good luck with your project — I hope we see you back again soon! :)
     
  8. Colin_MacLeod

    Colin_MacLeod

    Joined:
    Feb 11, 2014
    Posts:
    76
    I'm looking for a flexible modding library or framework, ideally something we can mold to our specific needs.

    Really like what you have done with MiniScript, Joe. I was leaning towards Lua as I've used that in the past (and it's impressively lightweight). However, MoonSharp looks pretty dormant - and it's almost impossible to find any thread on Unity with Lua without someone (sometimes, though not always, your good self) recommending MiniScript as a better solution.

    There are a couple pieces of the puzzle I'm still looking for:

    1) Is it possible for us to extend MiniScript - do you include the source code in the asset package?
    2) What support, if any, is there for using MiniScript to import assets at runtime? I'm thinking about 3D models and audio files, say
    3) Is MiniScript purely interpreted? Does that not cause issues on iOS (personally, not a major concern - I'm just curious). What about console platform support - Xbox-One, PS-4 and, particularly, Nintendo Switch?
    4) You've developed your own syntax - is there any editor support? Particularly syntax highlighting VS Code and/or Sublime?
    5) What about debugging - is there any runtime debug (breakpoint / watch) support?
     
    JoeStrout likes this.
  9. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    8,126
    Hey there! Thanks for considering MiniScript. In answer to your questions:

    1) Yep, source is included, though you can extend it most ways without modifying the source — see the Integration Guide.
    2) MiniScript doesn't add any new abilities with regard to importing models or other assets. So, you'd have to stick to what you can otherwise find some way to read. OBJ is a pretty easy format to read for models, for example.
    3) It's bytecode-compiled. And as far as I can tell, that's OK these days on iOS, as long as it's not used to create the entire app.
    4) We don't have any syntax support in VS Code or Sublime yet, and since I don't use those tools, I'm not really in a position to add it. But I'd definitely be interested in helping/supporting anybody else who wants to take a stab at it. I do have some MiniScript support for BBEdit, if that's your thing, as well as some online editors like this.
    5) Breakpoints and watchpoints, not so much I'm afraid. I debug mainly via
    print
    .

    I hope this helps clear things up — feel free to follow up with more questions, if you have any!
     
  10. Colin_MacLeod

    Colin_MacLeod

    Joined:
    Feb 11, 2014
    Posts:
    76
    Hey, thank you very much for getting back to me tonight, Joe.

    How does using bytecode-compiled code affect acceptance on any of the consoles? Do you know whether that can be an issue at all?
     
  11. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    8,126
    I don't know, but — how would you get user files (such as mods) onto a console anyway? I thought they were pretty tightly locked down.
     
  12. Colin_MacLeod

    Colin_MacLeod

    Joined:
    Feb 11, 2014
    Posts:
    76
    I was thinking of writing discete parts of the game using MiniScript and, if able to build a modding following on other platforms (PC/Mac) incorporating those mods into console versions.
     
  13. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    8,126
    That makes sense. Well, I'm afraid I don't have any experience shipping a MiniScript-using game on consoles. I would guess that there would be no objection, but I can't guarantee it. (Of course the same cautions would apply to Lua or any other runtime scripting language.)
     
    Colin_MacLeod likes this.
  14. Colin_MacLeod

    Colin_MacLeod

    Joined:
    Feb 11, 2014
    Posts:
    76
    Fairy snuff. I just bought MiniScript and look forward to trying it out.

    I'd like to have a go at writing a syntax highlighter for VS Code in particular - no idea how to that but how hard can it be, right?
     
    JoeStrout likes this.
  15. Colin_MacLeod

    Colin_MacLeod

    Joined:
    Feb 11, 2014
    Posts:
    76
  16. FirstTimeCreator

    FirstTimeCreator

    Joined:
    Sep 28, 2016
    Posts:
    717
    lol, what is the point of this exactly?

    I don't think purposefully slowing down your game with a script interpreter is the right way to go. I mean it's cool from a programming standpoint but not really practical to use in an actual game.

    Both C# and JS already do everything your miniscript does and it's faster, and with autofill in visual studio it's just as fast to write.
     
  17. Colin_MacLeod

    Colin_MacLeod

    Joined:
    Feb 11, 2014
    Posts:
    76
    Well, that's a fair enough question, I suppose. There can be different use cases.
    For me personally, I want to be able to let players script mini games within my game.
     
  18. FirstTimeCreator

    FirstTimeCreator

    Joined:
    Sep 28, 2016
    Posts:
    717
    Ah I see. Why not make an in-game node editor then? I think that could be a good option for non-programmers. Design your script interpreter around making use of an in-game node editor.
     
  19. Colin_MacLeod

    Colin_MacLeod

    Joined:
    Feb 11, 2014
    Posts:
    76
    Sure - that's a good idea. But it's easier and quicker to implement an existing scripting language which already hooks into the Unity framework.
     
  20. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    8,126
    Almost. Since then I've also added
    true
    and
    false
    . Pop open MiniscriptKeywords.cs to see for yourself.

    So coloring those is a good first step. Then you'll probably want to indent on code blocks. Those are mostly pretty easy:
    if
    /
    end if
    ,
    for
    /
    end for
    , and
    while
    /
    end while
    . Functions may be a little trickier, because
    function
    doesn't usually appear at the start of a line (unlike
    if
    ,
    for
    , and
    while
    ). So tackle that one last. :)

    Finally, you might want to consider also coloring intrinsic functions. You can find these in MiniscriptIntrinsics.cs, plus any additional intrinsics you add for your game.
     
  21. Colin_MacLeod

    Colin_MacLeod

    Joined:
    Feb 11, 2014
    Posts:
    76
  22. Colin_MacLeod

    Colin_MacLeod

    Joined:
    Feb 11, 2014
    Posts:
    76
    It's just highlighting at the moment, doesn't do anything else
     
  23. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    8,126
    That's fantastic! Thanks, Colin, that's really very cool. I see even functions didn't stump you. Great work!
     
  24. Colin_MacLeod

    Colin_MacLeod

    Joined:
    Feb 11, 2014
    Posts:
    76
    Thanks. It was actually fairly straightforward; I used the VS Code Lua highlighter as a basis.
     
    tosiabunio, aer0ace and JoeStrout like this.
  25. HBS-GARRET

    HBS-GARRET

    Joined:
    Oct 5, 2018
    Posts:
    1
    Joe,

    First, MiniScript seems really cool!

    I know you can set values to be consumed in C# e.g. in Robo-Reindeer you defined that speed and heading are values that can be set in MiniScript. Continuing with the Robo-Reindeer, what if I wanted to also use the script to define: the name of the reindeer, the sprite used, color to render, etc. Would you recommend using MiniScript for also defining data for certain objects?
     
  26. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    8,126
    Absolutely, you can pass as much data as you want! I have an (unfinished) project that basically recreates Scratch in Unity, with MiniScript in place of the block-based scripting. So the MiniScript code can move sprites around, change their costume, change scale or color tint, check for collisions, etc.

    That's a more extreme example than what most games would need, but yes, you can certainly use scripts to define any data you want to be script-configurable.
     
  27. siman007

    siman007

    Joined:
    Apr 13, 2017
    Posts:
    4
    Joe, I was just looking at building my own Scratch style interface for miniscript- if you have done one is it available in Git? Could I use it as the basis for mine? Thanks
     
  28. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    8,126
    No, my project didn't have a Scratch style interface; it used MiniScript instead of block-based coding. Also, it's not available in Git. :)

    But good luck with your project! It sounds really interesting. Keep us posted as it develops!
     
  29. sledgeman

    sledgeman

    Joined:
    Jun 23, 2014
    Posts:
    372
    I am new to coding. Just learning C# basics for Unity. I guess i didn´t get everything about miniScript, why to use it instead of the regular Unity-c#. It is, because its simpler, therefore faster to write your stuff down !? Do you got same performance as with Unity-C# ? You are able to modify code at runtime, without to compile again (sandbox) !? And so on ...
     
  30. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    8,126
    Hmm, if you're new to Unity and coding, then I don't imagine you'll have much use for MiniScript. It's a runtime scripting engine that would let players write code for your game (for example, for mod support or to make a programming game). It's not something you use to actually make your Unity game; it's a new feature you add to your game.
     
  31. sledgeman

    sledgeman

    Joined:
    Jun 23, 2014
    Posts:
    372
    Ah ok. I assumed it goes somewhere in this direction, but wasn´t 100percent sure. Thx for clarifing :)
     
    JoeStrout likes this.
  32. tiggus

    tiggus

    Joined:
    Sep 2, 2010
    Posts:
    1,232
    Hi Joe,

    I've got a programming game project I've been fiddling with off and on and this looks like it fits the bill perfectly. Just a couple questions when you have a moment:

    1) Does this work with the latest versions of Unity? 2018.2.16f etc.
    2) Does the script interpreter need to be on a monobehavior? Ie. could I just have a scriptmanager class that manages lists of scripts? It seems so just wanted to check.
     
    JoeStrout likes this.
  33. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    8,126
    Yep, no problems.

    No, it doesn't have to be. MiniScript itself is actually defined independent of Unity; I use it in some pure C# projects from time to time. So you can attach the scripts wherever is convenient for you.

    Yes, that'll work fine.

    Best,
    - Joe
     
    tiggus likes this.
  34. unity_xrPBQ-2zcfd-DA

    unity_xrPBQ-2zcfd-DA

    Joined:
    Jun 15, 2018
    Posts:
    17
    I have an unity app which integrates with android native so on button click from android it goes to unity part. I have multiple scenes in my app and with time there will be new scenes and feature will be added so surely there will be new scripts and changes are made in the script but I don't want my users to update app everytime I made changes. So is miniscript is useful for me. Can I just attach scripts written in miniscript with my asset bundle run them on android.
     
  35. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    8,126
    Yes, absolutely. This is one of the few ways you can actually include new/different code in an asset bundle.
     
  36. unity_xrPBQ-2zcfd-DA

    unity_xrPBQ-2zcfd-DA

    Joined:
    Jun 15, 2018
    Posts:
    17
    This is one of the few ways means there's other ways as well can you please tell me what are those other ways?
     
  37. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    8,126
    The other ways would be other scripting plug-ins, I guess (including writing your own from scratch). Or dynamic C# compiling, if you're on a platform that allows that (not all do).
     
  38. JSwigartPlayful

    JSwigartPlayful

    Joined:
    Feb 29, 2016
    Posts:
    13
    I'm thinking about trying MiniScript for a modding environment, but I'd like to get some clarity on a few details.

    Can you first confirm or correct some understandings I have so far.

    An interpreter can only reach outside its global state(into unity, etc) via Intrinsic functions right?
    An interpreter has a single global state I assume, correct?
    Can you load/compile to bytecode a script snippet, and then selectively call miniscript functions in it? For example a tick function, event callbacks, etc.

    Suppose a game had 10 NPCs, and each of them doing something differently. Is the intent for each AI to have its own script interpreter that contains the script state of that AI? If so, what is the overhead for Interpreter instances? Can interpreters share the same compiled byte code? (effectively making them share all but the global variable tables and such?)

    I ran into a similar issue when I was tinkering with MoonSharp, and the overhead of a bunch of script instances was going to be cost prohibitive because there was a non trivial setup cost in memory mostly with creating isolated script environments, plus that approach is garbage for allowing scripts to cross talk or send data back and forth.

    Does Moonscript support substituting the global table when you execute script code? What I've done in the past that is really nice is that each instance in situations like this was overriding the global table, such that the entire execution environment was effectively scoped to an object of my choice, without the separate interpreters and issues that brings. It also means that although they are hidden from each other by default, I could expose functions to the scripts where I could return the entity#1's object to entity#2.
     
  39. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    8,126
    Right.

    If you mean "global" from the perspective of the MiniScript code running inside it, yes. Of course from Unity's perspective, you could have many interpreters, each with their own global state (so that state is not global at all, in C# land).

    Yes. There are a couple ways to do that; the manual (and a demo scene in the asset) shows one approach.

    Yes, that's the standard approach. The overhead is pretty small: just the compiled code (which is quite tiny compared to the textures, sounds, etc. you already have in your project) and whatever variables are in the current state.

    Hmm. I believe they could, though it might require a bit of hacking. I'd be happy to help you do that if it seems worth it, though I think once you look at the size of the bytecode, you may decide it's not worth the bother.

    In MiniScript it's quite easy to set them up with some shared data. Your C# code can get and set global variables in the script contexts. So you could easily create a shared blackboard (or whatever you want to call it) map (i.e. dictionary) and stuff that into the context of every script. All scripts (as well as your C# code, for that matter) would then be able to read and write that shared map. And because MiniScript objects live in the C# heap, you can even pass around objects (maps) or lists this way.

    Yes, this is possible. What you're essentially doing there is setting up a threading environment, where you have multiple threads (interpreters) running with shared global state.

    However it's a bit of a hack, and not as clean as setting up a specific shared-data path like that above. There are other ways to do it too; you could set up a messaging system, for example, that lets you pass messages (as arbitrary MiniScript data) from one instance to another by making a call. We could probably come up with more ideas if none of these fit the bill!
     
  40. DrEvil

    DrEvil

    Joined:
    Aug 11, 2012
    Posts:
    18
    For the global table substitution, I was thinking more along the lines that as each script instance is updated, prior to calling into script it would substitute the global table with a map of data specific to that entity, with the goal of basically isolating each instance from doing anything destructive to the global state(without separate interpreters). Sorta like treating the global state as member variable storage of that instance. The thinking is that if that were possible then I'm wondering if only 1 interpreter would be necessary to load in a particular script and execute them across any number of instances of that entity.

    Basically the use case I am looking for modding is one where long term there would be a steam workshop level propagation of community modding, and I was wondering what degree of protection, if any, there might be between different discrete mods. I suppose the most protected they would be is by using their own script interpreter, I was just wondering of alternatives after having seen unacceptable memory bloat from making new script instances in due to each instance needing to have the bindings configured for it. It sounds as though that may not be of concern here, since if I understand correctly, the intrinsics are a set of static data, not data specific with the interpreter instance.
     
  41. DrEvil

    DrEvil

    Joined:
    Aug 11, 2012
    Posts:
    18
    Separate question that overlaps the above, am I correct to assume that the interpreter tracks stack information that might persist across calls to RunUntilDone(like if the script returns early due to waits, etc) ?

    If so, doesn't this mean that you may need to have a unique interpreter for each call into script that may not complete?

    For example, if you have an interpreter that you run a Tick function for each frame on an entity, and it has the possibility of returning part way through due to a wait or hitting the time limit, it seems like you would have to dedicate that interpreter to maintaining that state, and if you had any event functions like TakeDamage defined in script, you'd need to instantiate a new interpreter to execute that function on. If you did it on the same interpreter you'd be stomping the current state of that update stack?

    Am I missing something, or is that a reasonable interpretation for how it would work in that situation?

    This sorta thinking is what prompted the other two questions. I've used a lot of scripting languages over the years and I'm trying to map what I know about them onto this one. For instance, I take the Miniscript interpreter to be akin to the LuaState in lua. One big difference here that I love is that your bindings are all static(if I understand correctly), so that's already a big win in terms of the overhead of creating these, however what I'm also used to seeing in languages that provide functionality of interrupting executing scripts, is that those scripts run on a coroutine object, which is basically responsible for tracking the stack and progress of the script for later picking up where you left off. Not seeing that intermediary layer here of the coroutines has me wondering whether effectively your interpreter instance is effectively the coroutine, which makes some sense to me for a language that makes that pause/resume functionality standard across the language, but I would also expect there to be a sharing/caching mechanism on the front end somewhere so that if I have 1000 instances of an entity, I don't have to load, compile, and execute the same block of text 1000 times.

    My use case is a voxel game, so many instances of certain blocks, or AI scripts are to be expected, and part of the reason why I'm looking for an interpreted scripting language is so that I can effectively hot swap the scripts for rapid iteration. It seems like it would be much more preferable if there were a single place to compile a given script, after which I would need to re-init those 1000 interpreters with the new script, as oppposed to doing the compile + reinit 1000 times.
     
  42. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    8,126
    So, to your first point: the overhead of a script interpreter is pretty small, comparable to any other Unity component. As you noted, the intrinsics are all static data; the only data associated with an interpreter is the code (unless we make a way to share that) and the context data of the running script (which you would need in any case).

    And yes, that context data includes the call stack as well as the variables at each level thereof. This stuff is stored together in a "MiniscriptContext" object that an interpreter keeps a reference to. So, if we did want to hack out a way to use one interpreter to service several scripts, it would be by swapping out this context object. But the interpreter has almost no data apart from that, so I doubt it's worth the trouble.

    (There are no coroutines per se in MiniScript; the time-slicing is done just by keeping the call stack and data in these context objects, which allows the interpreter to jump back in whenever they are told to execute some more. Intrinsic functions have to be written in such a way that if they're going to take any appreciable time to complete their work, they bail out with a "partial result" and get re-invoked again until they report that they are done.)

    It is certainly possible, with the interfaces provided, to reset an Interpreter without recompiling the code. So if you are in a situation where you want to compile only once and share that compiled code across many interpreters, I'm certain that is doable — though I'm not certain at the moment if the interfaces you would need to do it are exposed publicly. If not, some minor changes would fix that. MiniScript is provided as source code, and I'd help you do it (and then probably roll those changes into the next update).
     
  43. unity_xrPBQ-2zcfd-DA

    unity_xrPBQ-2zcfd-DA

    Joined:
    Jun 15, 2018
    Posts:
    17
    Okay Thanks
     
    JoeStrout likes this.
  44. ifayad

    ifayad

    Joined:
    Jun 15, 2017
    Posts:
    24
    Hello @JoeStrout ,

    I am interested in adding a scripting engine to the game I am working on, and came across your asset. I also found another contender which is the moonsharp project. After learning more about Lua and miniscript, I found the syntax of the latter to be easier for non programmers, so I am leaning towrads it (besides the moonsharp project seems to be abandoned). However, before making the purchase, I just have one question, can we add classes from c# to the miniscript interpreter the same way we add intrinsic functions ? I saw in the demo that the ship is a class with some properties and a function, how were they implemented ?

    Regards
    Bob
     
    JoeStrout likes this.
  45. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    8,126
    Hi Bob,

    Yes, you can add your own classes. A class in MiniScript is just a map (optionally with a special
    __isa
    entry to define the superclass). And all MiniScript types (including maps) are available in C#. So there are two easy ways to do this: in C# code, create a ValueMap, and fill it with the methods and properties you want your class to have; or define the class in MiniScript itself, and insert this before the user's code.

    In the ship demo, I used the first approach (set up the map in C#). But both ways work fine.
     
  46. ifayad

    ifayad

    Joined:
    Jun 15, 2017
    Posts:
    24
    Wonderful, Looks like I am sold :D...
     
    JoeStrout likes this.
  47. ifayad

    ifayad

    Joined:
    Jun 15, 2017
    Posts:
    24
    Hello @JoeStrout ,

    While playing with MiniScript, I noticed there is no implementation of a do while block. There is a repeat keyword but no until and it's not handled anywhere (checked the lexer and parser). Am I missing something ?

    Edit: After looking at the Parser, I think that it's easy enough to add. I don't think I should touch the interpreter since it's a three address instruction interpreter and all the base is there. I'll tackle it when I have some time :D.

    Cheers
     
    Last edited: Jan 16, 2019
  48. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    8,126
    Perhaps you missed
    while
    ? For example:

    Code (miniscript):
    1. s = "Spam"
    2. while s.len < 50
    3.   s = s + ", spam"
    4. end while
    5. print(s + " and spam!")
    This example from the Quick Reference (and also in the MiniScript Manual) builds a string (s) by adding more spam to it, as long as the string length is less than 50. Once it is no longer less than 50, the loop exits, and the result is printed.

    There are also break and continue keywords to bail out of the loop (break) or skip to the next iteration of the loop (continue).
     
  49. ifayad

    ifayad

    Joined:
    Jun 15, 2017
    Posts:
    24
    No I noticed the while loop; I was talking about the:
    do
    {
    Statement/s
    } while(condition);

    Which ensures that the block will run at least once. I Know we can use the while bloc but the do-while has its uses
     
  50. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    8,126
    Ah, well it does, but keeping MiniScript's language minimal is an important design objective. We generally try to avoid having more than one obvious "best" way to do anything (which brings various benefits I'd be happy to get philosophical about if you like!).

    So if you need to check at the end of the loop, rather than at the beginning, then in MiniScript you would just do

    Code (miniscript):
    1. while true
    2.   Statements
    3.   if condition then break
    4. end while
     
    tosiabunio likes this.