Programación Específica => Programación de Videojuegos => Mensaje iniciado por: ibito en Martes 28 de Septiembre de 2004, 20:51
Título: Oop
Publicado por: ibito en Martes 28 de Septiembre de 2004, 20:51
este thread es para los usuarios mas avanzados, como utilizan los objetos a la hora de crear un juego? la duda es para hacerme un concepto ya "aplicado" al momento de programar, se lo que es un objeto, herencia y todas esas cosas que lei en un e-book llamado "Think in Cpp" (muy bueno por cierto), yo supongo que hacen un objeto player1 con tales y cuales atributos y metodos... espero estar en lo correcto, si pueden poner un ejemplo (no necesita ser muy completo, solo atributos y metodos mini-basicos) se los agradeceria.
Gracias
Título: Re: Oop
Publicado por: BlackWind en Miércoles 29 de Septiembre de 2004, 04:09
eso tambien lo puedes hacer con structuras, (me imagino que lo estas haciendo en c++), por ahi lei que es recomendable eviitar las clases, porque son mas lentas, pero no sabria decirte si es verdad o no, aqui te va un buen ejemplo con structuras, que lo puedes modificar para que sea una clase:
struct PLAYER { { int Health; int Mana; ARMOR Armor WEAPON Weapon; INV Inv; etc...; };
la ventaja, es que puedes ahorrar mucho codigo, a la hora de usar metodos, por ejemplo, con las estructuras en vez de poner:
void InitPlayer(int Health, int Mana, ARMOR armor,etc....);
puedes poner: void InitPlayer(PLAYER Player);
que te contendra todo....... aunque tambien con las clases puedes hacer eso.....
de todas formas, ese ejemplo de la estructura es totalmente aplicable a las clases....
Título: Re: Oop
Publicado por: ibito en Miércoles 29 de Septiembre de 2004, 07:31
muchas gracias, a ver como lo aplico ahora :P
no, en serio si muchas gracias, casi no tengo tiempo por culpa de la escuela pero pues intentare hacerlo en cuanto pueda ;)
Título: Re: Oop
Publicado por: Ruben3d en Viernes 1 de Octubre de 2004, 13:59
En verdad, has planteado mal el objeto, BlackWind. Si el struct se puede inicializar con esa función sin parámetros (a parte del propio struct), el constructor del objeto debería llevar ese mismo código, por lo que no hace falta que tenga parámetros.
Volviendo a la pregunta original, ibito, te diré que para crear un juego uso objetos para casi todos (sólo tengo unas 4 ó 5 funciones muy específicas en 8k líneas de código). En el caso concreto de los jugadores y la escena, lo que hago es crear un grafo de escena que los manipule, algo como (muy simplificado):
Código: Text
class Escena
{
public:
AñadirEntidad(Entidad*, int categoria);
ActualizarEscena();
DibujarEscena();
TestPorColision(int categoria1, int categoria2, Accion*);
private:
lista de entidades;
}
AñadirEntidad añade a la escena una nueva entidad (jugador, enemigo, item, etc). Se puede añadir en cierta categoría (la utilidad se ve más abajo).
ActualizarEscena recorre todas las entidades actualizando sus posiciones y reaccionando ante sus interacciones (colisiones, por ejemplo).
DibujarEscena recorre todas las entidades y las dibuja.
TestPorColision lo que hace es comprobar si hay colision entre objetos de la categoria 1 y de la categoria 2 y, si la hay, llama a la funcion apuntada por Accion (me he pasado la sintaxis un poco por la gorra) para que realice la acción adecuada.
Por otro lado, el tipo básico de entidad puede ser
Código: Text
class Entidad
{
public:
Entidad(pos,vel,accel);
Actualizar();
Dibujar();
private:
Vector2D posicion, velocidad, aceleracion;
}
A partir de este tipo se pueden crear el resto por herencia, como el jugador (con métodos para modificar sus atributos de velocidad, etc), enemigos ,etc. Cuando detectes que el jugador ha pulsado la tecla de disparar, sólo tendrás que crear una nueva entidad del tipo Disparo, añadirla a la escena, y hacer su test de colisión. O cuando un jugador presione la tecla de avanzar, sólo has de llamar al método de su entidad que actualice su velocidad o aceleración, que ya al actualizarse la escena se actualizarán todas las posiciones.
Es importante que se haga por herencia, para que el polimorfismo cumpla su función y se puedan añadir los punteros a la escena.
Este es un modelo simplificado de uno en el que estoy trabajando. Puede ampliarse añadiendo una jerarquía entre las entidades de forma que el movimiento de una se compute de manera relativa al movimiento de otra, y no de manera absoluta, por lo que se pueden conseguir efectos bastante buenos. Te pongo aqui el .h de ésto (si ves cosas raras piensa que es algo inacabado y que le tengo que invertir aún trabajo). <!--xc1--></div><table border='0' align='center' width='95%' cellpadding='3' cellspacing='1'><tr><td>XCODE </td></tr><tr><td id='XCODE'><!--exc1-->// // scene.h //
Revisar la manera en que se gestionan las colisiones. - ¿Se detiene en la primera colisión o testea todas? ¿Configurable? - ¿Usar clases en vez de funciones para gestionar los eventos de colisión?
class CEntity { public: CEntity(const unsigned int class_id, // Identificador de clase de objeto const Vector2D &pos, // Posición del objeto const float angle, // Ángulo del objeto const Vector2D &vel, // Velocidad del objeto const float accel = 0.0f, // Aceleración del objeto. const float rotvel = 0.0f, // Velocidad de rotación const float life = AE_ENTITY_INMORTAL, // Vida de la entidad, en segundos const delete_type del_action = DELETE_ALL // Acción al ser borrado ) : m_class_id(class_id),m_pos(pos), m_angle(angle), m_vel(vel), m_accel(accel), m_rotvel(rotvel), m_ondelete(DELETE_ALL), m_parent(NULL), m_life(life), m_life_left(life), m_properties(AE_ENTITY_BASE) {};
virtual ~CEntity();
// Añade una subentidad void Attach(CEntity *pEntity, const attach_type type = INHERIT_ALL);
// Se des-atachea de donde esté. Deja de estar bajo control del SceneManager. // Se puede reinsertar si se quiere que vaya a otro sitio. //void DeAttach(); *** Por el momento esto no es posible ***
unsigned int GetClassID() {return m_class_id;} const Vector2D &GetPos() {return m_pos;} float GetAngle() {return m_angle;} const Vector2D &GetVel() {return m_vel;} float GetAccel() {return m_accel;} float GetRotVel() {return m_rotvel;} float GetLife() {return m_life;} float GetLifeLeft() {return m_life_left;} // Retorna la vida en el rango [0,1]. 1 es toda la vida y 0 es que ha agotado el // tiempo. float GetNormLifeLeft() {return 1.0f - (m_life-m_life_left)/(m_life);}
Vector2D m_pos; Vector2D m_vel; float m_accel; float m_angle; // 0º está a las 3. float m_rotvel; // Grados/segundo. Positivo en contra de las agujas del reloj.
CEntity *m_parent; // Puntero al padre en la jerarquía }; /* // Es un renombramiento, ya que una entidad base se puede usar como ayudante para // movimientos jerárquicos. Ya que no se pinta y no se testea por colisión es // relativamente rápido. typedef class CEntity CHelper; */
class CHelper : public CEntity { public: CHelper(const Vector2D &pos, // Posición del objeto const float angle, // Ángulo del objeto const Vector2D &vel, // Velocidad del objeto const float accel = 0.0f, // Aceleración del objeto. const float rotvel = 0.0f, // Velocidad de rotación const float life = AE_ENTITY_INMORTAL, // Vida de la entidad, en segundos const delete_type del_action = DELETE_ALL // Acción al ser borrado ) : CEntity(ID_HELPER, pos, angle, vel, accel, rotvel, life, del_action) {}; };
class CEntityDrawable : public CEntity { public: CEntityDrawable(const unsigned int class_id, // Identificador de clase de objeto CTexture *pTexture, // Textura const float width, const float height, const Vector2D &pos, // Posición del objeto const float angle, // Ángulo del objeto const Vector2D &vel, // Velocidad del objeto const float accel = 0.0f, // Aceleración del objeto. const float rotvel = 0.0f, // Velocidad de rotación const float life = AE_ENTITY_INMORTAL, const delete_type del_action = DELETE_ALL, // Acción al ser borrado const float alpha = 1.0f, // const unsigned int flip = Renderer::FLIP_NONE // ) : CEntity(class_id, pos, angle, vel, accel, rotvel, life, del_action), m_pTex(pTexture), m_width(width), m_height(height), m_alpha(alpha), m_flip(flip) { m_properties |= AE_ENTITY_DRAWABLE; };
/* ¿Qué sucede cuando se hace el Update() de Scene? Se recorre el árbol, actualizando las posiciones relativas de cada entidad, y se calculan las matrices de transformación de cada una. A continuación se ejecuta el buffer de testeos de colisiones. */ class Scene { public: ~Scene();
void ClearScene(); void ClearClassID(const unsigned int class_id);
// La añade a root void AddEntity(CEntity *pEntity);
// Cuando se quiera borrar una entidad se ha de usar este método void DestroyEntity(CEntity *pEntity);
// Añade la entidad al buffer de borrado. Se eliminará tras actualizar todas las // entidades y antes de ejecutar el buffer de colisiones. void AddToDeleteBuffer(CEntity *pEntity);
// Tras ejecutar el buffer hay q volver a llenarlo cada fotograma void AddCollisionTest(const unsigned int left_id, const unsigned int right_id, const PCOLLISION_FUNC pFunc);
void Update(); void Draw();
static Scene &Instance() {return m_scene;};
friend class CEntity;
private: static Scene m_scene;
/* ************* GRAFO DE ESCENA **************** */ // Nodos que cuelgan de la escena directamente std::list<CEntity*> m_root;
// Borra todos los descendientes de un elemento del árbol. Además los da de baja // en las listas y libera la memoria. void RemoveFromTreeCascade(CEntity *pEntity);
/* ************* LISTAS DE ENTIDADES **************** */ typedef struct st_entities_list_t { unsigned int class_id; std::list<CEntity*> entities; } entities_list_t; // Lista de listas de entidades, ordenadas por class_id std::list<entities_list_t> m_entities_lists;
// Lista de las entidades que se han de borrar std::list<CEntity*> m_delete_entities;
// Registra una entidad en las listas. void RegisterEntity(CEntity *pEntity); // Da de baja una entidad de las listas. NO LIBERA MEMORIA. void UnRegisterEntity(CEntity *pEntity);
/* ************** PILA DE MATRICES ************ */ // Pila de matrices, para manejarse de manera similar a OpenGL std::stack<Matrix4x4> m_matrixstack; void ResetStack(); void PushMatrix(); void PopMatrix(); const Matrix4x4 &GetMatrix();
/* ************** TRANSFORMACIONES ************ */ void LoadIdentity(); void Translate(const double x, const double y, const double z = 0.0); void RotateZ(const double angle); // En grados void PreTranslate(const double x, const double y, const double z = 0.0); void PreRotateZ(const double angle); // En grados
/* ************** BUFFER DE DETECCIÓN DE COLISIONES ********* */ typedef struct st_collision_test_t { unsigned int left_class_id; unsigned int right_class_id; PCOLLISION_FUNC pFunc; } collision_test_t; // Buffer de rutinas de colisión a ejecutar std::vector<collision_test_t> m_collision_buffer;
// Ejecuta el contenido del buffer (al acabar le hace un clear) void ExecuteCollisionBuffer(); // Ejecuta una prueba de colisión en concreto void CollisionTest(const unsigned int left_id, const unsigned int right_id, const PCOLLISION_FUNC pFunc);
/* ********************** OTRAS COSAS ********************** */ Scene(); Scene(const Scene &); };