有什么方法可以在 Haskell 程序中使用 GLUT 来满足此要求?
Is there any way I can use GLUT to meet this requirement in a Haskell program?
我正在使用 Haskell 和 OpenGL 开发开源 3D 游戏引擎。初始化 window 和 OpenGL 上下文的代码以及获取用户输入的代码当前使用直接 WinAPI 调用。我现在想使应用程序成为多平台的,最初认为可以使用 GLUT 来替代上述解决方案,但是 运行 遇到了与回调相关的问题。
在我的应用程序中,函数 update_play(在 IO monad 中)具有以下行。
control <- messagePump (hwnd_ io_box)
其中 messagePump 是一个 IO 操作,它检查 window 消息队列中的键盘输入和 returns Int 以指示某个有效键被按下或没有有效输入。 update_play 根据结果分支并递归更新游戏状态。如果您想要更多上下文,这个 link 是 Github 上的相关模块:https://github.com/Mushy-pea/Game-Dangerous/blob/master/Game_logic.hs
我对 GLUT 的问题是它使用回调处理键盘输入,最接近我的要求(在 Graphics.UI.GLUT 上的 Hackage 中绑定)定义如下。
type KeyboardCallback = Char -> Position -> IO ()
下面是一个测试程序,我希望它能证明这种方法可行。由于回调(下面的 handle_input)由 GLUT 事件循环使用代表用户输入的参数调用,因此似乎不可能从我的程序的其余部分获取任何信息。因此,我的程序似乎不可能从中获得结果,因为它可能为此执行的任何 IO 操作(例如写入 IORef)都需要它具有对此类对象的引用。
在示例中,我尝试使用异常进行通信,但它们没有被捕获,我怀疑这是由于 handle_input 被外部库调用所致。如果有人可以建议我如何解决这个问题(即从回调中获取 Int,就像我在实际应用程序中从 messagePump 中所做的那样),我将不胜感激。谢谢。
module Main where
import System.IO
import Data.Bits
import Control.Exception
import Graphics.GL.Core33
import Graphics.UI.GLUT
data ControlButton = W_key | S_key | A_key | D_key | Default_control deriving (Eq, Show)
instance Exception ControlButton
main = do
initialize "test.exe" []
initialWindowSize $= (Size 800 800)
initialDisplayMode $= [RGBAMode, WithAlphaComponent, WithDepthBuffer, DoubleBuffered]
window_id <- createWindow "Test"
actionOnWindowClose $= Exit
displayCallback $= repaint_window
keyboardCallback $= (Just handle_input)
glClearColor 0 0 0.75 0
iteration 0
iteration :: Int -> IO ()
iteration c = do
threadDelay 33333
putStr ("\nc: " ++ show c)
control <- catch check_events (\e -> map_control e)
if control == 1 then putStr "\nW pressed"
else if control == 2 then putStr "\nS pressed"
else if control == 3 then putStr "\nA pressed"
else if control == 4 then putStr "\nD pressed"
else return ()
iteration (c + 1)
check_events :: IO Int
check_events = do
mainLoopEvent
return 0
map_control :: ControlButton -> IO Int
map_control e = do
if e == W_key then return 1
else if e == S_key then return 2
else if e == A_key then return 3
else if e == D_key then return 4
else return 0
repaint_window :: IO ()
repaint_window = do
glClear (GL_COLOR_BUFFER_BIT .|. GL_DEPTH_BUFFER_BIT)
swapBuffers
handle_input :: Char -> Position -> IO ()
handle_input key position = do
if key == 'w' then throw W_key
else if key == 's' then throw S_key
else if key == 'a' then throw A_key
else if key == 'd' then throw D_key
else throw Default_control
史蒂文
你说
any IO action it might perform to do this (such as writing to an IORef) would require it to have a reference to such an object
所以给它一个这样的对象的引用!
handle_input :: IORef ControlButton -> Char -> Position -> IO ()
handle_input = {- I bet you can write this yourself -}
iteration :: IORef ControlButton -> Int -> IO ()
iteration = {- same -}
main = do
{- ... -}
ref <- newIORef Default_control
keyboardCallback $= Just (handle_input ref)
{- ... -}
iteration ref 0
我正在使用 Haskell 和 OpenGL 开发开源 3D 游戏引擎。初始化 window 和 OpenGL 上下文的代码以及获取用户输入的代码当前使用直接 WinAPI 调用。我现在想使应用程序成为多平台的,最初认为可以使用 GLUT 来替代上述解决方案,但是 运行 遇到了与回调相关的问题。
在我的应用程序中,函数 update_play(在 IO monad 中)具有以下行。
control <- messagePump (hwnd_ io_box)
其中 messagePump 是一个 IO 操作,它检查 window 消息队列中的键盘输入和 returns Int 以指示某个有效键被按下或没有有效输入。 update_play 根据结果分支并递归更新游戏状态。如果您想要更多上下文,这个 link 是 Github 上的相关模块:https://github.com/Mushy-pea/Game-Dangerous/blob/master/Game_logic.hs
我对 GLUT 的问题是它使用回调处理键盘输入,最接近我的要求(在 Graphics.UI.GLUT 上的 Hackage 中绑定)定义如下。
type KeyboardCallback = Char -> Position -> IO ()
下面是一个测试程序,我希望它能证明这种方法可行。由于回调(下面的 handle_input)由 GLUT 事件循环使用代表用户输入的参数调用,因此似乎不可能从我的程序的其余部分获取任何信息。因此,我的程序似乎不可能从中获得结果,因为它可能为此执行的任何 IO 操作(例如写入 IORef)都需要它具有对此类对象的引用。
在示例中,我尝试使用异常进行通信,但它们没有被捕获,我怀疑这是由于 handle_input 被外部库调用所致。如果有人可以建议我如何解决这个问题(即从回调中获取 Int,就像我在实际应用程序中从 messagePump 中所做的那样),我将不胜感激。谢谢。
module Main where
import System.IO
import Data.Bits
import Control.Exception
import Graphics.GL.Core33
import Graphics.UI.GLUT
data ControlButton = W_key | S_key | A_key | D_key | Default_control deriving (Eq, Show)
instance Exception ControlButton
main = do
initialize "test.exe" []
initialWindowSize $= (Size 800 800)
initialDisplayMode $= [RGBAMode, WithAlphaComponent, WithDepthBuffer, DoubleBuffered]
window_id <- createWindow "Test"
actionOnWindowClose $= Exit
displayCallback $= repaint_window
keyboardCallback $= (Just handle_input)
glClearColor 0 0 0.75 0
iteration 0
iteration :: Int -> IO ()
iteration c = do
threadDelay 33333
putStr ("\nc: " ++ show c)
control <- catch check_events (\e -> map_control e)
if control == 1 then putStr "\nW pressed"
else if control == 2 then putStr "\nS pressed"
else if control == 3 then putStr "\nA pressed"
else if control == 4 then putStr "\nD pressed"
else return ()
iteration (c + 1)
check_events :: IO Int
check_events = do
mainLoopEvent
return 0
map_control :: ControlButton -> IO Int
map_control e = do
if e == W_key then return 1
else if e == S_key then return 2
else if e == A_key then return 3
else if e == D_key then return 4
else return 0
repaint_window :: IO ()
repaint_window = do
glClear (GL_COLOR_BUFFER_BIT .|. GL_DEPTH_BUFFER_BIT)
swapBuffers
handle_input :: Char -> Position -> IO ()
handle_input key position = do
if key == 'w' then throw W_key
else if key == 's' then throw S_key
else if key == 'a' then throw A_key
else if key == 'd' then throw D_key
else throw Default_control
史蒂文
你说
any IO action it might perform to do this (such as writing to an IORef) would require it to have a reference to such an object
所以给它一个这样的对象的引用!
handle_input :: IORef ControlButton -> Char -> Position -> IO ()
handle_input = {- I bet you can write this yourself -}
iteration :: IORef ControlButton -> Int -> IO ()
iteration = {- same -}
main = do
{- ... -}
ref <- newIORef Default_control
keyboardCallback $= Just (handle_input ref)
{- ... -}
iteration ref 0