├── .gitignore
├── .prettierignore
├── .prettierrc
├── LICENSE
├── README.md
├── examples
├── .htaccess
├── css
│ ├── app.css
│ ├── bootstrap.min.css
│ └── styles.css
├── index.html
└── js
│ ├── creator.js
│ ├── fsm
│ ├── index.html
│ ├── index.js
│ ├── model
│ │ └── yuka.glb
│ └── src
│ │ ├── Girl.js
│ │ └── States.js
│ ├── fuzzy
│ ├── index.html
│ ├── index.js
│ ├── model
│ │ ├── README.md
│ │ ├── assaultRifle.glb
│ │ ├── soldier.glb
│ │ └── zombie.glb
│ └── src
│ │ └── Soldier.js
│ ├── goal
│ ├── index.html
│ ├── index.js
│ ├── model
│ │ ├── yan-ots-UuBR5kbvt4Y-unsplash (1).jpg
│ │ └── yuka.glb
│ └── src
│ │ ├── Collectible.js
│ │ ├── Evaluators.js
│ │ ├── Girl.js
│ │ └── Goals.js
│ ├── graph
│ ├── GraphHelper.js
│ ├── corridor
│ │ ├── index.html
│ │ └── index.js
│ └── tictactoe
│ │ ├── index.html
│ │ ├── index.js
│ │ ├── scene.js
│ │ └── src
│ │ ├── TTTEdge.js
│ │ ├── TTTGraph.js
│ │ └── TTTNode.js
│ ├── math
│ └── orientation
│ │ ├── index.html
│ │ └── index.js
│ ├── misc
│ ├── savegame
│ │ ├── index.html
│ │ ├── index.js
│ │ └── src
│ │ │ ├── CustomEntity.js
│ │ │ └── CustomVehicle.js
│ └── trigger
│ │ ├── index.html
│ │ ├── index.js
│ │ └── src
│ │ └── CustomTrigger.js
│ ├── navigation
│ ├── common
│ │ ├── CellSpacePartitioningHelper.js
│ │ ├── NavMeshHelper.js
│ │ └── navmeshes
│ │ │ ├── basic
│ │ │ ├── buffer_navmesh.bin
│ │ │ └── navmesh.gltf
│ │ │ └── complex
│ │ │ └── navmesh.glb
│ ├── firstperson
│ │ ├── audio
│ │ │ ├── step1.ogg
│ │ │ └── step2.ogg
│ │ ├── index.html
│ │ ├── index.js
│ │ ├── model
│ │ │ ├── README.md
│ │ │ └── house.glb
│ │ ├── navmesh
│ │ │ └── navmesh.glb
│ │ └── src
│ │ │ ├── FirstPersonControls.js
│ │ │ └── Player.js
│ ├── navmesh
│ │ ├── index.html
│ │ └── index.js
│ └── navmeshPerformance
│ │ ├── index.html
│ │ ├── index.js
│ │ ├── model
│ │ ├── README.md
│ │ └── level.glb
│ │ └── src
│ │ ├── CustomVehicle.js
│ │ ├── PathPlanner.js
│ │ └── PathPlannerTask.js
│ ├── perception
│ ├── common
│ │ ├── Obstacle.js
│ │ └── VisionHelper.js
│ ├── lineOfSight
│ │ ├── index.html
│ │ └── index.js
│ └── memorySystem
│ │ ├── index.html
│ │ ├── index.js
│ │ └── src
│ │ └── CustomEntity.js
│ ├── playground
│ ├── hideAndSeek
│ │ ├── audio
│ │ │ ├── dead.ogg
│ │ │ ├── empty.ogg
│ │ │ ├── impact1.ogg
│ │ │ ├── impact2.ogg
│ │ │ ├── impact3.ogg
│ │ │ ├── impact4.ogg
│ │ │ ├── impact5.ogg
│ │ │ ├── reload.ogg
│ │ │ ├── shot.ogg
│ │ │ ├── shot_reload.ogg
│ │ │ ├── step1.ogg
│ │ │ └── step2.ogg
│ │ ├── index.html
│ │ ├── index.js
│ │ ├── model
│ │ │ ├── README.md
│ │ │ ├── bulletHole.png
│ │ │ ├── muzzle.png
│ │ │ ├── shotgun-new.glb
│ │ │ └── shotgun.glb
│ │ ├── src
│ │ │ ├── AssetManager.js
│ │ │ ├── Bullet.js
│ │ │ ├── CustomObstacle.js
│ │ │ ├── Enemy.js
│ │ │ ├── FirstPersonControls.js
│ │ │ ├── Ground.js
│ │ │ ├── HideBehavior.js
│ │ │ ├── Player.js
│ │ │ ├── Shotgun.js
│ │ │ └── World.js
│ │ └── textures
│ │ │ └── sparkStretched.png
│ └── shooter
│ │ ├── audio
│ │ ├── empty.ogg
│ │ ├── impact1.ogg
│ │ ├── impact2.ogg
│ │ ├── impact3.ogg
│ │ ├── impact4.ogg
│ │ ├── impact5.ogg
│ │ ├── reload.ogg
│ │ ├── shot.ogg
│ │ ├── step1.ogg
│ │ └── step2.ogg
│ │ ├── index.html
│ │ ├── index.js
│ │ ├── model
│ │ ├── README.md
│ │ ├── bulletHole.png
│ │ ├── gun.glb
│ │ ├── muzzle.png
│ │ └── target.glb
│ │ └── src
│ │ ├── AssetManager.js
│ │ ├── Blaster.js
│ │ ├── Bullet.js
│ │ ├── FirstPersonControls.js
│ │ ├── Ground.js
│ │ ├── Player.js
│ │ ├── Target.js
│ │ └── World.js
│ ├── showcases
│ └── kickoff
│ │ ├── assets
│ │ ├── ball.glb
│ │ └── goal.glb
│ │ ├── index.html
│ │ ├── index.js
│ │ ├── src
│ │ ├── core
│ │ │ ├── AssetManager.js
│ │ │ ├── Constants.js
│ │ │ └── World.js
│ │ ├── entities
│ │ │ ├── Ball.js
│ │ │ ├── FieldPlayer.js
│ │ │ ├── Goal.js
│ │ │ ├── Goalkeeper.js
│ │ │ ├── Pitch.js
│ │ │ ├── Player.js
│ │ │ └── Team.js
│ │ ├── etc
│ │ │ ├── Region.js
│ │ │ └── SupportSpotCalculator.js
│ │ └── states
│ │ │ ├── FieldPlayerStates.js
│ │ │ ├── GoalkeeperStates.js
│ │ │ └── TeamStates.js
│ │ └── textures
│ │ └── pitch_texture.jpg
│ ├── spacecarrier
│ ├── index.html
│ ├── index.js
│ └── src
│ │ ├── carrier.js
│ │ ├── states.js
│ │ └── textures_flare.png
│ ├── steering
│ ├── arrive
│ │ ├── index.html
│ │ └── index.js
│ ├── flee
│ │ ├── index.html
│ │ └── index.js
│ ├── flocking
│ │ ├── index.html
│ │ └── index.js
│ ├── followPath
│ │ ├── index.html
│ │ └── index.js
│ ├── interpose
│ │ ├── index.html
│ │ └── index.js
│ ├── obstacleAvoidance
│ │ ├── index.html
│ │ └── index.js
│ ├── offsetPursuit
│ │ ├── index.html
│ │ └── index.js
│ ├── pursuit
│ │ ├── index.html
│ │ └── index.js
│ ├── seek
│ │ ├── index.html
│ │ └── index.js
│ └── wander
│ │ ├── index.html
│ │ └── index.js
│ └── templates
│ ├── index.html
│ └── index.js
├── lib
├── yuka.js
├── yuka.min.js
└── yuka.module.js
├── package-lock.json
└── package.json
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldinor/yuka-babylonjs-examples/f8d9f36fe181dc5f3ef4c2746dbe39ea8d884e37/.prettierignore
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "semi": false,
4 | "printWidth": 120
5 | }
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 eldinor
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # yuka-babylonjs-examples
2 |
3 | ### Yuka Game AI + 3D rendering with Babylon.js.
4 |
5 | #### Live examples at https://yuka.babylonpress.org/examples/
6 |
7 | Yuka library source: https://github.com/Mugen87/yuka
8 |
9 | Babylon.js 3D engine source: https://github.com/BabylonJS/Babylon.js
10 |
11 | # Installation
12 |
13 | ## VS Code extensions to install
14 |
15 | https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode
16 |
17 | https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer
18 |
19 | ## How to run the examples
20 |
21 | Open the `index.html` file located in `examples/js/example_dir` with LiveServer in VS Code.
22 |
23 | ## Best practices
24 |
25 | 1. try to avoid parented `TransformNodes` with YUKA. YUKA will place your object in world space.Use YUKA's parenting instead.
26 | 2. you **must** scale, rotate and position your mesh before registering it as a YUKA `renderComponent` and bake the transformations into the vertices and freeze the world matrix of your mesh before doing so.
27 | 3. you **must** register your Mesh/TransformNode/Camera on the YUKA entity by setting it as a `renderComponent` and pass the `syncFunction` which will take care of syncing your BabylonJS object's position/rotation/scaling into with the YUKA world's position.
28 |
29 | ```
30 | const entity = new YUKA.GameEntity()
31 | entity.setRenderComponent(mesh, syncFunction)
32 | ```
33 |
34 | 4. `syncFunctions`:
35 | For syncing a `TransformNode` with the YUKA entity use this method:
36 |
37 | ```
38 | private _sync(entity, renderComponent) {
39 | Matrix.FromValues(...entity.worldMatrix.elements).decomposeToTransformNode(renderComponent)
40 | }
41 | ```
42 |
43 | If it doesn't work for you try this one:
44 |
45 | ```
46 | renderComponent.getWorldMatrix().copyFrom(BABYLON.Matrix.FromValues(...entity.worldMatrix.elements))
47 | ```
48 |
49 | For the `camera` use this:
50 |
51 | ```
52 | private _syncCamera(entity, camera) {
53 | camera.getViewMatrix().copyFrom(Matrix.FromValues(...entity.worldMatrix.elements).invert())
54 | }
55 | ```
56 |
57 | 5. you **must** register your YUKA entity in the `YUKA.EntityManager` with it's `add` function
58 | 6. you **must** update the YUKA EntityManager's time (make steps in YUKA world) to make things moving like this:
59 |
60 | ```
61 | private _time = new YUKA.Time()
62 | this._scene.onBeforeRenderObservable.add(() => {
63 | const delta = this._time.update().getDelta()
64 | this._entityManager.update(delta) // YUKA world step
65 | })
66 | ```
67 |
68 | ## License
69 |
70 | All these examples are open source, MIT License.
71 |
72 | Babylon.js: Apache-2.0 License
73 |
74 | Yuka.js: MIT License
75 |
76 | 3D Models: CC Attribution License (see readme.md in relevant folders)
77 |
--------------------------------------------------------------------------------
/examples/.htaccess:
--------------------------------------------------------------------------------
1 | # To be inside the /Directory
2 | Header set Access-Control-Allow-Origin "*"
3 |
4 |
5 | RewriteEngine On
6 | RewriteCond %{HTTPS} off
7 | RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
8 |
9 | RewriteEngine On
10 | RewriteBase /
11 | RewriteRule ^index\.html$ - [L]
12 | RewriteCond %{REQUEST_FILENAME} !-f
13 | RewriteCond %{REQUEST_FILENAME} !-d
14 | RewriteRule . /index.html [L]
15 |
16 |
17 |
--------------------------------------------------------------------------------
/examples/css/app.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css?family=Jura');
2 |
3 | :root {
4 | color-scheme: light dark;
5 | }
6 |
7 | body {
8 | padding-top: 3.5rem;
9 | }
10 |
11 | .jumbotron {
12 | text-align: center;
13 | background-image: url(../images/gamer.jpg);
14 | background-repeat: no-repeat;
15 | background-size: cover;
16 | border-radius: 0;
17 | color: #ffffff;
18 | margin-bottom: 4rem;
19 | }
20 |
21 | .vertical-center {
22 | min-height: 50%;
23 | min-height: 50vh;
24 | display: flex;
25 | align-items: center;
26 | }
27 |
28 | .lead {
29 | font-size: 2rem;
30 | }
31 |
32 | hr.divider {
33 | margin: 5rem 0;
34 | }
35 |
36 | .teaser h2 {
37 | text-align: center;
38 | }
39 | .teaser p {
40 | text-align: center;
41 | }
42 |
43 | .navbar {
44 | border-bottom: 1px solid #ccc;
45 | }
46 |
47 | .navbar-brand {
48 | padding: 0;
49 | }
50 |
51 | .navbar-toggler-icon {
52 | background-image: url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 32 32' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba(238, 8, 8, 1)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 8h24M4 16h24M4 24h24'/%3E%3C/svg%3E");
53 | }
54 |
55 | @media (max-width: 576px) {
56 | .showcase-images {
57 | width: 300px;
58 | height: 150px;
59 | }
60 | }
61 |
62 | @media (prefers-color-scheme: light) {
63 | .navbar {
64 | background-color: #fff !important;
65 | border-bottom: 1px solid #ccc;
66 | }
67 | }
68 |
69 | /* dark mode */
70 |
71 | @media (prefers-color-scheme: dark) {
72 | body {
73 | color: #ccc;
74 | background-color: #181818;
75 | }
76 | .navbar {
77 | background-color: rgb(24, 22, 22) !important;
78 | border-bottom: 1px solid #212121;
79 | }
80 | hr {
81 | border-top: 1px solid #666;
82 | }
83 | a.nav-link {
84 | color: #ccc;
85 | }
86 | a.nav-link:hover {
87 | color: #ee0808;
88 | }
89 | .jumbotron {
90 | background-color: #181818;
91 | }
92 |
93 | .card {
94 | background-color: #181818;
95 | }
96 | footer {
97 | background-color: rgb(24, 22, 22);
98 | border-top: 1px solid #212121;
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/examples/css/styles.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css?family=Jura');
2 |
3 | * {
4 | -webkit-user-select: none;
5 | -moz-user-select: none;
6 | -ms-user-select: none;
7 | user-select: none;
8 | }
9 |
10 | body {
11 | margin:0;
12 | overflow:hidden;
13 | font-family: 'Jura', sans-serif;
14 | }
15 |
16 | #info {
17 | width: 100%;
18 | color: #ffffff;
19 | position: fixed;
20 | text-align: center;
21 | }
22 |
23 | #intro {
24 | position: fixed;
25 | height: 100%;
26 | width: 100%;
27 | display: flex;
28 | flex-direction: column;
29 | align-items: center;
30 | justify-content: center;
31 | background-color: rgba(0,0,0,0.4);
32 | color: #ffffff;
33 | font-size: 40;
34 | z-index: 999;
35 | }
36 |
37 | #intro.hidden {
38 | display: none;
39 | }
40 |
41 | #intro .sub {
42 | font-size: 20;
43 | }
44 |
45 | #loading-screen{
46 | position: fixed;
47 | height: 100%;
48 | width: 100%;
49 | display: flex;
50 | align-items: center;
51 | justify-content: center;
52 | background-color: #000000;
53 | z-index: 1000;
54 | opacity: 1;
55 | transition: 0.5s opacity;
56 | }
57 |
58 | #loading-screen.fade-out {
59 | opacity: 0;
60 | }
61 |
62 | .spinner {
63 | margin: 100px auto;
64 | width: 50px;
65 | height: 40px;
66 | text-align: center;
67 | font-size: 10px;
68 | }
69 |
70 | .spinner > div {
71 | background-color: #ffffff;
72 | height: 100%;
73 | width: 6px;
74 | display: inline-block;
75 |
76 | -webkit-animation: sk-stretchdelay 1.2s infinite ease-in-out;
77 | animation: sk-stretchdelay 1.2s infinite ease-in-out;
78 | }
79 |
80 | .spinner .rect2 {
81 | -webkit-animation-delay: -1.1s;
82 | animation-delay: -1.1s;
83 | }
84 |
85 | .spinner .rect3 {
86 | -webkit-animation-delay: -1.0s;
87 | animation-delay: -1.0s;
88 | }
89 |
90 | .spinner .rect4 {
91 | -webkit-animation-delay: -0.9s;
92 | animation-delay: -0.9s;
93 | }
94 |
95 | .spinner .rect5 {
96 | -webkit-animation-delay: -0.8s;
97 | animation-delay: -0.8s;
98 | }
99 |
100 | @-webkit-keyframes sk-stretchdelay {
101 | 0%, 40%, 100% { -webkit-transform: scaleY(0.4) }
102 | 20% { -webkit-transform: scaleY(1.0) }
103 | }
104 |
105 | @keyframes sk-stretchdelay {
106 | 0%, 40%, 100% {
107 | transform: scaleY(0.4);
108 | -webkit-transform: scaleY(0.4);
109 | } 20% {
110 | transform: scaleY(1.0);
111 | -webkit-transform: scaleY(1.0);
112 | }
113 | }
114 |
115 | /* hide some information on small devices */
116 |
117 | @media screen and (max-width: 414px) {
118 | #info {
119 | display: none;
120 | }
121 | .dg {
122 | display: none;
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/examples/js/creator.js:
--------------------------------------------------------------------------------
1 | import 'https://preview.babylonjs.com/babylon.js'
2 |
3 | export const VehicleTypes = {
4 | default: 0,
5 | cone: 1,
6 | box: 2,
7 | // etc
8 | }
9 |
10 | /*
11 | options: {
12 | type: VehicleTypes
13 | name: string
14 | size: number
15 | x: number
16 | y: number
17 | z: number
18 | ... etc
19 | }
20 |
21 | */
22 |
23 | export const createVehicle = (scene, options) => {
24 | const vehicleType = options?.type ?? VehicleTypes.default
25 | const size = options?.size ?? 1
26 | const height = 1 * size
27 | const diameterBottom = 0.5 * size
28 | const diameterTop = 0
29 | const name = options?.name ?? 'vehicle'
30 |
31 | if (vehicleType === VehicleTypes.default) {
32 | const vehicleMesh = BABYLON.MeshBuilder.CreateCylinder('cone', { height, diameterTop, diameterBottom }, scene)
33 | vehicleMesh.rotation.x = Math.PI * 0.5
34 |
35 | const dodecahedron2 = BABYLON.MeshBuilder.CreatePolyhedron('dodecahedron', {
36 | type: 1,
37 | size: 0.22 * size,
38 | })
39 | dodecahedron2.position.z -= 0.1
40 |
41 | const newVehicleMesh = BABYLON.Mesh.MergeMeshes([vehicleMesh, dodecahedron2], true)
42 | newVehicleMesh.name = name
43 | newVehicleMesh.position.y = options?.y ?? 0
44 | newVehicleMesh.bakeCurrentTransformIntoVertices()
45 |
46 | return newVehicleMesh
47 | } else if (vehicleType === VehicleTypes.cone) {
48 | const vehicleMesh = BABYLON.MeshBuilder.CreateCylinder(name, { height, diameterTop, diameterBottom }, scene)
49 |
50 | vehicleMesh.position.y = options?.y ?? 0
51 | vehicleMesh.rotation.x = Math.PI * 0.5
52 | vehicleMesh.bakeCurrentTransformIntoVertices()
53 |
54 | return vehicleMesh
55 | } else if (vehicleType === VehicleTypes.box) {
56 | //
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/examples/js/fsm/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Yuka | State-driven Agent Design
4 |
5 |
6 |
7 |
8 |
9 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 | The game entity continuously changes its status between "IDLE" and "WALK".
45 | The State-driven agent design enables a clean implementation of basic AI logic.
46 | The cone changes its color depending on the current state and uses FollowPath behavior when IDLE and Arrive (to
47 | zero point) behavior in the WALK state.
48 |
49 |
50 |
51 |
52 | Current State:
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/examples/js/fsm/model/yuka.glb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldinor/yuka-babylonjs-examples/f8d9f36fe181dc5f3ef4c2746dbe39ea8d884e37/examples/js/fsm/model/yuka.glb
--------------------------------------------------------------------------------
/examples/js/fsm/src/Girl.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Mugen87 / https://github.com/Mugen87
3 | * @author modified at https://github.com/eldinor/yuka-babylonjs-examples
4 | */
5 |
6 | import * as YUKA from '../../../../lib/yuka.module.js'
7 |
8 | import { IdleState, WalkState } from './States.js'
9 |
10 | class Girl extends YUKA.Vehicle {
11 | constructor(meshToManage, vehicle) {
12 | super()
13 |
14 | this.meshToManage = meshToManage
15 | this.vehicle = vehicle
16 |
17 | this.ui = {
18 | currentState: document.getElementById('currentState'),
19 | }
20 |
21 | //
22 |
23 | this.stateMachine = new YUKA.StateMachine(this)
24 |
25 | this.stateMachine.add('IDLE', new IdleState())
26 | this.stateMachine.add('WALK', new WalkState())
27 |
28 | this.stateMachine.changeTo('IDLE')
29 |
30 | //
31 |
32 | this.currentTime = 0 // tracks how long the entity is in the current state
33 | this.idleDuration = 6 // duration of a single state in seconds
34 | this.walkDuration = 4 // duration of a single state in seconds
35 | this.crossFadeDuration = 1 // duration of a crossfade in seconds
36 | }
37 |
38 | update(delta) {
39 | this.currentTime += delta
40 |
41 | this.stateMachine.update()
42 | }
43 | }
44 |
45 | export { Girl }
46 |
--------------------------------------------------------------------------------
/examples/js/fsm/src/States.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Mugen87 / https://github.com/Mugen87
3 | * @author modified at https://github.com/eldinor/yuka-babylonjs-examples
4 | */
5 |
6 | import * as YUKA from '../../../../lib/yuka.module.js'
7 |
8 | const IDLE = 'IDLE'
9 | const WALK = 'WALK'
10 |
11 | class IdleState extends YUKA.State {
12 | enter(girl) {
13 | girl.ui.currentState.textContent = IDLE
14 |
15 | girl.meshToManage.material.diffuseColor = BABYLON.Color3.Blue()
16 | girl.vehicle.steering.behaviors[1].active = false
17 | girl.vehicle.steering.behaviors[0].active = true
18 | console.log(girl.vehicle.steering.behaviors[0])
19 | console.log(girl.vehicle.steering.behaviors[1])
20 | }
21 |
22 | execute(girl) {
23 | if (girl.currentTime >= girl.idleDuration) {
24 | girl.currentTime = 0
25 | girl.stateMachine.changeTo(WALK)
26 | }
27 | }
28 |
29 | exit(girl) {}
30 | }
31 |
32 | class WalkState extends YUKA.State {
33 | enter(girl) {
34 | girl.meshToManage.material.diffuseColor = BABYLON.Color3.Red()
35 | girl.ui.currentState.textContent = WALK
36 |
37 | girl.walk.start()
38 | girl.idle.stop()
39 | girl.walk.loopAnimation = true
40 |
41 | const target = new YUKA.Vector3(5, 0, 6)
42 |
43 | girl.vehicle.steering.behaviors[0].active = false
44 | girl.vehicle.steering.behaviors[1].active = true
45 | console.log(girl.vehicle.steering.behaviors[0])
46 | console.log(girl.vehicle.steering.behaviors[1])
47 | }
48 |
49 | execute(girl) {
50 | if (girl.currentTime >= girl.walkDuration) {
51 | girl.currentTime = 0
52 | girl.stateMachine.changeTo(IDLE)
53 | const target = new YUKA.Vector3(-5, 0, 3)
54 | }
55 | }
56 |
57 | exit(girl) {
58 | if ((girl.ui.currentState.textContent = IDLE)) {
59 | girl.idle.start()
60 | girl.walk.stop()
61 | girl.idle.loopAnimation = true
62 | }
63 | }
64 | }
65 | export { IdleState, WalkState }
66 |
--------------------------------------------------------------------------------
/examples/js/fuzzy/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Yuka | Fuzzy Logic
4 |
5 |
6 |
7 |
8 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | The soldier uses fuzzy inference to determine the best weapon
47 | based on the distance to the enemy and the available ammo.
48 |
49 |
50 |
51 |
52 |
53 | Current Weapon:
54 |
55 |
56 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/examples/js/fuzzy/model/README.md:
--------------------------------------------------------------------------------
1 | Assault Rifle from https://sketchfab.com/3d-models/sci-fi-assault-rifle-d2596ed504b84ffda761b0886c26b202 by Ptis
2 | License: CC Attribution-NonCommercial
3 |
--------------------------------------------------------------------------------
/examples/js/fuzzy/model/assaultRifle.glb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldinor/yuka-babylonjs-examples/f8d9f36fe181dc5f3ef4c2746dbe39ea8d884e37/examples/js/fuzzy/model/assaultRifle.glb
--------------------------------------------------------------------------------
/examples/js/fuzzy/model/soldier.glb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldinor/yuka-babylonjs-examples/f8d9f36fe181dc5f3ef4c2746dbe39ea8d884e37/examples/js/fuzzy/model/soldier.glb
--------------------------------------------------------------------------------
/examples/js/fuzzy/model/zombie.glb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldinor/yuka-babylonjs-examples/f8d9f36fe181dc5f3ef4c2746dbe39ea8d884e37/examples/js/fuzzy/model/zombie.glb
--------------------------------------------------------------------------------
/examples/js/goal/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Yuka | Goal-driven Agent Design
4 |
5 |
6 |
7 |
8 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | The main goal of the game entity is to gather collectibles. After a while, the game entity takes a short
47 | rest.
48 | The Goal-driven agent design enables a clean implementation of more advanced AI logic.
49 |
50 |
51 |
52 |
53 |
54 | Current Goal:
55 |
56 | | Subgoal:
57 |
58 |
59 |
60 | Tired
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/examples/js/goal/model/yan-ots-UuBR5kbvt4Y-unsplash (1).jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldinor/yuka-babylonjs-examples/f8d9f36fe181dc5f3ef4c2746dbe39ea8d884e37/examples/js/goal/model/yan-ots-UuBR5kbvt4Y-unsplash (1).jpg
--------------------------------------------------------------------------------
/examples/js/goal/model/yuka.glb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldinor/yuka-babylonjs-examples/f8d9f36fe181dc5f3ef4c2746dbe39ea8d884e37/examples/js/goal/model/yuka.glb
--------------------------------------------------------------------------------
/examples/js/goal/src/Collectible.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Mugen87 / https://github.com/Mugen87
3 | */
4 |
5 | import { GameEntity } from '../../../../lib/yuka.module.js';
6 |
7 | class Collectible extends GameEntity {
8 |
9 | constructor(scene){
10 | super()
11 | this.scene = scene; // scene variable is not necessary if you don't need to change anything in the scene
12 | // here it is using only to change collectible box material after spawning
13 | }
14 |
15 | spawn() {
16 |
17 | this.position.x = Math.random() * 15 - 7.5;
18 | this.position.z = Math.random() * 15 - 7.5;
19 |
20 | if ( this.position.x < 1 && this.position.x > - 1 ) this.position.x += 1;
21 | if ( this.position.z < 1 && this.position.y > - 1 ) this.position.z += 1;
22 |
23 | console.log("Collectible spawned ")
24 |
25 | this._renderComponent.material = this.scene.getMaterialByName("collectibleMat1")
26 | setTimeout(() => {
27 | this._renderComponent.material = this.scene.getMaterialByName("collectibleMat")
28 | }, 3000);
29 | }
30 |
31 | handleMessage( telegram ) {
32 |
33 | const message = telegram.message;
34 |
35 | switch ( message ) {
36 |
37 | case 'PickedUp':
38 |
39 | this.spawn();
40 | return true;
41 |
42 | default:
43 |
44 | console.warn( 'Collectible: Unknown message.' );
45 |
46 | }
47 |
48 | return false;
49 |
50 | }
51 |
52 | }
53 |
54 | export { Collectible };
55 |
--------------------------------------------------------------------------------
/examples/js/goal/src/Evaluators.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Mugen87 / https://github.com/Mugen87
3 | */
4 |
5 | import { GoalEvaluator } from '../../../../lib/yuka.module.js';
6 |
7 | import { RestGoal, GatherGoal } from './Goals.js';
8 |
9 | class RestEvaluator extends GoalEvaluator {
10 |
11 | calculateDesirability( girl ) {
12 |
13 | return ( girl.tired() === true ) ? 1 : 0;
14 |
15 | }
16 |
17 | setGoal( girl ) {
18 |
19 | const currentSubgoal = girl.brain.currentSubgoal();
20 |
21 | if ( ( currentSubgoal instanceof RestGoal ) === false ) {
22 |
23 | girl.brain.clearSubgoals();
24 |
25 | girl.brain.addSubgoal( new RestGoal( girl ) );
26 |
27 | }
28 |
29 | }
30 |
31 | }
32 |
33 | class GatherEvaluator extends GoalEvaluator {
34 |
35 | calculateDesirability() {
36 |
37 | return 0.5;
38 |
39 | }
40 |
41 | setGoal( girl ) {
42 |
43 | const currentSubgoal = girl.brain.currentSubgoal();
44 |
45 | if ( ( currentSubgoal instanceof GatherGoal ) === false ) {
46 |
47 | girl.brain.clearSubgoals();
48 |
49 | girl.brain.addSubgoal( new GatherGoal( girl ) );
50 |
51 | }
52 |
53 | }
54 |
55 | }
56 |
57 | export {
58 | RestEvaluator,
59 | GatherEvaluator
60 | };
61 |
--------------------------------------------------------------------------------
/examples/js/goal/src/Girl.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Mugen87 / https://github.com/Mugen87
3 | */
4 |
5 | import { Vehicle, Think, ArriveBehavior } from '../../../../lib/yuka.module.js'
6 | import { RestEvaluator, GatherEvaluator } from './Evaluators.js'
7 |
8 | class Girl extends Vehicle {
9 | constructor() {
10 | super()
11 |
12 | this.maxTurnRate = Math.PI * 0.5
13 | this.maxSpeed = 1.5
14 |
15 | this.ui = {
16 | currentGoal: document.getElementById('currentGoal'),
17 | currentSubgoal: document.getElementById('currentSubgoal'),
18 | fatigueLevel: document.getElementById('tired'),
19 | }
20 |
21 | // goal-driven agent design
22 |
23 | this.brain = new Think(this)
24 |
25 | this.brain.addEvaluator(new RestEvaluator())
26 | this.brain.addEvaluator(new GatherEvaluator())
27 |
28 | // steering
29 |
30 | const arriveBehavior = new ArriveBehavior()
31 | arriveBehavior.deceleration = 1.5
32 | this.steering.add(arriveBehavior)
33 |
34 | //
35 |
36 | this.fatigueLevel = 0 // current level of fatigue
37 | this.restDuration = 5 // duration of a rest phase in seconds
38 | this.pickUpDuration = 6 // duration of a pick phase in seconds
39 | this.crossFadeDuration = 0.5 // duration of a crossfade in seconds
40 | this.currentTarget = null // current collectible
41 |
42 | this.currentTime = 0 // tracks the current time of an action
43 | this.deltaTime = 0 // the current time delta value
44 |
45 | this.MAX_FATIGUE = 3 // the girl needs to rest if this amount of fatigue is reached
46 | }
47 |
48 | update(delta) {
49 | super.update(delta)
50 |
51 | this.deltaTime = delta
52 |
53 | this.brain.execute()
54 |
55 | this.brain.arbitrate()
56 |
57 | return this
58 | }
59 |
60 | tired() {
61 | return this.fatigueLevel >= this.MAX_FATIGUE
62 | }
63 | }
64 |
65 | export { Girl }
66 |
--------------------------------------------------------------------------------
/examples/js/graph/GraphHelper.js:
--------------------------------------------------------------------------------
1 | function createGraphHelper(scene, graph, nodeSize = 1, nodeColor = '#4e84c4', edgeColor = '#ffffff') {
2 | const nodes = []
3 | graph.getNodes(nodes)
4 |
5 | const parent = new BABYLON.TransformNode('nodes-parent', scene)
6 |
7 | for (let node of nodes) {
8 | const nodeMaterial = new BABYLON.StandardMaterial('node', scene)
9 | nodeMaterial.emmissiveColor = BABYLON.Color3.FromHexString(nodeColor)
10 |
11 | const nodeMesh = BABYLON.MeshBuilder.CreatePolyhedron(
12 | 'node',
13 | {
14 | type: 3, // Icosahedron
15 | size: nodeSize,
16 | },
17 | scene
18 | )
19 | nodeMesh.parent = parent
20 | nodeMesh.material = nodeMaterial
21 | nodeMesh.position = new BABYLON.Vector3(node.position.x, node.position.y, node.position.z)
22 |
23 | // edges
24 | const edges = []
25 | const lines = []
26 | for (let node of nodes) {
27 | graph.getEdgesOfNode(node.index, edges)
28 |
29 | const position = []
30 | for (let edge of edges) {
31 | const fromNode = graph.getNode(edge.from)
32 | const toNode = graph.getNode(edge.to)
33 |
34 | position.push(new BABYLON.Vector3(fromNode.position.x, fromNode.position.y, fromNode.position.z))
35 | position.push(new BABYLON.Vector3(toNode.position.x, toNode.position.y, toNode.position.z))
36 | }
37 |
38 | lines.push(position)
39 | }
40 |
41 | const pathHelper = BABYLON.MeshBuilder.CreateLineSystem(
42 | 'path-helper',
43 | {
44 | lines,
45 | updatable: false,
46 | },
47 | scene
48 | )
49 | pathHelper.color = BABYLON.Color3.Green()
50 | pathHelper.parent = parent
51 | }
52 |
53 | return parent
54 | }
55 |
56 | export { createGraphHelper }
57 |
--------------------------------------------------------------------------------
/examples/js/graph/corridor/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Yuka | Corridor + Follow Path
4 |
5 |
9 |
10 |
15 |
16 |
17 |
18 |
19 | A corridor is a sequence of portal edges representing a walkable way
20 | within a navigation mesh.
21 | The class is able to find the shortest path through this corridor as a
22 | sequence of waypoints.
23 |
24 |
25 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/examples/js/graph/tictactoe/index.js:
--------------------------------------------------------------------------------
1 | import { TTTGraph } from './src/TTTGraph.js'
2 |
3 | let player,
4 | graph,
5 | fin = false
6 |
7 | initUI()
8 |
9 | function initUI() {
10 | // init buttons
11 |
12 | const buttons = document.querySelectorAll('#startSection button')
13 |
14 | for (let button of buttons) {
15 | button.addEventListener('click', onButtonClick)
16 | }
17 |
18 | const button = document.querySelector('#endSection button')
19 |
20 | button.addEventListener('click', onRestart)
21 |
22 | // init cells
23 |
24 | const cells = document.querySelectorAll('.cell')
25 |
26 | for (let cell of cells) {
27 | cell.addEventListener('click', onCellClick)
28 | }
29 | }
30 |
31 | const boxCells = scene.getTransformNodeByName('allBoxes').getChildren()
32 |
33 | for (let boxCell of boxCells) {
34 | boxCell.actionManager = new BABYLON.ActionManager(scene)
35 | boxCell.actionManager.registerAction(
36 | new BABYLON.ExecuteCodeAction(BABYLON.ActionManager.OnPickTrigger, (e) => {
37 | onCellBoxClick(e)
38 | })
39 | )
40 | }
41 |
42 | function initGame() {
43 | const intro = document.getElementById('intro')
44 | intro.classList.add('hidden')
45 |
46 | // create game state graph
47 |
48 | graph = new TTTGraph(player)
49 |
50 | // let the ai make its first move
51 |
52 | if (player === 2) {
53 | graph.aiTurn()
54 | updateUI()
55 | }
56 | }
57 |
58 | function onButtonClick(event) {
59 | const button = event.target
60 | player = parseInt(button.dataset.player)
61 |
62 | initGame()
63 | }
64 |
65 | function onRestart() {
66 | window.location.reload()
67 | }
68 |
69 | function onCellBoxClick(event) {
70 | const cellB = event.meshUnderPointer
71 |
72 | const cellBid = parseInt(cellB.name)
73 |
74 | if (fin === false) {
75 | const cell = event.target
76 |
77 | const cellid = cellBid
78 |
79 | graph.turn(cellid, graph.currentPlayer)
80 | evaluate()
81 |
82 | if (fin === false) {
83 | graph.aiTurn()
84 | evaluate()
85 | }
86 |
87 | updateUI()
88 | }
89 | }
90 |
91 | function onCellClick(event) {
92 | if (fin === false) {
93 | const cell = event.target
94 |
95 | const cellid = cell.dataset.cellid
96 |
97 | graph.turn(cellid, graph.currentPlayer)
98 | evaluate()
99 |
100 | if (fin === false) {
101 | graph.aiTurn()
102 | evaluate()
103 | }
104 |
105 | updateUI()
106 | }
107 | }
108 |
109 | function evaluate() {
110 | const board = graph.getNode(graph.currentNode)
111 |
112 | if (board.win === true || board.finished === true) fin = true
113 | }
114 |
115 | function updateUI() {
116 | const node = graph.getNode(graph.currentNode)
117 |
118 | const board = node.board
119 | const cells = document.querySelectorAll('.cell')
120 |
121 | const cellBoxes = scene.getTransformNodeByName('allBoxes').getChildren()
122 |
123 | for (let cell of cells) {
124 | const cellid = cell.dataset.cellid
125 | const status = board[cellid]
126 |
127 | switch (status) {
128 | case 1:
129 | cell.textContent = 'X'
130 | cell.removeEventListener('click', onCellClick)
131 |
132 | let crossMove = scene.getMeshByName('cross').clone('crossMove')
133 | crossMove.isVisible = true
134 |
135 | bPos(crossMove, cell.dataset.cellid)
136 |
137 | crossMove.position.y = 0.6
138 |
139 | scene.getMeshByName(cell.dataset.cellid).isPickable = false
140 | crossMove.isPickable = false
141 |
142 | break
143 |
144 | case 2:
145 | cell.textContent = 'O'
146 | cell.removeEventListener('click', onCellClick)
147 |
148 | let torusMove = scene.getMeshByName('torus').clone('crossMove')
149 | torusMove.isVisible = true
150 |
151 | bPos(torusMove, cell.dataset.cellid)
152 | torusMove.position.y = 0.6
153 |
154 | scene.getMeshByName(cell.dataset.cellid).isPickable = false
155 | torusMove.isPickable = false
156 |
157 | break
158 |
159 | default:
160 | cell.textContent = ''
161 | // console.log("DEFAULT")
162 | break
163 | }
164 | }
165 |
166 | if (fin === true) {
167 | const intro = document.getElementById('intro')
168 | intro.classList.remove('hidden')
169 |
170 | const startSection = document.getElementById('startSection')
171 | startSection.style.display = 'none'
172 |
173 | const endSection = document.getElementById('endSection')
174 | endSection.style.display = 'flex'
175 |
176 | const result = document.getElementById('result')
177 |
178 | if (node.win === true) {
179 | if (node.winPlayer === player) {
180 | result.textContent = 'You win the game!'
181 | } else {
182 | result.textContent = 'Yuka AI wins the game!'
183 | }
184 | } else {
185 | result.textContent = 'Draw!'
186 | }
187 | }
188 | }
189 |
--------------------------------------------------------------------------------
/examples/js/graph/tictactoe/src/TTTEdge.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author robp94 / https://github.com/robp94
3 | */
4 |
5 | import { Edge } from '../../../../../lib/yuka.module.js';
6 |
7 | class TTTEdge extends Edge {
8 |
9 | constructor( from, to, cell, player ) {
10 |
11 | super( from, to );
12 |
13 | // the following properties represent the move which
14 | // transitions from one board to the next one
15 |
16 | this.cell = cell; // the chosen cell/field
17 | this.player = player; // this player made the move
18 |
19 | }
20 |
21 | }
22 |
23 | export { TTTEdge };
24 |
--------------------------------------------------------------------------------
/examples/js/graph/tictactoe/src/TTTNode.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author robp94 / https://github.com/robp94
3 | */
4 |
5 | import { Node } from '../../../../../lib/yuka.module.js';
6 |
7 | class TTTNode extends Node {
8 |
9 | constructor( index, board = [ 9, 9, 9, 9, 9, 9, 9, 9, 9 ] ) {
10 |
11 | super( index );
12 |
13 | // the board is represented as a flat array
14 | // 1 = cell marked by player 1
15 | // 2 = cell marked by player 2
16 | // 9 = cell is empty
17 |
18 | this.board = board;
19 |
20 | this.value = parseInt( this.board.join( '' ), 10 ); // number representation of the board array for faster comparision
21 | this.win = false; // whether this node represents a won game
22 | this.finished = false; // whether this node represents a finished game (win or draw)
23 | this.winPlayer = - 1; // represents the player who wins with the current board, - 1 if there is no winner
24 | this.weight = 0; // used for min/max algorithm
25 |
26 | this.evaluate();
27 |
28 | }
29 |
30 | evaluate() {
31 |
32 | // check for win
33 |
34 | // horizontal
35 |
36 | if ( [ this.board[ 0 ], this.board[ 1 ], this.board[ 2 ] ].every( condition ) ) {
37 |
38 | this.finished = true;
39 | this.winPlayer = this.board[ 0 ];
40 |
41 | }
42 |
43 | if ( [ this.board[ 3 ], this.board[ 4 ], this.board[ 5 ] ].every( condition ) ) {
44 |
45 | this.finished = true;
46 | this.winPlayer = this.board[ 3 ];
47 |
48 | }
49 |
50 | if ( [ this.board[ 6 ], this.board[ 7 ], this.board[ 8 ] ].every( condition ) ) {
51 |
52 | this.finished = true;
53 | this.winPlayer = this.board[ 6 ];
54 |
55 | }
56 |
57 | // vertical
58 |
59 | if ( [ this.board[ 0 ], this.board[ 3 ], this.board[ 6 ] ].every( condition ) ) {
60 |
61 | this.finished = true;
62 | this.winPlayer = this.board[ 0 ];
63 |
64 | }
65 |
66 | if ( [ this.board[ 1 ], this.board[ 4 ], this.board[ 7 ] ].every( condition ) ) {
67 |
68 | this.finished = true;
69 | this.winPlayer = this.board[ 1 ];
70 |
71 | }
72 |
73 | if ( [ this.board[ 2 ], this.board[ 5 ], this.board[ 8 ] ].every( condition ) ) {
74 |
75 | this.finished = true;
76 | this.winPlayer = this.board[ 2 ];
77 |
78 | }
79 |
80 | // diagonal
81 |
82 | if ( [ this.board[ 0 ], this.board[ 4 ], this.board[ 8 ] ].every( condition ) ) {
83 |
84 | this.finished = true;
85 | this.winPlayer = this.board[ 0 ];
86 |
87 | }
88 |
89 | if ( [ this.board[ 6 ], this.board[ 4 ], this.board[ 2 ] ].every( condition ) ) {
90 |
91 | this.finished = true;
92 | this.winPlayer = this.board[ 6 ];
93 |
94 | }
95 |
96 | if ( this.winPlayer !== - 1 ) this.win = true;
97 |
98 | // check for draw
99 |
100 | let count = 0;
101 |
102 | for ( let i = 0; i < 9; i ++ ) {
103 |
104 | if ( this.board[ i ] !== 9 ) {
105 |
106 | count ++;
107 |
108 | }
109 |
110 | }
111 |
112 | if ( count === 9 ) {
113 |
114 | this.finished = true;
115 |
116 | }
117 |
118 | }
119 |
120 | }
121 |
122 | function condition( v, i, a ) {
123 |
124 | return ( a[ i ] === a[ 0 ] && a[ i ] !== 9 );
125 |
126 | }
127 |
128 | export { TTTNode };
129 |
--------------------------------------------------------------------------------
/examples/js/math/orientation/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Yuka | Orientation
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | The entity is rotated by a defined angular step per second in order to face a specific target.
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/examples/js/math/orientation/index.js:
--------------------------------------------------------------------------------
1 | import 'https://preview.babylonjs.com/babylon.js'
2 |
3 | import * as YUKA from '../../../../../lib/yuka.module.js'
4 | import { createVehicle } from '../../creator.js'
5 |
6 | let engine, scene
7 |
8 | let entityManager, time, entity, target
9 |
10 | const entityMatrix = new BABYLON.Matrix()
11 |
12 | init()
13 | animate()
14 |
15 | function init() {
16 | const canvas = document.getElementById('renderCanvas')
17 | engine = new BABYLON.Engine(canvas, true, {}, true)
18 |
19 | scene = new BABYLON.Scene(engine)
20 | scene.clearColor = new BABYLON.Color4(0, 0, 0, 1)
21 |
22 | const camera = new BABYLON.ArcRotateCamera(
23 | 'camera',
24 | BABYLON.Tools.ToRadians(90),
25 | BABYLON.Tools.ToRadians(60),
26 | 10,
27 | BABYLON.Vector3.Zero(),
28 | scene
29 | )
30 | camera.target = new BABYLON.Vector3(0, 0, 0)
31 | camera.useAutoRotationBehavior = true
32 | camera.wheelDeltaPercentage = 0.002
33 | camera.attachControl(canvas, true)
34 |
35 | const light = new BABYLON.HemisphericLight('light', new BABYLON.Vector3(0, 1, 0))
36 | light.diffuse = BABYLON.Color3.Teal()
37 | //
38 |
39 | const entityMesh = createVehicle(scene, { size: 0.5 })
40 |
41 | const targetMesh = BABYLON.MeshBuilder.CreateSphere('sphere', { diameter: 0.1, segments: 16 })
42 |
43 | const meshMat = new BABYLON.StandardMaterial('meshMat', scene)
44 | meshMat.disableLighting = true
45 | meshMat.emissiveColor = BABYLON.Color3.Red()
46 | targetMesh.material = meshMat
47 |
48 | const sphere = BABYLON.MeshBuilder.CreateSphere('sphere', { diameter: 5, segments: 16 })
49 | sphere.material = new BABYLON.StandardMaterial('sphereMaterial', scene)
50 | sphere.material.disableLighting = true
51 | sphere.material.emissiveColor = new BABYLON.Color3(0.8, 0.8, 0.8)
52 | sphere.material.alpha = 0.2
53 | sphere.material.wireframe = true
54 |
55 | //
56 |
57 | window.addEventListener('resize', onWindowResize, false)
58 |
59 | // game entity setup
60 |
61 | entityManager = new YUKA.EntityManager()
62 | time = new YUKA.Time()
63 |
64 | target = new YUKA.GameEntity()
65 | target.setRenderComponent(targetMesh, sync)
66 |
67 | entity = new YUKA.GameEntity()
68 | entity.maxTurnRate = Math.PI * 0.5
69 | entity.setRenderComponent(entityMesh, sync)
70 |
71 | entityManager.add(entity)
72 | entityManager.add(target)
73 |
74 | //
75 |
76 | generateTarget()
77 | }
78 |
79 | function onWindowResize() {
80 | engine.resize()
81 | }
82 |
83 | function animate() {
84 | requestAnimationFrame(animate)
85 |
86 | const delta = time.update().getDelta()
87 |
88 | entity.rotateTo(target.position, delta)
89 |
90 | entityManager.update()
91 |
92 | scene.render()
93 | }
94 |
95 | function sync(entity, renderComponent) {
96 | entity.worldMatrix.toArray(entityMatrix.m)
97 | entityMatrix.markAsUpdated()
98 |
99 | const matrix = renderComponent.getWorldMatrix()
100 | matrix.copyFrom(entityMatrix)
101 | }
102 |
103 | function generateTarget() {
104 | // generate a random point on a sphere
105 |
106 | const radius = 2
107 | const phi = Math.acos(2 * Math.random() - 1)
108 | const theta = Math.random() * Math.PI * 2
109 |
110 | target.position.fromSpherical(radius, phi, theta)
111 |
112 | setTimeout(generateTarget, 2000)
113 | }
114 |
--------------------------------------------------------------------------------
/examples/js/misc/savegame/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Yuka | Savegames
4 |
5 |
6 |
7 |
8 |
25 |
26 |
27 |
28 |
29 | You can save and reload the current state of the game. The savegame is also loaded when the page is refreshed.
30 |
31 |
32 | Save
33 | Load
34 | Clear
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/examples/js/misc/savegame/src/CustomEntity.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Mugen87 / https://github.com/Mugen87
3 | */
4 |
5 | import { GameEntity } from '../../../../../lib/yuka.module.js'
6 |
7 | class CustomEntity extends GameEntity {
8 | constructor() {
9 | super()
10 |
11 | this.name = 'target'
12 | this.currentTime = 0
13 | this.endTime = 0
14 | }
15 |
16 | update(delta) {
17 | this.currentTime += delta
18 |
19 | if (this.currentTime >= this.endTime) {
20 | this.generatePosition()
21 | }
22 |
23 | return super.update(delta)
24 | }
25 |
26 | generatePosition() {
27 | const radius = 2
28 | const phi = Math.acos(2 * Math.random() - 1)
29 | const theta = Math.random() * Math.PI * 2
30 |
31 | this.position.fromSpherical(radius, phi, theta)
32 |
33 | this.endTime += 3 // 3s
34 | }
35 |
36 | toJSON() {
37 | const json = super.toJSON()
38 |
39 | json.currentTime = this.currentTime
40 | json.endTime = this.endTime
41 |
42 | console.log(json)
43 |
44 | return json
45 | }
46 |
47 | fromJSON(json) {
48 | super.fromJSON(json)
49 |
50 | this.currentTime = json.currentTime
51 | this.endTime = json.endTime
52 |
53 | return this
54 | }
55 | }
56 |
57 | export { CustomEntity }
58 |
--------------------------------------------------------------------------------
/examples/js/misc/savegame/src/CustomVehicle.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Mugen87 / https://github.com/Mugen87
3 | */
4 |
5 | import { Vehicle } from '../../../../../lib/yuka.module.js'
6 |
7 | class CustomVehicle extends Vehicle {
8 | constructor() {
9 | super()
10 |
11 | this.name = 'vehicle'
12 | this.target = null
13 | }
14 |
15 | update(delta) {
16 | const seekBehavior = this.steering.behaviors[0]
17 | seekBehavior.target.copy(this.target.position)
18 |
19 | return super.update(delta)
20 | }
21 |
22 | toJSON() {
23 | const json = super.toJSON()
24 |
25 | json.target = this.target.uuid
26 |
27 | return json
28 | }
29 |
30 | fromJSON(json) {
31 | super.fromJSON(json)
32 |
33 | this.target = json.target
34 |
35 | return this
36 | }
37 |
38 | resolveReferences(entities) {
39 | super.resolveReferences(entities)
40 |
41 | this.target = entities.get(this.target)
42 |
43 | return this
44 | }
45 | }
46 |
47 | export { CustomVehicle }
48 |
--------------------------------------------------------------------------------
/examples/js/misc/trigger/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Yuka | Trigger
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | The game entity's color changes to green when it enters a trigger region.
12 | Wireframe meshes are shown just for the visual hint. There is no need to render them actually.
13 | They just show RectangularTriggerRegion and SphericalTriggerRegion.
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/examples/js/misc/trigger/index.js:
--------------------------------------------------------------------------------
1 | import 'https://preview.babylonjs.com/babylon.js'
2 | import 'https://preview.babylonjs.com/materialsLibrary/babylonjs.materials.min.js'
3 |
4 | import * as YUKA from '../../../../../lib/yuka.module.js'
5 |
6 | import { CustomTrigger } from './src/CustomTrigger.js'
7 |
8 | let engine, scene
9 |
10 | let entityManager, time, entity
11 |
12 | const entityMatrix = new BABYLON.Matrix()
13 |
14 | init()
15 | animate()
16 |
17 | function init() {
18 | const canvas = document.getElementById('renderCanvas')
19 | engine = new BABYLON.Engine(canvas, true, {}, true)
20 |
21 | scene = new BABYLON.Scene(engine)
22 | scene.useRightHandedSystem = true
23 | scene.clearColor = new BABYLON.Color4(0, 0, 0, 1)
24 | // scene.debugLayer.show();
25 |
26 | const camera = new BABYLON.ArcRotateCamera(
27 | 'camera',
28 | BABYLON.Tools.ToRadians(90),
29 | BABYLON.Tools.ToRadians(60),
30 | 15,
31 | BABYLON.Vector3.Zero(),
32 | scene
33 | )
34 |
35 | camera.target = new BABYLON.Vector3(0, 0, 0)
36 | camera.upperBetaLimit = 1.4
37 | camera.attachControl(canvas, true)
38 |
39 | new BABYLON.HemisphericLight('light', new BABYLON.Vector3(0, 1, 0), scene)
40 |
41 | const ground = BABYLON.MeshBuilder.CreatePlane('plane', { width: 25, height: 25 }, scene)
42 | ground.position.y = -2
43 | ground.rotation.x = Math.PI / 2
44 |
45 | ground.material = new BABYLON.GridMaterial('grid', scene)
46 | ground.material.backFaceCulling = true
47 |
48 | //
49 |
50 | const entityMesh = BABYLON.MeshBuilder.CreateBox('entityMesh', { size: 0.5 }, scene)
51 |
52 | const meshMat = new BABYLON.StandardMaterial('meshMat', scene)
53 | meshMat.disableLighting = true
54 | meshMat.emissiveColor = BABYLON.Color3.Red()
55 | entityMesh.material = meshMat
56 |
57 | // game entity setup
58 |
59 | entityManager = new YUKA.EntityManager()
60 | time = new YUKA.Time()
61 |
62 | entity = new YUKA.GameEntity()
63 | entity.boundingRadius = 0.25
64 | entity.setRenderComponent(entityMesh, sync)
65 |
66 | entityManager.add(entity)
67 |
68 | const radius = 2
69 | const size = new YUKA.Vector3(3, 3, 3)
70 |
71 | const sphericalTriggerRegion = new YUKA.SphericalTriggerRegion(radius)
72 | const rectangularTriggerRegion = new YUKA.RectangularTriggerRegion(size)
73 |
74 | const trigger1 = new CustomTrigger(sphericalTriggerRegion, scene)
75 | trigger1.position.set(3, 0, 0)
76 |
77 | const trigger2 = new CustomTrigger(rectangularTriggerRegion, scene)
78 | trigger2.position.set(-3, 0, 0)
79 |
80 | entityManager.add(trigger1)
81 | entityManager.add(trigger2)
82 |
83 | // visualize triggers
84 |
85 | const triggerMesh1 = BABYLON.MeshBuilder.CreateSphere('sphere', { diameter: 4, segments: 8 })
86 | triggerMesh1.position = new BABYLON.Vector3(3, 0, 0)
87 |
88 | triggerMesh1.material = new BABYLON.StandardMaterial('sphereMaterial', scene)
89 | triggerMesh1.material.diffuseColor = BABYLON.Color3.Gray()
90 | triggerMesh1.material.wireframe = true
91 |
92 | trigger1.setRenderComponent(triggerMesh1, sync)
93 |
94 | const triggerMesh2 = BABYLON.MeshBuilder.CreateBox('triggerMesh2', { size: 3 }, scene)
95 | triggerMesh2.position = new BABYLON.Vector3(-3, 0, 0)
96 |
97 | triggerMesh2.material = triggerMesh1.material
98 |
99 | trigger2.setRenderComponent(triggerMesh2, sync)
100 |
101 | window.addEventListener('resize', onWindowResize, false)
102 | }
103 |
104 | function onWindowResize() {
105 | engine.resize()
106 | }
107 |
108 | function animate() {
109 | requestAnimationFrame(animate)
110 |
111 | const delta = time.update().getDelta()
112 | const elapsedTime = time.getElapsed()
113 |
114 | entity.position.x = Math.sin(elapsedTime) * 2
115 |
116 | scene.getMeshByName(entity._renderComponent.name).material.emissiveColor.r = 1
117 | scene.getMeshByName(entity._renderComponent.name).material.emissiveColor.g = 0
118 |
119 | entityManager.update(delta)
120 |
121 | scene.render()
122 | }
123 |
124 | function sync(entity, renderComponent) {
125 | entity.worldMatrix.toArray(entityMatrix.m)
126 | entityMatrix.markAsUpdated()
127 |
128 | const matrix = renderComponent.getWorldMatrix()
129 | matrix.copyFrom(entityMatrix)
130 | }
131 |
--------------------------------------------------------------------------------
/examples/js/misc/trigger/src/CustomTrigger.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Mugen87 / https://github.com/Mugen87
3 | * @author Examples with Babylon.js were made at https://github.com/eldinor/yuka-babylonjs-examples
4 | */
5 |
6 | import { Trigger } from '../../../../../lib/yuka.module.js'
7 |
8 | class CustomTrigger extends Trigger {
9 | constructor(triggerRegion, scene) {
10 | // scene variable is using here only to change the entity material color
11 | super(triggerRegion)
12 | this.scene = scene
13 | }
14 |
15 | execute(entity) {
16 | super.execute()
17 |
18 | this.scene.getMeshByName(entity._renderComponent.name).material.emissiveColor.r = 0
19 | this.scene.getMeshByName(entity._renderComponent.name).material.emissiveColor.g = 1
20 | }
21 | }
22 |
23 | export { CustomTrigger }
24 |
--------------------------------------------------------------------------------
/examples/js/navigation/common/CellSpacePartitioningHelper.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Mugen87 / https://github.com/Mugen87
3 | * @author Examples with Babylon.js were made at https://github.com/eldinor/yuka-babylonjs-examples / roland@babylonjs.xyz
4 | */
5 |
6 | function createCellSpaceHelper(spatialIndex, scene) {
7 | const cells = spatialIndex.cells
8 |
9 | const positions = []
10 |
11 | for (let i = 0, l = cells.length; i < l; i++) {
12 | const cell = cells[i]
13 | const min = cell.aabb.min
14 | const max = cell.aabb.max
15 |
16 | // generate data for twelve lines segments
17 |
18 | // bottom lines
19 |
20 | positions.push([new BABYLON.Vector3(min.x, min.y, min.z), new BABYLON.Vector3(max.x, min.y, min.z)])
21 | positions.push([new BABYLON.Vector3(min.x, min.y, min.z), new BABYLON.Vector3(min.x, min.y, max.z)])
22 | positions.push([new BABYLON.Vector3(max.x, min.y, max.z), new BABYLON.Vector3(max.x, min.y, min.z)])
23 | positions.push([new BABYLON.Vector3(max.x, min.y, max.z), new BABYLON.Vector3(min.x, min.y, max.z)])
24 |
25 | // top lines
26 |
27 | positions.push([new BABYLON.Vector3(min.x, max.y, min.z), new BABYLON.Vector3(max.x, max.y, min.z)])
28 | positions.push([new BABYLON.Vector3(min.x, max.y, min.z), new BABYLON.Vector3(min.x, max.y, max.z)])
29 | positions.push([new BABYLON.Vector3(max.x, max.y, max.z), new BABYLON.Vector3(max.x, max.y, min.z)])
30 | positions.push([new BABYLON.Vector3(max.x, max.y, max.z), new BABYLON.Vector3(min.x, max.y, max.z)])
31 |
32 | // torso lines
33 |
34 | positions.push([new BABYLON.Vector3(min.x, min.y, min.z), new BABYLON.Vector3(min.x, max.y, min.z)])
35 | positions.push([new BABYLON.Vector3(max.x, min.y, min.z), new BABYLON.Vector3(max.x, max.y, min.z)])
36 | positions.push([new BABYLON.Vector3(max.x, min.y, max.z), new BABYLON.Vector3(max.x, max.y, max.z)])
37 | positions.push([new BABYLON.Vector3(min.x, min.y, max.z), new BABYLON.Vector3(min.x, max.y, max.z)])
38 | }
39 |
40 | const linesMesh = BABYLON.MeshBuilder.CreateLineSystem('lines', { lines: positions }, scene)
41 |
42 | return linesMesh
43 | }
44 |
45 | export { createCellSpaceHelper }
46 |
--------------------------------------------------------------------------------
/examples/js/navigation/common/NavMeshHelper.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Mugen87 / https://github.com/Mugen87
3 | * @author Examples with Babylon.js were made at https://github.com/eldinor/yuka-babylonjs-examples / roland@babylonjs.xyz
4 | */
5 |
6 | function createConvexRegionHelper(navMesh, scene) {
7 | const regions = navMesh.regions
8 |
9 | const customMesh = new BABYLON.Mesh('custom', scene)
10 | const customMeshMaterial = new BABYLON.StandardMaterial('custom-mesh', scene)
11 | customMeshMaterial.emmissiveColor = BABYLON.Color3.Random()
12 |
13 | customMesh.material = customMeshMaterial
14 |
15 | const positions = []
16 | const colors = []
17 |
18 | for (let region of regions) {
19 | // one color for each convex region
20 | const color = BABYLON.Color3.Random()
21 |
22 | // count edges
23 |
24 | let edge = region.edge
25 | const edges = []
26 |
27 | do {
28 | edges.push(edge)
29 | edge = edge.next
30 | } while (edge !== region.edge)
31 |
32 | // triangulate
33 |
34 | const triangleCount = edges.length - 2
35 |
36 | for (let i = 1, l = triangleCount; i <= l; i++) {
37 | const v1 = edges[0].vertex
38 | const v2 = edges[i + 0].vertex
39 | const v3 = edges[i + 1].vertex
40 |
41 | positions.push(v1.x, v1.y, v1.z)
42 | positions.push(v2.x, v2.y, v2.z)
43 | positions.push(v3.x, v3.y, v3.z)
44 |
45 | colors.push(color.r, color.g, color.b, 1)
46 | colors.push(color.r, color.g, color.b, 1)
47 | colors.push(color.r, color.g, color.b, 1)
48 | }
49 | }
50 |
51 | const indices = []
52 | for (let i = 0; i < positions.length / 3; i++) {
53 | indices.push(i)
54 | }
55 |
56 | const normals = []
57 |
58 | const vertexData = new BABYLON.VertexData()
59 | BABYLON.VertexData.ComputeNormals(positions, indices, normals)
60 |
61 | vertexData.positions = positions
62 | vertexData.indices = indices
63 | vertexData.normals = normals
64 | vertexData.colors = colors
65 |
66 | vertexData.applyToMesh(customMesh)
67 |
68 | var mat = new BABYLON.StandardMaterial('mat', scene)
69 | mat.backFaceCulling = false
70 | customMesh.material = mat
71 |
72 | return customMesh
73 | }
74 |
75 | export { createConvexRegionHelper }
76 |
--------------------------------------------------------------------------------
/examples/js/navigation/common/navmeshes/basic/buffer_navmesh.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldinor/yuka-babylonjs-examples/f8d9f36fe181dc5f3ef4c2746dbe39ea8d884e37/examples/js/navigation/common/navmeshes/basic/buffer_navmesh.bin
--------------------------------------------------------------------------------
/examples/js/navigation/common/navmeshes/basic/navmesh.gltf:
--------------------------------------------------------------------------------
1 | {
2 | "accessors": [
3 | {
4 | "bufferView": 0,
5 | "byteOffset": 0,
6 | "componentType": 5126,
7 | "count": 30,
8 | "max": [
9 | 9.545417785644531,
10 | -2.9802322387695312e-08,
11 | 17.01628875732422
12 | ],
13 | "min": [
14 | -9.954584121704102,
15 | -2.9802322387695312e-08,
16 | -10.283712387084961
17 | ],
18 | "name": "accessor_buffer_Navmesh.001_POSITION_0",
19 | "type": "VEC3"
20 | },
21 | {
22 | "bufferView": 1,
23 | "byteOffset": 0,
24 | "componentType": 5126,
25 | "count": 30,
26 | "max": [
27 | 0.0,
28 | 1.0,
29 | 1.5949328641311016e-15
30 | ],
31 | "min": [
32 | 0.0,
33 | 0.9999999403953552,
34 | -8.310443533419282e-15
35 | ],
36 | "name": "accessor_buffer_Navmesh.001_NORMAL_0",
37 | "type": "VEC3"
38 | },
39 | {
40 | "bufferView": 2,
41 | "byteOffset": 0,
42 | "componentType": 5123,
43 | "count": 51,
44 | "max": [
45 | 29
46 | ],
47 | "min": [
48 | 0
49 | ],
50 | "name": "accessor_buffer_Navmesh.001_0",
51 | "type": "SCALAR"
52 | }
53 | ],
54 | "asset": {
55 | "copyright": "",
56 | "generator": "blendergltf v1.2.0",
57 | "version": "2.0"
58 | },
59 | "bufferViews": [
60 | {
61 | "buffer": 0,
62 | "byteLength": 360,
63 | "byteOffset": 0,
64 | "byteStride": 12,
65 | "name": "bufferView_buffer_Navmesh.001_POSITION_0",
66 | "target": 34962
67 | },
68 | {
69 | "buffer": 0,
70 | "byteLength": 360,
71 | "byteOffset": 360,
72 | "byteStride": 12,
73 | "name": "bufferView_buffer_Navmesh.001_NORMAL_0",
74 | "target": 34962
75 | },
76 | {
77 | "buffer": 0,
78 | "byteLength": 104,
79 | "byteOffset": 720,
80 | "name": "bufferView_buffer_Navmesh.001_0",
81 | "target": 34963
82 | }
83 | ],
84 | "buffers": [
85 | {
86 | "byteLength": 824,
87 | "name": "buffer_navmesh",
88 | "uri": "buffer_navmesh.bin"
89 | }
90 | ],
91 | "meshes": [
92 | {
93 | "name": "Navmesh.001",
94 | "primitives": [
95 | {
96 | "attributes": {
97 | "NORMAL": 1,
98 | "POSITION": 0
99 | },
100 | "indices": 2,
101 | "mode": 4
102 | }
103 | ]
104 | }
105 | ],
106 | "nodes": [
107 | {
108 | "mesh": 0,
109 | "name": "Navmesh",
110 | "rotation": [
111 | 0.0,
112 | 0.0,
113 | 0.0,
114 | 1.0
115 | ],
116 | "scale": [
117 | 1.0,
118 | 1.0,
119 | 1.0
120 | ],
121 | "translation": [
122 | 0.0,
123 | 0.0,
124 | 0.0
125 | ]
126 | }
127 | ],
128 | "scene": 0,
129 | "scenes": [
130 | {
131 | "extras": {
132 | "background_color": [
133 | 0.05087608844041824,
134 | 0.05087608844041824,
135 | 0.05087608844041824
136 | ],
137 | "frames_per_second": 24
138 | },
139 | "name": "Scene",
140 | "nodes": [
141 | 0
142 | ]
143 | }
144 | ]
145 | }
146 |
--------------------------------------------------------------------------------
/examples/js/navigation/common/navmeshes/complex/navmesh.glb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldinor/yuka-babylonjs-examples/f8d9f36fe181dc5f3ef4c2746dbe39ea8d884e37/examples/js/navigation/common/navmeshes/complex/navmesh.glb
--------------------------------------------------------------------------------
/examples/js/navigation/firstperson/audio/step1.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldinor/yuka-babylonjs-examples/f8d9f36fe181dc5f3ef4c2746dbe39ea8d884e37/examples/js/navigation/firstperson/audio/step1.ogg
--------------------------------------------------------------------------------
/examples/js/navigation/firstperson/audio/step2.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldinor/yuka-babylonjs-examples/f8d9f36fe181dc5f3ef4c2746dbe39ea8d884e37/examples/js/navigation/firstperson/audio/step2.ogg
--------------------------------------------------------------------------------
/examples/js/navigation/firstperson/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Yuka | First-Person Controls
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | Click to Play
22 | A navigation mesh defines the walkable area of this level.
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/examples/js/navigation/firstperson/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Mugen87 / https://github.com/Mugen87
3 | * @author Examples with Babylon.js were made at https://github.com/eldinor/yuka-babylonjs-examples / roland@babylonjs.xyz
4 | */
5 |
6 | import * as YUKA from '../../../../lib/yuka.module.js'
7 | import 'https://preview.babylonjs.com/babylon.js'
8 | import 'https://preview.babylonjs.com/materialsLibrary/babylonjs.materials.min.js'
9 | import 'https://preview.babylonjs.com/loaders/babylonjs.loaders.min.js'
10 |
11 | import { FirstPersonControls } from './src/FirstPersonControls.js'
12 | import { Player } from './src/Player.js'
13 |
14 | let engine, scene, camera, step1, step2
15 | let entityManager, time, controls
16 |
17 | const entityMatrix = new BABYLON.Matrix()
18 |
19 | init()
20 |
21 | //
22 |
23 | function init() {
24 | const canvas = document.getElementById('renderCanvas')
25 | engine = new BABYLON.Engine(canvas, true, {}, true)
26 |
27 | if (BABYLON.Engine.audioEngine) {
28 | BABYLON.Engine.audioEngine.useCustomUnlockedButton = true
29 | }
30 |
31 | scene = new BABYLON.Scene(engine)
32 | scene.clearColor = BABYLON.Color3.FromHexString('#a0a0a0')
33 | scene.useRightHandedSystem = true
34 |
35 | camera = new BABYLON.UniversalCamera('camera', new BABYLON.Vector3(-13, 0.75, -9), scene, true)
36 | camera.minZ = 0.1
37 |
38 | scene.fogMode = BABYLON.Scene.FOGMODE_EXP2
39 | scene.fogColor = BABYLON.Color3.FromHexString('#a0a0a0')
40 | scene.fogDensity = 0.01
41 |
42 | //
43 | const ground = BABYLON.MeshBuilder.CreateGround('ground', { width: 250, height: 250 }, scene)
44 | ground.position.y = -5
45 | const groundMaterial = new BABYLON.StandardMaterial('grid', scene)
46 | groundMaterial.diffuseColor = BABYLON.Color3.FromHexString('#999999')
47 | ground.material = groundMaterial
48 |
49 | //
50 |
51 | new BABYLON.HemisphericLight('light', new BABYLON.Vector3(1, 1, 0), scene)
52 | new BABYLON.DirectionalLight('dir-light', new BABYLON.Vector3(1, 1, 0), scene)
53 |
54 | //
55 | BABYLON.SceneLoader.ImportMesh(null, 'model/', 'house.glb', scene, (meshes) => {
56 | // 3D assets are loaded, now load nav mesh
57 |
58 | const loader = new YUKA.NavMeshLoader()
59 | loader.load('./navmesh/navmesh.glb', { epsilonCoplanarTest: 0.25 }).then((navMesh) => {
60 | const loadingScreen = document.getElementById('loading-screen')
61 |
62 | loadingScreen.classList.add('fade-out')
63 | loadingScreen.addEventListener('transitionend', onTransitionEnd)
64 |
65 | //
66 |
67 | step1 = new BABYLON.Sound('step1', 'audio/step1.ogg', scene, null, {
68 | loop: false,
69 | autoplay: false,
70 | })
71 |
72 | step2 = new BABYLON.Sound('step2', 'audio/step2.ogg', scene, null, {
73 | loop: false,
74 | autoplay: false,
75 | })
76 |
77 | //
78 |
79 | window.addEventListener('resize', onWindowResize, false)
80 |
81 | const intro = document.getElementById('intro')
82 |
83 | intro.addEventListener(
84 | 'click',
85 | () => {
86 | if (BABYLON.Engine.audioEngine) {
87 | BABYLON.Engine.audioEngine.unlock()
88 | }
89 |
90 | controls.connect()
91 | },
92 | false
93 | )
94 |
95 | // game setup
96 |
97 | entityManager = new YUKA.EntityManager()
98 | time = new YUKA.Time()
99 |
100 | const player = new Player()
101 | player.navMesh = navMesh
102 | player.head.setRenderComponent(camera, syncCamera)
103 | player.position.set(-13, -0.75, -9)
104 |
105 | controls = new FirstPersonControls(player)
106 | controls.setRotation(-2.2, 0.2)
107 |
108 | controls.sounds.set('rightStep', step1)
109 | controls.sounds.set('leftStep', step2)
110 |
111 | controls.addEventListener('lock', () => {
112 | intro.classList.add('hidden')
113 | })
114 |
115 | controls.addEventListener('unlock', () => {
116 | intro.classList.remove('hidden')
117 | })
118 |
119 | entityManager.add(player)
120 |
121 | animate()
122 | })
123 | })
124 | }
125 |
126 | function onWindowResize() {
127 | engine.resize()
128 | }
129 |
130 | function animate() {
131 | requestAnimationFrame(animate)
132 |
133 | const delta = time.update().getDelta()
134 | controls.update(delta)
135 | entityManager.update(delta)
136 |
137 | scene.render()
138 | }
139 |
140 | function syncCamera(entity, renderComponent) {
141 | renderComponent.getViewMatrix().copyFrom(BABYLON.Matrix.FromValues(...entity.worldMatrix.elements).invert())
142 | }
143 |
144 | function onTransitionEnd(event) {
145 | event.target.remove()
146 | }
147 |
--------------------------------------------------------------------------------
/examples/js/navigation/firstperson/model/README.md:
--------------------------------------------------------------------------------
1 | Model from https://sketchfab.com/models/645533aee9f448b8b20e1f4fb4b472ca by linhtatoo
2 | License: CC Attribution
3 |
--------------------------------------------------------------------------------
/examples/js/navigation/firstperson/model/house.glb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldinor/yuka-babylonjs-examples/f8d9f36fe181dc5f3ef4c2746dbe39ea8d884e37/examples/js/navigation/firstperson/model/house.glb
--------------------------------------------------------------------------------
/examples/js/navigation/firstperson/navmesh/navmesh.glb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldinor/yuka-babylonjs-examples/f8d9f36fe181dc5f3ef4c2746dbe39ea8d884e37/examples/js/navigation/firstperson/navmesh/navmesh.glb
--------------------------------------------------------------------------------
/examples/js/navigation/firstperson/src/Player.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Mugen87 / https://github.com/Mugen87
3 | * @author Examples with Babylon.js were made at https://github.com/eldinor/yuka-babylonjs-examples / roland@babylonjs.xyz
4 | */
5 |
6 | import { MovingEntity, Vector3, GameEntity } from '../../../../../lib/yuka.module.js'
7 |
8 | const startPosition = new Vector3()
9 | const endPosition = new Vector3()
10 |
11 | class Player extends MovingEntity {
12 | constructor() {
13 | super()
14 |
15 | this.maxSpeed = 4
16 | this.height = 2
17 |
18 | this.head = new GameEntity()
19 | this.add(this.head)
20 |
21 | this.updateOrientation = false
22 | this.navMesh = null
23 | this.currentRegion = null
24 | }
25 |
26 | update(delta) {
27 | startPosition.copy(this.position)
28 |
29 | super.update(delta)
30 |
31 | endPosition.copy(this.position)
32 |
33 | // ensure the entity stays inside its navmesh
34 |
35 | this.currentRegion = this.navMesh.clampMovement(this.currentRegion, startPosition, endPosition, this.position)
36 |
37 | // adjust height of player according to the ground
38 |
39 | const distance = this.currentRegion.distanceToPoint(this.position)
40 |
41 | this.position.y -= distance * 0.2 // smooth transition
42 | }
43 | }
44 |
45 | export { Player }
46 |
--------------------------------------------------------------------------------
/examples/js/navigation/navmesh/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Yuka | Navmesh
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | Click on the navigation mesh to define a new target for the game entity.
13 | The colored areas represent the convex regions of the navigation mesh.
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/examples/js/navigation/navmesh/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Mugen87 / https://github.com/Mugen87
3 | * @author Examples with Babylon.js were made at https://github.com/eldinor/yuka-babylonjs-examples / roland@babylonjs.xyz
4 | */
5 |
6 | import * as YUKA from '../../../../lib/yuka.module.js'
7 | import * as DAT from 'https://cdn.jsdelivr.net/npm/dat.gui@0.7.7/build/dat.gui.module.js'
8 |
9 | import 'https://preview.babylonjs.com/babylon.js'
10 | import 'https://preview.babylonjs.com/materialsLibrary/babylonjs.materials.min.js'
11 |
12 | import { createGraphHelper } from '../../graph/GraphHelper.js'
13 | import { createConvexRegionHelper } from '../common/NavMeshHelper.js'
14 | import { createVehicle } from '../../creator.js'
15 |
16 | let engine, scene, plane, pathHelper, graphHelper, navMeshGroup, vehicleMesh
17 | let entityManager, time, vehicle, target
18 | let navMesh
19 |
20 | const entityMatrix = new BABYLON.Matrix()
21 | const pointer = new BABYLON.Vector2(1, 1)
22 |
23 | const params = {
24 | showNavigationGraph: true,
25 | }
26 |
27 | init()
28 |
29 | function init() {
30 | const canvas = document.getElementById('renderCanvas')
31 | engine = new BABYLON.Engine(canvas, true, {}, true)
32 |
33 | scene = new BABYLON.Scene(engine)
34 | scene.clearColor = new BABYLON.Color4(0, 0, 0, 1)
35 | scene.useRightHandedSystem = true
36 |
37 | const camera = new BABYLON.ArcRotateCamera(
38 | 'camera',
39 | BABYLON.Tools.ToRadians(40),
40 | BABYLON.Tools.ToRadians(60),
41 | 30,
42 | BABYLON.Vector3.Zero(),
43 | scene
44 | )
45 |
46 | camera.target = new BABYLON.Vector3(0, 0, 0)
47 | camera.attachControl(canvas, true)
48 |
49 | const light = new BABYLON.HemisphericLight('light', new BABYLON.Vector3(0, -1, 0))
50 | light.intensity = 0.7
51 |
52 | //
53 |
54 | vehicleMesh = createVehicle(scene, { size: 1.5, y: 1 })
55 |
56 | window.addEventListener('resize', onWindowResize, false)
57 |
58 | // gui
59 |
60 | const gui = new DAT.GUI({ width: 300 })
61 |
62 | gui.add(params, 'showNavigationGraph', 1, 30).onChange((value) => {
63 | if (graphHelper) {
64 | graphHelper.setEnabled(value)
65 | }
66 | })
67 |
68 | gui.open()
69 |
70 | // YUKA specific
71 | target = new YUKA.Vector3()
72 |
73 | entityManager = new YUKA.EntityManager()
74 | time = new YUKA.Time()
75 |
76 | const loader = new YUKA.NavMeshLoader()
77 | loader.load('../common/navmeshes/basic/navmesh.gltf').then((navigationMesh) => {
78 | vehicle = new YUKA.Vehicle()
79 | vehicle.maxSpeed = 1.5
80 | vehicle.maxForce = 10
81 | vehicle.setRenderComponent(vehicleMesh, sync)
82 |
83 | // visualize convex regions
84 | navMesh = navigationMesh
85 | navMeshGroup = createConvexRegionHelper(navMesh, scene)
86 |
87 | // visualize graph
88 | const graph = navMesh.graph
89 | graphHelper = createGraphHelper(scene, graph, 0.2)
90 |
91 | scene.onPointerMove = () => {
92 | var pickResult = scene.pick(scene.pointerX, scene.pointerY)
93 | if (pickResult?.pickedPoint) {
94 | target.x = pickResult.pickedPoint.x
95 | target.y = pickResult.pickedPoint.y
96 | target.z = pickResult.pickedPoint.z
97 | }
98 | }
99 |
100 | scene.onPointerPick = (e, pickResult) => {
101 | if (pickResult?.pickedPoint) {
102 | findPathTo(new YUKA.Vector3().copy(pickResult.pickedPoint))
103 | }
104 | }
105 | const followPathBehavior = new YUKA.FollowPathBehavior()
106 | followPathBehavior.active = false
107 | vehicle.steering.add(followPathBehavior)
108 |
109 | entityManager.add(vehicle)
110 | animate()
111 | })
112 | }
113 |
114 | function onWindowResize() {
115 | engine.resize()
116 | }
117 |
118 | function animate() {
119 | requestAnimationFrame(animate)
120 |
121 | const delta = time.update().getDelta()
122 | entityManager.update(delta)
123 |
124 | scene.render()
125 | }
126 |
127 | function findPathTo(target) {
128 | const from = vehicle.position
129 | const to = target
130 |
131 | const path = navMesh.findPath(from, to)
132 |
133 | //
134 |
135 | if (pathHelper) {
136 | pathHelper.dispose()
137 | }
138 | pathHelper = BABYLON.MeshBuilder.CreateLines(
139 | 'path-helper',
140 | {
141 | points: path,
142 | updatable: false,
143 | },
144 | scene
145 | )
146 | pathHelper.color = BABYLON.Color3.Red()
147 |
148 | //
149 |
150 | const followPathBehavior = vehicle.steering.behaviors[0]
151 | followPathBehavior.active = true
152 | followPathBehavior.path.clear()
153 |
154 | for (const point of path) {
155 | followPathBehavior.path.add(point)
156 | }
157 | }
158 |
159 | function sync(entity, renderComponent) {
160 | BABYLON.Matrix.FromValues(...entity.worldMatrix.elements).decomposeToTransformNode(renderComponent)
161 | }
162 |
--------------------------------------------------------------------------------
/examples/js/navigation/navmeshPerformance/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Yuka | Navmesh with Spatial Index and Tasks
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | Navigation Mesh with Spatial Index and Tasks
24 | Active Game Entities:
25 | Convex Regions of NavMesh:
26 | Partitions of Spatial Index:
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/examples/js/navigation/navmeshPerformance/model/README.md:
--------------------------------------------------------------------------------
1 | Model from https://sketchfab.com/models/7842d43173c54ec18da10246cdc6da71 by hansolocambo
2 | License: CC Attribution
3 |
--------------------------------------------------------------------------------
/examples/js/navigation/navmeshPerformance/model/level.glb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldinor/yuka-babylonjs-examples/f8d9f36fe181dc5f3ef4c2746dbe39ea8d884e37/examples/js/navigation/navmeshPerformance/model/level.glb
--------------------------------------------------------------------------------
/examples/js/navigation/navmeshPerformance/src/CustomVehicle.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Mugen87 / https://github.com/Mugen87
3 | * @author Examples with Babylon.js were made at https://github.com/eldinor/yuka-babylonjs-examples / roland@babylonjs.xyz
4 | */
5 |
6 | import { Vehicle } from '../../../../../lib/yuka.module.js'
7 |
8 | class CustomVehicle extends Vehicle {
9 | constructor() {
10 | super()
11 |
12 | this.navMesh = null
13 |
14 | this.currentRegion = null
15 | this.fromRegion = null
16 | this.toRegion = null
17 | }
18 |
19 | update(delta) {
20 | super.update(delta)
21 |
22 | // this code is used to adjust the height of the entity according to its current region
23 |
24 | const currentRegion = this.navMesh.getRegionForPoint(this.position, 1)
25 |
26 | if (currentRegion !== null) {
27 | this.currentRegion = currentRegion
28 |
29 | const distance = this.currentRegion.distanceToPoint(this.position)
30 |
31 | this.position.y -= distance * 0.2
32 | }
33 |
34 | return this
35 | }
36 | }
37 |
38 | export { CustomVehicle }
39 |
--------------------------------------------------------------------------------
/examples/js/navigation/navmeshPerformance/src/PathPlanner.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Mugen87 / https://github.com/Mugen87
3 | * @author Examples with Babylon.js were made at https://github.com/eldinor/yuka-babylonjs-examples / roland@babylonjs.xyz
4 | */
5 |
6 | import { TaskQueue } from '../../../../../lib/yuka.module.js'
7 | import { PathPlannerTask } from './PathPlannerTask.js'
8 |
9 | class PathPlanner {
10 | constructor(navMesh) {
11 | this.navMesh = navMesh
12 |
13 | this.taskQueue = new TaskQueue()
14 | }
15 |
16 | findPath(vehicle, from, to, callback) {
17 | const task = new PathPlannerTask(this, vehicle, from, to, callback)
18 |
19 | this.taskQueue.enqueue(task)
20 | }
21 |
22 | update() {
23 | this.taskQueue.update()
24 | }
25 | }
26 |
27 | export { PathPlanner }
28 |
--------------------------------------------------------------------------------
/examples/js/navigation/navmeshPerformance/src/PathPlannerTask.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Mugen87 / https://github.com/Mugen87
3 | * @author Examples with Babylon.js were made at https://github.com/eldinor/yuka-babylonjs-examples / roland@babylonjs.xyz
4 | */
5 |
6 | import { Task } from '../../../../../lib/yuka.module.js'
7 |
8 | class PathPlannerTask extends Task {
9 | constructor(planner, vehicle, from, to, callback) {
10 | super()
11 |
12 | this.callback = callback
13 | this.planner = planner
14 | this.vehicle = vehicle
15 | this.from = from
16 | this.to = to
17 | }
18 |
19 | execute() {
20 | const path = this.planner.navMesh.findPath(this.from, this.to)
21 |
22 | this.callback(this.vehicle, path)
23 | }
24 | }
25 |
26 | export { PathPlannerTask }
27 |
--------------------------------------------------------------------------------
/examples/js/perception/common/Obstacle.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Mugen87 / https://github.com/Mugen87
3 | * @author Examples with Babylon.js were made at https://github.com/eldinor/yuka-babylonjs-examples / roland@babylonjs.xyz
4 | */
5 |
6 | import { GameEntity } from '../../../../lib/yuka.module.js'
7 |
8 | class Obstacle extends GameEntity {
9 | constructor(mesh) {
10 | super()
11 | this.mesh = mesh
12 | }
13 |
14 | lineOfSightTest(ray, intersectionPoint) {
15 | return this.mesh.intersectRay(ray, this.worldMatrix, true, intersectionPoint)
16 | }
17 | }
18 |
19 | export { Obstacle }
20 |
--------------------------------------------------------------------------------
/examples/js/perception/common/VisionHelper.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Mugen87 / https://github.com/Mugen87
3 | * @author Examples with Babylon.js were made at https://github.com/eldinor/yuka-babylonjs-examples / roland@babylonjs.xyz
4 | */
5 |
6 | function createVisionHelper(vision, division = 8, scene) {
7 | const fieldOfView = vision.fieldOfView
8 | const range = vision.range
9 |
10 | const customMesh = new BABYLON.Mesh('custom', scene)
11 | const customMeshMaterial = new BABYLON.StandardMaterial('custom-mesh', scene)
12 | customMeshMaterial.wireframe = true
13 | customMesh.material = customMeshMaterial
14 | customMeshMaterial.emissiveColor = new BABYLON.Color3(0.7, 0.7, 0.7)
15 | customMeshMaterial.disableLighting = true
16 |
17 | const positions = []
18 |
19 | const foV05 = fieldOfView / 2
20 | const step = fieldOfView / division
21 |
22 | // for now, let's create a simple helper that lies in the xz plane
23 |
24 | for (let i = -foV05; i < foV05; i += step) {
25 | positions.push(0, 0, 0)
26 | positions.push(Math.sin(i) * range, 0, Math.cos(i) * range)
27 | positions.push(Math.sin(i + step) * range, 0, Math.cos(i + step) * range)
28 | }
29 |
30 | const indices = []
31 | for (let i = 0; i < positions.length / 3; i++) {
32 | indices.push(i)
33 | }
34 |
35 | const normals = []
36 |
37 | const vertexData = new BABYLON.VertexData()
38 | BABYLON.VertexData.ComputeNormals(positions, indices, normals)
39 |
40 | vertexData.positions = positions
41 | vertexData.indices = indices
42 | vertexData.normals = normals
43 |
44 | vertexData.applyToMesh(customMesh)
45 |
46 | return customMesh
47 | }
48 |
49 | export { createVisionHelper }
50 |
--------------------------------------------------------------------------------
/examples/js/perception/lineOfSight/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Yuka | Perception | Line of Sight
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | The white fan represents the visibility range of the game entity.
15 | When the target is visible for the game entity, the target's color changes to green.
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/examples/js/perception/lineOfSight/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Mugen87 / https://github.com/Mugen87
3 | * @author Examples with Babylon.js were made at https://github.com/eldinor/yuka-babylonjs-examples / roland@babylonjs.xyz
4 | */
5 |
6 | import * as YUKA from '../../../../../lib/yuka.module.js'
7 |
8 | import 'https://preview.babylonjs.com/babylon.js'
9 | import 'https://preview.babylonjs.com/materialsLibrary/babylonjs.materials.min.js'
10 |
11 | import { createVisionHelper } from '../common/VisionHelper.js'
12 | import { Obstacle } from '../common/Obstacle.js'
13 | import { createVehicle, VehicleTypes } from '../../creator.js'
14 |
15 | let engine, scene, targetMaterial
16 | let entityManager, time, entity, target
17 |
18 | const entityMatrix = new BABYLON.Matrix()
19 | const pointer = new BABYLON.Vector2(1, 1)
20 |
21 | init()
22 | animate()
23 |
24 | function init() {
25 | const canvas = document.getElementById('renderCanvas')
26 | engine = new BABYLON.Engine(canvas, true, {}, true)
27 |
28 | scene = new BABYLON.Scene(engine)
29 | scene.clearColor = new BABYLON.Color4(0, 0, 0, 1)
30 | scene.useRightHandedSystem = true
31 |
32 | // scene.debugLayer.show()
33 |
34 | const camera = new BABYLON.ArcRotateCamera(
35 | 'camera',
36 | BABYLON.Tools.ToRadians(90),
37 | BABYLON.Tools.ToRadians(40),
38 | 12,
39 | new BABYLON.Vector3(0, 0, 0),
40 | scene
41 | )
42 |
43 | camera.setTarget(new BABYLON.Vector3(0, -4, 0))
44 | camera.attachControl(canvas, true)
45 |
46 | new BABYLON.HemisphericLight('light', new BABYLON.Vector3(1, 1, 0))
47 |
48 | const ground = BABYLON.MeshBuilder.CreateGround('ground', { width: 10, height: 10 }, scene)
49 | ground.position.y = -1
50 | ground.position.z = 1
51 | const groundMaterial = new BABYLON.GridMaterial('grid', scene)
52 | groundMaterial.gridRatio = 0.4
53 | ground.visibility = 0.35
54 | ground.material = groundMaterial
55 |
56 | const obstacleMesh = BABYLON.MeshBuilder.CreateBox('obstacleMesh', { width: 2, height: 2, depth: 0.2 }, scene)
57 | obstacleMesh.rotation.y = Math.PI
58 | obstacleMesh.position.z = 2
59 |
60 | obstacleMesh.material = new BABYLON.StandardMaterial('obstacle', scene)
61 | obstacleMesh.material.backFaceCulling = false
62 |
63 | const entityMesh = createVehicle(scene, { type: VehicleTypes.cone })
64 |
65 | const targetMesh = BABYLON.MeshBuilder.CreateSphere('target', { diameter: 0.15, segments: 8 }, scene)
66 | targetMaterial = new BABYLON.StandardMaterial('target', scene)
67 | targetMesh.material = targetMaterial
68 | targetMaterial.disableLighting = true
69 |
70 | scene.onPointerMove = () => {
71 | var pickResult = scene.pick(scene.pointerX, scene.pointerY)
72 | if (pickResult?.pickedPoint) {
73 | target.x = pickResult.pickedPoint.x
74 | target.y = pickResult.pickedPoint.y
75 | target.z = pickResult.pickedPoint.z
76 | }
77 | }
78 |
79 | window.addEventListener('resize', onWindowResize, false)
80 |
81 | // YUKA specific
82 | entityManager = new YUKA.EntityManager()
83 | time = new YUKA.Time()
84 |
85 | const vertices = obstacleMesh.getVerticesData(BABYLON.VertexBuffer.PositionKind)
86 | const indices = obstacleMesh.getIndices()
87 | const geometry = new YUKA.MeshGeometry(vertices, indices)
88 |
89 | const obstacle = new Obstacle(geometry)
90 | obstacle.position.z = 3
91 | obstacle.setRenderComponent(obstacleMesh, sync)
92 |
93 | target = new YUKA.GameEntity()
94 | target.setRenderComponent(targetMesh, sync)
95 |
96 | entity = new YUKA.GameEntity()
97 | entity.setRenderComponent(entityMesh, sync)
98 |
99 | const vision = new YUKA.Vision(entity)
100 | vision.range = 5
101 | vision.fieldOfView = Math.PI * 0.5
102 | vision.addObstacle(obstacle)
103 | entity.vision = vision
104 |
105 | const helper = createVisionHelper(vision)
106 |
107 | entityManager.add(entity)
108 | entityManager.add(obstacle)
109 | entityManager.add(target)
110 | }
111 |
112 | function onWindowResize() {
113 | engine.resize()
114 | }
115 |
116 | function animate() {
117 | requestAnimationFrame(animate)
118 |
119 | const delta = time.update().getDelta()
120 | const elapsed = time.getElapsed()
121 |
122 | // change color of target if visible
123 | target.position.set(Math.sin(elapsed * 0.5) * 4, 0, 4)
124 |
125 | if (entity.vision.visible(target.position) === true) {
126 | targetMaterial.emissiveColor = new BABYLON.Color3(0, 1, 0)
127 | } else {
128 | targetMaterial.emissiveColor = new BABYLON.Color3(1, 0, 0)
129 | }
130 |
131 | entityManager.update(delta)
132 |
133 | scene.render()
134 | }
135 |
136 | function sync(entity, renderComponent) {
137 | BABYLON.Matrix.FromValues(...entity.worldMatrix.elements).decomposeToTransformNode(renderComponent)
138 | }
139 |
--------------------------------------------------------------------------------
/examples/js/perception/memorySystem/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Yuka | Perception | Memory System
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | The game entity uses a memory system to remember the last sensed position of the target.
15 | If the target is outside the game entity's visual range for a certain amout of time,
16 | the game entity forgets the target.
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/examples/js/perception/memorySystem/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Mugen87 / https://github.com/Mugen87
3 | * @author Examples with Babylon.js were made at https://github.com/eldinor/yuka-babylonjs-examples / roland@babylonjs.xyz
4 | */
5 |
6 | import * as YUKA from '../../../../lib/yuka.module.js'
7 | // import * as DAT from 'https://cdn.jsdelivr.net/npm/dat.gui@0.7.7/build/dat.gui.module.js';
8 |
9 | import 'https://preview.babylonjs.com/babylon.js'
10 | import 'https://preview.babylonjs.com/materialsLibrary/babylonjs.materials.min.js'
11 | // import 'https://preview.babylonjs.com/inspector/babylon.inspector.bundle.js'
12 | // import 'https://preview.babylonjs.com/loaders/babylonjs.loaders.min.js'
13 |
14 | import { CustomEntity } from './src/CustomEntity.js'
15 | import { Obstacle } from '../common/Obstacle.js'
16 | import { createVisionHelper } from '../common/VisionHelper.js'
17 | import { createVehicle } from '../../creator.js'
18 |
19 | let engine, scene, targetMaterial
20 | let entityManager, time, entity, target
21 |
22 | const entityMatrix = new BABYLON.Matrix()
23 | const pointer = new BABYLON.Vector2(1, 1)
24 |
25 | init()
26 | animate()
27 |
28 | function init() {
29 | const canvas = document.getElementById('renderCanvas')
30 | engine = new BABYLON.Engine(canvas, true, {}, true)
31 |
32 | scene = new BABYLON.Scene(engine)
33 | scene.clearColor = new BABYLON.Color4(0, 0, 0, 1)
34 | scene.useRightHandedSystem = true
35 |
36 | // scene.debugLayer.show()
37 |
38 | const camera = new BABYLON.ArcRotateCamera(
39 | 'camera',
40 | BABYLON.Tools.ToRadians(90),
41 | BABYLON.Tools.ToRadians(40),
42 | 12,
43 | new BABYLON.Vector3(0, 0, 0),
44 | scene
45 | )
46 |
47 | camera.setTarget(new BABYLON.Vector3(0, -4, 0))
48 | camera.attachControl(canvas, true)
49 |
50 | new BABYLON.HemisphericLight('light', new BABYLON.Vector3(1, 1, 0))
51 |
52 | const ground = BABYLON.MeshBuilder.CreateGround('ground', { width: 10, height: 10 }, scene)
53 | ground.position.y = -1
54 | ground.position.z = 1
55 | const groundMaterial = new BABYLON.GridMaterial('grid', scene)
56 | groundMaterial.gridRatio = 0.4
57 | ground.visibility = 0.35
58 | ground.material = groundMaterial
59 |
60 | const obstacleMesh = BABYLON.MeshBuilder.CreateBox('obstacleMesh', { width: 2, height: 2, depth: 0.2 }, scene)
61 | obstacleMesh.rotation.y = Math.PI
62 | obstacleMesh.position.z = 2
63 | obstacleMesh.material = new BABYLON.StandardMaterial('obstacle', scene)
64 | obstacleMesh.material.backFaceCulling = false
65 |
66 | const entityMesh = createVehicle(scene)
67 |
68 | const targetMesh = BABYLON.MeshBuilder.CreateSphere('target', { diameter: 0.15, segments: 8 }, scene)
69 | targetMaterial = new BABYLON.StandardMaterial('target', scene)
70 | targetMesh.material = targetMaterial
71 | targetMaterial.emissiveColor = BABYLON.Color3.Red()
72 | targetMaterial.disableLighting = true
73 |
74 | scene.onPointerMove = () => {
75 | var pickResult = scene.pick(scene.pointerX, scene.pointerY)
76 | if (pickResult?.pickedPoint) {
77 | target.x = pickResult.pickedPoint.x
78 | target.y = pickResult.pickedPoint.y
79 | target.z = pickResult.pickedPoint.z
80 | }
81 | }
82 |
83 | window.addEventListener('resize', onWindowResize, false)
84 |
85 | // YUKA specific
86 | entityManager = new YUKA.EntityManager()
87 | time = new YUKA.Time()
88 |
89 | const vertices = obstacleMesh.getVerticesData(BABYLON.VertexBuffer.PositionKind)
90 | const indices = obstacleMesh.getIndices()
91 | const geometry = new YUKA.MeshGeometry(vertices, indices)
92 |
93 | const obstacle = new Obstacle(geometry)
94 | obstacle.name = 'obstacle'
95 | obstacle.position.z = 3
96 | obstacle.setRenderComponent(obstacleMesh, sync)
97 |
98 | target = new YUKA.GameEntity()
99 | target.name = 'target'
100 | target.setRenderComponent(targetMesh, sync)
101 |
102 | const entity = new CustomEntity()
103 | entity.setRenderComponent(entityMesh, sync)
104 |
105 | const helper = createVisionHelper(entity.vision)
106 |
107 | entityManager.add(entity)
108 | entityManager.add(obstacle)
109 | entityManager.add(target)
110 | }
111 |
112 | function onWindowResize() {
113 | engine.resize()
114 | }
115 |
116 | function animate() {
117 | requestAnimationFrame(animate)
118 |
119 | time.update()
120 |
121 | const delta = time.getDelta()
122 | const elapsed = time.getElapsed()
123 |
124 | target.position.set(Math.sin(elapsed * 0.3) * 5, 0, 4)
125 |
126 | entityManager.update(delta)
127 |
128 | scene.render()
129 | }
130 |
131 | function sync(entity, renderComponent) {
132 | BABYLON.Matrix.FromValues(...entity.worldMatrix.elements).decomposeToTransformNode(renderComponent)
133 | }
134 |
--------------------------------------------------------------------------------
/examples/js/perception/memorySystem/src/CustomEntity.js:
--------------------------------------------------------------------------------
1 | import { GameEntity, Vision, MemorySystem } from '../../../../../lib/yuka.module.js'
2 |
3 | class CustomEntity extends GameEntity {
4 | constructor() {
5 | super()
6 |
7 | this.memorySystem = new MemorySystem()
8 | this.memorySystem.memorySpan = 3
9 |
10 | this.vision = new Vision(this)
11 | this.vision.range = 5
12 | this.vision.fieldOfView = Math.PI * 0.5
13 |
14 | this.maxTurnRate = Math.PI * 0.5
15 |
16 | this.currentTime = 0
17 | this.memoryRecords = new Array()
18 |
19 | this.target = null
20 | }
21 |
22 | start() {
23 | const target = this.manager.getEntityByName('target')
24 | const obstacle = this.manager.getEntityByName('obstacle')
25 |
26 | this.target = target
27 | this.vision.addObstacle(obstacle)
28 |
29 | return this
30 | }
31 |
32 | update(delta) {
33 | this.currentTime += delta
34 |
35 | // In many scenarios it is not necessary to update the vision in each
36 | // simulation step. A regulator could be used to restrict the update rate.
37 |
38 | this.updateVision()
39 |
40 | // get a list of all recently sensed game entities
41 |
42 | this.memorySystem.getValidMemoryRecords(this.currentTime, this.memoryRecords)
43 |
44 | if (this.memoryRecords.length > 0) {
45 | // Pick the first one. It's highly application specific what record is chosen
46 | // for further processing.
47 |
48 | const record = this.memoryRecords[0]
49 | const entity = record.entity
50 |
51 | // if the game entity is visible, directly rotate towards it. Otherwise, focus
52 | // the last known position
53 |
54 | if (record.visible === true) {
55 | this.rotateTo(entity.position, delta)
56 | entity._renderComponent.material.emissiveColor = new BABYLON.Color3(0, 1, 0) // some visual feedback
57 | } else {
58 | // only rotate to the last sensed position if the entity was seen at least once
59 |
60 | if (record.timeLastSensed !== -1) {
61 | this.rotateTo(record.lastSensedPosition, delta)
62 |
63 | entity._renderComponent.material.emissiveColor = new BABYLON.Color3(1, 0, 0) // some visual feedback
64 | }
65 | }
66 | } else {
67 | // rotate back to default
68 |
69 | this.rotateTo(this.forward, delta)
70 | }
71 |
72 | return this
73 | }
74 |
75 | updateVision() {
76 | const memorySystem = this.memorySystem
77 | const vision = this.vision
78 | const target = this.target
79 |
80 | if (memorySystem.hasRecord(target) === false) {
81 | memorySystem.createRecord(target)
82 | }
83 |
84 | const record = memorySystem.getRecord(target)
85 |
86 | if (vision.visible(target.position) === true) {
87 | record.timeLastSensed = this.currentTime
88 | record.lastSensedPosition.copy(target.position)
89 | record.visible = true
90 | } else {
91 | record.visible = false
92 | }
93 | }
94 | }
95 |
96 | export { CustomEntity }
97 |
--------------------------------------------------------------------------------
/examples/js/playground/hideAndSeek/audio/dead.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldinor/yuka-babylonjs-examples/f8d9f36fe181dc5f3ef4c2746dbe39ea8d884e37/examples/js/playground/hideAndSeek/audio/dead.ogg
--------------------------------------------------------------------------------
/examples/js/playground/hideAndSeek/audio/empty.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldinor/yuka-babylonjs-examples/f8d9f36fe181dc5f3ef4c2746dbe39ea8d884e37/examples/js/playground/hideAndSeek/audio/empty.ogg
--------------------------------------------------------------------------------
/examples/js/playground/hideAndSeek/audio/impact1.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldinor/yuka-babylonjs-examples/f8d9f36fe181dc5f3ef4c2746dbe39ea8d884e37/examples/js/playground/hideAndSeek/audio/impact1.ogg
--------------------------------------------------------------------------------
/examples/js/playground/hideAndSeek/audio/impact2.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldinor/yuka-babylonjs-examples/f8d9f36fe181dc5f3ef4c2746dbe39ea8d884e37/examples/js/playground/hideAndSeek/audio/impact2.ogg
--------------------------------------------------------------------------------
/examples/js/playground/hideAndSeek/audio/impact3.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldinor/yuka-babylonjs-examples/f8d9f36fe181dc5f3ef4c2746dbe39ea8d884e37/examples/js/playground/hideAndSeek/audio/impact3.ogg
--------------------------------------------------------------------------------
/examples/js/playground/hideAndSeek/audio/impact4.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldinor/yuka-babylonjs-examples/f8d9f36fe181dc5f3ef4c2746dbe39ea8d884e37/examples/js/playground/hideAndSeek/audio/impact4.ogg
--------------------------------------------------------------------------------
/examples/js/playground/hideAndSeek/audio/impact5.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldinor/yuka-babylonjs-examples/f8d9f36fe181dc5f3ef4c2746dbe39ea8d884e37/examples/js/playground/hideAndSeek/audio/impact5.ogg
--------------------------------------------------------------------------------
/examples/js/playground/hideAndSeek/audio/reload.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldinor/yuka-babylonjs-examples/f8d9f36fe181dc5f3ef4c2746dbe39ea8d884e37/examples/js/playground/hideAndSeek/audio/reload.ogg
--------------------------------------------------------------------------------
/examples/js/playground/hideAndSeek/audio/shot.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldinor/yuka-babylonjs-examples/f8d9f36fe181dc5f3ef4c2746dbe39ea8d884e37/examples/js/playground/hideAndSeek/audio/shot.ogg
--------------------------------------------------------------------------------
/examples/js/playground/hideAndSeek/audio/shot_reload.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldinor/yuka-babylonjs-examples/f8d9f36fe181dc5f3ef4c2746dbe39ea8d884e37/examples/js/playground/hideAndSeek/audio/shot_reload.ogg
--------------------------------------------------------------------------------
/examples/js/playground/hideAndSeek/audio/step1.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldinor/yuka-babylonjs-examples/f8d9f36fe181dc5f3ef4c2746dbe39ea8d884e37/examples/js/playground/hideAndSeek/audio/step1.ogg
--------------------------------------------------------------------------------
/examples/js/playground/hideAndSeek/audio/step2.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldinor/yuka-babylonjs-examples/f8d9f36fe181dc5f3ef4c2746dbe39ea8d884e37/examples/js/playground/hideAndSeek/audio/step2.ogg
--------------------------------------------------------------------------------
/examples/js/playground/hideAndSeek/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Yuka | Hide And Seek
4 |
5 |
6 |
7 |
8 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
Click to Play
98 |
Try to frag as many enemies as you can within 60 seconds. Have fun 🙌!
99 |
100 |
101 |
Game Over
102 |
Congratulations! You've fragged enemies.
103 |
104 | Restart Game
105 |
106 |
107 |
108 |
109 |
112 |
113 |
118 |
119 |
122 |
123 |
124 |
125 |
126 | |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
--------------------------------------------------------------------------------
/examples/js/playground/hideAndSeek/index.js:
--------------------------------------------------------------------------------
1 | import world from './src/World.js'
2 |
3 | init()
4 |
5 | function init() {
6 | void world.init()
7 | }
8 |
--------------------------------------------------------------------------------
/examples/js/playground/hideAndSeek/model/README.md:
--------------------------------------------------------------------------------
1 | Shotgun from https://sketchfab.com/models/53b158b0d5a54b4491b09d1fb3058e29 by Harry_L
2 | License: CC Attribution
3 |
--------------------------------------------------------------------------------
/examples/js/playground/hideAndSeek/model/bulletHole.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldinor/yuka-babylonjs-examples/f8d9f36fe181dc5f3ef4c2746dbe39ea8d884e37/examples/js/playground/hideAndSeek/model/bulletHole.png
--------------------------------------------------------------------------------
/examples/js/playground/hideAndSeek/model/muzzle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldinor/yuka-babylonjs-examples/f8d9f36fe181dc5f3ef4c2746dbe39ea8d884e37/examples/js/playground/hideAndSeek/model/muzzle.png
--------------------------------------------------------------------------------
/examples/js/playground/hideAndSeek/model/shotgun-new.glb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldinor/yuka-babylonjs-examples/f8d9f36fe181dc5f3ef4c2746dbe39ea8d884e37/examples/js/playground/hideAndSeek/model/shotgun-new.glb
--------------------------------------------------------------------------------
/examples/js/playground/hideAndSeek/model/shotgun.glb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldinor/yuka-babylonjs-examples/f8d9f36fe181dc5f3ef4c2746dbe39ea8d884e37/examples/js/playground/hideAndSeek/model/shotgun.glb
--------------------------------------------------------------------------------
/examples/js/playground/hideAndSeek/src/Bullet.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Mugen87 / https://github.com/Mugen87
3 | * @author Examples with Babylon.js were made at https://github.com/eldinor/yuka-babylonjs-examples / roland@babylonjs.xyz
4 | */
5 |
6 | import { MovingEntity, MathUtils, Ray, Vector3 } from '../../../../../lib/yuka.module.js'
7 | import world from './World.js'
8 | import { Enemy } from './Enemy.js'
9 |
10 | const intersectionPoint = new Vector3()
11 | const normal = new Vector3()
12 | const ray = new Ray()
13 |
14 | class Bullet extends MovingEntity {
15 | constructor(owner = null, ray = new Ray()) {
16 | super()
17 |
18 | this.owner = owner
19 | this.ray = ray
20 |
21 | this.maxSpeed = 400 // 400 m/s
22 |
23 | this.position.copy(ray.origin)
24 | this.velocity.copy(ray.direction).multiplyScalar(this.maxSpeed)
25 |
26 | const s = 1 + Math.random() * 3 // scale the shot line a bit
27 |
28 | this.lifetime = 1
29 | this.currentTime = 0
30 | }
31 |
32 | update(delta) {
33 | this.currentTime += delta
34 |
35 | if (this.currentTime > this.lifetime) {
36 | world.remove(this)
37 | } else {
38 | ray.copy(this.ray)
39 | ray.origin.copy(this.position)
40 |
41 | super.update(delta)
42 |
43 | const obstacle = world.intersectRay(ray, intersectionPoint, normal)
44 |
45 | if (obstacle !== null) {
46 | // calculate distance from origin to intersection point
47 |
48 | const distanceToIntersection = ray.origin.squaredDistanceTo(intersectionPoint)
49 | const validDistance = ray.origin.squaredDistanceTo(this.position)
50 |
51 | if (distanceToIntersection <= validDistance) {
52 | // hit!
53 |
54 | const audio = world.audios.get('impact' + MathUtils.randInt(1, 5))
55 | if (audio.isPlaying === true) {
56 | audio.stop()
57 | }
58 | audio.play()
59 |
60 | // inform game entity about hit
61 |
62 | this.owner.sendMessage(obstacle, 'hit')
63 |
64 | // add visual feedback
65 | if (obstacle instanceof Enemy === false) {
66 | world.addBulletHole(intersectionPoint, normal, audio)
67 | } else {
68 | // enemy hit
69 | world.assetManager.explodeEnemy(obstacle._renderComponent)
70 | }
71 |
72 | // remove bullet from world
73 |
74 | world.remove(this)
75 | }
76 | }
77 | }
78 |
79 | return this
80 | }
81 | }
82 |
83 | export { Bullet }
84 |
--------------------------------------------------------------------------------
/examples/js/playground/hideAndSeek/src/CustomObstacle.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Mugen87 / https://github.com/Mugen87
3 | * @author Examples with Babylon.js were made at https://github.com/eldinor/yuka-babylonjs-examples / roland@babylonjs.xyz
4 | */
5 |
6 | import { GameEntity } from '../../../../../lib/yuka.module.js'
7 |
8 | class CustomObstacle extends GameEntity {
9 | constructor(geometry) {
10 | super()
11 |
12 | this.geometry = geometry
13 | }
14 |
15 | handleMessage() {
16 | // do nothing
17 |
18 | return true
19 | }
20 | }
21 |
22 | export { CustomObstacle }
23 |
--------------------------------------------------------------------------------
/examples/js/playground/hideAndSeek/src/Enemy.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Mugen87 / https://github.com/Mugen87
3 | * @author Examples with Babylon.js were made at https://github.com/eldinor/yuka-babylonjs-examples / roland@babylonjs.xyz
4 | */
5 |
6 | import { Vehicle } from '../../../../../lib/yuka.module.js'
7 |
8 | import { HideBehavior } from './HideBehavior.js'
9 | import world from './World.js'
10 |
11 | class Enemy extends Vehicle {
12 | constructor(geometry) {
13 | super()
14 |
15 | this.name = 'enemy'
16 |
17 | this.geometry = geometry
18 | this.maxSpeed = 5
19 | this.deathAnimDuration = 0.5
20 | this.currentTime = 0
21 | this.dead = false
22 | this.notifiedWorld = false
23 | this.spawningPoint = null
24 | }
25 |
26 | start() {
27 | const player = this.manager.getEntityByName('player')
28 |
29 | const hideBehavior = new HideBehavior(this.manager, player)
30 | this.steering.add(hideBehavior)
31 |
32 | return this
33 | }
34 |
35 | update(delta) {
36 | super.update(delta)
37 |
38 | if (this.dead) {
39 | if (this.notifiedWorld === false) {
40 | this.notifiedWorld = true
41 | world.hits++
42 | world.refreshUI()
43 |
44 | //
45 |
46 | const audio = world.audios.get('dead')
47 | if (audio.isPlaying === true) {
48 | audio.stop()
49 | }
50 | audio.play()
51 |
52 | audio.attachToMesh(this._renderComponent)
53 | }
54 |
55 | this.currentTime += delta
56 |
57 | if (this.currentTime <= this.deathAnimDuration) {
58 | const value = this.currentTime / this.deathAnimDuration
59 |
60 | // const shader = this._renderComponent.material.userData.shader
61 |
62 | // shader.uniforms.alpha.value = value <= 1 ? value : 1
63 | // this._renderComponent.material.opacity = 1 - shader.uniforms.alpha.value
64 | } else {
65 | world.remove(this)
66 | }
67 | }
68 |
69 | return this
70 | }
71 |
72 | handleMessage() {
73 | this.dead = true
74 |
75 | // this._renderComponent.castShadow = false
76 |
77 | return true
78 | }
79 | }
80 |
81 | export { Enemy }
82 |
--------------------------------------------------------------------------------
/examples/js/playground/hideAndSeek/src/Ground.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Mugen87 / https://github.com/Mugen87
3 | * @author Examples with Babylon.js were made at https://github.com/eldinor/yuka-babylonjs-examples / roland@babylonjs.xyz
4 | */
5 |
6 | import { GameEntity } from '../../../../../lib/yuka.module.js'
7 |
8 | class Ground extends GameEntity {
9 | constructor(geometry) {
10 | super()
11 | this.geometry = geometry
12 | this.name = 'ground'
13 | }
14 |
15 | handleMessage() {
16 | // do nothing
17 |
18 | return true
19 | }
20 | }
21 |
22 | export { Ground }
23 |
--------------------------------------------------------------------------------
/examples/js/playground/hideAndSeek/src/Player.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Mugen87 / https://github.com/Mugen87
3 | * @author Examples with Babylon.js were made at https://github.com/eldinor/yuka-babylonjs-examples / roland@babylonjs.xyz
4 | */
5 |
6 | import { MovingEntity, GameEntity, Quaternion, AABB, Ray, Vector3 } from '../../../../../lib/yuka.module.js'
7 | import { CustomObstacle } from './CustomObstacle.js'
8 |
9 | import { Shotgun } from './Shotgun.js'
10 | import world from './World.js'
11 |
12 | const q = new Quaternion()
13 | const aabb = new AABB()
14 | const ray = new Ray()
15 | const intersectionPoint = new Vector3()
16 | const intersectionNormal = new Vector3()
17 | const reflectionVector = new Vector3()
18 |
19 | class Player extends MovingEntity {
20 | constructor(camera) {
21 | super()
22 |
23 | this.name = 'player'
24 | this.camera = camera
25 |
26 | this.boundingRadius = 1
27 |
28 | this.headContainer = new GameEntity()
29 | this.add(this.headContainer)
30 |
31 | this.head = new GameEntity()
32 | this.head.position.set(0, 2, 0)
33 | this.headContainer.add(this.head)
34 |
35 | this.weaponContainer = new GameEntity()
36 | this.head.add(this.weaponContainer)
37 |
38 | this.weapon = new Shotgun(this)
39 | this.weaponContainer.add(this.weapon)
40 |
41 | //
42 |
43 | this.forward.set(0, 0, -1)
44 | this.maxSpeed = 8
45 | this.updateOrientation = false
46 | }
47 |
48 | getDirection(result) {
49 | q.multiplyQuaternions(this.rotation, this.head.rotation)
50 |
51 | return result.copy(this.forward).applyRotation(q).normalize()
52 | }
53 |
54 | update(delta) {
55 | const obstacles = world.obstacles
56 |
57 | for (let i = 0, l = obstacles.length; i < l; i++) {
58 | const obstacle = obstacles[i]
59 |
60 | if (obstacle instanceof CustomObstacle) {
61 | // first check bounding volumes for intersection
62 |
63 | const squaredDistance = this.position.squaredDistanceTo(obstacle.position)
64 | const range = this.boundingRadius + obstacle.boundingRadius
65 |
66 | if (squaredDistance <= range * range) {
67 | // compute AABB in world space for obstacle
68 |
69 | aabb.copy(obstacle.geometry.aabb).applyMatrix4(obstacle.worldMatrix)
70 |
71 | // enhance the AABB with the bounding radius of the player
72 |
73 | aabb.max.addScalar(this.boundingRadius)
74 | aabb.min.subScalar(this.boundingRadius)
75 |
76 | // setup ray
77 |
78 | ray.origin.copy(this.position)
79 | ray.direction.copy(this.velocity).normalize()
80 |
81 | // perform ray/AABB intersection test
82 |
83 | if (ray.intersectAABB(aabb, intersectionPoint) !== null) {
84 | // derive normal vector
85 |
86 | aabb.getNormalFromSurfacePoint(intersectionPoint, intersectionNormal)
87 |
88 | // compute reflection vector
89 |
90 | reflectionVector.copy(ray.direction).reflect(intersectionNormal)
91 |
92 | // compute new velocity vector
93 |
94 | const speed = this.getSpeed()
95 |
96 | this.velocity.addVectors(ray.direction, reflectionVector).normalize()
97 |
98 | const f = 1 - Math.abs(intersectionNormal.dot(ray.direction))
99 |
100 | this.velocity.multiplyScalar(speed * f)
101 | }
102 | }
103 | }
104 | }
105 |
106 | return super.update(delta)
107 | }
108 | }
109 |
110 | export { Player }
111 |
--------------------------------------------------------------------------------
/examples/js/playground/hideAndSeek/textures/sparkStretched.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldinor/yuka-babylonjs-examples/f8d9f36fe181dc5f3ef4c2746dbe39ea8d884e37/examples/js/playground/hideAndSeek/textures/sparkStretched.png
--------------------------------------------------------------------------------
/examples/js/playground/shooter/audio/empty.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldinor/yuka-babylonjs-examples/f8d9f36fe181dc5f3ef4c2746dbe39ea8d884e37/examples/js/playground/shooter/audio/empty.ogg
--------------------------------------------------------------------------------
/examples/js/playground/shooter/audio/impact1.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldinor/yuka-babylonjs-examples/f8d9f36fe181dc5f3ef4c2746dbe39ea8d884e37/examples/js/playground/shooter/audio/impact1.ogg
--------------------------------------------------------------------------------
/examples/js/playground/shooter/audio/impact2.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldinor/yuka-babylonjs-examples/f8d9f36fe181dc5f3ef4c2746dbe39ea8d884e37/examples/js/playground/shooter/audio/impact2.ogg
--------------------------------------------------------------------------------
/examples/js/playground/shooter/audio/impact3.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldinor/yuka-babylonjs-examples/f8d9f36fe181dc5f3ef4c2746dbe39ea8d884e37/examples/js/playground/shooter/audio/impact3.ogg
--------------------------------------------------------------------------------
/examples/js/playground/shooter/audio/impact4.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldinor/yuka-babylonjs-examples/f8d9f36fe181dc5f3ef4c2746dbe39ea8d884e37/examples/js/playground/shooter/audio/impact4.ogg
--------------------------------------------------------------------------------
/examples/js/playground/shooter/audio/impact5.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldinor/yuka-babylonjs-examples/f8d9f36fe181dc5f3ef4c2746dbe39ea8d884e37/examples/js/playground/shooter/audio/impact5.ogg
--------------------------------------------------------------------------------
/examples/js/playground/shooter/audio/reload.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldinor/yuka-babylonjs-examples/f8d9f36fe181dc5f3ef4c2746dbe39ea8d884e37/examples/js/playground/shooter/audio/reload.ogg
--------------------------------------------------------------------------------
/examples/js/playground/shooter/audio/shot.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldinor/yuka-babylonjs-examples/f8d9f36fe181dc5f3ef4c2746dbe39ea8d884e37/examples/js/playground/shooter/audio/shot.ogg
--------------------------------------------------------------------------------
/examples/js/playground/shooter/audio/step1.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldinor/yuka-babylonjs-examples/f8d9f36fe181dc5f3ef4c2746dbe39ea8d884e37/examples/js/playground/shooter/audio/step1.ogg
--------------------------------------------------------------------------------
/examples/js/playground/shooter/audio/step2.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldinor/yuka-babylonjs-examples/f8d9f36fe181dc5f3ef4c2746dbe39ea8d884e37/examples/js/playground/shooter/audio/step2.ogg
--------------------------------------------------------------------------------
/examples/js/playground/shooter/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Yuka | First-Person Shooter
4 |
5 |
6 |
7 |
8 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 | Click to Play
80 |
81 | This demo implements some basic concepts of First-Person shooters e.g. simulating bullets and collision
82 | detection.
83 |
84 |
85 |
86 |
89 |
90 |
93 |
94 |
95 |
96 |
97 | |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
--------------------------------------------------------------------------------
/examples/js/playground/shooter/index.js:
--------------------------------------------------------------------------------
1 | import world from './src/World.js'
2 |
3 | init()
4 |
5 | function init() {
6 | void world.init()
7 | }
8 |
--------------------------------------------------------------------------------
/examples/js/playground/shooter/model/README.md:
--------------------------------------------------------------------------------
1 | SciFi Gun from https://sketchfab.com/models/04a9f3ccb5b14dc38a28b27c1916e18e by Feche Pedroza
2 | License: CC Attribution
3 |
4 | Target (Hackathon tournament) from https://sketchfab.com/models/e2f631c75c83440887d2613fe4aeb84c by NikiYani
5 | License: CC Attribution
6 |
--------------------------------------------------------------------------------
/examples/js/playground/shooter/model/bulletHole.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldinor/yuka-babylonjs-examples/f8d9f36fe181dc5f3ef4c2746dbe39ea8d884e37/examples/js/playground/shooter/model/bulletHole.png
--------------------------------------------------------------------------------
/examples/js/playground/shooter/model/gun.glb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldinor/yuka-babylonjs-examples/f8d9f36fe181dc5f3ef4c2746dbe39ea8d884e37/examples/js/playground/shooter/model/gun.glb
--------------------------------------------------------------------------------
/examples/js/playground/shooter/model/muzzle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldinor/yuka-babylonjs-examples/f8d9f36fe181dc5f3ef4c2746dbe39ea8d884e37/examples/js/playground/shooter/model/muzzle.png
--------------------------------------------------------------------------------
/examples/js/playground/shooter/model/target.glb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldinor/yuka-babylonjs-examples/f8d9f36fe181dc5f3ef4c2746dbe39ea8d884e37/examples/js/playground/shooter/model/target.glb
--------------------------------------------------------------------------------
/examples/js/playground/shooter/src/Bullet.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Mugen87 / https://github.com/Mugen87
3 | * @author Examples with Babylon.js were made at https://github.com/eldinor/yuka-babylonjs-examples / roland@babylonjs.xyz
4 | */
5 |
6 | import { MovingEntity, MathUtils, Ray, Vector3 } from '../../../../../lib/yuka.module.js'
7 | import world from './World.js'
8 |
9 | const intersectionPoint = new Vector3()
10 | const normal = new Vector3()
11 | const ray = new Ray()
12 |
13 | class Bullet extends MovingEntity {
14 | constructor(owner = null, ray = new Ray()) {
15 | super()
16 |
17 | this.owner = owner
18 | this.ray = ray
19 |
20 | this.maxSpeed = 400 // 400 m/s
21 |
22 | this.position.copy(ray.origin)
23 | this.velocity.copy(ray.direction).multiplyScalar(this.maxSpeed)
24 |
25 | const s = 1 + Math.random() * 3 // scale the shot line a bit
26 |
27 | this.lifetime = 100
28 | this.currentTime = 0
29 | }
30 |
31 | update(delta) {
32 | this.currentTime += delta
33 |
34 | if (this.currentTime > this.lifetime) {
35 | world.remove(this)
36 | } else {
37 | ray.copy(this.ray)
38 | ray.origin.copy(this.position)
39 | super.update(delta)
40 |
41 | const entity = world.intersectRay(ray, intersectionPoint, normal)
42 |
43 | if (entity !== null && entity.name === 'target') {
44 | // calculate distance from origin to intersection point
45 | const distanceToIntersection = ray.origin.squaredDistanceTo(intersectionPoint)
46 | const validDistance = ray.origin.squaredDistanceTo(this.position)
47 |
48 | if (distanceToIntersection <= validDistance) {
49 | // hit!
50 | const audio = world.audios.get('impact' + MathUtils.randInt(1, 5))
51 |
52 | if (audio.isPlaying === true) {
53 | audio.stop()
54 | }
55 | audio.play()
56 |
57 | // inform game entity about hit
58 | this.owner.sendMessage(entity, 'hit')
59 |
60 | // add visual feedback
61 | world.addBulletHole(intersectionPoint, normal, audio)
62 |
63 | // remove bullet from world
64 | world.remove(this)
65 | }
66 | }
67 | }
68 |
69 | return this
70 | }
71 | }
72 |
73 | export { Bullet }
74 |
--------------------------------------------------------------------------------
/examples/js/playground/shooter/src/Ground.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Mugen87 / https://github.com/Mugen87
3 | * @author Examples with Babylon.js were made at https://github.com/eldinor/yuka-babylonjs-examples / roland@babylonjs.xyz
4 | */
5 |
6 | import { GameEntity } from '../../../../../lib/yuka.module.js'
7 |
8 | class Ground extends GameEntity {
9 | constructor(geometry) {
10 | super()
11 | this.geometry = geometry
12 | }
13 |
14 | handleMessage() {
15 | // do nothing
16 |
17 | return true
18 | }
19 | }
20 |
21 | export { Ground }
22 |
--------------------------------------------------------------------------------
/examples/js/playground/shooter/src/Player.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Mugen87 / https://github.com/Mugen87
3 | * @author Examples with Babylon.js were made at https://github.com/eldinor/yuka-babylonjs-examples / roland@babylonjs.xyz
4 | */
5 |
6 | import { MovingEntity, GameEntity, Quaternion } from '../../../../../lib/yuka.module.js'
7 | import { Blaster } from './Blaster.js'
8 |
9 | const q = new Quaternion()
10 |
11 | class Player extends MovingEntity {
12 | constructor(camera) {
13 | super()
14 | this.camera = camera
15 |
16 | this.headContainer = new GameEntity()
17 | this.add(this.headContainer)
18 |
19 | this.head = new GameEntity()
20 | this.head.position.set(0, 2, 10)
21 | this.headContainer.add(this.head)
22 |
23 | this.weaponContainer = new GameEntity()
24 | this.head.add(this.weaponContainer)
25 |
26 | this.weapon = new Blaster(this)
27 | this.weaponContainer.add(this.weapon)
28 |
29 | //
30 |
31 | this.forward.set(0, 0, -1)
32 | this.maxSpeed = 10
33 | this.updateOrientation = false
34 | }
35 |
36 | getDirection(result) {
37 | q.multiplyQuaternions(this.rotation, this.head.rotation)
38 |
39 | return result.copy(this.forward).applyRotation(q).normalize()
40 | }
41 | }
42 |
43 | export { Player }
44 |
--------------------------------------------------------------------------------
/examples/js/playground/shooter/src/Target.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Mugen87 / https://github.com/Mugen87
3 | * @author Examples with Babylon.js were made at https://github.com/eldinor/yuka-babylonjs-examples / roland@babylonjs.xyz
4 | */
5 |
6 | import { GameEntity } from '../../../../../lib/yuka.module.js'
7 |
8 | class Target extends GameEntity {
9 | constructor(geometry) {
10 | super()
11 |
12 | this.uiElement = document.getElementById('hit')
13 |
14 | this.name = 'target'
15 | this.endTime = Infinity
16 | this.currentTime = 0
17 | this.duration = 1 // 1 second
18 | this.geometry = geometry
19 | }
20 |
21 | update(delta) {
22 | this.currentTime += delta
23 |
24 | if (this.currentTime >= this.endTime) {
25 | this.uiElement.classList.add('hidden')
26 | this.endTime = Infinity
27 | }
28 |
29 | return this
30 | }
31 |
32 | handleMessage() {
33 | this.uiElement.classList.remove('hidden')
34 |
35 | this.endTime = this.currentTime + this.duration
36 |
37 | return true
38 | }
39 | }
40 |
41 | export { Target }
42 |
--------------------------------------------------------------------------------
/examples/js/showcases/kickoff/assets/ball.glb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldinor/yuka-babylonjs-examples/f8d9f36fe181dc5f3ef4c2746dbe39ea8d884e37/examples/js/showcases/kickoff/assets/ball.glb
--------------------------------------------------------------------------------
/examples/js/showcases/kickoff/assets/goal.glb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldinor/yuka-babylonjs-examples/f8d9f36fe181dc5f3ef4c2746dbe39ea8d884e37/examples/js/showcases/kickoff/assets/goal.glb
--------------------------------------------------------------------------------
/examples/js/showcases/kickoff/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Yuka | First-Person Controls
4 |
5 |
6 |
7 |
8 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | 0
51 | :
52 | 0
53 |
54 |
55 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/examples/js/showcases/kickoff/index.js:
--------------------------------------------------------------------------------
1 | import world from './src/core/World.js'
2 | world.init()
3 |
--------------------------------------------------------------------------------
/examples/js/showcases/kickoff/src/core/Constants.js:
--------------------------------------------------------------------------------
1 | export const MESSAGE = {
2 | RETURN_HOME: 'RETURN_HOME',
3 | PASS_TO_ME: 'PASS_TO_ME',
4 | RECEIVE_BALL: 'RECEIVE_BALL',
5 | SUPPORT_ATTACKER: 'SUPPORT_ATTACKER',
6 | GOAL_SCORED: 'GOAL_SCORED'
7 | };
8 | export const GOALKEEPER_STATES = {
9 | RETURN_HOME: 'RETURN_HOME',
10 | TEND_GOAL: 'TEND_GOAL',
11 | PUT_BALL_BACK_IN_PLAY: 'PUT_BALL_BACK_IN_PLAY',
12 | INTERCEPT_BALL: 'INTERCEPT_BALL'
13 | };
14 | export const FIELDPLAYER_STATES = {
15 | CHASE_BALL: 'CHASE_BALL',
16 | DRIBBLE: 'DRIBBLE',
17 | KICK_BALL: 'KICK_BALL',
18 | RECEIVE_BALL: 'RECEIVE_BALL',
19 | RETURN_HOME: 'RETURN_HOME',
20 | SUPPORT_ATTACKER: 'SUPPORT_ATTACKER',
21 | WAIT: 'WAIT'
22 | };
23 | export const TEAM_STATES = {
24 | ATTACKING: 'ATTACKING',
25 | DEFENDING: 'DEFENDING',
26 | PREPARE_FOR_KICKOFF: 'PREPARE_FOR_KICKOFF'
27 | };
28 | export const CONFIG = {
29 | GOALKEEPER_IN_TARGET_RANGE: 0.5, // the goalkeeper has to be this close to the ball to be able to interact with it
30 | GOALKEEPER_INTERCEPT_RANGE: 4, // when the ball becomes within this distance of the goalkeeper he changes state to intercept the ball
31 | GOALKEEPER_MIN_PASS_DISTANCE: 2, // // the minimum distance a player must be from the goalkeeper before it will pass the ball
32 | GOALKEEPER_TENDING_DISTANCE: 2, // this is the distance the keeper puts between the back of the net and the ball when using the interpose steering behavior
33 | PLAYER_CHANCE_OF_USING_ARRIVE_TYPE_RECEIVE_BEHAVIOR: 0.5, // this is the chance that a player will receive a pass using the "arrive" steering behavior, rather than "pursuit"
34 | PLAYER_CHANCE_ATTEMPT_POT_SHOT: 0.005, // the chance a player might take a random pot shot at the goal
35 | PLAYER_COMFORT_ZONE: 2.5, // when an opponents comes within this range the player will attempt to pass the ball. Players tend to pass more often, the higher the value
36 | PLAYER_IN_TARGET_RANGE: 0.25, // the player has to be this close to its steering target to be considered as arrived
37 | PLAYER_KICK_FREQUENCY: 1, // the number of times a player can kick the ball per second
38 | PLAYER_KICKING_RANGE: 0.3, // player has to be this close to the ball to be able to kick it
39 | PLAYER_MAX_DRIBBLE_AND_TURN_FORCE: 0.4, // the force used for dribbling while turning around
40 | PLAYER_MAX_DRIBBLE_FORCE: 0.6, // the force used for dribbling
41 | PLAYER_MAX_PASSING_FORCE: 3, // the force used for passing
42 | PLAYER_MAX_SHOOTING_FORCE: 4, // the force used for shooting at the goal
43 | PLAYER_MAX_SPEED_WITH_BALL: 0.8, // max speed with ball
44 | PLAYER_MAX_SPEED_WITHOUT_BALL: 1, // max speed without ball
45 | PLAYER_MIN_PASS_DISTANCE: 5, // the minimum distance a receiving player must be from the passing player
46 | PLAYER_NUM_ATTEMPTS_TO_FIND_VALID_STRIKE: 5, // the number of times the player attempts to find a valid shot
47 | PLAYER_RECEIVING_RANGE: 1, // how close the ball must be to a receiver before he starts chasing it
48 | PLAYER_PASS_INTERCEPT_SCALE: 0.3, // this value decreases the range of possible pass targets a player can reach "in time"
49 | PLAYER_PASS_REQUEST_SUCCESS: 0.1, // the likelihood that a pass request is successful
50 | PLAYER_PASS_THREAD_RADIUS: 3, // the radius in which a pass in dangerous
51 | SUPPORT_SPOT_CALCULATOR_SLICE_X: 12, // x dimension of spot
52 | SUPPORT_SPOT_CALCULATOR_SLICE_Y: 5, // y dimension of spot
53 | SUPPORT_SPOT_CALCULATOR_SCORE_CAN_PASS: 2, // score when pass is possible
54 | SUPPORT_SPOT_CALCULATOR_SCORE_CAN_SCORE: 1, // score when a goal is possible
55 | SUPPORT_SPOT_CALCULATOR_SCORE_DISTANCE: 2, // score for pass distance
56 | SUPPORT_SPOT_CALCULATOR_OPT_DISTANCE: 5, // optimal distance for a pass
57 | SUPPORT_SPOT_CALCULATOR_UPDATE_FREQUENCY: 1 // updates per second
58 | };
59 |
60 | export const TEAM = {
61 | RED: 0,
62 | BLUE: 1
63 | };
64 |
65 | export const ROLE = {
66 | GOALKEEPER: 0,
67 | ATTACKER: 1,
68 | DEFENDER: 2
69 | };
70 |
71 | CONFIG.GOALKEEPER_INTERCEPT_RANGE_SQ = CONFIG.GOALKEEPER_INTERCEPT_RANGE * CONFIG.GOALKEEPER_INTERCEPT_RANGE;
72 | CONFIG.GOALKEEPER_IN_TARGET_RANGE_SQ = CONFIG.GOALKEEPER_IN_TARGET_RANGE * CONFIG.GOALKEEPER_IN_TARGET_RANGE;
73 | CONFIG.PLAYER_COMFORT_ZONE_SQ = CONFIG.PLAYER_COMFORT_ZONE * CONFIG.PLAYER_COMFORT_ZONE;
74 | CONFIG.PLAYER_IN_TARGET_RANGE_SQ = CONFIG.PLAYER_IN_TARGET_RANGE * CONFIG.PLAYER_IN_TARGET_RANGE;
75 | CONFIG.PLAYER_KICKING_RANGE_SQ = CONFIG.PLAYER_KICKING_RANGE * CONFIG.PLAYER_KICKING_RANGE;
76 | CONFIG.PLAYER_RECEIVING_RANGE_SQ = CONFIG.PLAYER_RECEIVING_RANGE * CONFIG.PLAYER_RECEIVING_RANGE;
77 |
--------------------------------------------------------------------------------
/examples/js/showcases/kickoff/src/entities/FieldPlayer.js:
--------------------------------------------------------------------------------
1 | import { ArriveBehavior, PursuitBehavior, Regulator, SeekBehavior } from '../../../../../../lib/yuka.module.js'
2 | import { CONFIG, FIELDPLAYER_STATES } from '../core/Constants.js'
3 | import {
4 | ChaseBallState,
5 | DribbleState,
6 | GlobalState,
7 | KickBallState,
8 | ReceiveBallState,
9 | ReturnHomeState,
10 | SupportAttackerState,
11 | WaitState,
12 | } from '../states/FieldplayerStates.js'
13 | import Player from './Player.js'
14 |
15 | /**
16 | * Base class for representing a field player.
17 | *
18 | * @author {@link https://github.com/Mugen87|Mugen87}
19 | * @augments Player
20 | */
21 | class FieldPlayer extends Player {
22 | /**
23 | * Constructs a new field player.
24 | *
25 | * @param {Number} role - The role of the player.
26 | * @param {Team} team - A reference to its team.
27 | * @param {Pitch} pitch - A reference to the pitch.
28 | * @param {Number} defaultRegionId - The id of its default home region.
29 | */
30 | constructor(role, team, pitch, defaultRegionId) {
31 | super(role, team, pitch, defaultRegionId)
32 |
33 | /**
34 | * Regulates how often a field player is able to kick the ball in one second.
35 | * @type {Number}
36 | */
37 | this._kickRegulator = new Regulator(CONFIG.PLAYER_KICK_FREQUENCY)
38 |
39 | // steering behaviors
40 |
41 | const seekBehavior = new SeekBehavior()
42 | seekBehavior.active = false
43 | this.steering.add(seekBehavior)
44 |
45 | const arriveBehavior = new ArriveBehavior()
46 | arriveBehavior.active = false
47 | arriveBehavior.deceleration = 1.5
48 | this.steering.add(arriveBehavior)
49 |
50 | const pursuitBehavior = new PursuitBehavior()
51 | pursuitBehavior.active = false
52 | this.steering.add(pursuitBehavior)
53 |
54 | // states
55 |
56 | this.stateMachine.globalState = new GlobalState()
57 |
58 | this.stateMachine.add(FIELDPLAYER_STATES.CHASE_BALL, new ChaseBallState())
59 | this.stateMachine.add(FIELDPLAYER_STATES.DRIBBLE, new DribbleState())
60 | this.stateMachine.add(FIELDPLAYER_STATES.KICK_BALL, new KickBallState())
61 | this.stateMachine.add(FIELDPLAYER_STATES.RECEIVE_BALL, new ReceiveBallState())
62 | this.stateMachine.add(FIELDPLAYER_STATES.RETURN_HOME, new ReturnHomeState())
63 | this.stateMachine.add(FIELDPLAYER_STATES.SUPPORT_ATTACKER, new SupportAttackerState())
64 | this.stateMachine.add(FIELDPLAYER_STATES.WAIT, new WaitState())
65 |
66 | this.stateMachine.changeTo(FIELDPLAYER_STATES.WAIT)
67 | }
68 |
69 | /**
70 | * Updates the field player.
71 | *
72 | * @param {Number} delta - The time delta value.
73 | * @return {FieldPlayer} A reference to this field player.
74 | */
75 | update(delta) {
76 | super.update(delta)
77 |
78 | // In most states field players should always focus the ball. In other states (RETURN_HOME and SUPPORT_ATTACKER) the focus point
79 | // depends on the current situation. It might be the ball or the current steering target.
80 |
81 | if (
82 | this.stateMachine.in(FIELDPLAYER_STATES.RETURN_HOME) === false &&
83 | this.stateMachine.in(FIELDPLAYER_STATES.SUPPORT_ATTACKER) === false
84 | ) {
85 | this.rotateTo(this.team.ball.position, delta)
86 | }
87 | }
88 |
89 | /**
90 | * Returns true if the field player is able to kick the ball again.
91 | *
92 | * @return {Boolean} Whether the field player is able to kick the ball again or not.
93 | */
94 | isReadyForNextKick() {
95 | return this._kickRegulator.ready()
96 | }
97 | }
98 |
99 | export default FieldPlayer
100 |
--------------------------------------------------------------------------------
/examples/js/showcases/kickoff/src/entities/Goal.js:
--------------------------------------------------------------------------------
1 | import { GameEntity, Vector3 } from '../../../../../../lib/yuka.module.js'
2 | import { TEAM } from '../core/Constants.js'
3 |
4 | /**
5 | * Class for representing a soccer goal.
6 | *
7 | * @author {@link https://github.com/Mugen87|Mugen87}
8 | * @augments GameEntity
9 | */
10 | class Goal extends GameEntity {
11 | /**
12 | * Constructs a new goal.
13 | *
14 | * @param {Number} width - The width of the goal.
15 | * @param {Number} height - The height of the goal.
16 | * @param {Number} color - The color of the team that owns this goal.
17 | */
18 | constructor(width, height, color) {
19 | super()
20 |
21 | /**
22 | * The width of the goal.
23 | * @type {Number}
24 | */
25 | this.width = width
26 |
27 | /**
28 | * The height of the goal.
29 | * @type {Number}
30 | */
31 | this.height = height
32 |
33 | /**
34 | * The color of the team that owns this goal.
35 | * @type {Number}
36 | */
37 | this.color = color
38 |
39 | /**
40 | * The position of the left post. Computed by computePosts().
41 | * @type {Vector3}
42 | */
43 | this.leftPost = null
44 |
45 | /**
46 | * The position of the right post. Computed by computePosts().
47 | * @type {Vector3}
48 | */
49 | this.rightPost = null
50 | }
51 |
52 | /**
53 | * Returns the direction of the goal. This overwrites the implementation of
54 | * GameEntity since the direction only depends on the team color.
55 | *
56 | * @param {Vector3} direction - The direction of the goal.
57 | * @return {Vector3} The direction of the goal.
58 | */
59 | getDirection(direction) {
60 | if (this.color === TEAM.RED) {
61 | direction.set(-1, 0, 0)
62 | } else {
63 | direction.set(1, 0, 0)
64 | }
65 |
66 | return direction
67 | }
68 |
69 | /**
70 | * Computes the posts of the goal.
71 | */
72 | computePosts() {
73 | this.leftPost = new Vector3()
74 | this.rightPost = new Vector3()
75 |
76 | const halfSize = this.width / 2
77 |
78 | if (this.color === TEAM.RED) {
79 | this.leftPost.x = this.position.x
80 | this.leftPost.z = this.position.z + halfSize
81 |
82 | this.rightPost.x = this.position.x
83 | this.rightPost.z = this.position.z - halfSize
84 | } else {
85 | this.leftPost.x = this.position.x
86 | this.leftPost.z = this.position.z - halfSize
87 |
88 | this.rightPost.x = this.position.x
89 | this.rightPost.z = this.position.z + halfSize
90 | }
91 | }
92 | }
93 |
94 | export default Goal
95 |
--------------------------------------------------------------------------------
/examples/js/showcases/kickoff/src/entities/Goalkeeper.js:
--------------------------------------------------------------------------------
1 | import { ArriveBehavior, PursuitBehavior, Vector3 } from '../../../../../../lib/yuka.module.js'
2 | import { GOALKEEPER_STATES, CONFIG, ROLE } from '../core/Constants.js'
3 | import {
4 | GlobalState,
5 | InterceptBallState,
6 | PutBallBackInPlayState,
7 | ReturnHomeState,
8 | TendGoalState,
9 | } from '../states/GoalkeeperStates.js'
10 | import Player from './Player.js'
11 |
12 | const _target = new Vector3()
13 |
14 | /**
15 | * Base class for representing a goalkeeper.
16 | *
17 | * @author {@link https://github.com/Mugen87|Mugen87}
18 | * @augments Player
19 | */
20 | class Goalkeeper extends Player {
21 | /**
22 | * Constructs a new goalkeeper.
23 | *
24 | * @param {Team} team - A reference to its team.
25 | * @param {Pitch} pitch - A reference to the pitch.
26 | * @param {Number} defaultRegionId - The id of its default home region.
27 | */
28 | constructor(team, pitch, defaultRegionId) {
29 | super(ROLE.GOALKEEPER, team, pitch, defaultRegionId)
30 |
31 | this.maxSpeed = 1.5
32 |
33 | // steering behaviors
34 |
35 | const arriveBehavior = new ArriveBehavior()
36 | arriveBehavior.deceleration = 1
37 | arriveBehavior.active = false
38 | this.steering.add(arriveBehavior)
39 |
40 | const pursuitBehavior = new PursuitBehavior()
41 | pursuitBehavior.active = false
42 | this.steering.add(pursuitBehavior)
43 |
44 | // states
45 |
46 | this.stateMachine.globalState = new GlobalState()
47 |
48 | this.stateMachine.add(GOALKEEPER_STATES.RETURN_HOME, new ReturnHomeState())
49 | this.stateMachine.add(GOALKEEPER_STATES.TEND_GOAL, new TendGoalState())
50 | this.stateMachine.add(GOALKEEPER_STATES.INTERCEPT_BALL, new InterceptBallState())
51 | this.stateMachine.add(GOALKEEPER_STATES.PUT_BALL_BACK_IN_PLAY, new PutBallBackInPlayState())
52 |
53 | this.stateMachine.changeTo(GOALKEEPER_STATES.TEND_GOAL)
54 | }
55 |
56 | /**
57 | * Updates the goalkeeper.
58 | *
59 | * @param {Number} delta - The time delta value.
60 | * @return {Goalkeeper} A reference to this goalkeeper.
61 | */
62 | update(delta) {
63 | super.update(delta)
64 |
65 | this.rotateTo(this.team.ball.position, delta)
66 | }
67 |
68 | /**
69 | * Returns true if the ball is within the goalkeeper's target range. If so, the keeper is able
70 | * to trap the ball.
71 | *
72 | * @return {Boolean} Whether the ball is within the keeper's target range or not.
73 | */
74 | isBallWithinKeeperRange() {
75 | const ball = this.team.ball
76 |
77 | return this.position.squaredDistanceTo(ball.position) < CONFIG.GOALKEEPER_IN_TARGET_RANGE_SQ
78 | }
79 |
80 | /**
81 | * Returns true if the ball is within the goalkeeper's interception range. If so, the keeper will
82 | * start to pursuit the ball.
83 | *
84 | * @return {Boolean} Whether the ball is within the keeper's interception range or not.
85 | */
86 | isBallWithinRangeForIntercept() {
87 | const ball = this.team.ball
88 | const goal = this.team.homeGoal
89 |
90 | return goal.position.squaredDistanceTo(ball.position) <= CONFIG.GOALKEEPER_INTERCEPT_RANGE_SQ
91 | }
92 |
93 | /**
94 | * Returns true if the goalkeeper is too far away from the goalmouth.
95 | *
96 | * @return {Boolean} Whether the goalkeeper is too far away from the goalmouth or not.
97 | */
98 | isTooFarFromGoalMouth() {
99 | this.getRearInterposeTarget(_target)
100 |
101 | return this.position.squaredDistanceTo(_target) > CONFIG.GOALKEEPER_INTERCEPT_RANGE_SQ
102 | }
103 |
104 | /**
105 | * This method is called by the TendGoalState to determine the spot
106 | * along the goalmouth which will act as one of the interpose targets
107 | * (the other is the ball). The specific point at the goal line that
108 | * the keeper is trying to cover is flexible and can move depending on
109 | * where the ball is on the field. To achieve this we just scale the
110 | * ball's z value by the ratio of the goal width to playing field height.
111 | *
112 | * @param {Vector3} force - The interpose target.
113 | * @returns {Vector3} The interpose target.
114 | */
115 | getRearInterposeTarget(target) {
116 | const pitch = this.pitch
117 | const ball = this.team.ball
118 | const goal = this.team.homeGoal
119 |
120 | target.x = goal.position.x
121 | target.y = 0
122 | target.z = ball.position.z * (goal.width / pitch.playingArea.height)
123 |
124 | return target
125 | }
126 | }
127 |
128 | export default Goalkeeper
129 |
--------------------------------------------------------------------------------
/examples/js/showcases/kickoff/src/entities/Pitch.js:
--------------------------------------------------------------------------------
1 | import { GameEntity, Plane, Vector3 } from '../../../../../../lib/yuka.module.js'
2 | import { MESSAGE } from '../core/Constants.js'
3 |
4 | import Region from '../etc/Region.js'
5 |
6 | /**
7 | * Class for representing a soccer pitch.
8 | *
9 | * @author {@link https://github.com/Mugen87|Mugen87}
10 | * @augments GameEntity
11 | */
12 | class Pitch extends GameEntity {
13 | /**
14 | * Constructs a new pitch.
15 | *
16 | * @param {Number} width - The width of the pitch.
17 | * @param {Number} height - The height of the pitch.
18 | * @param {World} world - A reference to the World class.
19 | */
20 | constructor(width, height, world) {
21 | super()
22 |
23 | /**
24 | * A reference to the World class.
25 | * @type {World}
26 | */
27 | this.world = world
28 |
29 | /**
30 | * Represents the walls of the soccer pitch. The ball will
31 | * collide against these walls so it can leave the playing area.
32 | * @type {Array}
33 | */
34 | this.walls = [
35 | new Plane(new Vector3(0, 0, -1), 7.5), // top
36 | new Plane(new Vector3(0, 0, 1), 7.5), // bottom
37 | new Plane(new Vector3(-1, 0, 0), 10), // right (red goal)
38 | new Plane(new Vector3(1, 0, 0), 10), // left (blue goal)
39 | ]
40 |
41 | /**
42 | * Whether both teams are playing or not.
43 | * @type {Boolean}
44 | */
45 | this.isPlaying = true
46 |
47 | /**
48 | * Whether one of the goalkeepers is in ball possession or not.
49 | * @type {Boolean}
50 | */
51 | this.isGoalKeeperInBallPossession = false
52 |
53 | /**
54 | * A reference to the soccer ball.
55 | * @type {Ball}
56 | */
57 | this.ball = null
58 |
59 | /**
60 | * A reference to the red team.
61 | * @type {Team}
62 | */
63 | this.teamRed = null
64 |
65 | /**
66 | * A reference to the blue team.
67 | * @type {Team}
68 | */
69 | this.teamBlue = null
70 |
71 | /**
72 | * Represents the playing area of the pitch.
73 | * @type {Region}
74 | */
75 | this.playingArea = new Region(this.position.clone(), width, height)
76 |
77 | /**
78 | * The region count the pitch along the x axis.
79 | * @type {Number}
80 | */
81 | this.regionCountWidth = 6
82 |
83 | /**
84 | * The region count the pitch along the z axis.
85 | * @type {Number}
86 | */
87 | this.regionCountHeight = 3
88 |
89 | /**
90 | * Holds the regions of the soccer pitch.
91 | * @type {Array}
92 | */
93 | this.regions = []
94 |
95 | this._createRegions()
96 | }
97 |
98 | /**
99 | * Holds the implementation for the message handling of this pitch.
100 | *
101 | * @param {Telegram} telegram - The telegram with the message data.
102 | * @return {Boolean} Whether the message was processed or not.
103 | */
104 | handleMessage(telegram) {
105 | switch (telegram.message) {
106 | case MESSAGE.GOAL_SCORED:
107 | this.isPlaying = false
108 |
109 | this.world.refreshUI()
110 |
111 | return true
112 | }
113 |
114 | return false
115 | }
116 |
117 | /**
118 | * Returns the region for the given ID.
119 | *
120 | * @param {Number} id - The id for the requested region.
121 | * @return {Region} The requested region.
122 | */
123 | getRegionById(id) {
124 | return this.regions[id]
125 | }
126 |
127 | /**
128 | * Generates the regions of this pitch. All regions lie in a XZ at the origin.
129 | */
130 | _createRegions() {
131 | const playingArea = this.playingArea
132 |
133 | let id = 0
134 |
135 | const width = playingArea.width / this.regionCountWidth
136 | const height = playingArea.height / this.regionCountHeight
137 |
138 | for (let col = 0; col < this.regionCountWidth; col++) {
139 | for (let row = 0; row < this.regionCountHeight; row++) {
140 | const x = col * width + width / 2 - playingArea.width / 2
141 | const y = 0
142 | const z = row * height + height / 2 - playingArea.height / 2
143 |
144 | this.regions[id] = new Region(new Vector3(x, y, z), width, height, id)
145 |
146 | id++
147 | }
148 | }
149 | }
150 | }
151 |
152 | export default Pitch
153 |
--------------------------------------------------------------------------------
/examples/js/showcases/kickoff/src/etc/Region.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Defines a rectangular region. This class is used to split up the
3 | * pitch into multiple regions which can be used by the AI to implement
4 | * different strategies.
5 | *
6 | * @author {@link https://github.com/Mugen87|Mugen87}
7 | */
8 | class Region {
9 |
10 | /**
11 | * Constructs a new mesh geometry.
12 | *
13 | * @param {Vector3} center - The center point of the region.
14 | * @param {Number} width - The width of the region.
15 | * @param {Number} height - The height of the region.
16 | * @param {Number} id - The unique identifier of the region.
17 | */
18 | constructor( center, width, height, id = 0 ) {
19 |
20 | /**
21 | * The center point of the region.
22 | * @type {Vector3}
23 | */
24 | this.center = center;
25 |
26 | /**
27 | * The width of the region.
28 | * @type {Number}
29 | */
30 | this.width = width;
31 |
32 | /**
33 | * The height of the region.
34 | * @type {Number}
35 | */
36 | this.height = height;
37 |
38 | /**
39 | * The unique identifier of the region.
40 | * @type {Number}
41 | */
42 | this.id = id;
43 |
44 | /**
45 | * The outer left position of the region.
46 | * @type {Number}
47 | */
48 | this.left = center.x - ( width / 2 );
49 |
50 | /**
51 | * The outer right position of the region.
52 | * @type {Number}
53 | */
54 | this.right = center.x + ( width / 2 );
55 |
56 | /**
57 | * The outer top position of the region.
58 | * @type {Number}
59 | */
60 | this.top = center.z + ( height / 2 );
61 |
62 | /**
63 | * The outer bottom position of the region.
64 | * @type {Number}
65 | */
66 | this.bottom = center.z - ( height / 2 );
67 |
68 | }
69 |
70 | /**
71 | * Returns true if the given position is inside this region.
72 | *
73 | * @param {Vector3} position - The position to test.
74 | * @param {Boolean} isHalfSize - Whether the region has half size or not which makes the test more strict (optional).
75 | * @return {Boolean} Whether the given position is inside the region or not.
76 | */
77 | isInside( position, isHalfSize = false ) {
78 |
79 | let marginX, marginY;
80 |
81 | if ( isHalfSize === true ) {
82 |
83 | marginX = this.width * 0.25;
84 | marginY = this.height * 0.25;
85 |
86 | return ( ( position.x > ( this.left + marginX ) ) &&
87 | ( position.x < ( this.right - marginX ) ) &&
88 | ( position.z > ( this.bottom + marginY ) ) &&
89 | ( position.z < ( this.top - marginY ) ) );
90 |
91 | } else {
92 |
93 | return ( ( position.x > this.left ) &&
94 | ( position.x < this.right ) &&
95 | ( position.z > this.bottom ) &&
96 | ( position.z < this.top ) );
97 |
98 | }
99 |
100 | }
101 |
102 | }
103 |
104 | export default Region;
105 |
--------------------------------------------------------------------------------
/examples/js/showcases/kickoff/src/states/TeamStates.js:
--------------------------------------------------------------------------------
1 | import { State } from '../../../../../../lib/yuka.module.js'
2 | import { MESSAGE, TEAM_STATES } from '../core/Constants.js'
3 |
4 | /**
5 | * The global state of the team.
6 | *
7 | * @author {@link https://github.com/Mugen87|Mugen87}
8 | */
9 | class GlobalState extends State {
10 | onMessage(team, telegram) {
11 | // This state is only used for processing messages.
12 |
13 | switch (telegram.message) {
14 | case MESSAGE.GOAL_SCORED:
15 | if (telegram.data.team === team.color) team.goals++
16 |
17 | team.stateMachine.changeTo(TEAM_STATES.PREPARE_FOR_KICKOFF)
18 |
19 | return true
20 | }
21 |
22 | return false
23 | }
24 | }
25 |
26 | /**
27 | * In this state the team tries to make a goal.
28 | *
29 | * @author {@link https://github.com/Mugen87|Mugen87}
30 | */
31 | class AttackingState extends State {
32 | enter(team) {
33 | // Set up the player's new home regions.
34 |
35 | team.setupTeamPositions()
36 |
37 | // If a player is in either the WAIT or RETURN_HOME states, its
38 | // steering target must be updated to that of its new home region to
39 | // enable it to move into the correct position.
40 |
41 | team.updateSteeringTargetOfPlayers()
42 | }
43 |
44 | execute(team) {
45 | // If this team is no longer in control, change to defending.
46 |
47 | if (team.inControl() === false) {
48 | team.stateMachine.changeTo(TEAM_STATES.DEFENDING)
49 | }
50 |
51 | // Compute the best position for any supporting attacker to move to.
52 |
53 | team.computeBestSupportingPosition()
54 | }
55 |
56 | exit(team) {
57 | team.lostControl()
58 | }
59 | }
60 |
61 | /**
62 | * In this state the team tries to defend its goal.
63 | *
64 | * @author {@link https://github.com/Mugen87|Mugen87}
65 | */
66 | class DefendingState extends State {
67 | enter(team) {
68 | team.setupTeamPositions()
69 | team.updateSteeringTargetOfPlayers()
70 | }
71 |
72 | execute(team) {
73 | // If this team gets control over the ball, change to attacking.
74 |
75 | if (team.inControl()) {
76 | team.stateMachine.changeTo(TEAM_STATES.ATTACKING)
77 | }
78 | }
79 | }
80 |
81 | /**
82 | * In this state the team prepares for kickoff.
83 | *
84 | * @author {@link https://github.com/Mugen87|Mugen87}
85 | */
86 | class PrepareForKickOffState extends State {
87 | enter(team) {
88 | team.receivingPlayer = null
89 | team.playerClosestToBall = null
90 | team.controllingPlayer = null
91 | team.supportingPlayer = null
92 |
93 | // send all players to their default regions
94 |
95 | team.returnAllFieldPlayersToHome(true)
96 | }
97 |
98 | execute(team) {
99 | if (team.areAllPlayersAtHome() && team.opposingTeam.areAllPlayersAtHome()) {
100 | team.stateMachine.changeTo(TEAM_STATES.DEFENDING)
101 | }
102 | }
103 |
104 | exit(team) {
105 | team.pitch.isPlaying = true
106 | }
107 | }
108 |
109 | export { AttackingState, DefendingState, GlobalState, PrepareForKickOffState }
110 |
--------------------------------------------------------------------------------
/examples/js/showcases/kickoff/textures/pitch_texture.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldinor/yuka-babylonjs-examples/f8d9f36fe181dc5f3ef4c2746dbe39ea8d884e37/examples/js/showcases/kickoff/textures/pitch_texture.jpg
--------------------------------------------------------------------------------
/examples/js/spacecarrier/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Yuka | Space Carrier - State Machine
4 |
5 |
6 |
7 |
8 |
42 |
43 |
44 |
45 |
46 | Another example of Autonomous State-driven Agent Design.
47 | This small spaceship collects red artefacts inside the sphere. After collecting 2 artefacts the spaceship goes
48 | to the base to unload cargo.
49 |
50 |
51 |
52 |
53 | Current State:
54 |
55 | Current Time:
56 |
57 | Current Speed:
58 |
59 | Collected:
60 |
61 | Stored:
62 |
63 |
64 |
65 |
68 |
69 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/examples/js/spacecarrier/src/carrier.js:
--------------------------------------------------------------------------------
1 | import * as YUKA from '../../../../lib/yuka.module.js'
2 | import { IdleState, RotateState, FlyState, GatherState, ToTheBaseState, UnloadState } from './states.js'
3 | class Carrier extends YUKA.Vehicle {
4 | constructor(scene, time, target) {
5 | super()
6 |
7 | this.scene = scene
8 | this.time = time
9 | this.target = target
10 |
11 | this.idleDuration = 2
12 | this.walkDuration = 5
13 | this.gatherDuration = 5
14 | this.unloadDuration = 6
15 | this.currentTime = 0
16 | this.maxTurnRate = 0.8
17 | this.currentSpeed = 0
18 | this.goods = 0
19 |
20 | this.ui = {
21 | currentState: document.getElementById('currentState'),
22 | currentTime: document.getElementById('currentTime'),
23 | currentSpeed: document.getElementById('currentSpeed'),
24 | currentGoods: document.getElementById('currentGoods'),
25 | storedGoods: document.getElementById('storedGoods'),
26 | }
27 |
28 | this.stateMachine = new YUKA.StateMachine(this)
29 | this.stateMachine.add('IDLE', new IdleState(scene))
30 | this.stateMachine.add('ROTATE', new RotateState(time))
31 | this.stateMachine.add('FLY', new FlyState(scene))
32 | this.stateMachine.add('GATHER', new GatherState(scene))
33 | this.stateMachine.add('TO THE BASE', new ToTheBaseState(scene))
34 | this.stateMachine.add('UNLOAD', new UnloadState(scene))
35 | }
36 |
37 | generateTarget() {
38 | // generate a random point on a sphere
39 |
40 | const radius = 2
41 | const phi = Math.acos(2 * Math.random() - 1)
42 | const theta = Math.random() * Math.PI * 2
43 |
44 | this.target.position.fromSpherical(radius, phi, theta)
45 | }
46 | update(delta) {
47 | this.currentDelta = delta
48 |
49 | this.stateMachine.update()
50 | this.currentTime += delta
51 |
52 | super.update(delta)
53 |
54 | return this
55 | }
56 | }
57 |
58 | export { Carrier }
59 |
--------------------------------------------------------------------------------
/examples/js/spacecarrier/src/textures_flare.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldinor/yuka-babylonjs-examples/f8d9f36fe181dc5f3ef4c2746dbe39ea8d884e37/examples/js/spacecarrier/src/textures_flare.png
--------------------------------------------------------------------------------
/examples/js/steering/arrive/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Yuka | Steering Behaviors | Arrive | Babylon.js
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | This steering behavior produces a force that directs an agent toward a target position.
13 | Unlike "Seek", it decelerates so the agent comes to a gentle halt at the target position.
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/examples/js/steering/arrive/index.js:
--------------------------------------------------------------------------------
1 | import * as YUKA from '../../../../lib/yuka.module.js'
2 | import 'https://preview.babylonjs.com/babylon.js'
3 | import { createVehicle } from '../../creator.js'
4 |
5 | let engine, scene
6 | let entityManager, time, vehicle, target
7 |
8 | const entityMatrix = new BABYLON.Matrix()
9 |
10 | init()
11 | animate()
12 |
13 | function init() {
14 | const canvas = document.getElementById('renderCanvas')
15 | engine = new BABYLON.Engine(canvas, true, {}, true)
16 |
17 | scene = new BABYLON.Scene(engine)
18 | scene.clearColor = new BABYLON.Color4(0, 0, 0, 1)
19 | scene.useRightHandedSystem = true
20 |
21 | const camera = new BABYLON.ArcRotateCamera(
22 | 'camera',
23 | BABYLON.Tools.ToRadians(30),
24 | BABYLON.Tools.ToRadians(40),
25 | 8,
26 | BABYLON.Vector3.Zero(),
27 | scene
28 | )
29 |
30 | camera.target = new BABYLON.Vector3(0, 0, 0)
31 | camera.attachControl()
32 |
33 | new BABYLON.HemisphericLight('light', new BABYLON.Vector3(1, 1, 0))
34 |
35 | //
36 |
37 | const vehicleMesh = createVehicle(scene, { size: 0.5 })
38 |
39 | const sphere = BABYLON.MeshBuilder.CreateSphere('sphere', {
40 | diameter: 5,
41 | segments: 16,
42 | })
43 | sphere.material = new BABYLON.StandardMaterial('sphereMaterial', scene)
44 | sphere.material.disableLighting = true
45 | sphere.material.emissiveColor = new BABYLON.Color3(0.8, 0.8, 0.8)
46 | sphere.material.alpha = 0.2
47 | sphere.material.wireframe = true
48 |
49 | //
50 |
51 | const targetMesh = BABYLON.MeshBuilder.CreateSphere('target', {
52 | diameter: 0.1,
53 | segments: 16,
54 | })
55 | targetMesh.material = new BABYLON.StandardMaterial('targetMaterial', scene)
56 | targetMesh.material.disableLighting = true
57 | targetMesh.material.emissiveColor = new BABYLON.Color3(1, 0, 0)
58 |
59 | //
60 |
61 | window.addEventListener('resize', onWindowResize, false)
62 |
63 | // game setup
64 |
65 | entityManager = new YUKA.EntityManager()
66 | time = new YUKA.Time()
67 |
68 | target = new YUKA.GameEntity()
69 | target.setRenderComponent(targetMesh, sync)
70 |
71 | vehicle = new YUKA.Vehicle()
72 | vehicle.setRenderComponent(vehicleMesh, sync)
73 |
74 | const arriveBehavior = new YUKA.ArriveBehavior(target.position, 2.5, 0.1)
75 | vehicle.steering.add(arriveBehavior)
76 |
77 | entityManager.add(target)
78 | entityManager.add(vehicle)
79 |
80 | generateTarget()
81 | }
82 |
83 | function onWindowResize() {
84 | engine.resize()
85 | }
86 |
87 | function animate() {
88 | requestAnimationFrame(animate)
89 |
90 | const delta = time.update().getDelta()
91 |
92 | entityManager.update(delta)
93 |
94 | scene.render()
95 | }
96 |
97 | function sync(entity, renderComponent) {
98 | entity.worldMatrix.toArray(entityMatrix.m)
99 | entityMatrix.markAsUpdated()
100 |
101 | const matrix = renderComponent.getWorldMatrix()
102 | matrix.copyFrom(entityMatrix)
103 | }
104 |
105 | function generateTarget() {
106 | // generate a random point on a sphere
107 |
108 | const radius = 2
109 | const phi = Math.acos(2 * Math.random() - 1)
110 | const theta = Math.random() * Math.PI * 2
111 |
112 | target.position.fromSpherical(radius, phi, theta)
113 |
114 | setTimeout(generateTarget, 10000)
115 | }
116 |
--------------------------------------------------------------------------------
/examples/js/steering/flee/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Yuka | Steering Behaviors | Flocking
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | This steering behavior produces a force that steers an agent away from a target position.
13 | The target position is defined by the mouse cursor.
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/examples/js/steering/flee/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Mugen87 / https://github.com/Mugen87
3 | * @author Examples with Babylon.js were made at https://github.com/eldinor/yuka-babylonjs-examples / roland@babylonjs.xyz
4 | */
5 |
6 | import * as YUKA from '../../../../lib/yuka.module.js'
7 |
8 | import 'https://preview.babylonjs.com/babylon.js'
9 | import 'https://preview.babylonjs.com/materialsLibrary/babylonjs.materials.min.js'
10 | import { createVehicle } from '../../creator.js'
11 |
12 | let engine, scene, plane, ray
13 | let entityManager, time, vehicle, target
14 |
15 | const entityMatrix = new BABYLON.Matrix()
16 | const pointer = new BABYLON.Vector2(1, 1)
17 |
18 | init()
19 | animate()
20 |
21 | function init() {
22 | const canvas = document.getElementById('renderCanvas')
23 | engine = new BABYLON.Engine(canvas, true, {}, true)
24 |
25 | scene = new BABYLON.Scene(engine)
26 | scene.clearColor = new BABYLON.Color4(0, 0, 0, 1)
27 | scene.useRightHandedSystem = true
28 |
29 | const camera = new BABYLON.ArcRotateCamera(
30 | 'camera',
31 | BABYLON.Tools.ToRadians(90),
32 | BABYLON.Tools.ToRadians(0),
33 | 30,
34 | BABYLON.Vector3.Zero(),
35 | scene
36 | )
37 | camera.upperBetaLimit = Math.PI / 4
38 | camera.lowerBetaLimit = Math.PI / 4
39 |
40 | camera.target = new BABYLON.Vector3(0, 0, 0)
41 | camera.attachControl(canvas, true)
42 |
43 | new BABYLON.HemisphericLight('light', new BABYLON.Vector3(1, 1, 0))
44 |
45 | const ground = BABYLON.MeshBuilder.CreateGround('ground', { width: 40, height: 40 }, scene)
46 | ground.position.y = -1
47 | ground.material = new BABYLON.GridMaterial('grid', scene)
48 |
49 | const vehicleMesh = createVehicle(scene, { size: 2 })
50 |
51 | const pointerMesh = BABYLON.MeshBuilder.CreateSphere('pointer', scene)
52 |
53 | scene.onPointerMove = () => {
54 | var pickResult = scene.pick(scene.pointerX, scene.pointerY)
55 | if (pickResult?.pickedPoint) {
56 | target.x = pickResult.pickedPoint.x
57 | // target.y = pickResult.pickedPoint.y;
58 | target.z = pickResult.pickedPoint.z
59 | pointerMesh.position.x = target.x
60 | pointerMesh.position.z = target.z
61 | }
62 | }
63 |
64 | window.addEventListener('resize', onWindowResize, false)
65 |
66 | // YUKA specific
67 | target = new YUKA.Vector3()
68 |
69 | entityManager = new YUKA.EntityManager()
70 | time = new YUKA.Time()
71 | vehicle = new YUKA.Vehicle()
72 | vehicle.setRenderComponent(vehicleMesh, sync)
73 |
74 | const fleeBehavior = new YUKA.FleeBehavior(target, 5)
75 | vehicle.steering.add(fleeBehavior)
76 |
77 | entityManager.add(vehicle)
78 | }
79 |
80 | function onWindowResize() {
81 | engine.resize()
82 | }
83 |
84 | function animate() {
85 | requestAnimationFrame(animate)
86 |
87 | const delta = time.update().getDelta()
88 | entityManager.update(delta)
89 |
90 | scene.render()
91 | }
92 |
93 | function sync(entity, renderComponent) {
94 | BABYLON.Matrix.FromValues(...entity.worldMatrix.elements).decomposeToTransformNode(renderComponent)
95 | }
96 |
--------------------------------------------------------------------------------
/examples/js/steering/flocking/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Yuka | Steering Behaviors | Flocking
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | A group steering behavior defined by a combination of "Alignment", "Cohesion" and "Separation".
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/examples/js/steering/flocking/index.js:
--------------------------------------------------------------------------------
1 | import * as YUKA from '../../../../lib/yuka.module.js'
2 | import * as DAT from 'https://cdn.jsdelivr.net/npm/dat.gui@0.7.7/build/dat.gui.module.js'
3 |
4 | import 'https://preview.babylonjs.com/babylon.js'
5 | import 'https://preview.babylonjs.com/materialsLibrary/babylonjs.materials.min.js'
6 | import { createVehicle } from '../../creator.js'
7 | // import 'https://preview.babylonjs.com/inspector/babylon.inspector.bundle.js'
8 | // import 'https://preview.babylonjs.com/loaders/babylonjs.loaders.min.js'
9 |
10 | let engine, scene
11 |
12 | let entityManager, time
13 |
14 | const entityMatrix = new BABYLON.Matrix()
15 |
16 | const params = {
17 | alignment: 1,
18 | cohesion: 0.9,
19 | separation: 0.3,
20 | }
21 |
22 | init()
23 | animate()
24 |
25 | function init() {
26 | const canvas = document.getElementById('renderCanvas')
27 | engine = new BABYLON.Engine(canvas, true, {}, true)
28 |
29 | scene = new BABYLON.Scene(engine)
30 | scene.clearColor = new BABYLON.Color4(0, 0, 0, 1)
31 | scene.useRightHandedSystem = true
32 | // scene.debugLayer.show();
33 |
34 | const camera = new BABYLON.ArcRotateCamera(
35 | 'camera',
36 | BABYLON.Tools.ToRadians(90),
37 | BABYLON.Tools.ToRadians(0),
38 | 120,
39 | BABYLON.Vector3.Zero(),
40 | scene
41 | )
42 |
43 | camera.target = new BABYLON.Vector3(0, 0, 0)
44 | camera.attachControl(canvas, true)
45 |
46 | new BABYLON.HemisphericLight('light', new BABYLON.Vector3(1, 1, 0))
47 |
48 | const ground = BABYLON.MeshBuilder.CreateGround('ground', { width: 80, height: 80 }, scene)
49 | ground.position.y = -1
50 | ground.material = new BABYLON.GridMaterial('grid', scene)
51 | //
52 |
53 | // game setup
54 |
55 | entityManager = new YUKA.EntityManager()
56 | time = new YUKA.Time()
57 |
58 | const alignmentBehavior = new YUKA.AlignmentBehavior()
59 | const cohesionBehavior = new YUKA.CohesionBehavior()
60 | const separationBehavior = new YUKA.SeparationBehavior()
61 |
62 | alignmentBehavior.weight = params.alignment
63 | cohesionBehavior.weight = params.cohesion
64 | separationBehavior.weight = params.separation
65 |
66 | const vehicleMeshPrefab = createVehicle(scene, { size: 2 })
67 | vehicleMeshPrefab.setEnabled(false)
68 |
69 | for (let i = 0; i < 50; i++) {
70 | const vehicleMesh = vehicleMeshPrefab.clone('vehicle')
71 | vehicleMesh.setEnabled(true)
72 |
73 | const vehicle = new YUKA.Vehicle()
74 | vehicle.maxSpeed = 1.5
75 | vehicle.updateNeighborhood = true
76 | vehicle.neighborhoodRadius = 10
77 | vehicle.rotation.fromEuler(0, Math.PI * Math.random(), 0)
78 | vehicle.position.x = 10 - Math.random() * 20
79 | vehicle.position.z = 10 - Math.random() * 20
80 |
81 | vehicle.setRenderComponent(vehicleMesh, sync)
82 |
83 | vehicle.steering.add(alignmentBehavior)
84 | vehicle.steering.add(cohesionBehavior)
85 | vehicle.steering.add(separationBehavior)
86 |
87 | const wanderBehavior = new YUKA.WanderBehavior()
88 | wanderBehavior.weight = 0.5
89 | vehicle.steering.add(wanderBehavior)
90 |
91 | entityManager.add(vehicle)
92 | }
93 |
94 | // dat.gui
95 |
96 | const gui = new DAT.GUI({ width: 300 })
97 |
98 | gui
99 | .add(params, 'alignment', 0.1, 2)
100 | .name('alignment')
101 | .onChange((value) => (alignmentBehavior.weight = value))
102 | gui
103 | .add(params, 'cohesion', 0.1, 2)
104 | .name('cohesion')
105 | .onChange((value) => (cohesionBehavior.weight = value))
106 | gui
107 | .add(params, 'separation', 0.1, 2)
108 | .name('separation')
109 | .onChange((value) => (separationBehavior.weight = value))
110 |
111 | gui.open()
112 |
113 | //
114 |
115 | window.addEventListener('resize', onWindowResize, false)
116 | }
117 |
118 | function onWindowResize() {
119 | engine.resize()
120 | }
121 |
122 | function animate() {
123 | requestAnimationFrame(animate)
124 |
125 | const delta = time.update().getDelta()
126 |
127 | entityManager.update(delta)
128 |
129 | scene.render()
130 | }
131 |
132 | function sync(entity, renderComponent) {
133 | entity.worldMatrix.toArray(entityMatrix.m)
134 | entityMatrix.markAsUpdated()
135 |
136 | const matrix = renderComponent.getWorldMatrix()
137 | matrix.copyFrom(entityMatrix)
138 | }
139 |
--------------------------------------------------------------------------------
/examples/js/steering/followPath/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Yuka | Steering Behaviors | Follow Path
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | This steering behavior produces a force that moves a vehicle along a series of waypoints forming a path.
13 | An additional steering behavior (OnPath) can be used to realize a more strict path following.
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/examples/js/steering/followPath/index.js:
--------------------------------------------------------------------------------
1 | import * as YUKA from '../../../../lib/yuka.module.js'
2 | import * as DAT from 'https://cdn.jsdelivr.net/npm/dat.gui@0.7.7/build/dat.gui.module.js'
3 |
4 | import 'https://preview.babylonjs.com/babylon.js'
5 | import 'https://preview.babylonjs.com/materialsLibrary/babylonjs.materials.min.js'
6 | import { createVehicle } from '../../creator.js'
7 | // import 'https://preview.babylonjs.com/inspector/babylon.inspector.bundle.js'
8 | // import 'https://preview.babylonjs.com/loaders/babylonjs.loaders.min.js'
9 |
10 | let engine, scene
11 |
12 | let entityManager, time, vehicle
13 |
14 | const entityMatrix = new BABYLON.Matrix()
15 |
16 | let lines
17 |
18 | let onPathBehavior
19 |
20 | const params = {
21 | onPathActive: true,
22 | radius: 0.1,
23 | }
24 |
25 | init()
26 | animate()
27 |
28 | function init() {
29 | const canvas = document.getElementById('renderCanvas')
30 | engine = new BABYLON.Engine(canvas, true, {}, true)
31 |
32 | scene = new BABYLON.Scene(engine)
33 | scene.clearColor = new BABYLON.Color4(0, 0, 0, 1)
34 | scene.useRightHandedSystem = true
35 |
36 | // scene.debugLayer.show();
37 |
38 | const camera = new BABYLON.ArcRotateCamera(
39 | 'camera',
40 | BABYLON.Tools.ToRadians(90),
41 | BABYLON.Tools.ToRadians(0),
42 | 30,
43 | BABYLON.Vector3.Zero(),
44 | scene
45 | )
46 | camera.target = new BABYLON.Vector3(0, 0, 0)
47 | camera.attachControl(canvas, true)
48 |
49 | let light = new BABYLON.HemisphericLight('light', new BABYLON.Vector3(1, -1, 0))
50 | light.intensity = 2
51 | light.diffuse = BABYLON.Color3.Red()
52 | //
53 |
54 | const vehicleMesh = createVehicle(scene)
55 | // dat.gui
56 |
57 | const gui = new DAT.GUI({ width: 300 })
58 |
59 | gui
60 | .add(params, 'onPathActive')
61 | .name('activate onPath')
62 | .onChange((value) => (onPathBehavior.active = value))
63 | gui
64 | .add(params, 'radius', 0.01, 1)
65 | .name('radius')
66 | .onChange((value) => (onPathBehavior.radius = value))
67 |
68 | gui.open()
69 |
70 | //
71 | window.addEventListener('resize', onWindowResize, false)
72 |
73 | // game setup
74 |
75 | entityManager = new YUKA.EntityManager()
76 | time = new YUKA.Time()
77 | vehicle = new YUKA.Vehicle()
78 |
79 | vehicle.setRenderComponent(vehicleMesh, sync)
80 |
81 | const path = new YUKA.Path()
82 | path.loop = true
83 | path.add(new YUKA.Vector3(-4, 0, 4))
84 | path.add(new YUKA.Vector3(-6, 0, 0))
85 | path.add(new YUKA.Vector3(-4, 0, -4))
86 | path.add(new YUKA.Vector3(0, 0, 0))
87 | path.add(new YUKA.Vector3(4, 0, -4))
88 | path.add(new YUKA.Vector3(6, 0, 0))
89 | path.add(new YUKA.Vector3(4, 0, 4))
90 | path.add(new YUKA.Vector3(0, 0, 6))
91 |
92 | vehicle.position.copy(path.current())
93 |
94 | // use "FollowPathBehavior" for basic path following
95 |
96 | const followPathBehavior = new YUKA.FollowPathBehavior(path, 0.5)
97 | vehicle.steering.add(followPathBehavior)
98 |
99 | // use "OnPathBehavior" to realize a more strict path following.
100 | // it's a separate steering behavior to provide more flexibility.
101 |
102 | onPathBehavior = new YUKA.OnPathBehavior(path)
103 | vehicle.steering.add(onPathBehavior)
104 |
105 | entityManager.add(vehicle)
106 |
107 | //
108 |
109 | const position = []
110 |
111 | for (let i = 0; i < path._waypoints.length; i++) {
112 | const waypoint = path._waypoints[i]
113 |
114 | position.push(waypoint.x, waypoint.y, waypoint.z)
115 | }
116 |
117 | path._waypoints.push(path._waypoints[0]) // to close the line
118 | lines = BABYLON.MeshBuilder.CreateLines('lines', {
119 | points: path._waypoints,
120 | updatable: true,
121 | })
122 |
123 | lines.color = BABYLON.Color3.Teal()
124 | }
125 |
126 | function onWindowResize() {
127 | engine.resize()
128 | }
129 |
130 | function animate() {
131 | requestAnimationFrame(animate)
132 |
133 | const delta = time.update().getDelta()
134 |
135 | entityManager.update(delta)
136 |
137 | scene.render()
138 | }
139 |
140 | function sync(entity, renderComponent) {
141 | entity.worldMatrix.toArray(entityMatrix.m)
142 | entityMatrix.markAsUpdated()
143 |
144 | const matrix = renderComponent.getWorldMatrix()
145 | matrix.copyFrom(entityMatrix)
146 | }
147 |
--------------------------------------------------------------------------------
/examples/js/steering/interpose/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Yuka | Steering Behaviors | Interpose
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | "Interpose" produces a force that moves a vehicle to the midpoint of the imaginary line connecting two other
13 | agents.
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/examples/js/steering/interpose/index.js:
--------------------------------------------------------------------------------
1 | import * as YUKA from '../../../../lib/yuka.module.js'
2 | // import * as DAT from 'https://cdn.jsdelivr.net/npm/dat.gui@0.7.7/build/dat.gui.module.js';
3 |
4 | import 'https://preview.babylonjs.com/babylon.js'
5 | import 'https://preview.babylonjs.com/materialsLibrary/babylonjs.materials.min.js'
6 | import { createVehicle } from '../../creator.js'
7 | // import 'https://preview.babylonjs.com/inspector/babylon.inspector.bundle.js'
8 | // import 'https://preview.babylonjs.com/loaders/babylonjs.loaders.min.js'
9 |
10 | let engine, scene
11 | let lines
12 | let linePoints = []
13 |
14 | let entityManager, time, pursuer, entity1, entity2, target1, target2
15 |
16 | const entityMatrix = new BABYLON.Matrix()
17 |
18 | init()
19 | animate()
20 |
21 | function init() {
22 | const canvas = document.getElementById('renderCanvas')
23 | engine = new BABYLON.Engine(canvas, true, {}, true)
24 |
25 | scene = new BABYLON.Scene(engine)
26 | scene.clearColor = new BABYLON.Color4(0, 0, 0, 1)
27 | scene.useRightHandedSystem = true
28 | // scene.debugLayer.show();
29 |
30 | const camera = new BABYLON.ArcRotateCamera(
31 | 'camera',
32 | BABYLON.Tools.ToRadians(120),
33 | BABYLON.Tools.ToRadians(40),
34 | 20,
35 | BABYLON.Vector3.Zero(),
36 | scene
37 | )
38 |
39 | camera.target = new BABYLON.Vector3(0, 0, 0)
40 | camera.attachControl(canvas, true)
41 |
42 | const light = new BABYLON.HemisphericLight('light', new BABYLON.Vector3(1, 1, 0))
43 | light.diffuse = BABYLON.Color3.Magenta()
44 |
45 | const ground = BABYLON.MeshBuilder.CreateGround('ground', { width: 15, height: 15 }, scene)
46 | ground.position.y = -1
47 | ground.material = new BABYLON.GridMaterial('grid', scene)
48 | ground.visibility = 0.4
49 | ground.material.backFaceCulling = false
50 |
51 | // meshes
52 |
53 | const pursuerMesh = createVehicle(scene, { size: 0.8 })
54 |
55 | const entityMesh1 = BABYLON.MeshBuilder.CreateBox('entityMesh1', { size: 0.2 }, scene)
56 | const entityMesh2 = BABYLON.MeshBuilder.CreateBox('entityMesh2', { size: 0.2 }, scene)
57 |
58 | const entityMat = new BABYLON.StandardMaterial('entityMat', scene)
59 | entityMat.disableLighting = true
60 | entityMat.emissiveColor = BABYLON.Color3.Red()
61 |
62 | entityMesh1.material = entityMat
63 | entityMesh2.material = entityMat
64 |
65 | // helper
66 |
67 | linePoints[0] = entityMesh1.position
68 | linePoints[1] = entityMesh2.position
69 |
70 | lines = BABYLON.MeshBuilder.CreateLines('lines', {
71 | points: linePoints,
72 | updatable: true,
73 | })
74 | lines.color = BABYLON.Color3.Red()
75 |
76 | //
77 |
78 | window.addEventListener('resize', onWindowResize, false)
79 |
80 | // game setup
81 |
82 | entityManager = new YUKA.EntityManager()
83 | time = new YUKA.Time()
84 |
85 | target1 = new YUKA.Vector3()
86 | target2 = new YUKA.Vector3()
87 |
88 | entity1 = new YUKA.Vehicle()
89 | entity1.maxSpeed = 2
90 | entity1.setRenderComponent(entityMesh1, sync)
91 |
92 | const seekBehavior1 = new YUKA.SeekBehavior(target1)
93 | entity1.steering.add(seekBehavior1)
94 |
95 | entity2 = new YUKA.Vehicle()
96 | entity2.maxSpeed = 2
97 | entity2.setRenderComponent(entityMesh2, sync)
98 |
99 | const seekBehavior2 = new YUKA.SeekBehavior(target2)
100 | entity2.steering.add(seekBehavior2)
101 |
102 | pursuer = new YUKA.Vehicle()
103 | pursuer.maxSpeed = 3
104 | pursuer.setRenderComponent(pursuerMesh, sync)
105 |
106 | const interposeBehavior = new YUKA.InterposeBehavior(entity1, entity2, 1)
107 | pursuer.steering.add(interposeBehavior)
108 |
109 | entityManager.add(entity1)
110 | entityManager.add(entity2)
111 | entityManager.add(pursuer)
112 | }
113 |
114 | function onWindowResize() {
115 | engine.resize()
116 | }
117 |
118 | function animate() {
119 | requestAnimationFrame(animate)
120 |
121 | const delta = time.update().getDelta()
122 | const elapsedTime = time.getElapsed()
123 |
124 | target1.x = Math.cos(elapsedTime * 0.1) * Math.sin(elapsedTime * 0.1) * 6
125 | target1.y = Math.cos(elapsedTime * 0.1) * Math.sin(elapsedTime * 0.1) * 6
126 | target1.z = Math.sin(elapsedTime * 0.3) * 6
127 |
128 | target2.x = 1 + Math.cos(elapsedTime * 0.5) * Math.sin(elapsedTime * 0.3) * 4
129 | target2.y = 1 + Math.cos(elapsedTime * 0.5) * Math.sin(elapsedTime * 0.3) * 4
130 | target2.z = 1 + Math.sin(elapsedTime * 0.3) * 6
131 |
132 | entityManager.update(delta)
133 |
134 | linePoints[0] = entity1.position
135 | linePoints[1] = entity2.position
136 |
137 | lines = BABYLON.MeshBuilder.CreateLines('lines', {
138 | points: linePoints,
139 | instance: lines,
140 | })
141 |
142 | scene.render()
143 | }
144 |
145 | function sync(entity, renderComponent) {
146 | entity.worldMatrix.toArray(entityMatrix.m)
147 | entityMatrix.markAsUpdated()
148 |
149 | const matrix = renderComponent.getWorldMatrix()
150 | matrix.copyFrom(entityMatrix)
151 | }
152 |
--------------------------------------------------------------------------------
/examples/js/steering/obstacleAvoidance/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Yuka | Steering Behaviors | Obstacle Avoidance
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | This steering behavior produces a force so a vehicle avoids obstacles lying in its path.
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/examples/js/steering/obstacleAvoidance/index.js:
--------------------------------------------------------------------------------
1 | import * as YUKA from '../../../../lib/yuka.module.js'
2 | // import * as DAT from 'https://cdn.jsdelivr.net/npm/dat.gui@0.7.7/build/dat.gui.module.js';
3 |
4 | import 'https://preview.babylonjs.com/babylon.js'
5 | import 'https://preview.babylonjs.com/materialsLibrary/babylonjs.materials.min.js'
6 | import { createVehicle } from '../../creator.js'
7 | // import 'https://preview.babylonjs.com/inspector/babylon.inspector.bundle.js'
8 | // import 'https://preview.babylonjs.com/loaders/babylonjs.loaders.min.js'
9 |
10 | let engine, scene
11 | let entityManager, time, vehicle
12 |
13 | const obstacles = new Array()
14 | const entityMatrix = new BABYLON.Matrix()
15 |
16 | init()
17 | animate()
18 |
19 | function init() {
20 | const canvas = document.getElementById('renderCanvas')
21 | engine = new BABYLON.Engine(canvas, true, {}, true)
22 |
23 | scene = new BABYLON.Scene(engine)
24 | scene.clearColor = new BABYLON.Color4(0, 0, 0, 1)
25 | scene.useRightHandedSystem = true
26 | // scene.debugLayer.show()
27 |
28 | const camera = new BABYLON.ArcRotateCamera(
29 | 'camera',
30 | BABYLON.Tools.ToRadians(140),
31 | BABYLON.Tools.ToRadians(40),
32 | 50,
33 | BABYLON.Vector3.Zero(),
34 | scene
35 | )
36 |
37 | camera.target = new BABYLON.Vector3(0, 0, 0)
38 | camera.attachControl(canvas, true)
39 | camera.upperBetaLimit = 1.1
40 |
41 | new BABYLON.HemisphericLight('light', new BABYLON.Vector3(1, 1, 0))
42 |
43 | const ground = BABYLON.MeshBuilder.CreatePlane('plane', { width: 25, height: 25 }, scene)
44 | ground.rotation.x = Math.PI / 2
45 |
46 | ground.material = new BABYLON.GridMaterial('grid', scene)
47 | ground.material.backFaceCulling = true
48 | ground.visibility = 0.4
49 |
50 | const wayPointsMat = new BABYLON.StandardMaterial('wayPointsMat', scene)
51 | wayPointsMat.disableLighting = true
52 | wayPointsMat.emissiveColor = BABYLON.Color3.Magenta()
53 |
54 | const vehicleMesh = createVehicle(scene, { size: 2 })
55 | window.addEventListener('resize', onWindowResize, false)
56 |
57 | // game setup
58 |
59 | entityManager = new YUKA.EntityManager()
60 | time = new YUKA.Time()
61 |
62 | const path = new YUKA.Path()
63 | path.loop = true
64 | path.add(new YUKA.Vector3(10, 0, 10))
65 | path.add(new YUKA.Vector3(10, 10, -10))
66 | path.add(new YUKA.Vector3(-10, 0, -10))
67 | path.add(new YUKA.Vector3(-10, 0, 10))
68 |
69 | vehicle = new YUKA.Vehicle()
70 | vehicle.maxSpeed = 3
71 | vehicle.setRenderComponent(vehicleMesh, sync)
72 |
73 | vehicle.boundingRadius = vehicleMesh.getBoundingInfo().boundingSphere.radius
74 | vehicle.smoother = new YUKA.Smoother(20)
75 |
76 | entityManager.add(vehicle)
77 |
78 | const obstacleAvoidanceBehavior = new YUKA.ObstacleAvoidanceBehavior(obstacles)
79 | vehicle.steering.add(obstacleAvoidanceBehavior)
80 |
81 | const followPathBehavior = new YUKA.FollowPathBehavior(path)
82 | vehicle.steering.add(followPathBehavior)
83 |
84 | // obstacles
85 |
86 | setupObstacles()
87 | setupWaypoints(path)
88 | }
89 |
90 | function onWindowResize() {
91 | engine.resize()
92 | }
93 |
94 | function animate() {
95 | requestAnimationFrame(animate)
96 |
97 | const delta = time.update().getDelta()
98 |
99 | entityManager.update(delta)
100 |
101 | scene.render()
102 | }
103 |
104 | function sync(entity, renderComponent) {
105 | entity.worldMatrix.toArray(entityMatrix.m)
106 | entityMatrix.markAsUpdated()
107 |
108 | const matrix = renderComponent.getWorldMatrix()
109 | matrix.copyFrom(entityMatrix)
110 | }
111 |
112 | function setupObstacles() {
113 | const mesh1 = BABYLON.MeshBuilder.CreateBox('mesh1', { size: 2 }, scene)
114 | const mesh2 = BABYLON.MeshBuilder.CreateBox('mesh1', { width: 2, height: 6, depth: 2 }, scene)
115 | const mesh3 = BABYLON.MeshBuilder.CreateBox('mesh1', { size: 2 }, scene)
116 |
117 | const meshMat = new BABYLON.StandardMaterial('meshMat', scene)
118 | meshMat.disableLighting = true
119 | meshMat.emissiveColor = BABYLON.Color3.Red()
120 |
121 | mesh1.material = meshMat
122 | mesh2.material = meshMat
123 | mesh3.material = meshMat
124 |
125 | mesh1.position.set(-10, 0, 0)
126 | mesh2.position.set(11, 6, 0)
127 | mesh3.position.set(4, 8, -10)
128 |
129 | const obstacle1 = new YUKA.GameEntity()
130 | obstacle1.position.copy(mesh1.position)
131 | obstacle1.boundingRadius = mesh1.getBoundingInfo().boundingSphere.radius * 1.4
132 | entityManager.add(obstacle1)
133 | obstacles.push(obstacle1)
134 |
135 | const obstacle2 = new YUKA.GameEntity()
136 | obstacle2.position.copy(mesh2.position)
137 | obstacle2.boundingRadius = mesh2.getBoundingInfo().boundingSphere.radius
138 | entityManager.add(obstacle2)
139 | obstacles.push(obstacle2)
140 |
141 | const obstacle3 = new YUKA.GameEntity()
142 | obstacle3.position.copy(mesh3.position)
143 | obstacle3.boundingRadius = mesh3.getBoundingInfo().boundingSphere.radius
144 | entityManager.add(obstacle3)
145 | obstacles.push(obstacle3)
146 | }
147 |
148 | function setupWaypoints(path) {
149 | path._waypoints.forEach((p) => {
150 | const sphere = BABYLON.MeshBuilder.CreateSphere('sphere', { diameter: 0.4 }, scene)
151 | sphere.material = scene.getMaterialByName('wayPointsMat')
152 | sphere.position.x = p.x
153 | sphere.position.y = p.y
154 | sphere.position.z = p.z
155 | })
156 | }
157 |
--------------------------------------------------------------------------------
/examples/js/steering/offsetPursuit/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Yuka | Steering Behaviors | Offset Pursuit
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | This steering behavior produces a force that keeps a vehicle at a specified offset from a leader vehicle.
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/examples/js/steering/offsetPursuit/index.js:
--------------------------------------------------------------------------------
1 | import * as YUKA from '../../../../lib/yuka.module.js'
2 | // import * as DAT from 'https://cdn.jsdelivr.net/npm/dat.gui@0.7.7/build/dat.gui.module.js';
3 |
4 | import 'https://preview.babylonjs.com/babylon.js'
5 | import 'https://preview.babylonjs.com/materialsLibrary/babylonjs.materials.min.js'
6 | import { createVehicle } from '../../creator.js'
7 | // import 'https://preview.babylonjs.com/inspector/babylon.inspector.bundle.js'
8 | // import 'https://preview.babylonjs.com/loaders/babylonjs.loaders.min.js'
9 |
10 | let engine, scene
11 | let entityManager, time, target
12 |
13 | const entityMatrix = new BABYLON.Matrix()
14 |
15 | init()
16 | animate()
17 |
18 | function init() {
19 | const canvas = document.getElementById('renderCanvas')
20 | engine = new BABYLON.Engine(canvas, true, {}, true)
21 |
22 | scene = new BABYLON.Scene(engine)
23 | scene.clearColor = new BABYLON.Color4(0, 0, 0, 1)
24 | // scene.debugLayer.show();
25 |
26 | const camera = new BABYLON.ArcRotateCamera(
27 | 'camera',
28 | BABYLON.Tools.ToRadians(70),
29 | BABYLON.Tools.ToRadians(60),
30 | 15,
31 | BABYLON.Vector3.Zero(),
32 | scene
33 | )
34 |
35 | camera.target = new BABYLON.Vector3(0, 0, 0)
36 | camera.attachControl(canvas, true)
37 |
38 | const light = new BABYLON.HemisphericLight('light', new BABYLON.Vector3(1, 1, 0))
39 | light.diffuse = BABYLON.Color3.Green()
40 | //
41 |
42 | const leaderMesh = createVehicle(scene, { size: 0.8 })
43 |
44 | const followerMeshTemplate = createVehicle(scene, { size: 0.5 })
45 | followerMeshTemplate.isVisible = false
46 |
47 | const ground = BABYLON.MeshBuilder.CreateGround('ground', { width: 10, height: 10 }, scene)
48 | ground.position.y = -1
49 | ground.material = new BABYLON.GridMaterial('grid', scene)
50 | ground.material.backFaceCulling = false
51 | ground.visibility = 0.4
52 |
53 | //
54 |
55 | window.addEventListener('resize', onWindowResize, false)
56 |
57 | // game setup
58 |
59 | entityManager = new YUKA.EntityManager()
60 | time = new YUKA.Time()
61 |
62 | target = new YUKA.Vector3()
63 |
64 | // leader
65 |
66 | const leader = new YUKA.Vehicle()
67 | leader.setRenderComponent(leaderMesh, sync)
68 |
69 | const seekBehavior = new YUKA.SeekBehavior(target)
70 | leader.steering.add(seekBehavior)
71 |
72 | entityManager.add(leader)
73 |
74 | // follower
75 |
76 | const offsets = [
77 | new YUKA.Vector3(0.5, 0, -0.5),
78 | new YUKA.Vector3(-0.5, 0, -0.5),
79 | new YUKA.Vector3(1.5, 0, -1.5),
80 | new YUKA.Vector3(-1.5, 0, -1.5),
81 | ]
82 |
83 | for (let i = 0; i < 4; i++) {
84 | const followerMesh = followerMeshTemplate.clone()
85 | followerMesh.isVisible = true
86 |
87 | const follower = new YUKA.Vehicle()
88 | follower.maxSpeed = 2
89 | follower.position.copy(offsets[i]) // initial position
90 | follower.scale.set(0.5, 0.5, 0.5) // make the followers a bit smaller
91 | follower.setRenderComponent(followerMesh, sync)
92 |
93 | const offsetPursuitBehavior = new YUKA.OffsetPursuitBehavior(leader, offsets[i])
94 | follower.steering.add(offsetPursuitBehavior)
95 |
96 | entityManager.add(follower)
97 | }
98 | }
99 |
100 | function onWindowResize() {
101 | engine.resize()
102 | }
103 |
104 | function animate() {
105 | requestAnimationFrame(animate)
106 |
107 | time.update()
108 |
109 | const deltaTime = time.getDelta()
110 | const elapsedTime = time.getElapsed()
111 |
112 | target.z = Math.cos(elapsedTime * 0.2) * 5
113 | target.y = Math.cos(elapsedTime * 0.4) * 3
114 | target.x = Math.sin(elapsedTime * 0.2) * 5
115 |
116 | entityManager.update(deltaTime)
117 |
118 | scene.render()
119 | }
120 |
121 | function sync(entity, renderComponent) {
122 | entity.worldMatrix.toArray(entityMatrix.m)
123 | entityMatrix.markAsUpdated()
124 |
125 | const matrix = renderComponent.getWorldMatrix()
126 | matrix.copyFrom(entityMatrix)
127 | }
128 |
--------------------------------------------------------------------------------
/examples/js/steering/pursuit/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Yuka | Steering Behaviors | Pursuit
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | "Pursuit" is useful when an agent is required to intercept a moving agent.
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/examples/js/steering/pursuit/index.js:
--------------------------------------------------------------------------------
1 | import * as YUKA from '../../../../lib/yuka.module.js'
2 | import * as DAT from 'https://cdn.jsdelivr.net/npm/dat.gui@0.7.7/build/dat.gui.module.js'
3 |
4 | import 'https://preview.babylonjs.com/babylon.js'
5 | import 'https://preview.babylonjs.com/materialsLibrary/babylonjs.materials.min.js'
6 | import { createVehicle } from '../../creator.js'
7 | // import 'https://preview.babylonjs.com/inspector/babylon.inspector.bundle.js'
8 | // import 'https://preview.babylonjs.com/loaders/babylonjs.loaders.min.js'
9 |
10 | let engine, scene
11 | let entityManager, time, pursuer, evader, target
12 |
13 | const entityMatrix = new BABYLON.Matrix()
14 |
15 | init()
16 | animate()
17 |
18 | function init() {
19 | const canvas = document.getElementById('renderCanvas')
20 | engine = new BABYLON.Engine(canvas, true, {}, true)
21 |
22 | scene = new BABYLON.Scene(engine)
23 | scene.clearColor = new BABYLON.Color4(0, 0, 0, 1)
24 | scene.useRightHandedSystem = true
25 | // scene.debugLayer.show();
26 |
27 | const camera = new BABYLON.ArcRotateCamera(
28 | 'camera',
29 | BABYLON.Tools.ToRadians(30),
30 | BABYLON.Tools.ToRadians(40),
31 | 15,
32 | BABYLON.Vector3.Zero(),
33 | scene
34 | )
35 |
36 | camera.target = new BABYLON.Vector3(0, 0, 0)
37 | camera.attachControl(canvas, true)
38 |
39 | new BABYLON.HemisphericLight('light', new BABYLON.Vector3(1, 1, 0))
40 |
41 | const ground = BABYLON.MeshBuilder.CreateGround('ground', { width: 10, height: 10 }, scene)
42 | ground.position.y = -1
43 | ground.material = new BABYLON.GridMaterial('grid', scene)
44 | ground.visibility = 0.5
45 | ground.material.backFaceCulling = false
46 |
47 | //
48 |
49 | const pursuerMesh = createVehicle(scene)
50 |
51 | const evaderMesh = BABYLON.MeshBuilder.CreateBox('box', { size: 0.2 }, scene)
52 |
53 | const evaderMaterial = new BABYLON.StandardMaterial('evaderMaterial', scene)
54 | evaderMaterial.disableLighting = true
55 | evaderMaterial.emissiveColor = new BABYLON.Color3(1, 0, 0)
56 |
57 | evaderMesh.material = evaderMaterial
58 |
59 | /*
60 | const grid = new THREE.GridHelper( 10, 25 );
61 | scene.add( grid );
62 | */
63 | //
64 |
65 | //
66 |
67 | window.addEventListener('resize', onWindowResize, false)
68 |
69 | // game setup
70 |
71 | entityManager = new YUKA.EntityManager()
72 | time = new YUKA.Time()
73 |
74 | target = new YUKA.Vector3()
75 |
76 | evader = new YUKA.Vehicle()
77 | evader.maxSpeed = 3
78 | evader.setRenderComponent(evaderMesh, sync)
79 |
80 | pursuer = new YUKA.Vehicle()
81 | pursuer.maxSpeed = 3
82 | pursuer.position.z = -5
83 | pursuer.setRenderComponent(pursuerMesh, sync)
84 |
85 | const pursuitBehavior = new YUKA.PursuitBehavior(evader, 2)
86 | pursuer.steering.add(pursuitBehavior)
87 |
88 | const seekBehavior = new YUKA.SeekBehavior(target)
89 | evader.steering.add(seekBehavior)
90 |
91 | entityManager.add(evader)
92 | entityManager.add(pursuer)
93 |
94 | // dat.gui
95 |
96 | const gui = new DAT.GUI({ width: 300 })
97 |
98 | gui.add(pursuitBehavior, 'predictionFactor', 0, 5).name('prediction factor')
99 |
100 | gui.open()
101 | }
102 |
103 | function onWindowResize() {
104 | engine.resize()
105 | }
106 |
107 | function animate() {
108 | requestAnimationFrame(animate)
109 |
110 | const deltaTime = time.update().getDelta()
111 | const elapsedTime = time.getElapsed()
112 |
113 | target.x = Math.cos(elapsedTime) * Math.sin(elapsedTime * 0.2) * 6
114 | target.y = Math.cos(elapsedTime) * Math.sin(elapsedTime * 0.2) * 12 + 2
115 | target.z = Math.sin(elapsedTime * 0.8) * 6
116 |
117 | // console.log(target.x)
118 |
119 | entityManager.update(deltaTime)
120 |
121 | scene.render()
122 | }
123 |
124 | function sync(entity, renderComponent) {
125 | entity.worldMatrix.toArray(entityMatrix.m)
126 | entityMatrix.markAsUpdated()
127 |
128 | const matrix = renderComponent.getWorldMatrix()
129 | matrix.copyFrom(entityMatrix)
130 | }
131 |
--------------------------------------------------------------------------------
/examples/js/steering/seek/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Yuka | Steering Behaviors | Seek
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | This steering behavior produces a force that directs an agent toward a target position.
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/examples/js/steering/seek/index.js:
--------------------------------------------------------------------------------
1 | import * as YUKA from '../../../../lib/yuka.module.js'
2 | import 'https://preview.babylonjs.com/babylon.js'
3 | import { createVehicle } from '../../creator.js'
4 |
5 | let engine, scene
6 | let entityManager, time, vehicle, target
7 |
8 | const entityMatrix = new BABYLON.Matrix()
9 |
10 | init()
11 | animate()
12 |
13 | function init() {
14 | const canvas = document.getElementById('renderCanvas')
15 | engine = new BABYLON.Engine(canvas, true, {}, true)
16 |
17 | scene = new BABYLON.Scene(engine)
18 | scene.clearColor = new BABYLON.Color4(0, 0, 0, 1)
19 | scene.useRightHandedSystem = true
20 |
21 | const camera = new BABYLON.ArcRotateCamera(
22 | 'camera',
23 | BABYLON.Tools.ToRadians(30),
24 | BABYLON.Tools.ToRadians(40),
25 | 8,
26 | BABYLON.Vector3.Zero(),
27 | scene
28 | )
29 |
30 | camera.target = new BABYLON.Vector3(0, 0, 0)
31 | camera.attachControl()
32 |
33 | const light = new BABYLON.HemisphericLight('light', new BABYLON.Vector3(1, 1, 0))
34 | light.diffuse = BABYLON.Color3.Green()
35 | //
36 |
37 | const vehicleMesh = createVehicle(scene, { size: 0.5 })
38 |
39 | const sphere = BABYLON.MeshBuilder.CreateSphere('sphere', { diameter: 5, segments: 16 })
40 | sphere.material = new BABYLON.StandardMaterial('sphereMaterial', scene)
41 | sphere.material.disableLighting = true
42 | sphere.material.emissiveColor = new BABYLON.Color3(0.8, 0.8, 0.8)
43 | sphere.material.alpha = 0.2
44 | sphere.material.wireframe = true
45 |
46 | const targetMesh = BABYLON.MeshBuilder.CreateSphere('target', { diameter: 0.1, segments: 16 })
47 | targetMesh.material = new BABYLON.StandardMaterial('targetMaterial', scene)
48 | targetMesh.material.disableLighting = true
49 | targetMesh.material.emissiveColor = new BABYLON.Color3(1, 0, 0)
50 |
51 | //
52 |
53 | window.addEventListener('resize', onWindowResize, false)
54 |
55 | // game setup
56 |
57 | entityManager = new YUKA.EntityManager()
58 | time = new YUKA.Time()
59 |
60 | target = new YUKA.GameEntity()
61 | target.setRenderComponent(targetMesh, sync)
62 |
63 | vehicle = new YUKA.Vehicle()
64 | vehicle.setRenderComponent(vehicleMesh, sync)
65 |
66 | const seekBehavior = new YUKA.SeekBehavior(target.position)
67 | vehicle.steering.add(seekBehavior)
68 |
69 | entityManager.add(target)
70 | entityManager.add(vehicle)
71 |
72 | generateTarget()
73 | }
74 |
75 | function onWindowResize() {
76 | engine.resize()
77 | }
78 |
79 | function animate() {
80 | requestAnimationFrame(animate)
81 |
82 | const delta = time.update().getDelta()
83 | entityManager.update(delta)
84 |
85 | scene.render()
86 | }
87 |
88 | function sync(entity, renderComponent) {
89 | entity.worldMatrix.toArray(entityMatrix.m)
90 | entityMatrix.markAsUpdated()
91 |
92 | const matrix = renderComponent.getWorldMatrix()
93 | matrix.copyFrom(entityMatrix)
94 | }
95 |
96 | function generateTarget() {
97 | // generate a random point on a sphere
98 |
99 | const radius = 2
100 | const phi = Math.acos(2 * Math.random() - 1)
101 | const theta = Math.random() * Math.PI * 2
102 |
103 | target.position.fromSpherical(radius, phi, theta)
104 |
105 | setTimeout(generateTarget, 3000)
106 | }
107 |
--------------------------------------------------------------------------------
/examples/js/steering/wander/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Yuka | Steering Behaviors | Wander
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | "Wander" produces a steering force that will give the impression of a random walk through the agent’s
13 | environment.
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/examples/js/steering/wander/index.js:
--------------------------------------------------------------------------------
1 | import * as YUKA from '../../../../lib/yuka.module.js'
2 | // import * as DAT from 'https://cdn.jsdelivr.net/npm/dat.gui@0.7.7/build/dat.gui.module.js';
3 |
4 | import 'https://preview.babylonjs.com/babylon.js'
5 | import 'https://preview.babylonjs.com/materialsLibrary/babylonjs.materials.min.js'
6 | import { createVehicle } from '../../creator.js'
7 | // import 'https://preview.babylonjs.com/inspector/babylon.inspector.bundle.js'
8 | // import 'https://preview.babylonjs.com/loaders/babylonjs.loaders.min.js'
9 |
10 | let engine, scene
11 | let entityManager, time
12 |
13 | const entityMatrix = new BABYLON.Matrix()
14 |
15 | init()
16 | animate()
17 |
18 | function init() {
19 | const canvas = document.getElementById('renderCanvas')
20 | engine = new BABYLON.Engine(canvas, true, {}, true)
21 |
22 | scene = new BABYLON.Scene(engine)
23 | scene.clearColor = new BABYLON.Color4(0, 0, 0, 1)
24 | scene.useRightHandedSystem = true
25 | // scene.debugLayer.show();
26 |
27 | const camera = new BABYLON.ArcRotateCamera(
28 | 'camera',
29 | BABYLON.Tools.ToRadians(120),
30 | BABYLON.Tools.ToRadians(40),
31 | 25,
32 | BABYLON.Vector3.Zero(),
33 | scene
34 | )
35 |
36 | camera.target = new BABYLON.Vector3(0, 0, 0)
37 | camera.attachControl(canvas, true)
38 |
39 | const light = new BABYLON.HemisphericLight('light', new BABYLON.Vector3(1, 1, 0))
40 | light.diffuse = BABYLON.Color3.Teal()
41 |
42 | const ground = BABYLON.MeshBuilder.CreateGround('ground', { width: 30, height: 20 }, scene)
43 | ground.position.y = -1
44 | ground.material = new BABYLON.GridMaterial('grid', scene)
45 | ground.material.backFaceCulling = false
46 | ground.visibility = 0.4
47 | // game setup
48 |
49 | entityManager = new YUKA.EntityManager()
50 | time = new YUKA.Time()
51 |
52 | //
53 |
54 | const vehicleMeshPrefab = createVehicle(scene, { size: 0.8 })
55 | vehicleMeshPrefab.setEnabled(false)
56 |
57 | for (let i = 0; i < 50; i++) {
58 | const vehicleMesh = vehicleMeshPrefab.clone('vehicle')
59 | vehicleMesh.setEnabled(true)
60 |
61 | vehicleMesh.bakeCurrentTransformIntoVertices()
62 |
63 | const vehicle = new YUKA.Vehicle()
64 | vehicle.rotation.fromEuler(0, 2 * Math.PI * Math.random(), 0)
65 | vehicle.position.x = 2.5 - Math.random() * 5
66 | vehicle.position.z = 2.5 - Math.random() * 5
67 | vehicle.setRenderComponent(vehicleMesh, sync)
68 |
69 | const wanderBehavior = new YUKA.WanderBehavior()
70 | vehicle.steering.add(wanderBehavior)
71 |
72 | entityManager.add(vehicle)
73 | }
74 |
75 | //
76 |
77 | //
78 |
79 | window.addEventListener('resize', onWindowResize, false)
80 | }
81 |
82 | function onWindowResize() {
83 | engine.resize()
84 | }
85 |
86 | function animate() {
87 | requestAnimationFrame(animate)
88 |
89 | const delta = time.update().getDelta()
90 |
91 | entityManager.update(delta)
92 |
93 | scene.render()
94 | }
95 |
96 | function sync(entity, renderComponent) {
97 | entity.worldMatrix.toArray(entityMatrix.m)
98 | entityMatrix.markAsUpdated()
99 |
100 | const matrix = renderComponent.getWorldMatrix()
101 | matrix.copyFrom(entityMatrix)
102 | }
103 |
--------------------------------------------------------------------------------
/examples/js/templates/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Yuka | TODO-Title
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | TODO-Description
12 |
13 | canvas>
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/examples/js/templates/index.js:
--------------------------------------------------------------------------------
1 | import * as YUKA from '../../../../lib/yuka.module.js'
2 | // import * as DAT from 'https://cdn.jsdelivr.net/npm/dat.gui@0.7.7/build/dat.gui.module.js';
3 |
4 | import 'https://preview.babylonjs.com/babylon.js'
5 | import 'https://preview.babylonjs.com/materialsLibrary/babylonjs.materials.min.js'
6 | // import 'https://preview.babylonjs.com/inspector/babylon.inspector.bundle.js'
7 | // import 'https://preview.babylonjs.com/loaders/babylonjs.loaders.min.js'
8 |
9 | let engine, scene
10 | let entityManager, time, vehicle, target
11 |
12 | const entityMatrix = new BABYLON.Matrix()
13 |
14 | init()
15 | animate()
16 |
17 | function init() {
18 | const canvas = document.getElementById('renderCanvas')
19 | engine = new BABYLON.Engine(canvas, true, {}, true)
20 |
21 | scene = new BABYLON.Scene(engine)
22 | scene.clearColor = new BABYLON.Color4(0, 0, 0, 1)
23 | scene.useRightHandedSystem = true
24 |
25 | scene.debugLayer.show()
26 |
27 | const camera = new BABYLON.ArcRotateCamera(
28 | 'camera',
29 | BABYLON.Tools.ToRadians(90),
30 | BABYLON.Tools.ToRadians(0),
31 | 30,
32 | BABYLON.Vector3.Zero(),
33 | scene
34 | )
35 |
36 | camera.target = new BABYLON.Vector3(0, 0, 0)
37 | camera.attachControl(canvas, true)
38 |
39 | new BABYLON.HemisphericLight('light', new BABYLON.Vector3(1, 1, 0))
40 |
41 | const ground = BABYLON.MeshBuilder.CreateGround('ground', { width: 40, height: 20 }, scene)
42 | ground.position.y = -1
43 | ground.material = new BABYLON.GridMaterial('grid', scene)
44 |
45 | const vehicleMesh = BABYLON.MeshBuilder.CreateCylinder(
46 | 'cone',
47 | { height: 2, diameterTop: 0, diameterBottom: 1 },
48 | scene
49 | )
50 | vehicleMesh.rotation.x = Math.PI * 0.5
51 | vehicleMesh.bakeCurrentTransformIntoVertices()
52 |
53 | scene.onPointerMove = () => {
54 | var pickResult = scene.pick(scene.pointerX, scene.pointerY)
55 | if (pickResult?.pickedPoint) {
56 | target.x = pickResult.pickedPoint.x
57 | target.y = pickResult.pickedPoint.y
58 | target.z = pickResult.pickedPoint.z
59 | }
60 | }
61 |
62 | window.addEventListener('resize', onWindowResize, false)
63 |
64 | // YUKA specific
65 | target = new YUKA.Vector3()
66 |
67 | entityManager = new YUKA.EntityManager()
68 | time = new YUKA.Time()
69 | vehicle = new YUKA.Vehicle()
70 | vehicle.setRenderComponent(vehicleMesh, sync)
71 |
72 | //
73 | entityManager.add(vehicle)
74 | }
75 |
76 | function onWindowResize() {
77 | engine.resize()
78 | }
79 |
80 | function animate() {
81 | requestAnimationFrame(animate)
82 |
83 | const delta = time.update().getDelta()
84 | entityManager.update(delta)
85 |
86 | scene.render()
87 | }
88 |
89 | function sync(entity, renderComponent) {
90 | entity.worldMatrix.toArray(entityMatrix.m)
91 | entityMatrix.markAsUpdated()
92 |
93 | const matrix = renderComponent.getWorldMatrix()
94 | matrix.copyFrom(entityMatrix)
95 | }
96 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "yuka-babylon-examples",
3 | "version": "1.0.0",
4 | "description": "YUKA BabylonJS examples",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "git+https://github.com/eldinor/yuka-babylon-examples.git"
12 | },
13 | "author": "",
14 | "license": "ISC",
15 | "bugs": {
16 | "url": "https://github.com/eldinor/yuka-babylon-examples/issues"
17 | },
18 | "homepage": "https://github.com/eldinor/yuka-babylon-examples#readme",
19 | "devDependencies": {
20 | "prettier": "2.5.1"
21 | },
22 | "dependencies": {
23 | "@types/yuka": "^0.7.1",
24 | "babylonjs": "^5.0.0-beta.5",
25 | "babylonjs-inspector": "^5.0.0-beta.5",
26 | "babylonjs-loaders": "^5.0.0-beta.5",
27 | "babylonjs-materials": "^5.0.0-beta.5",
28 | "yuka": "^0.7.7"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------