| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218 |
- // ----------------------------------------------------------------
- // From Game Programming in C++ by Sanjay Madhav
- // Copyright (C) 2017 Sanjay Madhav. All rights reserved.
- //
- // Released under the BSD License
- // See LICENSE in root directory for full details.
- // ----------------------------------------------------------------
- #include "FPSActor.h"
- #include "MoveComponent.h"
- #include "SDL/SDL_scancode.h"
- #include "Renderer.h"
- #include "AudioSystem.h"
- #include "Game.h"
- #include "AudioComponent.h"
- #include "FPSCamera.h"
- #include "MeshComponent.h"
- #include "BallActor.h"
- #include "BoxComponent.h"
- #include "PlaneActor.h"
- FPSActor::FPSActor(Game* game)
- :Actor(game)
- {
- mMoveComp = new MoveComponent(this);
- mAudioComp = new AudioComponent(this);
- mLastFootstep = 0.0f;
- mFootstep = mAudioComp->PlayEvent("event:/Footstep");
- mFootstep.SetPaused(true);
- mCameraComp = new FPSCamera(this);
- mFPSModel = new Actor(game);
- mFPSModel->SetScale(0.75f);
- mMeshComp = new MeshComponent(mFPSModel);
- mMeshComp->SetMesh(game->GetRenderer()->GetMesh("Assets/Rifle.gpmesh"));
- // Add a box component
- mBoxComp = new BoxComponent(this);
- AABB myBox(Vector3(-25.0f, -25.0f, -87.5f),
- Vector3(25.0f, 25.0f, 87.5f));
- mBoxComp->SetObjectBox(myBox);
- mBoxComp->SetShouldRotate(false);
- }
- void FPSActor::UpdateActor(float deltaTime)
- {
- Actor::UpdateActor(deltaTime);
- FixCollisions();
- // Play the footstep if we're moving and haven't recently
- mLastFootstep -= deltaTime;
- if ((!Math::NearZero(mMoveComp->GetForwardSpeed()) ||
- !Math::NearZero(mMoveComp->GetStrafeSpeed())) &&
- mLastFootstep <= 0.0f)
- {
- mFootstep.SetPaused(false);
- mFootstep.Restart();
- mLastFootstep = 0.5f;
- }
-
- // Update position of FPS model relative to actor position
- const Vector3 modelOffset(Vector3(10.0f, 10.0f, -10.0f));
- Vector3 modelPos = GetPosition();
- modelPos += GetForward() * modelOffset.x;
- modelPos += GetRight() * modelOffset.y;
- modelPos.z += modelOffset.z;
- mFPSModel->SetPosition(modelPos);
- // Initialize rotation to actor rotation
- Quaternion q = GetRotation();
- // Rotate by pitch from camera
- q = Quaternion::Concatenate(q, Quaternion(GetRight(), mCameraComp->GetPitch()));
- mFPSModel->SetRotation(q);
- }
- void FPSActor::ActorInput(const uint8_t* keys)
- {
- float forwardSpeed = 0.0f;
- float strafeSpeed = 0.0f;
- // wasd movement
- if (keys[SDL_SCANCODE_W])
- {
- forwardSpeed += 400.0f;
- }
- if (keys[SDL_SCANCODE_S])
- {
- forwardSpeed -= 400.0f;
- }
- if (keys[SDL_SCANCODE_A])
- {
- strafeSpeed -= 400.0f;
- }
- if (keys[SDL_SCANCODE_D])
- {
- strafeSpeed += 400.0f;
- }
- mMoveComp->SetForwardSpeed(forwardSpeed);
- mMoveComp->SetStrafeSpeed(strafeSpeed);
- // Mouse movement
- // Get relative movement from SDL
- int x, y;
- SDL_GetRelativeMouseState(&x, &y);
- // Assume mouse movement is usually between -500 and +500
- const int maxMouseSpeed = 500;
- // Rotation/sec at maximum speed
- const float maxAngularSpeed = Math::Pi * 8;
- float angularSpeed = 0.0f;
- if (x != 0)
- {
- // Convert to ~[-1.0, 1.0]
- angularSpeed = static_cast<float>(x) / maxMouseSpeed;
- // Multiply by rotation/sec
- angularSpeed *= maxAngularSpeed;
- }
- mMoveComp->SetAngularSpeed(angularSpeed);
- // Compute pitch
- const float maxPitchSpeed = Math::Pi * 8;
- float pitchSpeed = 0.0f;
- if (y != 0)
- {
- // Convert to ~[-1.0, 1.0]
- pitchSpeed = static_cast<float>(y) / maxMouseSpeed;
- pitchSpeed *= maxPitchSpeed;
- }
- mCameraComp->SetPitchSpeed(pitchSpeed);
- }
- void FPSActor::Shoot()
- {
- // Get start point (in center of screen on near plane)
- Vector3 screenPoint(0.0f, 0.0f, 0.0f);
- Vector3 start = GetGame()->GetRenderer()->Unproject(screenPoint);
- // Get end point (in center of screen, between near and far)
- screenPoint.z = 0.9f;
- Vector3 end = GetGame()->GetRenderer()->Unproject(screenPoint);
- // Get direction vector
- Vector3 dir = end - start;
- dir.Normalize();
- // Spawn a ball
- BallActor* ball = new BallActor(GetGame());
- ball->SetPlayer(this);
- ball->SetPosition(start + dir*20.0f);
- // Rotate the ball to face new direction
- ball->RotateToNewForward(dir);
- // Play shooting sound
- mAudioComp->PlayEvent("event:/Shot");
- }
- void FPSActor::SetFootstepSurface(float value)
- {
- // Pause here because the way I setup the parameter in FMOD
- // changing it will play a footstep
- mFootstep.SetPaused(true);
- mFootstep.SetParameter("Surface", value);
- }
- void FPSActor::SetVisible(bool visible)
- {
- mMeshComp->SetVisible(visible);
- }
- void FPSActor::FixCollisions()
- {
- // Need to recompute my world transform to update world box
- ComputeWorldTransform();
- const AABB& playerBox = mBoxComp->GetWorldBox();
- Vector3 pos = GetPosition();
- auto& planes = GetGame()->GetPlanes();
- for (auto pa : planes)
- {
- // Do we collide with this PlaneActor?
- const AABB& planeBox = pa->GetBox()->GetWorldBox();
- if (Intersect(playerBox, planeBox))
- {
- // Calculate all our differences
- float dx1 = planeBox.mMax.x - playerBox.mMin.x;
- float dx2 = planeBox.mMin.x - playerBox.mMax.x;
- float dy1 = planeBox.mMax.y - playerBox.mMin.y;
- float dy2 = planeBox.mMin.y - playerBox.mMax.y;
- float dz1 = planeBox.mMax.z - playerBox.mMin.z;
- float dz2 = planeBox.mMin.z - playerBox.mMax.z;
- // Set dx to whichever of dx1/dx2 have a lower abs
- float dx = Math::Abs(dx1) < Math::Abs(dx2) ?
- dx1 : dx2;
- // Ditto for dy
- float dy = Math::Abs(dy1) < Math::Abs(dy2) ?
- dy1 : dy2;
- // Ditto for dz
- float dz = Math::Abs(dz1) < Math::Abs(dz2) ?
- dz1 : dz2;
-
- // Whichever is closest, adjust x/y position
- if (Math::Abs(dx) <= Math::Abs(dy) && Math::Abs(dx) <= Math::Abs(dz))
- {
- pos.x += dx;
- }
- else if (Math::Abs(dy) <= Math::Abs(dx) && Math::Abs(dy) <= Math::Abs(dz))
- {
- pos.y += dy;
- }
- else
- {
- pos.z += dz;
- }
- // Need to set position and update box component
- SetPosition(pos);
- mBoxComp->OnUpdateWorldTransform();
- }
- }
- }
|