Trucos y Consejos
Rutas relativas
DIV1 y DIV2 tienen una curiosa forma de trabajar con las rutas; por defecto mientras estés en el entorno de DIV2 todos los archivos se cargan con respecto a la carpeta de DIV2, y la forma de resolver las rutas es la siguiente (muchas gracias a @vii1 por la información):
- ruta\fichero.ext
- ext\ruta\fichero.ext
- fichero.ext
- ext\fichero.ext
Así que para poder compartir los juegos con comodidad y que todos podamos ejecutarlos a la primera, sin tener que andar reemplazando rutas, lo suyo sería utilizar el siguiente sistema:
C:\DIV2\USUARIO\JUEGO
De esta forma si hago un juego llamado JANDER y mi usuario es AZAZEL28, la carpeta de mi juego sería:
C:\DIV2\AZAZEL28\JANDER
NOTA: Las carpetas deberían ceñirse siempre al sistema de archivos de MS-DOS, es decir, máximo 8 carácteres para los nombres y máximo 3 carácteres para las extensiones. De esta forma no tenemos que pegarnos con nombres raros.
Y a la hora de cargar los archivos en mi .PRG el código quedaría de la siguiente manera:
load_fpg("azazel28\jander\graficos.fpg");
load_fnt("azazel28\jander\fuente.fnt");
load("azazel28\jander\ranking.dat");
Si no queréis tener que teclear siempre lo mismo podéis copiar el siguiente codigo y así sólo tendréis que cambiar las rutas una vez:
program paths_test;
const
PATH_USER="AZAZEL28";
PATH_PROG="TEST";
begin
load_fnt(path_resolve("MIFUENTE.FNT"));
end
function path_resolve(file)
begin
return (PATH_USER+"\"+PATH_PROG+"\"+file);
end
strncpy
Algo muy habitual al trabajar con cadenas en DIV es que ocurran errores
de acceso fuera de rango (error #140). Para poder copiar cadenas con
cadenas y garantizar que se respeta el ancho máximo lo mejor sería utilizar
strncpy
pero DIV no provee esta función, así que aquí tenéis una
implementación sencillota:
function strncpy(string pointer dest, string src, max_length)
private
length, src_length, index;
begin
src_length = strlen(src);
if (src_length > max_length)
length = max_length;
else
length = src_length;
end
for (index = 0; index < max_length; index++)
dest[index] = src[index];
end
return (length);
end
Dividir y multiplicar por 2
Esto son métodos clásicos para multiplicar y dividir por dos utilizando enteros (además no le afectan las divisiones entre 0).
width >> 1; // División entre 2
width << 1; // Multiplicación por 2
width >> 2; // División entre 4
width << 2; // Multiplicación por 4
width >> x;
Interpolación
Estas son algunas sencillas funciones de interpolación para poder hacer transiciones y animaciones.
// p es un valor que va de 0 a 100 (0% y 100% de interpolación)
// a es el valor inicial
// b es el valor final
function linear(p, a, b)
begin
return ((((100 - p) * a) + (p * b)) / 100);
end
// p es un valor que va de 0 a 100 (0% y 100% de interpolación)
// a es el valor inicial
// b es el valor de control
// c es el valor final
function quadratic(p, a, b, c)
private
d, e;
begin
d = linear(p, a, b);
e = linear(p, b, c);
return (linear(p, d, e));
end
// p es un valor que va de 0 a 100 (0% y 100% de interpolación)
// a es el valor inicial
// b es el valor de control 1
// c es el valor de control 2
// d es el valor final
function cubic(p, a, b, c, d)
private
e, f;
begin
e = quadratic(p, a, b, c);
f = quadratic(p, b, c, d);
return (linear(p, e, f));
end
Por ejemplo, si queremos hacer una pequeña animación con rebote de un texto podemos utilizar estas funciones de la siguiente manera:
program interpolation_test;
private
text_id;
begin
text_id = write(0, 0, 0, 0, "Hola, Mundo!");
loop
move_text(text_id, 0, quadratic(clamp(timer, 0, 100), 0, 120, 100));
frame;
end
end
Rotar la paleta
DIV 2 salió a la venta con un error que hacía que no se rotase la paleta:
program roll_pal_fix_prg;
global
// Es MUY importante definir este valor
// como global.
roll_pal_fix = 0;
begin
end
function roll_pal(start, count, inc)
begin
// Esta función pone activa_paleta=1 pero como
// la velocidad es 1 y la variación es muy poca
// no le da tiempo a cambiar la paleta.
fade(99 + roll_pal_fix, 100, 100, 1);
// Vamos rotando el valor de destino porque
// fade comprueba si el valor de destino es igual
// al actual y si es así, no llama a activa_paleta=1
roll_pall_fix = (roll_pall_fix + 1) % 3;
// Llamamos al roll_palette estropeado.
roll_palette(start, count, inc);
end
Funciones útiles
Estas son funciones útiles que no tengo muy claro en qué categoría poner:
Clamping
// Realiza el clamping de un valor
function clamp(value, min, max)
begin
if (value < min) return (min); end
if (value > max) return (max); end
return (value);
end
Scroll con tiles
program tiled_scroll_test;
private
tiles[31] =
1, 1, 1, 1, 1, 1, 1, 1,
1, 2, 2, 2, 1, 2, 2, 1,
1, 1, 2, 1, 1, 1, 2, 1,
1, 2, 2, 2, 2, 2, 2, 1,
1, 1, 1, 1, 1, 1, 2, 1,
1, 2, 2, 2, 2, 1, 2, 1,
1, 1, 1, 2, 1, 1, 2, 1,
1, 2, 2, 2, 2, 2, 2, 1,
1, 1, 1, 1, 1, 1, 1, 1;
tiled_map;
begin
c_type = c_scroll;
scroll.camera = id;
tiled_map = draw_tiles(tiles, 8, 8, 32, 32);
start_scroll(0, 0, tiled_map, 0, 0, 3);
loop
// si se quisiesen actualizar algunos de los tiles del mapa, se podría usar
// map_put o alguna otra función para actualizar los gráficos del scroll
// y entonces llamar a refresh_scroll(0) para actualizar el mapa.
frame;
end
end
function draw_tiles(pointer tiles_ptr, map_width, map_height, tile_width, tile_height)
private
buffer_width, buffer_height, buffer, tile_index;
begin
buffer_width = tile_width * map_width;
buffer_height = tile_height * map_height;
buffer = new_map(buffer_width, buffer_height, buffer_width >> 1, buffer_height >> 1, 0);
for (y = 0; y < map_height; y++)
for (x = 0; x < map_width; x++)
tile_index = width * y + x;
map_put(0, buffer, tiles_ptr[tile_index], x * tile_width, y * tile_height);
end
end
return (buffer);
end
Configuración de las teclas
Una manera sencilla para permitir que los usuarios configuren sus teclas es utilizar el siguiente código:
program control_test;
const
CONTROLS_FILE="controls.cfg";
global
struct controls
byte move_left, move_right, move_forward, move_backward, fire, use;
end = _left, _right, _up, _down, _control, _space;
begin
controls_init();
loop
if (key(controls.move_left))
// movemos a la izquierda
end
if (key(controls.move_right))
// movemos a la derecha
end
if (key(controls.move_forward))
// movemos a la arriba
end
if (key(controls.move_backward))
// movemos a la abajo
end
frame;
end
end
function controls_save()
begin
save(path_resolve(CONTROLS_FILE), offset controls, sizeof(controls));
end
function controls_load()
begin
load(path_resolve(CONTROLS_FILE), offset controls);
end
function controls_init()
begin
// Si no existe el archivo guardamos
// la configuración por defecto.
if (!get_fileinfo(path_resolve(CONTROLS_FILE))
controls_save();
end
// Cargamos la configuración de los controles.
controls_load();
end