功能反应编程 - 删除非功能部件

时间:2015-02-23 18:07:47

标签: c# functional-programming frp

我有一个功能反应性的“roguelike”,只要它在屏幕上移动一个“@”。但是,它仍然依赖于其他编程范例。 (它也可能是可怕的代码,但这与问题无关)

我的问题是,如何将播放器的数据源从带有组件的GameObject更改为适合FRP的内容?为了估计我的知识水平,我昨天逐渐了解了FRP。

    public struct KeyInfoEventArg
    {
        public ConsoleKeyInfo keyInfo;
    }


    public static event EventHandler<KeyInfoEventArg> keyPressEvent;

    public static event EventHandler<bool> redisplayEvent;


    private static Dictionary<ConsoleKeyInfo, Directions> consoleKeyInfoToDirections =
        new Dictionary<ConsoleKeyInfo, Directions>();
    private static HashSet<ConsoleKey> emergencyExitKeys =
        new HashSet<ConsoleKey>();


    static void Main(string[] args)
    {
        Console.SetBufferSize(80, 41);
        Console.SetWindowSize(80, 40);

        // Code to fill in consoleKeyDirections not relevant.
        // Code to fill in emergencyExitKeys not relevant.

        var keyInfo =
            Observable.FromEventPattern<KeyInfoEventArg>(
                ev => keyPressEvent += ev,
                ev => keyPressEvent -= ev)
                .Select(a => a.EventArgs.keyInfo);

        // Used to display the screen at the start.
        // Also, the player could be returning from another screen,
        // and need to display the map again.
        var redisplay =
            Observable.FromEventPattern<bool>(
                ev => redisplayEvent += ev,
                ev => redisplayEvent -= ev)
                .Where(a => a.EventArgs)
                .Select(a => a.EventArgs);

        GameObject player = new GameObject();

        // CharSymbol defaults to '@'.
        player.AddComponent<CharSymbol>();
        // DisplacedImage is an IPosition; I poll the IPosition
        // components to know where to draw the CharSymbol.
        player.AddComponent<DisplacedImage>()
            .Displacement = new Vector2I(5, 2);

        var playerDir =
            keyInfo
                .Where(
                    a =>
                        consoleKeyInfoToDirections.ContainsKey(a))
                .Select(
                    a =>
                        consoleKeyInfoToDirections[a]);

        // If a valid key command, or a redisplay command,
        // then playerDisplayUpdate.
        var playerDisplayUpdate =
            keyInfo
                .Select(a => !emergencyExitKeys.Contains(a.Key))
                .Merge(redisplay);

        var playerMove =
            playerDir.Select(a => a.ToVector2I());
        var prospectivePlayerPosition =
            playerMove
                .Select(
                    a =>
                        player
                            .GetComponents<Transform>()
                            .Select(b => b.Position + a)
                ).Merge();
        var newPlayerPositions = prospectivePlayerPosition.Where(
            a =>
                a.X >= 0 && a.X < Console.WindowWidth &&
                a.Y >= 0 && a.Y < Console.WindowHeight);

        newPlayerPositions.Subscribe(
            a =>
                player.GetComponent<Transform>().Position = a);

        playerDisplayUpdate.Subscribe(a => Console.Clear());

        var displayMe = playerDisplayUpdate.Select(a => player);
        // Remember DispacedImage? It's an IPosition;
        // there's also a transform IPosition.
        // Now the theoretical monster in the theoretical full game
        // has two possible targets to worry about.
        var displayData = displayMe.Select(
            a =>
            {
                return
                    player
                        .GetComponents(typeof(IPosition))
                        .Select(b => (IPosition)b)
                        .Select(
                            b =>
                                new ConsoleRenderInfo(
                                    b.Position,
                                    a.GetComponent<CharSymbol>().Symbol)
                            );
            }).Merge();
        displayData = displayData.Where(
            a =>
                a.Position.X >= 0 && a.Position.X < Console.WindowWidth &&
                a.Position.Y >= 0 && a.Position.Y < Console.WindowHeight);
        displayData
            .Subscribe(
                a =>
                {
                    Console.SetCursorPosition(a.Position.X, a.Position.Y);
                    Console.Write(a.Symbol);
                });

        // Remember when we were getting valid key values for this?
        // Yep, if it's an emergency exit key, 'a' is false, and we exit.
        playerDisplayUpdate
            .Subscribe(
                a =>
                {
                    Console.SetCursorPosition(0, 0);
                    if (a)
                        keyPressEvent(
                            null,
                            new KeyInfoEventArg()
                            {
                                keyInfo = Console.ReadKey(true)
                            });
                });

        // So the player doesn't have to move to see themselves.
        redisplayEvent(null, true);
    }

据我所知,使用FRP,我应该可以用一个或多个IObservables替换GameObject和Components吗?那是什么东西,我该怎么做?

0 个答案:

没有答案