本文介绍了F#中的非常简单的RogueLike,使其更“功能化”的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述 我有一些现有的C#代码,用于一个非常非常简单的RogueLike引擎。这是故意幼稚的,因为我试图尽可能简单地做到最低限度的金额。它所做的就是使用箭头键和System.Console在硬编码地图上移动@符号: //定义地图 var map = new List< string> {,,,,#### ###########################,##,####### #,####,#### #######,######, #######,#### #### #######,#=#,#=#, ###############################,, ,,,}; //在地图上设置初始玩家位置 var playerX = 8; var playerY = 6; //清除控制台 Console.Clear(); //将地图的每一行发送到控制台 map.ForEach(Console.WriteLine); //创建一个空的ConsoleKeyInfo来存储按下的最后一个键 var keyInfo = new ConsoleKeyInfo(); //继续处理按键直到玩家想要退出 while(keyInfo.Key!= ConsoleKey.Q){ //存储玩家的当前位置 var oldX = playerX; var oldY = playerY; //改变玩家的位置(如果他们按下了一个箭头键) switch(keyInfo.Key){ case ConsoleKey.UpArrow: playerY--; 休息; 案例ConsoleKey.DownArrow: playerY ++; 休息; 案例ConsoleKey.LeftArrow: playerX--; 休息; 案例ConsoleKey.RightArrow: playerX ++; 休息; } //检查玩家试图移动的方格是否为空 if(map [playerY] [playerX] ==''){ //确定它是空的,在之前清除它们所在的方块Console.SetCursorPosition(oldX,oldY); Console.Write(''); //现在将它们绘制在新的方形 Console.SetCursorPosition(playerX,playerY); Console.Write('@'); } else { //他们不能在那里移动,将他们的位置改回原位置 playerX = oldX; playerY = oldY; } //等待他们按下一个键并将其存储在keyInfo keyInfo = Console.ReadKey(true); } 我在F#中玩弄了一下,最初我正在写它使用的是功能概念,但事实证明我有点过头了,所以我做了一个非常直接的端口 - 它不是一个真正的F#程序(虽然它编译和运行),它是一个编写的过程程序在F#语法中: 打开系统 //定义地图 let map = [; ; ; ; ###############################; ##; ########; ####; #### #######; ######; ######; #### #### #######; #=#; #=#; ###############################; ; ; ; ; ] //在地图上设置初始玩家位置让可变玩家X = 8 让可变玩家Y = 6 //清除控制台 Console.Clear() //将地图的每一行发送到控制台 map |> Seq.iter(printfn%s) //创建一个空的ConsoleKeyInfo来存储按下的最后一个键 let mutable keyInfo = ConsoleKeyInfo() / /继续处理按键,直到玩家想要退出而不是(keyInfo.Key = ConsoleKey.Q)do //存储玩家的当前位置让mutable oldX = playerX let mutable oldY = playerY //改变玩家的位置,如果他们按下箭头键 if keyInfo.Key = ConsoleKey.UpArrow then playerY else if keyInfo.Key = ConsoleKey.DownArrow then playerY< - playerY + 1 else if keyInfo.Key = ConsoleKey.LeftArrow then playerX< - playerX - 1 else if keyInfo.Key = ConsoleKey.RightArrow then playerX< - playerX + 1 //检查玩家试图移动到的方格是否为空如果map.Item(playerY).Chars(playerX)=''则 //确定它是空的,清除他们站在上的方块Console.SetCursorPosition(oldX,oldY) Console.Write('') //现在在新的方块 Console.SetCursorPosition(playerX,playerY) Console.Write('@') else //他们不能在那里移动,将他们的位置改回旧位置 playerX< - oldX playerY< - oldY //等待他们按一个键并将其存储在keyInfo keyInfo< - 控制台中。 ReadKey(true) 所以我的问题是,我需要学习什么才能重写更多在功能上,你能给我一些提示,一个模糊的概述,那种事情。 我更喜欢向正确的方向推一下,而不是只看到一些代码,但如果这是最简单的方法让你向我解释,那么很好,但在这种情况下,请你也解释一下为什么而不是怎么样的问题? 解决方案您管理复杂性的能力。我发现函数式编程鼓励您将问题解决为小块。 您要做的第一件事是将脚本分成所有函数不同的担忧。我知道这听起来很愚蠢,但这样做的行为将使代码更加实用(双关意图)。您的主要关注点将是国家管理。我用一个记录来管理位置状态和一个元组来管理运行状态。随着代码变得更加先进,您需要对象来干净地管理状态。 尝试在游戏中添加更多内容,并随着游戏规模的不断扩大而将功能分开。最终你需要一些对象来管理所有的功能。 在游戏编程说明中,不要将状态改为别的东西,如果测试失败,请将其改回。你想要最小的状态改变。因此,举例来说,下面我计算 newPosition ,然后只有在这个未来的位置通过时才更改 playerPosition 。 打开系统 //为二维和三维位置使用第三方向量类 //或编写自己的练习类型Pos = {x:int; y:int} with static member(+)(a,b)= {x = a.x + b.x; y = ay + by} 让drawBoard map = //清除控制台 Console.Clear() //将地图的每一行发送给控制台 map |> List.iter(printfn%s) 让movePlayer(keyInfo:ConsoleKeyInfo)= 匹配keyInfo.Key和 | ConsoleKey.UpArrow - > {x = 0; y = -1} | ConsoleKey.DownArrow - > {x = 0; y = 1} | ConsoleKey.LeftArrow - > {x = -1; y = 0} | ConsoleKey.RightArrow - > {x = 1; y = 0} | _ - > {x = 0; y = 0} let validPosition(map:string list)position = map.Item(position.y).Chars(position.x)='' //清除广场玩家站在上让clearPlayer position = Console.SetCursorPosition(position.x,position.y) Console.Write('') //绘制方形玩家站在上让drawPlayer position = Console.SetCursorPosition(position.x,position.y) Console.Write('@') let takeTurn map playerPosition = let keyInfo = Console.ReadKey true //检查玩家是否想继续玩 let keepPlaying = keyInfo.Key< > ConsoleKey.Q //从玩家输入获取玩家动作让movement = movePlayer keyInfo //计算玩家新位置让newPosition = playerPosition +移动 / /检查有效移动 let validMove = newPosition |> validPosition map //如果移动有效,则更新绘图 if validMove then clearPlayer playerPosition drawPlayer newPosition //返回状态 if validMove then keepPlaying,newPosition else keepPlaying,playerPosition //主游戏循环让rec gameRun map playerPosition = let keepPlaying,newPosition = playerPosition |> takeTurn map if keepPlaying then gameRun map newPosition //安装游戏 let startGame map playerPosition = drawBoard map drawPlayer playerPosition gameRun map playerPosition //定义地图 let map = [; ; ; ; ###############################; ##; ########; ####; #### #######; ######; ######; #### #### #######; #=#; #=#; ###############################; ; ; ; ; ] //地图上的初始玩家位置 let playerPosition = {x = 8; y = 6} startGame map playerPosition I have some existing C# code for a very, very simple RogueLike engine. It is deliberately naive in that I was trying to do the minimum amount as simply as possible. All it does is move an @ symbol around a hardcoded map using the arrow keys and System.Console://define the mapvar map = new List<string>{ " ", " ", " ", " ", " ############################### ", " # # ", " # ###### # ", " # # # # ", " #### #### # # # ", " # # # # # # ", " # # # # # # ", " #### #### ###### # ", " # = # ", " # = # ", " ############################### ", " ", " ", " ", " ", " "};//set initial player position on the mapvar playerX = 8;var playerY = 6;//clear the consoleConsole.Clear();//send each row of the map to the Consolemap.ForEach( Console.WriteLine );//create an empty ConsoleKeyInfo for storing the last key pressedvar keyInfo = new ConsoleKeyInfo( );//keep processing key presses until the player wants to quitwhile ( keyInfo.Key != ConsoleKey.Q ) { //store the player's current location var oldX = playerX; var oldY = playerY; //change the player's location if they pressed an arrow key switch ( keyInfo.Key ) { case ConsoleKey.UpArrow: playerY--; break; case ConsoleKey.DownArrow: playerY++; break; case ConsoleKey.LeftArrow: playerX--; break; case ConsoleKey.RightArrow: playerX++; break; } //check if the square that the player is trying to move to is empty if( map[ playerY ][ playerX ] == ' ' ) { //ok it was empty, clear the square they were standing on before Console.SetCursorPosition( oldX, oldY ); Console.Write( ' ' ); //now draw them at the new square Console.SetCursorPosition( playerX, playerY ); Console.Write( '@' ); } else { //they can't move there, change their location back to the old location playerX = oldX; playerY = oldY; } //wait for them to press a key and store it in keyInfo keyInfo = Console.ReadKey( true );}I was playing around with doing it in F#, initially I was trying to write it using functional concepts, but turned out I was a bit over my head, so I did pretty much a straight port - it's not really an F# program (though it compiles and runs) it's a procedural program written in F# syntax:open System//define the maplet map = [ " "; " "; " "; " "; " ############################### "; " # # "; " # ###### # "; " # # # # "; " #### #### # # # "; " # # # # # # "; " # # # # # # "; " #### #### ###### # "; " # = # "; " # = # "; " ############################### "; " "; " "; " "; " "; " " ]//set initial player position on the maplet mutable playerX = 8let mutable playerY = 6//clear the consoleConsole.Clear()//send each row of the map to the Consolemap |> Seq.iter (printfn "%s")//create an empty ConsoleKeyInfo for storing the last key pressedlet mutable keyInfo = ConsoleKeyInfo()//keep processing key presses until the player wants to quitwhile not ( keyInfo.Key = ConsoleKey.Q ) do //store the player's current location let mutable oldX = playerX let mutable oldY = playerY //change the player's location if they pressed an arrow key if keyInfo.Key = ConsoleKey.UpArrow then playerY <- playerY - 1 else if keyInfo.Key = ConsoleKey.DownArrow then playerY <- playerY + 1 else if keyInfo.Key = ConsoleKey.LeftArrow then playerX <- playerX - 1 else if keyInfo.Key = ConsoleKey.RightArrow then playerX <- playerX + 1 //check if the square that the player is trying to move to is empty if map.Item( playerY ).Chars( playerX ) = ' ' then //ok it was empty, clear the square they were standing on Console.SetCursorPosition( oldX, oldY ) Console.Write( ' ' ) //now draw them at the new square Console.SetCursorPosition( playerX, playerY ) Console.Write( '@' ) else //they can't move there, change their location back to the old location playerX <- oldX playerY <- oldY //wait for them to press a key and store it in keyInfo keyInfo <- Console.ReadKey( true )So my question is, what do I need to learn in order to rewrite this more functionally, can you give me some hints, a vague overview, that kind of thing. I'd prefer a shove in the right direction rather than just seeing some code, but if that's the easiest way for you to explain it to me then fine, but in that case can you please also explain the "why" rather the "how" of it? 解决方案 Game programming in general will test your ability to manage complexity. I find that functional programming encourages you to break problems your solving into smaller pieces.The first thing you want to do is turn your script into a bunch of functions by separating all the different concerns. I know it sounds silly but the very act of doing this will make the code more functional (pun intended.) Your main concern is going to be state management. I used a record to manage the position state and a tuple to manage the running state. As your code gets more advanced you will need objects to manage state cleanly.Try adding more to this game and keep breaking the functions apart as they grow. Eventually you will need objects to manage all the functions.On a game programming note don't change state to something else and then change it back if it fails some test. You want minimal state change. So for instance below I calculate the newPosition and then only change the playerPosition if this future position passes.open System// use a third party vector class for 2D and 3D positions// or write your own for praticetype Pos = {x: int; y: int} with static member (+) (a, b) = {x = a.x + b.x; y = a.y + b.y}let drawBoard map = //clear the console Console.Clear() //send each row of the map to the Console map |> List.iter (printfn "%s")let movePlayer (keyInfo : ConsoleKeyInfo) = match keyInfo.Key with | ConsoleKey.UpArrow -> {x = 0; y = -1} | ConsoleKey.DownArrow -> {x = 0; y = 1} | ConsoleKey.LeftArrow -> {x = -1; y = 0} | ConsoleKey.RightArrow -> {x = 1; y = 0} | _ -> {x = 0; y = 0}let validPosition (map:string list) position = map.Item(position.y).Chars(position.x) = ' '//clear the square player was standing onlet clearPlayer position = Console.SetCursorPosition(position.x, position.y) Console.Write( ' ' )//draw the square player is standing onlet drawPlayer position = Console.SetCursorPosition(position.x, position.y) Console.Write( '@' )let takeTurn map playerPosition = let keyInfo = Console.ReadKey true // check to see if player wants to keep playing let keepPlaying = keyInfo.Key <> ConsoleKey.Q // get player movement from user input let movement = movePlayer keyInfo // calculate the players new position let newPosition = playerPosition + movement // check for valid move let validMove = newPosition |> validPosition map // update drawing if move was valid if validMove then clearPlayer playerPosition drawPlayer newPosition // return state if validMove then keepPlaying, newPosition else keepPlaying, playerPosition// main game looplet rec gameRun map playerPosition = let keepPlaying, newPosition = playerPosition |> takeTurn map if keepPlaying then gameRun map newPosition// setup gamelet startGame map playerPosition = drawBoard map drawPlayer playerPosition gameRun map playerPosition//define the maplet map = [ " "; " "; " "; " "; " ############################### "; " # # "; " # ###### # "; " # # # # "; " #### #### # # # "; " # # # # # # "; " # # # # # # "; " #### #### ###### # "; " # = # "; " # = # "; " ############################### "; " "; " "; " "; " "; " " ]//initial player position on the maplet playerPosition = {x = 8; y = 6}startGame map playerPosition 这篇关于F#中的非常简单的RogueLike,使其更“功能化”的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!
10-12 22:52