FPSActor.cpp 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. // ----------------------------------------------------------------
  2. // From Game Programming in C++ by Sanjay Madhav
  3. // Copyright (C) 2017 Sanjay Madhav. All rights reserved.
  4. //
  5. // Released under the BSD License
  6. // See LICENSE in root directory for full details.
  7. // ----------------------------------------------------------------
  8. #include "FPSActor.h"
  9. #include "MoveComponent.h"
  10. #include "SDL/SDL_scancode.h"
  11. #include "Renderer.h"
  12. #include "AudioSystem.h"
  13. #include "Game.h"
  14. #include "AudioComponent.h"
  15. #include "FPSCamera.h"
  16. #include "MeshComponent.h"
  17. #include "BallActor.h"
  18. #include "BoxComponent.h"
  19. #include "PlaneActor.h"
  20. FPSActor::FPSActor(Game* game)
  21. :Actor(game)
  22. {
  23. mMoveComp = new MoveComponent(this);
  24. mAudioComp = new AudioComponent(this);
  25. mLastFootstep = 0.0f;
  26. mFootstep = mAudioComp->PlayEvent("event:/Footstep");
  27. mFootstep.SetPaused(true);
  28. mCameraComp = new FPSCamera(this);
  29. mFPSModel = new Actor(game);
  30. mFPSModel->SetScale(0.75f);
  31. mMeshComp = new MeshComponent(mFPSModel);
  32. mMeshComp->SetMesh(game->GetRenderer()->GetMesh("Assets/Rifle.gpmesh"));
  33. // Add a box component
  34. mBoxComp = new BoxComponent(this);
  35. AABB myBox(Vector3(-25.0f, -25.0f, -87.5f),
  36. Vector3(25.0f, 25.0f, 87.5f));
  37. mBoxComp->SetObjectBox(myBox);
  38. mBoxComp->SetShouldRotate(false);
  39. }
  40. void FPSActor::UpdateActor(float deltaTime)
  41. {
  42. Actor::UpdateActor(deltaTime);
  43. FixCollisions();
  44. // Play the footstep if we're moving and haven't recently
  45. mLastFootstep -= deltaTime;
  46. if ((!Math::NearZero(mMoveComp->GetForwardSpeed()) ||
  47. !Math::NearZero(mMoveComp->GetStrafeSpeed())) &&
  48. mLastFootstep <= 0.0f)
  49. {
  50. mFootstep.SetPaused(false);
  51. mFootstep.Restart();
  52. mLastFootstep = 0.5f;
  53. }
  54. // Update position of FPS model relative to actor position
  55. const Vector3 modelOffset(Vector3(10.0f, 10.0f, -10.0f));
  56. Vector3 modelPos = GetPosition();
  57. modelPos += GetForward() * modelOffset.x;
  58. modelPos += GetRight() * modelOffset.y;
  59. modelPos.z += modelOffset.z;
  60. mFPSModel->SetPosition(modelPos);
  61. // Initialize rotation to actor rotation
  62. Quaternion q = GetRotation();
  63. // Rotate by pitch from camera
  64. q = Quaternion::Concatenate(q, Quaternion(GetRight(), mCameraComp->GetPitch()));
  65. mFPSModel->SetRotation(q);
  66. }
  67. void FPSActor::ActorInput(const uint8_t* keys)
  68. {
  69. float forwardSpeed = 0.0f;
  70. float strafeSpeed = 0.0f;
  71. // wasd movement
  72. if (keys[SDL_SCANCODE_W])
  73. {
  74. forwardSpeed += 400.0f;
  75. }
  76. if (keys[SDL_SCANCODE_S])
  77. {
  78. forwardSpeed -= 400.0f;
  79. }
  80. if (keys[SDL_SCANCODE_A])
  81. {
  82. strafeSpeed -= 400.0f;
  83. }
  84. if (keys[SDL_SCANCODE_D])
  85. {
  86. strafeSpeed += 400.0f;
  87. }
  88. mMoveComp->SetForwardSpeed(forwardSpeed);
  89. mMoveComp->SetStrafeSpeed(strafeSpeed);
  90. // Mouse movement
  91. // Get relative movement from SDL
  92. int x, y;
  93. SDL_GetRelativeMouseState(&x, &y);
  94. // Assume mouse movement is usually between -500 and +500
  95. const int maxMouseSpeed = 500;
  96. // Rotation/sec at maximum speed
  97. const float maxAngularSpeed = Math::Pi * 8;
  98. float angularSpeed = 0.0f;
  99. if (x != 0)
  100. {
  101. // Convert to ~[-1.0, 1.0]
  102. angularSpeed = static_cast<float>(x) / maxMouseSpeed;
  103. // Multiply by rotation/sec
  104. angularSpeed *= maxAngularSpeed;
  105. }
  106. mMoveComp->SetAngularSpeed(angularSpeed);
  107. // Compute pitch
  108. const float maxPitchSpeed = Math::Pi * 8;
  109. float pitchSpeed = 0.0f;
  110. if (y != 0)
  111. {
  112. // Convert to ~[-1.0, 1.0]
  113. pitchSpeed = static_cast<float>(y) / maxMouseSpeed;
  114. pitchSpeed *= maxPitchSpeed;
  115. }
  116. mCameraComp->SetPitchSpeed(pitchSpeed);
  117. }
  118. void FPSActor::Shoot()
  119. {
  120. // Get direction vector
  121. Vector3 start, dir;
  122. GetGame()->GetRenderer()->GetScreenDirection(start, dir);
  123. // Spawn a ball
  124. BallActor* ball = new BallActor(GetGame());
  125. ball->SetPlayer(this);
  126. ball->SetPosition(start + dir*20.0f);
  127. // Rotate the ball to face new direction
  128. ball->RotateToNewForward(dir);
  129. // Play shooting sound
  130. mAudioComp->PlayEvent("event:/Shot");
  131. }
  132. void FPSActor::SetFootstepSurface(float value)
  133. {
  134. // Pause here because the way I setup the parameter in FMOD
  135. // changing it will play a footstep
  136. mFootstep.SetPaused(true);
  137. mFootstep.SetParameter("Surface", value);
  138. }
  139. void FPSActor::SetVisible(bool visible)
  140. {
  141. mMeshComp->SetVisible(visible);
  142. }
  143. void FPSActor::FixCollisions()
  144. {
  145. // Need to recompute my world transform to update world box
  146. ComputeWorldTransform();
  147. const AABB& playerBox = mBoxComp->GetWorldBox();
  148. Vector3 pos = GetPosition();
  149. auto& planes = GetGame()->GetPlanes();
  150. for (auto pa : planes)
  151. {
  152. // Do we collide with this PlaneActor?
  153. const AABB& planeBox = pa->GetBox()->GetWorldBox();
  154. if (Intersect(playerBox, planeBox))
  155. {
  156. // Calculate all our differences
  157. float dx1 = planeBox.mMax.x - playerBox.mMin.x;
  158. float dx2 = planeBox.mMin.x - playerBox.mMax.x;
  159. float dy1 = planeBox.mMax.y - playerBox.mMin.y;
  160. float dy2 = planeBox.mMin.y - playerBox.mMax.y;
  161. float dz1 = planeBox.mMax.z - playerBox.mMin.z;
  162. float dz2 = planeBox.mMin.z - playerBox.mMax.z;
  163. // Set dx to whichever of dx1/dx2 have a lower abs
  164. float dx = Math::Abs(dx1) < Math::Abs(dx2) ?
  165. dx1 : dx2;
  166. // Ditto for dy
  167. float dy = Math::Abs(dy1) < Math::Abs(dy2) ?
  168. dy1 : dy2;
  169. // Ditto for dz
  170. float dz = Math::Abs(dz1) < Math::Abs(dz2) ?
  171. dz1 : dz2;
  172. // Whichever is closest, adjust x/y position
  173. if (Math::Abs(dx) <= Math::Abs(dy) && Math::Abs(dx) <= Math::Abs(dz))
  174. {
  175. pos.x += dx;
  176. }
  177. else if (Math::Abs(dy) <= Math::Abs(dx) && Math::Abs(dy) <= Math::Abs(dz))
  178. {
  179. pos.y += dy;
  180. }
  181. else
  182. {
  183. pos.z += dz;
  184. }
  185. // Need to set position and update box component
  186. SetPosition(pos);
  187. mBoxComp->OnUpdateWorldTransform();
  188. }
  189. }
  190. }