Mostrando entradas con la etiqueta juegos. Mostrar todas las entradas
Mostrando entradas con la etiqueta juegos. Mostrar todas las entradas

lunes, 6 de julio de 2015

Untrusted: como perder una noche de sueño

Como a todo nerd, me pierden los desafíos de código. :)

Ya perturbé la salud mental de algunas amigas y amigos con Ruby Warrior, pero los amigos de JavaScript Jabber (un podcast sobre JS muy recomendable) me enviciaron con Untrusted, un juego para ir resolviendo pistas en JS, que además tiene un atractivo adicional y es que cada nivel tiene una musiquita sintética muy pegadiza.

Como suele ocurrir en estos casos, Untrusted tiene una pantalla dividida, en este caso con "el juego" a la izquierda, donde tenemos que buscar una salida, y el código a la derecha (el verdadero juego) donde tenemos que ir tocando el código en JS que arma ese nivel para poder superarlo. Lo divertido es que las soluciones son triviales en cuanto a código, pero exigen pensar como hackear la construcción del nivel para poder escaparse.

Pantalla de Untrusted

Si pierden muchas horas de sueño, ¡no digan que no les avisé!

lunes, 14 de noviembre de 2011

MonoGame: XNA multi-plataforma

Mono Game

Como todos saben, soy bastante fanático de Mono, la implementación de código libre y abierto de .NET, liderada por Miguel de Icaza.

El año pasado se inició un proyecto llamado inicialmente XNATouch, cuyo objetivo era permitir portar juegos desarrollados en XNA (la plataforma para desarrollo de juegos en XBOX y Windows Phone) a iOS (iPhone, iPod y iPad). El proyecto avanzó bastante, cambió de nombre a MonoGame, y el mes pasado llegó a su versión 2.0, extendiendo el alcance de iOS a Android, Linux, Windows y Mac OS, y de paso, mudándose a GitHub.

El proyecto es muy interesante porque brinda un mecanismo sólido de desarrollo de juegos para múltiples plataformas, algo que es cada vez más requerido por quienes producen para esta industria, sobre todo en los ambientes móviles.

Como ejemplo, este es el código de arranque para ejecutar un juego desarrollado en XNA en iOS (utilizando MonoTouch):

using MonoTouch.Foundation;
using MonoTouch.UIKit;

using Microsoft.Xna.Framework;

namespace MonoGame.Samples.Storage
{
	[Register ("AppDelegate")]
	class Program : UIApplicationDelegate
	{
		private Game1 game;

		public override void FinishedLaunching (UIApplication app)
		{
			// Fun begins..
			game = new Game1();
			game.Run();
		}

		static void Main (string [] args)
		{
			UIApplication.Main (args,null,"AppDelegate");
		}
	}
}

Por ahora el framework soporta solamente juegos 2D, y el API de 3D es el foco principal de trabajo para la versión 3.0. Si está disponible el stack de networking suficiente para permitir jugadores simultáneos dentro de un mismo segmento de red, sin importar la plataforma en que estén ejecutando el juego.

Para quienes quieran ver el proceso en más detalle, recomiendo esta serie de 4 artículos de Alfredo Machin, en las que describe el proceso de migración de un juego XNA a Mac OS (utilizando MonoMac): Parte 1, Parte 2, Parte 3, Parte 4.

 

lunes, 12 de septiembre de 2011

Exposición de Videojuegos 2011 en Buenos Aires, 11 y 12 de noviembre

InventEVA2011, la novena edición del evento organizado por la Asociación de Desarrolladores de Videjuegos Argentina se llevará a cabo el viernes 11 y sábado 12 de noviembre en el Centro Metropolitano de Diseño, en Algarrobo 1041, en Barracas.

El evento está aun en proceso de organización final, y hasta el 23 de septiembre está abierta la llamada a propuestas para disertantes en los siguientes formatos:

  • Charlas y Postmortems
  • Workshops (talleres participativos)
  • Debates on‐stage
  • Mesas redondas

Y ésta es la lista sugerida de áreas temáticas (aunque no son excluyentes):

  • Arte
  • Negocios y Producción
  • Game Design
  • Música y Sonido
  • Programación

Las propuestas se reciben en línea en esta página.

Según la definición de los organizadores:

EVA es un espacio de encuentro, diálogo y capacitación. Se trata del evento más importante de la industria local de videojuegos, que año tras año crece en público, invitados y sponsors.
Este año, el foco estará puesto en analizar cómo llegar a nuestro primer blockbuster hasta simplemente tener un valor diferencial ofreciendo servicios de calidad superior.

Por lo que los principales ejes temáticos serán:

  • Creando Valor.
  • Generando marcas de entretenimiento.
  • Agregando valor en los servicios de desarrollo.
  • Futuro de los videojuegos.

Habrá workshops, mesas redondas, conferencias, talleres, business rounds, charlas y expofloor. Se tratarán temas que, de una manera amplia, expandan los horizontes de las empresas nacionales a partir de la participación de importantes Keynote Speakers internacionales, aportando conocimiento y experiencia.

Por ahora el primer invitado especial confirmado es Jonathan Blow, creador del premiado juego Braid.

jueves, 17 de marzo de 2011

El juego de la vida en HTML5 usando Canvas

El juego de la vida de Conway (también conocido simplemente como "Life") es un ejemplo clásicos de Autómatas Celulares creado por John Horton Conway en los 70.

Consiste en una grilla de puntos (el universo) donde cada punto puede contener un individuo o célula (un punto de la grilla que está encendido o vivo (los puntos tienen estado binario: vivos o muertos).

El juego funciona sólo (se lo conoce como un juego de cero jugadores), y lo único que se puede hacer es preparar el estado inicial y luego echarlo a correr. La corrida involucra generaciones, o pasadas por la grilla completa para analizar el estado y calcular el de la siguiente pasada. El cálculo se hace analizando para cada punto su estado y el de los ocho que lo rodean (sus vecinos):

Celula y Vecinos

Para determinar si la célula analizada vive o muere, se aplican las siguientes reglas:

  • Una célula viva con menos de dos vecinos vivos se muere (de soledad)
  • Una con dos o tres vecinos vivos sobrevive
  • Una con más de tres vecinos vivos se muere (por sobrepoblación)
  • Una célula muerta con exactamente tres vecinos vivos, nace (por reproducción)

La grilla debe considerarse como un toroide, o sea que los puntos de la última fila continúan en la superior, y los de la última columna derecha continúan en la primera de la derecha y viceversa.

Parece sencillo, pero ha sido estudiado durante todos estos años no solamente como un interesante Code Kata, sino como un interesante ejercicio de simulación de ecosistemas.

Como no hay mejor manera de entenderlo que verlo en acción, les recomiendo ver esta versión implementada en HTML 5, utilizando el tag Canvas.

Cosas interesantes que ocurren con este juego es que espontáneamente aparecen configuraciones estables (que permanecen constantes durante muchas generaciones, variando entre un número determinado de estados. Es un interesante ejemplo de comportamiento emergente, y aunque parezca un oximorón, un ejemplo sencillo de sistemas complejos.

Juego de la Vida

Por supuesto, una de las cosas muy interesantes de ver es código fuente. Son sólo 234 líneas de JavaScript y aunque parece un poco largo para un post, creo que vale la pena pegarlo completo, aunque recomiendo que si alguien lo quiere tocar baje siempre la última versión disponible en GitHub.

 

/*
 * Copyright 2011 Julian Pulgarin 
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0

 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var Point = function(x, y) {
    this.x = x;
    this.y = y;
};

var graphics = function() {
    var canvas;
    var ctx;
    var canvasId;

    var cellSize = 10; // pixels
    var onColour = 'rgb(0, 200, 0)';
    var offColour = 'rgb(200, 0, 0)';
    var gridColour = 'rgb(50, 50, 50)';

    var initCanvas = function(canvasId) {
        this.canvas = $(canvasId).get(0);
        this.ctx = this.canvas.getContext('2d'); 
        this.canvasId = canvasId;
    }

    var drawCell = function(x, y, alive) {
        var g = graphics;
        g.ctx.fillStyle = (alive)? onColour : offColour;
        g.ctx.fillRect(x * cellSize + 1, y * cellSize + 1, cellSize - 1, cellSize - 1);
    }

    var handleMouse = function(e) {
        var l = life;
        var g = graphics;
        var that = this;
        var cell = getCellPointUnderMouse(e);
        var state;
        processCell(cell);
        $(g.canvasId).mousemove(function(e) {
            cell = getCellPointUnderMouse(e);
            processCell(cell);
        });
        function getCellPointUnderMouse(e) {
            return new Point((e.pageX - that.offsetLeft) / g.cellSize | 0, ((e.pageY - that.offsetTop) / g.cellSize) | 0);
        }
        function processCell(cell) {
            var x = cell.x;
            var y = cell.y;
            if (x > l.xCells - 1 || y > l.yCells - 1) {
                return;
            }
            if (typeof state == 'undefined')
            {
                state = !l.prev[x][y];
            } 
            l.prev[x][y] = state;
            drawCell(x, y, state);
            // TODO: Consider setting next as well
        }
    }

    function paint() {
        var g = graphics;
        var l = life;

        for (var x = 0; x < l.xCells; x++) {
            for (var y = 0; y < l.yCells; y++) {
                g.drawCell(x, y, l.prev[x][y]);
            }
        }
    }

    return {
        canvas: canvas,
        ctx: ctx,
        canvasId: canvasId,
        cellSize: cellSize,
        onColour: onColour,
        offColour: offColour,
        gridColour: gridColour,
        initCanvas: initCanvas,
        drawCell: drawCell,
        handleMouse: handleMouse,
        paint: paint,
    }
}(); 

var life = function() { 

    var yCells; 
    var xCells;
    var prev = []; // previous generation
    var next = []; // next generation

    var _timeout;
    var _alive = false;

    var initUniverse = function(canvasId) {
        var l = life;
        var g = graphics;
        g.initCanvas(canvasId);
        l.xCells = ((g.canvas.width - 1) / g.cellSize) | 0;
        l.yCells = ((g.canvas.height - 1) / g.cellSize) | 0; 
        g.ctx.fillStyle = g.offColour;
        g.ctx.fillRect(0, 0, l.xCells * g.cellSize, l.yCells * g.cellSize);
        g.ctx.fillStyle = g.gridColour;

        for (var x = 0; x < l.xCells; x++) {
            l.prev[x] = [];
            l.next[x] = [];
            g.ctx.fillRect(x * g.cellSize, 0, 1, l.yCells * g.cellSize);
            for(var y = 0; y < l.yCells; y++)
            {
                l.prev[x][y] = false;
            }
        }
        g.ctx.fillRect(l.xCells * g.cellSize, 0, 1, l.yCells * g.cellSize);
        for(var y = 0; y < l.yCells; y++)
        {
            g.ctx.fillRect(0, y * g.cellSize, l.xCells * g.cellSize, 1);
        }
        g.ctx.fillRect(0, l.yCells * g.cellSize, l.xCells * g.cellSize, 1);
        $(canvasId).mousedown(g.handleMouse);
        $('body').mouseup(function(e)
        {
            $(g.canvasId).unbind('mousemove');
        });
    }

    var nextGen = function() {
        var l = life;
        var g = graphics;

        for (var x = 0; x < l.xCells; x++) {
            for (var y = 0; y < l.yCells; y++) {
                l.next[x][y] = l.prev[x][y];
            }
        }

        for (var x = 0; x < l.xCells; x++) {
            for (var y = 0; y < l.yCells; y++) {
                count = _neighbourCount(x, y);

                // Game of Life rules
                if (prev[x][y]) {
                    if (count < 2 || count > 3) {
                        next[x][y] = false;
                    }
                } else if (count == 3) {
                    next[x][y] = true;
                } 
            }
        }

        for (var x = 0; x < l.xCells; x++) {
            for (var y = 0; y < l.yCells; y++) {
                l.prev[x][y] = l.next[x][y];
            }
        }

        g.paint();
    }

    var toggleLife = function() {
        var l = life;

        if (!l._alive) {
            l._alive = true;
            l._timeout = setInterval("life.nextGen()", 100);
        } else {
            l._alive = false;
            clearInterval(l._timeout);
        }
    }

    var clear = function() {
        var l = life;
        var g = graphics;

        for (var x = 0; x < l.xCells; x++) {
            for (var y = 0; y < l.yCells; y++) {
                l.prev[x][y] = false;
            }
        }
        g.paint();
    }

    var _neighbourCount = function(x, y) {
        var l = life;
        var count = 0;
        var neighbours = [
            l.prev[x][(y - 1 + l.yCells) % l.yCells],
            l.prev[(x + 1 + l.xCells) % l.xCells][(y - 1 + l.yCells) % l.yCells],
            l.prev[(x + 1 + l.xCells) % l.xCells][y],
            l.prev[(x + 1 + l.xCells) % l.xCells][(y + 1 + l.yCells) % l.yCells],
            l.prev[x][(y + 1 + l.yCells) % l.yCells],
            l.prev[(x - 1 + l.xCells) % l.xCells][(y + 1 + l.yCells) % l.yCells],
            l.prev[(x - 1 + l.xCells) % l.xCells][y],
            l.prev[(x - 1 + l.xCells) % l.xCells][(y - 1 + l.yCells) % l.yCells],
        ];

        for (var i = 0; i < neighbours.length; i++) {
            if (neighbours[i]) {
                count++;
            }
        }
             
        return count;
    }

    return {
        yCells: yCells,
        xCells: xCells,
        prev: prev,
        next: next,
        initUniverse: initUniverse,
        nextGen: nextGen,
        toggleLife: toggleLife,
        clear: clear,
    }
}();

miércoles, 2 de febrero de 2011

Leisure Suit Larry en HTML5

En la puerta de Lefty's

La aparición de nuevas librerías para gráficos y animación en HTML5 / JavaScript han posibilitado una avalancha de regresos a juegos clásicos que se adaptan bien a ser ejecutados en el browser.

Y aunque ya se pueden encarar estilos más ambiciosos, los clásicos siguen volviendo. Esta vez encontré una versión sumamente fiel de un clásico de los 80: Leisure Suit Larry. Para quienes lo jugaron en aquella época o m's tarde, ya que su popularidad lo mantuvo vivo bastante tiempo, he aquí una oportunidad de revivir esos momentos sin tener que recurrir a la virtualización.

Algo interesante de este juego y otros clásicos disponibles en el mismo sitio, es que están autorizados por las compañías que mantienen los derechos de los juegos originales, Activision y CodeMasters.

Pero además están construidos sobre un par de componentes  para juegos licenciado bajo GPL o MIT, y disponibles en Google Code:

  • El intérprete Sarien.Net, que es una implementación sobre el browser del motor original usado por Sierra On-Line para los juegos originales Space Quest, Police Quest y el de Larry.

  • El motor multiplayer Q42 es una plataforma multi-usuario sumamente liviana para juegos basados en el browser, siempre por puerto 80. El API cliente son 5K de JavaScript, y el servidor es una clase en C# que puede embeberse o referenciarse como una DLL externa (imagino que probablemente pueda correr en Mono, pero habría que probarlo).

    El motor implementa básicamente un mecanismo de chat con salas separadas, escalamiento automático, liberación de salas vacías, propiedades por usuario como nombre y posición (x,y), mensajería y soporte para múltiples usuarios por sesión, lo que permite mantener usuarios distintos en tabas separados del mismo browser, por ejemplo.

Espero que alguno se divierta con esto, jugando o programando.

miércoles, 5 de enero de 2011

Javascript ya no es lo que era

Mario Kart

Siguiendo con las pruebas de cuánto se puede llegar a hacer con Javascript (más características de HTML5 como Canvas), aquí hay otra implementación de un juego clásico: Mario Karts. Por ahora sólo se trata de la carrera, sin obstáculos especiales ni armas, pero no está mal para 11 Kb, sobre todo porque lo impresionante es la velocidad a la que se mueve la pista.

Pero no me crean a mi. Veanlo ustedes mismos, con algún navegador moderno que tenga buen soporte para HTML5 y un motor rápido de Javascript. Firefox 3.x en adelante, Chrome o Safari funcionan. No probé con Internet Explorer 9 Beta, pero debería funcionar bien (espero reportes).

Y como verán en la página, pueden ver detalles sobre la implementación, y si piensan que es un ejemplo aislado, vean todos los juegos que hay en esta colección hechos a puro Javascript.