Skip to content

Latest commit

 

History

History
241 lines (212 loc) · 12.9 KB

README.md

File metadata and controls

241 lines (212 loc) · 12.9 KB

PROYECTO DE INVESTIGACIÓN DE PMUD

MARIO BROS CON CREATEJS

N|Solid

Proyecto de investigación de la asignatura de PMUD, EPSEVG-UPC.

  • Autor: Mario Kochan
  • Fecha: 18/11/2022
  • Profesor: Jordi Estve

Características

Despliegue y uso de CreateJS

CreateJS es un conjunto de librerías que nos permite realizar diferentes modificaciones a los objetos HTML presentes en nuestra página web. Esta librería nos permite hacer videojuegos para navegador, que es lo que haremos en este trabajo de investigación. Descubriremos como funciona un Game engine de este tipo, viendo sus debilidades y sus puntos fuertes. Aparte de la memoria redactada aquí, también está hecho el siguiente PowerPoint presentado en clase.

Instalación

Para poder usar la librería CreateJS, primer crearemos un index.html en el cual inicilizaremos la librería de JavaScript y el archivo .js principal.

    <script src="https://code.createjs.com/1.0.0/createjs.min.js"></script>
    <script src="main.js"></script>

A continuación, crearemos el canvas sobre cual trabajaremos y indicamos que el cargar el body se cargue la función init() que está inicializada en nuestro main.js.

    <body onload="init()">
        <canvas id="gameCanvas" width="600" height="300"  style="display: block; margin: 0 auto;">
        </canvas>
    </body>

CUIDADO: Para poder abrir nuestro index.html, primero deberemos crear nuestor main.js. Una vez creado nuestro main.js, el index.html se podrá abrir creando un servidor local que escuche por algun puerto. Esto se puede hacer con python. Dentro de la carpeta donde está el index.html, llamamos a esta comanda de python:

python3 -m http.server

main.js

El main.js es un archivo bastante extenso de diferentes funciones agrupadas. Tenemos primero el init(), que es la función encargada de inicializar todo. Dentro del init() podemos ver como se inicializa el escenario, los framerates y como se cargan las sheets de los elementos de este. Además de eso, usamos la libreria SoundJS para cargar la música principal de nuestra copia de Mario Bros.

Inicialización del escenario con la música:

    stage = new createjs.Stage("gameCanvas");

    // Añadimos framerate 
    createjs.Ticker.timingMode=createjs.Ticker.RAF_SYNCHED;
    createjs.Ticker.framerate=60;
    // Añadimos evento para actualizar cada tick
    createjs.Ticker.addEventListener("tick", stage);

    createjs.Sound.alternateExtensions = ["mp3"];
    createjs.Sound.addEventListener("fileload", music_loader);
    createjs.Sound.registerSound("/musica/ost.ogg", "/musica/");

Inicialización de los sheets del escenario:

  var manifest = [
            { "src": "mario.png", "id": "mario" },
            { "src": "nube.png", "id": "nube"},
            { "src": "bloque.png", "id": "bloque"},
            { "src": "suelo.png", "id": "suelo"},
            { "src": "bicho.png", "id": "tortuga"},
            { "src": "bicho_2.png", "id": "tortuga2"},
            { "src": "tubo.png", "id": "tubo"},
            { "src": "bloque_incognito.png", "id": "bloque_incognito"},
            { "src": "bloque_incognito_cambiado.png", "id": "bloque_cognito"},
            { "src": "saul_goodman.png", "id": "saul_goodman"},
            { "src": "meme.png", "id": "meme"}
        ];
    loader = new createjs.LoadQueue(true);
    // añadimos evento para cargar imagenes.
    loader.addEventListener("complete", handleComplete);
    loader.loadManifest(manifest, true, "./img/");

Una vez cargados los elementos del escenario, se ejecuta la función handleComplete que se encargará de llamar a diferentes funciones para colocar cada elemento en su escenario correspondiente:

    function handleComplete(){
        createSuelo();
        createNubes();
        createTubo();
        createBloques();
        createMario();
        createTortuga();
        createjs.Ticker.addEventListener("tick", checkCollision)
        createjs.Ticker.addEventListener("tick", tick);
    }

Para ejecutar los diferntes sonidos que tenemos cargados, debemos llamar a funciones que se encarguen de reproducir las canciones que se le pasen por el parámetro event:

    var musica_de_fondo;
    function music_loader(event) {
        musica_de_fondo = createjs.Sound.play(event.src);
        musica_de_fondo.volume = 0.5;
    }
    
    function end_loader(event) {
        createjs.Sound.removeSound("/musica/ost.ogg");
        let instance = createjs.Sound.play(event.src);
        instance.volume = 0.5;
    }
    
    function sorpresa_loader(event){
        let instance = createjs.Sound.play(event.src);
        instance.volume = 0.5;
    }

