En aquest tema, aprendrem a construir un joc simple utilitzant OpenGL. Aquest projecte ens permetrà aplicar molts dels conceptes apresos en els mòduls anteriors, com ara la renderització de formes, textures, il·luminació i més. El joc que crearem serà un senzill joc de "pong", on dos jugadors controlen pales per colpejar una pilota.

Objectius del Tema

  • Aplicar conceptes bàsics i avançats d'OpenGL en un projecte pràctic.
  • Aprendre a estructurar un projecte de joc.
  • Implementar la lògica del joc i la interacció amb l'usuari.
  • Gestionar la renderització i l'actualització de l'estat del joc.

Requisits Previs

  • Coneixements bàsics d'OpenGL.
  • Familiaritat amb la programació en C++ (o el llenguatge que estiguis utilitzant).
  • Entorn de desenvolupament configurat per a OpenGL.

Passos per Construir el Joc

  1. Configuració del Projecte

Abans de començar a codificar, assegura't que el teu entorn de desenvolupament estigui configurat correctament per a OpenGL. Si no ho has fet, revisa el mòdul "Configurar el Teu Entorn de Desenvolupament".

  1. Estructura del Projecte

Organitzarem el nostre projecte en diversos fitxers per mantenir el codi net i manejable.

/pong
  ├── src
  │   ├── main.cpp
  │   ├── game.cpp
  │   ├── game.h
  │   ├── paddle.cpp
  │   ├── paddle.h
  │   ├── ball.cpp
  │   ├── ball.h
  ├── shaders
  │   ├── vertex_shader.glsl
  │   ├── fragment_shader.glsl
  ├── textures
  │   ├── paddle.png
  │   ├── ball.png
  ├── CMakeLists.txt

  1. Implementació del Joc

3.1. main.cpp

Aquest fitxer contindrà el punt d'entrada del nostre programa i la configuració inicial d'OpenGL.

#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include "game.h"

int main() {
    // Inicialitzar GLFW
    if (!glfwInit()) {
        return -1;
    }

    // Crear una finestra
    GLFWwindow* window = glfwCreateWindow(800, 600, "Pong", NULL, NULL);
    if (!window) {
        glfwTerminate();
        return -1;
    }

    // Establir el context d'OpenGL
    glfwMakeContextCurrent(window);

    // Inicialitzar GLEW
    if (glewInit() != GLEW_OK) {
        return -1;
    }

    // Configurar el joc
    Game game;
    game.init();

    // Bucle principal
    while (!glfwWindowShouldClose(window)) {
        // Actualitzar l'estat del joc
        game.update();

        // Renderitzar el joc
        game.render();

        // Intercanviar els buffers
        glfwSwapBuffers(window);

        // Processar els esdeveniments
        glfwPollEvents();
    }

    // Alliberar recursos
    glfwTerminate();
    return 0;
}

3.2. game.h i game.cpp

Aquestes classes gestionaran la lògica principal del joc.

game.h

#ifndef GAME_H
#define GAME_H

class Game {
public:
    void init();
    void update();
    void render();
};

#endif

game.cpp

#include "game.h"
#include "paddle.h"
#include "ball.h"

// Objectes del joc
Paddle player1, player2;
Ball ball;

void Game::init() {
    // Inicialitzar objectes del joc
    player1.init(50, 300);
    player2.init(750, 300);
    ball.init(400, 300);
}

void Game::update() {
    // Actualitzar l'estat del joc
    player1.update();
    player2.update();
    ball.update();
}

void Game::render() {
    // Esborrar la pantalla
    glClear(GL_COLOR_BUFFER_BIT);

    // Renderitzar objectes del joc
    player1.render();
    player2.render();
    ball.render();
}

3.3. paddle.h i paddle.cpp

Aquestes classes gestionaran les pales del joc.

paddle.h

#ifndef PADDLE_H
#define PADDLE_H

class Paddle {
public:
    void init(float x, float y);
    void update();
    void render();
private:
    float x, y;
};

#endif

paddle.cpp

#include "paddle.h"
#include <GL/glew.h>

void Paddle::init(float x, float y) {
    this->x = x;
    this->y = y;
}

void Paddle::update() {
    // Actualitzar la posició de la pala
    // (Afegeix la lògica de moviment aquí)
}

void Paddle::render() {
    // Renderitzar la pala
    glBegin(GL_QUADS);
    glVertex2f(x - 10, y - 50);
    glVertex2f(x + 10, y - 50);
    glVertex2f(x + 10, y + 50);
    glVertex2f(x - 10, y + 50);
    glEnd();
}

3.4. ball.h i ball.cpp

Aquestes classes gestionaran la pilota del joc.

ball.h

#ifndef BALL_H
#define BALL_H

class Ball {
public:
    void init(float x, float y);
    void update();
    void render();
private:
    float x, y;
    float vx, vy;
};

#endif

ball.cpp

#include "ball.h"
#include <GL/glew.h>

void Ball::init(float x, float y) {
    this->x = x;
    this->y = y;
    this->vx = 0.1f;
    this->vy = 0.1f;
}

void Ball::update() {
    // Actualitzar la posició de la pilota
    x += vx;
    y += vy;

    // Rebotar en les parets
    if (y <= 0 || y >= 600) {
        vy = -vy;
    }
}

