Développement de blocs personnalisés dans Scratch-VM : de Scratch3MotionBlocks à vos propres extensions

Comprendre le module Scratch3MotionBlocks

Scratch3MotionBlocks constitue la base du système de mouvement dans Scratch-VM, en encapsulant des fonctionnalités comme le déplacement, la rotation et le placement des personnages. Ce module réside dans src/blocks/scratch3_motion.js et emploie une architecture orientée objet pour gérer les opérations de mouvement.

Mapping des opcodes et fonctionnalités

La méthode getPrimitives() établit une correspondance entre les opcodes des blocs et leurs fonctions d'implémentation. Parmi les opcodes courants, on trouve :

  • motion_movesteps : déplace le personnage d'un certain nombre de pas.
  • motion_gotoxy : téléporte le personnage à des coordonnées spécifiques.
  • motion_turnright : effectue une rotation vers la droite d'un angle donné.
  • motion_glidesecstoxy : effectue un déplacement fluide vers une position cible.

Ces opcodes correspondent aux blocs visuels dans l'éditeur Scratch, et leur modification permet d'ajuster le comportement des personnages.

Exemple d'implémentation pour le déplacement

Considérons la fonction deplacerPas, qui traduit les paramètres abstraits d'un bloc en actions concrètes sur le personnage :

deplacerPas (parametres, contexte) {
    let distance = Cast.toNumber(parametres.DISTANCE);
    let angleRad = MathUtil.degToRad(90 - contexte.cible.direction);
    let offsetX = distance * Math.cos(angleRad);
    let offsetY = distance * Math.sin(angleRad);
    contexte.cible.setXY(contexte.cible.x + offsetX, contexte.cible.y + offsetY);
}

Cette fonction convertit d'abord l'angle en radians, calcule les déplacements sur les axes X et Y, puis met à jour la position via setXY.

Architecture du système de blocs et mécanismes d'extension

Le système de blocs de Scratch-VM repose sur une conception modulaire, où chaque domaine fonctionnel (mouvement, apparence, son, etc.) est représenté par une classe dédiée. Pour développer des blocs personnalisés, il est essetniel de saisir les concepts clés suivants.

Structure des classes de blocs

Les classes de blocs partagent une structure commune :

  • Un constructeur qui reçoit l'instance du runtime pour établir la connexion avec l'environnement d'exécution.
  • La méthode getPrimitives() pour définir le mapping entre opcodes et fonctions de traitement.
  • La méthode getMonitored() pour déclarer les variables à surveiller, comme les coordonnées ou la direction.
  • Des méthodes spécifiques pour l'implémentation de chaque bloc.

Outils et points d'extension

Lors du développement de blocs personnalisés, plusieurs classes utilitaires offertes par le VM peuvent simplifier le processus :

  • Cast pour les conversions de types (située dans src/util/cast.js).
  • MathUtil pour les calculs mathématiques (située dans src/util/math-util.js).
  • Timer pour la gestion du temps (située dans src/util/timer.js).

Étapes pour développer des blocs personnalisés

Créer une classe de blocs

Commencez par définir une nouvelle classe de blocs, en héritant de la classe de base et en implémentant les méthodes requises :

class BlocsPersonnalises {
    constructor(runtime) {
        this.runtime = runtime;
    }
    
    getPrimitives() {
        return {
            bloc_personnalise: this.fonctionBloc
        };
    }
    
    fonctionBloc(parametres, contexte) {
        // Logique du bloc à implémenter
    }
}

module.exports = BlocsPersonnalises;

Enregistrer le module de blocs

Intégrez votre classe de blocs personnalisés dans l'initialisation du VM, typiquement dans src/index.js :

const BlocsPersonnalises = require('./blocs/blocs_personnalises');
// ...
vm.registry.registerModule(BlocsPersonnalises);

Définir les métadonnées des blocs

L'apparence et les paramètres des blocs sont décrits via des métadonnées, souvent contenues dans un fichier de manifeste d'extension, tel que src/extensions/scratch3_music/manifest.js.

Implémenter la logique des blocs

En fonction de la fonctionnalité souhaitée, implémentez la fonction de traitement en vous basant sur des modèles existants :

  • Blocs d'exécution directe : modifient les propriétés du personnage.
  • Blocs d'attente : utilisent contexte.yield() pour une exécution asynchrone.
  • Blocs de type reporter : retournent des résultats calculés pour d'autres blocs.

Astuces avancées et bonnes pratiques

Gestion de l'état

Pour les fonctionnalités nécessitant de conserver l'état entre plusieurs blocs, utilisez contexte.stackFrame pour stocker des données temporaires, comme dans la méthode de glissement où un minuteur est initialisé :

contexte.stackFrame.minuteur = new Timer();
contexte.stackFrame.minuteur.start();

Optimisation des performances

Pour les blocs exécutés fréquemment, envisagez d'utiliser des mécanismes de mise en cache (référence à src/engine/blocks-execute-cache.js). Les calculs complexes peuvent être délégués à des Web Workers pour éviter de bloquer le thread principal (référence à src/dispatch/worker-dispatch.js).

Stratégies de test

Rédigez des tests unitaires pour vos blocs personnalisés, en les plaçant dans le répertoire test/unit/, et inspirez-vous de la structure des fichiers de test existants comme test/unit/blocks_motion.js.

Ressources et parcours d'apprentissage

Documentation et exemples officiels

  • Guide de développement d'extensions : docs/extensions.md
  • Implémentation des blocs principaux : les fichiers dans le répertoire src/blocs/
  • Exemples d'extensions : les extensions officielles dans src/extensions/

Ordre d'apprentissage recommandé

  1. Étudier la logique d'implémentation de scratch3_motion.js.
  2. Analyser des extensions simples comme scratch3_pen.
  3. Modifier les fonctionnalités des blocs existants.
  4. Développer un module de blocs personnalisés indépendant.

Étiquettes: scratch-vm scratch3 JavaScript extension-development block-programming

Publié le 14 juin à 01h54