Search Unity

I need some critique from a programmer on a layout for a simple....'interpreter' I think it's called

Discussion in 'Scripting' started by astracat111, Oct 9, 2016.

  1. astracat111

    astracat111

    Joined:
    Sep 21, 2016
    Posts:
    725
    Hello there,

    After a year of using RPG Maker MV for my game I'm finally moving over to using Unity. So before learning the language, since I've done game programming for about 10 years off and on, I thought I would write out a kind of system for an in-game cutscene maker. Why a cutscene maker? Well, because my game is about 95% cutscenes with 5% qte action gameplay.

    I needed some critique on a basic layout before I actually delve into implementing this into Unity. It basically uses RPG Maker's system of creating cutscenes, as RPG Maker is basically just a cutscene creator anyway in my eyes.

    So the first thing I'm thinking of doing is creating what rpg maker calls an 'interpreter', although I think it's better called something like an 'event manager' because I'm not sure that that's exactly what an interpreter is usually called in the programming world.

    The point of having a main game interpreter is that one thing happens at a time in game, so that it's not just control of manipulating space, but complete control of manipulating space and time, thusly having you able to make a cinematic and tell a story.

    The idea is to create two caterpillar arrays:

    Main Interpreter - This array or data structure holds all of the strings that will trigger the functions from the Unity api to make stuff actually happen in Unity.
    Bin Interpreter - This array is Main's 'trash bin', kind of. It collects each string/event that's skimmed off of Main Interpreter. I don't know if this will be totally necessary, but in an editing program this is like your Undo bin, so that you can hit Undo, which will push the last entry from this Bin back into the Main Interpreter.

    I'm going to post two layouts. The trigger for the game is when a scene/map loads. The player object is placed within one of the scenes/map of the game.

    Here's what I've got so far. I'm mainly looking for critique from an experienced programmer for this outline:

    Code (JavaScript):
    1. On scene load:
    2.     Do: Set screen to loading image.
    3.  
    4.     Check: Is Save Game Being Loaded?
    5.         If It Is:
    6.             Load Main Game Database from Save File into Main Game Database in game.
    7.                 (includes Map data for every map in game)
    8.             Load Misc Database from Save File into Misc Database in game.
    9.             Load Main Interpreter from Save File into Main Interpreter in game.
    10.             Load Bin Interpreter from Save File into Bin Interpreter in game.
    11.         If It Isn't:
    12.            Using the Unity scene, pour events into Main Game Database. (this scene is starting for the first time)
    13.    Check: Debug mode?
    14.        Do: Place all object transform data based on data from Main Game Database
    15.        Check: Object trigger modes?
    16.            Do: Objects with Parallel Process gets it's own interpreter created with it's
    17.                events loaded into it.
    18.            Do: Objects with Autorun gets loaded into Main Game Interpreter.
    19.  
    20.    Check: Are all objects placed?
    21.        Do: Wait 3 seconds for good measure, then...
    22.        Do: Add Event::FadeIn::Frames:: string into first entry in Main Game Interpreter.
    23.  
    24.  
    25.    Loop:
    26.        Do: Take first line from Main Game Interpreter.
    27.        Do: Cut the string into arguments for the appropriate function.
    28.  
    29.        Check: Is this a proper script?
    30.            If It Is:
    31.                Do: Remove this entry from the Main Game Interpreter Database, next entry gets pushed to the front of the line.
    32.          
    33.                Loop:
    34.                    Do: Execute the appropriate function based on what the line said in the Interpreter. Stay in this loop
    35.                        until the function returns an "end" or 0 or whatevers necessary to break this loop.
    36.      
    37.            If It Isnt:
    38.                Do: Return an error message detailing what the string says like "Error in Main Game Interpreter at:".
    39.  
    The general idea is that the Main Game Interpreter takes the first entry from the top of it, cuts the string up into arguments and passes it to a function, then the Main Game Interpreter shrinks appropriately, throwing away the top string it just processed into the Bin Interpreter.

    Here are the events that I'm going to implement into Unity to make this cutscene creator. These take directly from RPG Maker:

    Code (JavaScript):
    1.     [Basics]
    2.     Wait::Seconds::
    3.     Event::FadeIn::Frames::
    4.     Event::FadeOut::Frames::
    5.  
    6.     [Interpreter Commands]
    7.     InterpreterCreateLabel::LabelName::
    8.     InterpreterSkipTo::LabelName::
    9.     InterpreterRewindToLabel::LabelName::
    10.     InterpreterRewindByNumberofEvents::NumberofEvents::
    11.  
    12.     [Save/Load]                 ***** Important to tackle this first and foremost
    13.     Event::SaveGame::
    14.     Event::LoadGame::
    15.  
    16.     [SFX]
    17.     Event::PlaySE::Name::
    18.     Event::StopSE::Channel::
    19.     Event::PlaySEEx::Name::Channel::Volume::Loop?::Wait::@ Obj,WaypointIDorName::
    20.     Event::PlayBGM::Name::Volume::Loop?::
    21.     Event::BGMFadeOut::Frames::
    22.     Event::BGMFadeIn::Frames::
    23.     Event::StopBGM::
    24.  
    25.     [Pictures]
    26.     Event::PictureShow::Name::PicNumber::X::Y::Opacity::
    27.     Event::PitureShowEx::Name::PicNumber::X::Y::FadeIn?::Frames::Opacity::
    28.     Event::PictureMove::PicNumber::X::Y::Opacity::
    29.     Event::PictureErase::PicNumber::
    30.     Event::PitureFadeIn::PicNumber::Frames::
    31.     Event::PictureFadeOut::PicNumber::Frames::
    32.  
    33.     [Screen FX]
    34.     Event::ShakeScreen::Intensity::Frames::
    35.     Event::FlashScreen::Intensity::Frames::
    36.     Event::Weather::Type::Intensity::
    37.     Event::TintScreen::Type::Intensity::
    38.  
    39.     [Objects]
    40.     Event::CreateObj::ObjName::        ***** "Player" cannot be created, and has to throw back an error 'Player cannot be created as a new object. Player is the primary game object already!'
    41.     Event::DeleteObj::ObjName::        ***** "Player" cannot be deleted, and has to throw back an error 'Player object cannot be deleted! It must remain invincible!'
    42.     Event::DeleteThisObj::
    43.  
    44.     [Object Events]
    45.         [Movement Events]
    46.         Obj::ObjName::Wait::Frames::
    47.         Obj::ObjName::WalkRunSpeed::Speed::
    48.         Obj::ObjName::MoveTo::Obj,WaypointIDorName::Wait?::
    49.         Obj::ObjName::RunAwayFrom::Obj,WaypointIDorName::Wait?::FramesIfWait::
    50.         Obj::ObjName::TurnToward::Obj,WaypointIDorName::Wait?::
    51.         Obj::ObjName::TurnAwayFrom::Obj,WaypointIDorName::Wait?::
    52.         Obj::ObjName::Turn90r::Wait?::
    53.         Obj::ObjName::Turn90l::Wait?::
    54.         Obj::ObjName::SteppingAniToggle::OnOrOff::
    55.         Obj::ObjName::DirectionFixToggle::OnOrOff::
    56.         Obj::ObjName::InstantMoveXYZ::X::Y::Z::
    57.         Obj::ObjName::InstantMoveTo::Obj,WaypointIDorName::
    58.         [Acrobatics]
    59.         Obj::ObjName::Jump::Speed::Wait?::
    60.         [Graphic Events]
    61.         Obj::ObjName::SetOpacity::OpacityNumber::Frames::
    62.         Obj::ObjName::ChangeGraphic::CharSetName::IndexNum::
    63.  
    64.     [Camera]
    65.     Event::CameraMove::X::Y::Z::Frames::
    66.     Event::CameraMoveTo::Xpos::Ypos::Zpos::Frames::
    67.     Event::CameraFollow::Obj,WaypointIDorName::FollowDelay::
    68.     Event::CameraRotate::Xr::Yr::Zr::Frames::
    69.     Event::CameraMoveRe::Obj,WaypointIDorName::
    70.     Event::CameraFocusToward::Obj,WaypointIDorName::
    71.     Event::CameraFaceShot::Obj,WaypointIDorName::Frames::
    72.     Event::CameraCloseUpFaceShot::Obj,WaypointIDorName::Frames::
    73.     Event::CameraZoom::Distance::Wait::
    74.  
    75.     [Game]
    76.     Event::HUDshow::
    77.     Event::HUDhide::
    78.     Event::GameOver::
    79.     Event::Title::
    80.     Event::TimerSet::Seconds::
    81.     Event::TimerShow::
    82.     Event::TimerHide::
    83.  
    84.     [Animations]
    85.     Event::ShowAnimation::AniSetName::
    86.     Event::HideAnimation::AniSetName::
    87.  
    88.     [Text]
    89.         [Standard Top, Mid, Bot Text Box]
    90.         Tp::position::
    91.         T::Text Goes Here::
    92.  
    93.         [Standard Choice Box]
    94.         Tcb::Ch1::Ch2::Ch3::Ch4::Ch5::Cancellable?
    95.  
    96.         [Text Box]
    97.         TBp::X::Y::
    98.         TBT::Text Goes Here::
    99.  
    100.         [Choice Box]
    101.         TBcb::X::Y::Ch1::Ch2::Ch3::Ch4::Ch5::Cancellable?
    102.  
    103.         Tfont::Font Name::
    104.         Tsize::Text Size::
    105.  
    106.         VA::SFXFileName::
    107.  
    108.         [Text Codes]
    109.         \| - Wait 1 second
    110.         \. - Wait 1/4 seconds
    111.         \{ - text size up one
    112.         \} - text size down one
    113.         \^ - don't wait for input after displaying text
    114.        \> - display remaining text on line all at once
    115.        $variable - display a variable with name of 'variable'. Requires space after it.
    116.  
    117.    [Movies]
    118.    Event::MoviePlay::Name::Skippable?::
    So the idea is that these events trigger functions from the api like this:

    Code (csharp):
    1.  
    2.     Check, If: Wait::Frames::
    3.         I think it'd be like: WaitForSecondsRealtime(MathForFrames(InsteadOfSeconds));
    4.  
     
    Last edited: Oct 9, 2016
  2. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,539
    I've never used RPG maker, but I'll proceed with what you've explained, and how you've explained.

    It appears to me that it is called an interpreter because it's following the 'interpreter pattern':
    https://en.wikipedia.org/wiki/Interpreter_pattern

    The interpreter pattern is used for parsing commands from text... it interprets text commands.

    You don't necessarily need 2 interpreters either... you can have just one interpreter that has a play head (the index) that can be moved forward or backward (facilitating an 'undo').

    Other than that, yeah, this is a fairly simple and common design. The devil is in the details of actually implementing your interpreter... it's not exactly the most simple. Especially since working with strings can generate loads of garbage strings (garbage objects can hurt performance as it causes the garbage collector to be called frequently).

    I actually ran into this writing a simple arithmetic interpreter some time back... I shrunk the thing down in a functional interpreter (rather than the classic object-oriented interpreter), and needed to figure out a way to do everything in as few string objects as possible... you can see it here:
    https://github.com/lordofduct/space...ob/master/SpacepuppyBase/Dynamic/Evaluator.cs

    The ReusableStringReader that cuts down on the string generation:
    https://github.com/lordofduct/space...puppyBase/Collections/ReusableStringReader.cs

    Of course yours would be getting far more complex as it's more than an arithmetic evaluator like this thing is.
     
    astracat111 likes this.
  3. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    So I get the point of the system, even if I don't understand all the details.

    One question: How big a game are you making that doing this makes sense, as opposed to straight out scripting, or using the animation system? I'm just not sure that building this is going to be simpler then the alternatives.
     
  4. Kalladystine

    Kalladystine

    Joined:
    Jan 12, 2015
    Posts:
    227
    I'm going to bite from a different direction then LordOfDuct.

    Why do you want to create the Interpreter?
    This is the essential question that you need to have a definite, and non-trivial answer.

    Did you know there are cinema directors in the asset store that were built solely for the purpose of making cutscenes in Unity? Teams of people worked on them, polishing and updating, taking feedback and modifying. Do you think you can do better? Because if yes, but realistically yes - be honest with yourself, - you're sitting on a pile of money. Literally.


    There are many drawbacks to the approach you're taking:
    • It's extremely easy to mess up the implementation
    • GC will hate you (probably)
    • You will be writing in plain text (as far as I understand your instructions-file will not be compiled) - this means no IntelliSense, no type-safety, no debugger support, no compiler optimizations (
    • You're adding another layer of abstraction to your code - this is always costly in processing, but also while actually designing
    • If something doesn't work as you want in the actual animation, debugging it will be a major PITA
    • If you'll need help from others, it might be hard to find, as the system will be extremely complex and noone will know it's internals
    • ...
    But there are advantages:
    • You will be able to "script" your cutscenes in a way you are familiar with
    • It enables modding very easily
    There are, IMHO of course, some questions that you should be positive you can answer "Yes!" before you start (or you are convinced you can learn it during your journey):
    • Do you know enough about C#/.Net memory handling and optimizations to not burn the machine this will run on?
    • Are you comfortable with thinking in pointers?
      • Sidenote: you don't need pointers in C#, but a interpreter like this could use similar concepts as memory management.
    • Do you know exactly how strings are handled? Encodings, string builders, mutations (btw - it's possible to make a mutable string in C# using pointers in unsafe context and operating on byte arrays), streams?
    • Can you design with a couple abstraction levels in mind? (it seems you are on a good track, since you're not jumping straight to code)
    • Do you know Unity API's good enough to make interfacing your script commands to needed instructions possible, robust and performant?
    • Do you have the willpower to endure months of developing without seeing results? It might take A LOT of time before you'll be able to run your first cutscene properly.

    Assuming you are confident you actually want to make it, I think you shouldn't start with it (as strange as it sounds).

    You will need Unity side components that will relay your instructions to the actual objects: moving needs pathfinding. LookAt needs to be able to find other objects. Dialogs need UI components.
    Make those. If you don't know how to, having an Interpreter for your commands won't change anything, because you can't translate them to the Unity API.
    Make a bunch of objects and hardcode their behaviours via scripting and existing Unity animation components. Make them do exactly what you want, because they will need to do it anyway - just the command will be called externally (from the Interpreter).

    I have a back-of-the-head hunch, that in the process of making the components needed for Unity to act on your objects, you will find that you don't need the Interpreter. You can use the Mecanim and animator components to control behaviours. You can use sequential invoker patterns with delegates to queue your actions. You can use "god classes", which is normally an anti-pattern, to control your cutscenes (btw - the Interpreter might end as a kind-of god class anyway I think).


    To give you a completely honest opinion, and I really hope you won't be offended by this, I think you want to create the Interpreter to stay in your comfort zone - you know RPG Maker way of doing cutscenes and you want to stick with it. Essentially - I think you're trying to fit Unity into a RPG Maker-sized box instead of working with Unity the way it's supposed to be used, which you are unfamiliar with (and that's normal - you just switched to it).

    Don't fall into the Not Invented Here pit. I've been there myself (but got pulled out of it by someone - and I'll be greatfull for that for the rest of my life) and I've seen people make completely unmanagable monstrosities, because they refused to go out of their comfort zone and learn the frameworks they now needed to work with, reinventing the wheel for everything. It was honestly one of the saddest things I've seen in my career to look at very talented developers, who were wasting hundreds of hours on essentially useless code.

    Know your tools of choice. Embrace them. Extend them. Don't fight them.

    If you find Unity-way, once you really understand it, is not working for you, switch to what works for you. There's no obligation to not switch, admitting that you don't like Unity-way is not a shame, it's the right thing to do in that situation.


    As a closing sentence - I hope you didn't find anything in my post offensive. If you did - please accept my apologies, that was not the intention.

    Happy coding and good luck on whichever route you take!

    PS. Needed to reload the page before posting and now I see BoredMormon asked essentially the same, but with two paragraphs... heh.. well... ninja'd :)
     
  5. recursive

    recursive

    Joined:
    Jul 12, 2012
    Posts:
    669
    I'll second what the other posters above me said but add some contributions you may find interesting.

    ink is a scripted story interpreter that may suit your needs, is extremely flexible, and can be modified to work with any type of game. Our creative director, who cannot program, has been playing around and likes using it. We actually found a bug in it and they patched it promptly.

    Another option you may interested in is fungus. It is mostly geared towards visual novels, but can be adapted to other genres as well.

    Both are free and I believe both are open source, so you can dissect and modify to your hearts content.
     
    TonyLi likes this.
  6. astracat111

    astracat111

    Joined:
    Sep 21, 2016
    Posts:
    725
    Wow, thanks guys for all of the detailed answers. : )

    @lordofduct

    Thanks for the links, I'll check them out now. Thinking about it now, I see your point. There's really no need to 'throw away' what's in the database/array at all...I don't know why I didn't see that...I guess in this way I could have just one database and a position that the game is on in the timeline.

    @BoredMormon

    Well the idea is that I would make this, and then make multiple games into the next 3-5 years using this system. I'm looking for something that can put my scripts into game.

    @Kalladystine

    Hey, thanks Kallady, this is exactly what I would be looking for. You're mentioning optimization, and since I'm a novice-intermediate programmer at best compared to some of the people here and that work professionally I have a lot to learn and am willing to take the time right now to learn it.

    I've worked with C++ so I've worked with pointers before, but I haven't worked in a practical way with many of the more complex tips you gave (how strings are handled, and memory handling mainly). From what I understand the drawbacks of programming my system would be that it's not optimized for speed, and it would be difficult to debug, both of which I would probably need a good amount of study and experimentation to wrap my head around and change.

    The advantages, though, are what I'm after for sure. Being able to completely understand and control every aspect of my game system so that I can move into the future with a solid foundation for creating cinematics.

    No way I've found anything offensive, I'm absorbing all of the pointers. I'm going to mostly be using what Unity has to offer already, I had just assumed that I would be using Methods to trigger Unity API functions already, so most of the work will already be done. I'm not going to build my own pathfinding system or collission system or movement system if I can help it, I'll just use what Unity already has there.

    I'm okay with working for about say 3-6 months on this non-stop 4 hours a day. I'm very determined to do it, but I know I will have to save as much time as possible, and so much is already done.

    @recursive

    Thanks for the suggestions I'm gonna check them out now.
     
  7. Kalladystine

    Kalladystine

    Joined:
    Jan 12, 2015
    Posts:
    227
    Glad you found it helpful :)

    combined with
    sadly will be very difficult. If you want total control, you need total control. You can have "enough" control using built-in functions, but it will never be complete - that's not a showstopper, but keep that in mind.

    Especially since:
    The biggest drawback I would say is not even speed (although it is important) - it's controlling time, and I don't know how that can be handled actually.

    I'll take a sample scenario to try to illustrate what I mean:

    Player (object) is at position (0,0,0). He should MoveTo (command) the FrontOfTheAltar (Waypoint Name, for simplicity let's assume it's directly ahead). When he reaches his destination start ShakeScreen (command) for 5 seconds. During that time FlashScreen (command) on 1st second and 2nd second. From the last flash Player should MoveTo (command) ALittleBitAwayFromAltar (Waypoint Name; essentially he got scared and moved away).

    So a very basic "something is going to happen" scenario, that should be easily written with your command-interpreter pattern.
    This is where one of the things you wrote in the beginning stops being true -
    Or to be more precise - this was never true, unless you literally want only one action being performed at once (which I don't think you do) - a character can walk OR turn OR talk OR... And only one character can perform an action at any given moment. And all of this needs to be synchronized frame-perfect with camera effects and movement, SFX, sound and UI (and also your QTE's later on).

    This is obviously not what you are aiming for if you want good cinematics, meaning you'll probably need a nested system - a timeline of timelines, if I can say it like this, which will need to use some sort of callback mechanism for starting next actions. But commands also need to be callback controlled, unless you want to calculate everything - how long the character will walk to the altar, what speed should he back away from it and how long will it take (and it's probably not constant speed, more like a step at a time, since he's scared) and those actions cannot block the interpreter, to enable multiple characters to f.e. walk at the same time.
    And even if you're able to calculate everything, it means that the scenes cannot change by any means at all, or the calculations will need to be redone.

    There's also one more hurdle to overcome that's related to current design - frames are not constant in length. Even setting target fps is not guaranteed, because Unity (or any other engine I'm aware of) will not "shorten" the frame, it can only make it wait if it completed too fast. You may have GC run a collection, some function (like pathfinding) will take longer than usually, a process completely not related to your game running on your players machine will hog CPU (antivir, windows update) etc. The only thing you can be certain of, is that a sequence of events on a single timeline will be executed in order.

    Ok, to summarize what we have so far (and please note this is just my take on it and I tend to overanalyse):
    - each individual animatable object (or meta-object like audio, sfx) needs to have its own controller with timelines - camera, sound, sfx, player, each npc etc.
    - timelines need a way to synchronize with each other
    - commands need to have meta-attributes - at least blocking/non-blocking (default being blocking), to enable more complex actions made of a couple of commands
    - commands need to have callbacks to their controller to signal that they finished and that timeline can proceed or they need to have a strict, predefined length
    - there needs to be a command like "wait for callback from" to mark breakpoints in controller execution

    There are more things, but I'll stop here.

    And now remember - the cutscene will be written in a 1-command=1-line text file without visualization! If that's really how RPG Maker cutscenes works, and you can achieve a lengthy cutscene that doesn't bore your players to death.. that's truly impressive.

    I don't normally advertise non-free assets, but this is one of the moments I think it's warrantied - check this one. I'm linking it solely because you are going to design and build a tool that a) already has existing equivalents, b) is not your goal as I understand it (you want to create a cinematic game experience, not a animation director engine).
    If you don't have disposable income to throw at the asset (and that's assuming you'll find it will fill your needs), but are willing to spend 3-6 months, 4h/day, change your priorities - spend a couple % of the time it would take you to build it and work an extra shift/take a side gig for some time (depending on where you live the subjective cost will vary) and just save up for it.

    If you still want to build your own, I'll reiterate and say start with what you need to build on the Unity side for the timeline to execute as you want it and get to know the systems you will want to use (NavMesh pathfinding, collisions, instantiating objects, working with the camera, sound, UI, animations etc.).

    Reading the commands from text is actually the easier part of what you'll need.
     
  8. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,338
    So Unity's making a "Director Sequencing Tool". It's not got a release date, but if you look at the Blacksmith demo and the Adam demo, I believe it was used to make both.

    You could wait for that, though it'll be a bit of a ways off.


    We have made a similar system for our current game, so if you're interested, I can give some pointers for how to create/not to create the implementation.

    The thing I can say that doesn't already seem to have been said is: you should probably not implement this stuff through writing text and then parsing it. What you're proposing is essentially a DSL, and those are a bit of a pain. In particular, you'll be spending a lot of time fixing bugs in both the interpreter and the specs of the language.

    I would create the cutscenes as C# classes, and store them directly to asset files. Then you'd write a custom editor to edit the cutscenes. This would have a bunch of advantages:
    - you don't have to define a custom language with it's own syntax and semantics
    - you don't have to handle spelling mistakes in said language
    - you don't have to write a parser
    - the objects you're storing is just the events in the cutscene system. So to play the cutscene, you just iterate the events in order and ask them to play themselves. Unity's ScriptableObject interface is the way to go here.
    - You can create a custom editor window to author the cutscene. This can allow for ie. reordering events by dragging them in a timeline, and visualising the order of things directly.

    It'll take a bit more time to set up, but with proper handling, iterating should be a lot faster, and you have to handle a lot less things.
     
    astracat111 and Kiwasi like this.
  9. astracat111

    astracat111

    Joined:
    Sep 21, 2016
    Posts:
    725
    @Kalladystine and @Baste

    Thanks for the replies guys! I wish I had more time to write as much as you did back to you at the moment. I'm absorbing pretty much everything your saying, and it's having me rethink a lot of things. I've looked through some guides on garbage collection and see that you can't just parse the text. It's simple in theory but much less so in execution it seems.
     
  10. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,338
    If you go down the route of parsing the text and making a dll anyways, you'll want to look into how to do that properly. That means looking up how to do that - the wikipedia page on Lexical analysis is actually a good starting point.

    I have written (very small, for Uni) languages both from scratch, and by using libraries designed for defining grammars and such, and the latter was much more manageable.