Stratos: Punto de Encuentro de Desarrolladores

¡Bienvenido a Stratos!

Acceder

Foros





Error al usar delete

Iniciado por aprendiz2, 07 de Agosto de 2012, 04:43:11 PM

« anterior - próximo »

aprendiz2

Pues estoy creando un jueguito con opengl y glut y el personaje principal que es la esfera roja tiene ke disparar a segun se mueve.
Los disparos los creo con new disparo, de la clase disparo, pero cuando llegan al limite de la pantalla llamo a borrar_disparo.
Sin embargo no se que ocurre que no se borra y vuelve a aparecer en el otro extremo de la pantalla.
A ver si alguien sabe porque pasa eso....

#include <GL/glut.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <windows.h>
#include <iostream>

#define PI 3.141592
#define v_alto 10
#define v_ancho 10

#define radio_j 0.25
#define radio_d 0.1

using namespace std;

const int ancho = 800;
const int alto = 600;
int tecla_a,tecla_w,tecla_s,tecla_d;

class jugador;
class disparo;

disparo *dis[20];

void pintar_circulo (float radio);

class jugador
{
      public:
             jugador () {x = v_ancho * 0.5; y = v_alto * 0.5;}
             void mover(bool arriba,bool derecha,bool abajo,bool izquierda);
             void pintar ();
             void crear_disparo (float x,float y,int dir);
             void actualizar_disparos ();
             int num_d;
      private:
              float x,y;
               
}j;

class disparo
{
public:
      disparo (float x1,float y1,int direcion) : x(x1), y(y1) ,dir(direcion) {}
      void mover ();
      void pintar ();
      void borrar_disparo (disparo *d);
private:
         float x,y;
         int dir;
         
};

void jugador::mover (bool arriba,bool derecha,bool abajo,bool izquierda)
{
     float vel = 0.15f;
     if (arriba) {y += vel; crear_disparo (x,y,1);}
     if (abajo) {y -= vel; crear_disparo (x,y,3);}
     if (derecha) {x += vel; crear_disparo (x,y,2);}
     if (izquierda) {x -= vel; crear_disparo (x,y,4);}
     
     if (y < radio_j) y = radio_j;
     if (y > v_alto - radio_j) y = v_alto-radio_j;
     if (x < radio_j) x = radio_j;
     if (x > v_ancho - radio_j) x = v_ancho - radio_j;
     
     
}
void jugador::pintar ()
{

     glLoadIdentity ();
     glTranslatef (x,y,0);
     glColor3f (1,0,1);
     pintar_circulo (radio_j);
}
void jugador::crear_disparo (float x,float y,int dir)
{   
     if (num_d >= 2) return;
     dis[num_d] = new disparo(x,y,dir);
     num_d++; 
}
void jugador::actualizar_disparos ()
{
  for (int i = 0; i < num_d; i++)
  {
      disparo *d;
      d = dis[i];

      d->mover();
      d->pintar();

  }
}

void disparo::borrar_disparo (disparo *d)
{
     j.num_d--;
     delete d;
}

void disparo::mover()
{
     float vel = 0.1;
     if (dir == 1) y += vel;
     if (dir == 2) x += vel;
     if (dir == 3) y -= vel;
     if (dir == 4) x -= vel;
     
     if (y < -radio_d || y > v_alto + radio_d || x < -radio_d || x > v_ancho + radio_d){
           borrar_disparo (this);
     }
}

void disparo::pintar ()
{
     glLoadIdentity ();
     glTranslatef (x,y,0);
     glColor3f (1,0,0);
     pintar_circulo (radio_d);
}

void reshape (int width,int height)
{
     glViewport (0,0,width,height);
     glMatrixMode (GL_PROJECTION);
     glLoadIdentity();
     
   
    glOrtho (0, v_ancho, 0, v_alto, 0, 10);
     
     glMatrixMode (GL_MODELVIEW);

}



void pintar_escena()
{
     glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
     glLoadIdentity();
     //glTranslatef (0,0,-5);
     j.mover(tecla_w,tecla_d,tecla_s,tecla_a);
     j.pintar();
     j.actualizar_disparos ();
     glFlush();
     glutSwapBuffers();

}

void init ()
{   

     glClearColor (0,0,0,0);
     glEnable(GL_DEPTH_TEST);
}
     
void idle ()
{
     pintar_escena();
     
     
}   
     
