Aunque aún no es el momento de añadir gráficos finales al proyecto, necesito sacar de esa demo el X-Wing. Considero que es un tipo de “trolleo” similar a aparecer vestido de Star Trek en un estreno de Star Wars.
Por eso me he entretenido en buscar una buena herramienta para crear pixel art. Hay quien usa Gimp para ello pero, después de pelearme con él y no conseguir eliminar por completo los difuminados, definitivamente no es para mí. Tras probar varias aplicaciones e incluso llegar a plantearme dibujar directamente en el MSX, me comprar Pixen. Es una herramienta sencilla con todo lo que necesito y a muy buen precio. Gracias a Pixen he descubierto que necesito desarrollar un poco (bastante) mis habilidades “pixelartísticas”.
Author: mramonlopez
Un año más tarde…
Al final las cosas nunca salen como uno planea. Después de un año sólo puedo decir que he aprendido mucho y que mis proyectos de juegos están en su mayoría parados. Por el camino ha caído un tercer premio en la Imagin Bank Challenge 2016 (Games Edition) y una vuelta a mis orígenes.
Me explico: Yo empecé en esto de la programación con unos 13 ó 14 años con un ordenador MSX de primera generación, concretamente con un Panasonic CF-2700. Por esos tiempos ya intentaba hacer mis primeros juegos y soñaba con tener un MSX2. Hace poco adquirí en una subasta de eBay un Philips NSM 8250 con una disquetera adicional (como un NMS 8255), que era uno de los modelos que más deseaba tener. Nunca llegué a publicar ningún juego, ni siquiera en la escena amateur que se creó casi al final de la vida de ese estándar de ordenadores de 8 bits.
MSX2 game concept test from Onikami on Vimeo.
He decidido quitarme esa espinita viendo que aun hoy en día se siguen publicando títulos para esas máquinas. Después de escribir en BASIC una prueba de concepto para comprobar si un MSX2 tiene la potencia necesaria, he decidido que mi primer juego para MSX2 (y espero que primero que publique para el estándar) sea un juego de combate espacial inspirado en la película The Last Starfighter. Espero que la próxima vez que escriba en este blog sea en breve para contar mis avances, y no dentro de un año.
Un nuevo comienzo
Llevo una semana de vacaciones y una de las cosas que quería hacer en ellas era construir un videojuego de principio a fin. El objetivo no es hacer un super hit comercial sino disfrutar del proceso de creación y de aprendizaje: Disfrutar del camino sin pensar en la meta. Para esta nueva locura mía he decidido retomar el nombre que usamos unos amigos y yo para lo que queríamos que fuera un estudio independiente de videojuegos. Me gusta el nombre y ya estaba pagando por el dominio, así que…
La idea es hacer un juego de plataformas cinemático. Pretendo encargarme del diseño del juego, los gráficos, la música y los sonidos y, por supuesto, de la programación. Puede que salga una cosa cutre, o puede que no. La semana pasada estuve haciendo diversas pruebas y haciéndome a las herramientas. He elegido el lenguaje C++ y el framework Cocos2d-x pues me permitirá publicar el juego en diversas plataformas si así lo decido, con un rendimiento nativo en cada una de ellas. De paso mejoro mis habilidades con dicho lenguaje, que falta me hace.
Intentaré que este blog sea un auténtico cuaderno de bitácoras de este viaje que comienza aquí y ahora.
Nuevo hosting
Adiós GoDaddy,
Han sido muchos años de relación, pero ya no soporto tu lentitud, que hayas subido los precios sin mejorar el servicio, esa migración nunca acabada y que ahora solo quieras comunicarte conmigo por teléfono.
Adiós.
Modificando un pequeño amplificador Kustom Tube 12A
Buscando por internet he encontrado en varios foros de habla inglesa que la gente se dedica a modificar este pequeño amplificador híbrido: el Kustom Tube 12A. De todo lo que he leído, la modificación más sencilla es realizar un par de simples cortes para eliminar del circuito de ganancia 4 diodos encargados de hacer distorsión mediante “clipping“. No me he podido resistir y me he atrevido a hacerlo. La mejora de sonido al activar la sección de ganancia es notoria. Lo mismo me animo a añadirle un conmutador y un led para poder activar o desactivar esta modificación.
Fabricando una imitación de una guitarra Parker
Hace 4 años (creo) construí un guitarra con la forma de una Parker, reutilizando un mástil de una guitarra que antes estuvo montada en una imitación de Explorer que también construí y que provenía de una guitarra destrozada abandonada en un piso de estudiantes.Cometí algunos errores pero al menos la terminé y sonaba.
He encontrado en la librería de mi blog estas viejas fotos y he decido recuperarlas para “bien de la humanidad”
Montar un servidor REST (II)
Hace unos meses empecé a explicar cómo montar un servidor REST sencillo para servir datos a una aplicación móvil (o de cualquier otro tipo) de forma rápida y reusable. En esa segunda parte voy por fin a completar la explicación entrando en la parte más interesante del código. Aprovecho para aclarar algo que se me pasó en la anterior entrega: parte de este código y las ideas de cómo estructurar todo el servicio en si provienen de este estupendo artículo.
Nos quedamos por ver la clase que debía procesar la petición. En nuestro proyecto, para cada recurso que necesitemos manejar deberemos crear una clase RecursoController, derivada de la clase RESTcontroller . También será necesario crear una clase RecursoModel, hija de RESTmodel, que se encargará de la interacción con la base de datos. Pero lo veremos después.
La clase RESTcontroller comienza con las definición de unas pocas propiedades y cuatro métodos abstractos que deberemos implementar para cada entidad.
abstract class RESTcontroller { protected $classname; protected $data; public $ID; abstract function post(&$error); abstract function put(&$error); abstract function get(&$error); abstract function delete(&$error); . . . }
La propiedad $data se utiliza para almacenar los datos recibidos en la petición. $classname se utiliza para que la clase hija indique a RESTController qué tipo de entidad maneja. Se utiliza para crear el objeto JSON o XML adecuadamente. La propiedad pública $ID sirve para identificar el recurso que se está tratando. Cuando se trabaja con una colección de recursos esta propiedad vale NULL.
Los métodos abstratos son los que debemos completar en cada clase derivada para responder a cada unas de las 4 operaciones REST básicas. Deben devolver un array de pares clave-valor (un diccionario hablando con mayor propiedad), cuyas claves son los nombres de las propiedades y los valores los obtenidos en la operación. Si el resultado de la operación es un conjunto de entidades (lo cual ocurre generalmente en las operaciones get en las que no especificamos un ID), estos métodos deben devolver un array de diccionarios como los que he descrito. Si queremos notificar algún error, lo haremos a través del parámetro por referencia $error. Por ahora esto puede resultar demasiado abstracto, pero espero que quede más claro al final de esta entrada.
Como vimos en la anterior entrada, index.php, entrega el control al método processRequest de la clase recursoController:
public function processRequest($request_method, $content_type, $data) { // Copiamos los parámetros recibidos // para usarlos en la case derivada. $this->data = $data; // Obtenemos el nombre del recurso // a partir de la propiedad o del // nombre de la clase $classname = isset($this->classname)?$this->classname:get_class($this); // En función del tipo de petición // llamamos a un método u otro // (definidos en la clase derivada) switch($request_method) { case 'get': $error = ""; $result = $this->get($error); . . . break; case 'post': case 'put': $error = ""; if ($request_method == 'post') { $result = $this->post($error); } else { $result = $this->put($error); } . . . break; case 'delete': if($this->delete($error)) { . . . } break; default: RESTcontroller::sendError(500, "Unkwon request method", $content_type); } }
El código de arriba está explicado básicamente en sus comentarios. Simplemente en función del método solicitado por el cliente se llama a un método u otro (que debemos implementar en la case derivada). Si os fijáis, utilizo el mismo caso del switch para los métodos put y post, porque como vamos a ver el procesamiento de la respuesta es el mismo. En caso de que el método pedido no sea reconocido, se devuelve un error mediante un método de clase que no tiene mucho misterio, y el poco que tiene será desvelado cuando terminemos con este.
Las clases derivadas de RESTController nos van a devolver los resultados como diccionarios (pares clave-valor) si se tratan de un solo recurso o bien un array de estos diccionarios si es un conjunto de resultados. En el caso de que haya habido un error, devuelven el valor null. Para devolver esos datos al cliente REST, debemos darles el formato adecuado, bien sea XML o JSON. La conversión de un diccionario en JSON es trivial en PHP usando json_encode($array). Para hacer la conversión a XML necesitamos apoyarnos en una pequeña función recursiva como se muestra en esta respuesta de stackoverflow. Así es como quedaría el tratamiento de las peticiones post y put, en las que si todo es correcto devolvemos al cliente el único registro insertado o modificado, respectivamente.
$error = ""; if ($request_method == 'post') { $result = $this->post($error); } else { $result = $this->put($error); } if($result) { if ($content_type == 'json') { RESTcontroller::sendResponse(200, json_encode($result), 'application/json'); } else { $xml = new SimpleXMLElement("<?xml version="1.0"?><$classname/>"); RESTcontroller::array_to_xml($result, $xml); RESTcontroller::sendResponse(200, $xml->asXML(), 'application/xml'); } } else { RESTcontroller::sendError(500, $error, $content_type); }
Y este sería el código de nuestro método protegido para convertir un array en un documento XML:
protected static function array_to_xml($data, &$xml) { foreach($data as $key => $value) { if(is_array($value)) { if(!is_numeric($key)){ $subnode = $xml->addChild("$key"); RESTcontroller::array_to_xml($value, $subnode); } else{ RESTcontroller::array_to_xml($value, $xml); } } else { $xml->addChild("$key","$value"); } } }
El tratamiento que hacemos de la respuesta de la clase derivada de RESTController es ligeramente diferente, pues tenemos que devolver correctamente tanto registros individuales como conjunto de registros. La forma que tenemos de diferenciar ambos casos es preguntando si está definida una clave llamada ID en el diccionario. Si existe (en el primer ‘nivel’ de profundidad) estamos tratando un registro individual. En caso contrario se trata de un conjunto de ellos.
case 'get': $error = ""; $result = $this->get($error); if ($result) { if ($content_type == 'json') { // Conjunto de conjunto de resultados if (!array_key_exists("ID", $result)) { $result = array("ArrayOf$classname" => $result); } RESTcontroller::sendResponse(200, json_encode($result), 'application/json'); } else { // Consulta de un sólo elemento if (array_key_exists("ID", $result)) { $xml = new SimpleXMLElement("<!--?xml version="1.0"?--><$classname/>"); RESTcontroller::array_to_xml($result, $xml); } else { // Conjunto de conjunto de resultados $xml = new SimpleXMLElement("<!--?xml version="1.0"?--><ArrayOf$classname/>"); foreach($result as $object) { $subnode = $xml->addChild("$classname"); RESTcontroller::array_to_xml($object, $subnode); } } RESTcontroller::sendResponse(200, $xml->asXML(), 'application/xml'); } } else { RESTcontroller::sendError(500, $error, $content_type); } break;
En realidad, podríamos tratar peticiones get, post y put en un solo caso del switch, pero por claridad los vamos a mantener separado por ahora.
Por último, en las peticiones de tipo delete se devolverá un mensaje de OK si se ha completado la operación con éxito, y el error en caso contrario.
if($this->delete($error)) { if ($content_type == 'json') { RESTcontroller::sendResponse(200, json_encode(array("message" => "OK")), 'application/json'); } else { RESTcontroller::sendResponse(200, "<?xml version="1.0"?><message>OK</message>", 'application/xml'); } RESTcontroller::sendResponse(200); } else { RESTcontroller::sendError(500, $error, $content_type); }
El código de los métodos de clase sendResponse y getStatusCodeMessage es exactamente el mismo que el del artículo que mencioné arriba.
¿CÓMO SE UTILIZA ESTA CLASE ABSTRACTA?
Cuando diseñamos un servicio REST, normalmente servirá para proporcionar servicios para la manipulación de datos persistentes, almacenados en bases de datos SQL o no SQL. En ambos casos tendremos recursos que se corresponderán con tablas o colecciones. En cualquier caso, tendremos que proporcionar un recurso con un nombre determinado. Si queremos proporcionar servicios sobre un recurso llamado User, lo primero que debemos hacer es crear un fichero user.php que tendrá el siguiente aspecto inicial:
<?PHP require_once('RESTcontroller.php'); class UserController extends RESTcontroller { function UserController() { $this->classname="User"; } function get(&$error) { $error = ''; $response = '': return $response; } function post(&$error) { $error = ''; $response = '': return $response; } function put(&$error) { $error = ''; $response = '': return $response; } function delete(&$error) { $error = ''; $response = '': return $response; } } ?>
En este fichero definimos una clase UserController heredada de la clase RESTController. Partiendo de esa plantilla, tendremos que escribir nuestro código en función de la lógica de nuestra aplicación y del motor de datos que tengamos debajo, en parte encapsulado por una clase RESTModel. Para continuar con esta entrada, vamos a suponer que trabajamos con una base de datos MySQL. De esta forma también podremos presentar la clase RESTModel. El código competo de nuestra clase UserController quedaría así:
<?PHP require_once('RESTcontroller.php'); require_once('RESTmodel.php'); class UserModel extends RESTmodel { function UserModel() { $this->tablename = "Users"; $this->properties["ID"] = 0; $this->properties["nick_name"] = ""; $this->properties["password"] = ""; } } class UserController extends RESTcontroller { function UserController() { $this->classname="User"; } function get(&$error) { $model = new UserModel(); $error = ''; // Leer los datos de usarios, conocido su ID if (isset($this->ID)) { $model->properties["ID"] = $this->ID; $response = $model->select(); if (!$response) { $error = "Not found User with ID ".$this->ID; } // Login } else if (isset($this->data["nick_name"]) && isset($this->data["password"])) { $nick_name = $this->data["nick_name"]; $password = $this->data["password"]; $response = $model->select("password='$password' AND nick_name='$nick_name'"); if (!$response) { $error = "Authentication failed"; } // Usuarios con nick similar a un nombre dado } else if (isset($this->data["name"]) ) { unset($model->properties["password"]); $name = $this->data["name"]; $response = $model->selectAll("`nick_name` LIKE '%$name%'"); if (!$response) { $error = "No users found"; } // Petición no reconocida } else { $response = false; $error = "No parameters"; } return $response; } function post(&$error) { // Validamos los datos if (isset($this->data["password"]) && $this->data["password"] != "" && isset($this->data["nick_name"]) && $this->data["nick_name"] != "") { $model = new UserModel(); // Comprobamos que no exista ya un usuario con ese nick $response = $model->selectAll("`nick_name`='".$this->data["nick_name"]."'"); if ($response && count($response)>0) { $error = "User nick already exits"; return false; } // Copiamos los datos en las propiedades del modelo $model->properties["nick_name"] = $this->data["nick_name"]; $model->properties["password"] = $this->data["password"]; $response = $model->insert(); if ($response) { $error = ""; } else { $response = false; $error = "Database Insert Error"; } } else { $response = false; $error = "Imcomplete User data"; } return $response; } function put(&$error) { $model = new UserModel(); // Comprobamos si existe el usuario // y de paso precargamos los datos existentes $model->properties["ID"] = $this->ID; $found = $model->select(); if ($found) { // Cmabiamos los datos que hayamos recibido if(isset($this->data["password"]) && $this->data["password"] != "") { $model->properties["password"] = $this->data["password"]; } $response = $model->update(); if (!$response) { $error = "Database Update Error"; } } else { $response = false; $error = "Not found resource with ID ".$this->ID; } return $response; } function delete(&$error) { $model = new UserModel(); $model->properties["ID"] = $this->ID; $result = $model->delete(); if ($result) { $error = ""; } else { $error = "Error deleting User ".$this->ID; } return $result; } }
En cada método he añadido los comentarios necesarios para entender el código. En el método get, en función de los parámetros recibidos se realiza una consulta u otra al modelo. En el método post, realizamos una validación de los datos obligatorios para hacer la inserción. En el método put, primero comprobamos que exista el registro, cargando de paso sus datos en las propiedades del modelo, y luego modificamos los datos que hayamos recibido en la petición, manteniendo el resto intacto. El método delete tiene poco que comentar.
Al principio del fichero hemos definido una clase derivada de la clase RESTModel. En ella definimos una propiedad llamada properties, que contiene un diccionario con los nombres de los campos de nuestra base de datos. Definimos además otra propiedad tablename que indica el nombre de la tabla que vamos a manipular. Mediante estas propiedades podemos crear de forma automatizada las consultas SELECT, INSERT, UPDATE y DELETE en la clase RESTModel. Esta clase además expone una serie de métodos que permite hacer estas operaciones básicas:
<?php abstract class RESTmodel { public $properties = array(); protected $tablename; function update() { } function insert() { } function select($where = "") { } function selectAll($where = "") { } function delete() { } } ?>
El funcionamiento de la clase RESTModel da para un futuro artículo. Antes de finalizar no hay que olvidar incluir el fichero user.php en index.html.
require_once('user.php');
Que aproveche.
Wanderer
Hace un tiempo hice el experimento de crear un juego en un rato. Estuve publicando mis progresos en twitter, pero de ahí se lo lleva el viento. Como quiero probar Calabash como framework para hacer TDD, he pensado que podría utilizar ese mini juego para ello. Aprovecho la ocasión para liberar con licencia MIT el código que resultó del experimento y anunciarlo aquí, junto con el vídeo que grabé para demostrar su funcionamiento.
Otra vez no…
La semana pasada empezó con el disco duro del portátil roto. Mi MacBook seguía funcionando pero de una forma muy degradada. Por suerte tengo un NAS donde TimeMachine hace las copias de seguridad. Salí corriendo a encargar en una tienda local un SSD de 128GB, pero como hasta hoy no me lo traían, opté por reciclar un disco de un NetBook caído en desgracia.
¿Y que tiene de interesante esta entrada? Pues cómo pude resolver algún problema y cómo no puede resolver otros. El primero con el que me encontré fue que la copia más actualizada de Mac OS X que tengo en CD es de Snow Leopard. No pasa nada, pensé, instalo y luego me paso a Maverick. Tras unas 16 a 18 horas de restauración del equipo desde Time Machine me encontré con el primer gran PROBLEMA: Las claves de los usuarios restaurados no eran reconocidas. Por lo visto, la forma en la que se gestionan la seguridad (claves, permisos, …) cambió a partir de Lion. La SOLUCIÓN fue comenzar de nuevo la instalación y crear un usuario temporal desde el que actualizar a Maverick, pasando de restaurar mis datos de usuario. Total, se trataba de un parche temporal.
En la actualización surgió el segundo PROBLEMA: La descarga de Maverick se quedó congelada, y la aplicación de la AppStore no paraba de fallar con botones “Descargar” que descargaban, parpadeos epilépticos, etc. Tuve la mala suerte de coincidir con el lanzamiento de la actualización 10.9.2 de Maverick. Después de probar varias soluciones, me tuve que crear un nuevo usuario con permisos de administrador, usar un App ID distinto para ese usuario y entonces sí descargar la actualización de Maverick. Otra cosa que hice fue pasar de la wifi y conectar directamente al router con un cable ethernet. Como la seda.
Como sabía que tenía que pasar de nuevo por todo el proceso de instalación esta semana con el disco SSD, tuve la precaución de crearme un USB “bootable” desde el que se puede instalar Maverick. De hecho así fue como finalmente hice la instalación la semana pasada.
En fin, espero que a alguien le sirva este post para no caer en los mismos problemas.
Calentador de velas
Últimamente se han visto muchos vídeos sobre la construcción de calentadores construidos con macetas de barro cuya energía proviene de unas pocas “velas de té”. Me pareció muy curioso y decidí construir uno con un diseño más o menos refinado. Yo opté por la variante que genera una corriente de convección. He aquí los pasos que seguí para construirlo.
ADVERTENCIA: Este invento en realidad NO FUNCIONA. Se necesitan unas 30 velas para que se pueda calentar una habitación.
Primer paso: Los materiales.
Hacen falta dos macetas de barro (dos tiestos). Una debe entrar dentro de la otra, dejando aproximadamente un centímetro de separación entre ellas. Yo como base he usado un plato de madera de los de servir pulpo, de un diámetro ligeramente superior al del tiesto mayor, para que sea estable. Para el soporte central hacen falta una barra roscada, tuercas y arandelas. Para dejar en su sitio la maceta mayor he usado unas alcayas (escarpias) bastante grandes. Herramientas al uso.
Segundo paso: Preparar el soporte.
Hacemos un agujero en el centro con una broca pequeña para que nos sirva de guía. Luego, con una broca de pala de un diámetro mayor que el de las arandelas que estemos usando hacemos un rebaje para que la arandela y la tuerca no sobresalgan más del reborde del plato. En mi caso el rebaje llega hasta la mitad del grosor de la madera.
Lo siguiente es colocar el tiesto pequeño en su sitio fijándolo provisionalmente con la barra roscada. Marcamos los tres puntos don irán las alcayatas.
Con la broca pequeña hacemos unos agujeros ciegos (no pasantes) que nos sirvan de guía para colocar las alcayatas sin rajar la madera. Solo nos queda colocarlas, montar el tiesto pequeño con la barra roscada y las tuercas, y medir dónde debemos cortar la barra.
Tercer paso: Montaje completo.
Una vez cortada. Ya podemos montar completamente el calentador. Para no astillar o rajar el tiesto pequeño, yo decidí usar unas arandelas de goma recortadas de una pieza que compré hace tiempo para el montaje de la campana de “casi vacío”. Fue una mala idea, porque el tiesto se pone realmente caliente y el olor a goma quemada es importante. Ahora no las puedo retirar, porque se han fundido con la arandela y el tiesto. El centro lo recorté con un sacabocados.
Nos tenemos que asegurar de que quede espacio suficiente ente los tiestos para que el aire circule, ascienda, y salga por el orificio superior del tiesto mayor. En otros diseños tapan ese orificio también. En ese caso sería un calentador más de tipo radiante, pero entonces no le veo sentido a usar dos tiestos (si alguien sabe el porqué…).
Paso final: La prueba.
Por último colocamos la velitas de té y las encendemos. Los tiestos se ponen realmente calientes, pero no lo suficiente como para calentar una habitación. Hay que tener cuidado porque la base de madera también se calienta muchísimo. Habría que mejorar el diseño colocando un platillo metálico suspendido sobre la madera, sostenido por dos tuercas en la barra roscada.
Fue una tarde divertida, aunque el resultado fuese algo decepcionante (Resultados que me lo esperaba por los cálculos de potencia que un amigo había hecho previamente).