Soccer Components.

This commit is contained in:
Michael Mainguy 2023-09-19 07:23:01 -05:00
parent f8259670e7
commit b06a158523
9 changed files with 241 additions and 46 deletions

View File

@ -20,6 +20,7 @@ import workerUrl from "./worker?worker&url";
import {DiagramEventType} from "./diagram/diagramEntity"; import {DiagramEventType} from "./diagram/diagramEntity";
import {PeerjsNetworkConnection} from "./integration/peerjsNetworkConnection"; import {PeerjsNetworkConnection} from "./integration/peerjsNetworkConnection";
import {DiagramExporter} from "./util/diagramExporter"; import {DiagramExporter} from "./util/diagramExporter";
import {Field} from "./soccer/field";
export class App { export class App {
@ -116,6 +117,7 @@ export class App {
camera.attachControl(canvas, true); camera.attachControl(canvas, true);
new HemisphericLight("light1", new Vector3(1, 1, 0), scene); new HemisphericLight("light1", new Vector3(1, 1, 0), scene);
environment.groundMeshObservable.add(async (ground) => { environment.groundMeshObservable.add(async (ground) => {
const xr = await WebXRDefaultExperience.CreateAsync(scene, { const xr = await WebXRDefaultExperience.CreateAsync(scene, {
floorMeshes: [ground], floorMeshes: [ground],
@ -137,8 +139,8 @@ export class App {
}); });
}); });
xr.baseExperience.onStateChangedObservable.add((state) => {
xr.baseExperience.onStateChangedObservable.add((state) => {
if (state == WebXRState.IN_XR) { if (state == WebXRState.IN_XR) {
scene.audioEnabled = true; scene.audioEnabled = true;
xr.baseExperience.camera.position = new Vector3(0, 1.6, 0); xr.baseExperience.camera.position = new Vector3(0, 1.6, 0);
@ -153,6 +155,10 @@ export class App {
}); });
import('./controllers/rigplatform').then((rigmodule) => { import('./controllers/rigplatform').then((rigmodule) => {
const rig = new rigmodule.Rigplatform(scene, xr, diagramManager, controllers); const rig = new rigmodule.Rigplatform(scene, xr, diagramManager, controllers);
setTimeout(() => {
const field = new Field(scene);
field.addControllers(controllers);
}, 5000);
}); });
}); });

View File

@ -1,6 +1,7 @@
import { import {
AbstractMesh, AbstractMesh,
HavokPlugin, HavokPlugin,
Mesh,
PhysicsMotionType, PhysicsMotionType,
Scene, Scene,
TransformNode, TransformNode,
@ -108,18 +109,28 @@ export class Base {
} }
private grab() { private grab() {
const mesh = this.xr.pointerSelection.getMeshUnderPointer(this.controller.uniqueId); let mesh = this.xr.pointerSelection.getMeshUnderPointer(this.controller.uniqueId);
if (!mesh) { if (!mesh) {
return; return;
} }
let player = false;
const template = mesh?.metadata?.template; const template = mesh?.metadata?.template;
if (!template) { if (!template) {
if (mesh?.metadata?.handle == true) { if (mesh?.metadata?.handle == true) {
mesh && mesh.setParent(this.controller.motionController.rootMesh); mesh && mesh.setParent(this.controller.motionController.rootMesh);
this.grabbedMesh = mesh; this.grabbedMesh = mesh;
} else {
if (mesh?.parent?.parent?.metadata?.grabbable) {
if (mesh?.parent?.parent?.parent) {
mesh = (mesh?.parent?.parent?.parent as Mesh);
this.grabbedMesh = mesh;
player = true;
}
} else { } else {
return; return;
} }
}
} else { } else {
if (template == '#connection-template') { if (template == '#connection-template') {
return; return;
@ -131,7 +142,7 @@ export class Base {
this.previousScaling = mesh?.scaling.clone(); this.previousScaling = mesh?.scaling.clone();
this.previousPosition = mesh?.position.clone(); this.previousPosition = mesh?.position.clone();
if ("toolbox" != mesh?.parent?.parent?.id) { if (("toolbox" != mesh?.parent?.parent?.id) || player) {
if (mesh.physicsBody) { if (mesh.physicsBody) {
const transformNode = setupTransformNode(mesh, this.controller.motionController.rootMesh); const transformNode = setupTransformNode(mesh, this.controller.motionController.rootMesh);
mesh.physicsBody.setMotionType(PhysicsMotionType.ANIMATED); mesh.physicsBody.setMotionType(PhysicsMotionType.ANIMATED);
@ -196,7 +207,7 @@ export class Base {
this.previousRotation = null; this.previousRotation = null;
this.previousPosition = null; this.previousPosition = null;
this.grabbedMesh = null; this.grabbedMesh = null;
if (mesh?.metadata?.template.indexOf('#') == -1) { if (mesh?.metadata?.template && (mesh?.metadata?.template.indexOf('#') == -1)) {
return; return;
} }
const entity = toDiagramEntity(mesh); const entity = toDiagramEntity(mesh);

View File

@ -29,7 +29,8 @@ export enum ControllerEventType {
UP_DOWN = 'updown', UP_DOWN = 'updown',
TRIGGER = 'trigger', TRIGGER = 'trigger',
MENU = 'menu', MENU = 'menu',
MOTION = 'motion' MOTION = 'motion',
GAZEPOINT = 'gazepoint',
} }
export class Controllers { export class Controllers {

View File

@ -124,6 +124,26 @@ export class Rigplatform {
case ControllerEventType.MENU: case ControllerEventType.MENU:
this.bMenu.toggle(); this.bMenu.toggle();
break; break;
case ControllerEventType.TRIGGER:
const worldRay = this.scene.activeCamera.getForwardRay();
worldRay.origin = this.scene.activeCamera.globalPosition;
const pickInfo = this.scene.pickWithRay(worldRay, null);
if (pickInfo?.hit) {
const circle = MeshBuilder.CreateSphere("circle", {diameter: .02}, this.scene);
circle.position = pickInfo.pickedPoint;
setTimeout(() => {
circle.dispose();
}, 500);
if (pickInfo?.pickedMesh?.name == 'Football Ball.001') {
this.controllers.controllerObserver.notifyObservers({
type: ControllerEventType.GAZEPOINT,
endPosition: pickInfo.pickedPoint,
startPosition: this.xr.baseExperience.camera.globalPosition
})
}
}
break;
case ControllerEventType.MOTION: case ControllerEventType.MOTION:
console.log(JSON.stringify(event)); console.log(JSON.stringify(event));
this.buildKickLine(event.startPosition, event.endPosition); this.buildKickLine(event.startPosition, event.endPosition);

View File

@ -37,7 +37,7 @@ export function diagramEventHandler(event: DiagramEvent,
case DiagramEventType.DROPPED: case DiagramEventType.DROPPED:
break; break;
case DiagramEventType.DROP: case DiagramEventType.DROP:
if (mesh.metadata.template.indexOf('#') > -1) { if (mesh?.metadata?.template && (mesh.metadata.template.indexOf('#') > -1)) {
TextLabel.updateTextNode(mesh, entity.text); TextLabel.updateTextNode(mesh, entity.text);
} }
break; break;

View File

@ -52,8 +52,9 @@ export class Ball {
this.scene?.getPhysicsEngine()?.getPhysicsPlugin()) { this.scene?.getPhysicsEngine()?.getPhysicsPlugin()) {
console.log("creating physics aggregate"); console.log("creating physics aggregate");
this.physicsAggregate = new PhysicsAggregate(this.parent, this.physicsAggregate = new PhysicsAggregate(this.parent,
PhysicsShapeType.SPHERE, {mass: 1, restitution: .5, friction: .6}, this.scene); PhysicsShapeType.SPHERE, {mass: 1, restitution: .6, friction: .6}, this.scene);
this.physicsAggregate.body.setLinearDamping(.2); this.physicsAggregate.body.setLinearDamping(.3);
this.physicsAggregate.body.setAngularDamping(2);
this.mesh.setParent(this.physicsAggregate.transformNode); this.mesh.setParent(this.physicsAggregate.transformNode);
this.mesh.position.y = 0; this.mesh.position.y = 0;
return; return;

View File

@ -1,31 +1,61 @@
import {InstancedMesh, Mesh, MeshBuilder, Scene, StandardMaterial, Vector2, Vector3} from "@babylonjs/core"; import {
InstancedMesh,
Mesh,
MeshBuilder,
Scene,
StandardMaterial,
TransformNode,
Vector2,
Vector3
} from "@babylonjs/core";
import {Ball} from "./ball"; import {Ball} from "./ball";
import {Rigplatform} from "../controllers/rigplatform"; import {Team} from "./team";
import {ControllerEventType, Controllers} from "../controllers/controllers";
export class Field { export class Field {
private readonly scene: Scene; private readonly scene: Scene;
private ball: Ball; private ball: Ball;
private rig: Rigplatform; private controllers: Controllers;
private goalMesh: Mesh; private goalMesh: Mesh;
private material: StandardMaterial; private material: StandardMaterial;
private team1: Team;
private readonly fieldCenter: TransformNode;
private team2: Team;
private gazePoint: Vector3;
constructor(scene: Scene) { constructor(scene: Scene) {
this.scene = scene; this.scene = scene;
this.fieldCenter = new TransformNode("fieldCenter", this.scene);
this.team1 = new Team(scene, 1, "one");
this.team2 = new Team(scene, -1, "two");
this.goalMesh = MeshBuilder.CreateCylinder("goalPost", {diameter: .1, height: 1}, this.scene); this.goalMesh = MeshBuilder.CreateCylinder("goalPost", {diameter: .1, height: 1}, this.scene);
this.material = new StandardMaterial("material", this.scene); this.material = new StandardMaterial("material", this.scene);
this.material.diffuseColor.set(1, 1, 1); this.material.diffuseColor.set(1, 1, 1);
this.material.alpha = .5; this.material.alpha = .5;
this.goalMesh.material = this.getMaterial(); this.goalMesh.material = this.getMaterial();
this.goalMesh.setEnabled(false); this.goalMesh.setEnabled(false);
this.ball = new Ball(this.scene);
this.buildField(); this.buildField();
} }
public addBall(ball: Ball) { public addControllers(controllers: Controllers) {
this.ball = ball; this.controllers = controllers;
this.controllers.controllerObserver.add((event) => {
switch (event.type) {
case ControllerEventType.MOTION:
this.ball.kick(event.startPosition.clone().subtract(event.endPosition).normalize(), event.duration / 100);
break;
case ControllerEventType.GAZEPOINT:
if (event.endPosition) {
this.gazePoint = event.endPosition.clone();
}
break;
} }
public addRig(rig: Rigplatform) { });
this.rig = rig;
} }
@ -43,7 +73,6 @@ export class Field {
this.buildLine(new Vector2(-18.33, -50 + 8.25), new Vector2(width, 16.5 - width)); this.buildLine(new Vector2(-18.33, -50 + 8.25), new Vector2(width, 16.5 - width));
this.buildLine(new Vector2(18.33, -50 + 8.25), new Vector2(width, 16.5 - width)); this.buildLine(new Vector2(18.33, -50 + 8.25), new Vector2(width, 16.5 - width));
this.buildLine(new Vector2(0, -50), new Vector2(70 - width, width)); this.buildLine(new Vector2(0, -50), new Vector2(70 - width, width));
this.buildLine(new Vector2(0, 50), new Vector2(70 - width, width)); this.buildLine(new Vector2(0, 50), new Vector2(70 - width, width));
@ -83,6 +112,7 @@ export class Field {
} }
goalPost.scaling.y = length; goalPost.scaling.y = length;
goalPost.visibility = 1; goalPost.visibility = 1;
goalPost.setParent(this.fieldCenter);
} }
private buildCircle(position: Vector2) { private buildCircle(position: Vector2) {
@ -92,6 +122,7 @@ export class Field {
circle.position.y = .01; circle.position.y = .01;
circle.rotation.x = Math.PI / 2; circle.rotation.x = Math.PI / 2;
circle.material = this.getMaterial(); circle.material = this.getMaterial();
circle.setParent(this.fieldCenter);
} }
private buildArc(position: Vector2, arc: number = Math.PI, rotation: number = 0) { private buildArc(position: Vector2, arc: number = Math.PI, rotation: number = 0) {
@ -109,6 +140,7 @@ export class Field {
circle.position.x = position.x; circle.position.x = position.x;
circle.position.z = position.y; circle.position.z = position.y;
circle.rotation = new Vector3(0, rotation, 0); circle.rotation = new Vector3(0, rotation, 0);
circle.setParent(this.fieldCenter);
} }
private getMaterial(): StandardMaterial { private getMaterial(): StandardMaterial {
@ -125,5 +157,6 @@ export class Field {
line.position.y = .01; line.position.y = .01;
line.position.x = position.x; line.position.x = position.x;
line.position.z = position.y; line.position.z = position.y;
line.setParent(this.fieldCenter);
} }
} }

View File

@ -1,60 +1,137 @@
import { import {
AbstractMesh, AbstractMesh,
AnimationGroup, AnimationGroup,
AssetContainer,
Mesh,
MeshBuilder, MeshBuilder,
Observable, Observable,
PhysicsAggregate, PhysicsAggregate,
PhysicsMotionType,
PhysicsShapeType, PhysicsShapeType,
Scene, Scene,
SceneLoader, SceneLoader,
TransformNode, Skeleton,
Vector2, Vector2,
Vector3 Vector3
} from "@babylonjs/core"; } from "@babylonjs/core";
export class PlayerFactory {
public onReadyObservable: Observable<any> = new Observable();
private readonly scene: Scene;
private container: AssetContainer;
constructor(scene: Scene) {
this.scene = scene;
SceneLoader.LoadAssetContainer("/assets/models/",
"player2.glb",
this.scene,
(container: AssetContainer) => {
this.container = container;
this.onReadyObservable.notifyObservers(this);
});
}
public buildPlayer(position: Vector3, number: number, teamName: string = "team"): Player {
return new Player(this.scene, position, this.container, number, teamName);
}
}
export class Player { export class Player {
public readonly onReadyObservable: Observable<any> = new Observable(); public readonly onReadyObservable: Observable<any> = new Observable();
private readonly scene: Scene; private readonly scene: Scene;
private readonly position: Vector3; private position: Vector3;
private mesh: TransformNode; private mesh: Mesh;
private parent: AbstractMesh; private parent: AbstractMesh;
private animationGroup: AnimationGroup; private animationGroup: AnimationGroup;
private physicsAggregate: PhysicsAggregate; private physicsAggregate: PhysicsAggregate;
private skeleton: Skeleton;
private number: number;
private teamName: string;
private forward = true;
private destination: Vector2;
constructor(scene: Scene, position: Vector3) { constructor(scene: Scene, position: Vector3, container: AssetContainer, number: number = 0, teamName: string = "team") {
this.scene = scene; this.scene = scene;
this.position = position; this.position = position;
this.number = number;
this.teamName = teamName;
const data = container.instantiateModelsToScene(undefined, false, {doNotInstantiate: true});
this.mesh = (data.rootNodes[0] as Mesh);
this.skeleton = data.skeletons[0];
this.animationGroup = data.animationGroups[6];
this.buildPlayer(); this.buildPlayer();
} }
buildPlayer() { public lookAt(location: Vector2) {
SceneLoader.ImportMesh(null, "/assets/models/", "player2.glb", this.scene, const body = this.physicsAggregate.body;
(meshes, particleSystems, skeletons, animationGroups) => {
this.mesh = meshes[0]; body.disablePreStep = false;
this.parent = MeshBuilder.CreateCylinder("playerParent", {diameter: .5, height: 1.6}, this.scene);
this.parent.position = this.position; body.transformNode.lookAt(new Vector3(location.x, body.transformNode.position.y, location.y));
this.parent.isVisible = false; this.scene.onAfterRenderObservable.addOnce(() => {
this.physicsAggregate = new PhysicsAggregate(this.parent,
PhysicsShapeType.CYLINDER, {mass: 50, restitution: .02, friction: 0}, this.scene); this.physicsAggregate.body.disablePreStep = true;
animationGroups[0].stop(); this.animationGroup.stop();
this.animationGroup.onAnimationGroupEndObservable.add(() => {
if (this.forward) {
this.animationGroup.start(false, .1, 256, 267);
} else {
this.animationGroup.start(false, .1, 267, 256);
}
this.forward = !this.forward;
}, -1, false, this);
this.animationGroup.start(false, .1, 256, 267);
this.animationGroup = animationGroups[6];
this.animationGroup.start(false, 1, 266, 266);
this.mesh.setParent(this.physicsAggregate.transformNode);
this.mesh.position.x = 3;
this.mesh.position.y = -.84;
this.onReadyObservable.notifyObservers(this);
}); });
} }
public runTo(location: Vector2) { public runTo(location: Vector2) {
this.destination = location;
const body = this.physicsAggregate.transformNode.physicsBody;
body.setMotionType(PhysicsMotionType.ANIMATED);
body.disablePreStep = false;
body.transformNode.lookAt(new Vector3(location.x, body.transformNode.position.y, location.y));
const vel = new Vector3(location.x, body.transformNode.position.y, location.y);
this.scene.onBeforeRenderObservable.addOnce(() => {
body.setLinearVelocity(vel.subtract(body.transformNode.position).normalize().scale(2));
});
this.scene.onAfterRenderObservable.addOnce(() => {
this.physicsAggregate.body.disablePreStep = true;
this.animationGroup.stop(); this.animationGroup.stop();
this.animationGroup.start(true, 1.5, 0, 250); this.animationGroup.start(true, 1.0, 0, 250);
this.physicsAggregate.body.transformNode.lookAt(new Vector3(location.x, 2, location.y)); });
const speed = location.normalize().scale(2); this.scene.onAfterPhysicsObservable.add(() => {
this.physicsAggregate.body.setLinearVelocity(new Vector3(0, 0, 3)); if (body.getLinearVelocity().length() > .1) {
//this.physicsAggregate.body.setAngularVelocity(new Vector3(0, .1, 0)); console.log(this.destination.subtract(new Vector2(body.transformNode.position.x,
body.transformNode.position.z)).length());
if (this.destination.subtract(new Vector2(body.transformNode.position.x,
body.transformNode.position.z)).length() < .1) {
body.setLinearVelocity(Vector3.Zero());
body.setMotionType(PhysicsMotionType.DYNAMIC);
this.animationGroup.stop();
this.animationGroup.start(false, .1, 256, 267);
}
}
});
} }
private buildPlayer() {
this.parent = MeshBuilder.CreateCylinder(`team${this.teamName}player${this.number}`, {
diameter: .5,
height: 1.6
}, this.scene);
this.parent.position = this.position;
this.parent.isVisible = false;
this.physicsAggregate = new PhysicsAggregate(this.parent,
PhysicsShapeType.CYLINDER, {mass: 100, restitution: .02, friction: .3}, this.scene);
this.physicsAggregate.body.setAngularDamping(.5);
this.mesh.parent = this.physicsAggregate.transformNode;
this.mesh.metadata = {type: "player", grabbable: true};
this.mesh.position.x = 3;
this.mesh.position.y = -.84;
}
} }

View File

@ -0,0 +1,46 @@
import {Player, PlayerFactory} from "./player";
import {Scene, Vector2, Vector3} from "@babylonjs/core";
export class Team {
private readonly scene: Scene;
private players: Player[] = [];
private goalSide: number = -1;
private playerFactory: PlayerFactory;
private positions: Vector2[] = [
new Vector2(3, 1),
new Vector2(-3, 1),
new Vector2(5, 2),
new Vector2(15, 5),
new Vector2(-15, 5),
new Vector2(2, 10),
new Vector2(-2, 15),
new Vector2(15, 20),
new Vector2(-15, 20),
new Vector2(0, 35),
new Vector2(0, 47),
];
private name: string;
constructor(scene: Scene, side: number = 1, name: string = "team") {
this.scene = scene;
this.goalSide = side;
this.name = name;
this.playerFactory = new PlayerFactory(this.scene);
this.playerFactory.onReadyObservable.add(() => {
this.buildTeam();
this.players[5].runTo(new Vector2(3, -3 * this.goalSide));
});
}
private buildTeam() {
for (let i = 0; i < 11; i++) {
const player = this.playerFactory
.buildPlayer(new Vector3(this.positions[i].x * this.goalSide, 1, this.positions[i].y * this.goalSide), i,
this.name);
player.lookAt(new Vector2(0, -50 * this.goalSide))
this.players.push(player);
}
}
}