[ Foro de C ]

duda rutina disparo

13-Jul-2014 19:43
Invitado (Tigran)
11 Respuestas

Hola Nacho. Primero felicitarte por la labor que haces
compartiendo tus conocimientos en el mudo de los juegos.
Soy 1 aficionado a la programación en C, hice hace años 1 curso
de c, visualbasic y java, aunque con java no me entere mucho:).
Estoy haciendo 1space invaders y tengo problemas con la rutina
disparo de mi nave. De momento utilizo 1pixel que simula la nave ememiga
y se desplaza de lado a lado por pantalla y luego 1pixel que simula mi nave
y lo desplazo con los cursores.
La nave enemiga esta dentro de 1bucle while (! Kbhit ()) y sobre
este tengo 1bucle infinito for (;;), dentro del cual controlo las pulsaciones del teclado, por sus números ascii. Pero al pulsar la barra espaciadora pal disparo este no asciende continuamentejeje, asciente algo pero pulsando la barra espacio.Podria si hace falta
mandarte el codigo por aqui o a alguna direcion de correo.Un saludo Nacho.


14-Jul-2014 10:38
Invitado (Caranim)

Es complicado con la información que das, pero a bote pronto me da la sensación de que te falta actualizar el movimiento del disparo en el bucle ( o usando alguna interrupción o temporizadores ) y solo la actualizas cuando está pulsado el espaciador. Una vez que pulses el espacio, el disparo debe de actualizarse cada cierto tiempo, para eso puedes poner una variable lógica que indique dentro del bucle si hay o no un disparo en pantalla: Si lo hay, actualizamos sus coordenadas y lo dibujas ( o lo añades a la lista de elementos a "pintar" ). Después de eso tienes que controlar si ha llegado a una posición en pantalla que lo haga desaparecer - colisiona con algún otro elemento o se sale de la pantalla... lo que sea - y en ese momento vuelves a modificar la variable para indicar que el disparo ya no hay que redibujarlo.

Espero que te ayude.

Saludos.


14-Jul-2014 18:30
Invitado (Tigran)

Hola caranim. Tienes razon, he pensado en ello y seguro que asi funciona. Cuando pulse tecla pondre.Unna variabl
e a 1 (por ejemplo) y la usare con un if en el bucle while(! Kbhit), cuando colisione o salga de pantalla, la pongo a 0. A propósito no uso ni SDL ni Allegro. Utilizo TurboC++3.0
programo en modo 13h (256 colores) ya se que es antiguo pero me gustan los juegos de los 80 y 90:-).Un saludo a todos.


18-Jul-2014 01:05
Invitado (Tigran)

Hola. Ya tengo la rutina del disparo funcionando.Al principio me salia en unas coordenadas fijas, pero lo he estado pensando y sa se como corregirlo. Tengo ke incrementar la x del disparo a cada pulsacion de cualquier cursor. Lo curioso ke cada vez ke disparo, tanto la nave enemiga como el disparo se ralentizan, y solo son 2 pixeles en pantalla . Cuando desaparece el disparo, vuelve la nave enemiga a recuperar la velocidad. Saludos a todos.
Ana . Estoy pensando meterme un poco con 1 lenguaje de alto nivel, pero estoy muy indeciso entre c++ y python. Python parece basic, por lo sencillo ke parece y permite hacer cualquier cosa, creo.


19-Jul-2014 11:23
Nacho Cabanes (+83)

Para que el disparo no salga desde unas coordenadas fijas, puedes tomar como punto de partida las coordenadas de tu nave.

En cuanto a por qué va despacio, no te puedo decir nada sin ver el fuente, pero dibujar dos píxeles en pantalla es instantáneo, así que no debería ralentizar. De hecho, lo habitual es forzar un pausa (con "delay") tras cada dibujado, para que el juego vaya a X fotogramas por segundo, en vez de a la velocidad máxima de tu equipo.

En cuanto a lenguaje por el que seguir, yo te recomendaría antes C++ que Python, porque se parece más al C que ya estás manejando. De hecho, te recomendaría antes C# que C++, que es un lenguaje más moderno y mejor diseñado, con una curva de aprendizaje más suave y pocas inconsistencias.

