Игра с использованием F # и Silverlight: избежание изменяемого состояния - PullRequest
4 голосов
/ 27 апреля 2011

Я пытаюсь написать игру, используя F # и Silverlight, и немного борюсь с неизменностью.

Я хочу немного отделить игру от вида, поэтому я поместил ее в модуль и заставил ее функцию обновления возвращать новый экземпляр состояния мира, обеспечивая тем самым неизменность.

Представление (AppControl) отвечает за рисование мира.

Тем не менее, я не вижу способа превратить мир в опорную ячейку в представлении.

Теперь, я думаю, что изменяемое состояние достаточно локально, чтобы не вызывать никаких проблем (пожалуйста, исправьте меня, если я ошибаюсь), мне просто любопытно, если кто-то может придумать способ полностью избежать изменяемого состояния?

Вот схема приложения, я попытался свести проблему к сути:

open System
open System.Windows
open System.Windows.Controls
open System.Windows.Media

module Game =
    type World = { State : int }

    let init() = 
        { State = 0 }

    // immutable update loop
    let updateWorld world = 
        { State = world.State + 1 }


type AppControl() =
    inherit UserControl()

    let canvas = new Canvas()
    let textBlock = new TextBlock()
    let world = Game.init() |> ref // mutable world

    let drawWorld (world : Game.World) = 
        textBlock.Text <- world.State.ToString()

    // mutating game loop
    let gameLoop world = 
        world := Game.updateWorld !world
        drawWorld !world
        ()

    do  
        canvas.Children.Add(textBlock)
        base.Content <- canvas
        CompositionTarget.Rendering.Add (fun _ -> gameLoop world)


type App() as this =
    inherit Application()
    let main = new AppControl()
    do this.Startup.Add(fun _ -> this.RootVisual <- main)

1 Ответ

7 голосов
/ 27 апреля 2011

Структура вашего кода выглядит нормально - изменяемое состояние локализовано в пользовательском интерфейсе (который в любом случае изменяем), так что все в порядке.Вы не изменяете поле из любого замыкания, поэтому вы можете использовать изменяемое поле (объявленное с использованием let mutable world = ..) вместо ref ячейки.

Чтобы полностью избежать мутации, вы можете использовать асинхронный рабочий процессвыполняется в потоке GUI):

type AppControl() =
    inherit UserControl()

    let canvas = new Canvas()
    let textBlock = new TextBlock()

    let drawWorld (world : Game.World) = 
        textBlock.Text <- world.State.ToString()

    // Asynchronous loop that waits for 'Rendering', updates
    // the world & draws it and then continues waiting
    let gameLoop world = async {
        let! _ = Async.AwaitEvent CompositionTarget.Rendering
        let newWorld = Game.updateWorld world
        drawWorld newWorld 
        return! gameLoop newWorld }

    do  
        canvas.Children.Add(textBlock)
        base.Content <- canvas
        gameLoop Game.init() |> Async.StartImmediate

Функция gameLoop является асинхронной, поэтому она не блокирует ни один поток.Он запускается с использованием Async.StartImmediate, что означает, что он будет работать только в потоке GUI (поэтому доступ к элементам и событиям GUI из тела безопасен).Внутри функции вы можете дождаться возникновения события (используя Async.AwaitEvent) и затем выполнить какое-либо действие.Последняя строка (return!) является хвостовым вызовом, поэтому функция будет продолжаться, пока приложение не будет закрыто.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...