galacean / engine-toolkit

Some out-of-the-box utility features based on the Galacean engine.
https://oasisengine.cn
MIT License
75 stars 25 forks source link

Add box selection controls #216

Closed HypnosNova closed 1 year ago

HypnosNova commented 1 year ago

A useful tool for selecting entities which position point is in rectangle drawed on the screen. It could be used in editor or RTS games.

Demo: (It could be added into official website later)

/**
 * @title Box Selection Controls
 * @category Controls
 */
import {
  Camera,
  MeshRenderer,
  PrimitiveMesh,
  UnlitMaterial,
  WebGLEngine
} from "@galacean/engine";
import { OutlineManager } from "@galacean/engine-toolkit-outline";
import { BoxSelectionComponent } from "./BoxSelectionComponent";
import { BoxSelectionControls } from "./BoxSelectionControls";
import { BoxSelectionDomHelper } from "./BoxSelectionHelper";

WebGLEngine.create({ canvas: "canvas" }).then((engine) => {
  engine.canvas.resizeByClientSize();
  engine.run();
  engine.canvas.resizeByClientSize();

  const scene = engine.sceneManager.activeScene;
  const rootEntity = scene.createRootEntity();

  const cameraEntity = rootEntity.createChild("camera_entity");
  cameraEntity.addComponent(Camera);
  // add BoxSelectionControls to camera entity
  const controls = cameraEntity.addComponent(BoxSelectionControls);
  // helper is an optional property which is only used for visualization.
  controls.helper = new BoxSelectionDomHelper(engine.canvas._webCanvas.parentElement, {
    border: '1px solid #55aaff',
    backgroundColor: 'rgba(75, 160, 255, 0.3)',
  });
  cameraEntity.transform.setPosition(0, 0, 15);

  const outlineManager = cameraEntity.addComponent(OutlineManager);
  outlineManager.size = 2;

  const mesh = PrimitiveMesh.createCuboid(engine, 1, 1, 1);
  const material = new UnlitMaterial(engine);
  for (let i = 0; i < 25; i++) {
    const entity = rootEntity.createChild('e' + i);
    // Entity with BoxSelectionComponent can be seleted by controls.
    const select = entity.addComponent(BoxSelectionComponent);
    entity.transform.setPosition(-4 + (i % 5) * 2, -4 + Math.floor(i / 5) * 2, 0);
    const renderer = entity.addComponent(MeshRenderer);
    renderer.setMaterial(material);
    renderer.mesh = mesh;

    select.onSelect = () => {
      outlineManager.addEntity(entity);
    }
    select.onUnselect = () => {
      outlineManager.removeEntity(entity);
    }
  }
});

https://github.com/galacean/engine-toolkit/assets/7953802/073aee92-cd8d-489e-8e89-d3cbeb26ee84

codecov[bot] commented 1 year ago

Codecov Report

Patch coverage: 14.08% and project coverage change: -2.52% :warning:

Comparison is base (f39ea3b) 22.19% compared to head (28b0cb9) 19.67%. Report is 3 commits behind head on dev/1.1.

:exclamation: Current head 28b0cb9 differs from pull request most recent head 38c562a. Consider uploading reports for the commit 38c562a to get more accurate results

