Cutscene for Intro scene in Phaser 3.60 using tweens

I often see people complaining that it’s hard to google for “Phaser intro scene” because the keywords are so common, so maybe someone who is looking for this sort of thing will find it here.

const config = {
  width: 800,
  height: 600,
  scene: { preload, create },
  loader: {
    baseURL: "https://www.ticklemonster.com.au",
    crossOrigin: "anonymous"
  }
};

function preload () {
  this.load.image("intro1", "codepen/intro1.png");
  this.load.image("intro2", "codepen/intro2.png");
  this.load.image("intro3", "codepen/intro3.png");
  this.load.image("intro4", "codepen/intro4.png");
  this.load.image("intro5", "codepen/intro5.png");
  this.load.image("intro6", "codepen/intro6.png");
    }

function create () {
  let begimg = 320, begtxt = 450, endimg = 220, endtxt = 350, topimg = 120, toptxt = 220, botimg = 420, bottxt = 520; 
let startimgdelay = 5000, starttxtdelay = 0, txtdelay = 3500, imgdelay = 3500, enddelay = 1500; 
let leftcol = 150, midcol = 400, rightcol = 650; 

let script = [
    {"type":"text","style":{"fontSize":25,"color":"#fff"},"delay":starttxtdelay,"align":"center",
    "body": ["You are about to play a game","A different kind of game."],
    "beat": [
        {"x":midcol,"y":begtxt,"scale":0.3,"alpha":0,"colour":"53fca1","duration":0},
        {"x":midcol,"y":endtxt,"scale":1,"alpha":1,"duration":txtdelay,"ease":"power2","completeDelay":1000},
        {"x":leftcol,"y":toptxt,"scale":0.5,"alpha":0.6,"duration":enddelay,"ease":"power2"}
        ]
    },
    {"type":"image","texture":"intro1","size":[800,450],"delay":startimgdelay,"align":"center",
    "beat": [
        {"x":midcol,"y":begimg,"scale":0.3,"alpha":0,"duration":0},
        {"x":midcol,"y":endimg,"scale":1,"alpha":1,"duration":imgdelay,"ease":"power2","completeDelay":1000},
        {"x":leftcol,"y":topimg,"scale":0.5,"alpha":0.6,"duration":enddelay,"ease":"power2"}
        ]
    },
    {"type":"text","style":{"fontSize":25,"color":"#fff"},"delay":starttxtdelay,"align":"center",
    "body": ["This game was made","in Phaser 3.60!"],
    "beat": [
        {"x":midcol,"y":begtxt,"scale":0.3,"alpha":0,"colour":"53fca1","duration":0},
        {"x":midcol,"y":endtxt,"scale":1,"alpha":1,"duration":txtdelay,"ease":"power2","completeDelay":1000},
        {"x":midcol,"y":toptxt,"scale":0.5,"alpha":0.6,"duration":enddelay,"ease":"power2"}
        ]
    },
    {"type":"image","texture":"intro2","size":[800,450],"delay":startimgdelay,"align":"center",
    "beat": [
        {"x":midcol,"y":begimg,"scale":0.3,"alpha":0,"duration":0},
        {"x":midcol,"y":endimg,"scale":1,"alpha":1,"duration":imgdelay,"ease":"power2","completeDelay":1000},
        {"x":midcol,"y":topimg,"scale":0.5,"alpha":0.6,"duration":enddelay,"ease":"power2"}
        ]
    },
    {"type":"text","style":{"fontSize":25,"color":"#fff"},"delay":starttxtdelay,"align":"center",
    "body": ["That means it's cooler","than being cool."],
    "beat": [
        {"x":midcol,"y":begtxt,"scale":0.3,"alpha":0,"colour":"53fca1","duration":0},
        {"x":midcol,"y":endtxt,"scale":1,"alpha":1,"duration":txtdelay,"ease":"power2","completeDelay":1000},
        {"x":rightcol,"y":toptxt,"scale":0.5,"alpha":0.6,"duration":enddelay,"ease":"power2"}
        ]
    },
    {"type":"image","texture":"intro3","size":[800,450],"delay":startimgdelay,"align":"center",
    "beat": [
        {"x":midcol,"y":begimg,"scale":0.3,"alpha":0,"duration":0},
        {"x":midcol,"y":endimg,"scale":1,"alpha":1,"duration":imgdelay,"ease":"power2","completeDelay":1000},
        {"x":rightcol,"y":topimg,"scale":0.5,"alpha":0.6,"duration":enddelay,"ease":"power2"}
        ]
    },
    {"type":"text","style":{"fontSize":25,"color":"#fff"},"delay":starttxtdelay,"align":"center",
    "body": ["This intro makes it so cool","it could fight global warming."],
    "beat": [
        {"x":midcol,"y":begtxt,"scale":0.3,"alpha":0,"colour":"53fca1","duration":0},
        {"x":midcol,"y":endtxt,"scale":1,"alpha":1,"duration":txtdelay,"ease":"power2","completeDelay":1000},
        {"x":leftcol,"y":bottxt,"scale":0.5,"alpha":0.6,"duration":enddelay,"ease":"power2"}
        ]
    },
    {"type":"image","texture":"intro4","size":[800,450],"delay":startimgdelay,"align":"center",
    "beat": [
        {"x":midcol,"y":begimg,"scale":0.3,"alpha":0,"duration":0},
        {"x":midcol,"y":endimg,"scale":1,"alpha":1,"duration":imgdelay,"ease":"power2","completeDelay":1000},
        {"x":leftcol,"y":botimg,"scale":0.5,"alpha":0.6,"duration":enddelay,"ease":"power2"}
        ]
    },
    {"type":"text","style":{"fontSize":25,"color":"#fff"},"delay":starttxtdelay,"align":"center",
    "body": ["Okay, maybe that's","a bit too cool..."],
    "beat": [
        {"x":midcol,"y":begtxt,"scale":0.3,"alpha":0,"colour":"53fca1","duration":0},
        {"x":midcol,"y":endtxt,"scale":1,"alpha":1,"duration":txtdelay,"ease":"power2","completeDelay":1000},
        {"x":midcol,"y":bottxt,"scale":0.5,"alpha":0.6,"duration":enddelay,"ease":"power2"}
        ]
    },
    {"type":"image","texture":"intro5","size":[800,450],"delay":startimgdelay,"align":"center",
    "beat": [
        {"x":midcol,"y":begimg,"scale":0.3,"alpha":0,"duration":0},
        {"x":midcol,"y":endimg,"scale":1,"alpha":1,"duration":imgdelay,"ease":"power2","completeDelay":1000},
        {"x":midcol,"y":botimg,"scale":0.5,"alpha":0.6,"duration":enddelay,"ease":"power2"}
        ]
    },
    {"type":"text","style":{"fontSize":30,"color":"#fff"},"delay":starttxtdelay,"align":"center",
    "body": ["Let's just say it's","cooler than ice-cream."],
    "beat": [
        {"x":midcol,"y":begtxt,"scale":0.3,"alpha":0,"colour":"53fca1","duration":0},
        {"x":midcol,"y":endtxt,"scale":1,"alpha":1,"duration":txtdelay,"ease":"power2","completeDelay":1000},
        {"x":rightcol,"y":bottxt,"scale":0.5,"alpha":0.6,"duration":enddelay,"ease":"power2"}
        ]
    },
    {"type":"image","texture":"intro6","size":[800,450],"delay":startimgdelay,"align":"center",
    "beat": [
        {"x":midcol,"y":begimg,"scale":0.3,"alpha":0,"duration":0},
        {"x":midcol,"y":endimg,"scale":1,"alpha":1,"duration":imgdelay,"ease":"power2","completeDelay":1000},
        {"x":rightcol,"y":botimg,"scale":0.5,"alpha":0.6,"duration":enddelay,"ease":"power2"}
        ]
    }
    ];
  
    let actor = {}, seqtime = 0, beattime = 0, newtween;
    
    script.forEach((line, shot) => {
        switch(line.type)
            {
            case "text":
                actor[shot] = this.add.text(line.beat[0].x, line.beat[0].y, line.body, line.style)
                    .setColor("#"+line.beat[0].colour).setScale(line.beat[0].scale).setAlpha(line.beat[0].alpha)
                    .setAlign(line.align).setOrigin(0.5).setVisible(false);
                break;
            case "image":
                actor[shot] = this.add.sprite(line.beat[0].x, line.beat[0].y, line.texture)
                    .setScale(line.beat[0].scale).setAlpha(line.beat[0].alpha).setOrigin(0.5).setVisible(false);
                break;
            }

        beattime = seqtime;
        line.beat.forEach((beat) => {
            newtween = beat;
            newtween.targets = [actor[shot]];
            newtween.delay = beattime;
            beattime += beat.duration;
            if (newtween.colour)
                newtween.color = newtween.colour;
            newtween.onStart = () => {
                    actor[shot].setVisible(true);
                    };
            this.tweens.add(newtween);
            });
        seqtime += line.delay;
        });
    }

new Phaser.Game(config);

The details of the six images and text elements of the shots are obviously not the important bit here. it’s the set up of the relatively short Phaser code to run it, plus the structure of the JSON file which you would probably put in your preloader. I am running again with the theatre analogy here with the variable names, so that the JSON file is labelled as “script”, and each game object is an “actor” which has a “line” with one or more “beats” to hit.

It would be possible to set the whole thing up as a tween chain instead of normal tweens with staggered delays, but each object would need its own chain which would make the timing more complicated.

Exit mobile version