5158 / 2770 / 465
Регистрация: 05.10.2013
Сообщений: 7,310
Записей в блоге: 147
1

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

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

Author24 — интернет-сервис помощи студентам
Цитата Сообщение от 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. Изучайте код, экспериментируйте, задавайте вопросы.

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


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 Кб, 14 просмотров)
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
13.01.2022, 23:12
Ответы с готовыми решениями:

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

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

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

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

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

30
5158 / 2770 / 465
Регистрация: 05.10.2013
Сообщений: 7,310
Записей в блоге: 147
24.10.2022, 12:23  [ТС] 21
Author24 — интернет-сервис помощи студентам
Моё краткое введение в WebGL на JavaScript
0
5158 / 2770 / 465
Регистрация: 05.10.2013
Сообщений: 7,310
Записей в блоге: 147
28.10.2022, 07:18  [ТС] 22
Ошибка в сообщении #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
5158 / 2770 / 465
Регистрация: 05.10.2013
Сообщений: 7,310
Записей в блоге: 147
22.11.2022, 19:49  [ТС] 23
Конвертация 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"
}
Миниатюры
Управление героем стрелками в 3D, вид от 3-го лица, на WebGL
Вложения
Тип файла: zip ammo-es-module-rollup-js.zip (3.9 Кб, 0 просмотров)
Тип файла: zip ammo-es.zip (434.0 Кб, 0 просмотров)
1
5158 / 2770 / 465
Регистрация: 05.10.2013
Сообщений: 7,310
Записей в блоге: 147
29.11.2022, 19:59  [ТС] 24
Загрузка физического движка 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
5158 / 2770 / 465
Регистрация: 05.10.2013
Сообщений: 7,310
Записей в блоге: 147
18.01.2023, 15:42  [ТС] 25
В примерах выше перед тегом <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
5158 / 2770 / 465
Регистрация: 05.10.2013
Сообщений: 7,310
Записей в блоге: 147
18.01.2023, 22:10  [ТС] 26
Для авторитарного сервера нужен физический движок на сервере, если делать перемещение от третьего лица с мультиплеером. Статья на эту тему: 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
5158 / 2770 / 465
Регистрация: 05.10.2013
Сообщений: 7,310
Записей в блоге: 147
28.05.2023, 17:05  [ТС] 27
На базе примера из первого сообщения темы реализовал простейший мультиплеер, используя WebSockets (пакет https://www.npmjs.com/package/ws) и Node.js. Отправляю ввод с клавиатуры на сервер, который ретранслирует его на другие клиенты. Используется WebGL 1.0, glMatrix и OimoPhysics. Извлёк оригинальные модели, текстуры и анимации, используя RE1MV. Создал скелетную анимацию без скиннинга с помощью Blender Python API. Скиннинг дорог по ресурсам и не нужен для моделей этой игры, в которой все части анимационных моделей - это отдельные объекты.



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

1
5158 / 2770 / 465
Регистрация: 05.10.2013
Сообщений: 7,310
Записей в блоге: 147
06.06.2023, 10:52  [ТС] 28
Доделал первую версию демки с авторитарным сервером, где можно перемещаться в кооперативном и одиночном режим от первого и третьего лица. Сделал для показа моделей, анимации сделанных в Blender и интерактивных взаимодействий на WebGL.

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



Миниатюры
Управление героем стрелками в 3D, вид от 3-го лица, на WebGL
0
5158 / 2770 / 465
Регистрация: 05.10.2013
Сообщений: 7,310
Записей в блоге: 147
05.03.2024, 13:32  [ТС] 29
Пример на 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>
Миниатюры
Управление героем стрелками в 3D, вид от 3-го лица, на WebGL
0
5158 / 2770 / 465
Регистрация: 05.10.2013
Сообщений: 7,310
Записей в блоге: 147
05.03.2024, 13:44  [ТС] 30
Пример рисования коллайдеров физического движка 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>
Миниатюры
Управление героем стрелками в 3D, вид от 3-го лица, на WebGL
1
5158 / 2770 / 465
Регистрация: 05.10.2013
Сообщений: 7,310
Записей в блоге: 147
10.04.2024, 19:23  [ТС] 31
Переписал первый пример темы на OimoPhysics и Three.js. Камера следует за кораблём. Камеру можно вращать вокруг объекта с помощью зажатой левой кнопкой мыши. Камеру можно приближать и отдалять с помощью колёсика мыши. На сцене есть куб через который корабля не может пройти, потому что вокруг куба статический коллайдер, а вокруг корабля динамический коллайдер в форме сферы.



Миниатюры
Управление героем стрелками в 3D, вид от 3-го лица, на WebGL
Вложения
Тип файла: zip ship-movement-oimophysics-rollup-threejs-js.zip (684.2 Кб, 0 просмотров)
0
10.04.2024, 19:23
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
10.04.2024, 19:23
Помогаю со студенческими работами здесь

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

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

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

Управление стрелками в игре
В продолжение этой темы Почему я нажимаю A W S D ,у меня ни чего не происходит,если же я потыкаю...

Управление стрелками на Android
Добрый день форумчане. Мучаюсь... не могу ни как реализовать управление стрелками по осям X и Y под...

Управление стрелками клавиатуры
Нужно присвоить действия при нажатии стрелок на клавиатуре. Пояснения Unity не смог понять....


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

Или воспользуйтесь поиском по форуму:
31
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru