From Zero to Game Dev, Part 2: Choosing a Visual Novel Engine - Utage vs Naninovel

From Zero to Game Dev, Part 2: Choosing a Visual Novel Engine - Utage vs Naninovel

Author : HAYAO(PG) HAYAO(PG)

 Hello! I’m HAYAO, a programmer at HazeDenki, Inc.


 As mentioned in the previous post, the planned series looks like this:

  1. Building the foundation of a visual novel: where to start
  2. Selecting a visual novel engine: features of Utage and Naninovel
  3. Setting up the development environment
  4. Customizing the engine source with AI
  5. A few deep-dives into Naninovel features


 This time, I’ll share the findings from my engine evaluation in #2.

Choosing an Engine for Visual Novels


 I’d already committed to Unity. Among Unity VN engines with reputations for extensibility, I compared Utage and Naninovel.


 Conclusion: we chose Naninovel.


 Below are my notes on each engine. If you think I’ve missed something—please leave a comment!


宴 -utage-



Features

 Utage is designed around Unity Inspector + spreadsheets as the main workflow.


 The Japanese documentation and support are excellent—a big advantage for developers reading Japanese.

 For English-speaking teams, that can be a hurdle. (By contrast, Naninovel’s docs are all in English, and the previous JP manual translation has been discontinued.)


↓Official manual


↓Forum

↓Official Discord


Dev experience

 You typically control text display, choices, function calls, and saved parameters via spreadsheet commands.

 Even when you extend Utage with your own features, you’ll generally trigger them from the scenario spreadsheet. Most engine-specific setup lives in the Inspector; if you read the manual attentively, you can get productive fairly quickly.


Extensibility

 Regarding the extensibility for implementing features not in the presets—which I think everyone cares about—you’ll mostly use SendMessage functions.

 You define messages (Commands) in the spreadsheet and send them during the scenario. On the engine’s receiving side, you branch based on the value of Arg1 and execute the desired process. That’s the basic flow.

※Excerpt from the manual

        // When the SendMessage command is executed
        void OnDoCommand(AdvCommandSendMessage command)
        {
            switch (command.Name)
            {
                case "DebugLog":
                    DebugLog(command);
                    break;
                case "InputField":
                    InputField(command);
                    break;
                case "AutoLoad":
                    AutoLoad(command);
                    break;
                default:
                    Debug.LogError("Unknown Message:" + command.Name);
                    break;
            }
        }


 As described in the manual, you sort messages with a switch statement like the above.

 When the selected row in the sample image I posted is executed, the case "DebugLog" branch would run.

 There may be differing opinions here, but concentrating operations in the spreadsheet is a merit in terms of management and lowering barriers.

 On the other hand, I can already see a future where debugging is painful…

 Calling a separate scene for a small minigame is sufficiently covered by SendMessage, so if you can get by with the preset effects, Utage is very much worth adopting.


Dissatisfaction

 If you’re not comfortable with spreadsheets, this will obviously be tough. Copying the script and shaping it into a game format is hard work. If your scenario writers use spreadsheets routinely, the barrier will be lower.


 Also, compared to NaniNovel, fine-grained customization is less nimble. It’s easy to call features you created from scratch, but when you want to replace a part of the core functionality, it’s hard.


 For example, suppose in one scene you want to display text falling from the top with per-character fade-in.

 When I looked into how the text is rendered in the text window and where I’d need to change things to alter the display animation, I found this kind of fine change wasn’t envisioned, and the structure would impact the whole system.


 It leaves the impression that fine details are just out of reach.

  

NaniNovel


$165! (pricey)


Features

 You specify visual-novel controls such as text display and CG display by editing a NaniNovel Script file.

 There’s a VS Code plugin for the IDE with input completion, which is extremely convenient.

You call Naninovel-specific features with @ commands.
The plugin suggests candidates—an amazing feature.

 As for the official manual, the sections are comprehensive and all in English. After purchase there’s also a period of dedicated Discord support. You won’t be lacking ways to find out what Naninovel can do by default.


↓Official manual


Strengths

 The best thing about NaniNovel is that you can integrate what vanilla Unity can do with almost no special steps.


 For example, backgrounds, portraits, the text window, and the camera can all be configured via Prefab references in Unity’s UI. This makes it intuitive to display complex layered characters, dynamic 3D backgrounds, special shaders, lights, and post effects.

 Since there are fewer constraints, there’s very little engine-specific UI; you need to be familiar with how to build games in vanilla Unity. It’s also more expensive than Utage, and if you can’t code in C#, you probably won’t get value equal to the price.


 When it comes to extending functionality, you’ll need to read existing code, and you’ll find very few unofficial development blogs about NaniNovel. Naturally, even on the Discord forum, answers on extensions often stay concise.

 


Extensibility

 As mentioned above, extensibility is intuitive and high.

 We’re using Naninovel for our visual novel, and to date its constraints haven’t stopped us from doing anything we could do in vanilla Unity. Gamepad support and blinking portrait animations, for example, were straightforward


