Pregunta

Estoy desarrollando un clon de juego de estrategia en tiempo real en la plataforma Java y tengo algunas preguntas conceptuales sobre dónde poner y cómo administrar el estado del juego. El juego utiliza oscilación / Java2D como prestación. En la fase de desarrollo actual, sin simulación y no IA está presente y sólo el usuario es capaz de cambiar el estado del juego (por ejemplo, la construcción / demolición de un edificio, complemento eliminar las líneas de producción, ensamble flotas y equipos). Por lo tanto, la manipulación de estado del juego se puede realizar en el hilo de distribución de eventos sin ningún tipo de consulta de la representación. El estado del juego también se utiliza para visualizar distintos tipos de información agregada para el usuario.

Sin embargo, ya que necesito para introducir la simulación (por ejemplo, la construcción de progreso, cambios en la población, movimientos de la flota, proceso de fabricación, etc.), el cambio del estado del juego en un temporizador y EDT seguramente sea más lento.

Deja la opinión que la operación de simulación / AI se realiza en cada 500ms y lo uso SwingWorker para el cálculo de alrededor de 250 ms de longitud. ¿Cómo puedo asegurar, que no existe una condición de carrera en relación con el estado del juego lee entre la simulación y la posible interacción del usuario?

Yo sé que el resultado de la simulación (que es pequeña cantidad de datos) se puede mover de manera eficiente de nuevo a la EDT a través de la SwingUtilities.invokeLater () llamada.

El modelo de estado del juego parece ser demasiado complejo para ser inviable por sólo utilizando clases de valores inmutables de todo el mundo.

¿Hay un enfoque relativamente correcta para eliminar esta condición de carrera de lectura? Tal vez hacer un estado de juego total / parcial clonación en cada paso del temporizador o cambiar el espacio de vida del estado del juego de EDT en algún otro hilo?

Actualización: (a partir de los comentarios que he dado) El juego funciona con 13 jugadores controlados por la IA, 1 jugador humano y tiene alrededor de 10.000 objetos del juego (planetas, edificios, equipos, investigación, etc.). Un objeto de juego, por ejemplo, tiene los siguientes atributos:

World (Planets, Players, Fleets, ...)
Planet (location, owner, population, type, 
    map, buildings, taxation, allocation, ...)
Building (location, enabled, energy, worker, health, ...)

En un escenario, el usuario construye un nuevo edificio en este planeta. Esto se lleva a cabo en EDT como hay que cambiar el mapa y edificios colección. Paralelamente a esto, una simulación se ejecuta en cada 500ms para calcular la asignación de energía a los edificios en todos los planetas del juego, que debe recorrer la colección de edificios para la recopilación de estadísticas. Si se calcula la asignación, que se presenta a la EDT y el campo energético de cada edificio se le asigna.

Sólo interacciones jugador humanos tienen esta propiedad, ya que los resultados del cálculo de IA se aplican a las estructuras de EDT de todos modos.

En general, el 75% de los atributos de los objetos son estáticos y se utilizan sólo para la representación. El resto de la misma se puede cambiar ya sea a través de la interacción del usuario o simulaciones de decisiones / AI. También se asegura que ninguna nueva simulación / AI paso se inicia hasta que el anterior ha escrito todos los cambios.

Mis objetivos son:

  • Evitar el retraso de la interacción del usuario, por ejemplo, usuario coloca el edificio en el planeta y sólo después de 0,5 s se pone la retroalimentación visual
  • Evitar el bloqueo de la EDT con el cálculo, de espera de bloqueo, etc.
  • Evite problemas de concurrencia con el recorrido de recogida y modificación, cambios de atributos

Opciones:

  • Bella objeto de grano de cierre
  • colecciones inmutables
  • campos volátiles
  • Instantánea parcial

Todo esto tiene ventajas, desventajas y hace que el modelo y el juego.

Actualización 2: Estoy hablando de este juego . Mi clon es aquí . Las imágenes podrían ayudar a imaginar las interacciones de representación y modelo de datos.

Actualización 3:

Voy a tratar de dar una pequeña muestra de código para aclarar mi problema, ya que parece partir de los comentarios que se entiende mal:

List<GameObject> largeListOfGameObjects = ...
List<Building> preFilteredListOfBuildings = ...
// In EDT
public void onAddBuildingClicked() {
    Building b = new Building(100 /* kW */);
    largeListOfGameObjects.add(b);
    preFilteredListOfBuildings.add(b);
}
// In EDT
public void paint(Graphics g) {
    int y = 0;
    for (Building b : preFilteredListOfBuildings) {
        g.drawString(Integer.toString(b.powerAssigned), 0, y);
        y += 20;
    }
}
// In EDT
public void assignPowerTo(Building b, int amount) {
    b.powerAssigned = amount;
}
// In simulation thread
public void distributePower() {
    int sum = 0;
    for (Building b : preFilteredListOfBuildings) {
        sum += b.powerRequired;
    }
    final int alloc = sum / (preFilteredListOfBuildings.size() + 1);
    for (final Building b : preFilteredListOfBuildings) {
        SwingUtilities.invokeLater(=> assignPowerTo(b, alloc));            
    }
}

Así que el solapamiento es entre el onAddBuildingClicked () y distributePower (). Ahora imagine el caso de que usted tiene 50 de este tipo de superposiciones entre diferentes partes del modelo de juego.

¿Fue útil?

Solución

Esto suena como que podría beneficiarse de un enfoque cliente / servidor:

El jugador es un cliente - la interactividad y la representación suceden en ese extremo. Por lo que el jugador presiona un botón, la solicitud va al servidor. La respuesta del servidor vuelve, y el estado del jugador se actualiza. En cualquier punto entre estas cosas que suceden, la pantalla se puede volver a pintar, y refleja el estado del juego y cuando el cliente lo sabe actualmente.

La IA es también un cliente - que es el equivalente a un bot

.

La simulación es el servidor. Se pone actualizaciones de sus clientes en diferentes momentos y actualiza el estado del mundo, a continuación, envía estas actualizaciones a todo el mundo, según corresponda. Aquí es donde se enlaza con su situación: La simulación / AI exige un mundo estático, y muchas cosas están sucediendo a la vez. El servidor puede simplemente poner en cola las solicitudes de cambio y aplicarlas antes de enviar las actualizaciones de vuelta al cliente (s). Así que en cuanto el servidor del que se trate, el mundo del juego no está realmente cambiando en tiempo real, que está cambiando cada vez que el servidor de tejer bien decide que es.

Por último, en el lado del cliente, se puede evitar el retraso entre la pulsación del botón y ver el resultado haciendo algunos cálculos aproximados rápidos y mostrando un resultado (por lo que se cumple la necesidad inmediata) y luego mostrando el resultado más correcto cuando el servidor obtiene en torno a hablar con usted.

Tenga en cuenta que esto no tiene realmente para ser implementado en una red TCP / IP over-the-internet tipo de camino, sólo que ayuda a pensar en esos términos.

Como alternativa, se puede colocar la responsabilidad de mantener los datos coherentes durante la simulación en una base de datos, puesto que ya están construidos con el bloqueo y la coherencia en mente. Algo así como sqlite podría trabajar como parte de una solución no en red.

Otros consejos

No estoy seguro de entender completamente el comportamiento que busca, pero suena como que necesita algo así como un cambio de estado de rosca / cola por lo que todos los cambios de estado son manejadas por un solo hilo.

Crear un API, tal vez como SwingUtilities.invokeLater () y / o SwingUtilities.invokeAndWait () para la cola de cambio de estado para manejar sus peticiones de cambio de estado.

¿Cómo que se refleja en la interfaz gráfica de usuario Creo depende del comportamiento que busca. es decir, no puede retirar el dinero porque el estado actual es de $ 0, o el pop de nuevo al usuario de que la cuenta estaba vacía cuando la solicitud se procesó retirar. (Probablemente no con la terminología ;-))

El método más sencillo es hacer la simulación lo suficientemente rápido para ejecutar en el EDT. Prefiero programas que funcionan!

En el modelo de dos hilos, lo que sugeriría es sincronizar el modelo de dominio con un modelo de renderización. El procesamiento modelo debe mantener los datos en lo que llegó a partir del modelo de dominio.

Para una actualización: En el hilo de simulación bloquear el modelo de render. Traverse el render actualización de modelos donde las cosas son diferentes de lo que se espera actualizar el modelo de render. Cuando haya terminado de atravesar, desbloquear el modelo de hacer y programar un repintado. Tenga en cuenta que en este enfoque no es necesario un bazillion oyentes.

El procesamiento modelo puede tener diferentes profundidades. En un extremo podría ser una imagen y la operación de actualización es sólo para reemplazar una sola referencia con el nuevo objeto de imagen (esto suele manejar, por ejemplo, cambiar el tamaño u otra interacción superficial muy bien). Es posible que no se moleste comprobar si un artículo ha de cambiar y actualizar sólo eveything.

Si se cambia el estado del juego es rápido (una vez que sepa qué hay que cambiar a) se puede tratar el estado del juego al igual que otros modelos de media vuelta y único cambio o ver el estado de la EDT. Si cambia el estado del juego no es rápido, entonces usted puede sincronizar cambio de estado y hacerlo en el columpio trabajador / temporizador (pero no la EDT) o puede hacerlo en hilo separado que trate de manera similar a la EDT (a la que se señale buscar en el uso de un BlockingQueue para manejar las solicitudes de cambio). La última es más útil si la interfaz de usuario nunca tiene que recuperar información del estado del juego, sino que tiene los cambios de representación enviados por los oyentes u observadores.

¿Es posible actualizar de forma incremental el estado del juego y aún así tener un modelo que es consistente? Por ejemplo volver a calcular para un subconjunto de planeta / reproductor / objetos flota en el medio hace que las actualizaciones / usuario.

Si es así, podría ejecutar actualizaciones incrementales de la EDT que sólo calcular una pequeña parte del estado antes de permitir que la EDT para procesar las entradas del usuario y de procesamiento.

Después de cada actualización incremental en el EDT que tendría que recordar cuánto del modelo aún no se ha actualizado y programar una nueva SwingWorker en la EDT para continuar con este proceso después de las entradas de usuario pendientes y la representación se ha realizado.

Esto debería permitir a evitar la copia o el bloqueo del modelo de juego al mismo tiempo mantener las interacciones de los usuarios sensibles.

Creo que no debería tener ningún dato de la tienda o hacer cambios a cualquiera de los objetos en sí, sólo se debe utilizar para mantener una referencia a un objeto y cuando ese objeto necesita ser cambiado, tiene el jugador que hace el cambio el cambio directamente. En este caso, lo único que tiene que hacer es sincronizar cada objeto en el mundo del juego de modo que cuando un jugador está haciendo un cambio, ningún otro jugador puede hacerlo. He aquí un ejemplo de lo que estoy pensando:

Un jugador necesita saber acerca de un planeta, por lo que pide Mundial para que el Planeta (cómo depende de su aplicación). Mundial devuelve una referencia al objeto planeta Un jugador pidió. Un jugador decide hacer un cambio, por lo que lo hace. Digamos que se añade un edificio. El método para agregar un edificio para el planeta está sincronizado por lo que sólo un jugador puede hacerlo a la vez. El edificio tendrá un seguimiento de su propio tiempo de construcción (si la hay) de modo método de construcción complemento del planeta sería liberado casi de inmediato. De esta manera los múltiples jugadores pueden solicitar información en el mismo planeta al mismo tiempo sin afectar a los demás y los jugadores pueden añadir edificios casi simultáneamente sin mucho aparición de retraso. Si dos jugadores están buscando un lugar para poner el edificio (si eso es parte de su juego), a continuación, comprobar la idoneidad de un lugar no será una consulta un cambio.

Lo siento si esto no responde a la pregunta que eres, no estoy segura de haber entendido correctamente.

¿Qué hay de la implementación de una arquitectura de tuberías y filtros. Tubos se conectan juntos filtros y solicitudes de cola si el filtro no es lo suficientemente rápido. Procesamiento ocurre filtros en su interior. El primer filtro es el motor AI, mientras que el motor de renderizado está implementado por un conjunto de filtros posteriores.

En cada paso del temporizador, el nuevo estado dinámico mundo se calcula sobre la base de todas las entradas (El tiempo es también una entrada) y un copia inserta en el primer tubo.

En el caso más sencillo su motor de renderizado se implementa como un solo filtro. Sólo toma las instantáneas del estado de la tubería de entrada y lo hace junto con el estado estático. En un juego en vivo, el motor de renderizado puede que desee saltar estados si hay más de uno en la tubería, mientras que si está haciendo un punto de referencia o la salida de un video que querrá hacer cada uno.

Los más filtros que pueden descomponerse su motor de renderizado en, mejor será el paralelismo será. Tal vez incluso es posible descomponer el motor de IA, por ejemplo, es posible que desee separar estado dinámico en constante transformación y el estado cambiante lento.

Esta arquitectura le da buen paralelismo sin un montón de sincronización.

Un problema con esta arquitectura es que la recolección de basura se va a ejecutar con frecuencia congelar todos los hilos cada vez, es posible matar a cualquier ventaja obtenida a partir de múltiples hilos.

Parece que se necesita un PriorityQueue para poner las actualizaciones al modelo adelante, en el que se actualiza frmo el usuario tiene prioridad sobre las actualizaciones de la simulación y otros insumos. Lo que te escucho decir es que el usuario siempre necesita una retroalimentación inmediata sobre sus acciones wheras las otras entradas (simulación, de otra manera) podría tener trabajadores que pueden tardar más de un paso de simulación. Entonces sincronizar en el PriorityQueue.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top