You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#文本冒险程序:如何实现输入menu随时唤起主菜单?

Solution for Global "menu" Input Listening in C# Text Adventure

Great question! Having to manually call Menu() after every line of your adventure is definitely clunky. Let's look at two solid approaches to add global "menu" listening without repeating code.

Approach 1: Encapsulate Input Handling (Simplest & Most Reliable)

The easiest way is to wrap all user input in a single function that automatically checks for "menu" before returning the input. This way, every time you need user input, you use this function instead of Console.ReadLine(), and it handles menu checks behind the scenes.

Here's the implementation:

class Program
{
    static void Main(string[] args)
    {
        StartAdventure();
    }

    // Reusable menu display function
    static void ShowMainMenu()
    {
        Console.WriteLine("\n=== Main Menu ===");
        Console.WriteLine("1. Entry 1");
        Console.WriteLine("2. Entry 2");
        Console.WriteLine("Any other key to exit menu");
        Console.Write("Select an option: ");
        
        if (int.TryParse(Console.ReadLine(), out int menuChoice))
        {
            switch (menuChoice)
            {
                case 1:
                    Console.WriteLine("You picked Entry 1!");
                    break;
                case 2:
                    Console.WriteLine("You picked Entry 2!");
                    break;
                default:
                    Console.WriteLine("Exiting menu...");
                    break;
            }
        }
        else
        {
            Console.WriteLine("Invalid input—exiting menu.");
        }
    }

    // Global input handler that checks for "menu" automatically
    static string GetAdventureInput(string prompt = "")
    {
        while (true)
        {
            if (!string.IsNullOrEmpty(prompt))
            {
                Console.Write(prompt);
            }
            
            string userInput = Console.ReadLine().Trim().ToLower();
            
            // Check if user wants the menu
            if (userInput == "menu")
            {
                ShowMainMenu();
                Console.WriteLine("\nBack to your adventure—type 'menu' anytime to return to the menu!");
                continue; // Loop back to wait for normal input
            }
            
            // Return valid non-menu input
            return userInput;
        }
    }

    static void StartAdventure()
    {
        Console.WriteLine("Welcome to the Text Adventure!");
        GetAdventureInput("Press enter to begin... ");
        
        Console.WriteLine("\nType 'menu' at any time to open the main menu!");
        
        Console.WriteLine("\nYou enter a dark cave.");
        GetAdventureInput("What do you do? ");
        
        Console.WriteLine("\nYou find a glowing crystal.");
        GetAdventureInput("Continue exploring? ");
        
        Console.WriteLine("\nYou emerge from the cave into sunlight.");
        GetAdventureInput("Finish your adventure? ");
        
        Console.WriteLine("\nAdventure complete! Thanks for playing!");
    }
}

How this works:

  • Every time you need user input (like to advance the story), call GetAdventureInput() instead of Console.ReadLine().
  • The function checks if the input is "menu"—if yes, it opens the menu, then loops back to wait for the user to enter normal adventure input.
  • No more manual Menu() calls—all menu logic is centralized in one place.

Approach 2: Background Thread Listening (For Non-Input-Heavy Adventures)

If your adventure has long stretches where the program is outputting content without waiting for input, you can use a background thread to listen for "menu" input in the background. Note that this requires thread synchronization to avoid conflicts with the main thread's input calls.

class Program
{
    private static bool _isMenuActive = false;
    private static readonly object _consoleLock = new object(); // Prevents input conflicts

    static void Main(string[] args)
    {
        // Start background menu listener
        Thread menuListener = new Thread(ListenForMenu);
        menuListener.IsBackground = true; // Ends when main thread ends
        menuListener.Start();

        StartAdventure();
    }

    static void ListenForMenu()
    {
        while (true)
        {
            lock (_consoleLock)
            {
                // Check if there's input available to avoid blocking the thread
                if (Console.KeyAvailable)
                {
                    string input = Console.ReadLine().Trim().ToLower();
                    if (input == "menu" && !_isMenuActive)
                    {
                        _isMenuActive = true;
                        ShowMainMenu();
                        _isMenuActive = false;
                    }
                }
            }
            Thread.Sleep(100); // Reduce CPU usage
        }
    }

    static void ShowMainMenu()
    {
        Console.WriteLine("\n=== Main Menu ===");
        Console.WriteLine("1. Entry 1");
        Console.WriteLine("2. Entry 2");
        Console.WriteLine("Any other key to exit menu");
        Console.Write("Select an option: ");
        
        lock (_consoleLock)
        {
            if (int.TryParse(Console.ReadLine(), out int menuChoice))
            {
                switch (menuChoice)
                {
                    case 1:
                        Console.WriteLine("You picked Entry 1!");
                        break;
                    case 2:
                        Console.WriteLine("You picked Entry 2!");
                        break;
                    default:
                        Console.WriteLine("Exiting menu...");
                        break;
                }
            }
            else
            {
                Console.WriteLine("Invalid input—exiting menu.");
            }
        }
    }

    static void StartAdventure()
    {
        Console.WriteLine("Welcome to the Text Adventure!");
        Console.WriteLine("Type 'menu' at any time to open the main menu!");
        
        Console.WriteLine("\nYou walk through a forest...");
        Thread.Sleep(2000); // Simulate story progression without input
        
        Console.WriteLine("\nYou hear a rustle in the bushes.");
        lock (_consoleLock)
        {
            Console.Write("Investigate? ");
            Console.ReadLine();
        }
        
        Console.WriteLine("\nYou find a hidden path.");
        Thread.Sleep(2000);
        
        Console.WriteLine("\nYou reach a castle gate.");
        lock (_consoleLock)
        {
            Console.Write("Knock on the gate? ");
            Console.ReadLine();
        }
        
        Console.WriteLine("\nAdventure complete!");
    }
}

How this works:

  • A background thread runs continuously, checking for available input every 100ms.
  • If it detects "menu" and the menu isn't already open, it triggers the menu.
  • The _consoleLock ensures the main thread and background thread don't try to read input at the same time, which would cause unexpected behavior.

Recommendation

Stick with Approach 1 for most text adventures—it's simpler, easier to maintain, and avoids the complexity of thread synchronization. The encapsulated input function keeps all menu logic in one place and makes your adventure code clean and readable.

内容的提问来源于stack exchange,提问作者C0deN00b

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.09 17:52:41