object touch events implemented

This commit is contained in:
azykov@mail.ru
2026-06-13 13:18:03 +03:00
parent 209ff3f710
commit 1ed3203ec5
6 changed files with 40 additions and 13 deletions
@@ -1,13 +1,13 @@
import * as Blockly from "blockly"; import * as Blockly from "blockly";
import { javascriptGenerator } from "blockly/javascript"; import { javascriptGenerator, Order } from "blockly/javascript";
Blockly.Blocks['object_touch_start_event'] = { Blockly.Blocks['object_touch_start_event'] = {
init(this: Blockly.Block) { init(this: Blockly.Block) {
this.appendDummyInput('NAME') this.appendDummyInput()
.appendField('When'); .appendField('When');
this.appendValueInput('NAME') this.appendValueInput('OTHER_OBJECT')
.setCheck('Object'); .setCheck('Object');
this.appendDummyInput('NAME') this.appendDummyInput()
.appendField('touches me'); .appendField('touches me');
this.setStyle('event_hat'); this.setStyle('event_hat');
this.setNextStatement(true); this.setNextStatement(true);
@@ -15,8 +15,13 @@ Blockly.Blocks['object_touch_start_event'] = {
} }
}; };
javascriptGenerator.forBlock['object_touch_start_event'] = function (_block, _generator) { javascriptGenerator.forBlock['object_touch_start_event'] = function (block, generator) {
return ''; const otherObjectValue = generator.valueToCode(block, 'OTHER_OBJECT', Order.NONE) || 'undefined';
return `
const touchTarget = ${otherObjectValue};
if (!context.other || !touchTarget || context.other.id !== touchTarget.id)
return;
`;
}; };
export { }; export { };
+6 -3
View File
@@ -6,13 +6,18 @@ export function generateCode(workspace: Blockly.WorkspaceSvg): string {
let result = ''; let result = '';
for (const block of workspace.getTopBlocks(true)) { for (const block of workspace.getTopBlocks(true)) {
if (block.type.endsWith('_event')) { if (block.type.endsWith('_event')) {
let body = '';
const eventName = block.type.replace(/_event$/, '');
let body = javascriptGenerator.blockToCode(block, true) as string;
let next = block.getNextBlock(); let next = block.getNextBlock();
while (next) { while (next) {
// opt_thisOnly=true prevents scrub_ from also chaining — we do it manually // opt_thisOnly=true prevents scrub_ from also chaining — we do it manually
body += javascriptGenerator.blockToCode(next, true); body += javascriptGenerator.blockToCode(next, true);
next = next.getNextBlock(); next = next.getNextBlock();
} }
body = javascriptGenerator.prefixLines(body, javascriptGenerator.INDENT.repeat(2)); body = javascriptGenerator.prefixLines(body, javascriptGenerator.INDENT.repeat(2));
// const args = Array.from(block.getFields()) // const args = Array.from(block.getFields())
@@ -22,8 +27,6 @@ export function generateCode(workspace: Blockly.WorkspaceSvg): string {
//TODO use args //TODO use args
const eventName = block.type.replace(/_event$/, '');
result += `eventBus.on( result += `eventBus.on(
'${eventName}', '${eventName}',
function* handle_${eventName}(context) { function* handle_${eventName}(context) {
@@ -1,4 +1,4 @@
import type { ObjectType, RuntimeGameObjectInstance, RuntimeObjectInstance } from "../types"; import type { ObjectType, ObjectInstanceRigidBody, RuntimeGameObjectInstance, RuntimeObjectInstance } from "../types";
import { useEffect, useImperativeHandle, useRef, type Ref } from "react"; import { useEffect, useImperativeHandle, useRef, type Ref } from "react";
import { Euler, Quaternion, type Group } from "three"; import { Euler, Quaternion, type Group } from "three";
import { Instance, Instances } from "@react-three/drei"; import { Instance, Instances } from "@react-three/drei";
@@ -7,6 +7,7 @@ import { ConvexHullCollider, RapierRigidBody } from "@react-three/rapier";
import { SyncRigidBody } from "./SyncRigidBody"; import { SyncRigidBody } from "./SyncRigidBody";
import { reaction, runInAction } from "mobx"; import { reaction, runInAction } from "mobx";
import { v3toRapier } from "../utils"; import { v3toRapier } from "../utils";
import { state } from "../state";
export type ObjectViewInternalHandle = { export type ObjectViewInternalHandle = {
group: Group | null; group: Group | null;
@@ -56,7 +57,7 @@ export const ObjectViewInternal = function ({ object, objectType, ref, ...props
}, },
[object, object.id, props.isPlayer] [object, object.id, props.isPlayer]
); );
const gameObj = object as RuntimeGameObjectInstance; const gameObj = object as RuntimeGameObjectInstance;
useEffect( useEffect(
@@ -84,6 +85,19 @@ export const ObjectViewInternal = function ({ object, objectType, ref, ...props
gravityScale={object.gravityScale} gravityScale={object.gravityScale}
position={object.position} position={object.position}
rotation={object.rotation} rotation={object.rotation}
onCollisionEnter={props.isEditor ? undefined : (payload) => {
const game = state.game;
if (!game)
return;
const otherHandle = payload.other.rigidBody?.handle;
if (otherHandle === undefined)
return;
const otherObj = Object.values(game.scene.objects)
.find((o) => o.rb()?.handle === otherHandle);
game.emitEvent('object_touch_start', { object: gameObj, other: otherObj });
if (otherObj?.id === game.scene.playerObjectId)
game.emitEvent('player_touch_start', { object: gameObj, other: otherObj });
}}
onSync={(data) => { onSync={(data) => {
runInAction(() => { runInAction(() => {
object.position = data.position; object.position = data.position;
+3
View File
@@ -1,3 +1,4 @@
import { toJS } from "mobx";
import type { ScriptContext, ScriptCoroutineContext, ScriptGameEventHandler } from "../types"; import type { ScriptContext, ScriptCoroutineContext, ScriptGameEventHandler } from "../types";
import type { ScriptCoroutines } from "./coroutines"; import type { ScriptCoroutines } from "./coroutines";
@@ -36,6 +37,8 @@ export class ScriptGameEventBus {
} }
emit(event: string, context: Omit<ScriptContext, keyof ScriptCoroutineContext>): void { emit(event: string, context: Omit<ScriptContext, keyof ScriptCoroutineContext>): void {
console.log('emitting ' + event, toJS(context));
const coroutines = this.handlers[event] const coroutines = this.handlers[event]
?.map((h) => ({ ?.map((h) => ({
owner: h.coroutine.thisObject.id, owner: h.coroutine.thisObject.id,
+3 -2
View File
@@ -2,7 +2,8 @@ import type { V3 } from "../3d";
import type { VoxelGroup } from "../runtime"; import type { VoxelGroup } from "../runtime";
import type { ObjectInstance } from "./object"; import type { ObjectInstance } from "./object";
export type RigidBodyHandle = { export type ObjectInstanceRigidBody = {
handle: number; // rapier RigidBodyHandle
applyImpulse(impulse: { x: number; y: number; z: number }, wakeUp: boolean): void; applyImpulse(impulse: { x: number; y: number; z: number }, wakeUp: boolean): void;
setLinvel(vel: { x: number; y: number; z: number }, wakeUp: boolean): void; setLinvel(vel: { x: number; y: number; z: number }, wakeUp: boolean): void;
setTranslation(translation: { x: number; y: number; z: number }, wakeUp: boolean): void; setTranslation(translation: { x: number; y: number; z: number }, wakeUp: boolean): void;
@@ -18,7 +19,7 @@ export type ObjectInstanceRuntimeData = {
boundingBox: { min: V3; max: V3 }; boundingBox: { min: V3; max: V3 };
updatedAt: number; updatedAt: number;
}; };
rb: () => RigidBodyHandle | null; rb: () => ObjectInstanceRigidBody | null;
} }
export type Runtime<T extends ObjectInstance> = T & ObjectInstanceRuntimeData; export type Runtime<T extends ObjectInstance> = T & ObjectInstanceRuntimeData;
+1
View File
@@ -17,6 +17,7 @@ export type ScriptContext = ScriptCoroutineContext & {
world: World; world: World;
scene: Scene; scene: Scene;
object?: RuntimeGameObjectInstance; object?: RuntimeGameObjectInstance;
other?: RuntimeGameObjectInstance;
} }
export type ScriptGameEventHandler = (context: ScriptContext) => ScriptCoroutine; export type ScriptGameEventHandler = (context: ScriptContext) => ScriptCoroutine;