-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathPlayerMovement.hx
More file actions
349 lines (295 loc) · 9.75 KB
/
PlayerMovement.hx
File metadata and controls
349 lines (295 loc) · 9.75 KB
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
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
// just helper class to hold object position on plane
class Point
{
public var x:Float;
public var y:Float;
public function new(x:Float = 0, y:Float = 0)
{
this.x = x;
this.y = y;
}
}
// just helper class to hold world bounds
class Rect
{
public var x:Float;
public var y:Float;
public var width:Float;
public var height:Float;
public function new(x:Float = 0, y:Float = 0, width:Float = 0, height:Float = 0)
{
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
}
/**
* This example uses `differ` library for collision detection.
* You can install it from git: `haxelib git differ https://github.com/snowkit/differ.git`
* (haxelib version of `differ` currently is incompatible with Haxe 4.0.0.5preview or higher)
**/
class PlayerMovement extends hxd.App {
// Light source for our scene
var light : h3d.scene.DirLight;
// player object, which we will controll
var player : h3d.scene.Object;
// just a cube representing floor
var floor: h3d.scene.Object;
// position of our player in the world
var playerPosition:Point;
// movement direction of player (angle in radians)
var playerDirection:Float;
// distance from camera to player
var cameraDistance:Float;
// camera height from floor level
var cameraHeight:Float;
// model cache, used for model and animation loading
var cache : h3d.prim.ModelCache;
// tells us whether player is currently moving or not
var isMoving:Bool = false;
// current movement speed of player
var movementSpeed:Float = 0.0;
// some player movement characteristics
var walkSpeed:Float = 0.04;
var turnSpeed:Float = 0.01;
// world's bounding rectangle
var worldBounds:Rect;
var walkingAnimation:h3d.anim.Animation;
// Cylinder mesh to visualize collision bounds of player object
var cylinder:h3d.scene.Mesh;
// Actual collision object for player object
var circle:differ.shapes.Circle;
// Collision bounds for every obstacle in the scene
var obstacles:Array<differ.shapes.Polygon>;
var interactive:h3d.scene.Interactive;
override function init()
{
// let's create and setup lighing on the scene
light = new h3d.scene.DirLight(new h3d.Vector( 0.3, -0.4, -0.9), s3d);
light.enableSpecular = true;
light.color.set(0.28, 0.28, 0.28);
s3d.lightSystem.ambientLight.set(0.74, 0.74, 0.74);
// load model and create player object
cache = new h3d.prim.ModelCache();
player = cache.loadModel(hxd.Res.Model);
player.scale(1 / 20);
player.rotate(0, 0, Math.PI / 2);
s3d.addChild(player);
playerPosition = new Point(player.x, player.y);
playerDirection = Math.PI / 2;
// load walking animation from the cache
walkingAnimation = cache.loadAnimation(hxd.Res.Model);
worldBounds = new Rect(-10, -10, 20, 20);
// create cube primitive which we will use for floor object
var prim = new h3d.prim.Cube(worldBounds.width, worldBounds.height, 1.0);
// translate it so its center will be at the center of the cube
// prim.translate( -0.5 * worldBounds.width, -0.5 * worldBounds.height, -0.5);
// unindex the faces to create hard edges normals
prim.unindex();
// add face normals
prim.addNormals();
// add texture coordinates
prim.addUVs();
// access the logo resource and convert it to a texture
var tex = hxd.Res.load("hxlogo.png").toImage().toTexture();
// create a material with this texture
var mat = h3d.mat.Material.create(tex);
// create textured floor
var floor = new h3d.scene.Mesh(prim, mat, s3d);
// set the cube color
floor.material.color.setColor(0xFFB280);
// put it under player
floor.x = worldBounds.x;
floor.y = worldBounds.y;
floor.z = -1;
obstacles = [];
// create wall obstacles for our location (they won't have visual representation):
// 1. upper wall
obstacles.push(differ.shapes.Polygon.rectangle(worldBounds.x - 1, worldBounds.y - 1, worldBounds.width + 1, 1, false));
// 2. right wall
obstacles.push(differ.shapes.Polygon.rectangle(worldBounds.x + worldBounds.width, worldBounds.y - 1, 1, worldBounds.height + 1, false));
// 3. bottom wall
obstacles.push(differ.shapes.Polygon.rectangle(worldBounds.x, worldBounds.y + worldBounds.height, worldBounds.width + 1, 1, false));
// 4. left wall
obstacles.push(differ.shapes.Polygon.rectangle(worldBounds.x - 1, worldBounds.y, 1, worldBounds.height + 1, false));
// create cylinder primitive to visualize collision shape of character.
var prim = new h3d.prim.Cylinder(12, 0.35, 0.1);
// translate it so its center will be at the bottom's center
// unindex the faces to create hard edges normals
// add face normals
prim.addNormals();
// add texture coordinates
prim.addTCoords();
// create colored mesh
cylinder = new h3d.scene.Mesh(prim, s3d);
// set the cylinder color
cylinder.material.color.setColor(0x00ff00);
cylinder.material.receiveShadows = false;
cylinder.material.mainPass.culling = None;
// create actual collision object for player
circle = new differ.shapes.Circle(0, 0, 0.35);
// let's create obstacles for our level:
// create cube primitive which we will use for obstacle objects
var prim = new h3d.prim.Cube(1.0, 1.0, 1.0);
// unindex the faces to create hard edges normals
prim.unindex();
// add face normals
prim.addNormals();
// add texture coordinates
prim.addUVs();
for (i in 0...50)
{
// create mesh object which will be rendered on the scene
var cube = new h3d.scene.Mesh(prim, s3d);
// set random color
cube.material.color.setColor(Std.int(Math.random() * 0xff0000));
// disable shadows
cube.material.receiveShadows = false;
cube.material.shadows = false;
// scale and place it randomly on the scene
var scale = 0.3 + 0.7 * Math.random(); // scale will be in the range from 0.3 to 1.0
cube.scale(scale);
cube.x = worldBounds.x + Math.random() * (worldBounds.width - scale);
cube.y = worldBounds.y + Math.random() * (worldBounds.height - scale);
// create actual collision object for obstacle
obstacles.push(differ.shapes.Polygon.square(cube.x, cube.y, scale, false));
}
collideWithObstacles();
// setup camera params
cameraDistance = 15;
cameraHeight = 5;
updateCamera();
interactive = new h3d.scene.Interactive(floor.getCollider(), s3d);
interactive.onMove = function(e:hxd.Event)
{
if (hxd.Key.isDown(hxd.Key.MOUSE_LEFT))
{
var dx = e.relX - playerPosition.x;
var dy = e.relY - playerPosition.y;
playerDirection = Math.atan2(dy, dx);
}
}
}
override function update(dt:Float)
{
// check whether player moves forward or backward
var playerSpeed = 0.0;
if (hxd.Key.isDown(hxd.Key.UP))
{
playerSpeed += 1;
}
if (hxd.Key.isDown(hxd.Key.DOWN))
{
playerSpeed -= 1;
}
if (hxd.Key.isDown(hxd.Key.MOUSE_LEFT))
{
playerSpeed = 1;
}
// check if player is running
var runningMultiplicator = 1.0;
if (hxd.Key.isDown(hxd.Key.SHIFT))
{
// running forward should be faster than backward
if (playerSpeed < 0)
{
runningMultiplicator = 1.3;
}
else
{
runningMultiplicator = 2;
}
}
// check if player is turning
if (hxd.Key.isDown(hxd.Key.LEFT))
{
playerDirection -= turnSpeed * runningMultiplicator;
}
if (hxd.Key.isDown(hxd.Key.RIGHT))
{
playerDirection += turnSpeed * runningMultiplicator;
}
if (playerSpeed != 0)
{
// update player's position if its moving
playerSpeed *= runningMultiplicator;
playerPosition.x += Math.cos(playerDirection) * walkSpeed * playerSpeed;
playerPosition.y += Math.sin(playerDirection) * walkSpeed * playerSpeed;
// Check collisions with each obstacle on the scene
collideWithObstacles();
// change player's animation if its speed has been changed
if (movementSpeed != playerSpeed)
{
if (playerSpeed > 0)
{
// playing animation for walking forward
// if player is running or walking, then we need to adjust animations speed
player.playAnimation(walkingAnimation).speed = runningMultiplicator;
}
else
{
// need to play backward animation, but i don't have one
// so i won't play anything :(
}
movementSpeed = playerSpeed;
}
isMoving = true;
}
else
{
// if player isn't moving we need to reset some of its params
if (isMoving)
{
player.stopAnimation();
// or migth play idle animation (use switchAnimation() method)...
}
movementSpeed = 0;
isMoving = false;
}
// update player's rotation
player.setRotation(0, 0, playerDirection + Math.PI / 2);
// and don't forget to update camera's position
updateCamera();
}
function collideWithObstacles()
{
circle.x = playerPosition.x;
circle.y = playerPosition.y;
for (i in 0...obstacles.length)
{
var obstacle = obstacles[i];
// check collision between circle (player) and rectangular obstacle (box or wall)
var collideInfo = differ.Collision.shapeWithShape(circle, obstacle);
if (collideInfo != null)
{
// if there is collision then we need to resolve collision.
// in our case we just move player outside of bounds of the box
circle.x += collideInfo.separationX;
circle.y += collideInfo.separationY;
}
}
playerPosition.x = circle.x;
playerPosition.y = circle.y;
// and finally set player's position
player.x = playerPosition.x;
player.y = playerPosition.y;
// and player's marker position
cylinder.x = playerPosition.x;
cylinder.y = playerPosition.y;
}
function updateCamera()
{
var cameraZ = cameraHeight;
var cameraXYDist = Math.sqrt(cameraDistance * cameraDistance - cameraHeight * cameraHeight);
var cameraXYAngle = Math.PI / 4;
var cameraX = player.x - cameraXYDist * Math.cos(cameraXYAngle);
var cameraY = player.y - cameraXYDist * Math.sin(cameraXYAngle);
s3d.camera.pos.set(cameraX, cameraY, cameraZ);
s3d.camera.target.set(player.x, player.y, player.z);
}
static function main() {
hxd.Res.initEmbed();
new PlayerMovement();
}
}