Si quieres ver algo de programación de juegos en C#, mira aquí:

http://www.nachocabanes.com/videojuegos/ipj2012/


23-Jul-2014 01:30
Invitado (Tigran)

Hola Nacho. Gracias por los consejos. He mirado el enlace de c# y juegos, me ha gustado mucho (se me ha hecho corto:-)).haber si un dia te da por ampliarlo mas:). En cuanto a mo juego utilizo las coordenadas de la nave, pero segun sube la bala, si muevo la nave, , tambien se desplaza el disparo:)).haber si lo resuel vo. De todas formas tengo que ordenar el codigo mejor, son unas  90 lineas, pero va creciendo:). Me gustaria poner aqui el codigo, para que mas gente interesada pueda verlo. Como puedo adjuntarlo aqui? Un saludo a todos.


23-Jul-2014 16:13
Nacho Cabanes (+83)

No puedes adjuntar ficheros. Es algo poco amigable, y que no indexan los buscadores. Puedes usar la alternativa natural: copiar y pegar el fuente del programa.  ;-)

Y la nave y el disparo deberán tener coordenadas X e Y independientes, para que el disparo no "siga" a la nave.


23-Sep-2014 18:18
Invitado (Tigran)

Hola, adjunto código de juego  un space-invader mio. Le faltan cosas, pero puede servir como base para todos aquellos que quieran hacer el suyo propio.
Esta compilado con Turbo C++ 3.0. Un saludo a todos. (aaah.. lo único4 que al realizar un disparo se ralentiza el movimiento de las imágenes).


#include<stdio.h> //PROGRAMA MEJORADO DE NAVE1.C.LA ENTRADA A MODO VIDEO Y A
#include<conio.h> //MODO TEXTO SE HACE LLAMANDO A LA FUNCION(MODOVIDEO..)
#include<dos.h>   //SIMULA ENEMIGO DESPLAZANDOSE DE LADO A LADO,Y BAJANDO.NAVE PROPIA
#include<stdlib.h>//DESPLAZANDOSE MEDIANTE TECLADO(TECLAS a y d),Y DISPARO PROPIO
#define derecha 77 //(TECLA c).
#define izquierda 75
#define escape 27
/////////////////////PROTOTIPOS DE FUNCIONES///////////////////////////
void modovideo(int);
void ponpunto(unsigned int, unsigned int, unsigned char);
void caja(int,int,int,int,unsigned char);
unsigned char far *vga;
void sprite1(int x1, int y1, int ancho1, int alto1);
void sprite2(int x2, int y2, int ancho2, int alto2);
void borrosprite(int x1, int y1, int ancho2, int alto2);
int borro[35]={0,0,0,0,0,
		0,0,0,0,0,
		0,0,0,0,0,
		0,0,0,0,0,
		0,0,0,0,0,
		0,0,0,0,0,
		0,0,0,0,0};
int nave[35]={0,0,5,0,0,
	       0,0,5,0,0,
	       0,5,5,5,0,
	       0,5,5,5,0,
	       5,0,5,0,5,
	       0,5,5,5,0,
	       0,0,5,0,0};
int enemy[35]={7,0,0,0,7,
		0,7,7,7,0,
		0,7,7,7,0,
		7,0,7,0,7,
		7,7,7,7,7,
		0,7,7,7,0,
		7,0,0,0,7};

////////////////////FUNCION PRINCIPAL MAIN/////////////////////////////
void main(void)
{   //CORDENADAS DE ENEMIGO. DE NAVE PROPIA Y DEL DISPARO PROPIO.
int x=25,y=35,x1=150,y1=195,xfuego=0,yfuego=0,ancho1=5,alto1=7,ancho2=5,alto2=7;
char direcion=0,tecla,fuego;
modovideo(0x13);
//caja(0,0,319,199,10);  //x1,y1,x2,y2,color
sprite1(x1,y1,ancho1,alto1); //POSICION INCIAL DE NUESTRA NAVE Y SUS MEDIDAS.
for(;;)
{
while(!kbhit()) //MIENTRAS NO SE PULSE UN BOTON EL PUNTO SE MUEVE DE LADO A LADO
	{
		if(yfuego<1)fuego=0; //SI DISPARO ALCANZA ZONA SUPERIOR,NO SE PINTA.
		//SI ENEMIGO ESTA EN LADO IZQUIERDO DE PANTALLA.
		if (x<=1){direcion=0;y+=3;}// y++= BAJAMOS ENEMIGO.
			if((x>=0)&&(direcion==0))
			{      //PINTAR ENEMIGO E INCREMENTAR SU POSICION.
			sprite2(x,y,ancho2,alto2);
			delay(10);
			borrosprite(x,y,ancho2,alto2);
			x=x+2;
			}
		//SI ENEMIGO ESTA EN LADO DERECHO DE PANTALLA.
		//315 ES POR EL ANCHO DEL ENEMIGO.
		if(x>=315){direcion=1;y+=3;} //y++= BAJAMOS ENEMIGO
			if((x>=0)&&(direcion==1))
			{    //PINTAR ENEMIGO E DECREMENTAR SU POSICION.
			sprite2(x,y,ancho2,alto2);
			delay(10);
			borrosprite(x,y,ancho2,alto2);
			x=x-2;
			}
		//SI PULSAMOS FUEGO Y DISPARO MAYOR QUE CORDENADA Y.
		if((fuego==1)&&(yfuego>=1))
			{     //PINTAMOS FUEGO Y DECREMENTAMOS COORDENADA Y.
			ponpunto(xfuego,yfuego,5);
			delay(10);
			ponpunto(xfuego,yfuego,0);
			yfuego=yfuego-2;
			}

       }//LLAVE DEL WHILE KBHIT

       tecla=getch();//ESPERA PULSACION DEL TECLADO
		if(tecla=='c')
			{
			fuego=1; //TECLA DISPARO a.
			xfuego=x1+2,yfuego=y1-1;//CORDENADA DEL DISPARO=CORDENADA NAVE.
			}
		else if(tecla=='d')  // TECLA CURSOR DERECHO
			{            //PINTAR NAVE E INCREMENTAR POSICION X.
			borrosprite(x1,y1,ancho2,alto2);
			x1+=2;
			sprite1(x1,y1,ancho1,alto1);
			}
	       else if(tecla=='a')  //TECLA CURSOR IZQUIERDO
			{           //PINTAR NAVE Y DECREMENTAR POSICION X.
			borrosprite(x1,y1,ancho2,alto2);
			x1-=2;
			sprite1(x1,y1,ancho1,alto1);

			}
		else if(tecla==escape)break;//SALIMOS DEL BUCLE FOR
delay(20);
}//LLAVE DEL FOR

modovideo(0x3);
}   //////////////////////FUNCIONES////////////////////////////////////
void modovideo(int modo)
{
union REGS regs;
regs.h.al=modo;
regs.h.ah=0;
int86(0x10,®s,®s);
vga=(unsigned char far*)MK_FP(0xa000,0);
}
void ponpunto(unsigned int x,unsigned int y,unsigned char color)
{
*(vga+(320*y)+x)=color;
}
 void caja(int x1,int y1,int x2,int y2,unsigned char color)
{
int i;
for(i=x1;i<=x2;i++) //0-319
	{
	ponpunto(i,y1,color);
	ponpunto(i,y2,color);
	}
for(i=y1;i<=y2;i++) //0-199
	{
	ponpunto(x1,i,color);
	ponpunto(x2,i,color);
	}
}
void sprite1(int x1, int y1, int ancho1, int alto1)
{
int i,j;
for(i=0;i<alto1;i++)
	for(j=0;j<ancho1;j++)
	ponpunto(x1+j,y1+i,nave[i*ancho1+j]);
}
void sprite2(int x1, int y1, int ancho2, int alto2)
{
int i,j;
for(i=0;i<alto2;i++)
	for(j=0;j<ancho2;j++)
	ponpunto(x1+j,y1+i,enemy[i*ancho2+j]);
}
void borrosprite(int x1, int y1, int ancho2, int alto2)
{
int i,j;
for(i=0;i<alto2;i++)
	for(j=0;j<ancho2;j++)
	ponpunto(x1+j,y1+i,borro[i*ancho2+j]);
}



