Актер/передача сообщений в многопоточном цикле игровых событий

Я создаю приложение для Android (используя Scala 2.9) и использую поток, который отображается в SurfaceView; это для игры, поэтому она должна обновляться как можно чаще. Я предполагаю, что эта проблема похожа на другие игровые «циклы событий», где ввод поступает из другого потока.

Вот грубое приближение текущего подхода, основанного на синхронизации. Это «работает достаточно хорошо», но у меня есть общие опасения по поводу необходимости использовать явную синхронизацию и «привязывать» поток просмотра/ввода.

Вид, "поток пользовательского интерфейса":

def View.onTouchEvent(e) { // on UI thread
  Game.handleInput(e)
}

Игра, "Игровая нить":

def Game.handleInput(e) = S synchronized { // on UI thread
  alterStateBasedOnInput
}
def Game.run () { // on Game thread 
  while (running) {
    S synchronized { 
      doGameStuff
    }
    View.post(someStateRelayedViaRunnable)
    yield
  }
}

Вместо того, чтобы явно использовать синхронизацию, я хотел бы иметь что-то вроде этого:

def View.onTouchEvent(e) { // on UI thread
   Game.sendMessage(e)
}
def Game.run () { // on Game thread 
  while (running) {
    processMessage
    doGameStuff
    View.sendMessage(someState) // hopefully same as Game.sendMessage
    yield
  }
}

Теперь это относительно легко реализовать вручную с помощью ConcurrentLinkedQueue или аналогичного, но я действительно не хотел бы изобретать велосипед здесь. Кроме того, было бы неплохо использовать такой актер/очередь для обратной отправки в пользовательский интерфейс — прямо сейчас я использую поддержку Android для публикации (асинхронного) Runnable в потоке пользовательского интерфейса.

Я кратко рассмотрел несколько различных реализаций акторов (в основном стандартные Scala и Scalaz) и несколько различных библиотек Java для «передачи сообщений», таких как Jetlang, но большинство из них, похоже, используют неявные потоки или службу исполнителя потоков. Но в моем случае я хочу [запускать актера и] обрабатывать сообщения в определенное время в определенном потоке. Для View.sendMessage сообщения также должны обрабатываться в потоке пользовательского интерфейса, но время не так важно и может быть связано с выполнением Runnable, упомянутым выше.

Тогда, я думаю, мой вопрос, учитывая вышеизложенное:

Каким был бы хороший (например, "эффективный" и идиоматический) подход к передаче данных между этими двумя потоками?

(Я также готов принять предположение, что я совершенно не понимаю акторы Scala и/или акторы Scalaz и/или другие библиотеки передачи сообщений; кажется, что Scalaz может работать так, как я себе представляю, но мне сложно уследить)


person Community    schedule 17.01.2013    source источник


Ответы (1)


Что ж, хотя мне все еще хотелось бы узнать об универсальном/многоразовом подходе к вышеизложенному, практичность требует. Это также можно сделать, запустив Looper в потоке игры, а затем поместив игровой "материал цикла событий" внутри IdleHandler, но мне не понравилась эта инверсия..

Вот как я это реализовал на данный момент:

Класс игры/потока:

var handler: Handler = _ // handler created on View thread

// Send Message to Looper that exists on View thread
// (Created implicitly for all UI threads.)
def publishEvent(event: OutputEvent) {
  handler.obtainMessage(0, event).sendToTarget
}

protected val queue = new ConcurrentLinkedQueue[InputEvent]

def queueEvent(event: InputEvent) { // on UI thread
  queue.add(event)
}

def processQueuedEvents() { // on game Thread
  @tailrec
  def processNextEvent {
    val event = queue.poll
    if (event ne null) {
      doStuffWithInputEvent(event)
      processNextEvent
    }
  }    
  processNextEvent
}

override def run() { // on game Thread
  while (running) {
    processQueuedEvents
    doOtherGameStuff ~ call_publishEvent ~ etc
  }
}

Посмотреть класс:

// Created on UI thread, passed to Game instance
// (The Looper will dispatch Messages to Handlers.)
val handler = new Handler {
  override def handleMessage(m: Message) {
    val event = m.obj
    doStuffWithOutputEvent(event)
  }
}

// on UI thread
override def onTouch(v: View, ev: MotionEvent): Boolean = {
   // safely queued, will be processed at the start of each game loop
   game.queueEvent(InputEvent(..))
}
person Community    schedule 18.01.2013