I had a plan. After dedicating months to my last couple pieces, I wanted to take a bit of a break from working big and focus on getting some smaller projects done. So in my infinite wisdom, I figured making an accurate arcade machine would be quick and easy. I was wrong. As it turns out, there's a lot more to it than I realized.
After making arcade machines in my final project last term, I knew this time I needed to work from real reference. It was the perfect excuse to check out 8-Bit Beans, a retro arcade caffe I'd been meaning to explore. Tucked away in a inconspicuously hidden room, I found it. A 1981 Galaga machine that you could easily tell had been in good use for its 41 years. It was perfect.
Base Model
As far as the model itself goes, it's pretty simple. I did all of the building and UVs in Maya, and worked using shipping dimensions I was able to find online. The one thing that I did find very helpful during the early stages, was creating a rough trim sheet of the cabinet art so I could see where my sizing or placement was slightly off.
The screen was done in two pieces, with the TV display recessed into the cabinet, and a plane covering the gap to make the entire front look like a single sheet of glass later in Unreal.
Texturing
Most of the texturing was done in Substance Painter, using Smart Materials as starting points and modifying their individual layers and masks. I worked a lot with generators and different noise maps to give it random wear, but a lot of the grunge had to be custom made. One of the things I didn't do the last time I made an arcade machine, was really think about what parts the dust would collect on, or where tiny scratches would happen. I'd never gotten to play on a classic arcade machine until researching for this project at 8BitBeans, so it was extremely beneficial to physically be able to see what areas people would be constantly touching.
I found all the cabinet art online, and used an AI upscaler on most of the images before bring them into Photoshop. Once I had all my images as high resolution as I could get, I'd use Photoshop to remove any backgrounds, straighten edges, and edit out light sources if need be. Then it was just a matter of saving each piece of cabinet art out as a separate PNG and applying them like stickers to my model in Substance Painter.
Details like the tape on the coin door, and the edge wear on the control panel were hand painted and faked using height and normals.
Glass (Substance Painter and UE5)
I can say with full confidence that I will never make glass this way again. My goal this time was to optimize my final files better while still maintaining quality, and I didn't achieve that balance with this particular part. My arcade machine was comprised of two main materials. One for the big areas like the sides and back, and one for the smaller pieces like the control panel, wheels, buttons, etc. I had my glass on the detail trim sheet thinking I would treat it as a regular material in Unreal, but use the alpha channel as a mask over a glass shader that I'd add in.
I fought with this blueprint for two days before finally admitting defeat and assigning the glass its own material. Trying to make a material act both as a classic hard surface, and glass at the same time was not a worthwhile resource management strategy to take. Once I had the materials separated, it was much more straight forward to apply my grunge overlay on the glass without messing up the cabinet itself.
Unreal
Setting up the file and importing all of my work was pretty standard. Sadly translucent materials still aren't supported by Nanite so I did have to uncheck that when I brought in my model.
Glass (UE5)
I did use the basis of my original plan in my new glass setup, incorporating the texture maps I'd previously made in Substance Painter. The Diffuse map acted as Base Colour, and a very slight Emissive. The Opacity was done using a Fresnel and parameters for the middle and edge opacity. For the Refraction, I used a lerp node to transition between the IOR strength for the edges and inner glass. Lastly, I set my Normal map, added some scaler parameters for the ORM map, and gave Specular a value of 1.
Cabinet Material
To save resources, I created a pretty standard master material that would work as a base for everything except the screen and glass. I parameterized the four maps I could potentially need (Diffuse, Normal, ORM, and Emissive), threw in some scalers to edit the intensity of particular components, and multiplied all of the textures with a Texture Coordinate and another scaler to edit any texture sizing for later when I build it a miniature set. Everything applied to my cabinet was a Material Instance.
Screen
With the exception of a couple scaler parameters, everything was done through the Emissive channel. This was unquestionably the most challenging part of the process and took me about a week to figure out. My end product was an amalgamation of concepts I liked from other artists screen work, and my own interpretation and integration.
Scaling and Position
- This is a pretty standard setup appending two scaler parameters as U Tile and V Tile, and multiplying the append by a Texture Coordinate, allowing me to adjust the placement and height/width of the final screen output.
Screen Jump
- I started with creating a seed, by adding a time node with a divisible scaler parameter. By multiplying the seed into two sine nodes, I was able to get a fairly random looking flash between black and white. Using a high contrast VHS noise texture, another Texture Coordinate, a Panner, and some more scaler parameters, I stretched the texture and panned it up to show randomly sized white stripes running through the black base. After that, I used an Fmod to bridge between the sines and my new moving VHS texture. Using a noise texture, I set it up with scaler parameters and a panner to travel diagonally. I merged my Scale/Position segment using a Break Out Float2 Component node, subtracting the R by my diagonal noise, and finishing with a Make Float2 to bring the Subtract (X) and the Break Out Float2 (Y) together.
Pixelation
- The pixelation was also pretty straightforward. I used a scaler parameter to determine the number of pixels the end product would be converted into, plugged that into a Floor node, multiplied it by my screen jump, plugged the multiply into another Floor, and divided my new Floor by the first.
Distortion
- I made the distortion/interference using the same method as explained in the screen jump. It really was just a matter of running different combinations/different valued texture coordinates, panners, and sines through a smudged image of a black and white gradient. When I had a couple variations of different effects, I used the texture samples R channels to bring them together. Lastly, I created an empty texture sample with my Pixelation plugged into its UVs, and the texture plugged into my texture object (what I want displayed on the screen). Using a Break Out Float3 Component node, I connected my texture sample with all of the distortions I just made.
Exposure Distortion
- Using another empty texture sample, I connected its texture input to my texture object, and added my distortion (previous step) with a Float2 calling on a scaler parameter. I gave it a clamp and another scaler parameter to adjust the strength and then added the end product with my distortion again.
RGB Pixel
- For this component, I used a standard image of an RGB pixel, and with a texture coordinate and scaler parameter, modified its scale and opacity.
Media Textures
Setup
Now's when things get tedious. I created a new folder called Movies in my root Content folder. If it's not put here or not precisely named Movies, Unreal can get upset. Leaving Unreal and opening a File Explorer window on my computer, I located my Movies folder in the UProject and dragged in my video file. The video file will only show up in a file explorer window and won't be visible in Unreal's content browser. Back in Unreal, I right clicked an empty area in my Movies folder and under the Media dropdown, created a Media Player. As soon as this is done, there is a popup asking to create a Media Texture out of the player, and it is imperative to say yes. With that done, I right clicked in another empty area, and in the Media dropdown, created a File Media Source. Opening the File Media Source, I set the file path the the video I'd dropped in my Movies folder through my computer's File Explorer. I then opened my Media Player, double clicked my video file at the bottom of the screen (it's ok if it doesn't display the video right now), and in the details panel, checked off Play on Open, and Loop. I opened my Media Texture, and assigned my Media Player in the details panel. Finally, In my project settings, I searched for RHI and made sure Default RHI was set to Default.
I'd been using DirectX 12 for another project and learned the hard way that it doesn't react well with media textures.
Level Blueprint
In my Level Blueprint, I added a new variable, and clicked the dropdown (default is Boolean) to change its type. In the dropdown, I searched Media Player and chose Object Reference from its dropdown.
I dragged my Media Player variable into the graph, and chose Get. By dragging the Media Player's output node into empty space, I could search Open Source and it created me a render target. For whatever reason, the Open Source node doesn't show up in the right click menu, so it has to be done this way. Because I wanted to have multiple different vides for different screens, I needed to create a sequence node. I plugged an Event Begin Play into the Sequence input, and the first Sequence output into my Open Source input. After compiling the blueprint, I was able to assign my content browser's Media Player to the media player node. Then, directly on the Open Source node, I clicked the dropdown under Media Source and selected the Media File Source I'd made in my content browser. To add more videos, I just repeated this entire process, only sharing the Event Begin Play node, the sequence input, and clicking the Sequence node's '+' to add more outputs. After one last compilation, it's done.
Application
This is the fun part. I created a Material Instance out of the base screen material, and changed my Texture Object parameter from the still image I'd been using to test the blueprint, with the Media Texture I'd made in my content browser. Now it's just a matter of playing with the parameters I'd set up to adjust the screen exactly how I want it, and applying the Material Instance to my model.
I learned a lot from this project and really enjoyed the process. As always, there are many things that I'd do differently next time, but it was a much more technical undertaking than I'd attempted before.