Talking about Card Game game, Game development, HTML5, Javascript and Phaser.
About three years ago I published a HTML5 card management prototype built with Phaser, and since developers are bringing back card games, here I am to update the prototype to Phaser 3, since the original post uses an old Phaser 2 version.
A deck of cards is shuffled and the first card is drawn on the table. Then you can swipe up to make next card appear above current card or swipe down to make next card appear below current card. Then next card takes the place of the current card which leaves the stage, and you can swipe to call the third card, and so on.
It could be useful for one of those games where players try to guess if next card will be higher or lower than current one.
Have a look at the prototype:
Swip up or down to draw next card.
The updated source code is also completely commented, for a clean understanding of the whole process:
// the game itself
let game;
// global object with game options
let gameOptions = {
// card width, in pixels
cardWidth: 334,
// card height, in pixels
cardHeight: 440,
// card scale. 1 = original size, 0.5 half size and so on
cardScale: 0.8
}
window.onload = function() {
let gameConfig = {
type: Phaser.AUTO,
backgroundColor: 0x4488aa,
scale: {
mode: Phaser.Scale.FIT,
autoCenter: Phaser.Scale.CENTER_BOTH,
parent: "thegame",
width: 750,
height: 1334
},
scene: playGame
}
game = new Phaser.Game(gameConfig);
window.focus();
}
// two constants for better understanding of "UP" and "DOWN"
const UP = -1;
const DOWN = 1;
class playGame extends Phaser.Scene {
constructor() {
super("PlayGame");
}
preload() {
// loading the sprite sheet with all cards
this.load.spritesheet("cards", "cards.png", {
frameWidth: gameOptions.cardWidth,
frameHeight: gameOptions.cardHeight
});
}
create() {
// can we swipe?
this.canSwipe = true;
// create an array with 52 integers from 0 to 51
this.deck = Phaser.Utils.Array.NumberArray(0, 51);
// shuffle the array
Phaser.Utils.Array.Shuffle(this.deck);
// the two cards in game
this.cardsInGame = [this.createCard(0), this.createCard(1)];
// we already have card 0 and 1 in game so next card index is 2
this.nextCardIndex = 2;
// a tween to make first card enter into play
this.tweens.add({
targets: this.cardsInGame[0],
x: game.config.width / 2,
duration: 500,
ease: "Cubic.easeOut"
});
// listener for player input
this.input.on("pointerup", this.checkSwipe, this);
}
// method to create a card, given an index
createCard(i) {
// the card itself, a sprite created outside the stage, on the left
let card = this.add.sprite(- gameOptions.cardWidth * gameOptions.cardScale, game.config.height / 2, "cards", this.deck[i]);
// scale the sprite
card.setScale(gameOptions.cardScale);
// return the card
return card;
}
// method to check if player input was a swipe
checkSwipe(e) {
// can the player swipe?
if(this.canSwipe) {
// determine swipe time, "release" timestamp minus "press" timestamp
let swipeTime = e.upTime - e.downTime;
// determine swipe vector
let swipe = new Phaser.Math. Vector2(e.upX - e.downX, e.upY - e.downY);
// get the magnitude, or length, of swipe vector
let swipeMagnitude = swipe.length();
// reduce the vector to a magnitude of 1
let swipeNormal = swipe.normalize();
// we have a vertical swipe when:
// * swipeMagnitude is bigger than 20, that is the player swiped for at least 20 pixels
// * swipeTime is less than 1 second, gestures longer than one second can't be considered swipes
// * the absolute value of the y component of the normal is 0.8
if(swipeMagnitude > 20 && swipeTime < 1000 && Math.abs(swipeNormal.y) > 0.8) {
// swiping down
if(swipeNormal.y > 0.8) {
this.handleSwipe(DOWN);
}
// swiping up
if(swipeNormal.y < -0.8) {
this.handleSwipe(UP);
}
}
}
}
// method to handle a swipe, given the direction
handleSwipe(direction) {
// we are swiping so we can't swipe anymore
this.canSwipe = false;
// which card are we moving?
let cardToMove = (this.nextCardIndex + 1) % 2;
// set moving card vertical position
this.cardsInGame[cardToMove].y += direction * gameOptions.cardHeight * gameOptions.cardScale * 1.1;
// tween the card to move to the horizontal center of the stage...
this.tweens.add({
targets: this.cardsInGame[cardToMove],
x: game.config.width / 2,
duration: 500,
ease: "Cubic.easeOut",
callbackScope: this,
onComplete: function() {
// ... then wait a second or little more...
this.time.addEvent({
delay: 1200,
callbackScope: this,
// ... then call moveCards method
callback: this.moveCards,
});
}
})
}
// method to update cards position
moveCards() {
// moving the first card
let cardToMove = this.nextCardIndex % 2;
// tween the card outside of the stage to the right
this.tweens.add({
targets: this.cardsInGame[cardToMove],
x: game.config.width + 2 * gameOptions.cardWidth * gameOptions.cardScale,
duration: 500,
ease: "Cubic.easeOut"
});
// moving the second card
cardToMove = (this.nextCardIndex + 1) % 2;
// tween the card to the center of the stage...
this.tweens.add({
targets: this.cardsInGame[cardToMove],
y: game.config.height / 2,
duration: 500,
ease: "Cubic.easeOut",
callbackScope: this,
onComplete: function(){
// ... then recycle the card which we moved outside the screen
cardToMove = this.nextCardIndex % 2;
this.cardsInGame[cardToMove].setFrame(this.deck[this.nextCardIndex]);
this.nextCardIndex = (this.nextCardIndex + 1) % 52;
this.cardsInGame[cardToMove].x = gameOptions.cardWidth * gameOptions.cardScale / -2;
// now we can swipe again
this.canSwipe = true;
}
});
}
}
Inside the source code of the project you will also find a PSD file with the card spritesheet, courtesy of game-icons.net, but keep in mind the texture I used is just for this test and it’s way too big for most, if not all, mobile devices.
Never miss an update! Subscribe, and I will bother you by email only when a new game or full source code comes out.