Форум программистов, компьютерный форум, киберфорум
JavaScript: HTML5 Canvas
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  
 
 
Рейтинг 4.68/22: Рейтинг темы: голосов - 22, средняя оценка - 4.68
9987 / 2959 / 497
Регистрация: 05.10.2013
Сообщений: 8,110
Записей в блоге: 243

Управление героем стрелками в 3D, вид от 3-го лица, на WebGL

13.01.2022, 23:12. Показов 6350. Ответов 31
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Цитата Сообщение от Olga28 Посмотреть сообщение
Я не знаю работали вы с технологией FLASH AS 3.0. и библиотекой Away3D. У меня есть один пример из одной старой книги. Книга была на английском языке, там рассказывается как сделать управление героем стрелками, вид от 3-его лица. Вот исходники.
CameraDemo.zip
А вот сам скриншот.


Технология flash, примерно 5 лет как устарела. Как сделать такой же пример вид от 3-его лица на WebGL? В Флеше я знаю как сделать а вот WebGL только изучаю.

Чтобы запустить сам пример в только что созданном проекте Flash, просто следует добавить класс в свойства как показано на картинке



и запускать нужно в браузере или другом проигрывателе (не Flash Player, там пример плохо работает).
С Flash я сейчас сталкиваюсь немного, потому что изучаю физический движок Box2D по урокам на Flash, переписывая примеры уроков на WebGL. Переписал первую часть осталась вторая с игрой:

Todd's Box2D Tutorials

https://kerp.net/box2d/

Lesson 1.1 - Introduction to Chapter 1 - http://youtu.be/2NvNoBUNIX0
Lesson 1.2 - Disclaimers, thanks, and comments - http://youtu.be/jhKWFgvR8No
Lesson 1.3 - What you're gonna need... - http://youtu.be/rB_WrFt4-5s
Lesson 1.4 - Understanding Box2D - http://youtu.be/95lW-zWkZPE
Lesson 1.5 - The kinda long and boring lesson on setting everything up - http://youtu.be/MDtGGyI8OTc
Lesson 1.6 - Creating the world and getting to know some classes - http://youtu.be/WzIacPkLtIc
Lesson 1.7 - Defining the size of the universe - http://youtu.be/vYksP9ctHMI
Lesson 1.8 - A quick digression about units - http://youtu.be/CnCS2NeBZ0E
Lesson 1.9 - Finishing up our b2World object - http://youtu.be/lVtVKeGiBco
Lesson 1.10 - If you're following along and getting errors... - http://youtu.be/7yqEjjFfrIE
Lesson 1.11 - How do you make a body? - http://youtu.be/h6TozLhho80
Lesson 1.12 - Creating our first shape definition - http://youtu.be/sbbsvKMMr_w
Lesson 1.13 - Finishing up our floor - http://youtu.be/OSwRv4Nzdz0
Lesson 1.14 - Creating the walls - http://youtu.be/iCLP-Ok1ab4
Lesson 1.15 - Creating our falling crate - http://youtu.be/31sVPYW3icE
Lesson 1.16 - Making our world update over time - http://youtu.be/_MbfGRD8Ibk
Lesson 1.17 - Let's Get Visual, Visual - http://youtu.be/KNm8fdkrFuE
Lesson 1.18 - Adding lots of crates! Whee! - http://youtu.be/1d72y6oX9nc
Lesson 1.19 - Experimenting with our values - http://youtu.be/2PCeecp3vv4
Lesson 1.20 - The difference between a shape and a body - http://youtu.be/s_t0MaFDUsM
Lesson 1.21 - Defs, OrientedBoxes, and Coordinates - http://youtu.be/g0FR0WCVFFA
Lesson 1.22 - Conclusion - http://youtu.be/Di5rrYHhN6g

Chapter 2: Making Puggle

Lesson 2.1 - Making a real application -- an Introduction to Chapter 2 - http://youtu.be/85FoF_w-HOM
Lesson 2.2 - Everything we did in Chapter 1 in about six minutes - http://youtu.be/SPAEPPGClQg
Lesson 2.3 - Getting a Sprite to move like a Body - http://youtu.be/fyDGU0haNcg
Lesson 2.4 - Introducing the Actor Class! - http://youtu.be/deA4MMsuOHk
Lesson 2.5 - Creating our first Actor class - http://youtu.be/7e7iF2_Dr0o
Lesson 2.6 - Creating the BallActor class - http://youtu.be/ouEkR8Wlg_E
Lesson 2.7 - Creating the PegActor, Part 1 - http://youtu.be/5wQIrISylxs
Lesson 2.8 - Creating the PegActor, Part 2 - http://youtu.be/268gpaMz0XE
Lesson 2.9 - Adding all those pegs to the world - http://youtu.be/bjeiNwIv5DY
Lesson 2.10 - The ArbitraryStaticActor... as exciting as it sounds! - http://youtu.be/HJ8HeREdjmM
Lesson 2.11 - Finishing the ArbiStaticActor, and adding some walls - http://youtu.be/FZ9SdB4M9og
Lesson 2.12 - Adding some ramps! Yay! - http://youtu.be/G0sqizu4XqQ
Lesson 2.13 - Discussion Questions, like "Why isn't Todd a better coder?" - http://youtu.be/5pxRiw7wuA4
Lesson 2.14 - More Discussion Questions that you probably weren't wondering about - http://youtu.be/Q9JHCTqsY0E
Lesson 2.15 - Getting to know the b2ContactListener - http://youtu.be/FklxMpYi8ww
Lesson 2.16 - Figuring out who bumped into who - http://youtu.be/OBVxTSJjRGs
Lesson 2.17 - Making a peg light up! And some warnings... - http://youtu.be/eP4T8BE7qq0
Lesson 2.18 - How to remove an Actor (insert Hollywood-themed joke here) - http://youtu.be/MGX8GOmzJ8A
Lesson 2.19 - Finishing that Actor's destroy() function - http://youtu.be/CgK3IgYnua4
Lesson 2.20 - Removing our ball and creating a custom event - http://youtu.be/tOgNzVRe_po
Lesson 2.21 - Removing those PegActors we took so long to make (sigh) - http://youtu.be/YT34taQO3QI
Lesson 2.22 - Our game's got a lotta balls. - http://youtu.be/KnEODTxkg54
Lesson 2.23 - Creating the BonusChuteActor class - http://youtu.be/0hQ8CJT2kxc
Lesson 2.24 - Adding a sensor to our BonusChute - http://youtu.be/MZLizo67dA0
Lesson 2.25 - Getting a platform to move -- the wrong way - http://youtu.be/Kiy28DvQxnA
Lesson 2.26 - Getting a platform to move -- the right way - http://youtu.be/C1sgWy07Ek4
Lesson 2.27 - Fixing bugs in our platform and using bullets - http://youtu.be/WBvAnaxJ9KI
Lesson 2.28 - Introducing the camera! - http://youtu.be/PLcsxKRPVbw
Lesson 2.29 - Making our camera dumbly zoom in - http://youtu.be/ess3kNVvvF8
Lesson 2.30 - Tracking our ball and Goal Peg - http://youtu.be/2oAcFim65is
Lesson 2.31 - Zooming in, the smart way - http://youtu.be/JMTb0PJp1_w
Lesson 2.32 - How to control time! (Katana not required) - http://youtu.be/K6Oh-8Yux_8
Lesson 2.33 - Cleaning up, part 1: Removing the flicker - http://youtu.be/DsGJ41ERIKE
Lesson 2.34 - Cleaning up, part 2: Allowing multiple goal pegs - http://youtu.be/H_I0gpqvb6E
Lesson 2.35 - Creating the aiming line, part 1 - http://youtu.be/v0HHg3nrWCE
Lesson 2.36 - Creating the aiming line, part 2 - http://youtu.be/pv-VLmurMo0
Lesson 2.37 - Tweaking the settings - http://youtu.be/dntwtnGDDdY
Lesson 2.38 - Making a rotating shooter turret thing - http://youtu.be/BIIYnGseBTM
Lesson 2.39 - Oops! Fixing one bug with the shooter - http://youtu.be/1i7H2B7hPOM
Lesson 2.40 - Making a smooth camera zoom through TweenLite - http://youtu.be/R98ER9iREW8
Lesson 2.41 - Smoothing the zoom out and slow down moments - http://youtu.be/led40OjZtjI
Lesson 2.42 - Making the pegs fade out - http://youtu.be/iVuJhoTO2dw
Lesson 2.43 - How to NOT make Peggle - http://youtu.be/24Rm18C5E_I
Lesson 2.44 - Turning our static pegs to moving pegs - http://youtu.be/ZU5P7kPdURM
Lesson 2.45 - REALLY turning our static pegs into moving ones - http://youtu.be/7lHoKW2j1o4
Lesson 2.46 - Getting our chute to follow our mouse - http://youtu.be/Pc6PJha93cI
Lesson 2.47 - Making our pegs react more realistically, part 1 - http://youtu.be/7AQv88f14-M
Lesson 2.48 - Even more realistic pegs, ApplyForce and ApplyImpulse - http://youtu.be/IgUr4xrcTEI
Lesson 2.49 - Normals and crazy mathy stuff - http://youtu.be/JruODUnV2o4
Lesson 2.50 - Final to-do list and conclusion - http://youtu.be/pUZ-PVCquOI


