diff --git a/scene-generator/actionableItemTypes.js b/scene-generator/actionableItemTypes.js index a095bcf..87aa67f 100644 --- a/scene-generator/actionableItemTypes.js +++ b/scene-generator/actionableItemTypes.js @@ -1,6 +1,7 @@ const ActionableItemType = { HITBOX: 'hitbox', IMAGE: 'image', + SPRITE: 'sprite', TEXT: 'text', }; diff --git a/scene-generator/getSceneClass.js b/scene-generator/getSceneClass.js index 4501b89..b6004e8 100644 --- a/scene-generator/getSceneClass.js +++ b/scene-generator/getSceneClass.js @@ -1,9 +1,11 @@ const getScenePreload = require('./getScenePreload'); const getSceneCreate = require('./getSceneCreate'); +const getSceneUpdate = require('./getSceneUpdate'); function getSceneClass(sceneName, sceneConfig) { const preloadCode = getScenePreload(sceneName, sceneConfig); const createCode = getSceneCreate(sceneName, sceneConfig); + const updateCode = getSceneUpdate(sceneName, sceneConfig); const sceneClass = ` import * as Phaser from 'phaser'; @@ -31,6 +33,10 @@ export class ${sceneName} extends Phaser.Scene { create() { ${createCode} } + + update() { + ${updateCode} + } } `; diff --git a/scene-generator/getSceneCreate.js b/scene-generator/getSceneCreate.js index a12f110..a42fb03 100644 --- a/scene-generator/getSceneCreate.js +++ b/scene-generator/getSceneCreate.js @@ -3,6 +3,7 @@ const getSetNameCode = require('./helpers/getSetNameCode.js'); const getAnimationCode = require('./helpers/getAnimationCode.js'); const getBackgroundCode = require('./helpers/getBackgroundCode.js'); const getLabelCode = require('./helpers/getLabelCode.js'); +const getSpriteLabelCode = require('./helpers/getSpriteLabelCode.js'); const getDragEventCode = require('./helpers/getDragEventCode.js'); const getSetScaleCode = require('./helpers/getSetScaleCode.js'); const getSetOriginCode = require('./helpers/getSetOriginCode.js'); @@ -10,6 +11,7 @@ const getSetOriginCode = require('./helpers/getSetOriginCode.js'); const getSetInteractiveCode = require('./helpers/getSetInteractiveCode.js'); const getHasPhysicsCode = require('./helpers/getHasPhysicsCode.js'); const getActionableItemImageCode = require('./helpers/getActionableItemImageCode.js'); +const getActionableItemSpriteCode = require('./helpers/getActionableItemSpriteCode.js'); const getActionableItemTextCode = require('./helpers/getActionableItemTextCode.js'); const getActionableItemHitboxCode = require('./helpers/getActionableItemHitboxCode.js'); const getEventsCode = require('./helpers/getEventsCode.js'); @@ -50,6 +52,9 @@ function geSceneCreate(sceneName, sceneConfig) { case ActionableItemType.IMAGE: createCode += getActionableItemImageCode(actionableItem); break; + case ActionableItemType.SPRITE: + createCode += getActionableItemSpriteCode(actionableItem); + break; case ActionableItemType.TEXT: createCode += getActionableItemTextCode(actionableItem); break; @@ -57,31 +62,40 @@ function geSceneCreate(sceneName, sceneConfig) { } createCode += getSetNameCode(actionableItem?.name); + if(actionableItem?.type !== ActionableItemType.SPRITE) { + createCode += getLabelCode( + actionableItem?.name, + actionableItem?.label, + actionableItem?.label?.styles + ); + createCode += getDragEventCode(sceneHasOneOrMoreDraggableItems); + } else { + createCode += getSpriteLabelCode( + actionableItem?.name, + actionableItem?.label, + actionableItem?.label?.styles + ); + } createCode += getSetScaleCode( - actionableItem?.name, - actionableItem?.scale - ); - createCode += getSetOriginCode( - actionableItem?.name, - actionableItem?.origin + actionableItem?.name, + actionableItem?.scale ); - createCode += getSetInteractiveCode(actionableItem?.name); createCode += getAnimationCode( - actionableItem?.name, - actionableItem?.animation + actionableItem?.name, + actionableItem?.animation ); - createCode += getSetDraggableCode( + createCode += getSetOriginCode( actionableItem?.name, - actionableItem?.isDraggable + actionableItem?.origin ); createCode += getHasPhysicsCode( actionableItem?.name, actionableItem?.hasPhysicsEnabled ); - createCode += getLabelCode( - actionableItem?.name, - actionableItem?.label, - actionableItem?.label?.styles + createCode += getSetInteractiveCode(actionableItem?.name); + createCode += getSetDraggableCode( + actionableItem?.name, + actionableItem?.isDraggable ); if (actionableItem?.actions?.length > 0) { @@ -99,8 +113,6 @@ function geSceneCreate(sceneName, sceneConfig) { } }); - createCode += getDragEventCode(sceneHasOneOrMoreDraggableItems); - if (process.env.NODE_ENV === 'development') { createCode += 'debug(this);'; } diff --git a/scene-generator/getScenePreload.js b/scene-generator/getScenePreload.js index e55a4c2..43f9827 100644 --- a/scene-generator/getScenePreload.js +++ b/scene-generator/getScenePreload.js @@ -11,6 +11,9 @@ function getScenePreload(sceneName, sceneConfig) { if (type === 'image') { preloadCode += `this.load.image("${name}", "${image.url}");\n`; } + if (type === 'sprite') { + preloadCode += `this.load.spritesheet("${name}", "${image.url}", { frameWidth: 37, frameHeight: 45 });\n`; + } }); if (mainNPC) { diff --git a/scene-generator/getSceneUpdate.js b/scene-generator/getSceneUpdate.js new file mode 100644 index 0000000..43a6310 --- /dev/null +++ b/scene-generator/getSceneUpdate.js @@ -0,0 +1,47 @@ +const ActionableItemType = require('./actionableItemTypes.js'); + +const getSpriteLabelUpdateCode = require('./helpers/getSpriteLabelUpdateCode'); + +function geSceneUpdate(sceneName, sceneConfig) { + if ( + !sceneName || + typeof sceneName !== 'string' || + !/^[A-Z][a-zA-Z0-9]*$/.test(sceneName) + ) { + throw new Error( + 'Invalid sceneName. A valid PascalCase string sceneName is required.' + ); + } + + if (!sceneConfig || typeof sceneConfig !== 'object') { + throw new Error('Invalid sceneConfig. An object sceneConfig is required.'); + } + + let updateCode = ''; + + renderSceneUpdate(sceneName, sceneConfig); + + function renderSceneUpdate(sceneName, sceneConfig) { + const { background, actionableItems, mainNPC } = sceneConfig; + + if (actionableItems?.length > 0) { + actionableItems.forEach((actionableItem) => { + + if(actionableItem?.type === ActionableItemType.SPRITE) { + updateCode += getSpriteLabelUpdateCode( + actionableItem?.name, + actionableItem?.label + ); + } + }); + + if (process.env.NODE_ENV === 'development') { + updateCode += 'debug(this);'; + } + } + } + + return updateCode; +} + +module.exports = geSceneUpdate; diff --git a/scene-generator/helpers/getActionableItemSpriteCode.js b/scene-generator/helpers/getActionableItemSpriteCode.js new file mode 100644 index 0000000..383559c --- /dev/null +++ b/scene-generator/helpers/getActionableItemSpriteCode.js @@ -0,0 +1,14 @@ +function getActionableItemSpriteCode(actionableItem) { + let options = JSON.stringify(actionableItem?.play?.options); + return `this.${actionableItem?.name}Anim = this.anims.create({ + key: '${actionableItem?.name}Key', + frames: this.anims.generateFrameNumbers('${actionableItem?.name}'), + frameRate: ${actionableItem?.animation?.options?.frameRate}, + }); + this.${actionableItem?.name} = this.add.sprite(${actionableItem?.position?.x},${actionableItem?.position?.y}, '${actionableItem?.name}').setScale(${actionableItem?.scale}); + this.${actionableItem?.name}.play({ key: '${actionableItem?.name}Key', ...${options} }); + + `; +} + +module.exports = getActionableItemSpriteCode; diff --git a/scene-generator/helpers/getEventsCode.js b/scene-generator/helpers/getEventsCode.js index f4757a2..813b160 100644 --- a/scene-generator/helpers/getEventsCode.js +++ b/scene-generator/helpers/getEventsCode.js @@ -4,52 +4,52 @@ const getSceneTransitionCode = require('./getSceneTransitionCode.js'); const getMainNPCDialogEventCode = require('./getMainNPCDialogEventCode.js'); function getEventsCode(name, actionType, actionTarget, events, sceneConfig) { - if (!events) return ''; + if (!events) return ''; - let content = ''; - if (actionType === ActionType.COLLIDE) { - if ( - events.some( - ({ eventType }) => - eventType === EventType.SCENE_TRANSITION || - eventType === EventType.MAIN_NPC_DIALOG - ) - ) { - content += ` + let content = ''; + if (actionType === ActionType.COLLIDE) { + if ( + events.some( + ({ eventType }) => + eventType === EventType.SCENE_TRANSITION || + eventType === EventType.MAIN_NPC_DIALOG + ) + ) { + content += ` this.physics.add.overlap(this.${name}, this.${actionTarget}, () => { `; - events.forEach(({ eventType, event }) => { - if (eventType === EventType.SCENE_TRANSITION) { - content += getSceneTransitionCode(event, sceneConfig); - } - if (eventType === EventType.MAIN_NPC_DIALOG) { - content += getMainNPCDialogEventCode(event, sceneConfig); - } - }); - content += `}); + events.forEach(({ eventType, event }) => { + if (eventType === EventType.SCENE_TRANSITION) { + content += getSceneTransitionCode(event, sceneConfig); + } + if (eventType === EventType.MAIN_NPC_DIALOG) { + content += getMainNPCDialogEventCode(event, sceneConfig); + } + }); + content += `}); `; - } - } else { - content += ` + } + } else { + content += ` this.${name}.on("${actionType.toLowerCase()}", function () {`; - events?.forEach(({ eventType, event }) => { - content += ` + events?.forEach(({ eventType, event }) => { + content += ` ${ - eventType === EventType.SCENE_TRANSITION - ? getSceneTransitionCode(event) - : '' - } + eventType === EventType.SCENE_TRANSITION + ? getSceneTransitionCode(event) + : '' + } ${ - eventType === EventType.MAIN_NPC_DIALOG - ? getMainNPCDialogEventCode(event) - : '' - }`; - }); - content += ` + eventType === EventType.MAIN_NPC_DIALOG + ? getMainNPCDialogEventCode(event) + : '' + }`; + }); + content += ` }, this); `; - } - return content; + } + return content; } module.exports = getEventsCode; diff --git a/scene-generator/helpers/getSpriteLabelCode.js b/scene-generator/helpers/getSpriteLabelCode.js new file mode 100644 index 0000000..f810af3 --- /dev/null +++ b/scene-generator/helpers/getSpriteLabelCode.js @@ -0,0 +1,15 @@ +const sceneConfig = require('../../src/configs/sceneConfig.json'); + +function getSpriteLabelCode(name, label, labelStyles = sceneConfig?.labelStyles) { + if (!label || !name) return ''; + return ` + this.${name}.label = this.add.text( + this.${name}.x + (this.${name}.width / 2), this.${name}.y, "${ + label.content + }", + ${JSON.stringify(labelStyles)} + ).setOrigin(0.5); + `; +} + +module.exports = getSpriteLabelCode; diff --git a/scene-generator/helpers/getSpriteLabelUpdateCode.js b/scene-generator/helpers/getSpriteLabelUpdateCode.js new file mode 100644 index 0000000..0290b20 --- /dev/null +++ b/scene-generator/helpers/getSpriteLabelUpdateCode.js @@ -0,0 +1,8 @@ +function getSpriteLabelUpdateCode(name, label) { + if (!label || !name) return ''; + return ` + this.${name}.label.setPosition(this.${name}.x + (this.${name}.width / 2), this.${name}.y); + `; +} + +module.exports = getSpriteLabelUpdateCode; diff --git a/scene-generator/scenes-requierments/Scene1.json b/scene-generator/scenes-requierments/Scene1.json index b52dcc3..2a822b0 100644 --- a/scene-generator/scenes-requierments/Scene1.json +++ b/scene-generator/scenes-requierments/Scene1.json @@ -87,6 +87,104 @@ ] } ] + }, + { + "name": "mummy", + "type": "sprite", + "image": { + "url": "src/assets/animations/mummy37x45.png" + }, + "label": { + "content": "mummy" + }, + "scale": 4, + "position": { + "x": 190, + "y": 660 + }, + "play": { + "options": { + "duration": 8800, + "frameRate": 20, + "ease": "Linear", + "yoyo": false, + "repeat": 330 + } + }, + "animation": { + "options": { + "x": 190, + "duration": 8800, + "frameRate": 20, + "ease": "Linear", + "yoyo": false, + "repeat": 1 + } + }, + "isDraggable": false, + "hasPhysicsEnabled": true, + "actions": [{ + "actionType": "pointerup", + "events": [ + { + "eventType": "mainNPCDialog", + "event": { + "dialog": { + "content": "Watch out! It's a mummy!" + } + } + } + ] + }] + }, + { + "name": "mummy2", + "type": "sprite", + "image": { + "url": "src/assets/animations/mummy37x45.png" + }, + "label": { + "content": "mummy2" + }, + "scale": 4, + "position": { + "x": 250, + "y": 630 + }, + "play": { + "options": { + "duration": 980, + "frameRate": 44, + "ease": "Linear", + "yoyo": false, + "repeat": 100 + } + }, + "animation": { + "options": { + "x": 750, + "duration": 9800, + "frameRate": 16, + "ease": "Linear", + "yoyo": true, + "repeat": 1 + } + }, + "isDraggable": false, + "hasPhysicsEnabled": true, + "actions": [{ + "actionType": "pointerup", + "events": [ + { + "eventType": "mainNPCDialog", + "event": { + "dialog": { + "content": "Watch out! It's a mummy!" + } + } + } + ] + }] } ], "mainNPC": { diff --git a/src/assets/animations/mummy37x45.png b/src/assets/animations/mummy37x45.png new file mode 100644 index 0000000..4d573e2 Binary files /dev/null and b/src/assets/animations/mummy37x45.png differ diff --git a/tests/node/getSceneCreate.test.js b/tests/node/getSceneCreate.test.js index 9078faa..80ccd42 100644 --- a/tests/node/getSceneCreate.test.js +++ b/tests/node/getSceneCreate.test.js @@ -288,19 +288,19 @@ describe('getSceneCreate function', () => { this.text1 = this.add.text(undefined, undefined, "undefined", undefined); this.text1.name = "text1"; - this.text1.setOrigin(0.5, 0.5); this.text1.setInteractive(); + this.text1.setOrigin(0.5, 0.5); this.image1 = this.add.image(undefined, undefined, "image1"); this.image1.name = "image1"; - this.image1.setOrigin(0.5, 0.5); this.image1.setInteractive(); + this.image1.setOrigin(0.5, 0.5); this.hitbox1 = this.add.rectangle(undefined, undefined, undefined, undefined, undefined); this.hitbox1.isHitbox = true; this.hitbox1.name = "hitbox1"; - this.hitbox1.setOrigin(0.5, 0.5); this.hitbox1.setInteractive(); + this.hitbox1.setOrigin(0.5, 0.5); `; const result = getSceneCreate(sceneName, sceneConfig); @@ -502,6 +502,15 @@ describe('getSceneCreate function', () => { this.scene.children.bringToTop(this.label); } }); + + this.input.on("drag", (pointer,gameObject,dragX,dragY) => { + gameObject.x = dragX; + gameObject.y = dragY; + if(gameObject.label) { + gameObject.label.x = gameObject.getBounds()?.x + gameObject.getBounds()?.width/2; + gameObject.label.y = gameObject.getBounds()?.y - gameObject.getBounds()?.height/2; + } + }); this.text1 = this.add.text(undefined, undefined, "undefined", undefined); this.text1.name = "text1"; @@ -513,6 +522,15 @@ describe('getSceneCreate function', () => { this.scene.children.bringToTop(this.label); } }); + + this.input.on("drag", (pointer,gameObject,dragX,dragY) => { + gameObject.x = dragX; + gameObject.y = dragY; + if(gameObject.label) { + gameObject.label.x = gameObject.getBounds()?.x + gameObject.getBounds()?.width/2; + gameObject.label.y = gameObject.getBounds()?.y - gameObject.getBounds()?.height/2; + } + }); this.hitbox1 = this.add.rectangle(undefined, undefined, undefined, undefined, undefined); this.hitbox1.isHitbox = true; diff --git a/tests/node/helpers/getActionableItemSpriteCode.test.js b/tests/node/helpers/getActionableItemSpriteCode.test.js new file mode 100644 index 0000000..8bc0403 --- /dev/null +++ b/tests/node/helpers/getActionableItemSpriteCode.test.js @@ -0,0 +1,20 @@ +const getActionableItemSpriteCode = require('../../../scene-generator/helpers/getActionableItemSpriteCode'); + +describe('getActionableItemSpriteCode', () => { + it('should return the correct code for creating a sprite with specified position', () => { + const actionableItem = { + name: 'item1', + position: { x: 100, y: 200 }, + move: { x: 190, y: 0 }, + }; + const result = getActionableItemSpriteCode(actionableItem); + const expectedBegin = `this.item1 = this.anims.create`; + const expectedPosition = `this.add.sprite(100,200,'item1')`; + expect(result.replace(/\s+/g, '')).toMatch( + new RegExp(`^${expectedBegin.replace(/\s+/g, '')}?`) + ); + expect(result.replace(/\s+/g, '')).toMatch( + expectedPosition + ); + }); +}); diff --git a/tests/node/helpers/getSetInteractiveCode.test.js b/tests/node/helpers/getSetInteractiveCode.test.js index db4dc8f..a9748ad 100644 --- a/tests/node/helpers/getSetInteractiveCode.test.js +++ b/tests/node/helpers/getSetInteractiveCode.test.js @@ -2,17 +2,24 @@ const getSetInteractiveCode = require('../../../scene-generator/helpers/getSetIn describe('getSetInteractiveCode', () => { it('should return an empty string if name is "input" or not provided', () => { - const name1 = 'input'; - const name2 = undefined; - const result1 = getSetInteractiveCode(name1); - const result2 = getSetInteractiveCode(name2); + const actionableItem1 = { + name: 'input', + }; + + const actionableItem2 = { + name: undefined, + }; + const result1 = getSetInteractiveCode(actionableItem1); + const result2 = getSetInteractiveCode(actionableItem2); expect(result1.replace(/\s+/g, '')).toEqual(''); expect(result2.replace(/\s+/g, '')).toEqual(''); }); it('should return the correct code for setting interactive property', () => { - const name = 'gameObject'; - const result = getSetInteractiveCode(name); + const actionableItem1 = { + name: 'gameObject', + }; + const result = getSetInteractiveCode(actionableItem1); const expectedCode = `this.gameObject.setInteractive();`; expect(result.replace(/\s+/g, '')).toEqual( expectedCode.replace(/\s+/g, '') diff --git a/tests/node/helpers/getSpriteLabelCode.test.js b/tests/node/helpers/getSpriteLabelCode.test.js new file mode 100644 index 0000000..99e1e5d --- /dev/null +++ b/tests/node/helpers/getSpriteLabelCode.test.js @@ -0,0 +1,27 @@ +const getSpriteLabelCode = require('../../../scene-generator/helpers/getSpriteLabelCode'); + +describe('getSpriteLabelCode', () => { + it('should return an empty string if label or name is falsy', () => { + const name = 'someName'; + const label = null; + const labelStyles = { fontSize: '16px', color: '#ffffff' }; + const result = getSpriteLabelCode(name, label, labelStyles); + expect(result).toEqual(''); + }); + + it('should return the correct code for valid label and name', () => { + const name = 'someName'; + const label = { content: 'Hello, World!' }; + const labelStyles = { fontSize: '16px', color: '#ffffff' }; + const result = getSpriteLabelCode(name, label, labelStyles); + + const expectedCode = ` + this.someName.label = this.add.text(this.someName.x + (this.someName.width / 2), this.someName.y - this.someName.height / 2, "Hello, World!", + {"fontSize":"16px","color":"#ffffff"} + ).setOrigin(0.5); + `; + expect(result.replace(/\s+/g, '')).toEqual( + expectedCode.replace(/\s+/g, '') + ); + }); +});