Bienvenido(a), Visitante. Por favor, ingresa o regístrate. ¿Perdiste tu email de activación?
Diciembre 12, 2024, 12:47:08 pm

Autor Tema: Tutorial de programar tu primer tonti juego (con Processing)  (Leído 53120 veces)

fre3men

  • Mecenas HeroQuest.es
  • Reseñer Plata HQ.es
  • Administrador
  • *
  • Mensajes: 6979
  • Me apetece jugar al Merchants & Marauders
    • HeroQuest.ES
Tutorial de programar tu primer tonti juego (con Processing)
« en: Marzo 26, 2012, 04:03:59 pm »
Aprendiendo a programar con Processing.js
A través de un sencillo ejemplo con POO


Presentación
Este "tutorial" está creado por fre3men1 para usuarios principiantes y lo comparto bajo la licencia de Creative Commons BY-NC-SA. Con este "tutorial" pretendemos crear un personaje que se mueva por una rejilla que formará la pantalla de juego. No obstante, antes de todo indicaremos ciertas pautas para programar "bien", algo así como un manual de la buena conducta del programador. Por supuesto que cada Programador Morcarcillo2 tiene su librillo ;), aun así intentaré que este "tutorial" se base en ciertos estándares.

Bien, ¿tienes prisa y ya quieres meterte ya a picar código como un loco? Pues puedes ir AQUÍ, cuando ya estés cansado podrás volver a este "tutorial" ;). Si has vuelto de picar código, es que haces renombre al título de Programador Morcarcillo! :D



Introducción
No, no es lo mismo que Presentación. No obstante podría unirse y ser un sólo encabezado, aunque es mejor separar los churros de las porras (si algún día se hace célebre esta frase, recordad que yo fui el primero en decirla).

Antes de seguir con el "tutorial", debes tener claro ciertos aspectos de la programación. Debes estar familiarizado con al menos algún lenguaje de alto nivel (C/C++, VisualBasic, PHP, Javascript, Ensamblador, Cobol, Pascal...). Ni que decir que también debes conocer "más o menos" el lenguaje de marcación HTML5, ya que Processing.js3 ataca a una etiqueta de este código de marcación denominada canvas.

Aprovecharemos también este "tutorial" para explicar un poco que es la Programación Orientada a Objetos (POO o en inglés OOP), pues el ejemplo que utilizaremos para explicar el desarrollo del código usado ya utilizará este paradigma de programación.

NOTA: Si te he colado la palabra Ensamblador, es que no tienes ni pajoteraidea de que es un lenguaje de alto nivel. En este enlace podrás hallar una buena explicación de a lo que me refiero con lo de alto nivel.



Enlaces de interés
Aquí mostraremos algunos sitios web necesarios o interesantes para desarrollar código y hacer vuestras pruebas dentro o fuera de los pasos de este "tutorial".
  • Referencia de Processing.js: Es la página oficial de Processing.js para conocer las instrucciones disponibles de su framework. Por supuesto que las instrucciones regulares de Javascript4 funcionan en Processing.js
  • KeyCodes Javascript: Muy útil para conocer rápidamente que valor numérico asigna a la variable de sistema keyCode cuando se pulsa una tecla en el teclado. Nota: No confundir con "key", que almacena el caracter pulsado.
  • Colores HTML: Página de la Wikipedia para conocer los valores de los colores más usados. Tendrás el valor rgb que utilizaremos en Processing.js, aunque hay otros tipos de valores y por supuesto más páginas para hallar los millones de colores que puedes representar.
  • WordReference: Este diccionario/traductor será muy útil para saber algunas palabras en inglés, sobretodo si vamos a utilizar nombres de variables en inglés (para respetar un poco los estándares...)
  • Tutorial de uso del IDE e instrucciones del Processing: Aquí hallarás un tutorial en castellano sobre el funcionamiento del IDE de este lenguaje, además de la explicación de la gran mayoría de instrucciones y funciones propias del lenguaje.
Manual de buena conducta
Este manual hace referencia a la escritura/presentación del código, aunque también es bueno aconsejar que antes de programar te hagas algún pseudocódigo o diagrama de flujo, establezcas milestones5, pienses bien las rutinas/funciones para que esté el código bien optimizado... Pero estas cosas no las voy a mencionar más, pues no son objetivo de este "tutorial" ya que dichos temas son muy extensos y además yo mismo pecaré de no hacerlo del todo bien. Así que (como mínimo) nos queda que al menos el código que hagamos sea inteligible para los demás e incluso para uno mismo si lo miras después de un buen tiempo (te asombrarás de no entenderte ni a ti mismo).

Normativa de la estructura
Es recomendable que al escribir el código respetes unas pautas (las sigas a rajatabla, o prácticamente al 100%). Si no lo haces, se coherente y no vayas cambiando según parecer o memoria.

Preferiblemente deberíamos usar BSD KNF Style o Allman Style. En este "tutorial" usaremos la primera. Para hallar más información de estos estilos puedes acceder AQUÍ.

Ejemplo del estilo BSD KNF:

Código: [Seleccionar]
if ("hola" == "adios") {
  println("se cumple!");
} else {
  println("que se va a cumplir...");
}

Varias costumbres que deberías adoptar (si quieres):
  • Comenta lo máximo de código posible, pues en el momento que lo escribes sabes para que es, pero con el tiempo será engorroso descifrar algunas cosas. ¡Hazlo! te lo agradecerás y mucho.
  • Declara siempre todas las variables antes de usarlas (aunque hayan lenguajes como el PHP que no lo requieran). En este caso, con Processing.js, tendremos que indicar de que tipo son (integer, float...).
  • Siempre debes usar {}, incluso si el condicional o bucle va a ejecutar una sola instrucción. Lo aceptará si no lo pones, pero no es buena conducta!
  • En este lenguaje no es necesario cerrar la ejecución de una instrucción con ";" (o el que pertoque según el lenguaje de programación) como en otros lenguajes (ejemplo PHP), siempre que la instrucción esté sola en una linea de comandos. Pero ciérrala siempre!!!!
  • No dejes espacios blancos después de cerrar la instrucción.
  • Comenta los bloques en su parte superior y las instrucciones al lado.
  • Las líneas de texto comentado no tendrían que tener más de 80 caracteres (aunque tengas una súper pantalla panorámica)
  • Deja dos espacios entre funciones, clases... y un espacio entre bloques de instrucciones como condicionales, llamadas a rutinas, bucles...
  • Deja espacios entre los parámetros de los condicionales, bucles, funciones... También entre operadores.
  • No dejes espacio cuando llamas una función y los () de sus parámetros.
  • Deja un espacio entre ) y { en declaración de funciones, usos de bucles y condicionales.

Código: [Seleccionar]
if ("hola" == "adios") {
  println("se cumple!");
} else {
  println("que se va a cumplir...");
}

// Este bucle imprimirá 10 veces la frase "hola mundo"
/* Otra forma de comentar hasta que cerramos el tag
si, si no lo cerramos podemos seguir comentando */
for (int i = 0; i < 10; i++  ) {
  println("hola mundo!"); // Comentamos esta línea de código así
}

Ahora un ejemplo de espaciar entre parámetros que facilita la lectura:
Código: [Seleccionar]
if (Ax1 >= Ax2 && Ax1 < Bx2 && Ay1 >= Ay2 && Ay1 <= Ay2) {
  return true;
} else {
  return false;
}

Es mejor que...
Código: [Seleccionar]
if(Ax1>=Ax2&&Ax1<Bx2&&Ay1>=Ay2&&Ay1<=Ay2){
Normativa de la nomenclatura
Es muy importante que a la hora de declarar variables o crear funciones, estas sean de fácil entendimiento. Además, debes seguir una norma a la hora de asignarles un nombre. Deberás elegir una de estas dos formas, CamelCase o Underscores. No debes en ningún caso mezclar ni escribirlos con formas raras. Los nombres lo más descriptivos posible.

  • CamelCase:
    • UpperCamelCase: VidaJugador
    • lowerCamelCase: vidaJugador
  • Underscores: vida_jugador
Nada de escribir variables como Vidajugador, Vida_jugad, vidadeljugador, vidaJ, vdj, vj...

Debes escoger una, y esta deberá ser usada por igual en toda la aplicación!, no empieces a cambiar de nomenclatura. Yo personalmente uso camelCase (la opción lowerCamelCase).

También es aconsejable que utilices nombres en inglés, y sólo acrónimos cuando es muy claro. También evita reducir las palabras a sólo los casos más claros, por ejemplo positionX o posicionX podría ser posX.

Añadir un inciso el cual es a la hora de diseñar los nombres para tablas y campos de una Base de Datos (BBDD), deberás siempre usar Underscores, ya que hay motores que dan problemas con mayúsculas (aunque no vamos a tratar con BBDD, mejor acostumbrarse desde el principio).



Herramientas y entorno de desarrollo
Para programar en Processing.js (y cualquier otro lenguaje) necesitaremos algún software que nos permita escribir el código. Podríamos utilizar el Bloc de Notas, pero seríamos muy cutres, por ello podemos utilizar el Notepad++  (del que estoy enamorado y he programado todo un "web browser6 mmorpg" con él) o para el lenguaje que veremos aquí un entorno de desarrollo integrado (IDE7) de este lenguaje (o que lo soporte).

Los creadores de Processing nos ofrecen un IDE gratuito hecho en Java8, que podemos incluso usarlo en línea (¡ojo! que no se puede salvar, sólo recomendado para hacer pruebas tontas) o el mismo para escritorio que descargaremos desde la url de descargas, Download IDE Processing v1.5.1. Por supuesto descarga la versión que sea para tu sistema operativo.

Si queremos programar para CANVAS, deberemos añadir a nuestra página web, la librería javascript de processing. Podemos acceder a este enlace (en inglés) en el que se indica varias formas para hacerlo: https://processingjs.org/articles/jsQuickStart.html

La librería de processing.js la podemos descargar desde AQUÍ.

La forma más sencilla sería cargar la librería y asingar nuestro archivo de código "puro de processing" a la etiqueta CANVAS del HTML5: Writing Pure Processing Code


Código: [Seleccionar]
<!DOCTYPE html>
<html>

<head>
<title>Pruebas Processing.js</title>
<script src="processing-1.3.6.min.js"></script> <!-- Cargamos la librería de processing.js -->
</head>

<body>
<h1>Pruebas con Processing.js</h1>
<p>Este será nuestro programa que podremos ver desde el navegador!:</p>
<!-- Entre las etiquetas de CANVAS podemos poner textos, imágenes... que se visualizarán si el navegador NO soporta CANVAS -->
<canvas data-processing-sources="nuestro_archivo.pde"><p style="font-style:italic;">Tu navegador no soporta CANVAS (HTML5)</p></canvas>
</body>

</html>


Programando que es gerundio
Bien, bien! Ya nos hemos quitado de encima los sermones al recién llegado (espero que hayas sido un buen Morcarcillo y los hayas hasta memorizado, pues al final de todo hay un examen). Ahora procederemos a explicar el código paso a paso para que quede claro y puedas aprender un poco de este mundillo.

Lo más básico es conocer los bloques básicos de Processing.js que son tres:

  • Declaración de variables globales
  • Función de iniciación que se ejecutará una vez al inicio de la aplicación.
  • Función principal que se irá ejecutando de forma interrumpida si no indicamos lo contrario.
Código: [Seleccionar]
// Variables globales
  // ... aquí declaramos las variables globales que vamos a utilizar ... //
 
void setup() {
 
  // ... aquí inicializamos aspectos de nuestro programa ... //
  // ... ejemplo el refresco de pantalla ... //
 
}

void draw() {
 
  // ... aquí añadimos instrucciones que se ejecutarán de forma continuada ... //
 
}

Ahora haremos nuestro primer programa que dibujará nuestro personaje en el centro de la panalla. He comentado el código para que veáis que hace cada cosa.

Código: [Seleccionar]
// Variables globales
int posX, posY; // Variables de posición de nuestro personaje
 
 
// Función que usamos para inicializar algunos parámetros de nuestro programa
void setup() {
 
  size(400, 400); // Asignamos un tamaño de 400x400 píxeles a nuestro programa
  frameRate(30); // Indicamos que la pantalla se refreque 30 veces por segunda (por defecto es 60)
 
  // La posición inicial de nuestro personaje será en medio de la pantalla
  posX = width/2; // width es una variable de sistema que nos indica el ancho de la pantalla
  posY = height/2; // height es una variable de sistema que nos indica la altura de la pantalla
 
}


// Main (Función principal)
void draw() {
 
  background(0); // Pintaremos el fondo de pantalla de color negro
 
  strokeWeight(2); // Indicamos que las líneas de dibujo sea de 2 píxeles de grosor
  stroke(100); // Seleccionamos un color gris para el borde (línea) de las formas
  fill(255); // Rellenaremos las formas dibujadas con el color blanco
 
  rect(posX, posY, 20, 20); // Dibujamos un cuadrado en la posición indicada con un tamaño 20x20 píxeles (anchura/altura)
}

Como veis, las variables globales son posX y posY, y las he declarado como integers. Además que he aprovechado y he concadenado la declaración, pero se puede hacer independientemente cada una en una línea:
int posX;
int posY;

Aunque indicamos que dibujamos en el centro al "personaje", esto en realidad no es verdad, ya que la función rect() dibuja un rectángulo a partir de la posición indicada (por defecto es la esquina superior izquierda). Si queremos que esté centrado realmente podemos hacerlo de tres formas.


Código: [Seleccionar]
  rect(posX-10, posY-10, 20, 20); // Restamos la mitad del tamaño a la posición para que esté centrado realmente
o...

Código: [Seleccionar]
  translate(-10, -10); // Desplazamos el punto de dibujo (se aplicará a todo lo que se dibuje después)
  rect(posX, posY, 20, 20);

o cambiamos el modo de dibujo de los rectángulos a CENTER (esto también es aplicable a otras formas como círculo o a la hora de dibujar imágenes cargadas). Eso sí, se aplicará a todas las formas de dicho tipo a partir de cambiar el modo. Aviso, me he encontrado que al usarla en varios sitios (si a veces te interesa un modo u otro) exportar para applet de Java no hay problema, pero deja de funcionar en CANVAS, para CANVAS por lo visto (a falta de profundizar e investigar más sobre el tema), sólo lo puedes utilizar en el Setup.

Código: [Seleccionar]
rectMode(CENTER);
//rectMode(CORNER); // Por defecto está este modo

No obstante, si usamos la instrucción "translate" todo lo que dibujemos a partir de la misma sufrirá una translación de posición. Por ello podemos utilizar unas instrucciones (funciones) que salvarán la configuración de transformaciones (rotate, translate y scale) y la podrán restaurar (pushMatrix(); y popMatrix(); ):

Código: [Seleccionar]
  pushMatrix();
  translate(-10, -10); // Desplazamos el punto de dibujo (se aplicará a todo lo que se dibuje después)
  rect(posX, posY, 20, 20);
  popMatrix();

Empezando con la animación
Ahora pondremos un poco más de carne en el asador, daremos "vida" a nuestro personaje, para ello utilizaremos un evento que detectará cuando "clickamos" sobre el lienzo (la pantalla de la aplicación). En tal caso cambiaremos los valores de posición de nuestro personaje a los valores de posicionamiento del puntero del ratón. Usaremos las variables del sistema mouseX y mouseY que guardan constantemente la posición del puntero respecto al lienzo. Nota: Esta función es del sistema, podemos utilizar otras como keyPressed() que se cumple o ejecuta si pulsamos una tecla del teclado. Además también pueden ser utilizadas en condicionales como el cumplimiento o no de ellos.

Código: [Seleccionar]
// Función que detecta cuando se pulsa un botón del ratón (cualquier botón)
void mouseClicked() {
 
  posX = mouseX;
  posY = mouseY;
 
}

Daros cuenta que declaramos que la función no devolverá ningún valor (void), si quisieramos devolver un valor (con la instrucción return, deberíamos indicar que tipo de valor será, int de integer, float...). Ahora bien, indicar que en este caso, no nos dejarían devolver un valor, pues esta función en concreto (es del entorno del lenguaje) no lo permite (así como muchas otras, esta por ejemplo está reservado para que devuelva 1 si se ha clickado el ratón y 0 si no), pero en las que hagamos nosotros mismos si nos puede interesar devolver un valor.

Podemos crear también una función para dibujar a nuestro personaje, manteniendo un código más inteligible y reutilizable. Así que procederemos a crear una función para ello. De momento no estamos programando en POO sino haciendo solamente uso de las funciones o rutinas (subprogramas), para ayudarnos en nuestra programación estructurada.


Código: [Seleccionar]
// Main (Función principal)
void draw() {
 
  background(0); // Pintaremos el fondo de pantalla de color negro
 
  drawHero(); // Dibujamos a nuestro personaje

}

void drawHero() {

  strokeWeight(2); // Indicamos que las líneas de dibujo sea de 2 píxeles de grosor
  stroke(100); // Seleccionamos un color gris para el borde (línea) de las formas
  fill(255); // Rellenaremos las formas dibujadas con el color blanco
 
  pushMatrix();
  translate(-10, -10); // Desplazamos el punto de dibujo (se aplicará a todo lo que se dibuje después)
  rect(posX, posY, 20, 20);
  popMatrix();

}

Nuestro primer contacto con la POO
Ahora empezaremos un poco con la POO. La POO son las siglas y un acrónimo de Programación Orientada a Objetos. No pretendo explicar que es la POO pero si hacer un simple ejemplo que la haga entendible y muy cercana para el que no la conozca. Es una forma de programar más abstracta, pero que acerca la programación a una forma de pensar más humana.

Nuestro personaje actualmente no es más que unas variables globales y una función (rutina) que lo dibuja cuando la llamamos. Ahora haremos que sea un objeto que manipularemos a nuestro antojo.

Lo primero que haremos es crear la clase a la que pertenecerá nuestro héroe. Será un objeto que pertenecerá a la clase Human (diseñada por nosotros). Las clases se componen de muchas cosas, y depende del lenguaje son más potentes o menos (en PHP por ejemplo no hay eventos). Pero aquí nos quedaremos con que tendremos atributos (variables que podremos acceder a ellas) y métodos (funciones que están dentro de las clases). Además tendremos una función que "inicializará nuestro objeto", la cual se llama Función constructora.


Código: [Seleccionar]
// Clase Humano
class Human {
 
  int posX, posY; // Atributos de esta clase
 
  // Función constructora, que utilizaremos para inicializar los objetos de esta clase
  Human(int x, int y) {
    this.posX = x;
    this.posY = y;
  }
 
  // Método (función) de esta clase que se encargará de dibujar por pantalla a nuestro personaje
  void drawHuman() {
   
    // Realizamos varios cambios para dibujar formas
    strokeWeight(2); // Tamaño del borde de las formas dibujadas
    stroke(50, 205, 50); // Color del borde de las formas dibujadas (LimeGreen)
    fill(173, 255, 47); // Color de relleno de las formas dibujadas (GreenYellow)
    ellipse(this.posX, this.posY, 40, 40); // Dibujamos un círculo (será el cuerpo del jugador)
   
    noStroke(); // No dibujaremos los bordes de la formas
    fill(0); // Relleno de color negro
    pushMatrix(); // Salvamos la configuración
    translate(-8, -6);
    ellipse(this.posX, this.posY, 8, 8); // Ojo derecho
    translate(16, 0);
    ellipse(this.posX, this.posY, 8, 8);   // Ojo izquierdo
    popMatrix();
   
    noFill(); // No rellenaremos las formas
    strokeWeight(1); // Ancho del borde de las formas de 1 píxel
    stroke(0); // Color negro del borde
    arc(this.posX, this.posY+5, 20, 10, 0, PI); // Con la instrucción de dibujar un arco hacemos la boca
  }
 
}

Como podéis ver hemos creado una clase llamada Human, esta tiene dos atributos que usaremos para saber la posición del mismo en pantalla (podemos eliminar las variables globales de posicionamiento, ya que estas aunque las dejemos no influirán a nuestro objeto, aunque se llamen igual). Además hemos creado una función con el mismo nombre que la clase, esta es la función constructora* que "instanciará" nuestro objeto (todavía no hemos "instanciado" el objeto (vamos, nuestro personaje todavía no existe).

* Podría ir más allá y explicar en este mismo punto lo que es la sobrecarga de funciones, que es declarar varias funciones con el mismo nombre y que estas serán llamadas dependiendo de los parámetros que le pasemos. Pero si queréis saber más puedes mirar el enlace que he puesto picando sobre el mismo nombre. (Nota: no todos los lenguajes lo soportan). Incluso podría explicar también que son variables (los atributos) privados o públicos (también los métodos), pero no es necesario para el "tutorial". Eso sí, por defecto, todos los atributos y métodos de una clase en processing son públicos.

También tenemos un método que dibujará a nuestro personaje, le he cambiado el nombre pero este puede tener el nombre que quieras. Además he aprovechado y ahora no será un rectángulo sino una cara sonriente :). Fijaros que para los ojos he usado "translate" y luego lo he desplazado en el plano X el doble de píxeles (para que el otro ojo sea totalmente simétrico), ya que el "translate" acumula las modificaciones del punto de dibujo. En si lo he desplazado 16 píxeles, 8 para dejarlo en el punto cero y otros 8 para desplazarlo a 8 píxeles del centro. El segundo translate he dejado cero píxeles en el plano vertical, porque el anterior ya lo había ajustado a la altura deseada.

Ahora añadiremos otro método que será el que se encargue de mover el objeto. Lo llamaremos moveHuman.

Código: [Seleccionar]
  // Método (función) que se encargará de mover al personaje
  void moveHuman(int x, int y) {
    this.posX = x;
    this.posY = y;
  }

Supongo que os habéis fijado que estamos haciendo referencia a las variables de la clase con la palabra reservada this. Esto realmente no es necesario, sin la palabra reservada funcionaría igual (pero hay algunos lenguajes de programación que nos obligan a usarla, es mejor acostumbrarse, además de ver rápidamente que nos referimos a un atributo de la clase). En caso que hubieran variables globales con el mismo nombre, siempre manda el atributo de la clase (aunque no pongas la palabra reservada). También podemos llamar a los métodos de una clase desde la misma con la palabra reservada que he indicado antes.

Ahora ya tenemos nuestra primera clase diseñada. Declararemos una variable para nuestro personaje indicando que será del tipo de dato de la clase Human. Lo haremos en la parte de variables globales (aunque que sepáis que se pueden declarar más objetos de una clase dentro de objetos de otra clase...) y para más INRI se pueden heredar atributos y métodos de una clase a otra, pero no es mi intención liaros con ello.


Código: [Seleccionar]
// Variables globales
Human player; // player tendrá un datatype de clase Human

En la función SETUP:
Código: [Seleccionar]
  player = new Human(width/2, height/2); // Creamos (o "instanciamos") el objeto player (jugador)
Para que se dibuje nuestro personaje debemos llamar al método de la clase que lo dibujará, para ello debemos hacerlo usando el operador . (un punto) entre el nombre de referencia de nuestra variable (player) que contiene un objeto de la clase Human y el método que hay en la clase para que sepa que hacer. El código siguiente lo pondremos dentro de la función principal draw()

Código: [Seleccionar]
  player.drawHuman(); // Llamámos al método de dibujar a nuestro personaje
Además, ahora para desplazar a nuestro personaje, en vez de asingar los valores directamente a las variables globales, llamaremos a un método que hemos creado en la clase:

Código: [Seleccionar]
// Función que detecta cuanso de pulsa un botón del ratón (cualquier botón)
void mouseClicked() {
 
  player.moveHuman(mouseX, mouseY); // Llamámos al método de desplazar a nuestro personaje
 
}

Pero por supuesto también podemos acceder y manipular un atributo de la misma forma. Ejemplo: player.posX = 100; Así que el código de arriba también podría ser el siguiente (pero para mostrar ejemplos de métodos que puede tener nuestra clase, hemos creado el de movimiento)

Código: [Seleccionar]
// Función que detecta cuanso de pulsa un botón del ratón (cualquier botón)
void mouseClicked() {

  player.posX = mouseX;
  player.posY = mouseY; 

}

Código completo de nuestro programa
Código: [Seleccionar]
// Variables globales
Human player; // player tendrá un datatype de clase Human


// Función de configuración
void setup() {
 
  size(400, 400); // Asignamos un tamaño de 400x400 píxeles a nuestro programa
  frameRate(30); // Indicamos que la pantalla se refreque 30 veces por segunda (por defecto es 60)
  smooth(); // Aplicamos el filtro anti-aliasing !!! Cuidado !!! que puede relentizar la máquina
 
  player = new Human(width/2, height/2); // Creamos el objeto player (jugador) y lo colocaremos en el centro de la pantalla
 
}


// Función principal
void draw() {
 
  background(0);
  player.drawHuman(); // Llamámos al método de dibujar a nuestro personaje
}


// Función que detecta cuanso de pulsa un botón del ratón (cualquier botón)
void mouseClicked() {
 
  player.moveHuman(mouseX, mouseY); // Llamámos al método de desplazar a nuestro personaje
 
}


// Clase Humano
class Human {
 
  int posX, posY; // Atributos de la clase
 
  // Función constructora, que utilizaremos para inicializar los objetos de esta clase
  Human(int x, int y) {
    this.posX = x;
    this.posY = y;
  }
 
  // Método (función) de esta clase que se encargará de dibujar por pantalla a nuestro personaje
  void drawHuman() {
   
    // Realizamos varios cambios para dibujar formas
    strokeWeight(2); // Tamaño del borde de las formas dibujadas
    stroke(50, 205, 50); // Color del borde de las formas dibujadas (LimeGreen)
    fill(173, 255, 47); // Color de relleno de las formas dibujadas (GreenYellow)
    ellipse(this.posX, this.posY, 40, 40); // Dibujamos un círculo que será la cara del jugador
   
    noStroke(); // No dibujaremos los bordes de la formas
    fill(0); // Relleno de color negro
    pushMatrix(); // Salvamos la configuración
    translate(-8, -6);
    ellipse(this.posX, this.posY, 8, 8);
    translate(16, 0);
    ellipse(this.posX, this.posY, 8, 8);   
    popMatrix();
   
    noFill(); // No rellenaremos las formas
    strokeWeight(1); // Ancho del borde de las formas de 1 píxel
    stroke(0); // Color negro del borde
    arc(this.posX, this.posY+5, 20, 15, 0, PI); // Con la instrucción de dibujar un arco hacemos la boca
  }
 
  // Método (función) que se encargará de mover al personaje
  void moveHuman(int x, int y) {
    this.posX = x;
    this.posY = y;
  }
 
}

Nuestro programa funcionando

Más adelante podemos expandir nuestro programa, creando una rejilla como tablero de fondo y hacer que nuestro personaje se mueva con los cursores, impidiendo que se salga de los márgenes del tablero.


Notas del autor
Este tutorial, como veis no está ni remotamente pensado para usuarios muy avanzados, pues el mismo creador se considera "Programador Morcarcillo Medio".

No obstante, he dedicado mucho tiempo en la estructuración del "tutorial", así como a su gramática y forma de expresión. Además de intentar explicar lo más cotidiano, enlazando nombres o formas a la Wikipedia u otros sitios web cuando lo he visto oportuno. También así he ido ampliando el "tutorial" más allá de la función de su título para hacer entender muchas cosas básicas (y no tan básicas) del mundo que rodea a la programación.

¿Algún error? No dudes en comunicármelo. Así podré perfeccionar y mejorar este "tutorial", haciéndolo aún mejor para que sea más accesible a los usuarios.



Referencias
1 Otros proyectos que tiene son www.heroquest.es & www.mazeofgalious.com
2 Nombre diminutivo del malo supremo del juego de tablero HeroQuest de MB.
3 Processing.js es un framework para canvas que facilita la tarea a la hora de programar.
4 Javascript es un lenguaje de programación de alto nivel utilizado sobretodo en páginas web y se ejecuta desde el lado del cliente. Es el utilizado para desarrollar código que afecte a la etiqueta canvas del HTML5.
5 Son metas cortas a la hora de desarrollar. Intentar lograr algún resultado en poco tiempo. Ayuda entre otras cosas a no desanimarse, ya que vas viendo como va progresando tu aplicación.
6 Web browser es el nombre en inglés de Navegador Web. El programa que utilizas para abrir páginas. Por ejemplo Internet Explorer, Mozilla Firefox, Opera, Chrome, Safari...
7 IDE es el acrónimo en inglés de integrated development environment
8 Es un lenguaje de programación de alto nivel. Realmente, cuando programamos con Processing.js, estamos programando de una forma similar al Java, ya que este es un intérprete que transforma nuestro código a Javascript.
1


La inmovilidad a veces se confunde con la paz | Mi colección de juegos

fre3men

  • Mecenas HeroQuest.es
  • Reseñer Plata HQ.es
  • Administrador
  • *
  • Mensajes: 6979
  • Me apetece jugar al Merchants & Marauders
    • HeroQuest.ES
Re:Tutorial de programar tu primer tonti juego (con Processing)
« Respuesta #1 en: Marzo 26, 2012, 04:04:41 pm »
ANEXO 1: Mejorando la aplicación
En este anexo iremos modificando y ampliando el código de nuestro programa, de esta forma mejoraremos nuestro código y lo haremos más completo.

Diseñamos un tablero
Aquí podemos ver como queda con un tablero de fondo y ajustando la posición a una casilla concreta cuando clickamos con el ratón. Podréis comprobar en el código como hemos creado otra clase, esta vez para el tablero. Ahí tenemos el método que dibuja la rejilla del mismo.

Código: [Seleccionar]
// Variables globales
Human player; // player tendrá un datatype de clase Human
Board board; // board tendrá un dataype de clase Board

// Función de configuración
void setup() {
 
  size(400, 400); // Asignamos un tamaño de 400x400 píxeles a nuestro programa
  frameRate(30); // Indicamos que la pantalla se refreque 30 veces por segunda (por defecto es 60)
  smooth(); // Aplicamos el filtro anti-aliasing !!! Cuidado !!! que puede relentizar la máquina
 
  board = new Board(40, 10, 10); // Creamos un tablero de 10x10 casillas (las casillas serán de 40x40 píxeles)
  player = new Human(0, 0, 40); // Creamos el objeto player (jugador) y lo colocaremos en la casilla 0 (0,0)
 
}


// Función principal
void draw() {
 
  board.drawBoard(); // Dibujamos el tablero
  player.drawHuman(); // Llamámos al método de dibujar a nuestro personaje
 
}


// Función que detecta cuanso de pulsa un botón del ratón (cualquier botón)
void mouseClicked() {
 
  player.moveHuman(mouseX, mouseY); // Llamámos al método de desplazar a nuestro personaje
 
}


// Clase Humano
class Human {
 
  // Atributos de la clase
  int posX, posY; // Indicará en que casillas está (no será el valor X e Y del lienzo)
  int sizeSquare; // Para poder ajustar la posición
 
  // Función constructora, que utilizaremos para inicializar los objetos de esta clase
  Human(int x, int y, int sizeSquare) {
    this.posX = x + sizeSquare/2;
    this.posY = y + sizeSquare/2;
    this.sizeSquare = sizeSquare;
  }
 
  // Método (función) de esta clase que se encargará de dibujar por pantalla a nuestro personaje
  void drawHuman() {
   
    // Realizamos varios cambios para dibujar formas
    strokeWeight(2); // Tamaño del borde de las formas dibujadas
    stroke(50, 205, 50); // Color del borde de las formas dibujadas (LimeGreen)
    fill(173, 255, 47); // Color de relleno de las formas dibujadas (GreenYellow)
    ellipse(this.posX, this.posY, 40, 40); // Dibujamos un círculo que será la cara del jugador
   
    noStroke(); // No dibujaremos los bordes de la formas
    fill(0); // Relleno de color negro
    pushMatrix(); // Salvamos la configuración
    translate(-8, -6);
    ellipse(this.posX, this.posY, 8, 8);
    translate(16, 0);
    ellipse(this.posX, this.posY, 8, 8);   
    popMatrix();
   
    noFill(); // No rellenaremos las formas
    strokeWeight(1); // Ancho del borde de las formas de 1 píxel
    stroke(0); // Color negro del borde
    arc(this.posX, this.posY+5, 20, 15, 0, PI); // Con la instrucción de dibujar un arco hacemos la boca
  }
 
  // Método (función) que se encargará de mover al personaje
  void moveHuman(int x, int y) {
    float tempX = x/this.sizeSquare;
    float tempY = y/this.sizeSquare;
    this.posX = floor(tempX) * this.sizeSquare + this.sizeSquare/2;
    this.posY = floor(tempY) * this.sizeSquare + this.sizeSquare/2;
  }
 
}


class Board {
 
  int squareSize, columns, rows; // Atributos de la clase

  // Función constructora
  Board(int squareSize, int columns, int rows) {
    this.squareSize = squareSize;
    this.columns = columns;
    this.rows = rows;
  }
 
  // Función que dibujará el tablero
  void drawBoard() {
    background(0); // Color del fondo del tablero
    // Configuramos el grosor y color de las líneas de dibujo
    fill(100); // Color de relleno
    strokeWeight(1); // Ancho del borde de las formas de 1 píxel
    stroke(0); // Color negro del borde
   
    // Dibujamos las casillas del interior del tablero
    for (int y = 0; y < rows; y++) {
      for (int x = 0; x < columns; x++) {
        rect(x * this.squareSize, y * this.squareSize, this.squareSize, this.squareSize);
      }
    }
  }
 
}



En la clase nueva de Board (Tablero), hemos creado un método que dibujará las casillas del mismo:
Código: [Seleccionar]
  // Función que dibujará el tablero
  void drawBoard() {
    background(0); // Color del fondo del tablero
    // Configuramos el grosor y color de las líneas de dibujo
    fill(100); // Color de relleno
    strokeWeight(1); // Tamaño de la línea
    stroke(0); // Color de la línea (negro)
   
    // Dibujamos las casillas del interior del tablero
    for (int y = 0; y < rows; y++) {
      for (int x = 0; x < columns; x++) {
        rect(x * this.squareSize, y * this.squareSize, this.squareSize, this.squareSize);
      }
    }
  }

La parte en la que dibuja las casillas está compuesta de dos bucles for, uno recorre las filas, y por cada fila recorremos todas las columnas dibujando las casillas. No tiene mucha complicación, aunque hay que saber como funcionan estos bucles.

Con el siguiente código hacemos que al picar sobre una casilla, nuestro personaje se centre en ella.

Código: [Seleccionar]
  // Método (función) que se encargará de mover al personaje
  void moveHuman(int x, int y) {
    float tempX = x/this.sizeSquare;
    float tempY = y/this.sizeSquare;
    this.posX = floor(tempX) * this.sizeSquare + this.sizeSquare/2;
    this.posY = floor(tempY) * this.sizeSquare + this.sizeSquare/2;
  }

Podéis fijaros que hemos declarado dos variables como float, esto es así porque al hacer la división es posible que de un valor con decimales. Luego, la instrucción floor lo que hace es eliminar los decimales y redondear hacia abajo (0,1 ó 0,8 será 0). También fijaros que se han declarado las variables a la vez que se realizaba operaciones con ellas, esto es posible, aunque podéis también declararlas primero y luego usarlas.

Entrada por teclado
Ahora cambiaremos el modo de entrada de datos (del posicionamiento de nuestro personaje) de "clicks" del ratón a los cursores. Con la modificiación que mostraremos a continuación, podremos utilizar los cursores del teclado para desplazar a nuestro personaje. De momento sólo lo haremos de forma ortogonal. Para ello cambiaremos el código del método de movimiento de la clase Human y eliminaremos la función que detecta "click" del ratón y añadiremos la función que detecta que se ha pulsado una tecla del teclado (es la función forma parte del entorno de programación y es keyPressed).


Los cambios que hemos realizado son los siguientes: Se ha añadido en la clase Human un nuevo atributo que es la dirección de nuestro personaje, y hemos decidido indicarlo con un valor numérico, además para hacerlo compatible en el futuro con el movimiento en diagonal, se ha diseñado de la forma que el 1 es hacia arriba, el 3 derecha (el 2 será movimiento en diagonal arriba-derecha), 5 hacia abajo y 7 hacia la izquierda.

Código: [Seleccionar]
// Método (función) que se encargará de mover al personaje
void moveHuman(int direction) {

this.direction = direction;
switch (this.direction) {
case 1: // ARRIBA
this.posY -= this.squareSize;
break;
case 5: // ABAJO
this.posY += this.squareSize;
break;
case 7: // IZQUIERDA
this.posX -= this.squareSize;
break;
case 3: // DERECHA
this.posX += this.squareSize;
break;
}
}

En el código de arriba destacamos la instrucción switch, la cual es un selector, haremos el caso que se cumpla (case), y utilizamos la instrucción break para finalizar el switch (sino sigue realizando el código de los siguientes casos hasta recorrerlos todos o encontrarse un break. También está la opción default que es para que ejecute código si no se cumple ningún caso anterior.

Hemos sustituido la función de sistema que detectaba si se ha "clickado" con el ratón por esta que detecta si se ha pulsado una tecla:

Código: [Seleccionar]
// Función que se ejecuta si se detecta que se ha pulsado una tecla
void keyPressed() {

switch(keyCode) {    
// MOVIMIENTO DEL PERSONAJE (JUGADOR)
case 38: // keyCode => tecla UP
player.moveHuman(1);
break;
case 39: // keyCode => tecla RIGHT
player.moveHuman(3);
break;
case 40: // keyCode => tecla DOWN
player.moveHuman(5);
break;
case 37: // keyCode => tecla LEFT
player.moveHuman(7);
break;
}

}

Ahora bien, os podéis dar cuenta de que nuestro personaje se puede salir del tablero que dibujamos y que la velocidad de movimiento no está controlada. Para ello podremos hace mejoras en el código del método del movimiento de nuestro personaje, así como añadir nuevos atributos e inicializarlos en la función constructora de la clase:

Código: [Seleccionar]
// Clase Humano
class Human {

// Atributos de la clase
int posX, posY; // Indicará en que casillas está (no será el valor X e Y del lienzo)
int squareSize; // Para poder ajustar la posición
int direction; // Nos idndica hacia donde mira nuestro personaje (y se desplazará). Útil para cuando disparemos
int timeHuman; // Muestreo del tiempo para controlar la velocidad del movimiento
int speedMovement; // Velocidad de movimiento de nuestro personaje

// Función constructora, que utilizaremos para inicializar los objetos de esta clase
Human(int x, int y, int squareSize) {
this.direction = 5; // Por defecto, la dirección (hacia donde mira nuestro personaje) es abajo.
this.speedMovement = 500; // Por defecto hacemos que la velocidad de movimiento sea de 500 milisegundos (medio segundo)
this.posX = x + squareSize/2;
this.posY = y + squareSize/2;
this.squareSize = squareSize;
}
 
// Método (función) de esta clase que se encargará de dibujar por pantalla a nuestro personaje
void drawHuman() {
   
// Realizamos varios cambios para dibujar formas
strokeWeight(2); // Tamaño del borde de las formas dibujadas
stroke(50, 205, 50); // Color del borde de las formas dibujadas (LimeGreen)
fill(173, 255, 47); // Color de relleno de las formas dibujadas (GreenYellow)
ellipse(this.posX, this.posY, 40, 40); // Dibujamos un círculo que será la cara del jugador
   
noStroke(); // No dibujaremos los bordes de la formas
fill(0); // Relleno de color negro
pushMatrix(); // Salvamos la configuración
translate(-8, -6);
ellipse(this.posX, this.posY, 8, 8); // Ojo izquierdo
translate(16, 0);
ellipse(this.posX, this.posY, 8, 8); // Ojo derecho
popMatrix();

noFill(); // No rellenaremos las formas
strokeWeight(1); // Ancho del borde de las formas de 1 píxel
stroke(0); // Color negro del borde
arc(this.posX, this.posY + 5, 20, 15, 0, PI); // Con la instrucción de dibujar un arco hacemos la boca

}

// Método (función) que se encargará de mover al personaje
void moveHuman(int direction) {
this.direction = direction;
int tempTime = millis(); // Capturamos el milisegundo actual del reloj

if (tempTime > this.timeHuman + this.speedMovement) { // Intentamos relentizar el movimiento a X milisegundos!
// Selector de movimiento
switch (this.direction) {
case 1: // ARRIBA
if (this.posY > this.squareSize) {
this.posY -= this.squareSize;
}
break;
case 5: // ABAJO
if (this.posY < this.squareSize * (board.rows - 1)) { // NOTA: Restamos un valor (constante) 1 porque la casilla 1 es la casilla 0 realmente
this.posY += this.squareSize;
}
break;
case 7: // IZQUIERDA
if (this.posX > this.squareSize) {
this.posX -= this.squareSize;
}
break;
case 3: // DERECHA
if (this.posX < this.squareSize * (board.columns - 1)) {
this.posX += this.squareSize;
}
break;
}
 
this.timeHuman = millis(); // Capturamos el valor en milisegundos actual del reloj
}
}
 
}

La instrucción (función de sistema) millis(); es muy práctica para hacer temporizadores, pues cada vez que la llamamos nos da un valor incremental (desde que se ejecutó la aplicación) en milisegundos. También hemos añadido varios atributos más, uno es para indicar el mínimo de tiempo que ha de pasar para que nuestro personaje pueda avanzar (es como indicamos la velocidad), podemos ajustarlo como queramos, aunque hay que darse cuenta que si pulsas varias veces seguidas, no hará caso a las pulsaciones que están fuera del límite de tiempo, si quisieramos que fuera avanzando, tendríamos que crear otra variable que fuera un "buffer" de pulsaciones e ir comprobando cuantas veces hemos pulsado.

También, y muy importante, es que para saber el tamaño del tablero, hemos hecho referencia a un atributo de un objeto que debe estar ya instanciado (board.columns y board.rows), con lo cual hemos de asegurarnos que board se "instancia" primero, este objeto (board) realmente es el que utilizaremos como base de nuestra aplicación, (podríamos "instanciar" al/los jugadores dentro de la clase Board). Por supuesto que podríamos crear atributos dentro de la clase Human para indicar el tamaño del tablero cuando se "instancia" un objeto de la clase Human, pero he decidido hacerlo así porque lo veo más coherente. Ahora bien!, más coherente y mejor programado sería crear una clase nueva para las configuraciones de nuestra aplicación (o en vez de un objeto hacerlo con variables globales) y este objeto con las variables generales de nuestro programa pasarlo a los objetos que instanciamos luego y las necesitan o consultar directamente los valores de este objeto que podríamos llamar por ejemplo settings.

Código: [Seleccionar]
// Variables globales
Human player; // player tendrá un datatype de clase Human
Board board; // board tendrá un dataype de clase Board

// Función de configuración
void setup() {
 
  size(400, 400); // Asignamos un tamaño de 400x400 píxeles a nuestro programa
  frameRate(30); // Indicamos que la pantalla se refreque 30 veces por segunda (por defecto es 60)
  smooth(); // Aplicamos el filtro anti-aliasing !!! Cuidado !!! que puede relentizar la máquina
 
  board = new Board(40, 10, 10); // Creamos un tablero de 10x10 casillas (las casillas serán de 40x40 píxeles)
  player = new Human(0, 0, 40); // Creamos el objeto player (jugador) y lo colocaremos en la casilla 0 (0,0)
 
}


// Función principal
void draw() {
 
  board.drawBoard(); // Dibujamos el tablero
  player.drawHuman(); // Llamámos al método de dibujar a nuestro personaje
 
}


// Función que se ejecuta si se detecta que se ha pulsado una tecla
void keyPressed() {

switch(keyCode) {    

// MOVIMIENTO DEL PERSONAJE (JUGADOR)
case 38: // keyCode => tecla UP
player.moveHuman(1);
break;
case 39: // keyCode => tecla RIGHT
player.moveHuman(3);
break;
case 40: // keyCode => tecla DOWN
player.moveHuman(5);
break;
case 37: // keyCode => tecla LEFT
player.moveHuman(7);
break;

// ACCIONES ESPECIALES DEL PERSONAJE (JUGADOR)
// Guiñamos el ojo
case 107: // keyCode => tecla ADD => *+]
player.wink();
break;
// Realizamos un ataque
case 191: // keyCode => tecla / => ç}
player.attack();
break;

}

}


// Clase Humano
class Human {

// Atributos de la clase
int posX, posY; // Indicará en que casillas está (no será el valor X e Y del lienzo)
int squareSize; // Para poder ajustar la posición
int direction; // Nos idndica hacia donde mira nuestro personaje (y se desplazará). Útil para cuando disparemos
int timeHuman; // Muestreo del tiempo para controlar la velocidad del movimiento
int speedMovement; // Velocidad de movimiento de nuestro personaje
boolean closeEye; // Para indicar si cerramos el ojo de nuestro personaje (valores true o false)

// Función constructora, que utilizaremos para inicializar los objetos de esta clase
Human(int x, int y, int squareSize) {
this.direction = 5; // Por defecto, la dirección (hacia donde mira nuestro personaje) es abajo.
this.speedMovement = 500; // Por defecto hacemos que la velocidad de movimiento sea de 500 milisegundos (medio segundo)
this.closeEye = 0; // Ojo abierto por defecto

this.posX = x + squareSize/2;
this.posY = y + squareSize/2;
this.squareSize = squareSize;
}
 
// Método (función) de esta clase que se encargará de dibujar por pantalla a nuestro personaje
void drawHuman() {
   
// Realizamos varios cambios para dibujar formas
strokeWeight(2); // Tamaño del borde de las formas dibujadas
stroke(50, 205, 50); // Color del borde de las formas dibujadas (LimeGreen)
fill(173, 255, 47); // Color de relleno de las formas dibujadas (GreenYellow)
ellipse(this.posX, this.posY, 40, 40); // Dibujamos un círculo que será la cara del jugador
   
noStroke(); // No dibujaremos los bordes de la formas
fill(0); // Relleno de color negro
pushMatrix(); // Salvamos la configuración
translate(-8, -6);
// Comprobamos si queremos guiñar el ojo (cerrarlo o abrirlo)
if (this.closeEye == 0) {
ellipse(this.posX, this.posY, 8, 8); // Ojo izquierdo abierto
} else {
ellipse(this.posX, this.posY, 8, 2); // Ojo izquierdo cerrrado
}
translate(16, 0);
ellipse(this.posX, this.posY, 8, 8); // Ojo derecho
popMatrix();

noFill(); // No rellenaremos las formas
strokeWeight(1); // Ancho del borde de las formas de 1 píxel
stroke(0); // Color negro del borde
arc(this.posX, this.posY + 5, 20, 15, 0, PI); // Con la instrucción de dibujar un arco hacemos la boca

}

// Método (función) que se encargará de mover al personaje
void moveHuman(int direction) {
this.direction = direction;
int tempTime = millis(); // Capturamos el milisegundo actual del reloj

if (tempTime > this.timeHuman + this.speedMovement) { // Intentamos relentizar el movimiento a X milisegundos!
// Selector de movimiento
switch (this.direction) {
case 1: // ARRIBA
if (this.posY > this.squareSize) {
this.posY -= this.squareSize;
}
break;
case 5: // ABAJO
if (this.posY < this.squareSize * (board.rows - 1)) { // NOTA: Restamos un valor (constante) 1 porque la casilla 1 es la casilla 0 realmente
this.posY += this.squareSize;
}
break;
case 7: // IZQUIERDA
if (this.posX > this.squareSize) {
this.posX -= this.squareSize;
}
break;
case 3: // DERECHA
if (this.posX < this.squareSize * (board.columns - 1)) {
this.posX += this.squareSize;
}
break;
}
 
this.timeHuman = millis(); // Capturamos el valor en milisegundos actual del reloj
}
}

// Método sencillo para indicar si cerramos un ojo
// Podremos perfeccionarlo si queremos para indicar que ojo, como lo cerramos...
void wink() {
if (this.closeEye == true) {
this.closeEye = false;
} else {
this.closeEye = true;
}
}

// Método para realizar ataque del jugador
void attack() {

}
 
}


class Board {
 
// Atributos de la clase
int squareSize, columns, rows;

// Función constructora
Board(int squareSize, int columns, int rows) {
this.squareSize = squareSize;
this.columns = columns;
this.rows = rows;
}
 
// Función que dibujará el tablero
void drawBoard() {
background(0); // Color del fondo del tablero
// Configuramos el grosor y color de las líneas de dibujo
fill(100); // Color de relleno
strokeWeight(1) // Tamaño de la línea
stroke(0); // Color de la línea (negro)
   
// Dibujamos las casillas del interior del tablero
for (int y = 0; y < rows; y++) {
for (int x = 0; x < columns; x++) {
rect(x * this.squareSize, y * this.squareSize, this.squareSize, this.squareSize);
}
}
}
 
}



Arriba tenemos nuestro programa mejorado, además, para hacer más incapié en la POO (Programación Orientada a Objetos), hemos creado un par de métodos más, uno guiñará (cerrará) un ojo y otro será para atacar (aunque esta la dejaremos para más adelante). Pulsando la tecla *+] que está al lado del ENTER, veremos como cierra el ojo y otra vez para abrirlo. Hemos declarado para ello una variable del tipo booleana, que su valor será TRUE o FALSE (1 ó 0).
1


La inmovilidad a veces se confunde con la paz | Mi colección de juegos

Bultack

  • Goblin
  • *
  • Mensajes: 1
Re:Tutorial de programar tu primer tonti juego (con Processing)
« Respuesta #2 en: Abril 28, 2012, 01:02:51 pm »
Continuarás con el tutorial añadiendo más cosas?
0

fre3men

  • Mecenas HeroQuest.es
  • Reseñer Plata HQ.es
  • Administrador
  • *
  • Mensajes: 6979
  • Me apetece jugar al Merchants & Marauders
    • HeroQuest.ES
Re:Tutorial de programar tu primer tonti juego (con Processing)
« Respuesta #3 en: Abril 28, 2012, 06:16:22 pm »
Supongo que sí. Al menos tengo la intención de  enseñar los ArrayList, y hacer algún ejemplo para que nuestro personaje dispare "balas". Bueno, de paso "instanciar" otro personaje y que se manejen ambos por teclado y alguna sencilla función de colisión para que detecte si una bala alcanza a un jugador.
0


La inmovilidad a veces se confunde con la paz | Mi colección de juegos

fre3men

  • Mecenas HeroQuest.es
  • Reseñer Plata HQ.es
  • Administrador
  • *
  • Mensajes: 6979
  • Me apetece jugar al Merchants & Marauders
    • HeroQuest.ES
Re:Tutorial de programar tu primer tonti juego (con Processing)
« Respuesta #4 en: Marzo 13, 2013, 08:14:37 pm »
Bueno, viendo que en el Google sale este tema el primero al buscar PROCESSING JUEGO, creo que toca empezar un verdadero juego (eso sí, Vien TONTUNO :D). De todos modos lo iré haciendo a ratos laaargos, así que de momento sólo pongo el proyecto.


¿Qué tenemos que hacer primero de todo?
Primero de todo nos imaginaremos en la cabeza alguna tontería que podamos implementar a corto plazo y sin que nos de la sensación que será muy complicado. Pues en principio será un elfo con un paraguas y que tendrá que "capturar" las gotas de una lluvia absurda de guerreros del Caos.

Luego, buscamos un nombre para nuestro proyecto, en este caso será Lluvia tonta del Caos

Ahora, nos queda hacer un boceto de cómo será el juego, como es sencillo, sólo tendrá una pantalla. El juego será algo así:



Sí, no tengo pulso con la herramienta pincel de píxel del Corel PhotoPaint   :P

Como podéis ver en el esquema de arriba, los "guerreros del caos" caerán de arriba y a su vez rotarán. Haremos que salgan de forma aleatoria. El jugador controlará un muñeco que sólo podrá mover de forma horizontal. Finalmente pondremos un texto para mostrar la puntuación (gotas que capture) aunque podremos desarrollar un poquito más e indicar los que no capture e incluso perder la partida si se falla X veces (ya se verá).

En este caso vemos que habrán dos tipos de objetos claros, el jugador y el guerrero del caos, lo que se puede traducir por dos clases que programaremos (aunque puede que hagamos una clase criatura y que cuelguen dos subclases de la misma).


CONTINUARÁ...
0


La inmovilidad a veces se confunde con la paz | Mi colección de juegos

fre3men

  • Mecenas HeroQuest.es
  • Reseñer Plata HQ.es
  • Administrador
  • *
  • Mensajes: 6979
  • Me apetece jugar al Merchants & Marauders
    • HeroQuest.ES
Re:Tutorial de programar tu primer tonti juego (con Processing)
« Respuesta #5 en: Junio 04, 2013, 11:26:34 am »
Aquí os traigo una clase que creé para hacer de temporizador:

// by fre3men (HeroQuest.es)
// Clase que actúa como temporizador
// CC BY-NC-SA

// Variables Globales
Timer timer;
int trigger;
int seconds = 0;

// Setup del programa
void setup() {
  size(200, 200); // Tamaño de la pantalla
  frameRate(60); // Refresco de pantalla
  //smooth(); // anti-aliasing!!! cuidado que puede relentizar la aplicación
  //imageMode(CENTER); // Forzosamente en setup 
// (en java va bien repetirlo... pero en canvas peta si no está aquí)

  timer = new Timer(1000); // Creamos un temporizador de 1 segundo (1000 milisegundos)
  //timer = new Timer(1,1); // Igual que arriba en modo segundos
}

// Main
void draw() {
  background(100, 100, 100);
  trigger = timer.run();
  if (trigger == 1) {
    seconds += 1;
  }
  text(seconds, 100, 100);
}


// Clase Temporizador
class Timer {
  int startClock, stopClock;
  int timeClock; // Tiempo actual
  int modeClock = 1; // Por defecto estará en milisegundos

  // Constructor
  Timer(int stopClock) {
    this.startClock = millis(); // Inicializamos el temporizador
    this.stopClock = stopClock; // Tiempo límite del temporizador
  }
  Timer(int stopClock, int modeClock) { // Sobre carga de funciones (segunda función constructora)
    this.startClock = millis(); 
    this.stopClock = stopClock; 
    this.setup(modeClock); // Cambiamos el modo del tiempo pasado (en milis o segundos)
  }

  int run() {
    this.timeClock = millis();
    int triggerClock = 0;
    if (this.timeClock - this.startClock >= this.stopClock * this.modeClock) {
      triggerClock = 1;
      this.startClock = millis(); // Volvemos a actualizar el temporizador
    }
    return triggerClock;
  }

  void setup(int modeClock) {
    //0 -> millis ; 1 -> seconds
    switch(modeClock) {
    case 0:
      this.modeClock = 1; // El múltiplo ser&#38;#38;#225; de 1
      break;
    case 1:
      this.modeClock = 1000; // El mútliplo será de 1000 para pasar a segundos.
      break;
    }
  }
}


Es muy necesario en la mayoría de juegos el disponer de temporizadores. En el ComeCocos no lo hice así (con clases), más bien como he realizado algún ejemplo más arriba, pero la verdad es que así es mucho más cómodo ;) y más práctico.
0
« Última modificación: Agosto 28, 2014, 10:21:41 pm por fre3men, Razón: Pongo el código más claro (mediante la exportación html del IDE, pero puede que se cuele código html por acentos... »


La inmovilidad a veces se confunde con la paz | Mi colección de juegos

fre3men

  • Mecenas HeroQuest.es
  • Reseñer Plata HQ.es
  • Administrador
  • *
  • Mensajes: 6979
  • Me apetece jugar al Merchants & Marauders
    • HeroQuest.ES
Re:Tutorial de programar tu primer tonti juego (con Processing)
« Respuesta #6 en: Septiembre 03, 2014, 01:08:05 pm »
Me está dando otra vez por el processing...

Me he creado varias clases para facilitarme la creación de aplicaciones, como colisiones, sonido, mejorado mucho más el temporizador... y vamos, que de momento os pongo la versión 0.2 de Lluvia Tonta del Caos (para Windows).

Lluvia Tonta del Caos
Versión 0.2


CLICK AQUÍ para descargar
CLICK HERE for download
ZIP de 35MB (85 descomprimido, pues lleva las librerías de JAVA que requiere)


Esta versión incluye sonido, algo de lluvia en la Intro y tormenta durante el Juego, para mutar el sonido podéis pulsar "S".

Las gotas (guerreros del caos) que caces con el paraguas otorgarán 1 punto, las que se te escapen te restarán 2

Espero que alguien lo pruebe ::) :D

Saludos y no os mojéis.
0


La inmovilidad a veces se confunde con la paz | Mi colección de juegos

Axuss

  • Mecenas HeroQuest.es
  • Global Moderator
  • *
  • Mensajes: 4315
Re:Tutorial de programar tu primer tonti juego (con Processing)
« Respuesta #7 en: Septiembre 04, 2014, 02:38:08 am »
Juas, imposible coger todo, ni siquiera desde el principio, ¡menudos granizos!

He visto que algunas gotas salen muy al borde y están medio ocultas, supongo que porque el centro de la gota es la referencia de pintado.

El elfo también se puede dar paseos fuera de la pantalla.  :P

Como sugerencia, añade un sonido cuando se para una gota y/o cuando se escapa.

En defintiva, un chorrijuego idóneo para matar 5 minutos sueltos.
0
Corrige al sabio y te amará, corrige al necio y te odiará toda la vida. La Biblia, Proverbios

kipiyo

  • Esqueleto
  • *
  • Mensajes: 20
Re:Tutorial de programar tu primer tonti juego (con Processing)
« Respuesta #8 en: Septiembre 04, 2014, 02:53:58 pm »
Gracias por el tuto esta muy interesante, cuando tenga un rato lo trasteare ;)
0