void teclas_down (unsigned char key, int x, int y)
{
    if (key == 'w' || key == 'W') tecla_w = 1;
    if (key == 'a' || key == 'A') tecla_a = 1;
    if (key == 's' || key == 'S') tecla_s = 1;
    if (key == 'd' || key == 'D') tecla_d = 1;
}

void teclas_up (unsigned char key, int x, int y)
{
    if (key == 'w' || key == 'W') tecla_w = 0;
    if (key == 'a' || key == 'A') tecla_a = 0;
    if (key == 's' || key == 'S') tecla_s = 0;
    if (key == 'd' || key == 'D') tecla_d = 0;
}


int main (int argc, char **argv)
{
    glutInit (&argc,argv);
    glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB);
    glutInitWindowPosition (100,100);
    init();
    glutInitWindowSize (ancho,alto);
    glutCreateWindow ("Cubo 1");
   
    glutDisplayFunc (pintar_escena);
    glutReshapeFunc (reshape);
    glutIdleFunc (idle);
    glutKeyboardFunc (teclas_down);
    glutKeyboardUpFunc(teclas_up);
    glutMainLoop();
    return 0;
}             

void pintar_circulo (float radio)
{
     glBegin (GL_TRIANGLE_FAN);
             glVertex2f (0,0);
             for (float ang = 0; ang <= PI*2; ang += PI/8) glVertex2f (radio * cosf(ang),radio * sinf(ang));
     glEnd();
}

Gallo

madre mia  no se ni como te esta funcionando :S en teoria un objeto no puede hacer delete de si mismo como haces ahí. Yo lo que haria es indicarle al objeto jugador que ha de borrar un disparo, por ejemplo le haces una propiedad que sea, int disparo_a_borrar; que por defecto sea -1 y donde guardas el indice del disparo que se ha de borrar, y  en cada update (o pintar en tu caso), en caso de ser diferente de -1 haces un delete de esa posición del array:

if(disparo_a_borrar > -1){
    delete dis[disparo_a_borrar];
    disparo_a_borrar = -1;
}


Ahora bien, estas matando a la orientación a objetos eh con todo esto XD

aprendiz2

Si ya se que soy principiante en c++...es mi primer código en c++ y con opengl.

Si tienes tiempo de contestarme gallo, ¿cual sería la forma correcta (sin matar la orientación a objetos) de hacer eso? Es decir, crear nuevos objetos disparo y borrarlos después según salen fuera de la pantalla.
Si puedes poner algun código de ejemplo te lo agradezco.

TrOnTxU

#3
1) Si quieres hacerlo con OOP y stl utiliza un std::vector o un std::list para "sprites" o disparos.
2) Si la lista es de punteros debes crear y borrar los objetos con new y delete.
3) En tu caso la lista podria ser de objetos en vez de punteros ya que solo amacenas posicion y "direccion" (yo no pondria un entero, pero weno)
4) Acerca de OGL: no crees un lista de visualizacion para el ciruclo cada vez que lo dibujes. Precalcula la lista antes con listas precompiladas o, mucho mejor, con vertex buffers, y luego haces un draw call y listo.
5) No deberias actualizar y pintar los objetos en el mismo for NUNCA!!

Aqui lo importante es que machaques libros y ejemplos de C++ hasta que entiendas la diferencia entre "memoria estatica" y dinámica, variables y punteros, stack y heap dinamico.

La idea es que una clase o un struct realmente es una zona de memoria que ocupa, tanto como ocupen los campos de la misma.
Por ejemplo, tu clase disparo tiene 2 floats y 1 int. En el caso (normal) de que el compilador entienda un int como 32bits o 4 bytes (tb puede ser que algunos compiladores para algunas plataformas entiendan el int como 8 bytes), tendriamos 2 * 4 (cada float) + 1 * 4 (cada int) =  12 bytes. Que seria lo que devolveria el codigo: "sizeof(disparo)", te recomiendo que lo pruebes con un simple main tal que asi: "printf("Clase disparo ocupa: %d", sizeof(disparo) );", codigo que deberia escribir en la consola el tamaño de la clase.

Dicho esto entramos en el tema de la OPP. Los structs y las clases, ademas de datos tiene "metodos". Pero estos son simplemente funciones, y la forma de llamarlos depende de una cosa llamada "casting", que es la forma en que el compilador entiende que tipo de objeto esta manejando.
Esto supone en C++, que al final cualquier direccion de memoria se puede tratar como cualquier tipo de objeto, pero el contenido de esa zona de memoria no se conoce y puede encontrarse fuera de los limites de direccionamiento de tu programa, puediendo crear cualquier tipo de comportamiento inesperado. Esto hace de C++ un lenguaje tan "poderoso" como "peligroso".