28-Sep-2014 12:13
Nacho Cabanes (+83)

El código sigue siendo un tanto repetitivo y a veces díficil de seguir: las funciones deberían tener un nombre que sea un verbo, y, si es posible, más aclarador, como "dibujarNave" en vez de "sprite1" y "dibujarEnemigo" en vez de "sprite2". Se ralentiza porque cada elemento lo dibujas, esperas y luego lo borras, de modo que cada nuevo elemento supone más retraso.

Ya que tienes un "delay(20)" al final del bucle de juego, sería más razonable:

- Borrar todo
- Comprobar teclas y movimientos automáticos
- Dibujar todo
- Esperar 20 ms

Verás que así se ralentizará menos y parpadeará menos.


30-Sep-2014 10:10
Invitado (Tigran)

Hola Nacho.

Gracias por los consejos, voy a ordenar todo el codigo como dices, haber si lo consigo :-). Estoy pensando en meter todo en funciones (borrar imagenes, dibujar imagenes, eventos....)

Tengo otro programita mas corto para poder realizar varios disparos, pero no consigo que aparezcan en pantalla todos los disparos que realizo, solo uno. Utilizo una estructura con los datos cordenada x,cordenada y, activo, pero sigue saliendome solo un disparo. Adjunto el codigo, pero segire dandole vueltas, haber si lo resuelvo.

Un saludo a todos.

A proposito Nacho, me ha gustado mucho lo que he visto de la actualizacion del curso pascal (version 5). Lo das todo muy "mascado" y resulta muy ameno de leer. Da gusto leer tutoriales asi, en comparacion con algunos libros que se venden por hay.


#include<stdio.h> //LA ENTRADA A MODO VIDEO Y A
#include<conio.h> //MODO TEXTO SE HACE LLAMANDO A LA FUNCION(MODOVIDEO..)
#include<dos.h>   //SIMULA NAVE DESPLAZANDOSE DE LADO A LADO Y NAVE PROPIA
#include<stdlib.h>//DESPLAZANDOSE MEDIANTE LOS CURSORES.
#define derecha 77
#define izquierda 75
#define escape 27
#define nbalas 5
void modovideo(int);
void ponpunto(unsigned int, unsigned int, unsigned char);
void caja(int,int,int,int,unsigned char);
unsigned char far *vga;
struct bala
	{
	int balax;
	int balay;
	char activo;
	}fuego[5];