Additional details and impacted files ```diff @@ Coverage Diff @@ ## dev/1.1 #216 +/- ## =========================================== - Coverage 22.19% 19.67% -2.52% =========================================== Files 11 16 +5 Lines 473 686 +213 Branches 70 90 +20 =========================================== + Hits 105 135 +30 - Misses 364 547 +183 Partials 4 4 ``` | [Files Changed](https://app.codecov.io/gh/galacean/engine-toolkit/pull/216?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=galacean) | Coverage Δ | | |---|---|---| | [packages/controls/src/box-selection/PlaneMesh.ts](https://app.codecov.io/gh/galacean/engine-toolkit/pull/216?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=galacean#diff-cGFja2FnZXMvY29udHJvbHMvc3JjL2JveC1zZWxlY3Rpb24vUGxhbmVNZXNoLnRz) | `5.00% <5.00%> (ø)` | | | [...ontrols/src/box-selection/BoxSelectionComponent.ts](https://app.codecov.io/gh/galacean/engine-toolkit/pull/216?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=galacean#diff-cGFja2FnZXMvY29udHJvbHMvc3JjL2JveC1zZWxlY3Rpb24vQm94U2VsZWN0aW9uQ29tcG9uZW50LnRz) | `10.00% <10.00%> (ø)` | | | [...ntrols/src/box-selection/BoxSelectionSSMaterial.ts](https://app.codecov.io/gh/galacean/engine-toolkit/pull/216?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=galacean#diff-cGFja2FnZXMvY29udHJvbHMvc3JjL2JveC1zZWxlY3Rpb24vQm94U2VsZWN0aW9uU1NNYXRlcmlhbC50cw==) | `11.76% <11.76%> (ø)` | | | [...controls/src/box-selection/BoxSelectionControls.ts](https://app.codecov.io/gh/galacean/engine-toolkit/pull/216?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=galacean#diff-cGFja2FnZXMvY29udHJvbHMvc3JjL2JveC1zZWxlY3Rpb24vQm94U2VsZWN0aW9uQ29udHJvbHMudHM=) | `15.44% <15.44%> (ø)` | | | [...controls/src/box-selection/BoxSelectionSSHelper.ts](https://app.codecov.io/gh/galacean/engine-toolkit/pull/216?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=galacean#diff-cGFja2FnZXMvY29udHJvbHMvc3JjL2JveC1zZWxlY3Rpb24vQm94U2VsZWN0aW9uU1NIZWxwZXIudHM=) | `16.66% <16.66%> (ø)` | | ... and [1 file with indirect coverage changes](https://app.codecov.io/gh/galacean/engine-toolkit/pull/216/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=galacean)

:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.

JujieX commented 1 year ago

I think using initial engine feature, such as lineDrawer in toolkit, for the box instead of dom element would serves better for integrity

HypnosNova commented 1 year ago
/**
 * @title Box Selection Controls
 * @category Controls
 */
import {
  Camera,
  MeshRenderer,
  PrimitiveMesh,
  UnlitMaterial,
  WebGLEngine
} from "@galacean/engine";
import { OutlineManager } from "@galacean/engine-toolkit-outline";
import { BoxSelectionComponent } from "./BoxSelectionComponent";
import { BoxSelectionControls } from "./BoxSelectionControls";
// import { BoxSelectionDomHelper } from "./BoxSelectionDomHelper";
import { BoxSelectionSSHelper } from "./BoxSelectionSSHelper";

WebGLEngine.create({ canvas: "canvas" }).then((engine) => {
  engine.canvas.resizeByClientSize();
  engine.run();
  engine.canvas.resizeByClientSize();

  const scene = engine.sceneManager.activeScene;
  const rootEntity = scene.createRootEntity();

  const cameraEntity = rootEntity.createChild("camera_entity");
  cameraEntity.addComponent(Camera);
  // add BoxSelectionControls to camera entity
  const controls = cameraEntity.addComponent(BoxSelectionControls);
  // helper is an optional property which is only used for visualization.
  controls.helper = new BoxSelectionSSHelper(engine, rootEntity);
  // this helper is drawed by using div dom.
  // controls.helper = new BoxSelectionDomHelper(engine.canvas._webCanvas.parentElement, {
  //   border: '1px solid #55aaff',
  //   backgroundColor: 'rgba(75, 160, 255, 0.3)',
  // });
  cameraEntity.transform.setPosition(0, 0, 15);

  const outlineManager = cameraEntity.addComponent(OutlineManager);
  outlineManager.size = 2;

  const mesh = PrimitiveMesh.createCuboid(engine, 1, 1, 1);
  const material = new UnlitMaterial(engine);
  for (let i = 0; i < 25; i++) {
    const entity = rootEntity.createChild('e' + i);
    // Entity with BoxSelectionComponent can be seleted by controls.
    const select = entity.addComponent(BoxSelectionComponent);
    entity.transform.setPosition(-4 + (i % 5) * 2, -4 + Math.floor(i / 5) * 2, 0);
    const renderer = entity.addComponent(MeshRenderer);
    renderer.setMaterial(material);
    renderer.mesh = mesh;

    select.onSelect = () => {
      outlineManager.addEntity(entity);
    }
    select.onUnselect = () => {
      outlineManager.removeEntity(entity);
    }
  }
});