Un caso especial es el polimorfismo. Una clase con polimorfismo (con metodos virtuales), ademas de los datos que declares en ella, tiene un puntero al principio, que es la direccion de la vtable que contiene las direcciones de memoria de los metodos virtuales de tu clase. Esto implica que una clase con polimorfismo tiene un "pequeño" trozo de información sobre su tipo, que le permite seleccionar en tiempo de ejecucion que funcion se ejecutará sobre el objeto.


Todo esto te puede parecer muy abstracto, pero trata de comprenderlo (sino con mi explicacion con otra que encuentres por ahi), si realmente quieres entrar en "C/C++".


Como he dicho, tu clase disparo es suficientemente pequeña (tipicamente 12 bytes) copiar sus datos no es problema. Asi que puede ser mucho más eficiente tener un array de objetos, en vez de punteros:

disparo dis[20];

... y al crear el disparo no hay new, solo copias la info (los dos floats y el int):

dis[num_d] = disparo(x,y,dir); // sin el new!!: ahora dis es un array de objetos no punteros

... ahora cambias el update:

disparo *d;
d = &(dis[i]); // el "&" es necesario para traducir la direccion de memoria a donde va a apuntar el puntero


... y al borrar el disparo NO tienes que hacer DELETE porque no has echo new (los datos no estan en el heap dinámico, son estaticos).
Pero tu principal problema aqui es que no necesitas el puntero al disparo, necesitas el indice del array.

La solucion es basica, tu metodo de mover NUNCA llama a borrar_disparo!!!!!!
Puedes hacer que devuelva un bool que sea false cuando el objeto deba ser destruido, pero como estas actualizando el array no puedes borrar el objeto dentro del for de actualizar.
Yo suelo hacerme una lista local en la funcion (un array de ints y un entero con el numero de ementos del array) y almacenar los indices del array a borrar. Eliminar los elementos del array se hace en el mismo metodo, pero despues, con otro for. Si utilizas "arrays contiguos" (ya sean con objetos o punteros) deberás mover los datos para rellenar el "hueco" del objeto que quieres eliminar. Lo puedes hacer conservando el orden o no, si no necesitas conservar el orden puedes utilizar la tecnica "swap-last" que es bastante rapida, y solo tienes que mover muy pocoes elementos del array.

Otra opcion si no quieres tener que rehacer el array al borrar, puedes utilizar std::vector o std::list en vez de array de C. Aunque por varias razones (sobretodo de eficiencia) yo prefiero los "arrays de toda la vida", aunque haya que borrar los elementos del array a mano.


Como he dicho, pegale caña a ejemplos de libros y ejercicios aunque no sean exclusivos de juegos, porque no tienes claros muchos conceptos básicos de C.

Un saludo, y suerte.

EDIT: Si quieres ejemplos, en mi firma esta el link a mi blog, y en el articulo que colgue sobre gestión de memoria (que te vendria muy bien leer, aunque creo que es un poco más vanzado para el nivel que tienes ahora), hay un ejemplo sobre un emisor de particulas (muy basico) en version "C++" con std::lists y otra version en "C" con arrays. El código tiene todo esto de lo que he hablado aqui, y esta puesto con licencia MIT asi que lo puedes bajar y utilizar si quieres. http://typhoeus-sdk.blogspot.com.es/2012/06/problemas-de-memoria-no-lo-se-no-me.html
Vicent: Linked-In  ***  ¡¡Ya tengo blog!!

aprendiz2

Pues muchas gracias por toda la info, tendré que digerirla poco a poco.

y otra pregunta, la tecnica swap-last en que consiste? Intuyo que se coge el último objeto y se mete en el lugar donde se ha borrado el objeto, no?

TrOnTxU

Si, exactamente eso.

En el código del emisor de particulas, version C, del link que he puesto, esta implementado con arrays, lista de "elementos a borrar" y "swap-last", por si quieres cogerlo de ahi.
Vicent: Linked-In  ***  ¡¡Ya tengo blog!!






Stratos es un servicio gratuito, cuyos costes se cubren en parte con la publicidad.
Por favor, desactiva el bloqueador de anuncios en esta web para ayudar a que siga adelante.
Muchísimas gracias.