Large props to Ats and Raimond for their help!
Videos for those lazy ones who do not like to read
The aim is to create an arcade-like racing game which takes place in space. Emphasis of the outcome is on comicbook-like looks and "full of action" feel.
Main goals for CG course project
Since there is a lot to do and it takes more than one person to finish the project, the main goals for me are to:
- Implement thruster based flight control of the spaceship. Since it is quite weird to move around in space when every single thruster is controlled, it was decided, that the spaceship should be controlled as like an airplane in atmosphere without gravity. Subgoals:
- Create mechanical concept of moving a spaceship by using typical pitch, yaw, roll and thrust movement.
- Create controllable (amount, brightness) particle system to visualize the thrusters in action.
- Create force field effect. Spaceships have forcefields around them and if the ship collides with another object, the field generates a repelling force. Subgoals:
- Create a bounding box to the spaceship to detect overlaps with external objects. Based on speeds and directions of colliding objects, repelling force is generated.
- Somekind of brief warping effect should be present in the area of collision.
- Create a playable prototype with menus and etc.
Initially I had two approaches to choose from:
- Approach 1: Use "Add Force At Location" nodes
- Approach 2: Use "Set Physics Linear Velocity" nodes
The first approach is much more similar to how it really works. Each thruster would be assigned to "Add Force At Location" node and the thruster "pysically" pushes the spaceship. All nice things aside, I didnt even bother to try that out, since the thrusters must be carefully placed. Misalignment of paired thurusters (like acceleration and deacceleration thrusters etc.) causes unwanted rotations of the ship. Although it would not be explodingly difficult to implement that approach. If the center of the mass is known and some roational speed damping hackery is used, then its fine (maybe) but the thruster placement when designing a ship must be taken into account.
The second approach is a lot easier to implement. The idea was to just change the rotational speed of the ship based on user input values. In this case, thrusters have actually no other purpose but to look nice and be convincing. But "looking nice and being convincing" is a whole new problem by itself.
Thruster particle system
Thrusters (now reffering to it as a visual effect) were implemented as Paricle System (PS) that have dynamic parameters that can be controlled with blueprints. I started experimenting with sample PS's offered by starter contents, namely with the system called "P_Sparks". It basically had everything I needed. So after getting to know what different variables and things do, I managed to get something like this:
There are two basic emitters. One emits the thrust particles, other one creates "standby" effect and adds brightness. So the brightness and amount of emitted paricles were changed into dynamic variables that can be controlled with blueprints. Its not the perfect solution, but will work as a protoype. To convinently access each thruster, I made a small function:
There were ALWAYS some issues with the "simple" things I tried o achieve. In this case, the issue was that GPU particles somehow do not like to inherit parent velocities and have initial velocity added to it. Partiles start to lag behind as the speed of the ship increases. CPU particles work fine BUT those start showing weird behaviour when spawned in "larger" numbers (<200). So in order to proceed with the project, I had to accept weird effects of the back thruster where particles local initial velocity is depending on the speed of the ship.
Moving and rotating the ship
As described in the second approach, spaceship is moved by just adjusting its velocity parameters.
In the construction script, a node is called which sets the damping coefficient for the rotation movements. Without damping the ship would keep spinning continuously, as it actually should do in reality. Yet this would be awful for the gaming experience, unless it was a simulator game. Angular damping is played off with thrusters as deacceleration.
Example of roll rotation script:
Basic steps were:
- Listen to user keypress event "InputAxis A and D Axis Map". when pressed, 1 is returned, otherwise 0.
- Get the forward vector
- Multiply the forward vector by some arbitrary value, all about tweaking
- Assign the new vector to "Set Physics Angular Velocity" node
- Calculate the numerical value of the yaw angular velocity. This is done by multiplying the forward vector with angular velocity vector, which is updated in every tick event. This value is used to compare it with previous yaw velocity, sort of estimating the angular acceleration inside "Set Yaw Thrusters node". Based on this acceleration, thrusters are fired accordingly.
The "brightness" and "Trail Spawn" are actually not connected to internal components of this node, these are left there just in case as seen in the following image of the "Set Yaw Thrusters" node:
Translation movemend required some hackery. Like in the case of rotation, the conservation of energy applies also to translation. Again, it would be awfully awkward to control the ship when keeping this conservation in mind. So I decided that the ship should be controlled like it was flying inside an atmosphere, without gravity ofcourse. And thrusters do the visual job of converting this "atmospheric user control input" to "movement in space". So no laws of physics were violated, kind of.
Since the forward movement blueprint is currently an absolute example of a behemoth because there are a lot of other bells and whistles attached to it. I will only describe the algorthm. There are more bits and pieces but the basics are following:
- Listen to user key press axis map. "W" pressed = 1, standby = 0, "S" pressed = -1
- Get the velocity vector of the ship
- Calculate the speed (lenght of the velocity vector)
- Multiply the speed with direction variable. 1 = moving forward, -1 = moving backwards
- If the ship is accelerating (user key press), add some arbitrary value to the speed vector. Again all about tweaking
- Get the forward vector of the ship and multiply it with speed value. Assign this vector to "Set Physics Linear Velocity" node.
- Update the direction of the ship variable. This is done by dot product between the speed vector and forward vector. When the product is negative, then the ship is moving backwards and vice versa.
- Fire the thrusters based on translation and rotation accerelation
By glance everything is quite fine with this method but problems start to appear when spaceship is crashing into a object with a force field. The forcefield should repel objects in any direction, regardless of in what position those objects collided. But since the direction of the ship is basically always overwritten, the ship cannot be pushed in "up" or "right" directions. The workaround is to temporarily disable overwriting in "Begin Overlap" event, endable movement in all directions and smoothly damp the velocity in "up"/"right" directions till according velocities are 0. Heres the blueprint code of this dampener (taking this wide screenshot was hackery by itself. Unreal should have somekind of a screen capture utility for blueprnts):
This code ALMOST works, but there is a little problem: When spaceship rotates during this dampening process, the forward vector is not synchronised in the forward movement code and up/right dampening code. Because of that, the speed is always increased for up or right direction components when user rotates the ship. This causes the ship to slow down (because velocity vector components are currently damped) and manouver in a weird way. Didnt have time for finding the workaround, had to proceed.
"Spaceship without a turbo boost is not considered as a spaceship" - Echecrates. Well he most probably didnt actually say that but its a scientific fact nonetheless.
Idea was that, when space bar and "W" button are pressed, then the ship will:
- Rotate its roll thrusters 90 deg, for sort of getting the additional boost effect. In this state the spaceship cannot be rotated in roll axis. I deliberately left this tradeoff in.
- When rotation is complete, according roll thrusters are fired
- Camera starts shaking to emphasise the severity of the situation - boost is not a joke
- Vision gets blurred. More away from the spaceship the pixel is, the blurrier the region of this pixel gets. I think the effect I was trying to achieve is officially called zoom blur.
Rotation of roll thruster arms
Roll thruster arms are separate meshes which can be rotated. So when space bar is pressed, a timeline is trigerred that starts rotating the arms. When spacebar is released, then arms immedeiately rotate back to original position and the ship can be rolled again. The following depicts the events that are trigerred when space bar is pressed or released:
Shaking the camera
For shaking the camera I created a separate "camera shake" blueprint and accessed it with "Play World Camera Shake" node. Really convenient thingy. How shaking (and blurring, and other weird stuff) is accessed is depicted in following image:
Zoom blur effect
This was one of the most anoying things (among forcefield effect) to implement. Ofcourse I didnt make the blur material up myself, but there were just few key things that I missed. Since unreal engine is constantly upgraded, few "small things" tend to chante. Add some sleep deprivation to the mix and you get a big "WTF? why this? why that?" out of it.
I was constantly searching for "some other solution". It seemed so basic to me that UE4 should have this effect but no. At some point I followed the instructions that were given in THIS POST and after got it working. It suggested to create a post proccess material. Dynamically accessing the variables of this material was whole another issue. Solved that by making a "Collection" fore those neccessary variables. For some reason UE likes to access each and every thing in a different manner. I guess there is a good reason behind it but it does not give the wasted time back. XP over 9000 though. Image of the material that creates the zoom blur effect:
And all things combined (apart from shaking, needs a video):
And a video
- Boost demo: Link
Force field effect
"The dreaded and despised forcefield..." - Echecrates.
The current and defenately not close to the best implementation of the forcefield pipeline:
- Two "Overlap event" generating objects overlap. For a spaceship, its a capsule mesh
- Object with a force field (Object A) queries the reference to colliding object (Object B)
- Object A gets the location of the Object B in world space
- Object A calculates the direction vector and distance between the objects
- Convert the direction vector into local space of Object A
- Enable sidebounce (Described in translation chapter)
- Depending on how far the object B has intruded, generate a repelling force in the direction of the previously calculated direction vector
- Start the force field warp animation in the direction of the direction vector
This is probably the sloppiest approach but its all firstly because of my incompetence and secondly, "Overlap Events" do not function properly, or at least are way unintuitive to be used. "Overlap event" does not return the location of the overlap, nor does this "sphere trace". Somehow "Hit events" return the location of the event but in that case, the forcefield has to be basically a hard object. My aim was to make the forcefield look elastic.
One workaround would be to turn "Generate Hit Events" off right after the event. With this approach, the exact location can be known and field can be intruded. For some reason, this can be achieved in c++ based projects, not in blueprint based. Since I just started learning the UE and more than half of the project was already blueprint based, I neglected this option.
So yeah, there are also some other ideas, but better idea is to investigate the "Begin Overlap Event" further. There has to be a reasonable solution.
Forcefield was implemented as a material. As seen in the following image, 2 meshes surround the spaceship, one is for trigerring the "Begin Overlap Event" and the other (sphere) is where the forcefield material is applied.
Forcefield material itself has 2 basic components. One component is a texture of a "cloud", which is panned and connected to refrection input of the material. This creates the flowing warp effect. The other main component is sphere mask. Sphere mask is used to allow the material to be seen in places where sphere mask and the mesh, where material is applied, overlap. So when capsule detetects overlap event, sphere mask is directed to according direction and the radius of the mask is increased. This creates the propagating warp effect. Forcefield material in the material editor:
And a video
- Forcefield demo: Link