Game Engines - Roboy Phyre Engine 2
During the development of our GDW game, Roboy in the Hood, I came across some intense movement and camera controlling code in one of the Phyre engine samples, Space Station Demo. During the phase where we were planning and mapping out how our character would move and the camera would be controlled, we decided on the key design implementations, but overlooked the actual mechanics and details that would need to go in. More specifically, I found a piece of code in the sample that implements proper movement and easing given the 2 vector inputs of the controller and outputting the proper direction to move in, in relation to the current camera position.
Looking through a lot of samples and reading the Phyre documentation, we found that a lot of Phyre classes and components already implement movement physics and camera handling, it was just a matter of stitching them together. We have been putting time into dynamically adding and linking components with the correct properties to instances at runtime, instead of through the Phyre level editor, so this step was essential for setting up character movement and expected camera controls.
Starting with understanding the math behind moving and easing the character towards the proper direction in relation to the camera, I focused on the sample as we hadn't been able to instantiate components and handlers on our own character. The sample had absolutely no comments for the math that was going on for this particular problem, so I had to figure it out line by line. Getting right into it, because the samples are confidential, the following code has been altered.
1) The update first checks and handles the joystick inputs, storing them in vectors. While it doesn't matter, for the right analog stick, the up-down value is stored in the z property of the vector for an easier grasp on the upcoming camera math. This step is the first of 3 main aspects of the whole algorithm.
2) As of now we simply have two Vec3's. controlVector, which is the left stick that holds the values for the X: horizontal displacement from origin, 0.0f, and Y: vertical displacement from origin, and a cameraInputVector which has values for X: horizontal input * cInvert1, vertical input * cInvert2, 0.0f, where the invert variables are 1 or -1.
For the second step now that we have input, we will move the camera based on the previous camera position, and set up some variables for movement of the actual character after.
camRotation = camRotation + (input - camRotation ) / 4;
float conDamp = max(0.0f, (dot(normalize(prevConvVec), normalize(conVec) ));
controllerDp = controllerDp * 0.75f + 0.35f;
The first line is a simple easing equation for the current direction (vec3) towards the target direction.
For the second line, the variable conDamp represents a value based on the change in the past 2 analog input vectors for movement. An angle > 90 degrees between the current and the previous positions will result in 0, 90 degrees > n > 1 degrees will result in 0 to 1, and 0 degrees will result in 1. This value is used to influence a constrained movement vector to in the end of the function to change the final movement speed. It is important to note that for two normalized vectors, the dot product will always be between 0 and 1, which is essential for the above equations to work.
And the last part is simply constraining the value to 100% of n > n > 35% of n, which based on above, will be 0.35 to 1.0.
prevConvVec+= (conVec- prevConvVec) * 0.5f;
float spinSpeed = (clamp(0.0f + length(prevConvVec), 0.0f, 1.0f) * 0.6f + 0.4f) * 2.0f;
camera.applyRotation(camRotation.x * dT * spinSpeed, camRotation.y * dT* spinSpeed);
For the first line, ease current direction towards target direction. For the second line, spin speed is based on how far they are pushing the analog stick, clamped to 0 and 1, and also restrained to 100% of n > n > 40% of n. Finally, for the last line, it simply, applies the rotation in the correct direction to the main camera.
3) Now that the camera is moved, and the variable holding the data representing the direction is set up, we can get deep into moving the actual character the right way based on all the parameters:
Vec3 camDir = camera.getDirection();
camDir.y = 0.0f;
camDir = normalize(camDir);
Vec3 camPerpendicular = normalize(cross(camDir, Vec3(0.0f,1.0f,0.0f)));
Vec3 characterDir = camDir * prevConvVec.z + camPerpendicular * prevConvVec.x * 0.8f;
conDamp *= clamp(dot(camDir, normalize(characterDir)) * 0.5f + 0.5f, 0.0f, 1.0f) * 0.5f + 0.5f;
movementRate += (conDamp - movementRate ) * 0.2f;
movementVector = characterDir * 0.25f;
movementVector *= movementRate ;
characterCluster.entity.physicsComponent.setVelocity(movementVector);
First of all, we set up a variable to hold the old camera direction.
For the second line, we disregard 3rd dimension, since we're using an analog stick, and then we normalize for dot product calculations.
Next, we need a vector relative to the direction that is orthogonal, so we can scale along each vector based on the input later. So for this step, we make camPerpendicular, that is orthogonal to the current camera, that is not on the z axis.
Afterwards, we create a Vec3 called characterDir, which is used to dictate which direction the character should be pushed towards. characterDir in the end represents the direction the character instance actually needs to move is relative to the current camera direction scaled by how much they are pushing on Y, vector added to the orthogonal vector scaled by how much they are pushing on X.
It gets a bit tricky here, reference the picture below. Using conDamp uses the previous math where it was declared: Based on the camera's direction and the direction that the character actually needs move (as a result of the left analog input), we influence the final movement speed again with the altered camera direction input. The final value ranges from 0.5 - 1 regarding the cases: > 90 degrees (0.5), orthogonal (0.5), < 90 degrees (approaches 1), which then is multiplied by the original percentage based on the change in controller vector rotation.
After this calculation, a variable named movementRate is eased towards the conDamp we calculated.
Finally, we do basic calculations to scale the movement direction by the calculated movement speed and set scales to make the movement feel right, which leads us to finally set the target velocity with the final movement vector and speed for the character to move.
In the end, the camera and character controls in this sample can easily be modified now that there is a deep understanding of each part of the equation, and since the movement and camera controls feel exactly how we envisioned the game, a lot of logic and formulas can be taken and tweaked for use in our GDW game.
![]() |
| Space Station Demo with 3rd person movement and camera |
Looking through a lot of samples and reading the Phyre documentation, we found that a lot of Phyre classes and components already implement movement physics and camera handling, it was just a matter of stitching them together. We have been putting time into dynamically adding and linking components with the correct properties to instances at runtime, instead of through the Phyre level editor, so this step was essential for setting up character movement and expected camera controls.
Starting with understanding the math behind moving and easing the character towards the proper direction in relation to the camera, I focused on the sample as we hadn't been able to instantiate components and handlers on our own character. The sample had absolutely no comments for the math that was going on for this particular problem, so I had to figure it out line by line. Getting right into it, because the samples are confidential, the following code has been altered.
1) The update first checks and handles the joystick inputs, storing them in vectors. While it doesn't matter, for the right analog stick, the up-down value is stored in the z property of the vector for an easier grasp on the upcoming camera math. This step is the first of 3 main aspects of the whole algorithm.
2) As of now we simply have two Vec3's. controlVector, which is the left stick that holds the values for the X: horizontal displacement from origin, 0.0f, and Y: vertical displacement from origin, and a cameraInputVector which has values for X: horizontal input * cInvert1, vertical input * cInvert2, 0.0f, where the invert variables are 1 or -1.
For the second step now that we have input, we will move the camera based on the previous camera position, and set up some variables for movement of the actual character after.
camRotation = camRotation + (input - camRotation ) / 4;
float conDamp = max(0.0f, (dot(normalize(prevConvVec), normalize(conVec) ));
controllerDp = controllerDp * 0.75f + 0.35f;
The first line is a simple easing equation for the current direction (vec3) towards the target direction.
For the second line, the variable conDamp represents a value based on the change in the past 2 analog input vectors for movement. An angle > 90 degrees between the current and the previous positions will result in 0, 90 degrees > n > 1 degrees will result in 0 to 1, and 0 degrees will result in 1. This value is used to influence a constrained movement vector to in the end of the function to change the final movement speed. It is important to note that for two normalized vectors, the dot product will always be between 0 and 1, which is essential for the above equations to work.
And the last part is simply constraining the value to 100% of n > n > 35% of n, which based on above, will be 0.35 to 1.0.
prevConvVec+= (conVec- prevConvVec) * 0.5f;
float spinSpeed = (clamp(0.0f + length(prevConvVec), 0.0f, 1.0f) * 0.6f + 0.4f) * 2.0f;
camera.applyRotation(camRotation.x * dT * spinSpeed, camRotation.y * dT* spinSpeed);
For the first line, ease current direction towards target direction. For the second line, spin speed is based on how far they are pushing the analog stick, clamped to 0 and 1, and also restrained to 100% of n > n > 40% of n. Finally, for the last line, it simply, applies the rotation in the correct direction to the main camera.
3) Now that the camera is moved, and the variable holding the data representing the direction is set up, we can get deep into moving the actual character the right way based on all the parameters:
Vec3 camDir = camera.getDirection();
camDir.y = 0.0f;
camDir = normalize(camDir);
Vec3 camPerpendicular = normalize(cross(camDir, Vec3(0.0f,1.0f,0.0f)));
Vec3 characterDir = camDir * prevConvVec.z + camPerpendicular * prevConvVec.x * 0.8f;
conDamp *= clamp(dot(camDir, normalize(characterDir)) * 0.5f + 0.5f, 0.0f, 1.0f) * 0.5f + 0.5f;
movementRate += (conDamp - movementRate ) * 0.2f;
movementVector = characterDir * 0.25f;
movementVector *= movementRate ;
characterCluster.entity.physicsComponent.setVelocity(movementVector);
First of all, we set up a variable to hold the old camera direction.
For the second line, we disregard 3rd dimension, since we're using an analog stick, and then we normalize for dot product calculations.
Next, we need a vector relative to the direction that is orthogonal, so we can scale along each vector based on the input later. So for this step, we make camPerpendicular, that is orthogonal to the current camera, that is not on the z axis.
Afterwards, we create a Vec3 called characterDir, which is used to dictate which direction the character should be pushed towards. characterDir in the end represents the direction the character instance actually needs to move is relative to the current camera direction scaled by how much they are pushing on Y, vector added to the orthogonal vector scaled by how much they are pushing on X.
It gets a bit tricky here, reference the picture below. Using conDamp uses the previous math where it was declared: Based on the camera's direction and the direction that the character actually needs move (as a result of the left analog input), we influence the final movement speed again with the altered camera direction input. The final value ranges from 0.5 - 1 regarding the cases: > 90 degrees (0.5), orthogonal (0.5), < 90 degrees (approaches 1), which then is multiplied by the original percentage based on the change in controller vector rotation.
After this calculation, a variable named movementRate is eased towards the conDamp we calculated.
Finally, we do basic calculations to scale the movement direction by the calculated movement speed and set scales to make the movement feel right, which leads us to finally set the target velocity with the final movement vector and speed for the character to move.
In the end, the camera and character controls in this sample can easily be modified now that there is a deep understanding of each part of the equation, and since the movement and camera controls feel exactly how we envisioned the game, a lot of logic and formulas can be taken and tweaked for use in our GDW game.


Comments
Post a Comment