From e76d7f1fbd8fb1ccb743640bc5be338c45a9ca90 Mon Sep 17 00:00:00 2001 From: OriIIusion <442901055@qq.com> Date: Sun, 21 Jan 2024 20:16:00 +0800 Subject: [PATCH] feat(sample): add EatTheBox sample,add ShootTheBox sample --- samples/physics/Sample_EatTheBox.ts | 284 ++++++++++++++++++++++++++ samples/physics/Sample_ShootTheBox.ts | 208 +++++++++++++++++++ 2 files changed, 492 insertions(+) create mode 100644 samples/physics/Sample_EatTheBox.ts create mode 100644 samples/physics/Sample_ShootTheBox.ts diff --git a/samples/physics/Sample_EatTheBox.ts b/samples/physics/Sample_EatTheBox.ts new file mode 100644 index 00000000..8b389007 --- /dev/null +++ b/samples/physics/Sample_EatTheBox.ts @@ -0,0 +1,284 @@ +import { BoxGeometry, Camera3D, Engine3D, LitMaterial, MeshRenderer, Object3D, Scene3D, View3D, Object3DUtil, Vector3, AtmosphericComponent, ColliderComponent, BoxColliderShape, KeyEvent, SphereColliderShape, DirectLight, SphereGeometry, ComponentBase, KeyCode, KelvinUtil, Time } from "@orillusion/core"; +import { Stats } from "@orillusion/stats"; +import { Ammo, Physics, Rigidbody } from "@orillusion/physics"; +import dat from "dat.gui"; + +class Sample_EatTheBox { + view: View3D; + ammoWorld: Ammo.btDiscreteDynamicsWorld; + foods: Object3D[] = []; + dispatcher: Ammo.btDispatcher; + numManifolds: number; + Manifold: Ammo.btPersistentManifold; + objIndex: number; + tempObj: Object3D; + score: number = 0; + moveScript: MoveScript; + async run() { + //init physics and engine + await Physics.init(); + await Engine3D.init({ + renderLoop: () => this.loop() + }); + + //set shadow + Engine3D.setting.shadow.updateFrameRate = 1; + Engine3D.setting.shadow.shadowSize = 2048; + + //get original ammo world for processing more custom function + this.ammoWorld = Physics.world; + + //create scene,add sky and FPS + let scene = new Scene3D(); + let sky = scene.addComponent(AtmosphericComponent); + scene.addComponent(Stats); + + //create camera + let cameraObj = new Object3D(); + let camera = cameraObj.addComponent(Camera3D); + camera.enableCSM = true; + camera.perspective(60, Engine3D.aspect, 1, 5000); + camera.lookAt(new Vector3(0, 40, 35), new Vector3()); + scene.addChild(cameraObj); + + //add DirectLight + let lightObj = new Object3D(); + let light = lightObj.addComponent(DirectLight); + light.intensity = 50; + light.castShadow = true; + lightObj.rotationX = 60; + lightObj.rotationY = 80; + sky.relativeTransform = light.transform; + light.lightColor = KelvinUtil.color_temperature_to_rgb(5355); + scene.addChild(lightObj); + + //create view + this.view = new View3D(); + this.view.scene = scene; + this.view.camera = camera; + + //create floor and wall + this.createFloor(); + //create foods(box) + this.createFoods(); + //create player(ball) + this.createBall(); + //start render + Engine3D.startRenderView(this.view); + + //add debug UI + const gui = new dat.GUI(); + let tip = gui.addFolder("Orillusion"); + tip.add({ tip1: "press wasd to move" }, "tip1"); + tip.add({ tip2: "eat box to get score" }, "tip2"); + tip.add(this, "score").listen(); + tip.add(this.moveScript, "moveSpeed", 10, 50, 1); + tip.open(); + } + private createFloor() { + //create floor and wall + let floor = Object3DUtil.GetSingleCube(50, 1, 50, 0.3, 0.3, 0.6); + let border1 = Object3DUtil.GetSingleCube(50, 5, 1, 0.3, 0.3, 0.6); + let border2 = Object3DUtil.GetSingleCube(50, 5, 1, 0.3, 0.3, 0.6); + let border3 = Object3DUtil.GetSingleCube(50, 5, 1, 0.3, 0.3, 0.6); + let border4 = Object3DUtil.GetSingleCube(50, 5, 1, 0.3, 0.3, 0.6); + //set their mass to 0,because they are static + let rigidbody = floor.addComponent(Rigidbody); + let rigidbody1 = border1.addComponent(Rigidbody); + let rigidbody2 = border2.addComponent(Rigidbody); + let rigidbody3 = border3.addComponent(Rigidbody); + let rigidbody4 = border4.addComponent(Rigidbody); + rigidbody.mass = rigidbody1.mass = rigidbody2.mass = rigidbody3.mass = rigidbody4.mass = 0; + //add collider component ,collider shape and size is same as their geometry + let collider = floor.addComponent(ColliderComponent); + collider.shape = new BoxColliderShape(); + collider.shape.size = new Vector3(50, 1, 50); + let colshape = new BoxColliderShape(); + colshape.size = new Vector3(50, 5, 1); + let collider1 = border1.addComponent(ColliderComponent); + let collider2 = border2.addComponent(ColliderComponent); + let collider3 = border3.addComponent(ColliderComponent); + let collider4 = border4.addComponent(ColliderComponent); + collider1.shape = collider2.shape = collider3.shape = collider4.shape = colshape; + //place the floor and walls + border1.y = 3; + border1.z = 24.5; + border2.y = 3; + border2.z = -24.5; + border3.y = 3; + border3.x = 24.5; + border3.rotationY = 90; + border4.y = 3; + border4.x = -24.5; + border4.rotationY = 90; + //set friction and restitution + rigidbody.rollingFriction = 10; + rigidbody1.restitution = rigidbody2.restitution = rigidbody3.restitution = rigidbody4.restitution = 0.3; + //set their index to -1 + rigidbody.addInitedFunction(() => { + rigidbody.btRigidbody.setUserIndex(-1); + }, this); + rigidbody1.addInitedFunction(() => { + rigidbody1.btRigidbody.setUserIndex(-1); + }, this); + rigidbody2.addInitedFunction(() => { + rigidbody2.btRigidbody.setUserIndex(-1); + }, this); + rigidbody3.addInitedFunction(() => { + rigidbody3.btRigidbody.setUserIndex(-1); + }, this); + rigidbody4.addInitedFunction(() => { + rigidbody4.btRigidbody.setUserIndex(-1); + }, this); + this.view.scene.addChild(floor); + this.view.scene.addChild(border1); + this.view.scene.addChild(border2); + this.view.scene.addChild(border3); + this.view.scene.addChild(border4); + } + private createFoods() { + //create the sample food + let boxobj = new Object3D(); + let mr = boxobj.addComponent(MeshRenderer); + mr.geometry = new BoxGeometry(2, 2, 2); + mr.material = new LitMaterial(); + let boxColliderShape = new BoxColliderShape(); + boxColliderShape.size = new Vector3(2, 2, 2); + //create 10 food box + for (let index = 0; index < 10; index++) { + let boxObj = boxobj.clone(); + boxObj.y = 2; + //random position + boxObj.x = Math.random() * 40 - 20; + boxObj.z = Math.random() * 40 - 20; + let rig = boxObj.addComponent(Rigidbody); + rig.mass = 0; + let col = boxObj.addComponent(ColliderComponent); + col.shape = boxColliderShape; + rig.addInitedFunction(() => { + //get original rigidbody to get/set more property + let btrig = rig.btRigidbody; + //set this colider as trigger,trigger will not respond to collision + btrig.setCollisionFlags(4); + //set index to 0~9 + btrig.setUserIndex(index); + this.foods[index] = boxObj; + }, this); + boxObj.addComponent(RotateScript); + this.view.scene.addChild(boxObj); + } + } + private createBall() { + //add player(ball) + let sphereObj = new Object3D(); + let mr = sphereObj.addComponent(MeshRenderer); + mr.geometry = new SphereGeometry(1, 20, 20); + let mat = new LitMaterial(); + mr.material = mat; + mat.baseColor = KelvinUtil.color_temperature_to_rgb(1325); + sphereObj.y = 5; + //add movescript + this.moveScript = sphereObj.addComponent(MoveScript); + this.moveScript.rigidbody = sphereObj.addComponent(Rigidbody); + this.moveScript.rigidbody.addInitedFunction(() => { + this.moveScript.rigidbody.btRigidbody.setUserIndex(-1); + }, this); + this.moveScript.rigidbody.mass = 10; + let collider = sphereObj.addComponent(ColliderComponent); + collider.shape = new SphereColliderShape(1); + this.view.scene.addChild(sphereObj); + } + private loop() { + if (Physics.isInited) { + //get ammo world collision info + this.dispatcher = this.ammoWorld.getDispatcher(); + //get count of collision info + this.numManifolds = this.dispatcher.getNumManifolds(); + if (this.numManifolds > 0) { + //iterate all collision info + for (let index = 0; index < this.numManifolds; index++) { + this.Manifold = this.dispatcher.getManifoldByIndexInternal(index); + //detect ammo rigidbody's userindex,if greater than -1,the box(food) is colliding + if (this.Manifold.getBody0().getUserIndex() > -1 || this.Manifold.getBody1().getUserIndex() > -1) { + this.objIndex = Math.max(this.Manifold.getBody0().getUserIndex(), this.Manifold.getBody1().getUserIndex()); + //destroy this box + this.tempObj = this.foods[this.objIndex]; + if (this.tempObj) { + this.foods[this.objIndex] = undefined; + this.score++; + this.tempObj.destroy(); + } + } + } + } + Physics.update(); + } + } +} + +class MoveScript extends ComponentBase { + forward: boolean = false; + back: boolean = false; + left: boolean = false; + right: boolean = false; + moveSpeed: number = 30; + rigidbody: Rigidbody; + x: number = 0; + y: number = 0; + direction: Vector3 = new Vector3(); + init(): void { + Engine3D.inputSystem.addEventListener(KeyEvent.KEY_DOWN, this.keyDown, this); + Engine3D.inputSystem.addEventListener(KeyEvent.KEY_UP, this.keyUp, this); + } + private keyDown(e: KeyEvent) { + if (e.keyCode == KeyCode.Key_A) { + this.left = true; + } + else if (e.keyCode == KeyCode.Key_D) { + this.right = true; + } + else if (e.keyCode == KeyCode.Key_W) { + this.forward = true; + } + else if (e.keyCode == KeyCode.Key_S) { + this.back = true; + } + } + private keyUp(e: KeyEvent) { + if (e.keyCode == KeyCode.Key_A) { + this.left = false; + } + else if (e.keyCode == KeyCode.Key_D) { + this.right = false; + } + else if (e.keyCode == KeyCode.Key_W) { + this.forward = false; + } + else if (e.keyCode == KeyCode.Key_S) { + this.back = false; + } + } + + onUpdate() { + //if w or a or s or d was pressed + if (this.forward || this.back || this.left || this.right) { + //activate ammo btrigidbody if its state is inactive + if (!this.rigidbody.btRigidbody.isActive()) { + this.rigidbody.btRigidbody.activate(); + } + //force the ball to move + this.x = -1 * Number(this.left) + Number(this.right); + this.y = -1 * Number(this.forward) + Number(this.back); + this.direction.set(this.x * this.moveSpeed * Time.delta, 0, this.y * this.moveSpeed * Time.delta); + this.rigidbody.velocity = this.direction; + } + } +} +//rotate script +class RotateScript extends ComponentBase { + onUpdate() { + this.object3D.rotationY += 90 * Time.delta / 1000; + } +} + +new Sample_EatTheBox().run(); \ No newline at end of file diff --git a/samples/physics/Sample_ShootTheBox.ts b/samples/physics/Sample_ShootTheBox.ts new file mode 100644 index 00000000..ee154941 --- /dev/null +++ b/samples/physics/Sample_ShootTheBox.ts @@ -0,0 +1,208 @@ +import dat from "dat.gui"; +import { BoxGeometry, Camera3D, Engine3D, LitMaterial, MeshRenderer, Object3D, Scene3D, View3D, Color, Object3DUtil, Vector3, AtmosphericComponent, HoverCameraController, ColliderComponent, BoxColliderShape, KeyEvent, SphereColliderShape, DirectLight, SphereGeometry, ComponentBase, KeyCode, KelvinUtil, Time } from "@orillusion/core"; +import { Stats } from "@orillusion/stats"; +import { Physics, Rigidbody } from "@orillusion/physics"; + +class Sample_ShootTheBox { + aim: Object3D; + moveScript: MoveScript; + view: View3D; + ballSpeed: Vector3 = new Vector3(0, 0, -30000); + async run() { + //init Physics System + await Physics.init(); + await Engine3D.init({ + //make Physics System continuously effective + renderLoop: () => { + if (Physics.isInited) { + Physics.update(); + } + } + }); + //update shadow every frame + Engine3D.setting.shadow.updateFrameRate = 1; + + //add keydown event listener + Engine3D.inputSystem.addEventListener(KeyEvent.KEY_DOWN, this.keyDown, this); + + //create scene,add sky and FPS + let scene = new Scene3D(); + let sky = scene.addComponent(AtmosphericComponent); + scene.addComponent(Stats); + + //create camera + let cameraObj = new Object3D(); + let camera = cameraObj.addComponent(Camera3D); + camera.enableCSM = true; + camera.perspective(60, Engine3D.aspect, 1, 5000); + let con = cameraObj.addComponent(HoverCameraController); + con.setCamera(0, -30, 50); + scene.addChild(cameraObj); + + //add DirectLight + let lightObj = new Object3D(); + let light = lightObj.addComponent(DirectLight); + light.intensity = 50; + light.castShadow = true; + lightObj.rotationX = 60; + lightObj.rotationY = 140; + sky.relativeTransform = light.transform; + scene.addChild(lightObj); + + //add a floor + { + let floor = Object3DUtil.GetSingleCube(50, 1, 50, 0.3, 0.3, 0.3); + floor.y = -0.5; + let rigidBody = floor.addComponent(Rigidbody); + //set static object mass(0) + rigidBody.mass = 0; + let collider = floor.addComponent(ColliderComponent); + collider.shape = new BoxColliderShape(); + collider.shape.size = new Vector3(50, 1, 50); + scene.addChild(floor); + } + let mats = [new LitMaterial(), new LitMaterial(), new LitMaterial(), new LitMaterial(), new LitMaterial()]; + { + let box = new Object3D(); + let mr = box.addComponent(MeshRenderer); + mr.geometry = new BoxGeometry(2, 2, 2); + mats.forEach(element => { + element.baseColor = Color.random(); + }); + //add 100 box with different color + for (let i = 0; i < 10; i++) { + for (let j = 0; j < 10; j++) { + let b = box.clone(); + b.x = 2 * i - 9; + b.y = 2 * j + 1; + b.z = -10; + b.getComponent(MeshRenderer).material = mats[Math.floor(Math.random() * 5)]; + let rig = b.addComponent(Rigidbody); + rig.mass = 10; + let col = b.addComponent(ColliderComponent); + col.shape = new BoxColliderShape(); + col.shape.size = new Vector3(2, 2, 2); + scene.addChild(b); + } + } + } + + //add the aiming point + { + this.aim = new Object3D(); + let aim1 = Object3DUtil.GetSingleCube(0.5, 2, 0.5, 0.8, 0.2, 0.1); + this.aim.addChild(aim1); + let aim2 = Object3DUtil.GetSingleCube(0.5, 2, 0.5, 0.8, 0.2, 0.1); + aim2.rotationZ = 90; + this.aim.addChild(aim2); + this.aim.z = 10; + this.aim.y = 10; + this.moveScript = this.aim.addComponent(MoveScript); + scene.addChild(this.aim); + } + + //add a ball as prefeb + let sphereObj = new Object3D(); + let mr = sphereObj.addComponent(MeshRenderer); + mr.geometry = new SphereGeometry(1, 20, 20); + let mat = new LitMaterial(); + mr.material = mat; + mat.baseColor = KelvinUtil.color_temperature_to_rgb(1325); + Engine3D.res.addPrefab("ball", sphereObj); + + //add some tips + const gui = new dat.GUI(); + let tip = gui.addFolder("Tips"); + let tips = { + tip1: "press WASD to move", + tip2: "press space to fire" + }; + tip.add(tips, "tip1"); + tip.add(tips, "tip2"); + tip.open(); + let speed = gui.addFolder("Speed"); + speed.add({ ballSpeed: 30 }, "ballSpeed", 10, 60, 1).onChange((v) => { + this.ballSpeed.z = -1 * v * 1000; + }); + speed.add(this.moveScript, "moveSpeed", 1, 50, 1); + speed.open(); + + //start render + this.view = new View3D(); + this.view.scene = scene; + this.view.camera = camera; + Engine3D.startRenderView(this.view); + } + private keyDown(e: KeyEvent) { + //fire only when pressed the space key + if (e.keyCode == KeyCode.Key_Space) { + let sphereObj = Engine3D.res.getPrefab("ball"); + let collider = sphereObj.addComponent(ColliderComponent); + collider.shape = new SphereColliderShape(1); + let rigidBody = sphereObj.addComponent(Rigidbody); + rigidBody.mass = 10; + //set velocity after rigidbody inited + rigidBody.addInitedFunction(() => { + rigidBody.velocity = this.ballSpeed; + }, this); + sphereObj.transform.localPosition = this.aim.localPosition; + this.view.scene.addChild(sphereObj); + } + } +} +//move script +class MoveScript extends ComponentBase { + up: boolean; + down: boolean; + left: boolean; + right: boolean; + moveSpeed: number = 10; + init(): void { + Engine3D.inputSystem.addEventListener(KeyEvent.KEY_DOWN, this.keyDown, this); + Engine3D.inputSystem.addEventListener(KeyEvent.KEY_UP, this.keyUp, this); + } + private keyDown(e: KeyEvent) { + if (e.keyCode == KeyCode.Key_A) { + this.left = true; + } + else if (e.keyCode == KeyCode.Key_D) { + this.right = true; + } + else if (e.keyCode == KeyCode.Key_W) { + this.up = true; + } + else if (e.keyCode == KeyCode.Key_S) { + this.down = true; + } + } + private keyUp(e: KeyEvent) { + if (e.keyCode == KeyCode.Key_A) { + this.left = false; + } + else if (e.keyCode == KeyCode.Key_D) { + this.right = false; + } + else if (e.keyCode == KeyCode.Key_W) { + this.up = false; + } + else if (e.keyCode == KeyCode.Key_S) { + this.down = false; + } + } + onUpdate() { + if (this.up) { + this.transform.y += this.moveSpeed * Time.delta / 1000; + } + else if (this.down) { + this.transform.y -= this.moveSpeed * Time.delta / 1000; + } + else if (this.left) { + this.transform.x -= this.moveSpeed * Time.delta / 1000; + } + else if (this.right) { + this.transform.x += this.moveSpeed * Time.delta / 1000; + } + } +} + +new Sample_ShootTheBox().run(); \ No newline at end of file