(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á!)
- Comience con datos , y comience con algo pequeño: vuelva a cargar en los límites ("niveles" o similares), luego continúe hasta usar la funcionalidad del sistema operativo para obtener notificaciones de cambio de archivo o simplemente sondear regularmente.
- (Para puntos de bonificación y tiempos de carga más bajos (nuevamente, disminuyendo el tiempo de iteración), mire la cocción de datos ).
- Las secuencias de comandos son datos y le permiten iterar el comportamiento. Si usa un lenguaje de secuencias de comandos, ahora tiene las notificaciones / la capacidad de volver a cargar esas secuencias de comandos, interpretadas o compiladas. También puede conectar su intérprete a una consola del juego, una toma de red o similar para una mayor flexibilidad de tiempo de ejecución.
- El código también puede ser datos : su compilador puede admitir superposiciones , bibliotecas compartidas, archivos DLL o similares. Por lo tanto, ahora puede elegir un momento "seguro" para descargar y volver a cargar una superposición o DLL, ya sea manual o automática. Las otras respuestas entran en detalles aquí. Tenga en cuenta que algunas variantes de esto pueden interferir con la vecificación de firma criptográfica, el bit NX (sin ejecución) o mecanismos de seguridad similares.
- Considere un sistema profundo de guardado / carga versionado . Si puede guardar y restaurar su estado de manera sólida incluso ante los cambios de código, puede apagar su juego y reiniciarlo con una nueva lógica en el mismo punto exacto. Es más fácil decirlo que hacerlo, pero es factible, y es notablemente más fácil y más portátil que cambiar la memoria para introducir instrucciones.
- Dependiendo de la estructura y el determinismo de su juego, puede grabar y reproducir . Si esa grabación está por encima de los "comandos del juego" (piense en un juego de cartas, por ejemplo), puede alterar todo el código de representación que desee y reproducir la grabación para ver sus cambios. Para algunos juegos esto es tan "fácil" como registrar algunos parámetros iniciales (por ejemplo, una semilla aleatoria) y luego las acciones del usuario. Para algunos es mucho más complicado.
- Haga esfuerzos para reducir el tiempo de compilación . En combinación con los sistemas de guardar / cargar o grabar / reproducir mencionados anteriormente, o incluso con superposiciones o archivos DLL, esto puede disminuir su respuesta más que cualquier otra cosa.
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:
- Se guardó un salvado "superficial", no en el disco, sino en un motor CRC para garantizar que los juegos multijugador permanecieran en simulación de paso de bloqueo, un CRC cada 10-30 cuadros; esto aseguró que nadie estaba haciendo trampa y atrapó errores de desincronización unos cuadros más tarde
- Si se producía un error de desincronización en el modo multijugador, se realizaba un guardado extra profundo en cada cuadro, y nuevamente se alimentaba al motor CRC, pero esta vez el motor CRC generaría muchos CRC, cada uno para lotes más pequeños de bytes. De esta manera, podría decirle exactamente qué parte del estado había comenzado a divergir dentro del último cuadro. Detectamos una desagradable diferencia de "modo de punto flotante predeterminado" entre los procesadores AMD e Intel que usan esto.
- Un ahorro de profundidad normal podría no guardar, por ejemplo, el cuadro exacto de animación que estaba jugando tu unidad, pero obtendría la posición, la salud, etc. de todas tus unidades, permitiéndote guardar y reanudar en cualquier momento durante el juego.
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.