Go Go Kart
Go Go Karts is a 3D arcade multiplayer couch racer that is set in the fantastic Fable World, a magical theme park where legends and myths are real. Adventure awaits, and legends will be built in the wonderful land of Fable World!
This project is an archived student project, the content here is considered obsolete.
Description
This project was chosen to give a fun and creative approach for a racing game, allowing us to explore a cartoony art style and to learn the Unreal engine. The project was completed over 16 weeks – throughout the process, we struggled with being on a fixed timeline, working towards getting a strong and fun game feel and with trying to ensure that our game felt like a theme park. This process helped the entire team learn how to work as a large team of almost 50, and understand how to be part of a larger team in the games industry.
Role & Responsibility
Roles
- VFX Designer
- VFX Framework Programmer
- Audio Framework Programmer
- Audio Programmer
- VFX Programmer
Responsibilities
- Work with Game Designer and Lead Artists to decide the Art Direction and VFX Art Style
- Create pixel/vertex shaders with HLSL and Material Editor (UE4) for particles, post process effect and object materials
- Work with teammates and Lead Programmer to design the framework of VFX and Audio playback system, with consideration of easy to extend and robust enough to work with existing code architecture
- Create workflow and pipelines for different deliverables
- Help other VFX designers to solve mathematical issues or rendering bugs
- Implement VFX and Audio into game
- Performance Optimization for VFXs
- Design API and write API docs for other teams to use
- Create Dynamic Audio System that automatically duck down sounds based on AI/Player, and 3D Spatial Sound for Singleplayer mode
Work Examples
VFX Works
Drifting Particles and VFX in Go Go Kart
Collision Spark:
The Collision Spark is created to help convey where a collision is happening (Whether on other vehicles or scene static meshes) This is achieved by getting the contact point of collision event and the normal of it, then cross product the normal and tangent to get the spark spawn direction along the reversed velocity direction. The base particle material is just an emissive gradiant mask calculated in shader, and stretched over life towards velocity direction in cascade. They are pure GPU particles with masked shader dormain instead of translucent so the performance impact was very few.
Decal Based Self Ambient Shadowmap:
The Decal Based Self Ambient Shadowmap is designed to solve the problem that in a dark scene, the light source illuminance is not high enough to cast static shadow, and dynamic shadow is turned off due to performance consideration. Although screen based ambient occulsion or ambient cubemap will work, it takes the whole frame buffer into calculation and is not quite worthwhile to do so for local 4 split screens. So the Decal Based Self Ambient Shadowmap is a simple trick that align with the actor’s downwards vector (local space (0,0,-1)) and project a gradiant mask underneath it. Which not only added more dynamics into the game, but also almost free on performance.
Smoke from exhaust pipe:
Smoke from exhaust pipe is designed to make the feel of driving more juicy. There are some restrictions though, first, for 4 split screen local gameplay, all the particles will be rendered for all vehicles, on both left and right wheels. Given the fact that smokes has to be translucent to gain best result, the performance is a key variable when creating the shader. This was eventually achieved by chaning the shader dormain from translucent to Additive. So that to reduce a little work on CPU sorting stage. This particle is a CPU particle in order to make subUV work. I eventually found the balance between effect and performance.
The Coin is Bobbing and Rotating in the Scene, and will pop up into the air when gets collected, but the only logic in the actor class was add scores to player. This is because the animation of bobbing and rotating are purely vertex and pixel shaders. The reason for making such design decision is because there are a lot of those items in the scene, while CPU is doing the majority work for the 4 players game logic, so there isn’t much budget left on CPU side. However, due to our performance control, GPU budget was still plenty, so i moved this part from game logic to pure shader solution to help optimize the overal performance.
The Tire Skid is designed as a convey evidence that the car is now entering “Drifting” status, this was achieved by spawning decals underneath the wheel, we choose circle gradiant mask as basic decal because circle can be connected to a line without having sharp edges.
Volumetric Fog
The Volumetric Fog is designed as a atmosphere juicy effect. Especially to create a spooky feel in Haunted level, I’ve tried pure particles and vertex shader based fog just like Super Mario: Odyssey, but eventually I decided to go with unreal built-in Volumetric Fog support, which basically was using raycast to create a realistic scattering effect in the world space. It was easy to implement into our project and flexible to adjust.
Water Shader
The water shader is a cartoon style shader which designed as part of level juice. There are some features of this shader and its corresponding class. First, the edge detection, water can automatically detect anything that overlaps it and create a foam effect. Second, reflection and refraction, these two parts are used to create a realistic physics feeling of water, instead of let it looks too cheap. Third, unbounded extendable water spline, the water mesh itself is just a simple plane, I converted it to a spline actor so designer can easily create waters that flow towards anywhere they want without break the UV and texture resolution.
Fully Customizable Water Shader
—- Vehicle FX System Breakdown —-
BPAC_VehicleFXComponent (BP Component)
All vehicle related fxs are dedicated to the BPAC_VehicleFXComponent, where there’s a Map (Similar to Dict, but more powerful in UE4) that contains the key of a fx, and a structure that contains a VFX Class and a SFX Class. (Passed into the value as a strucure, both are child class of BP_FXClass), for example:
PlayFXOnCar API Where PlayFXOnCar() is a function in the component.
PlayFXOnCar(Enum FXType, SceneComponentReference AttachTo)
The function will spawn corresponding fx based on the key passed in, and will attach to a scene component, such as a wheel of car, an exhaust pipe, etc.
FX Class (Actor BP)
FX Class The FX Class only contains a PlayFX() function for children classes to override.
FX Classes Library
For each fx type, there are 2 corresponding FX classes responsible for the SFX and VFX, however, the framework still works even 1 of them are empty.
—-AUDIO SYSTEM BREAKDOWN—-
Sound Class Structure
The audio system is mainly driven by several components: GGKMaster(Sound Class) that controls every sub classes and their relationships, GlobalAudioDuck / MIXER_MusicDefault / MIXER_MusicDuck (Sound Mix) that controls passive duck down effect for corresponding sound classes, GlobalAttenuationOverride (Sound Attenuation) that controls the spatial audio sources for single-player mode, BPA_Music (Actor BP) that contains the actual bgm file to play, and the BPF_AudioMix (Blueprint Function Library) that contains the global API for calling them at anywhere.
Sound Cue
Audio Assets are imported as raw files, then we create another layer before playing them - a Sound Cue, this abstraction can then grants us the ability to control the assets more dynamically instead of just directly play them. E.g: Sound Cue Example
Here we have 2 types of acceleration audio, instead of playing them directly, we mixed them in sound cue, and then added a modulate operation to provide slightly difference each time we call the cue, and set it to looping, to make sure the audios have variances each time player hearing it. Meanwhile, sound cue also have a reference pointing to a specific sound class. So every time the cue is called, the sound class will fire an event for future process.
GGKMaster Sound Class
GGKMaster Sound Class The GGK Master Sound Class is basically a logic layout of the sub sound classes and their relationships, each class highlighted with green color means they are actively listening the call from sound cues, once they getting called, the passive sound mix will also execute, results in duck down the volume and pitch of other sound classes playing.
Sound Mix
This is an example of a typical sound mix class used to duck down musics and ambient sounds, when some other important sounds like gameplay items or offensive action sounds is playing, this three less important sound class (Music, Ambient, Vehicle) will be passively duck down, to ensure that players always gets the most prioritized information.
BPF_AudioMix (Blueprint Function Library)
BPF_AudioMix contains 3 functions, PlaySoundInGame(), PlayMusic() and StopPlayMusic()
PlaySoundInGame(SoundCue InputCue, float VolMul, float PitchMul, float StartTime, ActorReference Caller):
This is the core function that handles all SFXs.
- InputCue: the sound cue to play
- VolMul: the volume multiplier, default is 1
- PitchMul: the pitch multiplier, default is 1
- StartTime: when to play the sound cue
- Caller: which class is calling this function, this is used to determine whether the sound is playing by an AI or actual player, if it’s AI, then we do another duck down process to improve real player’s experience
PlayMusic(Enum MusicType, bool IsFinalLap)
This is the core function to play musics, because every music needs a final lap variance version, so we seperate them between SFX.
Music Type Enum
Music Sound Cue
For every music, we have 3 audio raw files, an intro that plays only once at the very beginning of the match, a loopable part that loops in the match, and a final lap variance version for the final lap. The final lap version is controlled by a boolean.
StopPlayMusic(AudioComponent InMusic)
The purpose for this class is to avoid music overlapping, every time when we spawn a new music, we also want to make sure that other musics are stopped.
Post Mortem:
What Went Well
- My communication within team turned out quite well
- We are able to prioritize tasks for each milestone
- My experience of VFX and Audio helped to accelerate the development process
- The asset list is helpful for tracking what assets are created and where they are
What Went Wrong
- I used to crunch overtime, which unfortunately results in breaking a build near release
- Could have been using JIRA earlier
- The communication between teams and leads at early production stage doesn’t go well, we come up with some frameworks and architectures from brainstorm phas and then some of them just doesn’t get approved for no reason
- The approval for concept takes too long, as a guidance of how the game feel and how the art direction looks like, the concept art for cars got approved too late, eventually cause the aesthetic in the game doesn’t consistent
- When new tasks and changes keep coming in, the scrum board and tasks are useless because we are not doing what we planned to do at the beginning, which then leads to cut on features
What I Learned
- DO NOT work out of the working hour, because that might break the build unexpectedly
- Even if I do, DO NOT push anything on P4
- Unless everyone in the team know this push and approves me to do so, which is extremely important because I have no idea if that push will break the build or not
- Left time for polish, and give them a higher priority, because there are still a lot of placeholder assets in the game at the time it released, which is because I planned to put them in first, while new tasks just keep popping in, and all of them have higher priorities, which left no time for polishing
- QUALITY OVER QUANTITY
Sub-Team Post Mortem:
What Went Well
- The communication within our team (VJA team) is strong, since we are only three people
- The structure of our team is formed in a very efficient way, given the fact that:
- I have experience on Visual FX, Juicy and Audio
- My teammates have experience in at least 1 specific area
- We have setup our team workflow and pipeline at a very early stage
- We don’t have to spent much time explain a certain term since everyone in our team more or less have an idea on related topic
- The responsibility and role is very clear in our team, everyone knows whom to talk to if an issue happens
What Went Wrong
- Since all of us in our team are Level Designers, but for VJA we are doing more Programming/Art hybrid works. Yet it wasn’t until almost release phase that the Lead Artist/Programmer start to take us in consideration
- Communication between teams and other designers doesn’t go quite well at the beginning, because nobody know which part we belongs to. (Programmer/Level Designer/Artist)
- Features changed very often and doesn’t go through process, and GDD also changed very often, which sometimes left us working on some features that’s already been cut
What We Learned
- If leads don’t know who should be responsible or approve our work, we should actively go to them and address these problems
- If a new feature comes, always follow the pipeline
- Dynamically manage resources within team, when a big feature or system was assigned to us
- Have an idea on how the other teams work, what are they working on
- Have a regular meeting with those who are working on logic frameworks and go through these technical details is very helpful, and will make everyone have a clear image about what is going on.