diff --git a/assets/blocks.json b/assets/blocks.json
index 37fbace..4e87ac4 100644
--- a/assets/blocks.json
+++ b/assets/blocks.json
@@ -151,18 +151,28 @@
"message0": "set direction %1 %2",
"args0": [
{
- "type": "field_number",
- "name": "angle",
- "value": 0
+ "type": "field_dropdown",
+ "name": "direction",
+ "options": [
+ [
+ "left",
+ "left"
+ ],
+ [
+ "right",
+ "right"
+ ]
+ ]
},
{
"type": "input_dummy",
- "name": "direction"
+ "name": "dummy"
}
],
"previousStatement": null,
"nextStatement": null,
- "colour": 285
+ "colour": 285,
+ "inputsInline": true
},
{
"type": "controls_repeat_forever",
diff --git a/src/app/components/scene/scene.component.html b/src/app/components/scene/scene.component.html
index e86aeaf..db6d9e7 100644
--- a/src/app/components/scene/scene.component.html
+++ b/src/app/components/scene/scene.component.html
@@ -58,29 +58,44 @@
label="rotation"
(valueChange)="obj.rotation = $event; drawImages();"
/>
+
+
+
}
Scene Objects
+
+
+ @for (obj of sceneObjects; track obj.id) {
+
![]()
+ }
-
- @for (obj of sceneObjects; track obj.id) {
-
![]()
- }
-
- @if (modeService.getMode() === 'teacher') {
-
-
-
- }
+ @if (modeService.getMode() === 'teacher') {
+
+
+
+ }
+
@@ -90,7 +105,9 @@
Scene Objects
[style.left.px]="contextMenuX"
[style.top.px]="contextMenuY"
>
-
+
diff --git a/src/app/components/scene/scene.component.ts b/src/app/components/scene/scene.component.ts
index de31fa8..e3c8766 100644
--- a/src/app/components/scene/scene.component.ts
+++ b/src/app/components/scene/scene.component.ts
@@ -58,20 +58,17 @@ export class SceneComponent implements AfterViewInit {
protected contextMenuY = 0;
protected contextMenuObject: SceneObject | null = null;
- protected selectedObject= computed(() => {
+ protected selectedObject = computed(() => {
if (!this.selectedObjectId()) return undefined;
return this.sceneObjects.find(obj => obj.id === this.selectedObjectId());
});
ngAfterViewInit(): void {
- this.initCanvas().then(() => {
- this.setupMouseEvents();
- this.drawImages();
- });
+ this.initCanvas();
}
- private async initCanvas() {
+ private initCanvas() {
const canvasEl = this.canvas.nativeElement;
const style = getComputedStyle(canvasEl);
@@ -83,8 +80,17 @@ export class SceneComponent implements AfterViewInit {
this.ctx = canvasEl.getContext('2d');
- this.sceneObjects.map(async obj => obj.img = await loadImage(obj.imgSrc));
- if (this.bgSrc) this.bgImage = await loadImage(this.bgSrc);
+ const imageLoadPromises = this.sceneObjects.map(async obj => obj.img = await loadImage(obj.imgSrc));
+ if (this.bgSrc) {
+ const bgPromise = (async () => this.bgImage = await loadImage(this.bgSrc!));
+ imageLoadPromises.push(bgPromise())
+ }
+
+ Promise.all(imageLoadPromises).then(() => {
+ if (this.sceneObjects.length > 0) this.objectSelected.emit(this.sceneObjects[0].id);
+ this.drawImages()
+ });
+ this.setupMouseEvents();
}
private setupMouseEvents() {
@@ -111,7 +117,6 @@ export class SceneComponent implements AfterViewInit {
const mouseX = e.offsetX;
const mouseY = e.offsetY;
- // Move the image based on mouse position
this.draggingObject.x = mouseX - this.offsetX;
this.draggingObject.y = mouseY - this.offsetY;
@@ -141,6 +146,12 @@ export class SceneComponent implements AfterViewInit {
this.contextMenuObject = obj;
}
+ protected onChangeSelect(event: Event) {
+ const selectElement = event.target as HTMLSelectElement;
+ this.selectedObject()!.lookingLeft = (selectElement.value === 'left');
+ this.drawImages();
+ }
+
@HostListener('document:click')
hideContextMenu() {
this.contextMenuVisible = false;
@@ -166,6 +177,7 @@ export class SceneComponent implements AfterViewInit {
this.ctx.translate(centerX, centerY);
this.ctx.rotate(angleInRadians);
+ if (!obj.lookingLeft) this.ctx.scale(-1, 1);
if (obj.id === this.selectedObjectId()) {
this.ctx.shadowColor = 'red';
@@ -174,7 +186,7 @@ export class SceneComponent implements AfterViewInit {
this.ctx.shadowOffsetY = 0;
}
- if (obj.img) this.ctx.drawImage(obj.img!, -obj.size / 2, -obj.size / 2, obj.size, obj.size);
+ this.ctx.drawImage(obj.img!, -obj.size / 2, -obj.size / 2, obj.size, obj.size);
this.ctx.restore();
}
diff --git a/src/app/models/scene-object.ts b/src/app/models/scene-object.ts
index 4f842c1..04916b5 100644
--- a/src/app/models/scene-object.ts
+++ b/src/app/models/scene-object.ts
@@ -7,13 +7,20 @@ export class SceneObject {
public rotation: number,
public size: number,
public workspace: string,
+ public lookingLeft: boolean = true,
public img?: HTMLImageElement,
) {}
moveForward(steps: number) {
const radians = (this.rotation * Math.PI) / 180;
- this.x += Math.cos(radians) * steps;
- this.y += Math.sin(radians) * steps;
+
+ if (this.lookingLeft) {
+ this.x -= Math.cos(radians) * steps;
+ this.y -= Math.sin(radians) * steps;
+ } else {
+ this.x += Math.cos(radians) * steps;
+ this.y += Math.sin(radians) * steps;
+ }
}
moveTo(x: number, y: number) {
@@ -21,8 +28,8 @@ export class SceneObject {
this.y = y;
}
- setDirection(angle: number) {
-
+ setDirection(direction: 'left' | 'right') {
+ this.lookingLeft = direction === 'left';
}
turnLeft(angle: number) {
diff --git a/src/app/pages/activity-detail/activity-detail.component.ts b/src/app/pages/activity-detail/activity-detail.component.ts
index 92cf42c..6a5b801 100644
--- a/src/app/pages/activity-detail/activity-detail.component.ts
+++ b/src/app/pages/activity-detail/activity-detail.component.ts
@@ -97,9 +97,8 @@ export class ActivityDetailComponent implements AfterViewInit, OnDestroy {
this.BLOCK_LIMITS = new Map
(Object.entries(this.activity()!.toolboxInfo.BLOCK_LIMITS));
if (this.activity()!.sceneObjects.length > 0) {
-
const restoredObjects = this.activity()!.sceneObjects.map(obj =>
- new SceneObject(obj.id, obj.imgSrc, obj.x, obj.y, obj.rotation, obj.size, obj.workspace)
+ new SceneObject(obj.id, obj.imgSrc, obj.x, obj.y, obj.rotation, obj.size, obj.workspace, obj.lookingLeft)
);
this.activity.set({...this.activity()!, sceneObjects: restoredObjects});
@@ -112,7 +111,6 @@ export class ActivityDetailComponent implements AfterViewInit, OnDestroy {
this.activity.set({...this.activity()!, workspace: jsonWorkspace});
this.activity()!.sceneObjects.forEach(sceneObject => this.generateCode(sceneObject));
- if (this.activity()!.sceneObjects.length > 0) this.selectSceneObject(this.activity()!.sceneObjects[0].id);
}
ngOnDestroy(): void {
@@ -136,6 +134,7 @@ export class ActivityDetailComponent implements AfterViewInit, OnDestroy {
obj.rotation,
obj.size,
obj.workspace,
+ obj.lookingLeft,
img
);
} else {
@@ -152,6 +151,7 @@ export class ActivityDetailComponent implements AfterViewInit, OnDestroy {
0,
100,
this.activity()!.workspace,
+ true,
img
);
}
@@ -361,7 +361,6 @@ export class ActivityDetailComponent implements AfterViewInit, OnDestroy {
this.runningInterpreters = this.objectsCode.size;
this.objectsCode.forEach((v, k) => {
- console.log('Executing code of object with id ', k);
this.isRunning.set(true);
this.runInterpreter(v, k);
});
@@ -413,7 +412,6 @@ export class ActivityDetailComponent implements AfterViewInit, OnDestroy {
if (this.runningInterpreters === 0) {
this.isRunning.set(false);
- console.log('Execution finished');
}
}
}
diff --git a/src/app/services/blockly.service.ts b/src/app/services/blockly.service.ts
index 1931809..ec14a11 100644
--- a/src/app/services/blockly.service.ts
+++ b/src/app/services/blockly.service.ts
@@ -51,5 +51,10 @@ export class BlocklyService {
const innerCode = javascriptGenerator.statementToCode(block, 'statement');
return `while (true) {\n${innerCode}}\n`;
}
+
+ javascriptGenerator.forBlock['movement_set_direction'] = function (block: any) {
+ const direction = block.getFieldValue('direction');
+ return `setDirection("${direction}")\n`;
+ }
}
}