void Ball::render() {
    // Renderitzar la pilota
    glBegin(GL_QUADS);
    glVertex2f(x - 10, y - 10);
    glVertex2f(x + 10, y - 10);
    glVertex2f(x + 10, y + 10);
    glVertex2f(x - 10, y + 10);
    glEnd();
}

  1. Afegir Interacció amb l'Usuari

Per permetre als jugadors controlar les pales, afegirem la lògica de moviment en la funció update de la classe Paddle.

paddle.cpp

#include "paddle.h"
#include <GL/glew.h>
#include <GLFW/glfw3.h>

void Paddle::update() {
    // Controlar la pala amb les tecles
    if (glfwGetKey(glfwGetCurrentContext(), GLFW_KEY_W) == GLFW_PRESS) {
        y += 0.1f;
    }
    if (glfwGetKey(glfwGetCurrentContext(), GLFW_KEY_S) == GLFW_PRESS) {
        y -= 0.1f;
    }
}

  1. Afegir Col·lisions

Per fer el joc més interessant, afegirem col·lisions entre la pilota i les pales.

ball.cpp

#include "ball.h"
#include "paddle.h"
#include <GL/glew.h>

extern Paddle player1, player2;

void Ball::update() {
    // Actualitzar la posició de la pilota
    x += vx;
    y += vy;

    // Rebotar en les parets
    if (y <= 0 || y >= 600) {
        vy = -vy;
    }

    // Col·lisions amb les pales
    if ((x <= player1.x + 10 && y >= player1.y - 50 && y <= player1.y + 50) ||
        (x >= player2.x - 10 && y >= player2.y - 50 && y <= player2.y + 50)) {
        vx = -vx;
    }
}

  1. Afegir Textures i Shaders

Per millorar l'aparença del joc, podem afegir textures i shaders. Això es pot fer seguint els passos descrits en els mòduls anteriors sobre textures i shaders.

  1. Conclusió

En aquest tema, hem creat un joc simple utilitzant OpenGL. Hem après a estructurar un projecte de joc, implementar la lògica del joc, gestionar la renderització i afegir interacció amb l'usuari. Aquest projecte ens ha permès aplicar molts dels conceptes apresos en els mòduls anteriors i ens ha proporcionat una base sòlida per a projectes més complexos en el futur.

Exercicis Pràctics

  1. Afegir Puntuació: Implementa un sistema de puntuació que incrementi cada vegada que un jugador falla en colpejar la pilota.
  2. Millorar la Lògica de Moviment: Ajusta la velocitat de la pilota i les pales per fer el joc més equilibrat.
  3. Afegir Sons: Integra efectes de so per a col·lisions i punts utilitzant una llibreria d'àudio com OpenAL.

Solucions dels Exercicis

  1. Afegir Puntuació

// game.h
class Game {
public:
    void init();
    void update();
    void render();
private:
    int score1, score2;
};

// game.cpp
void Game::init() {
    score1 = 0;
    score2 = 0;
    // Inicialitzar objectes del joc
    player1.init(50, 300);
    player2.init(750, 300);
    ball.init(400, 300);
}

void Game::update() {
    // Actualitzar l'estat del joc
    player1.update();
    player2.update();
    ball.update();

    // Comprovar si la pilota surt de la pantalla
    if (ball.x <= 0) {
        score2++;
        ball.init(400, 300);
    } else if (ball.x >= 800) {
        score1++;
        ball.init(400, 300);
    }
}

void Game::render() {
    // Esborrar la pantalla
    glClear(GL_COLOR_BUFFER_BIT);

    // Renderitzar objectes del joc
    player1.render();
    player2.render();
    ball.render();

    // Renderitzar la puntuació
    // (Afegeix el codi per renderitzar el text aquí)
}

  1. Millorar la Lògica de Moviment

// paddle.cpp
void Paddle::update() {
    // Controlar la pala amb les tecles
    if (glfwGetKey(glfwGetCurrentContext(), GLFW_KEY_W) == GLFW_PRESS) {
        y += 0.2f;
    }
    if (glfwGetKey(glfwGetCurrentContext(), GLFW_KEY_S) == GLFW_PRESS) {
        y -= 0.2f;
    }
}

// ball.cpp
void Ball::init(float x, float y) {
    this->x = x;
    this->y = y;
    this->vx = 0.15f;
    this->vy = 0.15f;
}

  1. Afegir Sons

Per afegir sons, pots utilitzar una llibreria com OpenAL. Aquí tens un exemple bàsic de com integrar OpenAL per reproduir un so de col·lisió.

#include <AL/al.h>
#include <AL/alc.h>

// Inicialitzar OpenAL
ALCdevice* device = alcOpenDevice(NULL);
ALCcontext* context = alcCreateContext(device, NULL);
alcMakeContextCurrent(context);

// Carregar el so
ALuint buffer;
alGenBuffers(1, &buffer);
// (Carrega el fitxer de so en el buffer aquí)

// Crear una font de so
ALuint source;
alGenSources(1, &source);
alSourcei(source, AL_BUFFER, buffer);

// Reproduir el so en col·lisió
if ((x <= player1.x + 10 && y >= player1.y - 50 && y <= player1.y + 50) ||
    (x >= player2.x - 10 && y >= player2.y - 50 && y <= player2.y + 50)) {
    vx = -vx;
    alSourcePlay(source);
}

Amb aquests exercicis i solucions, hauràs millorat el teu joc de pong i hauràs après a aplicar conceptes avançats d'OpenGL en un projecte pràctic.

© Copyright 2024. Tots els drets reservats