El Snake de toda la vida, completamente jugable, con una particularidad: todo el
tablero vive dentro de un único DataWindow. Sin imágenes, sin gráficos externos, sin
controles de terceros — solo Modify() y un truco de renderizado enlazado a
expresiones del DataWindow.
Hace un tiempo me monté un buscaminas en el que todo el campo de minas era un DataWindow con rectángulos creados al vuelo. Quise llevar la idea más lejos: ¿podría un DataWindow aguantar animación en tiempo real a varios FPS, de forma fluida y sin parpadeo? Pues resulta que sí. Este repo es el resultado.
| Tecla | Acción |
|---|---|
| ⬆️ ⬇️ ⬅️ ➡️ | Mover la serpiente |
Espacio |
Pausa / reanudar |
Botón Restart |
Partida nueva |
- Come el cuadradito rojo para crecer.
- Las paredes dan la vuelta: sales por un lado y apareces por el opuesto.
- No te muerdas a ti mismo.
El estado del juego vive en dos arrays de long (las coordenadas del cuerpo) más un
integer con la dirección actual. La rejilla es de 31 × 21 celdas.
El DataWindow dw_snake tiene:
- Una fila por cada fila del juego (21 filas).
- Una columna
char(40)llamadacellscuyo valor codifica el estado de cada celda de esa fila usando un carácter por celda:
| Carácter | Significado | Color |
|---|---|---|
0 |
vacío | invisible |
1 |
cuerpo | 🟢 verde |
2 |
cabeza | 🟡 amarillo |
9 |
comida | 🔴 rojo |
Al abrir la ventana, wf_init_grid() lanza 31 Modify("create rectangle …") —
una sola vez — para crear un rectángulo persistente por columna. Cada rectángulo
tiene sus propiedades visible y brush.color enlazadas a una expresión del
DataWindow que lee de la columna cells:
visible = "0~tIf(long(mid(cells,N,1))>0,1,0)"
brush.color = "65280~tCASE(long(mid(cells,N,1)) WHEN 2 THEN 65535 WHEN 9 THEN 255 ELSE 65280)"
A partir de ahí, el timer no vuelve a llamar a Modify() nunca más. Para pintar
un nuevo frame solo hace 2 o 3 SetItem por tick (nueva cabeza, vieja cabeza → cuerpo,
vieja cola → vacío). El DataWindow reevalúa las expresiones y repinta solo las celdas
afectadas. Sin parpadeo, sin reservas de memoria, sin basura.
Un pequeño flag ib_intimer hace de guardia de reentrada para que el evento del
timer no se solape consigo mismo si un tick tarda más de la cuenta.
- PowerBuilder 2025.
- Ni base de datos ni recursos externos.
- Abre
Pb_Snake_DW.pbslnen PowerBuilder 2025. - Compila el workspace.
- Ejecuta
w_snake.
Se despliega como PowerClient (objeto de proyecto pc_pbsnakedw).
Pb_Snake_Dw/
├── pb_snake_dw.pbl/
│ ├── w_snake.srw ← ventana, lógica del juego, timer y teclado
│ └── dw_snake.srd ← definición del DataWindow
├── pb_snake_dw.pbproj
├── Pb_Snake_DW.pbsln
├── LICENSE
└── README.md
Dos ficheros de código. Alrededor de 280 líneas en total.
https://github.com/rasanfe/Pb_Snake_Dw
La chispa original fue el Minefield DataWindow Game
de Appeon CodeExchange — un buscaminas donde todo el campo se construye dinámicamente
dentro de un DataWindow con Modify(). Esa fue la prueba de que un DataWindow podía
usarse como lienzo de juego completo, y lo que me animó a probar algo animado.
El truco de enlazar las celdas (crear los rectángulos una sola vez y dirigirlos con expresiones del DataWindow en lugar de recrearlos en cada frame) lo aprendí de las Three Simple Games de René Ullrich en Appeon CodeExchange. Su Snake fue quien me enseñó el patrón que convirtió mi prototipo parpadeante en algo que de verdad va suave. ¡Mil gracias!
Este proyecto fue desarrollado por Claude Code siguiendo mis ideas y dirección.
MIT © 2026 Ramón San Félix Ramón
¡Nos vemos en el próximo artículo! Y recuerda: en PowerBuilder, los límites solo están en nuestra imaginación. 🚀
📨 Blog: https://rsrsystem.blogspot.com/