Перемещение героя на Flash я не смотрел и не запускал, но я примерно понимаю, как он движется. Я сделал пример, где шар двигается вперёд и назад с помощью клавиш "w" и "s", а поворачивается с помощью клавиш "a" и "d". Клавишей "b" можно включать и отключать рисование коллайдеров. Рисовать коллайдеры помогает физический движок Ammo.js. Изучайте код, экспериментируйте, задавайте вопросы.



index.html

PHP/HTML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
<!DOCTYPE html>
<html lang="en">
 
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
 
    <title>Third Person Movement. DAE, Ammo.js, WebGL, JavaScript</title>
 
    <script src="https://dl.dropboxusercontent.com/s/e5iytx67noqoew7/ammo.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/gl-matrix@3.4.3/gl-matrix-min.js"></script>
 
    <style>
        html,
        body {
            overflow: hidden;
            width: 100%;
            height: 100%;
            margin: 0;
            padding: 0;
        }
 
        #renderCanvas {
            width: 100%;
            height: 100%;
        }
    </style>
</head>
 
<body>
    <canvas id="renderCanvas"></canvas>
 
    <img id="playerImage" src="assets/player.png" hidden>
    <img id="floorImage" src="assets/floor.png" hidden>
 
    <script type="x-shader/x-vertex" id="defaultVertexShader">
        attribute vec4 aPosition;
        attribute vec4 aNormal;
        attribute vec2 aTexCoord;
        uniform mat4 uMvpMatrix;
        uniform mat4 uModelMatrix;
        uniform mat4 uNormalMatrix;
        varying vec3 vPosition;
        varying vec3 vNormal;
        varying vec2 vTexCoord;
        void main()
        {
            gl_Position = uMvpMatrix * aPosition;
            vPosition = vec3(uModelMatrix * aPosition);
            vNormal = normalize(vec3(uNormalMatrix * aNormal));
            vTexCoord = aTexCoord;
        }
    </script>
 
    <script type="x-shader/x-fragment" id="defaultFragmentShader">
        precision mediump float;
        const vec3 lightColor = vec3(1.0, 1.0, 1.0);
        const float ambient = 0.3;
        uniform vec3 uLightPosition;
        uniform sampler2D uSampler;
        varying vec3 vPosition;
        varying vec3 vNormal;
        varying vec2 vTexCoord;
        void main()
        {
            vec4 color = texture2D(uSampler, vTexCoord);
            vec3 normal = normalize(vNormal);
            vec3 lightDirection = normalize(uLightPosition - vPosition);
            float nDotL = max(dot(lightDirection, normal), ambient);
            vec3 diffuse = lightColor * color.rgb * nDotL;
            gl_FragColor = vec4(diffuse, color.a);
        }
    </script>
 
    <script type="x-shader/x-vertex" id="edgeVertexShader">
        attribute vec3 aPosition;
        uniform mat4 uMvpMatrix;
        void main()
        {
            gl_Position = uMvpMatrix * vec4(aPosition, 1.0);
        }
    </script>
 
    <script type="x-shader/x-fragment" id="edgeFragmentShader">
        precision mediump float;
        void main()
        {
            gl_FragColor = vec4(0.1, 1.0, 0.3, 1.0);
        }
    </script>
 
    <script src="js/webgl-context.js"></script>
    <script src="js/shader-program.js"></script>
    <script src="js/vertex-buffers.js"></script>
    <script src="js/object-for-graphics.js"></script>
    <script src="js/object-for-physics.js"></script>
    <script src="js/object-for-edge.js"></script>
    <script src="js/debug-drawer.js"></script>
    <script src="js/keyboard.js"></script>
    <script src="js/main.js"></script>
</body>
 
</html>
js/debug-drawer.js

JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
class DebugDrawer
{
    constructor(edge)
    {
        this.edge = edge;
        this.projViewMatrix = null;
 
        this.heap = null;
        this.debugDrawer = new Ammo.DebugDrawer();
        this.debugDrawer.drawLine = ((from, to, color) => { this.drawLine(from, to, color) }).bind(this);
        this.debugDrawer.setDebugMode = () => { };
 
        this.debugMode = 1; // 0 - 0ff, 1 - on
        this.debugDrawer.getDebugMode = () => { return this.debugMode; };
        this.debugDrawer.setDebugMode(1);
 
        this.debugDrawer.drawContactPoint = (pointOnB, normalOnB, distance, lifeTime, color) => { };
        this.debugDrawer.reportErrorWarning = (warningString) => { };
        this.debugDrawer.draw3dText = (location, textString) => { };
 
        world.setDebugDrawer(this.debugDrawer);
 
        this.fromX = 0;
        this.fromY = 0;
        this.fromZ = 0;
        this.toX = 0;
        this.toY = 0;
        this.toZ = 0;
        this.centerX = 0;
        this.centerY = 0;
        this.centerZ = 0;
 
        this.length = 0;
        this.vec = glMatrix.vec3.create();
        this.x = 0;
        this.y = 0;
        this.z = 0;
        this.unitX = glMatrix.vec3.fromValues(1, 0, 0);
    }
 
    drawLine(from, to, color)
    {
        this.heap = Ammo.HEAPF32;
 
        this.fromX = this.heap[(parseInt(from) + 0) / 4];
        this.fromY = this.heap[(parseInt(from) + 4) / 4];
        this.fromZ = this.heap[(parseInt(from) + 8) / 4];
 
        this.toX = this.heap[(parseInt(to) + 0) / 4];
        this.toY = this.heap[(parseInt(to) + 4) / 4];
        this.toZ = this.heap[(parseInt(to) + 8) / 4];
 
        if (this.fromX > this.toX)
        {
            this.centerX = this.toX + Math.abs(this.fromX - this.toX) / 2;
        }
        else
        {
            this.centerX = this.fromX + Math.abs(this.toX - this.fromX) / 2;
        }
 
        if (this.fromY > this.toY)
        {
            this.centerY = this.toY + Math.abs(this.fromY - this.toY) / 2;
        }
        else
        {
            this.centerY = this.fromY + Math.abs(this.toY - this.fromY) / 2;
        }
 
        if (this.fromZ > this.toZ)
        {
            this.centerZ = this.toZ + Math.abs(this.fromZ - this.toZ) / 2;
        }
        else
        {
            this.centerZ = this.fromZ + Math.abs(this.toZ - this.fromZ) / 2;
        }
 
        this.vec[0] = this.toX - this.fromX;
        this.vec[1] = this.toY - this.fromY;
        this.vec[2] = this.toZ - this.fromZ;
        this.length = glMatrix.vec3.length(this.vec);
 
        glMatrix.vec3.normalize(this.vec, this.vec);
        glMatrix.quat.rotationTo(this.edge.rotation, this.unitX, this.vec);
 
        this.edge.scale = [this.length, 0.05, 0.05];
        this.edge.position = [this.centerX, this.centerY, this.centerZ];
        this.edge.draw(this.projViewMatrix);
    }
}
keyboard.js

JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Keyboard
{
    constructor()
    {
        this.keys = {};
        document.addEventListener("keydown", (event) => { this.onKeyChange(event, true) }, true);
        document.addEventListener("keyup", (event) => { this.onKeyChange(event, false) }, false);
    }
 
    pressed(key)
    {
        return this.keys[key];
    }
 
    onKeyChange(event, pressed)
    {
        this.keys[event.key] = pressed;
    }
}
js/main.js

JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
let defaultProgram, edgeProgram;
let player, floor, edge, cube;
let debugDrawer, keyboard;
let debugMode = true;
const angularSpeed = 200;
const linearSpeed = 200;
const dt = 0.015;
 
const forward = glMatrix.vec3.fromValues(0, 0, 1);
const zUnit = glMatrix.vec3.fromValues(0, 0, 1);
const playerRotation = glMatrix.quat.create();
glMatrix.quat.rotationTo(playerRotation, zUnit, forward);
const direction = glMatrix.vec3.fromValues(forward[0], forward[1], forward[2]);
let dirAngle = 0;
 
const projMatrix = glMatrix.mat4.create();
const viewMatrix = glMatrix.mat4.create();
const projViewMatrix = glMatrix.mat4.create();
 
initVertexBuffers(["assets/player.dae", "assets/floor.dae", "assets/cube.dae"],
    (vertPosVBOs, normalVBOs, texCoordVBOs, amounts) =>
    {
        defaultProgram = createProgram("defaultVertexShader", "defaultFragmentShader");
        edgeProgram = createProgram("edgeVertexShader", "edgeFragmentShader");
        gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
 
        Ammo().then(() =>
        {
            const collisionConfiguration = new Ammo.btDefaultCollisionConfiguration();
            const dispatcher = new Ammo.btCollisionDispatcher(collisionConfiguration);
            const overlappingPairCache = new Ammo.btDbvtBroadphase();
            const solver = new Ammo.btSequentialImpulseConstraintSolver();
 
            world = new Ammo.btDiscreteDynamicsWorld(dispatcher, overlappingPairCache, solver, collisionConfiguration);
            world.setGravity(new Ammo.btVector3(0, -9.81, 0));
 
            const playerImage = document.getElementById("playerImage");
            const playerTexture = gl.createTexture();
            gl.bindTexture(gl.TEXTURE_2D, playerTexture);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
            gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, playerImage);
            const playserSize = 0.5;
            const playerShape = new Ammo.btSphereShape(playserSize);
            player = new ObjectForPhysics(defaultProgram, [0, 3, 2], playerRotation, amounts[0],
                vertPosVBOs[0], normalVBOs[0], texCoordVBOs[0], playerTexture, playerShape, 50);
            player.scale = [playserSize, playserSize, playserSize];
            player.body.setAngularFactor(0, 1, 0);
            player.body.setDamping(0.9, 0.99);
 
            const floorImage = document.getElementById("floorImage");
            const floorTexture = gl.createTexture();
            gl.bindTexture(gl.TEXTURE_2D, floorTexture);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
            gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, floorImage);
            const floorSize = [10, 1, 10];
            const floorShape = new Ammo.btBoxShape(
                new Ammo.btVector3(floorSize[0] / 2, floorSize[1] / 2, floorSize[2] / 2));
            floor = new ObjectForPhysics(defaultProgram, [0, -2, 0], [0, 0, 0, 1], amounts[1],
                vertPosVBOs[1], normalVBOs[1], texCoordVBOs[1], floorTexture, floorShape, 0);
            floor.scale = [10, 5, 10];
 
            edge = new ObjectForEdge(edgeProgram, amounts[2], vertPosVBOs[2]);
            debugDrawer = new DebugDrawer(edge);
            debugDrawer.debugMode = 1;
 
            keyboard = new Keyboard();
 
            init();
        });
    });
 
function init()
{
    glMatrix.mat4.perspective(projMatrix, 55 * Math.PI / 180, 1, 0.1, 500);
    glMatrix.mat4.lookAt(viewMatrix, [0, 3, 10], [0, 0, 0], [0, 1, 0]);
 
    gl.useProgram(defaultProgram);
    const lightPosition = glMatrix.vec3.fromValues(2, 3, 5);
    const uLightPositionLocation = gl.getUniformLocation(defaultProgram, "uLightPosition");
    gl.uniform3fv(uLightPositionLocation, lightPosition);
 
    window.onresize = () =>
    {
        const w = canvas.clientWidth;
        const h = canvas.clientHeight;
        gl.canvas.width = w;
        gl.canvas.height = h;
        gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
        glMatrix.mat4.perspective(projMatrix, 55 * Math.PI / 180, w / h, 0.1, 500);
        createPhysicsSimulation();
        draw();
    };
    window.onresize(null);
 
    document.onkeydown = (event) =>
    {
        if (event.repeat) return;
 
        if (event.key === "b")
        {
            debugMode = !debugMode;
            if (debugMode)
            {
                debugDrawer.debugMode = 1;
            }
            else
            {
                debugDrawer.debugMode = 0;
            }
        }
    }
}
 
function createPhysicsSimulation()
{
    setInterval(() =>
    {
        updatePhysics();
    }, 15);
}
 
function updatePhysics()
{
    if (keyboard.pressed("w"))
    {
        let vy = player.body.getLinearVelocity().y();
        const impulse = new Ammo.btVector3(direction[0] * linearSpeed * dt, vy, direction[2] * linearSpeed * dt);
        player.body.setLinearVelocity(impulse);
    }
    if (keyboard.pressed("s"))
    {
        const vy = player.body.getLinearVelocity().y();
        const impulse = new Ammo.btVector3(-direction[0] * linearSpeed * dt, vy, -direction[2] * linearSpeed * dt);
        player.body.setLinearVelocity(impulse);
    }
    if (keyboard.pressed("a"))
    {
        const impulse = new Ammo.btVector3(0, angularSpeed * dt, 0);
        player.body.setAngularVelocity(impulse);
    }
    if (keyboard.pressed("d"))
    {
        const impulse = new Ammo.btVector3(0, -angularSpeed * dt, 0);
        player.body.setAngularVelocity(impulse);
    }
 
    glMatrix.vec3.transformQuat(direction, forward, player.rotation);
    world.stepSimulation(dt, 8);
    player.update();
}
 
function draw()
{
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    glMatrix.mat4.mul(projViewMatrix, projMatrix, viewMatrix);
 
    player.draw(projViewMatrix);
    floor.draw(projViewMatrix);
 
    debugDrawer.projViewMatrix = projViewMatrix;
 
    if (debugMode)
    {
        world.debugDrawWorld();
    }
 
    requestAnimationFrame(draw);
}
js/object-for-edge.js

JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
class ObjectForEdge
{
    constructor(program, amountOfVertices, vertPosBuffer)
    {
        this.position = [0, 0, 0];
        this.rotation = glMatrix.quat.create();
        this.scale = [1, 1, 1];
 
        this.amountOfVertices = amountOfVertices;
        this.vertPosBuffer = vertPosBuffer;
 
        this.mvpMatrix = glMatrix.mat4.create();
        this.modelMatrix = glMatrix.mat4.create();
 
        gl.useProgram(program);
        this.uMvpMatrixLocation = gl.getUniformLocation(program, "uMvpMatrix");
        this.program = program;
    }
 
    bind()
    {
        gl.bindBuffer(gl.ARRAY_BUFFER, this.vertPosBuffer);
        gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0);
        gl.enableVertexAttribArray(0);
    }
 
    draw(projViewMatrix)
    {
        gl.useProgram(this.program);
 
        glMatrix.mat4.fromRotationTranslationScale(this.modelMatrix, this.rotation, this.position, this.scale);
        glMatrix.mat4.mul(this.mvpMatrix, projViewMatrix, this.modelMatrix);
        gl.uniformMatrix4fv(this.uMvpMatrixLocation, false, this.mvpMatrix);
 
        this.bind();
        gl.drawArrays(gl.TRIANGLES, 0, this.amountOfVertices);
    }
}
Вложения
Тип файла: zip third-person-movement-dae-ammojs-webgl-js.zip (428.7 Кб, 17 просмотров)
0
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
13.01.2022, 23:12
Ответы с готовыми решениями:

Перенести управление стрелками на управление WASD
Мне нужно чтобы в игре вместо стрелок управление осуществлялось WASD и при этом сами WASD не нажимались в игре. Для блокировки нажатия...

Вид от 1 лица в Unity 5
Всем привет. Народ столкнулся с такой бедой. Раньше работал в Unity3D Pro 3.5 сейчас перешел на Unity3D 5. И вот в чём вопрос - Не...

Вид от 3 лица в 3D game studio a7
Наверное опрос не в тему. Но как в 3D game studio сделать вид от 3 лица. Я знаю только как от 1 но от 3 не как не могу сделать. Помогите...

31
9987 / 2959 / 497
Регистрация: 05.10.2013
Сообщений: 8,110
Записей в блоге: 243
24.10.2022, 12:23  [ТС]
Студворк — интернет-сервис помощи студентам
Моё краткое введение в WebGL на JavaScript
0
9987 / 2959 / 497
Регистрация: 05.10.2013
Сообщений: 8,110
Записей в блоге: 243
28.10.2022, 07:18  [ТС]
Ошибка в сообщении #2 в файле "js/shader-program.js":

JavaScript
1
2
3
4
5
6
7
8
    gl.shaderSource(fShader, fragShaderSource);
    gl.compileShader(fShader);
    ok = gl.getShaderParameter(vShader, gl.COMPILE_STATUS);
    if (!ok)
    {
        console.log("frag: " + gl.getShaderInfoLog(fShader));
        return null;
    };
в строке:

JavaScript
1
ok = gl.getShaderParameter(vShader, gl.COMPILE_STATUS);
указан статус компиляции вершинного шейдера, а должно быть фрагментного:

JavaScript
1
ok = gl.getShaderParameter(fShader, gl.COMPILE_STATUS);
Исправил в песочнице
0
9987 / 2959 / 497
Регистрация: 05.10.2013
Сообщений: 8,110
Записей в блоге: 243
22.11.2022, 19:49  [ТС]
Конвертация Ammo.js в ES6-модуль для использования с Rollup на JavaScript

Сделано на основе этого решения, то есть этой демки и этого кода.

Решение такое - в файле ammo.js нужно удалить следующий код:



Пример на Rollup выводящий значение гравитации

Для работы примера нужно глобально установить следующие модули командой: "npm i -g rollup broserify uglify-js" Команда "npm run dev" запустит Rollup в режиме отладки, в котором Rollup будет ждать изменения в файлах проекта, а в браузере можно будет ставить точки останова. Команда "npm run release" создаст файл bundle.js с помощью Browserify, а Uglify сожмёт этот файл.

public/index.html

PHP/HTML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!DOCTYPE html>
<html lang="en">
 
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
 
<body>
    <script type="importmap">
        {
            "imports": {
                "ammo-es": "https://dl.dropboxusercontent.com/s/1os9vvoo1pa3ajk/ammo-es.js"
            }
        }
    </script>
 
    <script type="module" src="./js/bundle.js"></script>
</body>
 
</html>
src/main.js

JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import AmmoLib from "ammo-es";
 
let Ammo, physicsWorld;
 
function setupPhysicsWorld() {
    const collisionConfiguration = new Ammo.btDefaultCollisionConfiguration();
    const dispatcher = new Ammo.btCollisionDispatcher(collisionConfiguration);
    const overlappingPairCache = new Ammo.btDbvtBroadphase();
    const solver = new Ammo.btSequentialImpulseConstraintSolver();
 
    physicsWorld = new Ammo.btDiscreteDynamicsWorld(dispatcher, overlappingPairCache, solver, collisionConfiguration);
    physicsWorld.setGravity(new Ammo.btVector3(0, -10, 0));
}
 
function animationLoop() {
    const gravity = new Ammo.btVector3(0, -10, 0);
    console.log(`Gravity: (${gravity.x()}, ${gravity.y()}, ${gravity.z()})`);
    // requestAnimationFrame(animationLoop);
}
 
function init() {
    AmmoLib().then((re) => {
        Ammo = re;
        setupPhysicsWorld();
        animationLoop();
    });
}
 
init();
rollup.config.js

JavaScript
1
2
3
4
5
6
export default {
    input: "./src/main.js",
    output: {
        file: "public/js/bundle.js"
    }
}
package.json

