Cómo hice un juego para el Bloc de notas



Mientras leía sobre soluciones inusuales de desarrolladores independientes, me encontré con el oro. Aquí hay un artículo sobre el juego en un editor de texto. Arte, animación, trama: todo es como debería ser.

Creé el juego Y, sin embargo, dolió ( quizás el autor quería decir que duele, pero podría usar esta opción a propósito, aprox. ).


El proyecto se puede descargar aquí , y el código se puede ver en Github .

Todo comenzó en 2017 con la pregunta: "¿Es posible hacer un juego en el Bloc de notas?" Entonces solo sonreí. Han pasado tres años. Después de pensar en cómo funcionará todo y de asegurarme de que sea real, decidí hacer este juego.

Por lo general, presionas un botón y sucede algo en el juego. Presiona A y Mario salta. Todo está relacionado con la recepción de información y comentarios. El juego recibe información y muestra la suya.

En el juego para el Bloc de notas, habrá cambios en la entrada que el usuario realiza en el archivo, y en la salida, cambios que el juego realiza en el archivo. Para hacer esto, la aplicación rastrea la última vez que se guardó el archivo. Si ha cambiado, el juego lee el contenido del archivo e introduce nuevos datos en él.

Hay un problema: el Bloc de notas de Microsoft no comprueba si el archivo ha sido modificado. Tendría que guardar el archivo, cerrarlo y abrirlo nuevamente. Crear un juego así es posible, pero no suena muy divertido. Tuve que buscar una alternativa.

Puedo entender tu decepción debido al hecho de que el juego como resultado no se realizó en el Bloc de notas habitual. Mi título se puede ejecutar en él, solo un proceso un poco confuso. Decidí sacrificar la frescura del proyecto para hacer que el juego sea más agradable.



Alternativa


Tuve que buscar otro editor de texto. El único requisito era la actualización automática de archivos. Aunque un poco más tarde verás que utilicé otra función.

Primero, Notepad ++ y Sublime Text me vinieron a la mente. Pero no se parecen en nada al Bloc de notas; el encanto del proyecto se habría desvanecido por completo. Además, le preguntan al jugador si desea actualizar el archivo. Esto es mucho mejor que cerrar y abrir un archivo, pero aún distrae de la jugabilidad. Quería que el archivo se actualizara automáticamente. Entonces Notepad2 me llamó la atención. El era casi perfecto.

El editor se puede configurar para que parezca MS Notepad, y lo más importante: verifica los cambios realizados en el archivo. Pero al igual que Notepad ++ y Sublime Text, Notepad2 le pregunta al jugador si es necesario cambiar el archivo. Afortunadamente, el editor tiene código fuente abierto, y podría pulirlo a la perfección.



Notepad2 está escrito en C. Estoy un poco familiarizado con este lenguaje, incluso si no se me puede llamar un experto. Un programador de Javascript experimentado podrá leer y comprender la esencia general del código, pero no fue tan fácil entender el código fuente de Notepad2 para hacer los cambios necesarios.

Para empezar, decidí buscar texto en el cuadro de diálogo: “El archivo fue modificado por un programa externo. ¿Recargar el archivo? ". Este es el valor de la variable que se utiliza como argumento en la función del cuadro de diálogo. Y la encontré.

if ((iFileWatchingMode == 2 && !bModified && iEncoding == iOriginalEncoding) ||
        MsgBox(MBYESNO,IDS_FILECHANGENOTIFY) == IDYES) {

Este código verifica si el contenido del archivo ha cambiado. Si ha cambiado, se abre una ventana y el programa verifica si el usuario ha elegido "Sí". Solo necesitaba reemplazar una pieza

MsgBox(MBYESNO,IDS_FILECHANGENOTIFY) == IDYES

a VERDADERO, y el programa comenzó a actualizar automáticamente el archivo. Entonces creé un render basado en ASCII. Queda por crear un motor adecuado.

Dibujo


El juego está hecho con amor: LÖVE es un marco de código abierto para juegos 2D escritos en Lua. Usé esta plataforma durante muchos años e incluso preparé un tutorial . Para este proyecto, el módulo LÖVE del sistema de archivos se utilizó principalmente porque proporciona todas las características necesarias. Por lo general, LÖVE crea una imagen que luego se muestra en la pantalla.

Necesitaba casi lo mismo: la salida del arte ASCII en un archivo de texto. Comencé con una casa y un pájaro, y se suponía que el pájaro debía volar a través de un archivo. Tomó el arte que encontró en ASCII Art , pero el juego usa solo trabajo original (excepto las fuentes).



Y:



descargar arte es solo leer un archivo.

house = love.filesystem.read("art_house.txt")
bird = love.filesystem.read("art_bird.txt")

La casa se usa como fondo, así que comencé dibujando esta imagen en la "pantalla". La pantalla en este caso es home.txt.

love.filesystem.write("home.txt", house)

Quería que el pájaro funcionara de esta manera:

local x, y = 20, 40
drawArt(bird, x, y)

x es el número de columna, y es el número de fila. Por lo tanto, rompió la pantalla y el pájaro en listas de líneas.

-- Get the current canvas
screen = love.filesystem.read("home.txt")

-- Create a table. A table is like an array.
screen_lines = {}

-- The lua pattern (.-)\n says capture everything until the \n (newline).
-- We add an \n to the end of the file so that it captures the last line as well.
for line in (screen .. "\n"):gmatch("(.-)\n") do
    table.insert(screen_lines, line)
end

Con el pájaro hizo lo mismo. Ahora se suponía que el código que describía al pájaro se superponía con el código de la casa. Esto es lo que necesitaba:

  1. Encuentra la línea en la que se debe dibujar el pájaro.
  2. Imprime toda la línea en x.
  3. Imprima el resto de la línea, comenzando con x + la longitud del arte con el pájaro.
  4. Crea una nueva línea con la primera parte, el pájaro y la parte restante.
  5. Repita lo mismo para todas las demás líneas.

En código, se ve así:

function drawArt(art, x, y)
    art_lines = getLines(art)

    -- In Lua, you can get the length of a table and string with #
    for i=1, #screen_lines do
        if i == y then
            for j=1 ,#art_lines do
                -- With string:sub(start, end) we can get part of a string
                local first_part = screen_lines[i]:sub(1, x - 1)
                local second_part = screen_lines[i]
                                    :sub(x + #art_lines[j], #screen_lines[i])
                screen_lines[i] = first_part .. art_lines[i] .. second_part
            end
        end
    end
end

Lo que sucedió:



Probablemente, notó que el pájaro es un rectángulo: en su arte se usan espacios. Para corregir la situación, calculé el número de espacios al comienzo de cada línea y agregué este número a las coordenadas para que solo se dibujara el arte.

-- (%s) gets all the whitespace characters.
local spaces = art_lines[j]:match("(%s*)")
-- Remove the spaces from the line.
art_lines[i] = art_lines[i]:sub(#spaces + 1, #art_lines[i])
local first_part = screen_lines[i]:sub(1, x + #spaces - 1)
local second_part = screen_lines[i]:sub(x + #spaces + #art_lines[j], #screen_lines[i])
screen_lines[i] = first_part .. art_lines[i] .. second_part

Se ha vuelto mucho mejor:



Animación


Comencé a agregar más chips, por ejemplo, animación:



todos los marcos están ubicados en un archivo y separados por la etiqueta {{F}}. La etiqueta se determina durante la lectura y le permite establecer la secuencia de cuadros. Gracias a esto, obtenemos una animación clásica. Crea un temporizador y dibuja cuadros de acuerdo con él.

{{F}}
_   _ 
 'v'
{{F}}
      
--v--
{{F}}
      
_/v\_

También implementé el resultado del texto impreso y mostré por separado una pantalla, un inventario y una ventana para ingresar a la solución. Hubo un problema ¿Cómo sabe el juego que se ha abierto un archivo? Esta es la segunda característica de la que hablé anteriormente.

En el código fuente de Notepad2, prescribí que el archivo debería guardarse inmediatamente después de abrirse. El juego luego verifica si el último tiempo de guardado ha cambiado. Entonces descubre que el archivo estaba abierto y puede cambiarlo.

// At the end of the FileLoad function.
// If the file is not new, and the file has not been reloaded, save it.
if (!bNew && !bReload) {
    FileSave(TRUE,FALSE,FALSE,FALSE);
}

Como resultado, obtuve un marco en el que puedes trabajar. Es hora de crear un juego.

Durante nueve días de desarrollo (a juzgar por la fecha en que se crearon los archivos gif), hice esto:



si ejecutaste el juego, entonces sabes que no tiene texto o animación imprimible. Hubieron varias razones para esto:

  • , HDD/SSD . . , .
  • . , , . , .
  • — , . . . , , -, . . , , . , , , — .


Estaba furioso porque el usuario tuvo que arrastrar el archivo a la ventana del Bloc de notas2 para iniciar el juego. Al hacer doble clic en el archivo, se abrió el Bloc de notas u otro programa predeterminado para leer .txt. Era posible registrar un comando que cambiaba la aplicación para tales archivos en Notepad2, pero personalmente no me gustaría si algún tipo de juego hiciera tal fin en mi computadora.

¿Quizás devuelva la configuración original cuando cierre el juego? Es posible, pero habrá un problema si el juego se bloquea o se cierra inesperadamente.

Todas las decisiones parecían insuficientemente fundamentadas hasta que me di cuenta de que en lugar de los archivos .txt habituales con otra "x" podrían usarse. Para ser precisos, el carácter Unicode es U + 0445 (Letra minúscula cirílica Ha). Para no confundirme, llamé al archivo * .tXt. Como resultado, todos los archivos del juego tenían una resolución * .tXt y, por defecto, se abrieron en Notepad2.

assoc .tXt=tXt
ftype tXt=[directory]/.notepad/Notepad2.exe "%1"

El programa predeterminado solo se puede asignar como administrador. Si abre el juego con una cuenta diferente, se utilizarán los archivos txt. Si abre el archivo en el Bloc de notas normal, el juego le informará que necesita arrastrar el archivo a la ventana abierta del Bloc de notas. O ejecútelo como administrador para que se abra haciendo doble clic.

Motivación


De hecho, todo se hizo hace tres años. ¿Qué hice el resto del tiempo? Un ejemplo clásico de falta de motivación.

Inicialmente, la trama era un poco más larga que ahora. El dragón mató a tus padres, tienes que ir al herrero Ferdan, así que forjó una espada. Se pensó que la espada está hecha de tres materiales que deben recogerse. Esto aumentó enormemente el volumen del juego y empujó el final del desarrollo. El juego no fue muy entretenido, y abandoné el proyecto dos meses después.

Pero lo mantenía en mi cabeza todo el tiempo. Depuré un marco completo que me permitió crear un juego en el Bloc de notas, y el proyecto no se movió del suelo. Era necesario terminarlo. En 2019, no completé casi ningún proyecto. La decepción me llevó a decidir: terminar lo inacabado en 2020.

Y aquí está ella. Acorté la trama, me di un mes para todo (resultó una semana más) y corrí a la batalla. Todavía solicité un laberinto. Los premios, respectivamente, la fecha límite estaba programada para el 2 de febrero. Entonces hubo motivación.

Conclusión


Me alegro de haber completado el juego. Es sorprendente cuánto tiempo el proyecto acaba de acumular polvo digital, pero al final, un mes fue suficiente. El juego no debería haberse hecho tan voluminoso como quería al principio; un proyecto no estándar solo debería mostrar las características que se pueden implementar en él.

¿Que sigue? ¿Un juego de pintura? Juego en la calculadora? Es poco probable que los haga. Pero me gusta pensar en juegos que usan plataformas no tradicionales.

All Articles