-
Notifications
You must be signed in to change notification settings - Fork 0
/
PlayerActor.cpp
513 lines (438 loc) · 12.6 KB
/
PlayerActor.cpp
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
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
#include "StdAfx.h"
#include "PlayerActor.h"
#include "dxManager.h"
#define COLLISION_MINISCULE_OFFSET 0.01f
void moveAll(float, float);
//sets up the player and tells the person whether they're first or second
PlayerActor::PlayerActor(dxManager* dx, bool isRed, int playerNum) : Actor(dx)
{
animationArray = new SimpleSprite*[numAnimations];
//get a ref to the dxmanager display device to make sprites here
ID3D10Device* pD3DDevice = dx->getPD3DDevice();
playerNumber = playerNum;
dxm = dx;
int numPlayerSprites = 9;
if(isRed)
{
WCHAR* spriteNames[] = {L"Images/RedWalk1.png", L"Images/RedWalk2.png", L"Images/RedWalk3.png"
, L"Images/RedJump.png", L"Images/RedWalk1Left.png", L"Images/RedWalk2Left.png"
, L"Images/RedWalk3Left.png", L"Images/RedJumpLeft.png", L"Images/RedPoof.png"};
//load red sprites
for (int i = 0; i < numPlayerSprites; i ++)
{
dx->MakeSprite(spriteNames[i], &animationArray[i]);
animationArray[i]->Visible = false;
}
}
else
{
WCHAR* spriteNames[] = {L"Images/BlueWalk1.png", L"Images/BlueWalk2.png", L"Images/BlueWalk3.png"
, L"Images/BlueJump.png", L"Images/BlueWalk1Left.png", L"Images/BlueWalk2Left.png"
, L"Images/BlueWalk3Left.png", L"Images/BlueJumpLeft.png", L"Images/BluePoof.png"};
for (int i = 0; i < numPlayerSprites; i ++)
{
dx->MakeSprite(spriteNames[i], &animationArray[i]);
animationArray[i]->Visible = false;
}
}
//make the starting sprite the standing still, right sprite.
mySprite = animationArray[1];
SetWidthHeight();
facingRight = true;
moving = false;
movingIndex = 0;
//gravity variables
xVelocity = 0;
yVelocity = 0;
isOnGround = true;
canMove = true;
dead = false;
respawnTime = 2000;
//jumpHeld = false;
totalTimePassed = 0.0f;
}
//deconstructor
PlayerActor::~PlayerActor(void)
{
}
//makes the playyer jump
void PlayerActor::Jump()
{
jumpPower = MAX_JUMP_POWER_TIME;
yVelocity = MAX_JUMP_SPEED;
isOnGround = false;
}
//sets the variables to load the animation sprites
void PlayerActor::SetAnimSprite(int idx)
{
mySprite->Visible = false;
mySprite = animationArray[idx];
mySprite->Visible = true;
}
//kills the player
void PlayerActor::Die()
{
prevTime = GetCurrentTime();
dead = true;
canMove = false;
}
// we had encountered issues with the sprite not being scaled correctly; this is a manual method of fixing that by altering
// the sprite width and height.
void PlayerActor::FixFrameScales(float adjustX, float adjustY)
{
//cycles through the sprites to resize
for(int i=0; i < numAnimations; i++)
{
animationArray[i]->AdjustScaling(0.4f, 0.4f);
//animationArray[i]->AdjustScaling(adjustX, adjustY);
float adjustX2 = 1.0f;
float adjustY2 = 1.0f;
int height = animationArray[i]->spriteHeight;
int width = animationArray[i]->spriteWidth;
if(width > height)
{
adjustY2 = ((float)height)/width;
}
else if(width < height)
{
adjustX2 = ((float)width)/height;
}
animationArray[i]->AdjustScaling(adjustX2, adjustY2);
}
}
//checks for wall collisions
void PlayerActor::doCollision(float* nextX, float* nextY)
{
// wall collision
// we will loop through and find the "standing wall" again. If there suddenly isn't one,
// the player will start falling. (this should be set in TestWallCollision)
standingWall = 0;
float prevNextY = *nextY;
list<ICollidable*>* walls = dxMan->GetWalls();
for (list<ICollidable*>::iterator iter = walls->begin(); iter != walls->end(); ++iter)
{
ICollidable* wallTest = *iter;
bool hit = TestWallCollision(wallTest, nextX, nextY);
if (hit)
{
wallTest->OnPlayerCollide(this);
}
}
if (standingWall == 0)
{
isOnGround = false;
}
}
//checks for trap collisions
void PlayerActor::doTrapCollision(float* nextX, float* nextY)
{
//loop through the traps and check collision
vector<Trap*>* traps = dxMan->GetTraps();
for(vector<Trap*>::iterator iter = traps->begin(); iter != traps->end(); ++iter)
{
Trap* trapTest = *iter;
bool hit = TestTrapCollision(trapTest, nextX, nextY);
if (hit)
{
//check if trap is dangerous at this time
if(trapTest->trapWillKill() || trapTest->trapType == 3)
{
trapTest->OnPlayerCollide(this);
}
}
}
}
//this function takes two potential variables for the next position, and checks
//for any collision with the current wall. Since it takes pointers, it modifies
//the position to correct for collision.
bool PlayerActor::TestWallCollision(ICollidable* wall, float* nextX, float* nextY)
{
// collision detection done using seperating axis theorem, summary found here:
// http://www.metanetsoftware.com/technique/tutorialA.html
float wallHalfX, wallHalfY;
wall->GetHalfWidth(&wallHalfX, &wallHalfY);
float wallX, wallY;
wall->GetPosition(&wallX, &wallY);
float differenceX, differenceY, signX, signY; // green lines, figure 3
differenceX = *nextX - wallX; // distance FROM wall TO player
signX = sign(differenceX);
differenceX = abs(differenceX);
// maximum amount the two objects can be seperated, otherwise there is a collision
float maxSeperX = halfXWidth + wallHalfX;
float maxSeperY = halfYWidth + wallHalfY;
//if we're farther than the max X seperation distance, we're not colliding.
if (differenceX > maxSeperX)
{
return false;
}
//if we're here, we know the X plane is colliding, so check Y.
differenceY = *nextY - wallY;
signY = sign(differenceY);
differenceY = abs(differenceY);
// check if this wall is under our feet, set it as the standing wall.
if (isOnGround)
{
if (signY == 1.0 && differenceY < maxSeperY + COLLISION_MINISCULE_OFFSET)
{
standingWall = wall;
}
}
if (differenceY > maxSeperY)
{
return false;
}
//now that we know we're colliding, do special adjustments
float collisionAmtX = maxSeperX - differenceX;
float collisionAmtY = maxSeperY - differenceY;
//check if they're colliding with a wall on the side
if (collisionAmtY > collisionAmtX)
{
// fix along X axis
xVelocity = 0.0f;
*nextX = *nextX + collisionAmtX * signX;
}
else if (!isOnGround) // only accept Y collisions if we aren't already on the ground
{
// fix along Y axis
if (yVelocity < 0.0f && signY == 1.0f && !isOnGround)
{
// just landed
isOnGround = true;
standingWall = wall;
}
if (yVelocity > 0.0f && signY == -1.0f && !isOnGround)
{
jumpPower = 0.0f;
}
yVelocity = 0.0f;
*nextY = *nextY + collisionAmtY * signY;
}
return true;
}
bool PlayerActor::TestTrapCollision(Trap* trap, float* nextX, float* nextY)
{
// uses a variant of seperating axis that doesn't bother computing collision-handling
// (we don't need to relocate a doomed player)
float trapHalfX, trapHalfY;
trap->GetHalfWidth(&trapHalfX, &trapHalfY);
float trapX, trapY;
trap->GetPosition(&trapX, &trapY);
float differenceX, differenceY; // green lines, figure 3
differenceX = *nextX - trapX; // distance FROM trap TO player
differenceY = *nextY - trapY;
differenceX = abs(differenceX);
differenceY = abs(differenceY);
// maximum amount the two objects can be seperated, otherwise there is a collision
float maxSeperX = halfXWidth + trapHalfX;
float maxSeperY = halfYWidth + trapHalfY;
if (differenceX > maxSeperX)
{
return false;
}
// now we know X is colliding
if (differenceY > maxSeperY)
{
return false;
}
//if the trap is currently dangerous
if(trap->trapWillKill())
{
yVelocity = 0.0f;
jumpPower = 0.0f;
}
return true;
}
//updates the player actor in time by the specified
//elapsing of time
void PlayerActor::Update(float timePassed)
{
if (!timePassed) return;
totalTimePassed += timePassed;
cout << "T Passed: " << totalTimePassed;
//check the collisions of walls and traps, as well as movement and gravity
doUpdatePhysics(timePassed);
}
void PlayerActor::doUpdatePhysics(float timePassed)
{
float xMove = controlsSource->getXMove();
bool jump = controlsSource->getJump();
//keep track of which direction we were facing last
//check if a current direction is held down
bool wasMoving = moving; //check if we were moving in the last frame; so we can set moving timer if not and current are moving
//in the case we can't move or are dead, we'll just stay where we are
float nextX = xPosition;
float nextY = yPosition;
//only do things if the character isn't dead
if(!dead)
{
//check if we are holding left or right; whether we are moving
if(controlsSource->getLeft())
{
facingRight = false;
moving = true;
}
else if(controlsSource->getRight())
{
facingRight = true;
moving = true;
}
else
{
moving = false;
}
//if we're moving and previously weren't, set the 'timer' for motion animation
if(!wasMoving && moving)
{
prevTime = GetCurrentTime();
}
if (jump) // if jump held
{
//only jump if the player is allowed to currently move
if(canMove)
{
//jumpHeld = true;
if (isOnGround && !jumpLastFrame)
{
Jump();
}
if (jumpPower > 0.0f)
{
jumpPower -= timePassed;
yVelocity = MAX_JUMP_SPEED;
}
jumpLastFrame = true;
}
}
else
{
// jump button is not held
if (!isOnGround)
{
jumpPower = 0.0f;
}
jumpLastFrame = false;
}
xVelocity += X_ACCEL_RATE * xMove * MAX_SPEED;
xVelocity = min(max(xVelocity, -MAX_SPEED), MAX_SPEED);
if (!xMove)
{
if (xVelocity > 0.0f)
{
xVelocity -= X_FRICTION * timePassed;
}
else if (xVelocity < 0.0f)
{
xVelocity += X_FRICTION * timePassed;
}
if (abs(xVelocity) < 0.01f)
{
xVelocity = 0.0f;
}
}
if(!isOnGround && jumpPower <= 0.0f)
{
yVelocity -= FALL_GRAVITY * timePassed;
yVelocity = max(MAX_FALL_SPEED, yVelocity);
}
//else we'll move according to velocity
if(canMove && !dead)
{
nextX = xPosition + (timePassed * xVelocity);
nextY = yPosition + (timePassed * yVelocity);
doCollision(&nextX, &nextY);
}
//CHARACTER ANIMATION
//check if in the air, either falling or jumping. Also check direction.
if(!isOnGround)
{
if(facingRight)
SetAnimSprite(3);
else
SetAnimSprite(7);
}
else //If we're not in the air, we're on the ground! Check for motion.
{
//if We're moving...
if(moving)
{
//check how long we've been moving
currentTime = GetCurrentTime();
int timeElapsed = currentTime - prevTime;
//if its been more than stepTime since our last animation update...
if(timeElapsed >= stepTime)
{
//increment our animation index
movingIndex++;
//loop around if at max (goes 0 1 2 3, with 3 being frame '1' so it alternates. See below.)
if(movingIndex > 3)
{
movingIndex = 0;
}
//reset time counter
prevTime = currentTime;
}
//if we're on frame '4' of our animation, use the middle idle animation that goes between walk frames.
if(movingIndex == 3)
{
if(facingRight)
SetAnimSprite(1);
else
SetAnimSprite(5);
}
//otherwise just use the movingIndex location. If facing left offset by 4.
else
{
if(facingRight)
SetAnimSprite(movingIndex);
else
SetAnimSprite(4+movingIndex);
}
}
else //no motion
{
//we're not jumping, and not moving, we're standing still. So change the sprite.
if(facingRight)
SetAnimSprite(1);
else
SetAnimSprite(5);
}
}
doTrapCollision(&nextX, &nextY);
SetPosition(nextX, nextY);
}
else //if we're dead, show the dead animation for half the respawn time
{
//show the death sprite
SetAnimSprite(8);
SetPosition(nextX, nextY);
currentTime = GetCurrentTime();
int timeElapsed = currentTime - prevTime;
//once the respawn time is over, the player can move again and isn't dead.
if(timeElapsed >= respawnTime)
{
canMove = true;
dead = false;
}
//pause for half the respawn time to show the player where they died
else if(timeElapsed > respawnTime/2)
{
//when we're at half respawn time passed, move the player to the last checkpoint
dxm->KillPlayer(this, playerNumber);
}
}
}
//Sets which 'controller' object to check controls from.
//This determines where to get input from; WASD, arrows, controller
void PlayerActor::SetController(IController* controller)
{
controlsSource = controller;
}
IController* PlayerActor::GetController(void)
{
return controlsSource;
}
//Multiplies the current scale by the specified amount.
void PlayerActor::AdjustScale(float x, float y)
{
mySprite->AdjustScaling(x,y);
}