game_start_event and console_log_action
This commit is contained in:
parent
4b00dbfc00
commit
b3ed15fa30
|
|
@ -0,0 +1,23 @@
|
||||||
|
import * as Blockly from "blockly";
|
||||||
|
import { javascriptGenerator, Order } from "blockly/javascript";
|
||||||
|
|
||||||
|
Blockly.Blocks['console_log_action'] = {
|
||||||
|
init(this: Blockly.Block) {
|
||||||
|
this.appendValueInput('VALUE')
|
||||||
|
.setAlign(Blockly.inputs.Align.RIGHT)
|
||||||
|
.appendField('print');
|
||||||
|
this.setInputsInline(false)
|
||||||
|
this.setPreviousStatement(true, null);
|
||||||
|
this.setNextStatement(true, null);
|
||||||
|
this.setTooltip('Print value to console');
|
||||||
|
this.setHelpUrl('');
|
||||||
|
this.setColour(150);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
javascriptGenerator.forBlock['console_log_action'] = function (block, generator) {
|
||||||
|
const valueValue = generator.valueToCode(block, 'VALUE', Order.ATOMIC);
|
||||||
|
return `console.log(${valueValue});\n`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export { };
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './consoleLog';
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
export * from './events';
|
export * from './events';
|
||||||
export * from './values';
|
export * from './values';
|
||||||
|
export * from './actions';
|
||||||
export * from './theme';
|
export * from './theme';
|
||||||
export * from './generateCode';
|
export * from './generateCode';
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,10 @@ import { javascriptGenerator, Order } from "blockly/javascript";
|
||||||
Blockly.Blocks['current_object_value'] = {
|
Blockly.Blocks['current_object_value'] = {
|
||||||
init(this: Blockly.Block) {
|
init(this: Blockly.Block) {
|
||||||
this.appendEndRowInput('NAME')
|
this.appendEndRowInput('NAME')
|
||||||
.appendField('current object');
|
.appendField('Current object');
|
||||||
this.setInputsInline(false)
|
this.setInputsInline(false)
|
||||||
this.setOutput(true, 'String');
|
this.setOutput(true, 'String');
|
||||||
this.setTooltip('asd');
|
this.setTooltip('Returns id of current object instance');
|
||||||
this.setHelpUrl('asd');
|
|
||||||
this.setColour(315);
|
this.setColour(315);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
import * as Blockly from "blockly";
|
||||||
|
import { javascriptGenerator, Order } from "blockly/javascript";
|
||||||
|
|
||||||
|
Blockly.Blocks['current_object_type_value'] = {
|
||||||
|
init(this: Blockly.Block) {
|
||||||
|
this.appendEndRowInput('NAME')
|
||||||
|
.appendField('Current object type');
|
||||||
|
this.setInputsInline(false)
|
||||||
|
this.setOutput(true, 'String');
|
||||||
|
this.setTooltip('Returns id of current object type');
|
||||||
|
this.setColour(315);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
javascriptGenerator.forBlock['current_object_type_value'] = function (_block, _generator) {
|
||||||
|
return ['objectType.id', Order.NONE];
|
||||||
|
};
|
||||||
|
|
||||||
|
export { };
|
||||||
|
|
@ -1 +1,2 @@
|
||||||
export * from './currentObject';
|
export * from './currentObject';
|
||||||
|
export * from './currentObjectType';
|
||||||
|
|
|
||||||
|
|
@ -9,15 +9,44 @@ import '../../blockly';
|
||||||
import { gameTheme, generateCode } from '../../blockly';
|
import { gameTheme, generateCode } from '../../blockly';
|
||||||
|
|
||||||
const TOOLBOX: Blockly.utils.toolbox.ToolboxDefinition = {
|
const TOOLBOX: Blockly.utils.toolbox.ToolboxDefinition = {
|
||||||
kind: 'flyoutToolbox',
|
kind: 'categoryToolbox',
|
||||||
contents: [
|
contents: [
|
||||||
{ kind: 'block', type: 'text' },
|
{
|
||||||
{ kind: 'block', type: 'text_print' },
|
kind: 'category',
|
||||||
{ kind: 'block', type: 'game_start_event' },
|
name: 'Events',
|
||||||
{ kind: 'block', type: 'object_touch_start_event' },
|
colour: '180',
|
||||||
{ kind: 'block', type: 'player_touch_start_event' },
|
contents: [
|
||||||
{ kind: 'block', type: 'current_object_value' },
|
{ kind: 'block', type: 'game_start_event' },
|
||||||
|
{ kind: 'block', type: 'object_touch_start_event' },
|
||||||
|
{ kind: 'block', type: 'player_touch_start_event' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
kind: 'category',
|
||||||
|
name: 'Values',
|
||||||
|
colour: '260',
|
||||||
|
contents: [
|
||||||
|
{ kind: 'block', type: 'current_object_value' },
|
||||||
|
{ kind: 'block', type: 'current_object_type_value' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
kind: 'category',
|
||||||
|
name: 'Actions',
|
||||||
|
colour: '20',
|
||||||
|
contents: [
|
||||||
|
{ kind: 'block', type: 'console_log_action' },
|
||||||
|
{ kind: 'block', type: 'text_print' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
kind: 'category',
|
||||||
|
name: 'Text',
|
||||||
|
colour: '160',
|
||||||
|
contents: [
|
||||||
|
{ kind: 'block', type: 'text' },
|
||||||
|
],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,11 @@
|
||||||
import type { Game, ObjectType, RuntimeGameScene, RuntimeObjectInstance, RuntimeScene, World } from "../types";
|
import type { Game, RuntimeGameScene, RuntimeScene, World } from "../types";
|
||||||
import type { GameEventHandler, GameEventContext } from "../types/runtime/gameBus";
|
import type { GameEventHandler, GameEventContext, InternalGameEventBus } from "../types/runtime/gameBus";
|
||||||
import { clone } from "../utils";
|
import { clone } from "../utils";
|
||||||
import { populateRuntimeScene } from "../utils/runtime";
|
import { populateRuntimeScene } from "../utils/runtime";
|
||||||
|
import { ObjectApi } from "../utils/runtime/objectApi";
|
||||||
type OwnedGameEventHandler = {
|
|
||||||
handler: GameEventHandler;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class GameEventBus {
|
export class GameEventBus {
|
||||||
private handlers = new Map<string, OwnedGameEventHandler[]>();
|
private handlers = new Map<string, GameEventHandler[]>();
|
||||||
|
|
||||||
on(event: string, handler: GameEventHandler): void {
|
on(event: string, handler: GameEventHandler): void {
|
||||||
console.log('subscribing to ' + event, handler);
|
console.log('subscribing to ' + event, handler);
|
||||||
|
|
@ -17,8 +14,8 @@ export class GameEventBus {
|
||||||
set = [];
|
set = [];
|
||||||
this.handlers.set(event, set);
|
this.handlers.set(event, set);
|
||||||
}
|
}
|
||||||
if (!set.some((h) => h.handler === handler))
|
if (!set.includes(handler))
|
||||||
set.push({ handler });
|
set.push(handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
off(event: string, handler: GameEventHandler): void {
|
off(event: string, handler: GameEventHandler): void {
|
||||||
|
|
@ -27,14 +24,14 @@ export class GameEventBus {
|
||||||
if (!handlers)
|
if (!handlers)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this.handlers.set(event, handlers.filter((h) => h.handler !== handler));
|
this.handlers.set(event, handlers.filter((h) => h !== handler));
|
||||||
}
|
}
|
||||||
|
|
||||||
emit(event: string, context: GameEventContext): void {
|
emit(event: string, context: GameEventContext): void {
|
||||||
console.log('emitting ' + event);
|
console.log('emitting ' + event);
|
||||||
console.log(Array.from(this.handlers.entries()));
|
console.log(Array.from(this.handlers.entries()));
|
||||||
this.handlers.get(event)
|
this.handlers.get(event)
|
||||||
?.forEach((h) => h.handler(context));
|
?.forEach((handler) => handler(context));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -70,9 +67,10 @@ export class GameFactory {
|
||||||
world: World,
|
world: World,
|
||||||
scene: RuntimeGameScene,
|
scene: RuntimeGameScene,
|
||||||
eventBus: GameEventBus,
|
eventBus: GameEventBus,
|
||||||
|
|
||||||
): void {
|
): void {
|
||||||
|
|
||||||
const internalBus = {
|
const internalBus: InternalGameEventBus = {
|
||||||
onGameEvent: eventBus.on.bind(eventBus),
|
onGameEvent: eventBus.on.bind(eventBus),
|
||||||
offGameEvent: eventBus.off.bind(eventBus),
|
offGameEvent: eventBus.off.bind(eventBus),
|
||||||
};
|
};
|
||||||
|
|
@ -81,12 +79,13 @@ export class GameFactory {
|
||||||
Object.values(world.objectTypes)
|
Object.values(world.objectTypes)
|
||||||
.filter((ot) => ot.javascript)
|
.filter((ot) => ot.javascript)
|
||||||
.forEach((ot) => {
|
.forEach((ot) => {
|
||||||
const otCode = eval(`(function gameScript({onGameEvent, offGameEvent}, object, objectType) {${ot.javascript}})`);
|
const otCode = eval(`(function gameScript({onGameEvent, offGameEvent }, {api, object, objectType}) {${ot.javascript}})`);
|
||||||
|
const api = new ObjectApi(o, ot, world, scene);
|
||||||
|
|
||||||
Object.values(scene.objects)
|
Object.values(scene.objects)
|
||||||
.filter((o) => o.typeId == ot.id)
|
.filter((o) => o.typeId == ot.id)
|
||||||
.forEach((o) => {
|
.forEach((o) => {
|
||||||
otCode(internalBus, o, ot);
|
otCode(internalBus, { api, o, ot });
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,13 @@ import type { WorldState } from "./worldState";
|
||||||
import type { Game, Pos3, V3, R3, RuntimeGameScene } from "../types";
|
import type { Game, Pos3, V3, R3, RuntimeGameScene } from "../types";
|
||||||
import type { CameraProps } from "@react-three/fiber";
|
import type { CameraProps } from "@react-three/fiber";
|
||||||
import { GameEventBus, GameFactory } from "../model/gameFactory";
|
import { GameEventBus, GameFactory } from "../model/gameFactory";
|
||||||
import type { GameEventContext } from "../types/runtime/gameBus";
|
|
||||||
|
|
||||||
export class GameState {
|
export class GameState {
|
||||||
private readonly world: WorldState;
|
private readonly world: WorldState;
|
||||||
|
|
||||||
public isPaused: boolean = false;
|
public isPaused: boolean = false;
|
||||||
public time: number = 0;
|
public time: number = 0;
|
||||||
|
public isFirstTick: boolean;
|
||||||
public scene: RuntimeGameScene;
|
public scene: RuntimeGameScene;
|
||||||
|
|
||||||
private eventBus: GameEventBus;
|
private eventBus: GameEventBus;
|
||||||
|
|
@ -40,14 +40,13 @@ export class GameState {
|
||||||
const game = GameFactory.create(rawWorld);
|
const game = GameFactory.create(rawWorld);
|
||||||
this.isPaused = game.paused;
|
this.isPaused = game.paused;
|
||||||
this.time = game.time;
|
this.time = game.time;
|
||||||
|
this.isFirstTick = true;
|
||||||
this.scene = game.scene;
|
this.scene = game.scene;
|
||||||
|
|
||||||
const eventBus = new GameEventBus();
|
const eventBus = new GameEventBus();
|
||||||
this.eventBus = eventBus;
|
this.eventBus = eventBus;
|
||||||
GameFactory.evalGameScripts(rawWorld, game.scene, eventBus);
|
GameFactory.evalGameScripts(rawWorld, game.scene, eventBus);
|
||||||
|
|
||||||
this.emitEvent('game_start');
|
|
||||||
|
|
||||||
this._stopAutoSave = this.startAutoSave();
|
this._stopAutoSave = this.startAutoSave();
|
||||||
makeAutoObservable(
|
makeAutoObservable(
|
||||||
this,
|
this,
|
||||||
|
|
@ -145,5 +144,10 @@ export class GameState {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this.time += deltaTime;
|
this.time += deltaTime;
|
||||||
|
|
||||||
|
if (this.isFirstTick) {
|
||||||
|
this.isFirstTick = false;
|
||||||
|
this.emitEvent('game_start');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
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";
|
||||||
|
|
||||||
|
|
@ -6,6 +7,9 @@ export type ObjectInstanceRuntimeData = {
|
||||||
voxelGroups: VoxelGroup[];
|
voxelGroups: VoxelGroup[];
|
||||||
colliderMesh: [Float32Array, Uint32Array] | null;
|
colliderMesh: [Float32Array, Uint32Array] | null;
|
||||||
};
|
};
|
||||||
|
pendingActions: {
|
||||||
|
impulse: { direction: V3, amplitude: number };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Runtime<T extends ObjectInstance> = T & ObjectInstanceRuntimeData;
|
export type Runtime<T extends ObjectInstance> = T & ObjectInstanceRuntimeData;
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,19 @@
|
||||||
import type { Scene, World } from "../model";
|
import type { ObjectType, RuntimeGameObjectInstance, Scene, World } from "../model";
|
||||||
|
|
||||||
export type GameEventContext = {
|
export type GameEventContext = {
|
||||||
world: World;
|
world: World;
|
||||||
scene: Scene;
|
scene: Scene;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type GameEventHandler = (context: GameEventContext) => void;
|
// export type GameObjectEventContext = GameEventContext & {
|
||||||
|
// object: RuntimeGameObjectInstance;
|
||||||
|
// objectType: ObjectType;
|
||||||
|
// }
|
||||||
|
|
||||||
export type GameBus = {
|
export type GameEventHandler = (context: GameEventContext) => void;
|
||||||
onGameEvent: (
|
// export type GameObjectEventHandler = (context: GameObjectEventContext) => void;
|
||||||
event: string,
|
|
||||||
params: Record<string, unknown>,
|
export type InternalGameEventBus = {
|
||||||
callback: GameEventHandler,
|
onGameEvent: (event: string, handler: GameEventHandler) => void;
|
||||||
) => void;
|
offGameEvent: (event: string, handler: GameEventHandler) => void;
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
import type { ObjectType, RuntimeGameObjectInstance, RuntimeGameScene, V3, World } from "../../types";
|
||||||
|
|
||||||
|
export class ObjectApi {
|
||||||
|
private object: RuntimeGameObjectInstance;
|
||||||
|
private obectType: ObjectType;
|
||||||
|
private world: World;
|
||||||
|
private scene: RuntimeGameScene;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
object: RuntimeGameObjectInstance,
|
||||||
|
obectType: ObjectType,
|
||||||
|
world: World,
|
||||||
|
scene: RuntimeGameScene,
|
||||||
|
) {
|
||||||
|
this.object = object;
|
||||||
|
this.obectType = obectType;
|
||||||
|
this.world = world;
|
||||||
|
this.scene = scene;
|
||||||
|
}
|
||||||
|
|
||||||
|
public applyImpulse(direction: V3, amplitude: number) {
|
||||||
|
this.object.pendingActions.impulse = { direction, amplitude };
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue