Los tiempos de iteración rápidos son clave para desarrollar juegos, mucho más que gráficos y motores sofisticados con un montón de características en mi opinión. No es de extrañar que muchos desarrolladores pequeños elijan lenguajes de script.
La forma Unity 3D de poder pausar un juego y modificar activos Y código, luego continuar y hacer que los cambios surtan efecto de inmediato es absolutamente genial para esto. Mi pregunta es, ¿alguien ha implementado un sistema similar en los motores de juego C ++?
He oído que algunos motores realmente súper de alta gama lo hacen, pero estoy más interesado en descubrir si hay una manera de hacerlo en un motor o juego de cosecha propia.
Obviamente, habría compensaciones, y no puedo imaginar que puedas recompilar tu código mientras el juego está en pausa, volver a cargarlo y trabajar en cualquier circunstancia o plataforma.
Pero tal vez sea posible para la programación de IA y los módulos lógicos de nivel simple. No es que quiera hacer eso en ningún proyecto a corto (o largo plazo), pero tenía curiosidad.
fuente
El intercambio en caliente es un problema muy, muy difícil de resolver con código binario. Incluso si su código se coloca en bibliotecas de enlaces dinámicos separadas, su código debe asegurarse de que no haya referencias directas a las funciones en la memoria (ya que la dirección de las funciones puede cambiar con una recompilación). Esto esencialmente significa no usar funciones virtuales, y cualquier cosa que use punteros de función lo hace a través de algún tipo de tabla de despacho.
Cosas peludas y restrictivas. Es mejor usar un lenguaje de script para el código que necesita una iteración rápida.
En el trabajo, nuestra base de código actual es una mezcla de C ++ y Lua. Lua tampoco es una parte pequeña de nuestro proyecto: es casi una división 50/50. Hemos implementado la recarga sobre la marcha de Lua, para que pueda cambiar una línea de código, recargar y continuar. De hecho, puedes hacer esto para corregir errores que ocurren en el código Lua, ¡sin reiniciar el juego!
fuente
(Es posible que desee saber sobre el término "parche de mono" o "golpe de pato" si no es por otra cosa que la imagen mental humorística).
Aparte de eso: si su objetivo es disminuir el tiempo de iteración para los cambios de "comportamiento", intente algunos enfoques que lo lleven a la mayor parte del camino, y combine bien para permitir más de esto en el futuro.
(¡Esto saldrá por una tangente, pero prometo que volverá!)
Muchos de estos puntos son beneficiosos incluso si no llega a recargar los datos o el código.
Anécdotas de apoyo:
En una gran PC RTS (~ equipo de 120 personas, principalmente C ++), había un sistema de ahorro de estado increíblemente profundo, que se usaba al menos para tres propósitos:
Desde entonces he usado grabación / reproducción determinista en un juego de cartas C ++ y Lua para el DS. Nos conectamos a la API que diseñamos para la IA (en el lado de C ++) y registramos todas las acciones del usuario y de la IA. Utilizamos esta funcionalidad en el juego (para proporcionar una repetición para el jugador), pero también para diagnosticar problemas: cuando hubo un bloqueo o un comportamiento extraño, todo lo que tuvimos que hacer fue obtener el archivo guardado y reproducirlo en una compilación de depuración.
Desde entonces, también he usado superposiciones más de un par de veces, y lo combinamos con nuestro "araña automáticamente este directorio y cargamos nuevo contenido en el sistema portátil". Todo lo que tendríamos que hacer es dejar la escena / nivel / lo que sea y volver, y no solo se cargarían los nuevos datos (sprites, diseño de nivel, etc.) sino también cualquier código nuevo en la superposición. Desafortunadamente, eso se está volviendo mucho más difícil con las computadoras de mano más recientes debido a la protección contra copias y los mecanismos anti-piratería que tratan el código especialmente. Sin embargo, todavía lo hacemos para guiones lua.
Por último, pero no menos importante: puede (y lo he hecho, en varias circunstancias específicas muy pequeñas) hacer un poco de puñetazos al parchear directamente los códigos de operación de instrucciones. Sin embargo, esto funciona mejor si está en una plataforma fija y un compilador, y debido a que es casi imposible de mantener, muy propenso a errores y limitado en lo que puede lograr rápidamente, en su mayoría solo lo uso para redirigir el código durante la depuración. Sin embargo , sí te enseña muchísimo sobre la arquitectura de tu conjunto de instrucciones.
fuente
Puede hacer algo como módulos de intercambio en caliente en tiempo de ejecución implementando los módulos como bibliotecas de enlaces dinámicos (o bibliotecas compartidas en UNIX), y usando dlopen () y dlsym () para cargar funciones dinámicamente desde la biblioteca.
Para Windows, los equivalentes son LoadLibrary y GetProcAddress.
Este es un método de C, y tiene algunas dificultades al usarlo en C ++, puede leer sobre esto aquí .
fuente
Si está utilizando Visual Studio C ++, puede pausar y recompilar su código en determinadas circunstancias. Visual Studio admite Editar y continuar . Adjunte a su juego en el depurador, haga que se detenga en un punto de interrupción y luego modifique el código después de su punto de interrupción. Si va a guardar y luego continuar, Visual Studio intentará recompilar el código, reinsertarlo en el ejecutable en ejecución y continuar. Si todo va bien, el cambio que realizaste se aplicará al juego en ejecución sin tener que hacer un ciclo completo de compilación-construcción-prueba. Sin embargo, lo siguiente evitará que esto funcione:
He usado Editar y continuar para mejorar dramáticamente la velocidad de cosas como la iteración de la interfaz de usuario. Supongamos que tiene una interfaz de usuario funcional construida completamente en código, excepto que cambió accidentalmente el orden de dibujo de dos cuadros para que no pueda ver nada. Al cambiar 2 líneas de código en vivo, puede ahorrarse un ciclo de compilación / construcción / prueba de 20 minutos para verificar una solución trivial de la interfaz de usuario.
Esta NO es una solución completa para un entorno de producción, y he encontrado que la mejor solución es mover la mayor parte posible de su lógica a los archivos de datos y luego hacer que esos archivos de datos sean recargables.
fuente
Como han dicho otros, es un problema difícil, vinculando dinámicamente C ++. Pero es un problema resuelto: es posible que haya oído hablar de COM o uno de los nombres de marketing que se le han aplicado a lo largo de los años: ActiveX.
COM tiene un poco de mal nombre desde el punto de vista del desarrollador porque puede ser un gran esfuerzo implementar componentes C ++ que expongan su funcionalidad al usarlo (aunque esto se hace más fácil con ATL, la biblioteca de plantillas ActiveX). Desde el punto de vista del consumidor, tiene un mal nombre porque las aplicaciones que lo usan, por ejemplo, para incrustar una hoja de cálculo de Excel en un documento de Word o un diagrama de Visio en una hoja de cálculo de Excel, tienden a fallar bastante en el día. Y eso se reduce a los mismos problemas: incluso con toda la orientación que ofrece Microsoft, COM / ActiveX / OLE fue / es difícil de acertar.
Voy a enfatizar que la tecnología de COM no es inherentemente mala en sí misma. En primer lugar, DirectX usa interfaces COM para exponer su funcionalidad y eso funciona bastante bien, al igual que una multitud de aplicaciones que integran Internet Explorer usando su control ActiveX. En segundo lugar, es una de las formas más simples de vincular dinámicamente el código C ++: una interfaz COM es esencialmente una clase virtual pura. Aunque tiene un IDL como CORBA, no está obligado a usarlo, especialmente si las interfaces que define solo se usan dentro de su proyecto.
Si no está escribiendo para Windows, no piense que no vale la pena considerar COM. Mozilla lo volvió a implementar en su base de código (utilizada en el navegador Firefox) porque necesitaban una forma de componente de su código C ++.
fuente
Hay una aplicación de tiempo de ejecución C ++ compilado para el código del juego aquí . Además, sé que hay al menos un motor de juego patentado que hace lo mismo. Es complicado, pero factible.
fuente