FPSActor.cpp 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  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 start point (in center of screen on near plane)
  121. Vector3 screenPoint(0.0f, 0.0f, 0.0f);
  122. Vector3 start = GetGame()->GetRenderer()->Unproject(screenPoint);
  123. // Get end point (in center of screen, between near and far)
  124. screenPoint.z = 0.9f;
  125. Vector3 end = GetGame()->GetRenderer()->Unproject(screenPoint);
  126. // Get direction vector
  127. Vector3 dir = end - start;
  128. dir.Normalize();
  129. // Spawn a ball
  130. BallActor* ball = new BallActor(GetGame());
  131. ball->SetPlayer(this);
  132. ball->SetPosition(start + dir*20.0f);
  133. // Rotate the ball to face new direction
  134. ball->RotateToNewForward(dir);
  135. // Play shooting sound
  136. mAudioComp->PlayEvent("event:/Shot");
  137. }
  138. void FPSActor::SetFootstepSurface(float value)
  139. {
  140. // Pause here because the way I setup the parameter in FMOD
  141. // changing it will play a footstep
  142. mFootstep.SetPaused(true);
  143. mFootstep.SetParameter("Surface", value);
  144. }
  145. void FPSActor::SetVisible(bool visible)
  146. {
  147. mMeshComp->SetVisible(visible);
  148. }
  149. void FPSActor::FixCollisions()
  150. {
  151. // Need to recompute my world transform to update world box
  152. ComputeWorldTransform();
  153. const AABB& playerBox = mBoxComp->GetWorldBox();
  154. Vector3 pos = GetPosition();
  155. auto& planes = GetGame()->GetPlanes();
  156. for (auto pa : planes)
  157. {
  158. // Do we collide with this PlaneActor?
  159. const AABB& planeBox = pa->GetBox()->GetWorldBox();
  160. if (Intersect(playerBox, planeBox))
  161. {
  162. // Calculate all our differences
  163. float dx1 = planeBox.mMax.x - playerBox.mMin.x;
  164. float dx2 = planeBox.mMin.x - playerBox.mMax.x;
  165. float dy1 = planeBox.mMax.y - playerBox.mMin.y;
  166. float dy2 = planeBox.mMin.y - playerBox.mMax.y;
  167. float dz1 = planeBox.mMax.z - playerBox.mMin.z;
  168. float dz2 = planeBox.mMin.z - playerBox.mMax.z;
  169. // Set dx to whichever of dx1/dx2 have a lower abs
  170. float dx = Math::Abs(dx1) < Math::Abs(dx2) ?
  171. dx1 : dx2;
  172. // Ditto for dy
  173. float dy = Math::Abs(dy1) < Math::Abs(dy2) ?
  174. dy1 : dy2;
  175. // Ditto for dz
  176. float dz = Math::Abs(dz1) < Math::Abs(dz2) ?
  177. dz1 : dz2;
  178. // Whichever is closest, adjust x/y position
  179. if (Math::Abs(dx) <= Math::Abs(dy) && Math::Abs(dx) <= Math::Abs(dz))
  180. {
  181. pos.x += dx;
  182. }
  183. else if (Math::Abs(dy) <= Math::Abs(dx) && Math::Abs(dy) <= Math::Abs(dz))
  184. {
  185. pos.y += dy;
  186. }
  187. else
  188. {
  189. pos.z += dz;
  190. }
  191. // Need to set position and update box component
  192. SetPosition(pos);
  193. mBoxComp->OnUpdateWorldTransform();
  194. }
  195. }
  196. }