A continuación tenemos el sistema de físicas del juego. Es algo muy básico: Tratamos cada objeto como un conjunto de puntos que crean un rectangulo de diferentes formas. Generamos, de 2 formas diferentes, una bloque de códigos que saltan en el momento que 2 espacios de coordenadas de diferentes bloques coinciden. En la función checkCollision(), se puede ver como se comprueban de las dos formas difernetes si dos objetos están colisionando.

  • Tenemos el ejemplo del objeto mario con el tubo, que en caso de coincidir sus espacios, tratamos de adaptar mario al terreno.
    if(detect_object_collision(mario,tubo)){
        if(mario.y != tubo.y-tubo.image.height ){
            if(tubo.x > mario.x) poder_avanzar_xder=false;
            if(tubo.x < mario.x) poder_avanzar_xizq=false;
            if(mario.y < tubo.y - tubo.image.height/2){
                mario.y = tubo.y-tubo.image.height;
                createjs.Tween.get(mario, {override:true}).to({y: mario.y}, 300, createjs.Ease.linear())
            }
        }
        else{
            if(!poder_avanzar_xder)
            poder_avanzar_xder=true;
            if(!poder_avanzar_xizq)
            poder_avanzar_xizq=true;
        }
    }
    else{
        if(mario.y == tubo.y-tubo.image.height){
            mario.y = 200-mario.image.height/2;
            createjs.Tween.get(mario, {override:true}).to({y: mario.y}, 300, createjs.Ease.linear())
        }
        if(!poder_avanzar_xder)
        poder_avanzar_xder=true;
        if(!poder_avanzar_xizq)
        poder_avanzar_xizq=true;
    }
  • Tenemos el ejemplo del objeto mario con el bicho, que en caso de coincidr sus espacios, finaliza la partida.
    var leftX= mario.x - mario.regX + 5;
    var leftY= mario.y - mario.regY + 5;
    var points = [
        new createjs.Point(leftX, leftY),
        new createjs.Point(leftX + mario.image.width-10, leftY),
        new createjs.Point(leftX, leftY + mario.image.height-10),
        new createjs.Point(leftX + mario.image.width-10, leftY+mario.image.height-10)
    ];
    for(var i=0; i<points.length; i++){
        var objects = stage.getObjectsUnderPoint(points[i].x, points[i].y);
        if(objects.filter((object)=>object.name == "tortuga").length > 0){
            gameOver();
            return;
        }
    }

Para mover el personaje, hacemos uso de la funcion keydown event, que es una función del propio JavaScript que nos permite reaccionar ante el accionamiento de una tecla:

    var keypress=true;
    document.addEventListener("keydown", (event) => {
        if(!endgame){
            switch(event.key){
                case 'd' || 'ArrowRight':
                    mario.scaleX = 1;
                    right=true
                    createjs.Ticker.addEventListener("tick", tick_mario);
                    break;
                case 'a' || 'ArrowLeft':
                    mario.scaleX = -1;
                    right=false
                    createjs.Ticker.addEventListener("tick", tick_mario);
                    break;
                case ' ':
                    if (keypress == true){
                        keypress=false;
                        pos_inicial_y=mario.y;
                        if(mario.x>bloque_incognito.x-5 && mario.x-5<bloque_incognito.x+bloque_incognito.image.width){
                            createjs.Tween.get(mario).to({y: 5+bloque_incognito.y+bloque_incognito.image.height}, 150, createjs.Ease.linear()).to({y: 200-mario.image.height/2}, 300, createjs.Ease.linear());
                            create_saul_goodman();
                        }
                        else{
                            createjs.Tween.get(mario).to({y: mario.y-60}, 300, createjs.Ease.linear()).to({y: 200-mario.image.height/2}, 300, createjs.Ease.linear());
                        }
                        setTimeout(()=>{
                            keypress=true;
                        }, 600)
                    }
                    break;
            }
        }
    });

Como se puede ver, aquí hacemos uso de la librería TweenJS para realizar la animación del salto de Mario.

Por último, tenemos la función GameOver, que cuando se llama se congela el juego y se da por entendido que ha finalizado:

    function gameOver(){
        createjs.Tween.removeAllTweens();
        createjs.Ticker.removeEventListener("tick", checkCollision);
        createjs.Ticker.removeEventListener("tick", tick);
        createjs.Sound.alternateExtensions = ["mp3"];
        createjs.Sound.addEventListener("fileload", end_loader);
        createjs.Sound.registerSound("/musica/death.ogg", "/musica/");
        endgame=true;
    }

Sheets

Dentro de la carepta /img se pueden ver los sheets usados para crear el escenario. Además, en /sheets se pueden ver los sheets completos. Para editar los sheets, se ha hecho uso de la página web ezgif

Música

Dentro de la carpeta /musica están los sonidos usados para hacer el videojuego. He usado un convertidor online de youtube a ogg para obtener los archivos.

Curiosidad

Como meme para la demostración, el bloque sorpresa hace una cosa distinta a lo que suele hacer. Esta hecho a próposito.

Fotos ingame

N!SOLID N|SOLID