void main(void)
{
int x=5,y=5,x1=150,y1=195,i=0;
char direcion,tecla;
for(i=0;i<nbalas;i++)fuego[i].activo=0;
modovideo(0x13);
//caja(0,0,319,199,10);  //x1,y1,x2,y2,color
ponpunto(x1,y1,7);
for(;;)
{
while(!kbhit()) //MIENTRAS NO SE PULSE UN BOTON EL PUNTO SE MUEVE DE LADO A LADO
	{
		if(fuego[i].balay==1)fuego[i].activo=0;

		if (x<=1)direcion=0;//ENEMIGO EN EL LADO IZQUIERDO
			if((x>=0)&&(direcion==0))
			{
			ponpunto(x,y,4);
			delay(50);
			ponpunto(x,y,0);
			x=x+2;   //LO MOVEMOS PARA LA DERECHA.
			}
		if(x>=320)direcion=1; //ENEMIGO EN EL LADO DERECHO
			if((x>=0)&&(direcion==1))
			{
			ponpunto(x,y,4);
			delay(50);
			ponpunto(x,y,0);
			x=x-2;       //LO MOVEMOS PARA EL LADO IZQUIERDO.
			}
			if(fuego[i].activo==1)
			{
			ponpunto(fuego[i].balax,fuego[i].balay,6);
			delay(20);
			ponpunto(fuego[i].balax,fuego[i].balay,0);
			fuego[i].balay-=2;
			}

       }//LLAVE DEL WHILE KBHIT
  tecla=getch();//ESPERA PULSACION DEL TECLADO

		if(tecla=='c')
			{
		       for(i=0;i<nbalas;i++)
			{
			   if(fuego[i].activo==0)
				{
				fuego[i].activo=1;
				fuego[i].balax=x1;
				fuego[i].balay=y1;
			      //	printf("disparo: %d activo: %d %d\n",i,fuego[i].activo);
				break;
				}

			 }

			}
	       else if(tecla==derecha)  // TECLA CURSOR DERECHO
			{
			ponpunto(x1,y1,0);
			x1+=2;
			ponpunto(x1,y1,7);
			}
	       else if(tecla==izquierda)  //TECLA CURSOR IZQUIERDO
			{
			ponpunto(x1,y1,0);
			x1-=2;
			ponpunto(x1,y1,7);
			}

       else if(tecla==escape)break;//SALIMOS DEL BUCLE FOR
}//LLAVE DEL FOR


modovideo(0x3);

}
void modovideo(int modo)
{
union REGS regs;
regs.h.al=modo;
regs.h.ah=0;
int86(0x10,®s,®s);
vga=(unsigned char far*)MK_FP(0xa000,0);
}
void ponpunto(unsigned int x,unsigned int y,unsigned char color)
{
*(vga+(320*y)+x)=color;
}
 void caja(int x1,int y1,int x2,int y2,unsigned char color)
{
int i;
for(i=x1;i<=x2;i++) //0-319
	{
	ponpunto(i,y1,color);
	ponpunto(i,y2,color);
	}
for(i=y1;i<=y2;i++) //0-199
	{
	ponpunto(x1,i,color);
	ponpunto(x2,i,color);
	}
}



03-Oct-2014 23:15
Nacho Cabanes (+83)

El fuente resulta MUY difícil de seguir: entre las tabulaciones anárquicas, los comentarios al estilo C++ que hacen que no compile en Turbo C (sólo en Turbo C++), el formato de "main" que no se considera correcto en estándares modernos de C, el usar "break" que rompen el flujo del programa, el que los nombres de las funciones no sean verbos...

Yo empezaría por reformatearlo un poco, para convertirlo en algo así, que sí compile con un Turbo C, no sólo con Turbo C++, y que sea más fácil de leer:


/*
 * LA ENTRADA A MODO VIDEO Y A
 * MODO TEXTO SE HACE LLAMANDO A LA FUNCION(MODOVIDEO..)
 * SIMULA NAVE DESPLAZANDOSE DE LADO A LADO Y NAVE PROPIA
 * DESPLAZANDOSE MEDIANTE LOS CURSORES.
 */
 
#include <stdio.h>
#include <conio.h>
#include <dos.h>
#include <stdlib.h>

/* Teclas */
#define DERECHA 77
#define IZQUIERDA 75
#define ESCAPE 27
#define NBALAS 5

/* Prototipos de funciones */
void cambiarModoVideo(int);
void dibujarPunto(unsigned int, unsigned int, unsigned char);
void dibujarCaja(int,int,int,int,unsigned char);

unsigned char far *vga;
struct bala
{
    int balax;
    int balay;
    char activo;
} fuego[5];
 
/* Cuerpo del programa */
int main()
{
    int x=5,y=5,x1=150,y1=195,i=0;
    int terminado = 0;
    char direccion,tecla;
    
    for(i=0;i<NBALAS;i++)
        fuego[i].activo=0;
        
    cambiarModoVideo(0x13);
        
    /* dibujarCaja(0,0,319,199,10);  //x1,y1,x2,y2,color */
    dibujarPunto(x1,y1,7);
    while ( terminado == 0)
    {
        /* MIENTRAS NO SE PULSE UN BOTON EL PUNTO SE MUEVE DE LADO A LADO */
        while(!kbhit()) 
        {
            if(fuego[i].balay==1)
                fuego[i].activo=0;

            if (x<=1) /* ENEMIGO EN EL LADO IZQUIERDO */
                direccion=0;
                
            if((x>=0) && (direccion==0))
            {
                dibujarPunto(x,y,4);
                delay(50);
                dibujarPunto(x,y,0);
                x=x+2;   /* LO MOVEMOS PARA LA DERECHA. */
            }
            
            if(x>=320)
                direccion=1; /* ENEMIGO EN EL LADO DERECHO */
                
            if((x>=0)&&(direccion==1))
            {
                dibujarPunto(x,y,4);
                delay(50);
                dibujarPunto(x,y,0);
                x=x-2;       /* LO MOVEMOS PARA EL LADO IZQUIERDO.*/
            }

            if(fuego[i].activo==1)
            {
                dibujarPunto(fuego[i].balax,fuego[i].balay,6);
                delay(20);
                dibujarPunto(fuego[i].balax,fuego[i].balay,0);
                fuego[i].balay -= 2;
            }

        } /* LLAVE DEL WHILE KBHIT */
        tecla=getch();/* ESPERA PULSACION DEL TECLADO */

        if(tecla=='c')
        {
            for(i=0;i<NBALAS;i++)
            {
                if(fuego[i].activo==0)
                {
                    fuego[i].activo=1;
                    fuego[i].balax=x1;
                    fuego[i].balay=y1;
                    /*  printf("disparo: %d activo: %d %d\n",i,fuego[i].activo); */
                    terminado = 1;
                }
            }
        }
        else if(tecla == DERECHA)  /* TECLA CURSOR DERECHO */
        {
            dibujarPunto(x1,y1,0);
            x1+=2;
            dibujarPunto(x1,y1,7);
        }
        else if(tecla == IZQUIERDA)  /* TECLA CURSOR IZQUIERDO */
        {
            dibujarPunto(x1,y1,0);
            x1-=2;
            dibujarPunto(x1,y1,7);
        }

        else if(tecla==ESCAPE)
            break; /* SALIMOS DEL BUCLE FOR */
    }/* LLAVE DEL FOR */
    cambiarModoVideo(0x3);

    return 0;
}


void cambiarModoVideo(int modo)
{
    union REGS regs;
    regs.h.al=modo;
    regs.h.ah=0;
    int86(0x10,®s,®s);
    vga=(unsigned char far*)MK_FP(0xa000,0);
}


void dibujarPunto(unsigned int x,unsigned int y,unsigned char color)
{
    *(vga+(320*y)+x)=color;
}


void dibujarCaja(int x1,int y1,int x2,int y2,unsigned char color)
{
    int i;
    for(i=x1;i<=x2;i++) /* 0-319 */
    {
        dibujarPunto(i,y1,color);
        dibujarPunto(i,y2,color);
    }
    for(i=y1;i<=y2;i++) /* 0-199 */
    {
        dibujarPunto(x1,i,color);
        dibujarPunto(x2,i,color);
    }
}


Si lo miras ahora con detalle, verás que la parte que anima los disparos no está dentro de ningún"for", de modo que sólo mueve el disparo 0, en la línea 76:


if(fuego[i].activo==1)




30-Oct-2014 17:51
Invitado (Tigran)

Hola Nacho.
Gracias por la pista :-). Ya resolví el problema del disparo. Ahora tengo varios disparos.
He dejado un poco de lado el lenguaje C, y me estoy estudiando tu manual sobre C# (versión 0.99). Me alegra ver que poco a poco,estas actualizando el curso, con nuevos ejercicios y ejemplos. Supongo que pondrás también unos capítulos sobre POO, me parece muy importante aprender bien ese tema. Haber si hay suerte, y pones algo mas sobre programación windows (windows forms), y si añades algo sobre programación paginas web con C#, seria genial.
Un saludo Nacho.






(No se puede continuar esta discusión porque tiene más de dos meses de antigüedad. Si tienes dudas parecidas, abre un nuevo hilo.)