Space Hoops





Download - Link (~300 MB)

The download link is the January 2017 release. The game can only be played with a gamepad on a Windows PC. To play simply click on the executable entitled "Sandbox" in the root folder. Make sure your gamepad is plugged in before running the executable. See the "controls" section below for the gamepad configuration. You can go full screen by hitting [alt+enter]. To quit you can hit [start] on the gamepad and then select quit, or you can press [alt-f4]. You can access the console by hitting the tilde-key [`] once or twice. A full list of commands can be found here. Some useful ones are
  • R.SetRes 1920x1080 - set resolution
  • Stat FPS - show frame rate
  • Stat Unit - more FPS data
  • T.MaxFps 120 - set max frame rate
  • R.Vsync 1 - turn on vsync
If you don't want to do this everytime, navigate to ./Sandbox/Saved/Config/WindowsNoEditor/ and paste the following into GameUserSettings.ini. The full list of flags can be found here.

[/Script/Engine.GameUserSettings]
bUseVSync= True
WindowPosX=-1
WindowPosY=-1
bUseDesktopResolutionForFullscreen=True
FullscreenMode=0
LastConfirmedFullscreenMode=0
Version=5
FrameRateLimit=120


Control Scheme

At the current time there are two fixed control schemes, both only for the controller. The default configuration maps thrust onto the two triggers and rotation onto the two thumbsticks. The second configuration mimics the controls of Rocket League; boost on B, impulse (flip) on A, rotation on the left thumbstick and left shoulder etc. My target audience is primarily RL players so I figured I'd give them something familiar. The default scheme has clear advantages however. For one, the analog-triggers allow for thrust at fractional values. Also, rotation on the two sticks allows for stronger off-axis torque and enables optimal rotation (shortest-path) from one orientation to another.

I would like to allow the player to reconfigure keys in-game and save their own layouts. I actually figured out a very elegant way to do this entirely in Blueprint. If you have any experience with UE4, and have searched for a solution, you'll either find people saying that it can't be done and that you should use Rama's Victory library, or you'll find implementations that have hardcoded every input imaginable in a mass of wires. Just no. I've outlined the basic idea here , which requires a bit more organization for games of larger scope (eg. an enum for actions, a struct for action-key pairs, etc.). The cool thing is that it fully supports one-to-many as well as many-to-one mappings. Making keys configurable is pretty high on my priority list right now. In fact, the logic is already implemented, the only thing holding me back is the UI.

You can zoom the camera in and out using the d-pad and you can change between a velocity-cam and a ball-cam (similar the camera modes in Rocket League). There is also a rearview camera that does some fancy things when you bounce off of a wall. Because of the box-like shape of the arena, the z-direction takes special preference. This preference imposes an annoying gimbal lock at the z-poles (same as in RL). I tried a camera mode that had no preference of direction, but I found it nauseating. I'll probably add it as an option at some point. You can view all the controls in-game by pressing the select button, or by looking at the images below (click to enlarge).





Videos

The first two videos show the current state of the game. As my familiarity with Unreal Engine grew, so did the scope of the game. I still want to make a space-shooter, but throwing in an arena and a ball and making a Rocket League clone was too easy to ignore. The second demo actually features the game sounds, albeit at a poor quality and with wonky volumes (the wind is too loud and the boost suffers from static). I don't have a powerful enough computer to record a high-quality game video in real time. I got around this constraint in the first demo by slowing the sim-speed of the game down to 20% while recording at only 20fps. Then I set the playback rate to x4 so the result is an 80fps video of the game running at 80% speed. While this is a good trick to get nice video resolution, the audio doesn't survive the time-warp. Hence the music. The download link above contains the game as seen in these videos.

I found the last two videos on my phone. I do have old versions of my project saved, so you can expect better versions soon$^\text{TM}$. The last one must have been from my first week of using Unreal Engine, and it features primitive versions of gamepad controls and gravity. In the second to last video you can see the celestial bodies and gravity system completed, as well as the start of a background grid. At this point the idea for my game was to have a pseudo-2D top down shooter where the gameplay was heavily centered around gravity. I say "pseudo-2D" because I had the gravity well idea at this point, and I wanted the game world to be constrained to the gravity-surface. Making a physics game constrained to a surface is still on my to-do list. As long as the surface is twice continuously differentiable, it is fairly easy to derive the equations of motion while automatically satisfying the surface constraint (see this post). And you don't even need to know the derivatives explicitly, since they can be computed exactly via automatic differentiation. But I digress.





Gameplay

Right now, it's not really a game. All you can do is hit a ball around inside a box. I haven't worked on the game since late 2016 and I'm starting a new job now so I won't have much time to add to it. I'm thinking about hacking it into a multiplayer keepaway game. Hopefully I can get that done soon.



Physics

Prototyping is extremely important for physics-based games. You can't design a game down to the wire before you even start developing. It's impossible to predict how an untried mechanic is going to feel before testing it in game. Prototypes let you make final decisions as late as possible.

This game is extremely hard to play. The player has to control a spaceship in a zero-gravity environment with only forces and torques, and has to master such control well enough to play with a flying ball. The mechanics will be familiar to Rocket League veterans, but I expect a steep learning curve for the average gamer. I have done a lot of subtle tweaks to make the game easier to play. For one, the ship has a spherical hitbox and it bounces off the arena walls with no friction (no rotation on impact). The restitution is also set to match that of the ball, so that you can easily chase the ball off the wall. The friction between the ball and the ship is set fairly high, allowing for more directional hits and easier dribbling.



Movement

Flying the ship is the core mechanic of the game. The way it works is very similar to flying a car in Rocket League. The ship has an internal gyroscope that applies torques along the three local body axes. The gyroscope has a fairly strong damping mechanism so that the ship will quickly stop rotating after you release the controls. This rotational damping, often coined "flight assist", is fairly ubiquitous in space-flight games and it makes the controls a lot more forgiving. Rotation is also in a sense "fake", or rather the ship has a mass-distribution equivalent to a uniform sphere. If you start something spinning out in space it will typically not continue rotating around the same axis. While angular momentum is conserved, angular velocity is only conserved along principal axes. Most game physics engines ignore this fact and alter the equations of motion so that angular velocity remains conserved. Indeed, in UE4 rotation is specified by angular velocity vector $\boldsymbol{\omega}$, where the vector-direction corresponds to the (local) rotation axis and the vector-length corresponds to the speed of rotation. The equation of motion $\boldsymbol{τ}=I \boldsymbol{\sigma}$ is analogous to that of linear motion $\boldsymbol{F}=m\boldsymbol{a}$ where $\boldsymbol{τ}$ is the external torque, $I$ rather than being the 3x3 inertia tensor is condensed to a vector specifying the rotational resistance along each local axes, and $\boldsymbol{\sigma}$ is the angular acceleration that directly alters $\boldsymbol{\omega}$. While I typically like to keep things realistic, this simplification makes sense for video games. It does has its limitations -- you can't simulate the precession of a spinning top -- but it also makes rotation a lot more predictable, allowing for simpler controls and easier development.

There are a few things that are different from Rocket League. For one, you have the ability to thrust backwards. You can also toggle linear damping which will apply an air resistance $- \lambda \boldsymbol{v}$ to the ship eventually bringing it to a stop. Both make stopping the ship and changing directions easier. The "flip" mechanic also works differently. In Rocket League you can perform an impulsive flip, which works by projecting the user-input onto the world xy plane (parallel to the floor) and then applying a strong force and torque to push your car in and around the projected axis. In my game the applied force lies on the local xy-plane which depends on the ship's current orientation. I also found the "flipping" nature of the mechanic difficult to work with, so I got rid of it completely. I'm not completely happy with how the mechanic feels, so it will be subject to change.



Arena

The arena is just a box with configurable dimensions. If you set the wall dimensions to zero it degenerates to a sphere. I made the walls into massive windows to show off the solar system in the background. One problem that this introduces is that there is not enough nearby scenery to allow players to adequately judge their own position and velocity. To alleviate this issue I've added (yellow) circular projections on the walls that track the ball. I'm thinking about adding projections of the ships too. Being able to see your own projection would certainly help, and you could use the other players' projections as a "radar" to detect their movement. One thing I am planning on adding is an electric arc particle effect that shoots between objects in close proximity. This will make dribbling the ball a lot easier as you'll be able to see the exact point of contact.





Ship and Ball

There are a variety of different ship meshes -- which are one of the few things I didn't make myself. While I'm somewhat familiar with Blender, spending time making complicated models is not high on my priority list. I did however make all the materials and particle effects; the look of the ship, so to speak. The ball is simply an asteroid with a spherical shield around it.





Goals

The goals are three circular hoops located at each end of the arena (setup like Quidditch). The goals are pushed forward so that the walls wall behind them can act as a backboard. The shape of the goals is highly configurable; it can be specified by any piecewise cubic spline. It currently supports a bunch of different parametrizations. I might get rid of the goals entirely and turn the game into a game of keep-away -- we'll see. One of the main complaints in Rocket League is that the walls sometimes block your camera vision (especially inside the goals). I just recently developed a material that fixes the issue. Basically, if an opaque pixel is within a spherical raycast from the camera to the ball (or ship), then you make that pixel translucent. The translucency can be achieved in UE4 via dithered opacity, or with an extra translucent material layered on top of the opaque one. One of the two videos shows off this effect (I forget which one).





Celestial Bodies

I made the Earth by downloading high-resolution Nasa textures and pasting them onto a sphere. The clouds are a mix of moving noise textures. The dark side of the Earth features city lights. The asteroid-ball and other celestial bodies came from the UE4 marketplace, though I've edited most of them to the point where they are unrecognizeable. The background is a skybox that I made using spacescape.


All motion in the game comes from the physics engine. There is no kinematic movement at all. Even the planets and moons move via gravitational forces. The solar system is mostly to scale. The radii, masses, rotations, distances and orbits of the planets are independently proportional. In fact, it takes about 360 seconds for the Earth to orbit the Sun, so one game-second equates to about one day (though the planets don't spin quite that fast).





Gravity Well Grid

The grid was a major milestone, and by the time I was done I felt I had mastered the UE4's material editor. The grid is the core visual-effect in my game. I'm not a terribly artistic person. If you ask me to draw a person I'll send you back a stick figure. But I do know how to make some cool effects mathematically, eg. gravity wells, celestial stars, the ship thruster engines, a dissolve effect, clouds, tron-like lighting, etc.


The grid is essentially an array of emissive contour lines. While the game features lines of constant $x,y,z$-values, the material also supports affine and spherical contours and allows for variable spacing (constant, logarithmic, exponential, power growth, and so forth). You can also change the color, opacity, line-width, brightness, etc. The main issue I had in making the grid was the horrible aliasing of thin emissive lines. So all of these parameters come with some falloff value. Notice how the lines fade away with distance in the image above.

The material also supports tileable "distance" textures. A distance texture sets each pixel to a grayscale value that measures the distance to the nearest tile edge. I wrote a Python script that will generate such a texture if you input the tile-edge line segments. Note that a square tile will yield a grid of UV contours. The picture below shows an assortment of generated textures.



The grid is the next easiest optimization of my game (after the Sun). I've made the material extremely configurable, but this configurability comes at a computational cost. Now that I've settled on a particular configuration that I like for this particular level, I can trim all the extra stuff out (distance textures, if any, can be heavily preprocessed as well). It's not just the material that can be optimized, but also the mesh itself. I initially tried using a UE4 landscape, however due to an issue with culling I was forced to use a high-definition plane mesh (2000x2000 vertices yuck!). The LOD optimization that is built into Landscapes easily gives an extra 10-20 FPS on my machine. My game is currently running 4.10 version of the engine and the bug was fixed in 4.11 after I had brought it to Epic Games' attention.



Audio

Most of the game sounds come from UE4 marketplace, an online database, or my personal music library. I used Audacity to trim and edit the clips.



Game Framework

Since this was my first project, I went into it with a focus on learning the engine and building myself a suite of reusable tools. The theme of the game was less important to me than the modular components that went into it. This project is completely done in Blueprint (I wanted to learn the visual scripting language). Blueprint makes prototyping extremely easy, indeed everything you see in the current game is a highly configurable prototype. From the ship's movement and camera view, to the ball's behavior, the arena, the celestial bodies, and the gravity well grid. If I wanted to add flight assist, wind resistance or the ability to strafe with the ship, I can simply click on the ship and change a few parameters in its movement component. If I wanted to add a magnus effect to the ball, or change its max speed, again, I simply need to change a few float values in the editor itself. If I wanted to make the arena and everything in it orbit the star I simply need to enable the respective gravity receivers with a checkbox. No recompilation needed. Indeed I'll probably add a menu that lets the player tweak these settings as game-mutators. The only downside to prototyping in Blueprint is performance. Blueprint runs slower than C++, and abstraction always comes at a cost. But for Space Hoops that's not an issue, since the the bottleneck is the graphics rendering (gpu) rather than the game logic (cpu). Feel free to mention any bugs or suggestions in the comment section.





No comments:

Post a Comment