Skip to content
Snippets Groups Projects
Commit 9de54feb authored by jeremy.gobet's avatar jeremy.gobet
Browse files

Initial commit

parents
No related branches found
No related tags found
No related merge requests found
# ignore all files starting with . or ~
.*
~*
# ignore node/grunt dependency directories
node_modules/
# ignore packaged files
*.7z
*.dmg
*.gz
*.iso
*.jar
*.rar
*.tar
*.zip
# ignore private/secret files
*.der
*.key
*.pem
# track these files, if they exist
!.gitignore
dist/
# Breakout
Jeu du casse brique réalisé sur Phaser.js.
## Sources
Les fichiers sources du programme se trouve dans le dossier **src**.
## Installation
Installer les dépendances du projet avec NPM. La commande crée un dossier **node_modules** contenant les dépendances du projet:
`npm install`
## Exécution en local
La commande ci-dessous transpile le projet et exécute automatiquement le jeu dans un onglet de votre navigateur :
`npm run start`
Pour modifier l'exécution du programme en local voir le fichier **webpack.dev.js**.
## Build
Le build du projet est effectué dans le dossier **dist** créé à la première exécution de la commande ci-dessous:
`npm run build`
Pour modifier la transilation du programme pour la production, voir le fichier **webpack.prod.js**
## Test
exécutez les tests unitaires présent dans le dossier **test** avec la commande suivante:
`npm run test`
## Lint
Exécuter ESLint pour vérifier les règles sur le code source en TypeScript:
`npx eslint src/**/**.ts`
## Deploy
Pour déployer sur Firebase il faut au préalable faire la configuration et créer un nouveau projet Firebase. Toutes les étapes sont décritent dans le fichier **firebase_setup.md**
Déployer le projet sur Firebase:
`firebase deploy`
module.exports = {
presets: [
['@babel/preset-env', {targets: {node: 'current'}}],
'@babel/preset-typescript',
],
};
Pour setup Firebase.
1. Créer un projet firebase depuis [la console firebase](https://console.firebase.google.com/u/0/)
2. Installer sur votre machine les outils `firebase` avec la commande :
```bash
npm install -g firebase-tools
```
3. Log in depuis la `CLI` en utilisant le __même compte__ que celui utilisé pour créer le projet `firebase` en utilisant la commande
```bash
firebase login
```
4. Puis utiliser cette commande pour initialiser `firebase` depuis le répertoire contenant le frontend.
```
firebase init
```
Dans la `CLI` interative, sélectionner :
Hosting: Configure files for Firebase Hosting and (optionally) set up GitHub Action deploys
Choisir "Use an existing project" et choisir le projet créé préalablement.
Le `public directory` indique le sous-répertoire où se trouveront les fichiers à déployer. Par convention web, nous utiliserons `/dist/<nom_du_projet>` : C'est ici que le framework angular génère des fichiers lors du build.
Indiquer "yes" à l'option de génération sous forme de `single-page app`.
Puis ne pas automatiquement générer des `GitHub Action deploys` puisque nous utiliserons GitLab.
Cette commande crée alors un fichier `.json` qui doit se trouver à la racine de votre projet frontend. Ce dernier contient les détails du hosting pour firebase.
5. Pour push depuis une pipeline CI/CD, nous avons besoin de commandes non interactives, or la commande `firebase login` est interactive et ne fonctionnera pas depuis la pipeline. Nous allons alors générer un `token` que nous pourrons utiliser pour déployer le projet en nous authentifiant, sans passer par la commande `login`. Utiliser la commande suivante pour générer un token :
```bash
firebase login:ci
```
Le `token` retourné doit être noté puisqu'il sera nécessaire pour le déploiement.
6. Finallement, pour vérifier votre installation : `build` le projet angular en utilisant `ng build --aot`, et déployer le contenu en utilisant la commande :
```bash
firebase deploy --token <token>
```
En remplaçant `<token>` par le votre, obtenu à l'étape 5. `Firebase` déploie alors le contenu du dossier indiqué lors de la commande `init`, en s'authentifiant à l'aide de votre `token`. La `CLI` vous retourne l'adresse à laquelle votre projet est disponible.
Il est désormait possible d'utiliser cette commande `deploy` dans votre CI/CD pour mettre à jour le frontend à l'exécution de votre pipeline, après avoir installé les outils `firebase` sur le runner. Attention à ne pas hardcoder votre `token` !!! (très mauvaise pratique !), pensez bien à la variabiliser depuis l'interface graphique de `gitlab` et à limiter son accès. Dans votre `.gitlab-ci.yml` remplacez la valeur du `token` par votre variable.
{
"name": "breakout",
"version": "1.0.0",
"description": "",
"main": "main.js",
"scripts": {
"test": "jest",
"start": "webpack-dev-server --open --config webpack.dev.js",
"build": "webpack --config webpack.prod.js"
},
"author": "",
"license": "",
"dependencies": {
"firebase": "^9.0.1",
"phaser": "^3.55.2"
},
"devDependencies": {
"@babel/core": "^7.20.5",
"@babel/preset-env": "^7.20.2",
"@babel/preset-typescript": "^7.18.6",
"@typescript-eslint/eslint-plugin": "^5.45.1",
"@typescript-eslint/parser": "^5.45.1",
"babel-jest": "^29.3.1",
"clean-webpack-plugin": "^3.0.0",
"copy-webpack-plugin": "^6.0.2",
"eslint": "^8.29.0",
"jest": "^29.3.1",
"ts-loader": "^7.0.5",
"typescript": "^3.9.5",
"webpack": "^5.75.0",
"webpack-cli": "^5.0.1",
"webpack-dev-server": "^4.11.1",
"webpack-merge": "^4.2.2"
}
}
{"frames": {
"ball1":
{
"frame": {"x":452,"y":2,"w":22,"h":22},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":22,"h":22},
"sourceSize": {"w":22,"h":22}
},
"ball2":
{
"frame": {"x":476,"y":2,"w":22,"h":22},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":22,"h":22},
"sourceSize": {"w":22,"h":22}
},
"blue1":
{
"frame": {"x":386,"y":2,"w":64,"h":32},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":64,"h":32},
"sourceSize": {"w":64,"h":32}
},
"blue2":
{
"frame": {"x":108,"y":53,"w":64,"h":32},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":64,"h":32},
"sourceSize": {"w":64,"h":32}
},
"button":
{
"frame": {"x":2,"y":2,"w":190,"h":49},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":190,"h":49},
"sourceSize": {"w":190,"h":49}
},
"buttonOver":
{
"frame": {"x":194,"y":2,"w":190,"h":49},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":190,"h":49},
"sourceSize": {"w":190,"h":49}
},
"green1":
{
"frame": {"x":2,"y":79,"w":64,"h":32},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":64,"h":32},
"sourceSize": {"w":64,"h":32}
},
"green2":
{
"frame": {"x":174,"y":53,"w":64,"h":32},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":64,"h":32},
"sourceSize": {"w":64,"h":32}
},
"paddle1":
{
"frame": {"x":386,"y":36,"w":104,"h":24},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":104,"h":24},
"sourceSize": {"w":104,"h":24}
},
"paddle2":
{
"frame": {"x":2,"y":53,"w":104,"h":24},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":104,"h":24},
"sourceSize": {"w":104,"h":24}
},
"particle1":
{
"frame": {"x":492,"y":26,"w":10,"h":10},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":10,"h":10},
"sourceSize": {"w":10,"h":10}
},
"particle2":
{
"frame": {"x":492,"y":38,"w":10,"h":10},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":10,"h":10},
"sourceSize": {"w":10,"h":10}
},
"particle3":
{
"frame": {"x":68,"y":79,"w":20,"h":19},
"rotated": false,
"trimmed": true,
"spriteSourceSize": {"x":0,"y":1,"w":20,"h":19},
"sourceSize": {"w":20,"h":21}
},
"purple1":
{
"frame": {"x":240,"y":53,"w":64,"h":32},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":64,"h":32},
"sourceSize": {"w":64,"h":32}
},
"purple2":
{
"frame": {"x":306,"y":53,"w":64,"h":32},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":64,"h":32},
"sourceSize": {"w":64,"h":32}
},
"red1":
{
"frame": {"x":372,"y":62,"w":64,"h":32},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":64,"h":32},
"sourceSize": {"w":64,"h":32}
},
"red2":
{
"frame": {"x":438,"y":62,"w":64,"h":32},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":64,"h":32},
"sourceSize": {"w":64,"h":32}
},
"silver1":
{
"frame": {"x":90,"y":87,"w":64,"h":32},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":64,"h":32},
"sourceSize": {"w":64,"h":32}
},
"silver2":
{
"frame": {"x":156,"y":87,"w":64,"h":32},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":64,"h":32},
"sourceSize": {"w":64,"h":32}
},
"yellow1":
{
"frame": {"x":222,"y":87,"w":64,"h":32},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":64,"h":32},
"sourceSize": {"w":64,"h":32}
},
"yellow2":
{
"frame": {"x":288,"y":87,"w":64,"h":32},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":64,"h":32},
"sourceSize": {"w":64,"h":32}
}},
"meta": {
"app": "http://www.codeandweb.com/texturepacker",
"version": "1.0",
"image": "breakout.png",
"format": "RGBA8888",
"size": {"w":504,"h":121},
"scale": "1",
"smartupdate": "$TexturePacker:SmartUpdate:001f51c9029c40bfd47dffb21081606f:e188858ff10f5876d9b056ceae3435f7:aa94e37632703bccd6a813800bf3159a$"
}
}
src/assets/breakout.png

8.4 KiB

import Ball from './scripts/ball';
import Bricks from './scripts/bricks';
import Paddle from './scripts/paddle';
export default class Breakout extends Phaser.Scene {
bricks: Bricks;
paddle: Paddle;
ball: Ball;
constructor(config: Phaser.Types.Core.GameConfig) {
super(config);
}
// Function de PhaserJS permettant de charger toutes les ressources nécessaires
preload() {
this.load.atlas('assets', 'assets/breakout.png', 'assets/breakout.json');
}
// Function de PhaserJS pour définir l'état initial de la scène
create() {
this.bricks = new Bricks(this);
this.paddle = new Paddle(this);
this.ball = new Ball(this);
// Collisions
// https://photonstorm.github.io/phaser3-docs/Phaser.Physics.Arcade.Factory.html#collider__anchor
this.physics.add.collider(this.ball.ballImage, this.bricks.group, this.bricks.hitBrick.bind(this.bricks), null, this);
this.physics.add.collider(this.ball.ballImage, this.paddle.image, this.ball.hitPaddle.bind(this.paddle), null, this);
// Active les collisions sur la bordure sauf au sol
this.physics.world.setBoundsCollision(true, true, true, false);
// Evénements
this.bricks.on("allBricksDestroyedEvent", this.resetLevel.bind(this));
this.paddle.on("paddleMovedEvent", this.ball.paddleMoved.bind(this.ball));
this.ball.on("outOfBoundsEvent", this.outOfBounds.bind(this));
}
// Function de PhaserJS pour la mise à jour de la scène (Boucle de gameplay)
update() {
this.ball.update();
}
resetLevel() {
this.ball.resetPosition(this.paddle.image);
this.bricks.reset();
}
outOfBounds() {
this.ball.resetPosition(this.paddle.image);
}
};
<!DOCTYPE html>
<html lang="fr">
<head>
<title>Phaser3 - Typescript</title>
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
<meta charset="utf-8">
</head>
<body>
<div id="phaser-canvas"></div>
<script src="bundle.js"></script>
</body>
</html>
import 'phaser';
import Breakout from './breakout';
/*
Configuration de PhaserJS
https://photonstorm.github.io/phaser3-docs/Phaser.Types.Core.html#.GameConfig
*/
const config: Phaser.Types.Core.GameConfig = {
type: Phaser.AUTO, // AUTO, WEBGL ou CANVAS
width: 800, height: 600, // Taille du canvas
parent: 'phaser-canvas', // élement HTML qui contiendra le canvas
scene: [ Breakout ], // JS Classe contenant les fonctions: preload, create, update
physics: { // Configuration de la physique
default: 'arcade' // arcade ou
}
};
var game = new Phaser.Game(config);
const VELOCITY_FACTOR = 10;
// https://photonstorm.github.io/phaser3-docs/Phaser.GameObjects.GameObject.html
export default class Ball extends Phaser.GameObjects.GameObject {
ballImage: Phaser.Physics.Arcade.Image;
onPaddle: Boolean;
constructor(scene: Phaser.Scene) {
super(scene, "ball");
this.create();
}
create() {
const canvas = this.scene.game.canvas;
this.ballImage = this.scene.physics.add.image(canvas.width / 2, canvas.height - 100, 'assets', 'ball1')
.setCollideWorldBounds(true)
.setBounce(1);
this.onPaddle = true;
this.scene.input.on('pointerup', () => {
if (this.onPaddle)
{
this.ballImage.setVelocity(-75, -300);
this.onPaddle = false;
}
}, this);
}
resetPosition(paddleImage: Phaser.Physics.Arcade.Image) {
this.ballImage.setVelocity(0);
this.ballImage.setPosition(paddleImage.x, this.scene.game.canvas.height - 100);
this.onPaddle = true;
}
hitPaddle(ballImage: Phaser.Physics.Arcade.Image, paddleImage: Phaser.Physics.Arcade.Image) {
var diff = 0;
if (ballImage.x < paddleImage.x)
{
// Ball is on the left-hand side of the paddle
diff = paddleImage.x - ballImage.x;
ballImage.setVelocityX(-VELOCITY_FACTOR * diff);
}
else if (ballImage.x > paddleImage.x)
{
// Ball is on the right-hand side of the paddle
diff = ballImage.x -paddleImage.x;
ballImage.setVelocityX(VELOCITY_FACTOR * diff);
}
else
{
// Ball is perfectly in the middle
// Add a little random X to stop it bouncing straight up!
ballImage.setVelocityX(2 + Math.random() * 8);
}
}
paddleMoved(paddleImage: Phaser.Physics.Arcade.Image) {
if (this.onPaddle)
{
this.ballImage.x = paddleImage.x;
}
}
update() {
if (this.ballImage.y > this.scene.game.canvas.height)
{
this.emit("outOfBoundsEvent");
}
}
};
// https://photonstorm.github.io/phaser3-docs/Phaser.GameObjects.GameObject.html
export default class Bricks extends Phaser.GameObjects.GameObject {
group: Phaser.Physics.Arcade.StaticGroup;
constructor(scene: Phaser.Scene) {
super(scene, "bricks");
this.create();
}
create() {
// Création des briques dans une grille de 10x6
this.group = this.scene.physics.add.staticGroup({
key: 'assets', frame: [ 'blue1', 'red1', 'green1', 'yellow1', 'silver1', 'purple1' ],
frameQuantity: 10,
gridAlign: { width: 10, height: 6, cellWidth: 64, cellHeight: 32, x: 112, y: 100 }
});
}
reset() {
this.group.children.each((brick: Phaser.Physics.Arcade.Image) => {
https://photonstorm.github.io/phaser3-docs/Phaser.Physics.Arcade.Sprite.html#enableBody__anchor
brick.enableBody(false, 0, 0, true, true);
});
}
hitBrick(ball: Phaser.Physics.Arcade.Image, brick: Phaser.Physics.Arcade.Image) {
// https://photonstorm.github.io/phaser3-docs/Phaser.Physics.Arcade.Sprite.html#disableBody__anchor
brick.disableBody(true, true);
if (this.group.countActive() === 0)
{
this.emit("allBricksDestroyedEvent");
}
}
};
// https://photonstorm.github.io/phaser3-docs/Phaser.GameObjects.GameObject.html
export default class Paddle extends Phaser.GameObjects.GameObject {
image: Phaser.Physics.Arcade.Image;
constructor(scene: Phaser.Scene) {
super(scene, "paddle");
this.create();
}
create() {
const canvas = this.scene.game.canvas;
this.image = this.scene.physics.add.image(canvas.width / 2, canvas.height - 50, 'assets', 'paddle1').setImmovable();
this.scene.input.on('pointermove', function (pointer) {
// Limite le mouvement du plateau aux extrémités gauches et droites
this.image.x = Phaser.Math.Clamp(pointer.x, 52, canvas.width - 52);
this.emit("paddleMovedEvent", this.image);
}, this);
}
};
test('very usefull test', () => {
expect(true).toBe(true);
})
{
"compilerOptions": {
"typeRoots": [
"./node_modules/phaser/types"
],
"types": [
"Phaser"
]
}
}
\ No newline at end of file
const path = require('path');
const CopyPlugin = require('copy-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
entry: {
app: './src/main.ts',
},
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/
}
]
},
resolve: {
extensions: [ '.ts', '.tsx', '.js' ]
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
},
plugins: [
new CleanWebpackPlugin({ cleanStaleWebpackAssets: false }),
new CopyPlugin({
patterns: [
{ from: 'index.html', context: 'src/' },
{ from: 'assets/*', context: 'src/' },
],
}),
],
};
const merge = require('webpack-merge');
const common = require('./webpack.common.js');
module.exports = merge(common, {
mode: 'development',
devtool: 'inline-source-map',
devServer: {
static: './dist',
},
});
const merge = require('webpack-merge');
const common = require('./webpack.common.js');
module.exports = merge(common, {
mode: 'production',
performance: {
hints: false,
maxEntrypointSize: 512000,
maxAssetSize: 512000
}
});
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment