Skip to content

Commit

Permalink
Added setting and language button to Game Editor Select Screen.
Browse files Browse the repository at this point in the history
Added additional check to only update preview on "real" code changes.
Added additional check to defer preview updates on text inputs.
Added endless loop protected for "Restart Game" and "Game Start" block.
Added error messages for missing audio keys.
Added code editor is disabled for un-save changes.
Fixed bgm sound block.
  • Loading branch information
MarkusBordihn committed Apr 24, 2024
1 parent efdaf3b commit a6e4fb5
Show file tree
Hide file tree
Showing 11 changed files with 346 additions and 248 deletions.
445 changes: 233 additions & 212 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "coding-with-chrome",
"version": "10.6.0",
"version": "10.7.0",
"description": "Educational Coding Development Environment",
"repository": {
"type": "git",
Expand Down
29 changes: 25 additions & 4 deletions src/components/BlockEditor/BlockEditor.js
Original file line number Diff line number Diff line change
Expand Up @@ -485,7 +485,25 @@ export class BlockEditor extends React.PureComponent {
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars, no-unused-vars, require-jsdoc
handleWorkspaceChange(workspace) {
// Not used.
// Not implemented yet.
}

/**
* @return {boolean}
*/
isSelectedBlockATextInput() {
return (
typeof Blockly !== 'undefined' &&
Blockly.getSelected() &&
Blockly.getSelected().inputList &&
Blockly.getSelected().inputList[0] &&
Blockly.getSelected().inputList[0].fieldRow &&
Blockly.getSelected().inputList[0].fieldRow.some(
(input) =>
input instanceof Blockly.FieldTextInput ||
input instanceof Blockly.FieldNumber,
)
);
}

/**
Expand Down Expand Up @@ -540,9 +558,12 @@ export class BlockEditor extends React.PureComponent {
isFirstXMLUpdate: false,
});
} else {
this.timer.handleXMLChange = setTimeout(() => {
this.updateCodeAfterXMLChange();
}, 200);
this.timer.handleXMLChange = setTimeout(
() => {
this.updateCodeAfterXMLChange();
},
this.isSelectedBlockATextInput() ? 5000 : 200,
);
}
}

Expand Down
1 change: 1 addition & 0 deletions src/components/BlockEditor/BlockEditorToolbar.js
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,7 @@ export class BlockEditorToolbar extends React.PureComponent {
{this.props.blockEditor.codeEditor && (
<ToolbarIconButton
aria-label="code"
disabled={this.props.hasChanged}
onClick={() => {
this.props.blockEditor.showCodeEditor();
}}
Expand Down
10 changes: 10 additions & 0 deletions src/components/GameEditor/GameEditor.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ export class GameEditor extends React.PureComponent {
handleXMLChange: null,
};

this.changeCache = {
code: '',
};

// Set trusted origin for postMessage and other communication.
this.trustedOrigin = `${window.location.origin}`;

Expand Down Expand Up @@ -415,6 +419,12 @@ export class GameEditor extends React.PureComponent {
this.timer.handleXMLChange = null;
}

// Skip update if the code is the same as before.
if (this.changeCache.code == code) {
return;
}
this.changeCache.code = code;

// Allow updates for the preview directly on the first run / load and use
// auto-fresh for further refresh / updates.
if (this.state.isFirstRunPreview) {
Expand Down
10 changes: 9 additions & 1 deletion src/components/GameEditor/GameEditorSelectScreen.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ import { Project } from '../Project/Project';
import { ProjectType } from '../Project/ProjectType';

import LanguageSetting from '../Settings/LanguageSetting';
import SettingScreen from '../Settings/SettingScreen';

const NewGameProject = lazy(() => import('./dialog/NewGameProject'));
const OpenGameProject = lazy(() => import('./dialog/OpenGameProject'));

Expand Down Expand Up @@ -100,6 +102,11 @@ export class GameEditorSelectScreen extends React.PureComponent {
Project.hasProjects(ProjectType.GAME_EDITOR).then((hasProjects) => {
this.setState({ hasProjects });
});

// Update language
i18next.on('languageChanged', () => {
this.forceUpdate();
});
}

/**
Expand Down Expand Up @@ -153,7 +160,8 @@ export class GameEditorSelectScreen extends React.PureComponent {
>
{i18next.t('GAME_EDITOR')}
</Typography>
<LanguageSetting color="inherit" />
<SettingScreen color="inherit" showIcon={true} />
<LanguageSetting color="inherit" showIcon={true} />
</Toolbar>
</AppBar>

Expand Down
46 changes: 21 additions & 25 deletions src/components/GameEditor/blocks/AudioBlocks.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,33 +67,24 @@ Blocks['phaser_audio_add_bgm'] = {
* @return {string}
*/
javascriptGenerator.forBlock['phaser_audio_add_bgm'] = function (block) {
const text_audio = block.getFieldValue('audio');
const number_volume = block.getFieldValue('volume');
const dropdown_loop = block.getFieldValue('loop');
const textAudio = block.getFieldValue('audio');
const numberVolume = block.getFieldValue('volume');
const dropdownLoop = block.getFieldValue('loop');
const variable = javascriptGenerator.valueToCode(
block,
'variable',
javascriptGenerator.ORDER_ATOMIC,
);
return (
'if (typeof ' +
variable +
" === 'undefined') {\n" +
' ' +
variable +
" = game.add.audio('" +
text_audio +
"', " +
number_volume / 100 +
', ' +
dropdown_loop +
');\n' +
'} else {\n ' +
variable +
'.stop();\n}\n' +
variable +
'.play();\n'
);
return `
try {
${variable} = this.sound.add('${textAudio}', {
volume: ${numberVolume / 100}, loop: ${dropdownLoop}
});
${variable}.play();
} catch (e) {
window.alert(e);
}
`;
};

/**
Expand Down Expand Up @@ -144,9 +135,14 @@ javascriptGenerator.forBlock['phaser_audio_add'] = function (block) {
javascriptGenerator.ORDER_ATOMIC,
);
return `
${variable} = this.sound.add('${textAudio}', {
volume: ${numberVolume / 100}, loop: ${dropdownLoop}
});`;
try {
${variable} = this.sound.add('${textAudio}', {
volume: ${numberVolume / 100}, loop: ${dropdownLoop}
});
} catch (e) {
window.alert(e);
}
`;
};

/**
Expand Down
4 changes: 4 additions & 0 deletions src/components/GameEditor/blocks/CreateBlocks.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ javascriptGenerator.forBlock['phaser_create'] = function (block) {
// Default definition for easier access.
this.default_group = this.add.group(undefined, 'default_group');
// Reset reload protection.
this.helper_.resetReloadProtection('phaser_game_start');
this.helper_.resetReloadProtection('phaser_game_restart');
${javascriptGenerator.statementToCode(block, 'CODE')}
// Separate event block for better code structure.
Expand Down
14 changes: 10 additions & 4 deletions src/components/GameEditor/blocks/GameBlocks.js
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,11 @@ javascriptGenerator.forBlock['phaser_game_start'] = function (block) {
'variable',
javascriptGenerator.ORDER_ATOMIC,
);
return `this.scene.start('${variable}');\n`;
return `
if (this.helper_.checkReloadProtection('phaser_game_start')) {
this.scene.start('${variable}');
}
`;
};

/**
Expand All @@ -186,8 +190,10 @@ Blocks['phaser_game_restart'] = {
*/
javascriptGenerator.forBlock['phaser_game_restart'] = function () {
return `
this.events.off();
this.registry.destroy();
this.scene.restart();
if (this.helper_.checkReloadProtection('phaser_game_restart')) {
this.events.off();
this.registry.destroy();
this.scene.restart();
}
`;
};
31 changes: 31 additions & 0 deletions src/frameworks/phaser/phaser_helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class PhaserHelper {
this.sprite = new Set();
this.tileSprite = new Set();
this.arcadeSprite = new Set();
window.phaserReloadProtection = window.phaserReloadProtection || {};
}

/**
Expand All @@ -56,6 +57,36 @@ class PhaserHelper {
this.arcadeSprite.add(arcadeSprite);
}

/**
* @param {string} id
* @return {boolean}
*/
checkReloadProtection(id) {
if (window.phaserReloadProtection[id] < 5) {
window.phaserReloadProtection[id] = window.phaserReloadProtection[id] + 1;
return true;
} else {
console.error('⚠️ Reload protection triggered for', id);
console.warn('💡 Please check your code for endless loops!');
}
return false;
}

/**
* @param {string} id
*/
resetReloadProtection(id) {
// Reset reload protection.
if (
window.phaserReloadProtection[id] &&
window.phaserReloadProtection[id] > 0
) {
window.setTimeout(() => {
window.phaserReloadProtection[id] = 0;
}, 100);
}
}

/**
* Handles input specific functions, if needed.
*/
Expand Down
2 changes: 1 addition & 1 deletion src/frameworks/phaser/phaser_helper.min.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class PhaserHelper{constructor(game){this.game=game,this.sprite=new Set,this.tileSprite=new Set,this.arcadeSprite=new Set}addSprite(sprite){this.sprite.add(sprite)}addTileSprite(tileSprite){this.tileSprite.add(tileSprite)}addArcadeSprite(arcadeSprite){this.arcadeSprite.add(arcadeSprite)}handleInput(){this.game&&this.game.input_&&this.game.input_()}handleUpdate(){this.game&&this.tileSprite.forEach(tileSprite=>{PhaserHelper.scrollTileSprite(tileSprite)})}static scrollTileSprite(tileObject){tileObject&&(tileObject.scrollFactorX&&(tileObject.tilePositionX+=tileObject.scrollFactorX),tileObject.scrollFactorY)&&(tileObject.tilePositionY+=tileObject.scrollFactorY)}}
class PhaserHelper{constructor(game){this.game=game,this.sprite=new Set,this.tileSprite=new Set,this.arcadeSprite=new Set,window.phaserReloadProtection=window.phaserReloadProtection||{}}addSprite(sprite){this.sprite.add(sprite)}addTileSprite(tileSprite){this.tileSprite.add(tileSprite)}addArcadeSprite(arcadeSprite){this.arcadeSprite.add(arcadeSprite)}checkReloadProtection(id){return window.phaserReloadProtection[id]<5?(window.phaserReloadProtection[id]=window.phaserReloadProtection[id]+1,!0):(console.error("⚠️ Reload protection triggered for",id),console.warn("💡 Please check your code for endless loops!"),!1)}resetReloadProtection(id){window.phaserReloadProtection[id]&&0<window.phaserReloadProtection[id]&&window.setTimeout(()=>{window.phaserReloadProtection[id]=0},100)}handleInput(){this.game&&this.game.input_&&this.game.input_()}handleUpdate(){this.game&&this.tileSprite.forEach(tileSprite=>{PhaserHelper.scrollTileSprite(tileSprite)})}static scrollTileSprite(tileObject){tileObject&&(tileObject.scrollFactorX&&(tileObject.tilePositionX+=tileObject.scrollFactorX),tileObject.scrollFactorY)&&(tileObject.tilePositionY+=tileObject.scrollFactorY)}}

0 comments on commit a6e4fb5

Please sign in to comment.