Dedicado a mis proyectos en Gambas ,un lenguaje de programación parecido al Visual Basic + Java pero ampliamente mejorado y...¡¡para gnu/linux!!.La potencia del gnu/linux sumada a la facilidad del Basic



Consultas, Desarrollo de programas y petición de presupuestos:



sábado, 26 de mayo de 2012

Gambas: Programando Tu Primer VideoJuego "Salir de la Mansión"


Gambas:

 Programando Tu Primer Un videojuego

 "Salir de la Mansión"


Muchos de los que empezaron con la programación era por el interés que les desperto los videojuegos, y la idea de hacer sus propios juegos.

He encontrado un libro  gratuito llamado Programacion de videojuegos con sdl para windows y linux (os lo podeis descargar pulsando en el enlace), donde el autor Alberto Garcia Serrano, explica como realizar varios juegos, detallando los pasos a seguir, esquemas, y codigos fuentes.



Os recomiendo su lectura para los que os querrais iniciaros o simplemente, tengais curiosidad en como se hacen los videojuegos.

 El primer ejemplo del libro lo he pasado a Gambas 3.1.1, ya que los ejemplos del libro estan en C, no os desanimeis por ello, ya que  no resulta complicado entender los ejemplos del libro, porque el autor los comenta muy detalladamente.

Os podeis descargar el código fuente en Gambas, de mi versión, en este enlace: EnlaceEjecutable en Gambas




Se trata de un juego tipo texto, donde tienes que moverte por las habitaciones de una mansión cogiendo objetos y usandolos, escribiendo las ordenes en un textbox.

He añadido una mejora, para que las habitaciones y objetos se coloquen aleatoriamente cada vez que jugamos. (haciendo que el juego nunca se repita :) )

Para ello, genero una lista que contiene un entero que indica el indice del array que va a contener cada habitación , y la desordeno aleatoriamente, obteniendo una disposicion siempre diferente ya que la llamo cada vez que se inicia el juego:

Public Function GeneraOrdenHabitaciones() As String[]




Dim lista As New Integer[]
Dim a As Integer
Dim obtener1 As Integer
Dim obtener2 As Integer
Dim temporal As Integer
'creo la lista
For a = 0 To 9
lista.Add(a)
Next




'barajo la lista
For a = 1 To 50 'barajo 50 veces
'obtengo nuevas posiciones...
obtener1 = Int(Rnd(1, 10))




obtener2 = obtener1 'hacemos esto para que el bucle se ejecute por lo menos una vez
While (obtener1 = obtener2)
obtener2 = Int(Rnd(1, 10))
Wend




'intercambiar
temporal = lista[obtener1]
lista[obtener1] = lista[obtener2]
lista[obtener2] = temporal




Next
Print "Lista obtenida de habitaciones: "
For a = 1 To 9
Print lista[a]; " ";
Next
Return lista




End

Esta funcion, devuelve una lista, que usare como el indice del array de habitaciones.

Todo el programa sigue el paradigma de programación estructural, son sus limitaciones y complicaciones para posteriores ampliaciones (algo muy tipico en los juegos..).

En una próxima entrega, realizaré este mismo juego basandome en el paradigma de Programación Orientada en Objetos, donde os mostraré que beneficios trae a la hora de programar y podreis aprecias las diferencias y beneficios (por ejemplo, sera muy facil añadir más objetos, ordenes, etc) al juego.


Espero que os guste y os resulte útil. Saludos

miércoles, 23 de mayo de 2012

Gambas y OOP: Recibir información de Formularios

Es habitual que cuando llamemos a un formulario que necesitemos la información que haya introducido el usuaria para usarla en el programa principal.

Os voy a explicar un método para hacer esto usando el paradigma de programación orientada a objetos (POO). (ver nota 1)

Bien, imaginaos que tengamos dos formularios:
a) El formulario  Fmain, donde esta el programa principal
b) El formulario  FormIntroduceDatos, que es el encargado de pedirle al usuario que introduzca sus datos, por ejemplo nombre de usuario y clave.

El programa principal necesita en algún momento de su ejecución que el usuario introduzca su nombre y clave, y que según esos datos le permita continuar o no en el programa. Por lo tanto necesitamos que nos devuelva información el formulario FormIntroduceDatos al programa principal para operar con ella.

¿como lo hacemos?
El formulario FormIntroduceDatos, necesita tener una referencia del programa principal (formulario Fmain), para informale de los datos recibidos. Para ello le vamos a crear una propiedad que sea una referencia al objeto que le ha llamado.


Vamos a ver como sería el código del FormIntroduceDatos:



' Gambas class file

Property ref As Object 'creamos una propiedad ref, que va a contener la referencia del objeto  que le llama
Private href As Object ' href, es la forma de trabajar con ref, de forma privada dentro de la clase


Public Sub Form_Open()


End

Private Function ref_Read() As Object

Return href 'retornamos ref

End

Private Sub ref_Write(Value As Object)

href = value 'asignamos a ref

End

Public Sub ToolButtonCancelar_Click()

Me.close 'cerramos el formulario

End

Public Sub ToolButtonAceptar_Click()

'Hemos pulsado Aceptar, los datos introducidos los vamos a asignar al objeto de referencia

href.nombre = TextBoxNombre.Text
href.password = TextBoxPassword.text
'ya hemos pasados los datos al programa principal....
'De esta forma hemos conseguido "recuperar", la información del formulario antes de cerrarlo

'cerramos el formulario
Me.close
End

Ahora vamos a ver como llamamos al formulario FormIntroduceDatos, en el programa principal:



Property nombre As String 'propiedad que contiene el nombre de usuario
Private hnombre As String

Property password As String 'propiedad que contiene la contraseña
Private hpassword As String

Private Function nombre_Read() As String

Return hnombre

End

Private Sub nombre_Write(Value As String)

hnombre = Value

End

Private Function password_Read() As String

Return hpassword

End

Private Sub password_Write(Value As String)

hpassword = value

End

'aqui empieza lo interesante....


Public Sub ToolButtonIntroduce_Click()

Dim f As New FormIntroduceDatos 'creamos una instancia del FormIntroduceDAtos
f.ref = Me 'a la propiedad que tiene nuestra instancia (.ref) le vamos a asignar la referencia del objeto actual (osea el programa principal, formulario Fmain)
f.ShowDialog 'mostramos el formulario y esperamos a que se cierre

'aqui vamos a ver los datos que se han actualizado de nuestras variable privadas hnombre y hpassword
TextLabelDatos.text = "El nombre de usurio es <u>" &
hnombre & " </u><br> y su contrasena es <u>" & hpassword & " </u>"
End


Si quereis descargar el codigo completo: Enlace Version Gambas 3.1.1





Fuente:
http://www.gambas-es.org/viewtopic.php?f=1&t=491&highlight=valores+formulario
Referencias:
http://gambasdoc.org/help/lang/me?es&v3

Nota 1:
Podríamos usar variables globales (definir en un módulo una variable publica, y usarla donde queramos). A la larga este método nos traerá problemas al querer reutilizar el código, ampliar el programa, etc...

viernes, 18 de mayo de 2012

Gambas Y Arduino: Moviendo dos servomotores


Gambas Y Arduino:
3º Ejemplo
Moviendo dos Servomotores


En el ejemplo anterior ( lectura y graficos de datos de arduino con gambas), vimos como gambas podía reconocer una trama de datos que enviaba arduino, y se encargaba de mostrarlos en una gráfica.

Esta vez, vamos a hacer el paso contrario: Pasar "ordenes" desde Gambas a Arduino, y que este las interprete y ejecute lo que le estemos diciendo (mueva el motor indicado una serie de grados).


Para ello la tramo de datos, (protocolo)  es la siguiente:
12333

siendo:
1: Letra  que indica cual dispositivo es sobre hay que actuar. Usamos la "M" para indicar servomotor
2: Numero que indica cual de los dispositivos vamos a mover. (si hay varios servomotores, el 0, sera el primero, el 1 será el segundo... etc, el limite son 10 (de 0 a 9)
333: Indica los grados que se van a girar los servomotores.

Por ejemplo, si le mandamos desde gambas a arduino este comando:
M0180  -> Girara, el servomotor (M), nº 0, 180º grados.

M190 -> Girará, el servomotor (M), nº 1, 90 º grados.


Para facilitar al usuario, esta labor, lo que hacemos es usar  unos botones gráficos tipo "DIAL"





y  el programa en Gambas, crea y manda las ordenes a Arduino, que se encarga de leer la orden, interpretarla y ejecutarla.











Aqui teneis el montaje:



Nota: se usan los puertos digitales (PWM) para conectar los servomotores con arduino. Es importante no usar el pin nº 0 ni el pin nº 1, ya que ambos se usan para la comunicación en Serie-USB con el ordenador.

Aqui teneis el código del programa en Gambas:




' Gambas class file

Public sport As New SerialPort

Public conectado As Boolean

Public mensaje As String

Public Sub _new()

End

Public Sub Form_Open()

Me.Center
conectado = False
End

Public Sub ToolButton1_Click()

If conectado = False Then

If Sport.Status = Net.Active Then
Close Sport
Else
sport.PortName = "/dev/ttyUSB0" 'esto puede cambiar segun al puerto al que conectes tu arduino
sport.speed = 9600 
  'tiene que ser la misma velocidad que tenga el programa de arduino.
sport.Parity = SerialPort.None
sport.DataBits = SerialPort.Bits8
sport.StopBits = SerialPort.Bits1
sport.FlowControl = SerialPort.None
Try sport.Open()
If Error Then
Message.Error("Error al intentar conectar con puerto: " & sport.PortName)
conectado = False
Else
'le mando el comando "S" para que me conteste con los sensores que me va a devolver...

Try Print #sport, "S"
If Error Then
Message.Error("Error al intentar enviar datos...")
Print "No he podido conectarme"
conectado = False
Else
Print "ok...Conectado"
ToolButton1.picture = Picture["icon:/16/stop"]
conectado = True
mensaje = "M00"
EscribeEnArduino(mensaje)

Endif

'creo las clases para graficar los datos que voy a recibir
Endif
Endif

Else
conectado = False
ToolButton1.picture = Picture["icon:/16/play"]
Close Sport
Endif

End

Public Sub DialServo1_Change()

LCDNumber1.value = DialServo1.Value
'mortor 0, lo giro lo que indique el LCDNumber1
mensaje = "M0" & Str$(LCDNumber1.Value)
EscribeEnArduino(mensaje)

End

Public Sub DialServo2_Change()

LCDNumber2.value = DialServo2.Value
'mortor 1, lo giro lo que indique el LCDNumber1
mensaje = "M1" & Str$(LCDNumber2.Value)
EscribeEnArduino(mensaje)

End

Public Sub DialServo3_Change()

LCDNumber3.value = DialServo3.Value

DialServo1.Value = LCDNumber3.value
DialServo2.Value = LCDNumber3.value
'mortor 1, lo giro lo que indique el LCDNumber1
mensaje = "M0" & Str$(LCDNumber3.Value)
EscribeEnArduino(mensaje)
Wait 0.1
mensaje = "M1" & Str$(LCDNumber3.Value)
EscribeEnArduino(mensaje)

End

Public Sub EscribeEnArduino(mensaje As String)

Try Print #sport, mensaje
If Error Then
Message.Error("Error al intentar enviar datos...")
Print "No he podido conectarme"
Endif
Print MENSAJE

End


Y el código de Arduino:



#include <Servo.h>

const int TotalServos=2; // indico el numero de servos que tengo conectados
Servo servo[TotalServos]; //defino un array de N elementos de instancia de la clase Servo
int pos[TotalServos]; // guarda posicion de los servos
byte a; // contador


void setup()
{
Serial.begin(9600);
Serial.println("Arduino conectado...");
Serial.println(" a la espera de ordenes");
// Definos los pin donde ira conectado cada servo: TotalServos

servo[0].attach(2);// el servo 0, le asigno el pin 2; al servo 1, le asigno el pin 3,....
servo[1].attach(3);
}
void loop()
{
//Formato de orden de reconozco:
//M0180
//M=Motor
//0=Nº del motor (si recibo N entoces son todos los motores)
//180= grados de giro..
int dato; // leo
byte contador=0;


if (Serial.available()) {
dato=Serial.read();

Serial.println("Recibido: ");
Serial.println(dato);

if (dato==83) {
Serial.println("Recibido S");
}
if (dato==77)
{
// entrada de orden de motor
Serial.println("Recibido M");
dato=Serial.read();
vermotor(dato);
}
// entrada para ampliar a otras ordenes
// … (para añadir mas sensores... por ejemplo)

}
}

void vermotor(int dato) {
// convierto el caracter en numero (que indicara el servo a mover)
int valor=0;
int b=0;

if (dato==78) {
Serial.println("Recibido N");
Serial.print("Todos los motores los muevos grados: ");
valor= recibirnumero();

Serial.println(valor);
// coloco a los servos al valor que me han pasado...
for (b=0;b<=TotalServos-1;b++)
{
pos[b]=valor;
Serial.println("Servo (");
Serial.println(b);
Serial.println("): ");
Serial.println(valor);
servo[b].write(valor);
}
}

if ((dato>=48) && (dato<=57)) {
Serial.println("escribo en motor");
movermotor(dato);
}
}


int recibirnumero()
{
boolean entrada=true;
char cadena[24];
int n;
a=0;
while (Serial.available()>0)
{
// delay(5);
cadena[a]=Serial.read();
a++;
}

cadena[a]='\0';

Serial.println("En letra :");
Serial.println(cadena);
n=atoi(cadena);
Serial.println("En numero :");
Serial.println(n);

return n;
}


void movermotor(int dato){
int valor;
Serial.print("Muevo el motor (");
Serial.print(dato-48);
Serial.print("): ");
valor=recibirnumero();
Serial.println("valor: ");
Serial.println(valor);
pos[dato-48]=valor;
servo[dato-48].write(valor);
delay(150);
dato=0;
}


Como veis tiene varias subrutinas (vermotor, movermotor y recibirnumero), que ayudan a que sea más claro el programa.

Lo que me costó más trabajo, fue la lectura de las ordenes del puerto serie, ya que Arduino lee caracteres, o mejor dicho la conversión a ASCII de ellos, por eso cuando analizo la orden, no analizo caracteres sino números:


if
 (dato==78) {
Serial.println("Recibido N");

N en ASCII es el nº 78,


Además teneia que "unir" los caracteres que formaban el nº que indica los grados a girar, eso lo solucioné usando un array del tipo Char, como podeis ver en el código de la subrutina recibirnumero.






Os dejo un video donde muestro como se usa el programa:



Os dejo los enlaces de descarga del cógido fuente de los dos programas, ya que el  "copiar y pegar" os puede dar muchos problemas.

Códigos fuentes: New: 28/7/2014 Enlaces a google drive
 DosServoMotores.pde
ControlServos.tar.gz


¿donde compre los servomotores y los cables?
dealextreme.com