我有一个功能反应性的“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吗?那是什么东西,我该怎么做?