Могу ли я использовать GLUT для выполнения этого требования в программе на Haskell? - PullRequest
2 голосов
/ 29 апреля 2019

Я занимаюсь разработкой игрового движка с открытым исходным кодом для 3D, используя Haskell и OpenGL.Код, который инициализирует окно и контекст OpenGL, а также код для получения пользовательского ввода, в настоящее время использует прямые вызовы WinAPI.Теперь я хочу сделать приложение мультиплатформенным и изначально думал, что GLUT можно использовать для замены вышеприведенного решения, но столкнулся с проблемой, связанной с обратными вызовами.

В моем приложении функция update_play (в монаде IO)имеет следующую строку:

control <- messagePump (hwnd_ io_box)

, где messagePump - это действие ввода-вывода, которое проверяет очередь сообщений окна на ввод с клавиатуры и возвращает Int, чтобы указать, нажата ли определенная действительная клавиша или нет действительного ввода.update_play разветвляет результат и рекурсивно обновляет состояние игры.Если вы хотите больше контекста, эта ссылка на соответствующий модуль на 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 с аргументами, представляющими пользовательский ввод, кажется невозможным получить в него какую-либо информацию из остальной части моей программы.Поэтому для моей программы кажется невозможным получить от нее результат, поскольку любое действие ввода-вывода, которое она может выполнить для этого (например, запись в 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

Стивен

1 Ответ

4 голосов
/ 29 апреля 2019

Вы говорите

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

так что дайте ему ссылку на такой объект!

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
...