Blog de programación e ideas locas

Desarrollo

Desarrollo: Programando con Lua II

by on Ene.10, 2012, under Desarrollo, EGa2Dengine, Engine

Hoy, aprovechando el rato libre que me quedaba decidí seguir programando con Lua. No es mucho lo que hice, es más estoy reprogramando una implementación que hice hace tiempo, le estoy aumentando el acoplamiento para ver si puedo evitar una clase Proxy (aunque con pocas esperanzas).

Hace tiempo atrás me topé con una maravillosa biblioteca se llama luabind que permite por arte de magia integrar Lua a C++, pero no pude integrarla ya que mi tiempo y mis ganas de aprender como funcionaba eran pocas. Por lo que lei necesitaba compilarse de una manera especial con bjam y Boost Build V2, asi que preferí hacer mi propia implementación pero no desde 0, si no modifiqué un ejemplo encontrado en la wiki de Lua Cpp Binding With Lunar. Con excelentes resultados en esa ocasión.

Ahora le agregué lo que hice en el articulo pasado y obtuve esto.

 print(entero);
 print(string);
 test = Test();
 test.a = 1;
 test.b = 2;
 test.c = 2.0;
 test.hola = "Hola"
 test.a = test:loles(test.b)
 funcion(test);
 

Codigo en lua a ejecutar

int funcion(lua_State *L){
    int n = lua_gettop(L);    /* number of arguments */
    printf("Numeros de argumentos %d\n",n);
    //printf("%s\n",luaL_typename(L,1));
    LuaType v1(L,1);
    Test t = (Test)v1;
    printf("Test.a = %d\n",t.a);
    printf("Test.b = %d\n",t.b);
    printf("Test.c = %f\n",t.c);
    printf("Test.hola = %s\n",t.hola);
    lua_settop(L,0); //borramos todo
    return 0;
};

Codigo en C++ de la funcion pasada a Lua

Resulado_lua

Y el nada glorioso resultado

El resto del codigo, no lo publicaré para no hacer más latoso este tema LOL (aunque si alguien desea, puedo publicar las modificaciones que le hice a Lunar)

Saludos!

Leave a Comment :, , , , , , , more...

Desarrollo: Programando con Lua

by on Ene.08, 2012, under Desarrollo, EGa2Dengine, Engine

Hoy dia, evitando mis quehaceres como siempre.
Me dedique a programar un ratito con Lua, no hice la gran cosa pero vamos bien encaminado.

template
class LuaType {
protected:
private:
    T valor; //!< Contiene el valor que estamos Wrappeando
public:
    /**
      * \brief Crea una nueva variable lista, para ser pasada a Lua
      */
    LuaType(const T t):valor(t) {};
    /**
      * \brief Extrae un valor en la pila, pero sin borrarlo
      */
    LuaType(lua_State *L,int i = -1);

    /**
      * \brief Pone este valor en la sima de la pila de Lua
      *
      * \param L estado actual de Lua
      */
    void pushValue(lua_State *L);

    /**
      * \brief Esta funcion permite recuperar el tipo original de la variable
      *
      * Ejemplo:

      * <code>
 * LuaType entero(5);
 * int variable_normal = (int)entero;
 * </code>
      * <code>
 * LuaType cadena("Hola Lua");
 * char *string = (char *)cadena;
 * </code>
      * \return el valor de la variable del tipo de este template
      */
    operator T() const {
        return valor;
    }

};

Primero lo que fue es crear una plantilla de una clase Wrapper, la cual me permitira extraer y colocar variables entre Lua y C++.

Usando especialización de templates (plantillas) podemos especificar como agregaremos cada tipo de variable en lua.

template<> LuaType::LuaType(lua_State *L,int i) {
    if(!lua_isnumber(L,i)) {
        luaL_error(L,"Argumento incorrecto");
    }
    valor  = lua_tointeger(L,i);
};

template<> LuaType::LuaType(lua_State *L,int i) {
    if(!lua_isnumber(L,i)) {
        luaL_error(L,"Argumento incorrecto");
    }
    valor  = static_cast(lua_tonumber(L,i)); // -1 top stack
};

template<> LuaType::LuaType(lua_State *L,int i) {
    if(!lua_isnumber(L,i)) {
        luaL_error(L,"Argumento incorrecto");
    }
    valor  = lua_tonumber(L,i); // -1 top stack
};

template<> LuaType::LuaType(lua_State *L,int i) {
    if(!lua_isstring(L,i)) {
        luaL_error(L,"Argumento incorrecto");
    }
    valor = const_cast(lua_tostring(L,i)); // -1 top stack
};

template<> LuaType::LuaType(lua_State *L,int i) {
    if(!lua_isuserdata(L,i)) {
        luaL_error(L,"Argumento incorrecto");
    }
    valor  = lua_touserdata(L,i);
};

Conviertiendo valores de la Pila de Lua a variables en C++

template<> void LuaType::pushValue(lua_State *L) {
    lua_pushinteger(L,valor);
}
template<> void LuaType::pushValue(lua_State *L) {
    lua_pushnumber(L,valor);
}
template<> void LuaType::pushValue(lua_State *L) {
    lua_pushnumber(L,valor);
}

template<> void LuaType::pushValue(lua_State *L) {
    lua_pushstring(L,valor);
}

template<> void LuaType::pushValue(lua_State *L) {
    lua_pushlightuserdata(L,valor);
}

Pasando las variables a Lua.

