Skip to content
95 changes: 82 additions & 13 deletions src/extensions/microbitMore/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -1606,6 +1606,14 @@ class MbitMoreBlocks {
*/
get GESTURES_MENU () {
return [
{
text: formatMessage({
id: 'mbitMore.gesturesMenu.moved',
default: 'moved',
description: 'label for moved gesture in gesture picker for microbit more extension'
}),
value: 'MOVED'
},
{
text: formatMessage({
id: 'mbitMore.gesturesMenu.tiltUp',
Expand Down Expand Up @@ -1693,14 +1701,6 @@ class MbitMoreBlocks {
description: 'label for shaken gesture in gesture picker for microbit more extension'
}),
value: MbitMoreGestureName.SHAKE
},
{
text: formatMessage({
id: 'mbitMore.gesturesMenu.moved',
default: 'moved',
description: 'label for moved gesture in gesture picker for microbit more extension'
}),
value: 'MOVED'
}
];
}
Expand Down Expand Up @@ -2149,6 +2149,30 @@ class MbitMoreBlocks {
*/
this.prevGestureEvents = {};

/**
* The last time the "moved" event was fired.
* @type {number}
*/
this.lastMovedEventTime = null;

/**
* The last time any gesture event was detected.
* @type {number}
*/
this.lastGestureOccurredTime = null;

/**
* Whether the extension is waiting for a gap in movement before firing again.
* @type {boolean}
*/
this.isWaitingForGap = false;

/**
* The timer of updateLastGestureEvent.
* @type {number}
*/
this.updateLastGestureEventTimer = null;

/**
* The previous timestamps of pin events.
* @type {Object.<number, Object.<number, number>>}
Expand Down Expand Up @@ -2278,7 +2302,7 @@ class MbitMoreBlocks {
GESTURE: {
type: ArgumentType.STRING,
menu: 'gestures',
defaultValue: MbitMoreGestureName.SHAKE
defaultValue: 'MOVED'
}
}
},
Expand Down Expand Up @@ -2865,18 +2889,63 @@ class MbitMoreBlocks {
* @return {boolean} - true if the event raised.
*/
whenGesture (args) {
if (!this.updateLastGestureEventTimer) {
if (this.updateLastGestureEventTimer === null) {
this.updateLastGestureEventTimer = setTimeout(() => {
this.updatePrevGestureEvents();
this.updateLastGestureEventTimer = null;
}, this.runtime.currentStepTime);
}
const gestureName = args.GESTURE;
if (gestureName === 'MOVED') {
return Object.entries(this._peripheral.gestureEvents).some(([name, timestamp]) => {
if (!this.prevGestureEvents[name]) return true;
return timestamp !== this.prevGestureEvents[name];
const now = Date.now();
const stepTime = this.runtime.currentStepTime;
const gapThreshold = stepTime * 5; // 5フレームの空白
const timeoutThreshold = stepTime * 30; // 30フレームで強制発火

const changedGestures = [];
Object.entries(this._peripheral.gestureEvents).forEach(([name, timestamp]) => {
if (this.prevGestureEvents[name]) {
if (timestamp !== this.prevGestureEvents[name]) {
changedGestures.push(`${name}(${this.prevGestureEvents[name]}->${timestamp})`);
}
} else {
changedGestures.push(`${name}(new->${timestamp})`);
}
});

const eventDetected = changedGestures.length > 0;
if (eventDetected) {
this.lastGestureOccurredTime = now;
}

// 静止判定(隙間ができたか)
const timeSinceLastOccurred = this.lastGestureOccurredTime === null ?
Infinity : (now - this.lastGestureOccurredTime);
if (timeSinceLastOccurred >= gapThreshold) {
this.isWaitingForGap = false;
}

let shouldFire = false;

if (eventDetected) {
if (this.isWaitingForGap) {
// 動き続けている場合の強制発火判定
const timeSinceLastFired = this.lastMovedEventTime === null ?
Infinity : (now - this.lastMovedEventTime);
if (timeSinceLastFired >= timeoutThreshold) {
shouldFire = true;
}
} else {
shouldFire = true;
}
}

if (shouldFire) {
this.lastMovedEventTime = now;
this.isWaitingForGap = true;
}

return shouldFire;
}
const lastTimestamp =
this._peripheral.getGestureEventTimestamp(gestureName);
Expand Down
Loading