Extension example

 I can’t introduce every extended feature, so here’s one especially fundamental to visual novels: customizing the text window.


 NaniNovel provides five text window types (printers) by default:

Dialog

Wide

FullScreen

Bubble

Chat

 

 You can show/hide these on screen within the scenario written in NaniScript.

@printer FullScreen   // The specified text window is displayed
Heroine: Hello.         // The text appears in the invoked window


 As you can see in the videos already published, our VN required at least two kinds of text windows:

 1.Full-screen with text advancing via vertical scrolling. (See video 0:21–)

 2.When showing a still (CG), wide dialog at the bottom with paged text.

 #2 is achievable with the default “wide” printer, but #1 required modifying the text reveal behavior.



 Each text window is prefabbed, so we investigated the default Full Screen prefab to try modifications; fortunately, a base class for features that run on text reveal was provided.

public abstract class TextRevealEffect : MonoBehaviour
{
    protected virtual RevealableText Text { get; } // Register the display text object.
    protected virtual IRevealInfo Info { get; }    // Interface to get reveal state
}


public interface IRevealInfo
{
    event Action OnChange;            // ★ Fires each time text is revealed
    int LastRevealedCharIndex { get; } // Index of the last revealed character
}


 Given a base class that notifies whenever the specified text object changes, all we had to do was create a script inheriting from it and attach it to the text component inside the prefab.


 The scrolling logic makes the text area scroll when its height overflows the window. Any overflow is masked.


 For reference, here’s a simplified version of the code we use in the actual game.

public class RevealScrollRect : TextRevealEffect
{
    [Tooltip("Viewport RectTransform of the ScrollRect.")]
    public RectTransform viewport;


    public float textSpace = 40f;   // margin so text doesn't sit flush at the bottom
    public float scrollTime = 0.5f;


    private void OnEnable() // runs when the printer is invoked
    {
        Info.OnChange += HandleRevealChanged; // called on every reveal
        Text.ForceMeshUpdate();
    }


    private async void HandleRevealChanged()
    {
        Text.ForceMeshUpdate();
        float addedHeight = OverflowingHeight(); // compute how much to scroll
        if (addedHeight > 0)
            await AnimateScrollY(addedHeight, scrollTime);
    }


    private async UniTask AnimateScrollY(float addedHeight, float duration)
    {
        var pos = Text.rectTransform.anchoredPosition;
        float startY = pos.y;
        float targetY = startY + addedHeight;


        await Text.rectTransform.DOAnchorPosY(targetY, duration)
            .SetEase(Ease.OutCubic)
            .SetLink(gameObject)
            .AsyncWaitForCompletion();
    }


    public float OverflowingHeight()
    {
        if (Text.text == null || viewport == null) return 0;


        float contentHeight = Text.preferredHeight;
        float viewportHeight = viewport.rect.height;
        float scrollOffset = Text.rectTransform.anchoredPosition.y;


        float textHeight = contentHeight - scrollOffset;
        float res = textHeight - viewportHeight - textSpace;


        return res;
    }
}



 When you have a mechanism that notifies on text reveal, it starts to feel like you can do all kinds of text-advance and reveal animations.


 NaniNovel has countless base classes of this granularity, and that’s what makes it so powerful.


Weaknesses

・Price

 It’s expensive at $165. If you’re not going to extend it, to be honest, there’s not that much you can do with NaniNovel that you can’t with Utage.

 Since Naninovel depends more heavily on Unity know-how, when both Utage and Naninovel can satisfy the project’s requirements, Utage will often be the easier engine to develop with.


・The manual lacks detailed design guidance and apt samples.

 For example, the “base class for performing processing on text reveal” I just introduced—the manual doesn’t say, “there’s a base class that fires an action whenever the specified text object changes.”

 What’s written there is hard to grasp. I can’t count how many times I had to debug to figure out what a setting meant.


 Because of this, the hurdle shoots up… or so you’d think, but there’s a modern workaround.


 Have AI read the engine source for you.

 (When I asked on the Naninovel Discord forum, I was told more than once: “Let AI help you.”)


 Using this method, NaniNovel gains limitless potential. I plan to write in a future post how I investigated NaniNovel together with AI.


Summary

 As I wrote at the beginning, we chose NaniNovel. Unless we encounter fatal issues as development proceeds, this won’t change.


 If you once gave up on NaniNovel, try again with AI at your side—your world might change.


 Next time, I’ll share how I set up a coding environment with AI and how I proceed with development.


 Thank you for reading to the end.

 See you next time, and please keep supporting HazeDenki.

 

Back to Articles

Comments (2)

Leave a comment

0/1000
☆ MALK ☆ 1 hours ago
i'm not a fan of ai, but i'm excited for new developments you guys make!
herba 2 hours ago
thank you for writing in comprehensive detail ! (and gosh ! in english too ! thank you ! ; ;) it was an interesting read ! i love how that engine synergizes greatly with your prior programming experience !