Parece que todo este código es muy engorroso, pero sin embargo nos facilitará más adelante para programar una clase proxy (la que nos facilitará el uso de estructuras o clases definidas en C++ en Lua)

Ejemplo:

#include <cstdio>
#include <cstdlib>
#include <EGa2D/Lua/Type.h>
#include <EGa2D/Lua/Member.h>

using namespace EGa2D;
using namespace Lua;

char *script = "print(string,entero)\nfuncion(entero,string)";

int funcion(lua_State *L){
    int n = lua_gettop(L);    /* number of arguments */
    LuaType<int> v1(L,1);
    LuaType<char *> v2(L,2);
    lua_settop(L,0); //borramos todo
    printf("%d %s",(int)v1,(char *)v2);
    return 0;
};

int main(int argc,char **argv){
    lua_State *L;
    L = lua_open();//abrimos lua
    luaL_openlibs(L); //con todas sus bibliotecas

    LuaType<int> entero = 5;
    entero.pushValue(L);
    lua_setglobal(L, "entero");

    LuaType<char*> string = "Holaaa";
    string.pushValue(L);
    lua_setglobal(L, "string");

    lua_pushcfunction(L,funcion);
    lua_setglobal(L, "funcion");

    if(luaL_dostring(L,script)){
        printf("Error %s\n",lua_tostring(L, -1));
    }

    if(L) {
        lua_gc(L, LUA_GCCOLLECT, 0);  // collected garbage
        lua_close(L);
    }

    return 0;
}

La idea de usar un Wrapper de los tipos de variables en C++ no es idea mía pero, lo vi programado en codigo que hacia lo mismo pero para una versión vieja de Lua. Así que quise probarlo y hasta el momento me ha traído algunos beneficios al programar (el código se vuelve más sencillo), y bueno espero poder terminar rápido, para poder implementarle scripting al juego y así volverlo más flexible a la hora de programar

Saludos

Leave a Comment :, , , , , , , , , , more...

Desarrollo: Cargando niveles de Tiled

by on Ene.05, 2012, under Desarrollo, EGa2Dengine, Engine

Ya había logrado cargar niveles con Tiled, aunque tenia un defecto en la física del nivel. Entonces tuve que primero agregar unas cosas que me recomendaron, una capa extra para definir la fisica en el Tiled. Y ya habia ordenado todo el codigo, asi que ahora me disponia a  cargar los niveles hecho en Tiled.

Cargando Niveles de Tiled(1)
Vista en el editor Tiled. Lo rojo transparente indica que es colisionable

Gracias a esta capa extra y sin muchos problemas (habian unos problemas tontos, pero como siempre no más) pude añadirle la capa fisica que necesitaba el juego.

Cargando Niveles de Tiled(3)   Vista en el juego.

Aunque no todo era de color rosa, había algo que me incomodaba en ese momento. Tiled entregaba la capa fisica (los tiles de color rojo transpararente) como una matriz o una secuencia de 0 y 1.

Por ejemplo.

1,0,0,0,1,
1,0,1,0,1,
1,1,1,1,1

Pero box2D trabaja con rectangulos, circulos, lineas y polygonos y además tenia muchos problemas con la logica del juego como por ejemplo un fantasma que camina a lo largo de una plataforma no lograba caminar correctamente porque creia que su plataforma era más pequeña de lo que se veia. Entonces ¿Como podia pasar de las colisiones que estan en una matriz a muchos rectangulos?

Bueno como esa ocasión estaba apurado hice un algoritmo bastante ingenuo (sencillo o basico)

//codigo
uint16 *data; //es la matriz con los datos guardada en un arreglo
int layer_w,layer_h; // dimensiones de la matriz
//necesitamos procesar todos los rectangulos
        Rect **r = new Rect*[layer_w];
        for(int i=0; i<layer_w; i++) {
            r[i] = new Rect[layer_h];
        }
        //agregamos los datos en los rectangulos
        for(int y=0; y<layer_h; y++) {
            for(int x=0; x<layer_w; x++) {
                if(data[y*layer_w+x]) { //todo colisionable
                    r[x][y] = Rect(x,y,1,1);
                }
            }
        }
        //ahora aplicar el algoritmo ingenuo
        //!\todo optimizar el algoritmo ingenuo
        for(int x=0; x < layer_w; x++) {
            for(int y=0; y < layer_h; y++) {
                Rect a = r[x][y];
                if(a.width !=0 || a.height != 0) {
                    //buscar
                    for(int i=x; i<layer_w; i++) {
                        for(int j=y; j<layer_h; j++) {
                            Rect v = r[i][j];
                            if(v.width != 0 || v.height != 0) {
                                Rect suma = a + v;
                                //se pueden fusionar y son los mismos datos
                                if(suma.area() == a.area()+v.area() && data[layer_w*y+x] == data[layer_w*j+i]) {
                                    r[x][y] = suma;
                                    a = suma;
                                    r[i][j].width = 0;
                                    r[i][j].height = 0;
                                }
                            }
                        }
                    }
                }
            }
        }

Algoritmo sencillo en C++, se basa en crear una matriz de rectangulos de dimension 1×1 que reprensentan los 1s en las matrices y que pueden sumarse si hay algun rectangulo contiguo.

Luego en Box2D, consideraba los rectangulos que tuvieran Area distinto de 0 quedando esto.

Cargando Niveles de Tiled(2) Vista actual del juego después de la optimización

Lo que me permitia, que mis fantasmas u otro enemigo, pueda ver correctamente la plataforma y caminar a lo largo de ella.

Saludos

Leave a Comment :, , , , , , , , , more...