Specialized components
World editor
Editor control functions
22 min
overview the editor control functions component is a fundamental element of the ir engine's world editor that provides a centralized set of operations for manipulating entities and components it implements an internal api that abstracts common editing tasks, enabling consistent behavior across different parts of the user interface by separating the implementation of editing operations from the ui elements that trigger them, this component creates a modular and maintainable architecture this chapter explores the implementation, usage, and benefits of the editor control functions within the world editor core concepts function architecture the editor control functions follow a consistent architecture centralization core editing operations are defined in a single location abstraction functions hide implementation details from ui components consistency common operations use the same underlying logic validation input parameters are checked before operations are performed feedback operations provide appropriate return values and error handling this architecture creates a reliable foundation for editor operations operation types the editor supports various types of operations entity operations creating, duplicating, and removing entities component operations adding, removing, and modifying components selection operations selecting, deselecting, and querying entities hierarchy operations managing parent child relationships transform operations manipulating position, rotation, and scale utility operations common helper functions for editor tasks these operations cover the essential editing capabilities of the world editor state integration the control functions integrate with the editor's state management scene modification operations mark the scene as modified selection updates selection state is updated after operations undo/redo operations can be recorded for undo/redo functionality event notification state changes trigger appropriate ui updates validation operations check state conditions before execution this integration ensures that the editor's state remains consistent implementation entity creation the entity creation function creates new entities in the scene // simplified from src/functions/editorcontrolfunctions ts import { createentity, addcomponent } from '@ir engine/ecs'; import { v4 as uuidv4 } from 'uuid'; import { editorstate } from ' /services/editorservices'; import { scenestate } from ' /services/scenestate'; / creates a new entity in the scene @param options creation options @returns the created entity / export const createobject = (options { name? string; parentid? string; beforeentityid? string; components? array<{ type string; data any; }>; }) entity => { const { name = 'new entity', parentid, beforeentityid, components = \[] } = options; // create the entity const entity = createentity(); // add uuid component addcomponent(entity, 'uuid', { id uuidv4() }); // add name component addcomponent(entity, 'name', { value name }); // add transform component addcomponent(entity, 'transform', { position { x 0, y 0, z 0 }, rotation { x 0, y 0, z 0 }, scale { x 1, y 1, z 1 } }); // add hierarchy component if parent specified if (parentid) { const parententity = findentitybyid(parentid); if (parententity) { // add to parent's children const parenthierarchy = getcomponent(parententity, 'hierarchy') || { children \[] }; if (beforeentityid) { // insert at specific position const index = parenthierarchy children indexof(beforeentityid); if (index >= 0) { parenthierarchy children splice(index, 0, entity id); } else { parenthierarchy children push(entity id); } } else { // add to end parenthierarchy children push(entity id); } addcomponent(parententity, 'hierarchy', parenthierarchy); } } // add additional components for (const component of components) { addcomponent(entity, component type, component data); } // add to scene const scenestate = getmutablestate(scenestate); scenestate entities merge(\[entity]); // mark scene as modified const editorstate = getmutablestate(editorstate); editorstate ismodified set(true); return entity; }; this function creates a new entity with a unique identifier adds basic components like name and transform handles parent child relationships in the hierarchy adds any additional components specified in the options updates the scene state and marks it as modified entity duplication the entity duplication function creates copies of existing entities // simplified from src/functions/editorcontrolfunctions ts import { createentity, addcomponent, getcomponent, getallcomponents } from '@ir engine/ecs'; import { v4 as uuidv4 } from 'uuid'; import { editorstate } from ' /services/editorservices'; import { scenestate } from ' /services/scenestate'; / duplicates entities in the scene @param entityids ids of entities to duplicate @returns the duplicated entities / export const duplicateobjects = (entityids string\[]) entity\[] => { const duplicatedentities entity\[] = \[]; const scenestate = getmutablestate(scenestate); for (const entityid of entityids) { // find the original entity const originalentity = scenestate entities value find(e => e id === entityid); if (!originalentity) continue; // skip scene root if (getcomponent(originalentity, 'scene')) continue; // create new entity const newentity = createentity(); // add uuid component addcomponent(newentity, 'uuid', { id uuidv4() }); // copy name component with " copy" suffix const namecomponent = getcomponent(originalentity, 'name'); addcomponent(newentity, 'name', { value namecomponent ? `${namecomponent value} copy` 'copy' }); // copy all other components except uuid and special components const components = getallcomponents(originalentity); for (const \[type, data] of object entries(components)) { if (type === 'uuid' || type === 'scene') continue; // deep copy component data const componentdata = json parse(json stringify(data)); // special handling for hierarchy component if (type === 'hierarchy') { // don't copy children references componentdata children = \[]; } addcomponent(newentity, type, componentdata); } // add to same parent as original const parententity = findparententity(originalentity); if (parententity) { const parenthierarchy = getcomponent(parententity, 'hierarchy') || { children \[] }; // add after the original in the hierarchy const index = parenthierarchy children indexof(entityid); if (index >= 0) { parenthierarchy children splice(index + 1, 0, newentity id); } else { parenthierarchy children push(newentity id); } addcomponent(parententity, 'hierarchy', parenthierarchy); } // add to scene scenestate entities merge(\[newentity]); duplicatedentities push(newentity); } // mark scene as modified const editorstate = getmutablestate(editorstate); editorstate ismodified set(true); // select the duplicated entities editorstate selectedentityids set(duplicatedentities map(e => e id)); return duplicatedentities; }; this function creates new entities for each entity being duplicated copies all components and their data from the original entities handles special cases like hierarchy components places duplicates in the same parent as the originals selects the newly created duplicates entity removal the entity removal function deletes entities from the scene // simplified from src/functions/editorcontrolfunctions ts import { removeentity, hascomponent, getcomponent } from '@ir engine/ecs'; import { editorstate } from ' /services/editorservices'; import { scenestate } from ' /services/scenestate'; / removes entities from the scene @param entityids ids of entities to remove @returns success status / export const removeobjects = (entityids string\[]) boolean => { const scenestate = getmutablestate(scenestate); const editorstate = getmutablestate(editorstate); // deselect entities that will be removed const currentselection = editorstate selectedentityids value; const newselection = currentselection filter(id => !entityids includes(id)); editorstate selectedentityids set(newselection); for (const entityid of entityids) { // find the entity const entity = scenestate entities value find(e => e id === entityid); if (!entity) continue; // skip scene root if (hascomponent(entity, 'scene')) continue; // remove from parent's hierarchy const parententity = findparententity(entity); if (parententity) { const parenthierarchy = getcomponent(parententity, 'hierarchy'); if (parenthierarchy) { const childindex = parenthierarchy children indexof(entityid); if (childindex >= 0) { parenthierarchy children splice(childindex, 1); addcomponent(parententity, 'hierarchy', parenthierarchy); } } } // remove the entity removeentity(entity); // update entity list scenestate entities set(scenestate entities value filter(e => e id !== entityid)); } // mark scene as modified editorstate ismodified set(true); return true; }; this function deselects entities that will be removed removes entities from their parent's hierarchy deletes the entities and their components updates the scene's entity list marks the scene as modified component modification the component modification function updates component properties // simplified from src/functions/editorcontrolfunctions ts import { getcomponent, updatecomponent } from '@ir engine/ecs'; import { editorstate } from ' /services/editorservices'; import { scenestate } from ' /services/scenestate'; / modifies component properties on entities @param entityids ids of entities to modify @param componenttype type of component to modify @param properties properties to update @returns success status / export const modifyproperties = ( entityids string\[], componenttype string, properties record\<string, any> ) boolean => { const scenestate = getmutablestate(scenestate); const editorstate = getmutablestate(editorstate); for (const entityid of entityids) { // find the entity const entity = scenestate entities value find(e => e id === entityid); if (!entity) continue; // check if entity has the component if (!hascomponent(entity, componenttype)) continue; // get current component data const component = getcomponent(entity, componenttype); // update properties const updatedcomponent = { component, properties }; // apply updated component updatecomponent(entity, componenttype, updatedcomponent); } // mark scene as modified editorstate ismodified set(true); return true; }; this function finds the entities to modify checks if each entity has the specified component gets the current component data updates the specified properties applies the changes and marks the scene as modified component management the component management function adds or removes components // simplified from src/functions/editorcontrolfunctions ts import { hascomponent, addcomponent, removecomponent } from '@ir engine/ecs'; import { editorstate } from ' /services/editorservices'; import { componentregistry } from ' /services/componentregistry'; / adds or removes components from entities @param entityids ids of entities to modify @param componenttype type of component to add or remove @param add true to add, false to remove @param defaultdata default data for new components @returns success status / export const addorremovecomponent = ( entityids string\[], componenttype string, add boolean, defaultdata? any ) boolean => { const editorstate = getmutablestate(editorstate); for (const entityid of entityids) { // find the entity const entity = findentitybyid(entityid); if (!entity) continue; if (add) { // skip if entity already has the component if (hascomponent(entity, componenttype)) continue; // get default data if not provided const data = defaultdata || componentregistry getdefaultdata(componenttype); // add the component addcomponent(entity, componenttype, data); } else { // skip if entity doesn't have the component if (!hascomponent(entity, componenttype)) continue; // remove the component removecomponent(entity, componenttype); } } // mark scene as modified editorstate ismodified set(true); return true; }; this function processes each entity in the provided list for addition, adds the component with default or provided data for removal, removes the component if it exists skips operations that don't apply (adding existing or removing non existent) marks the scene as modified after changes control function workflow the complete control function workflow follows this sequence sequencediagram participant user participant ui participant controlfunctions participant entitysystem participant editorstate user >>ui interacts with editor interface ui >>controlfunctions calls appropriate function controlfunctions >>controlfunctions validates parameters controlfunctions >>entitysystem performs entity/component operations entitysystem >>controlfunctions returns operation results controlfunctions >>editorstate updates editor state editorstate >>ui triggers ui updates ui >>user shows updated scene this diagram illustrates the user interacts with the editor interface (buttons, menus, etc ) the ui calls the appropriate control function the control function validates parameters and performs operations the entity system processes the requested changes the editor state is updated to reflect the changes the ui updates to show the modified scene integration with other components the editor control functions integrate with several other components of the world editor user interface control functions are called from various ui elements // example of ui integration import { createobject, removeobjects } from ' / /functions/editorcontrolfunctions'; / hierarchy panel context menu component @param props component properties @returns context menu component / export const hierarchycontextmenu react fc<{ entityid string; position { x number; y number }; onclose () => void; }> = ({ entityid, position, onclose }) => { // handle create empty entity const handlecreateempty = () => { createobject({ name 'new entity', parentid entityid }); onclose(); }; // handle delete entity const handledelete = () => { removeobjects(\[entityid]); onclose(); }; return ( \<div classname="context menu" style={{ left position x, top position y }}> \<div classname="context menu item" onclick={handlecreateempty}> create empty \</div> \<div classname="context menu item" onclick={handledelete}> delete \</div> \</div> ); }; this integration connects ui elements directly to control functions provides a clean separation between ui and logic ensures consistent behavior across different ui elements simplifies ui component implementation centralizes operation logic for better maintainability keyboard shortcuts control functions are triggered by keyboard shortcuts // example of keyboard shortcut integration import { duplicateobjects, removeobjects } from ' / /functions/editorcontrolfunctions'; import { editorstate } from ' / /services/editorservices'; / handles editor keyboard shortcuts @param event keyboard event / export const handlekeyboardshortcuts = (event keyboardevent) => { const editorstate = editorstate state; const selectedentityids = editorstate selectedentityids value; // skip if no entities are selected if (selectedentityids length === 0) return; // handle delete key if (event key === 'delete') { event preventdefault(); removeobjects(selectedentityids); } // handle duplicate (ctrl+d) if (event key === 'd' && (event ctrlkey || event metakey)) { event preventdefault(); duplicateobjects(selectedentityids); } // additional shortcuts }; this integration provides keyboard shortcuts for common operations uses the same control functions as ui elements ensures consistent behavior regardless of input method centralizes shortcut handling logic improves editor usability with keyboard navigation undo/redo system control functions integrate with the undo/redo system // example of undo/redo integration import { modifyproperties } from ' / /functions/editorcontrolfunctions'; import { undoredostate } from ' / /services/undoredostate'; / records an operation for undo/redo and executes it @param entityids entities to modify @param componenttype component type @param newproperties new property values @param oldproperties old property values for undo / export const recordandmodifyproperties = ( entityids string\[], componenttype string, newproperties record\<string, any>, oldproperties record\<string, any> ) => { // create undo operation const undooperation = { type 'modifyproperties', entityids, componenttype, properties oldproperties }; // create redo operation const redooperation = { type 'modifyproperties', entityids, componenttype, properties newproperties }; // record the operation undoredostate recordoperation(undooperation, redooperation); // execute the operation modifyproperties(entityids, componenttype, newproperties); }; this integration records operations for undo/redo functionality stores the information needed to reverse operations uses the same control functions for both initial execution and undo/redo maintains consistency between direct operations and undo/redo provides a reliable history of user actions benefits of control functions the editor control functions component provides several key advantages centralization consolidates editing logic in a single location consistency ensures the same behavior across different ui elements modularity separates ui implementation from editing logic maintainability makes it easier to update or fix editing operations extensibility provides a foundation for adding new editing capabilities reliability implements validation and error handling for operations testability enables unit testing of editing logic independent of ui these benefits create a more robust and maintainable foundation for the world editor next steps with an understanding of editor control functions, the next chapter explores how modal dialogs provide focused interaction for important decisions and inputs next modal dialog management docid\ xvquiahh8maso6ucv4jif