[実践・Scratch3拡張機能]〜scratch-vmでスコアボードのゲーム・モジュール化
※ 当ページには【広告/PR】を含む場合があります。
2021/02/22
スクラッチゲームのモジュール化
自然な
ゲーム・モジュール
メリット:
+ プロックコードによるプログラミング作業が楽になる
+ 色々なスクラッチゲームへ機能を簡単に移植できる
+ モジュール単位なのでコードのメンテナンスがしやすい
デメリット:
- 高度なJavascriptプログラミングの知識は必須
- scratch-vmの技術関連の資料が少ない
- scratchの公式サイトではアプリ開発できない(scratch-gui推奨)
Scratch-vmを活用したエクステンションの実装
1. scratch-vmの src/extensions フォルダ以下にindex.jsを作成
2. extention-manager.jsに登録
3. scratch-guiの方へエクスポート
モジュール化したスコアボード(拡張機能のindex.js)
const ArgumentType = require('../../extension-support/argument-type');
const BlockType = require('../../extension-support/block-type');
const TargetType = require('../../extension-support/target-type');
const Cast = require('../../util/cast');
const formatMessage = require('format-message');
const log = require('../../util/log');
const StageLayering = require('../../engine/stage-layering');
const gsp = require('raw-loader!./svg_parts/grasses_part.dat');
const tcp = require('raw-loader!./svg_parts/taco_part.dat');
const app = require('raw-loader!./svg_parts/apple_part.dat');
const wmp = require('raw-loader!./svg_parts/watermeron_part.dat');
const dnp = require('raw-loader!./svg_parts/dounuts_part.dat');
const fp = require('raw-loader!./svg_parts/fish_part.dat');
const dp = require('raw-loader!./svg_parts/dragon_part.dat');
class TacoswishScoreboard {
constructor (runtime) {
this.runtime = runtime;
this.totalScore = 0;
this.apCount = 0;
this.tcCount = 0;
this.wmCount = 0;
this.dntCount = 0;
this.fCount = 0;
this.dCount = 0;
this._scoreboardDrawbleId = -1;
this._svgSkinId = -1;
this.scoreboardConfig = {
isShowGrasses: true,
isShowTaco: true,
isShowApple: true,
isShowWaterMeron: false,
isShowDounuts: false,
isShowFish: false,
isShowDragon: false,
};
this.svgElements = [];
this.emptyImg = '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="0" height="0" viewBox="0 0 0 0"></svg>';
this._initParts();
}
_initParts() {
this.svgElements.push(gsp.default); // 0 > grasses
this.svgElements.push(app.default); // 1 > apple
this.svgElements.push(tcp.default); // 2 > tacos
this.svgElements.push(wmp.default); // 3 > watermeron
this.svgElements.push(dnp.default); // 4 > dounuts
this.svgElements.push(fp.default); // 5 > fish
this.svgElements.push(dp.default); // 6 > dragon
}
getInfo () {
return {
id: 'tacoswishsb',
name: formatMessage({
id: 'tacoswishsb.categoryName',
default: 'Tacoswishsb',
description: 'Label for the tacoswishsb extension category'
}),
blocks: [
{
opcode: 'initScoreboard',
blockType: BlockType.COMMAND
},
{
opcode: 'clearScoreboard',
blockType: BlockType.COMMAND
},
{
opcode: 'goScoreboardToBack',
blockType: BlockType.COMMAND
},
{
opcode: 'goScoreboardToFront',
blockType: BlockType.COMMAND
},
{
opcode: 'updateScoreboard',
blockType: BlockType.COMMAND
},
{
opcode: 'addScore',
text: 'Change to ([TARGET], [NEW_SCORE])',
blockType: BlockType.COMMAND,
arguments: {
TARGET: {
type: ArgumentType.NUMBER,
defaultValue: 0
},
NEW_SCORE: {
type: ArgumentType.NUMBER,
defaultValue: 0
}
}
},
{
opcode: 'showElement',
blockType: BlockType.COMMAND,
text: 'Have an item#([TARGET]) shown',
arguments: {
TARGET: {
type: ArgumentType.NUMBER,
defaultValue: 1
}
}
}
],
menus: {}
};
}
initScoreboard() {
this.totalScore = 0;
this.apCount = 0;
this.tcCount = 0;
this.wmCount = 0;
this.dntCount = 0;
this.fCount = 0;
this.dCount = 0;
this.scoreboardConfig = {
isShowGrasses: true,
isShowTaco: true,
isShowApple: true,
isShowWaterMeron: false,
isShowDounuts: false,
isShowFish: false,
isShowDragon: false,
};
if (this._svgSkinId < 0 && this.runtime.renderer) {
const skinId = this.runtime.renderer.createSVGSkin(this.emptyImg);
this._svgSkinId = skinId;
this._scoreboardDrawbleId = this.runtime.renderer.createDrawable(StageLayering.SPRITE_LAYER);
this.runtime.renderer.updateDrawableProperties(this._scoreboardDrawbleId, {
skinId: this._svgSkinId
});
}
}
clearScoreboard() {
if (this._svgSkinId >= 0) {
this.runtime.renderer.updateSVGSkin(this._svgSkinId, this.emptyImg);
}
}
addScore(args, util) {
const _target = Cast.toNumber(args.TARGET);
const _score = Cast.toNumber(args.NEW_SCORE);
switch(_target) {
case 0:
this._updateScore(_score, null, null, null, null, null, null);
break
case 1:
this._updateScore(null, _score, null, null, null, null, null);
break
case 2:
this._updateScore(null, null, _score, null, null, null, null);
break
case 3:
this._updateScore(null, null, null, _score, null, null, null);
break
case 4:
this._updateScore(null, null, null, null, _score, null, null);
break
case 5:
this._updateScore(null, null, null, null, null, _score, null);
break
case 6:
this._updateScore(null, null, null, null, null, null, _score,);
break
default:
break
}
}
showElement(args, util) {
const _target = Cast.toNumber(args.TARGET);
switch(_target) {
case 0:
this.scoreboardConfig.isShowGrasses = true;
break
case 1:
this.scoreboardConfig.isShowApple = true;
break
case 2:
this.scoreboardConfig.isShowTaco = true;
break
case 3:
this.scoreboardConfig.isShowWaterMeron = true;
break
case 4:
this.scoreboardConfig.isShowDounuts = true;
break
case 5:
this.scoreboardConfig.isShowFish = true;
break
case 6:
this.scoreboardConfig.isShowDragon = true;
break
default:
break
}
}
updateScoreboard(args, util) {
if (this._scoreboardDrawbleId >= 0 && this._svgSkinId >= 0) {
this.runtime.renderer.updateSVGSkin(this._svgSkinId, this._drawScoreBoard());
}
}
goScoreboardToBack() {
if (this._scoreboardDrawbleId >= 0 && this._svgSkinId >= 0) {
this.runtime.renderer.setDrawableOrder(this._scoreboardDrawbleId, 1, StageLayering.SPRITE_LAYER);
}
}
goScoreboardToFront() {
if (this._scoreboardDrawbleId >= 0 && this._svgSkinId >= 0) {
this.runtime.renderer.setDrawableOrder(this._scoreboardDrawbleId, Infinity, StageLayering.SPRITE_LAYER);
}
}
_drawScoreBoard() {
const gsp_str = this.scoreboardConfig.isShowGrasses ? this.svgElements[0] : '';
const app_str = this.scoreboardConfig.isShowApple ? this.svgElements[1] : '';
const tcp_str = this.scoreboardConfig.isShowTaco ? this.svgElements[2] : '';
const wmp_str = this.scoreboardConfig.isShowWaterMeron ? this.svgElements[3] : '';
const dnp_str = this.scoreboardConfig.isShowDounuts ? this.svgElements[4] : '';
const fp_str = this.scoreboardConfig.isShowFish ? this.svgElements[5] : '';
const dp_str = this.scoreboardConfig.isShowDragon ? this.svgElements[6] : '';
const total_score_str = this.scoreboardConfig.isShowGrasses ? this._convDrawbleText(this.totalScore, 55, 50, 16) : '';
const apple_count_str = this.scoreboardConfig.isShowApple ? this._convDrawbleText(this.apCount, 130, 50, 14) : '';
const tacos_count_str = this.scoreboardConfig.isShowTaco ? this._convDrawbleText(this.tcCount, 200, 50, 14) : '';
const wm_count_str = this.scoreboardConfig.isShowWaterMeron ? this._convDrawbleText(this.wmCount, 260, 50, 14) : '';
const dnt_count_str = this.scoreboardConfig.isShowDounuts ? this._convDrawbleText(this.dntCount, 320, 50, 14) : '';
const fish_count_str = this.scoreboardConfig.isShowFish ? this._convDrawbleText(this.fCount, 390, 50, 14) : '';
const drgn_count_str = this.scoreboardConfig.isShowDragon ? this._convDrawbleText(this.dCount, 450, 50, 14) : '';
return `<svg version="1.1" width="480" height="360" viewBox="0 0 480 360" xmlns="http://www.w3.org/2000/svg">
<g transform="(121 -5)">
<g stroke-width="0" fill-opacity="50%"><path d="m-121 57v-57h719v57z"/></g>
${gsp_str} ${total_score_str}
${app_str} ${apple_count_str}
${tcp_str} ${tacos_count_str}
${wmp_str} ${wm_count_str}
${dnp_str} ${dnt_count_str}
${fp_str} ${fish_count_str}
${dp_str} ${drgn_count_str}
</g></svg>`;
}
_convDrawbleText(txt_, x_, y_, fontSize_) {
return `<g transform="translate(${x_} ${y_})" >
<text fill="#F0E68C" font-family="sans-serif" font-size="${fontSize_}px">
<tspan>${txt_}</tspan>
</text>
</g>`;
}
_updateScore(ts_, ap_, tc_, wm_, dnt_, f_, d_) {
this.totalScore = ts_ !== null ? ts_ : this.totalScore;
this.apCount = ap_ !== null ? ap_ : this.apCount;
this.tcCount = tc_ !== null ? tc_ : this.tcCount;
this.wmCount = wm_ !== null ? wm_ : this.wmCount;
this.dntCount = dnt_ !== null ? dnt_ : this.dntCount;
this.fCount = f_ !== null ? f_ : this.fCount;
this.dCount = d_ !== null ? d_ : this.dCount;
}
}
module.exports = TacoswishScoreboard;
svg_parts
raw-loader
まとめ
メリット:
+ プロックコードによるプログラミング作業が楽になる
+ 色々なスクラッチゲームへ機能を簡単に移植できる
+ モジュール単位なのでコードのメンテナンスがしやすい
デメリット:
- 高度なJavascriptプログラミングの知識は必須
- scratch-vmの技術関連の資料が少ない
- scratchの公式サイトではアプリ開発できない(scratch-gui推奨)
記事を書いた人
ナンデモ系エンジニア
これからの"地方格差"なきプログラミング教育とは何かを考えながら、 地方密着型プログラミング学習関連テーマの記事を不定期で独自にブログ発信しています。
カテゴリー