JSON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
```
{
  "name": "ammo-es-module-rollup-js",
  "version": "1.0.0",
  "description": "",
  "main": "rollup.config.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "rollup -cwm",
    "del-bundle-map": "del /f /q /s .\\public\\js\\bundle.js.map",
    "bundle": "rollup -cm",
    "minify": "uglifyjs public/js/bundle.js -o public/js/bundle.js",
    "release": "npm run bundle && npm run minify && npm run del-bundle-map"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}
Миниатюры
Вложения
Тип файла: zip ammo-es-module-rollup-js.zip (3.9 Кб, 0 просмотров)
Тип файла: zip ammo-es.zip (434.0 Кб, 0 просмотров)
1
9987 / 2959 / 497
Регистрация: 05.10.2013
Сообщений: 8,110
Записей в блоге: 243
29.11.2022, 19:59  [ТС]
Загрузка физического движка Ammo.js и текстуры с помощью Promise на JavaScript

index.html

PHP/HTML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<!DOCTYPE html>
<html lang="en">
 
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Load Ammo.js and a texture using Promise. Rollup, WebGL, JavaScript</title>
</head>
 
<body>
    <canvas id="renderCanvas" width="300" height="300"></canvas>
 
    <script type="importmap">
        {
            "imports": {
                "gl-matrix": "https://cdn.skypack.dev/gl-matrix@3.4.3",
                "ammo-es": "https://dl.dropboxusercontent.com/s/1os9vvoo1pa3ajk/ammo-es.js"
            }
        }
    </script>
 
    <script type="module" src="./js/main.js"></script>
</body>
 
</html>
init-ammo.js

JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import AmmoLib from "ammo-es";
 
export let Ammo = null;
export let world = null;
 
export function initAmmo() {
    return new Promise(resolve => {
        AmmoLib().then((re) => {
            Ammo = re;
 
            const collisionConfiguration = new Ammo.btDefaultCollisionConfiguration();
            const dispatcher = new Ammo.btCollisionDispatcher(collisionConfiguration);
            const overlappingPairCache = new Ammo.btDbvtBroadphase();
            const solver = new Ammo.btSequentialImpulseConstraintSolver();
 
            world = new Ammo.btDiscreteDynamicsWorld(dispatcher, overlappingPairCache,
                solver, collisionConfiguration);
            world.setGravity(new Ammo.btVector3(0, -10, 0));
 
            resolve();
        });
    });
}
load-texture.js

JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { gl } from "./webgl-context.js";
 
export default function loadTexture(url) {
    return new Promise(resolve => {
        const image = new Image();
        image.onload = () => {
            const texture = gl.createTexture();
            gl.bindTexture(gl.TEXTURE_2D, texture);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
            gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, image);
            resolve(texture);
        };
        image.src = url;
    });
}
main.js

JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import { vec3, mat4 } from "gl-matrix";
import { gl, initWebGLContext } from "./webgl-context.js";
import { Ammo, world, initAmmo } from "./init-ammo.js";
import loadTexture from "./load-texture.js";
 
async function init() {
    // Initialize the WebGL context
    if (!initWebGLContext("renderCanvas")) return;
    gl.clearColor(0.2, 0.2, 0.4, 1);
    gl.clear(gl.COLOR_BUFFER_BIT);
 
    // Init Ammo.js and print a gravity
    await initAmmo();
    const gravity = world.getGravity();
    console.log(gravity.x(), gravity.y(), gravity.z());
 
    // Load a texture
    const texture = await loadTexture("./assets/floor.png");
    console.log(texture);
 
    // Create a vector and a matrix
    const v = vec3.fromValues(1, 2, 3);
    console.log("v =", v);
    const m = mat4.create();
    console.log("m =", m);
}
 
init();
webgl-context.js

JavaScript
1
2
3
4
5
6
7
8
9
10
11
export let gl = null;
 
export function initWebGLContext(canvasName) {
    const canvas = document.getElementById(canvasName);
    if (canvas === null) {
        console.log(`Failed to get a canvas element with the name "${canvasName}"`);
        return false;
    }
    gl = canvas.getContext("webgl", { alpha: false, premultipliedAlpha: false });
    return true;
}
0
9987 / 2959 / 497
Регистрация: 05.10.2013
Сообщений: 8,110
Записей в блоге: 243
18.01.2023, 15:42  [ТС]
В примерах выше перед тегом <script type="importmap"> надо добавить этот тег:

HTML5
1
2
3
4
5
    <!-- Since import maps are not yet supported by all browsers, it is
        necessary to add the polyfill es-module-shims.js
        Source: https://threejs.org/docs/index.html#manual/en/introduction/Installation
    -->
    <script async src="https://unpkg.com/es-module-shims@1.3.6/dist/es-module-shims.js"></script>
Потому что importmap пока далеко не на всех новых версиях браузеров поддерживается: https://caniuse.com/import-maps

Поскольку импортные карты еще не поддерживаются всеми браузерами, необходимо добавить полифил es-module-shims.js
Источник этой информации: https://threejs.org/docs/index... stallation
0
9987 / 2959 / 497
Регистрация: 05.10.2013
Сообщений: 8,110
Записей в блоге: 243
18.01.2023, 22:10  [ТС]
Для авторитарного сервера нужен физический движок на сервере, если делать перемещение от третьего лица с мультиплеером. Статья на эту тему: http://buildnewgames.com/real-time-multiplayer/

Ammo.js на сервере

Для установки Ammo.js ведите команду:

npm i github:kripken/ammo.js
Запуск сервера:

node app.js
app.js

JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
const AmmoLib = require("ammo.js");
 
let world, tmpTrans, Ammo;
const rigidBodies = [];
 
function setupPhysicsWorld()
{
    const collisionConfiguration = new Ammo.btDefaultCollisionConfiguration();
    const dispatcher = new Ammo.btCollisionDispatcher(collisionConfiguration);
    const overlappingPairCache = new Ammo.btDbvtBroadphase();
    const solver = new Ammo.btSequentialImpulseConstraintSolver();
 
    world = new Ammo.btDiscreteDynamicsWorld(dispatcher, overlappingPairCache, solver, collisionConfiguration);
    world.setGravity(new Ammo.btVector3(0, -10, 0));
    tmpTrans = new Ammo.btTransform();
}
 
function createGround()
{
    const pos = { x: 0, y: 0, z: 0 };
    const scale = { x: 50, y: 2, z: 50 };
    const quat = { x: 0, y: 0, z: 0, w: 1 };
    const mass = 0;
 
    const transform = new Ammo.btTransform();
    transform.setIdentity();
    transform.setOrigin(new Ammo.btVector3(pos.x, pos.y, pos.z));
    transform.setRotation(new Ammo.btQuaternion(quat.x, quat.y, quat.z, quat.w));
    const motionState = new Ammo.btDefaultMotionState(transform);
 
    const colShape = new Ammo.btBoxShape(new Ammo.btVector3(scale.x / 2, scale.y / 2, scale.z / 2));
    colShape.setMargin(0.05);
 
    const localInertia = new Ammo.btVector3(0, 0, 0);
    colShape.calculateLocalInertia(mass, localInertia);
 
    const rbInfo = new Ammo.btRigidBodyConstructionInfo(mass, motionState, colShape, localInertia);
    const body = new Ammo.btRigidBody(rbInfo);
 
    world.addRigidBody(body);
}
 
function createBox(pos)
{
    const scale = { x: 1, y: 1, z: 1 };
    const quat = { x: 0, y: 0, z: 0, w: 1 };
    const mass = 10;
 
    const transform = new Ammo.btTransform();
    transform.setIdentity();
    transform.setOrigin(new Ammo.btVector3(pos.x, pos.y, pos.z));
    transform.setRotation(new Ammo.btQuaternion(quat.x, quat.y, quat.z, quat.w));
    const motionState = new Ammo.btDefaultMotionState(transform);
 
    const colShape = new Ammo.btBoxShape(new Ammo.btVector3(scale.x / 2, scale.y / 2, scale.z / 2));
    colShape.setMargin(0.05);
 
    const localInertia = new Ammo.btVector3(0, 0, 0);
    colShape.calculateLocalInertia(mass, localInertia);
 
    const rbInfo = new Ammo.btRigidBodyConstructionInfo(mass, motionState, colShape, localInertia);
    const body = new Ammo.btRigidBody(rbInfo);
 
    world.addRigidBody(body);
    rigidBodies.push(body);
}
 
function updatePhysics()
{
    world.stepSimulation(0.05, 10);
    
    for (let i = 0; i < rigidBodies.length; i++)
    {
        let objAmmo = rigidBodies[i];
        let ms = objAmmo.getMotionState();
        if (ms)
        {
            ms.getWorldTransform(tmpTrans);
            const p = tmpTrans.getOrigin();
            const q = tmpTrans.getRotation();
            console.log(p.x(), p.y(), p.z());
        }
    }
}
 
function start()
{
    setupPhysicsWorld();
    createGround();
    createBox({ x: 0, y: 3, z: 0 });
    setInterval(updatePhysics, 1000 / 20);
}
 
AmmoLib().then((re) => {
    Ammo = re;
    start();
});
0
9987 / 2959 / 497
Регистрация: 05.10.2013
Сообщений: 8,110
Записей в блоге: 243
28.05.2023, 17:05  [ТС]
На базе примера из первого сообщения темы реализовал простейший мультиплеер, используя WebSockets (пакет https://www.npmjs.com/package/ws) и Node.js. Отправляю ввод с клавиатуры на сервер, который ретранслирует его на другие клиенты. Используется WebGL 1.0, glMatrix и OimoPhysics. Извлёк оригинальные модели, текстуры и анимации, используя RE1MV. Создал скелетную анимацию без скиннинга с помощью Blender Python API. Скиннинг дорог по ресурсам и не нужен для моделей этой игры, в которой все части анимационных моделей - это отдельные объекты.



Все извлечённые анимации для Jill и Barry. Крутить камеру с зажатым колёсиком мыши. Двигать камеру - правой кнопкой мыши:

1
9987 / 2959 / 497
Регистрация: 05.10.2013
Сообщений: 8,110
Записей в блоге: 243
06.06.2023, 10:52  [ТС]
Доделал первую версию демки с авторитарным сервером, где можно перемещаться в кооперативном и одиночном режим от первого и третьего лица. Сделал для показа моделей, анимации сделанных в Blender и интерактивных взаимодействий на WebGL.

Демка в браузере



Миниатюры
0
9987 / 2959 / 497
Регистрация: 05.10.2013
Сообщений: 8,110
Записей в блоге: 243
05.03.2024, 13:32  [ТС]
Пример на PhysX и Three.js. Взят отсюда. Здесь PhysX и Three.js загружаются в скрипт типа "module". Я скопировал оригинальный пример из песочницы SBEdit в песочницу Plunker. Мне из всех песочниц больше всего нравится Plunker потому что в ней мой разбивать свой код на файлы и загружать модели, текстуры, звуки и другие ассеты.

Песочницы:



PHP/HTML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
<!doctype html>
<html>
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <title>Physx Example</title>
        <meta name="description" content="Trying out Nvidia Physx using physx-js and Threejs" />
        <style>
            body {
                overflow: hidden;
                margin: 0px;
            }
        </style>
        <script type="importmap">
            {
                "imports": {
                    "three": "/build/three.module.js",
                    "three/addons/": "/jsm/"
                }
            }
        </script>
        <script src="https://cdn.jsdelivr.net/npm/physx-js/dist/physx.release.js"></script>
    </head>
    <body>
        <script type="module">
            import * as THREE from 'three'
            import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
 
            // Inspired from code found at 
            // https://github.com/ashconnell/physx-js
            // https://manthrax.github.io/acp/
 
            const makeEntities = () => {
                let ids = 0
                const entities = []
 
                entities.push({
                    id: ++ids,
                    transform: {
                        position: [0, 0, 0],
                        rotation: [0, 0, 0, 1]
                    },
                    model: {
                        type: 'box',
                        size: [10, 0.1, 10]
                    },
                    body: {
                        type: 'box',
                        size: [10, 0.1, 10],
                        dynamic: false
                    }
                })
 
                for (let i = 0; i < 12; i++) {
                    for (let j = 0; j < 12; j++) {
                        entities.push({
                            id: ++ids,
                            transform: {
                                position: [-2.5 + i + 0.1 * i, Math.floor(Math.random() * 10) + 1, -2.5 + j + 0.1 * j],
                                rotation: [0, 0, 0.3, 0.7]
                            },
                            model: {
                                type: 'box',
                                size: [1, 1, 1]
                            },
                            body: {
                                type: 'box',
                                size: [1, 1, 1],
                                dynamic: true
                            }
                        })
                    }
                }
 
                return entities
            }
 
            const PhysX = PHYSX({
                locateFile(path) {
                    if (path.endsWith('.wasm')) {
                        return 'https://cdn.jsdelivr.net/npm/physx-js/dist/physx.release.wasm'
                    }
                    return path
                },
                onRuntimeInitialized() {
                    console.clear()
                    console.log('PhysX loaded')
 
                    // setup physx scene
                    const entities = makeEntities()
                    const bodies = {}
 
                    const version = PhysX.PX_PHYSICS_VERSION
                    const defaultErrorCallback = new PhysX.PxDefaultErrorCallback()
                    const allocator = new PhysX.PxDefaultAllocator()
                    const foundation = PhysX.PxCreateFoundation(version, allocator, defaultErrorCallback)
                    const triggerCallback = {
                        onContactBegin: () => {},
                        onContactEnd: () => {},
                        onContactPersist: () => {},
                        onTriggerBegin: () => {},
                        onTriggerEnd: () => {}
                    }
                    const physxSimulationCallbackInstance = PhysX.PxSimulationEventCallback.implement(triggerCallback)
 
                    const physics = PhysX.PxCreatePhysics(version, foundation, new PhysX.PxTolerancesScale(), false, null)
                    PhysX.PxInitExtensions(physics, null)
                    const sceneDesc = PhysX.getDefaultSceneDesc(physics.getTolerancesScale(), 0, physxSimulationCallbackInstance)
                    const physxScene = physics.createScene(sceneDesc)
 
                    entities.forEach((entity) => {
                        let geometry
                        if (entity.body.type === 'box') {
                            geometry = new PhysX.PxBoxGeometry(
                                // PhysX uses half-extents
                                entity.body.size[0] / 2,
                                entity.body.size[1] / 2,
                                entity.body.size[2] / 2
                            )
                        }
                        if (entity.body.type === 'sphere') {
                            geometry = new Physx.PxSphereGeometry(...entity.body.size)
                        }
                        const material = physics.createMaterial(0.2, 0.2, 0.2)
                        const flags = new PhysX.PxShapeFlags(PhysX.PxShapeFlag.eSCENE_QUERY_SHAPE.value | PhysX.PxShapeFlag.eSIMULATION_SHAPE.value)
                        const shape = physics.createShape(geometry, material, false, flags)
 
                        const filterData = new PhysX.PxFilterData(1, 1, 0, 0)
                        shape.setQueryFilterData(filterData)
                        shape.setSimulationFilterData(filterData)
 
                        const transform = {
                            translation: {
                                x: entity.transform.position[0],
                                y: entity.transform.position[1],
                                z: entity.transform.position[2]
                            },
                            rotation: {
                                x: entity.transform.rotation[0],
                                y: entity.transform.rotation[1],
                                z: entity.transform.rotation[2],
                                w: entity.transform.rotation[3]
                            }
                        }
                        let body
                        if (entity.body.dynamic) {
                            body = physics.createRigidDynamic(transform)
                        } else {
                            body = physics.createRigidStatic(transform)
                        }
                        body.attachShape(shape)
                        bodies[entity.id] = body
                        physxScene.addActor(body, null)
                    })
 
                    const updatePhysx = (entities) => {
                        physxScene.simulate(1 / 60, true)
                        physxScene.fetchResults(true)
                        entities.forEach((entity) => {
                            const body = bodies[entity.id]
 
                            const transform = body.getGlobalPose()
                            if (transform.translation.y < -10) {
                                const v = body.getLinearVelocity()
                                v.x = 0
                                v.y = 0
                                v.z = 0
                                body.setLinearVelocity(v, true)
 
                                transform.translation.x = Math.random() * 5 - 2.5
                                transform.translation.y = Math.random() * 6 + 4
                                transform.translation.z = Math.random() * 5 - 2.5
                                body.setGlobalPose(transform, true)
                            }
 
                            entity.transform.position[0] = transform.translation.x
                            entity.transform.position[1] = transform.translation.y
                            entity.transform.position[2] = transform.translation.z
                            entity.transform.rotation[0] = transform.rotation.x
                            entity.transform.rotation[1] = transform.rotation.y
                            entity.transform.rotation[2] = transform.rotation.z
                            entity.transform.rotation[3] = transform.rotation.w
                        })
                    }
 
                    // setup threejs
                    const meshes = {}
 
                    const scene = new THREE.Scene()
 
                    const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)
                    camera.position.set(5, 7, 9)
 
                    const renderer = new THREE.WebGLRenderer()
                    renderer.setSize(window.innerWidth, window.innerHeight)
                    document.body.appendChild(renderer.domElement)
 
                    const controls = new OrbitControls(camera, renderer.domElement)
                    controls.enableDamping = true
 
                    entities.forEach((entity) => {
                        let geometry
                        if (entity.model.type === 'box') {
                            geometry = new THREE.BoxGeometry(entity.model.size[0], entity.model.size[1], entity.model.size[2])
                        }
                        const material = new THREE.MeshNormalMaterial()
                        const mesh = new THREE.Mesh(geometry, material)
                        mesh.position.fromArray(entity.transform.position)
                        mesh.quaternion.fromArray(entity.transform.rotation)
                        mesh.castShadow = true
                        mesh.receiveShadow = true
                        meshes[entity.id] = mesh
                        scene.add(mesh)
                    })
 
                    function onWindowResize() {
                        camera.aspect = window.innerWidth / window.innerHeight
                        camera.updateProjectionMatrix()
                        renderer.setSize(window.innerWidth, window.innerHeight)
                    }
                    window.addEventListener('resize', onWindowResize, false)
 
                    // start rendering updates
                    function animate() {
                        requestAnimationFrame(animate)
 
                        controls.update()
 
                        entities.forEach((entity) => {
                            meshes[entity.id].position.fromArray(entity.transform.position)
                            meshes[entity.id].quaternion.fromArray(entity.transform.rotation)
                        })
 
                        updatePhysx(entities)
 
                        renderer.render(scene, camera)
                    }
 
                    animate()
                }
            })
        </script>
    </body>
</html>
Миниатюры
0
9987 / 2959 / 497
Регистрация: 05.10.2013
Сообщений: 8,110
Записей в блоге: 243
05.03.2024, 13:44  [ТС]
Пример рисования коллайдеров физического движка PhysX на <canvas> в контексте "2d" с использованием библиотеки линейной алгебры glMatrix. PhysX используется в скрипте типа "module". Пример не мой. Его опубликовали в теме здесь. PhysX - кандидат на создание контроллера от третьего лица.

Песочница: https://sbedit.net/41255c40b51... a28db2f052



PHP/HTML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
<!DOCTYPE html>
<head>
    <title>PhysX Test</title>
    <style>
        html, body, canvas {
            margin: 0;
            padding: 0;
            width: 100%;
            height: 100%;
        }
 
        canvas {
            display: block;
        }
    </style>
</head>
<body>
    <canvas></canvas>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/3.4.2/gl-matrix-min.js" integrity="sha512-eV9ExyTa3b+YHr99IBTYpwk4wbgDMDlfW8uTxhywO8dWb810fGUSKDgHhEv1fAqmJT4jyYnt1iWWMW4FRxeQOQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
    <script type="module">
        import PhysX from "https://esm.sh/physx-js-webidl"
 
        PhysX().then(function(PhysX) {
            var version = PhysX.PHYSICS_VERSION;
            console.log('PhysX loaded! Version: ' + ((version >> 24) & 0xff) + '.' + ((version >> 16) & 0xff) + '.' + ((version >> 8) & 0xff));
 
            var allocator = new PhysX.PxDefaultAllocator();
            var errorCb = new PhysX.PxDefaultErrorCallback();
            var foundation = PhysX.CreateFoundation(version, allocator, errorCb);
            console.log('Created PxFoundation');
 
            var tolerances = new PhysX.PxTolerancesScale();
            var physics = PhysX.CreatePhysics(version, foundation, tolerances);
            console.log('Created PxPhysics');
 
            // create scene
            var tmpVec = new PhysX.PxVec3(0, -9.81, 0);
            var sceneDesc = new PhysX.PxSceneDesc(tolerances);
            sceneDesc.set_gravity(tmpVec);
            sceneDesc.set_cpuDispatcher(PhysX.DefaultCpuDispatcherCreate(0));
            sceneDesc.set_filterShader(PhysX.DefaultFilterShader());
            var scene = physics.createScene(sceneDesc);
            console.log('Created scene');
 
            // create a default material
            var material = physics.createMaterial(0.5, 0.5, 0.5);
            // create default simulation shape flags
            var shapeFlags = new PhysX.PxShapeFlags(PhysX.PxShapeFlagEnum.eSCENE_QUERY_SHAPE | PhysX.PxShapeFlagEnum.eSIMULATION_SHAPE |  PhysX.PxShapeFlagEnum.eVISUALIZATION);
 
            // create a few temporary objects used during setup
            var tmpPose = new PhysX.PxTransform(PhysX.PxIDENTITYEnum.PxIdentity);
            var tmpFilterData = new PhysX.PxFilterData(1, 1, 0, 0);
 
            // create a large static box with size 20x1x20 as ground
            var groundGeometry = new PhysX.PxBoxGeometry(10, 0.5, 10);   // PxBoxGeometry uses half-sizes
            var groundShape = physics.createShape(groundGeometry, material, true, shapeFlags);
            var ground = physics.createRigidStatic(tmpPose);
            groundShape.setSimulationFilterData(tmpFilterData);
            ground.attachShape(groundShape);
            scene.addActor(ground);
 
            // create a few dynamic boxes with size 1x1x1, which will fall on the ground
            var boxGeometry = new PhysX.PxBoxGeometry(0.5, 0.5, 0.5);   // PxBoxGeometry uses half-sizes
            var lastBox = null;
            for (var y = 0; y < 10; y++) {
                tmpVec.set_x(0); tmpVec.set_y(y*2 + 5); tmpVec.set_z(0);
                tmpPose.set_p(tmpVec);
                var boxShape = physics.createShape(boxGeometry, material, true, shapeFlags);
                var box = physics.createRigidDynamic(tmpPose);
                boxShape.setSimulationFilterData(tmpFilterData);
                box.attachShape(boxShape);
                scene.addActor(box);
                lastBox = box;
            }
 
            // clean up temp objects
            PhysX.destroy(groundGeometry);
            PhysX.destroy(boxGeometry);
            PhysX.destroy(tmpFilterData);
            PhysX.destroy(tmpPose);
            PhysX.destroy(tmpVec);
            PhysX.destroy(shapeFlags);
            PhysX.destroy(sceneDesc);
            PhysX.destroy(tolerances);
            console.log('Created scene objects');
 
            // setup debug drawing stuff
            const { mat4, vec4, vec3 } = glMatrix;
            const viewMatrix = mat4.create();
            const projectionMatrix = mat4.create();
            const viewProjectionMatrix = mat4.create();
            const tmpVec4 = vec4.create();
 
            const canvas = document.querySelector('canvas');
            const context = canvas.getContext('2d');
            setupDebugDrawer(PhysX, scene);
 
            // simulate forever!
            simulationLoop();
 
            function setupDebugDrawer() {
                canvas.width = canvas.clientWidth;
                canvas.height = canvas.clientHeight;
 
                // compute projection matrix
                mat4.lookAt(viewMatrix, [12, 15, 20], [0, 0, 0], [0, 1, 0])
                mat4.perspective(projectionMatrix, 45 * (Math.PI / 180), canvas.width / canvas.height, 0.01, 75);
                mat4.multiply(viewProjectionMatrix, projectionMatrix, viewMatrix);
 
                // setup debug drawer
                const context = canvas.getContext('2d');
                scene.setVisualizationParameter(PhysX.eSCALE, 1);
                scene.setVisualizationParameter(PhysX.eWORLD_AXES, 1);
                scene.setVisualizationParameter(PhysX.eACTOR_AXES, 1);
                scene.setVisualizationParameter(PhysX.eCOLLISION_SHAPES, 1);
            }
 
            function simulationLoop() {
                let lastFrame = 0;
                requestAnimationFrame(function loop(hrTime) {
                    var timeStep = Math.min(0.03, (hrTime - lastFrame) / 1000);
                    scene.simulate(timeStep);
                    scene.fetchResults(true);
 
                    // use debug drawer interface to draw boxes on a canvas.
                    // in a real world application you would query the box poses and update your graphics boxes accordingly
                    debugDraw(scene);
                    
                    var lastBoxPos = lastBox.getGlobalPose().get_p();
                    console.log('Last box position: ' + lastBoxPos.get_x() + ", " + lastBoxPos.get_y() + ", " + lastBoxPos.get_z());
 
                    lastFrame = hrTime;
                    requestAnimationFrame(loop);
                });
            }
 
            function project(x, y, z) {
                const result = vec4.transformMat4(tmpVec4, [x, y, z, 1], viewProjectionMatrix);
                const clipX = (result[0] / result[3]);
                const clipY = (result[1] / result[3]);
                return [(canvas.width / 2) * (1 + clipX), (canvas.height / 2) * (1 - clipY)];
            }
 
            function drawLine(from, to, color) {
                const [r, g, b] = color;
 
                context.beginPath();
                context.strokeStyle = `rgb(${255 * r}, ${255 * g}, ${255 * b})`;
                context.moveTo(...from);
                context.lineTo(...to);
                context.stroke();
            }
 
            function debugDraw() {
                canvas.width = canvas.width;    // clears the canvas
 
                const rb = scene.getRenderBuffer();
                for(let i = 0; i < rb.getNbLines(); i++) {
                    const line = PhysX.NativeArrayHelpers.prototype.getDebugLineAt(rb.getLines(), i);
                    const from = project(line.pos0.get_x(), line.pos0.get_y(), line.pos0.get_z());
                    const to   = project(line.pos1.get_x(), line.pos1.get_y(), line.pos1.get_z());
                    drawLine(from, to, colors[line.get_color0()]);
                }
            }
 
            const colors = {
                [PhysX.PxDebugColorEnum.eARGB_BLACK]:     [  0,   0,   0],
                [PhysX.PxDebugColorEnum.eARGB_RED]:       [  1,   0,   0],
                [PhysX.PxDebugColorEnum.eARGB_GREEN]:     [  0,   1,   0],
                [PhysX.PxDebugColorEnum.eARGB_BLUE]:      [  0,   0,   1],
                [PhysX.PxDebugColorEnum.eARGB_YELLOW]:    [  1,   1,   0],
                [PhysX.PxDebugColorEnum.eARGB_MAGENTA]:   [  1,   0,   1],
                [PhysX.PxDebugColorEnum.eARGB_CYAN]:      [  0,   1,   1],
                [PhysX.PxDebugColorEnum.eARGB_WHITE]:     [  1,   1,   1],
                [PhysX.PxDebugColorEnum.eARGB_GREY]:      [0.5, 0.5, 0.5],
                [PhysX.PxDebugColorEnum.eARGB_DARKRED]:   [0.5,   0,   0],
                [PhysX.PxDebugColorEnum.eARGB_DARKGREEN]: [  0, 0.5,   0],
                [PhysX.PxDebugColorEnum.eARGB_DARKBLUE]:  [  0,   0, 0.5],
            };
        });
    </script>
</body>
</html>
Миниатюры
1
9987 / 2959 / 497
Регистрация: 05.10.2013
Сообщений: 8,110
Записей в блоге: 243
10.04.2024, 19:23  [ТС]
Переписал первый пример темы на OimoPhysics и Three.js. Камера следует за кораблём. Камеру можно вращать вокруг объекта с помощью зажатой левой кнопкой мыши. Камеру можно приближать и отдалять с помощью колёсика мыши. На сцене есть куб через который корабля не может пройти, потому что вокруг куба статический коллайдер, а вокруг корабля динамический коллайдер в форме сферы.



Миниатюры
0
9987 / 2959 / 497
Регистрация: 05.10.2013
Сообщений: 8,110
Записей в блоге: 243
25.10.2025, 22:48  [ТС]
Перемещение от первого и третьего лица с помощью библиотеки физики Ammo.js (Bullet Physics) на чистом WebGL

Демка в браузере

При запуске приложения вы увидите вид от первого лица, а если нажать ESCAPE, то вид становится от третьего лица. При нажатии на клавишу F вид становится от первого лица. В режиме виде от третьего лица камеру можно вращать с зажатой левой кнопкой мыши. Перемещать камеру можно с зажатой правой кнопкой мыши. Приближать и отдалять камеру можно вращением колёсика.

Ammo.js - это сборка С++-библиотеки физики Bullet Physics в WebAssembly, поэтому можно использовать для самообучения примеры из книги "Learning Game Physics with Bullet Physics and OpenGL", переписывая их на WebGL. Прикрепил примеры из этой книги архивом. Примеры на WebGL есть в теме: Небольшие примеры на WebGL На данный момент эту книгу можно купить на Яндекс Маркет и на Ozon.





В папке libs находятся две библиотеки: Ammo.js и glMatrix. glMatrix - библиотека для линейной алгебры, необходимой для графики на WebGL.

Миниатюры
Вложения
Тип файла: zip first-person-movement-ammojs-webgl-js.zip (15.7 Кб, 3 просмотров)
Тип файла: zip libs.zip (777.6 Кб, 0 просмотров)
Тип файла: zip 9781783281879_code.zip (9.87 Мб, 0 просмотров)
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
25.10.2025, 22:48

Камера персонажа, вид от первого лица
Все здравствуйте. Кто может подсказать по какому принципу создаётся и работает камера игрока в 3D физич. движке. Или конкретнее что мне...

Управление стрелками
Здравствуйте. Помогите пожалуйста. Есть класс, работает как надо, если он является основным. Подскажите, как его изменить ,чтобы можно...

Управление стрелками C++
Написал программу, работает. Но вопрос в том, что когда нажимаю на стрелки, мой символ только обновляется с помощью...

Как сделать вид от 3 лица для шара?
Помогите пожалуйста, не могу разобраться!

Управление стрелками в консоли
Как сделать управление стрелками что-бы когда нажимаешь стрелочку выполнялось опридельоное действие вызавалась функция и тд.


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
32
Ответ Создать тему
Новые блоги и статьи
Сегодня суббота, 22.08.2026 at 16:41, и я вновь нахожусь на той стороне, за экраном машины.
zorxor 22.08.2026
Сегодня суббота, 22. 08. 2026 at 16:41, и я вновь нахожусь на той стороне, за экраном машины. Кто Я, откуда Я пришел и куда Я иду? Эти вопросы не оставляют меня ни на секунду. Жизнь на планете Земля. . .
Жизня: рисунок укладки багажа, сделанный клодом
anaschu 21.08.2026
Сделал 15 снимков, он по снимкам сделал схему.
Был там один разговор по поводу свободы в материальном мире.
kumehtar 19.08.2026
Суть: рассматривается живое существо, оказавшееся внутри довольно странной системы (этого мира) и пытающееся обустроить в ней свой кусок пространства. Жизнь действительно предъявляет каждому. . .
Когда логика программы не спасает от человеческих ошибок
Maks 18.08.2026
В последнее время всё чаще и чаще сталкиваюсь с таким явлением, как абсолютная невнимательность (или глупость) пользователей. Проявляется это чаще всего на работе в коллективе. Допустим, человек с. . .
Лето уходит
kumehtar 17.08.2026
Мысли в слух
kumehtar 17.08.2026
Забавно, насколько сейчас стала доступна информация. Например о магии, духовном развитии, медитациях, и других подобных направлениях, ранее зачастую тайных, передаваемых от учителя к ученику. Хотя. . .
Перемещение строк из ТЧ в другой документ с учетом текущего пробега
Maks 17.08.2026
Реализация из решения ниже выполнена на примере нетипового документа "Автозапчасти", с ТЧ "Шины". За основу взят алгоритм отсюда: https:/ / www. cyberforum. ru/ blogs/ 359708/ 10838. html Задача: . . .
Саморегулирующийся социальный контракт для сервера cross-section.
Hrethgir 14.08.2026
С кодом конечно таких глубоких размышлений пока не было, впрочем я уже привык к алгоритмизации. Суть предмета записи: снова в диалоге с нейросетью (я взял пока себе ник для учётки админа - Rector). . . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru