Jump to content

Pixley's Cleverly Named Blog



Photo

Auto-Leveling: How to Align a Ship

Posted by Pixley , 19 December 2016 · 832 views

Auto-Leveling: How to Align a Ship

One of the most frequently requested features from the classic Descent games for DU was auto-leveling. Many players swear by it, and it's also a good set of "training wheels" for those unfamiliar with 6DoF or more traditional flight games. It's taken a lot of thought regarding implementation, and it's finally here in DU with the second "Jingle" build (2881).

 

Terminology

 

Auto-level - a system by which the roll of the ship is automatically adjusted to align the ship to its environment, such that the player can stay focused on pitch and yaw when flying.

 

Roll - rotation around the forward-reverse axis. When a plane banks to make a turn, it's rolling.

 

Pitch - rotation around the left-right axis. Look up and down. You've just pitched your head.

 

Yaw - rotation around the vertical axis. Look left and right. Your head just yawed.

 

Posted Image

 

Raycasting - travelling along a straight line from a source point to a target point and reporting the first (or all) surface(s) hit along the way.

 

Cube - in classic Descent, the fundamental building block of any environment in the game. Every space in those games is built out of deformed cubes whose sides can be closed or open. The data structure behind these cubes was fundamental to the way the classic Descent engines worked, right down to the renderer.

 

A History Lesson

 

Posted Image

 

The implementation of classic Descent's auto-leveling system was simple - the game knew which cube the player's ship occupied, so it was a simple matter to determine which face of the cube the bottom of the ship was most closely aligned to, and it could be rolled to match. This does not adapt well to modern games, and there are a few reasons for this:

  • The concept of the deformed cubes was essential to get the games to run at an acceptable rate back then. Graphics cards hadn't been invented yet, so the functionality we now rely on them for was performed by the ill-suited CPU, using a software-based renderer. One could go into all sorts of detail on this topic, but, basically, cubes made it a lot easier to run. Nowadays, we don't have those kinds of limitations, so structures like that have fallen to the wayside. As such, the data that these cubes tracked is no longer simple to keep, as a ship now exists in "space", rather than a location relative to the cube in which it resides.
  • Because environments are built out of arbitrary geometry, there's not necessarily a flat, well-defined "floor" to which to align. Take BAMM, for example. The bottom section is mostly rough rock. Barely any of that is flat, even checking against simpler collision geometry, so aligning the ship to the surface directly below it is out of the question, as that would result in something like this:

 

Posted Image

  • Even putting aside rough surfaces, there's another kind of geometry that causes even an approximation of the classic system to fall apart: round tunnels. Alignment to the surface of a round tunnel is basically impossible, because the target changes as the ship rolls. This would make flight in Tycho infuriating.
Posted Image

 

What to do?

 

The first line of thought, and this was kind of alluded to in the section above, is simply checking the surface below the ship. While we don't have cubes anymore, we do have the magic of raycasting. We could raycast straight down (relative to the ship, of course) from the center of the ship, but we'd run into the issues mentioned above.

 

Another idea pitched way back when was to simply auto-level to -Z (straight down relative to the world). There are three issues with that approach that caused us to reject it: First, it is a severe step back from the functionality that existed in classic Descent, since a ship wouldn't be able to flip over with auto-level engaged. Second, it'd make levels like Tycho completely unflyable, since Tycho relies on being able to realign to the plane of a ring to fly through it effectively. Finally, this approach does not handle the all-too-common situation where a ship's nose is pointed directly up or down.

 

In case you've never met a programmer before, here's something that drives us nuts: unhandled cases. A programmer must always be on the look out for so-called "edge cases", otherwise, those sorts of situations don't get explicitly handled, which leads to a programmer's worst nightmare: UNDEFINED BEHAVIOR. So, yeah, that last issue with the -Z option really cheesed my onions.

 

So, once we decided against these approaches, we kind of let the issue sit for a while. "A while" in this case probably being well over a year. Meanwhile, every so often, we'd get someone asking where auto-leveling is. And then just a couple of weeks ago, I had an idea.

 

The Running Average

 

Doing a simple raycast from the bottom of the ship wasn't going to work, I knew that, but what if I did multiple raycasts and averaged the result? And then, from there, to keep flight smooth, keep a running average of the results over the past second or so?

 

The basic principle was something like shocks in a car. Each wheel is equipped with a spring, such that, when a car goes over rough terrain, the springs keep the chassis from jerking suddenly and allow the wheels a bit of freedom in vertical movement. That was the thought, anyway. In practice, it didn't do much better than a single raycast would have. In addition, there was a case I hadn't even considered, one that would have never come up in classic Descent: corners.

 

Of course there are corners in classic Descent, but they're the unrealistic old-game kind: infinitely sharp. Real corners have curves (heh), and so modern games, including DU, have corners that have in-between surface geometry. On no map is this more apparent than Rama.

 

Posted Image

 

This edge right here made the development of the running average approach incredibly painful. One would typically fly the main room of Rama aligned to the floor, but that stupid edge, even with only one cast hitting it, would cause the ship to roll, which would then get the other casts to hit the wall below it, so you'd end up sideways, more or less. Disorienting and unfun.

 

Speaking of disorienting and unfun, another place where this method broke down was the U-shaped tunnels in each base of Colosseum. I tend to fly through them vertically, not bothering to rotate with them. With this auto-level implementation, I would invariably come out of them sideways.

 

Posted Image

 

There was another issue with this approach: raycasts take up more CPU time the farther they go, so there needed to be an upper limit on how far they cast out. This creates a -gulp- unhandled case. Wanting to avoid UNDEFINED BEHAVIOR (which, in this case, ended up being turning over infinitely), I came up with a way to handle the out-of-range case: default to the world axes.

 

For the sake of map-builders' sanity, our map kit is built to align to the world axes. As such, it can very easily be assumed that any given room large enough to let a ship go out-of-range on its level casts would be completely aligned to the world. So the ship would just roll to the nearest world axis. Easy enough.

 

So a ship near the floor would level to follow the geometry of the floor, while a ship up high would not. Playing around with this, I realized that the world-alignment just felt better. So I decided to try it with only world-alignment.

 

Finish Line

 

Stupid edge in Rama: resolved. Curved walls on the side of Rama: resolved. Curved tunnels which still exhibited some of the behavior mentioned above: resolved. Uneven surfaces: resolved. Grates: resolved. Random slants: resolved. The Colosseum U-shaped tunnels: resolved.

 

It felt so good. Again, the map kit is aligned to the world axes, so, as it turns out, any space within a map would be aligned to the world, at least in the direction one would be flying anyway. This difference is best made apparent in the outer ring of Mobius. All those tunnels are slanted, so flying sideways down them would have a ship drag along the floor or ceiling. But who does that for any appreciable distance?

 

Because the world-alignment method was so successful, I completely removed the raycasting code and packaged it all up. This was finished a day before the first "Jingle" build (2874) was put out.

 

Of course, you can, just like in the 90s, just turn it off in the settings. Personally, I haven't even bothered to turn it off, and I despised the implementation in the old games.

 

The Rest of the Story, for Pedants Like Me

 

For those of you familiar with 3D math and linear algebra and the like, you'd know that of course you can't roll to align perfectly to an axis if you're pitched away from alignment with it. Technically, what's actually happening is that the nearest world axis is projected onto the ship's YZ plane (X being the forward direction for Unreal) and then the roll is made towards that direction.




Photo

Happy Penguin Branch - How the Linux Version of DU Learned to Love Joysticks

Posted by Pixley , 15 September 2016 · 1,087 views
linux, joystick, sdl, input and 1 more...
Happy Penguin Branch - How the Linux Version of DU Learned to Love Joysticks

By now, I'm sure you've heard about the options menu revamp. As part of that, there were some modifications I needed to make to the joystick plugin to support the new joystick assignment system. Since I was going to be in there anyway, I took a look at making adjustments to provide some features that have been requested for a long time:

 
  • Separately identifying joysticks of the same model
  • Supporting DirectInput-only gamepads on Windows
  • Supporting joysticks on Linux
Supporting joysticks on Linux has been something I've been pondering for many months, and getting it accomplished was an interesting process.

 

Simple DirectMedia Library

 

SDL (https://www.libsdl.org/) is a multiplatform software library that makes input handling, window management, and many other input/output systems platform-independent through its abstraction layer. You can find it in a lot of different programs and many games, especially those that make it onto Linux. You can even find it used in Unreal Engine 4's Linux framework.

 

SDL is also used as the basis for the joystick plugin we use. You see, UE4 does not natively support joysticks, despite it having robust support for XBox-style gamepads, so we use an open-source plugin that several people have worked on (https://github.com/D...EJoystickPlugin). While robust, it has required some modification. However, its SDL foundation is strong, and has led to it working very well for Windows and Mac users.

 

Of course, all this discussion is meaningless without explaining how SDL works when it comes to handling input (of which joystick handling is a part). SDL reads in input events from the OS (that is, Windows, Mac OS X, or any number of Linux distros), then queues them up in an event queue. Then the user software (i.e., the joystick plugin) polls the event queue until all the events have been handled, either by reacting to an event or ignoring it, depending on what the user needs.

 

The Underlying Problem

 

However, like I said, the Linux version of the engine also uses SDL for its system events, especially gamepad input. As would be expected, the engine's "LinuxApplication" class is initialized (and rightfully so) before any plugins are, so the LinuxApp controls SDL for the entire executable. The joystick plugin is coded to respect this, detecting whether SDL has already been initialized and ceding the event-polling responsibilities to the engine.

 

I needed to get the LinuxApp to tell the plugin about joystick events, which were being flat-out ignored.

 

It was about 11 PM on a Tuesday night (long after I had stopped working for the day) when I made this connection. I furiously Googled the SDL documentation and pulled up the joystick plugin and engine code that I needed to begin the following day.

 

The Delegate

 

No, that heading isn't some kind of announcement that I'm making a TV drama about the internals of American intra-party politics.

 

Delegates are a way to abstract communication between two parts of a code base. Normally, if, say, the LinuxApp wanted to send some data to the joystick plugin, it'd need a reference to one of the joystick plugin's classes, thus making the engine dependent on the plugin. This is not desirable and defeats the point of a plugin.

 

Instead, what a delegate does is gives the intended receiver of the data the ability to say "hey, it doesn't matter who I am, but when you send out this event, I'm going to be listening for it". So, in this instance, the LinuxApp doesn't care who's bound to the delegate. It just calls it whenever it needs to, and whoever's listening will get the message.

 

From there, it's just a matter of having the receiving end of the delegate feed the data into the normal event handling system in the plugin.

 

How to Test

 

I believe I've mentioned before to some of you that I didn't have an actual Linux machine. I've done a couple of things just by running a virtual machine (finding out where the config and log files live), but I wanted to use a proper Linux box for this one, for a couple of reasons: 1) I didn't want to risk interference that VM software could introduce as far as getting the joystick to be properly recognized. 2) Unlike checking where the log files end up, I needed to actually play the game, and I was not about to put up with VM-hamstrung performance. So I used a computer I had made from old parts I had from previous iterations of my personal rig and installed Ubuntu on it. Then I plugged in my personal Logitech Extreme 3D Pro (the left-hand stick used in the Studio Snapshot clip demonstrating dual-same-model-joystick support).

 

Got the game installed and ran the build following the change.

 

And It Didn't Work!

 

As is to be expected, it's never just one thing. I, of course, made sure that the joystick was being recognized (with D1X-Rebirth, of course Posted Image ), and it was, but the inputs weren't being registered by DU. The joystick was recognized, though. All right, fine, let's throw in logging and see where the hang-up is.

 

After a build-download-install cycle, it turned out that everything was being initialized correctly. All the "keys" were being registered correctly. And, looking at the code, all the input events should have made it into the game. Something was missing...

 

Joystick Event Queueing

 

Way back in development (I don't remember if this ever made it out to a public build, that's how long ago this was), we had a problem with joysticks. Occasionally, we'd see the joystick report axis values way outside the bounds it should have been inside. Like, we'd move the joystick to half tilt and the game would report receiving ten times full input in that direction. As it turned out, multiple joystick motion events were being reported each frame. This had an additive effect. As such, I had to put in a mechanism by which the joystick plugin only reported the most recent position of that axis for that frame so that only one position was received by the game's input system. This solved the problem, and the way this was accomplished was to tally up all input events for each button/axis/hat, then send all the events we wanted to in one big push.

 

That big push was the thing I had forgotten. The push function was gated behind SDL ownership. Since the Linux version of the joystick plugin doesn't own SDL for the whole program, that push would never get called, thus never actually sending those events to the game's input system. So I moved it outside that gate.

 

And Now it Works!

 

One build-download-install cycle later, it looked like it worked exactly as intended. Bindings could be made, and the game was flyable (well, as flyable as it could be on an ancient Core 2 Duo and a GTX 550 Ti).

 

So that's how I got joystick support to work for the Linux version of DU. It's been a long time coming, and I wish I had made the realization I did a lot sooner, but that's how things go. It's like how you realize later on that everyone despises the input configuration system you made.

 

Speaking of which, next blog is about designing the new options menu.

 

Before I go, I should point out that the link above to the joystick plugin is actually to our version of it. Jason has put up several of the plugins we created or modified as open-source code on Github. So, if you'd like to peruse some of what we do on the C++ side of things, or even use them to help with your own game, by all means, take a look at https://github.com/DescendentStudios. I will warn you, though, that our version of the joystick plugin, due to what I described here, has a dependency on custom engine code. It is not compatible with UE4 out-of-the-box.

 

Until next time,
Tyler




Photo

The Persistent Lobby and Post-Game Scoreboard - An Unreal Motion Graphics Case Study

Posted by Pixley , 11 August 2016 · 2,988 views
umg, persistent lobby, scoreboard and 3 more...
The Persistent Lobby and Post-Game Scoreboard - An Unreal Motion Graphics Case Study

Would you believe that the imbecile who created the control settings menu (I really am sorry) is the same person who created the new persistent lobby-related UI?

 

Attached Image: Controls vs Lobby.PNG

 

You've probably noticed by now that the multiplayer lobby and the post-game scoreboard have both undergone severe cosmetic changes on top of the new functionality they now have. Function often (and should, in my opinion) drives form. Completely throwing out an asset is a commonplace in most industries involving iterative development, and it's especially so in game development.

 

Some Terminology

 

In order for this discussion to go smoothly, here are a few terms you'll need to know:

  • Widget - A UI element. It's not an atomic definition, though. DU's interface is built with many widgets inside of widgets inside of widgets. For example, the in-game chat box is a widget. Inside of it are two major widgets: the chat entry box and the chat log. Inside of the chat log are a scroll box widget and the chat tabs. Inside of the scroll box are each of the chat entries, which each contain a text widget.
  • UMG (Unreal Motion Graphics) - One of Unreal 4's user interface creation tools. UMG gives UI designers an interface in which to place and organize widgets without needing to have programming knowledge. It's a very powerful tool, especially if you know what you're doing.
  • Blueprint (BP) - Unreal 4's visual programming language. Instead of writing lines of code in text, BP is built out of nodes and wires connecting them. It's incredibly powerful and is easier on novice programmers than traditional programming languages, like C++. On DU, we use it for almost all gameplay functionality.
  • Scale Box - A thing that gives me headaches whenever it gets used. A UMG widget that ostensibly allows widgets inside of it to be scaled uniformly. It does not play nice with several other native UMG widgets, especially text. If you remember back when lobby chat would get all cut off on 4:3 monitors, that was because the whole match lobby was inside a scale box.
  • Canvas Panel - The most full-fledged of the UMG panel widgets. It allows child widgets to be effectively be placed arbitrarily on the screen, relative to certain anchor points (corners, center, and screen-edge middles).
Match Lobby and Creation: What Needed to Change?

 

It's pretty obvious that the old match lobby screen was kind of thrown together. Buttons and text fields crammed into the top of the window to provide a bit more functionality than was originally intended when the widget was created. Meanwhile, there was a ton of wasted space on the match creation window because we had intended to add to it.

 

The major contributing factor to the hodge-podgeness of the old match lobby was the addition of the chat. We needed an entire side of the screen cleared up to make room for it. All those buttons and text had to be shoved to the top: the only place left. It resulted in a barely-functional (as far as user-experience is concerned) mess of a lobby.

 

Attached Image: Old lobby.PNG

 

So, when I was assigned to do the persistent lobby, I knew I needed to throw the whole thing out, because we needed to accommodate one more major feature: configurable match settings. We needed a place to edit them, and we needed a place to display them. Also, to be consistent with every other major screen in the game, the chat was to be moved to the left side of the screen. But we only have so much screen space, and there's one more design consideration that is very important here: differing resolutions. 4:3 resolutions give us a lot less effective horizontal space. In order to make everything we wanted to fit, we had to remove a major block of screen space. That ended up being the game mode summary that used to occupy the lower-left corner of the window.

 

Also, we had always wanted to have the lobby show which ship each player currently has selected. There isn't a ton of room inside each player entry in the lobby, and the ready checkboxes were already there. But they were confusing, especially combined with the ready button in the old lobby. So the button was turned into a checkbox and the ready checks were turned into red/green backgrounds for the ship selection (with a bit of darkening on the ship icons themselves to emphasize the not-ready state). A simple combination, but an important and effective one.

 

Speaking of ship selection, we had to move the ship selector into 2D screen space. The 3D version of it was going to be far too much of a hassle to try to make sure it fit neatly beneath the rest of the UI.

 

Attached Image: New match creation.PNG

 

As for match configuration, because we were also adding that functionality to be lobby, it needed to be consistent with the lobby's way of organizing match info. Thus, we moved from the wide and empty screen to the tall and dense window. Again, mode summary was removed, but that gave room to add what we really wanted: proper match configuration. It's also worth noting that this screen is much more expandable. You can't see it now, since we only have the first batch of options in thus far, but that's all contained within a scroll box, so it can be added to as much as we want. This is in great contrast to the old match creation screen, which had the map and mode selectors in the upper left, taking up space. What would even go below them? What goes to the right? Can the right side scroll? There wasn't a coherent division of screen space.

 

Attached Image: Old match creation.PNG

 

Coherent Division of Screen Space

 

You'll notice with the lobby and the bottom third of the post-game scoreboard that the UI's neatly divided into three sections. This ensures compatibility across aspect ratios because the widgets are not placed arbitrarily. Instead, they are laid out horizontally, taking up different percentages of that space. You'll see in these examples what I call the "1-2-1" principle.

 

Attached Image: 1-2-1.PNG

 

Basically, the most important/space-intensive element (usually the same thing) occupies the center of the layout. Then, the two less-focused elements are placed on either side of it. The "1-2-1" comes in by dividing the layout into 4 columns. The center section will take up two of them, solidifying it as the point of focus. Meanwhile, the other sections will each have their own column. The layout is thus symmetrical, but emphasizes what needs to be emphasized. This looks much better than dividing the layout into even thirds, as it keeps certain things from taking up a needless amount of space. This is especially the case with chat. Chat taking up a third of the screen would just be ridiculous. Most chat messages are not long enough to fill that space, so it's effectively wasted. It also means that certain elements don't take up too much vertical space.

 

Take the map and mode images in the lobby, for example. Because they sit in a 1/4 column, they take up enough screen space to be seen, but not so much as to dominate the match settings display directly beneath them. If the column were 1/3 of the layout, one of two things would happen: 1) they would have to be blown up and take up an unnecessary amount of vertical space, or 2) the images would have to change to a wider aspect ratio, which would be fine, but the images could a lot more difficult to manage across aspect ratios. The images' behavior at their current 1:1 is predictable and will be easy to see across many different screens. However, at wider image aspect ratios and lower screen aspect ratios, they could potentially become difficult to "read".

 

Plus, it means that there is plenty of room to read players' names and see what ships they'll be flying.

 

A Scoreboard Fit for a Persistent Lobby

 

Coming back from the specifics of layout, the post-game scoreboard necessitated a lot of change in order to accommodate the new features we wanted to add as we brought it into the drone bay to be part of the persistent lobby system, so that players could peruse the scoreboard at their leisure, rather than having to cram all that reading into 10 seconds before the game server kicks them back to the drone bay. Three major UI features were to be added: information on the next match, especially what the ready status of the match is; chat, so that people can be notified when the other people in the lobby think they're spending too long in the scoreboard; and buttons to continue to the lobby or leave the lobby entirely.

 

From the standpoint of simply adding these things to what the user sees, it's pretty simple to accomplish: shove buttons in empty space, then raise the bottom of the scoreboard to shove chat and match status underneath. But there's a lot more that goes into that.

 

Attached Image: Old scoreboard.PNG

 

First of all, we're now having to get information to a completely different place than it was stored: score information is stored on the game server, and the client keeps a local copy of it for display to the in-game scoreboard. When the client disconnects from the server at the end of the game, due to the way Unreal handles its game framework, that information is lost on the map transition. I'm not going to go into detail, but the underlying reason this happens is a design decision I happen to personally agree with, and it makes in-game net code so much more sane. So, how do we keep the match results?

 

Well, again, two options: 1) when the server tells the client to disconnect at the end of a match, the client could copy the relevant information from the server-controlled game state into a data structure that won't get deleted upon map transition; or 2) use the match info we're sending to the API anyway and just have a way to get the match info back from it. Option 1 involves a lot of Blueprint that would make me very angry, not because it's hard, but because it'd be tedious to accomplish, a waste of memory for the game client, and bloat one of the few game framework objects that persist across map loads. (Blueprint bloat makes Rob's and my lives sad) Option 2 does not do either of those things.

 

What option 2 does do, however, is make it so that I'm working with an entirely different data structure than with the in-game scoreboard, which means that separate Blueprint has to be created to drive the UI. In short, the difference is that the in-game scoreboard uses the in-game player states, which are native to Blueprint and option 2 means I have to work with the data structure the web API gives back, which, as you might guess, is not an Unreal-native object. It's not a problem. It's just different.

 

Point is, we can't just shove the in-game scoreboard into the end-game scoreboard like we used to. But this is good. It means we have flexibility in how we decide to lay everything out. And boy did I need that.

 

My first instinct was to "1-3" the entire scoreboard (chat and next game info taking up 25% of the screen). It looked decent, but had a major problem: displaying the minimum amount of data for each player was a tight squeeze in 16:9. It was not going to happen in 4:3. Not to mention that it made the screen asymmetrical. I like layout symmetry. Plus, it did overemphasize the chat and next game info. Chat taking up 25% of the screen in the lobby makes sense because pre-game conversations are an important part of the lobby experience. However, the chat on the score screen exists as a "hurry-up" mechanism. As for the next game info, there's only four pieces of information that are important enough to transcend the boundary between lobby and score screen: map, mode, number of ready players, and when the next match will start. Neither of these things warrant a combined 25% of the screen.

 

Okay, great, let's let the scoreboard proper take up the full horizontal width of the screen, and then put chat and next game info below it. That creates another problem: the chat doesn't really look all that great when flattened out. You see very few messages and there's a lot of wasted space. And, again, with only four pieces of information, the next game info was taking up an entire half of the lower third of the screen. And how do you even lay the text within out with all that space? I needed something to take up more space. Something to force the lower third into 1-2-1, which would be perfect for chat on the left and next game info on the right. But what could be the '2'?

 

The solution: use the information I'm getting anyway! We've been sending more information to the API than we had displayed on the scoreboard historically. As such, putting the player's detailed stats as the '2' slot was a natural fit. Sure, it doesn't currently take up all the space, but, as we track more and more information, it certainly can and will. I didn't even tell the rest of the team that I was doing that. I just let them see it for themselves the first time we tested it together.

 

Attached Image: New scoreboard.PNG

 

The Take-Away

 

Even though I'm not a UI designer, nor had I really ever had any intention of being one, I've learned a lot since I cursed you guys with the control config.

  • Keep the layout neat and symmetrical.
  • Use size to emphasize the important parts of a screen and to de-emphasize the less important parts of a screen.
  • If a bit of UI can serve two purposes without being confusing, let it do that to save space.
  • Just because having a bit of UI in 3D may look awesome, doesn't mean it should be done. 3D UI has all sorts of considerations that need to be made, from field of view to separation from the rest of the 2D UI.
  • If you don't need a piece of UI to be ever-present, make it its own screen, rather than trying to shove it in where it doesn't fit and doesn't need to be.
  • Most importantly, consider the user, and don't confuse them, even if it makes your life a bit harder because it means you have to abstract away the way the functionality actually works under the hood.
Someday, probably soon, I'll be revisiting the control config screen with these lessons in mind. However, until then, as I look forward to this quality-of-life pass we're spinning up on, I need to take a quick look back. Posted Image

 

Until next time, which I hope will come much sooner this time,
Have fun,
Keep reporting bugs,
And, for your own safety, don't play on Wingman's team.

 

-Pixley




Featured
Photo

Proving Grounds Build 2065 - Featuring the FUSION CANNON

Posted by Pixley , 07 April 2016 · 1,472 views

Proving Grounds Build 2065 - Featuring the FUSION CANNON

Greetings, Undergrounders!

 

We need your help! We have some things to test out and you guys are wonderful for helping out!

 

Proving Grounds is getting updated today with a few changes we think are pretty cool. Skylab has undergone a performance pass, ping now shows up in the Match Browser, and, oh yeah, the FUSION CANNON is in!

 

 

 

NEW STUFF & Patch Notes (2065)

  • FUSION CANNON - this super-powerful primary weapon releases bolts of energy directly from your drone's fusion reactor. Charge it up to increase damage, but charge too much and you'll start taking damage from the excess energy. And don't think you're safe behind someone else if one's aimed at you, because it goes straight through ships, dealing massive damage on the way through. Look for it when flying around Skylab!
  • SKYLAB PERFORMANCE IMPROVEMENTS - Skylab turned out to be too much for VR, so we've made some optimizations to make it run more smoothly. Players on lower-end machines should also see an improvement!
  • IN-GAME FPS COUNTER - To make things easier on folks when evaluating performance (such as with Skylab and VR), we've added an optional FPS counter to the HUD (right next to the ping). You can enable it by checking the box in the Gameplay Options sub-menu.
  • MATCH BROWSER FIXES - Your ping to the game server now shows up in the Match Browser. Also, the "time remaining" column has been fixed, so now you can see how much time is left in a match before you join it. Finally, the Match Browser's default channel has been changed to "General" (thanks, tsa!).
  • Various minor bug fixes and improvements.
Known Issues:
  • Combat, Graphics, and Sound
    • The explosion particle effect for destructibles took some much needed holiday in Rio and is currently visiting Mr. Drax.
    • The Drone Bay has too many lights and so some people are getting poor performance while admiring their ships.
    • Objects near the corners of the screen can experience "barrel distortion" because of the way that 3D objects are mapped to a 2D screen. Using the Field-of-View slider can help with this in some cases.
    • Our guard bots in "Vs. Bots" mode did not pass the Drone Efficiency Recruitment Protocol and need more education.
    • Collisions are more sticky than they are supposed to be, sometimes causing ships to catch on each other.
  • Launching, Installing, and Platforms
    • Installer & Launcher need to be run as administrator on Windows.
    • Streaming software may need to run as administrator to capture video.
    • Standalone (non-Steam) launcher not correctly self-updating on Linux and Macintosh.
    • Uninstalling & reinstalling the launcher may not properly clear settings.
    • Steam Overlay doesn't work on Mac and Linux.
    • Part of the ship selection panel in the Drone Bay can get cut off on 4:3 displays. Selecting a 16:9 resolution can sometimes help with this.
  • Input
    • In VR, drop-down menus may not respond properly.
    • Some menus are not interacting with the VR head-look pointer correctly.
    • The axis configuration menu is somewhat clunky and uses too-technical language (power changes the curvature of the sensitivity curve, factor scales the sensitivity curve)
    • Linux appears to not be detecting joysticks in many cases.
    • Many joysticks do not yet have a default mapping and need to be configured manually.
    • Controller customization issues are still being experienced by some users. Control customization needs a "reset to defaults" button.
    • If you are having mouse issues, please make sure that your Mouse axes and sensitivities are NOT inverted and reversed at the same time.
    • Multiple joysticks of the same model cannot currently be distinguished by the game.
  • Matches
    • Private matches can launch even if some players are marked "not ready".
    • Auto-start timer in the match lobby doesn't yet display. Keep an eye on the status shown above for an indication as to how soon your match will start.
    • There may be a wait for matches at times. While our all-backer player base is much larger than the beta pool, player play times can vary.
    • In a match lobby, gamepad users sometimes cannot exit the lobby using the gamepad.
    • All servers are currently in the U.S., other locations are planned.
General Information
If you need to get the installer, CLICK HERE for instructions.
This test is public and we are happy if people want to share on Twitch and YouTube. In the event that something is running poorly or breaks while you're streaming, please let your viewers know that this is an Early Access build that you are playtesting. It is NOT the final retail release.
If you do plan to stream, you may need to run your streaming software as an administrator.
Our game manual is a series of forum posts:
http://descendentstu...46-game-manual/
If you have any questions about downloading or playing the game, that's the best place to check for a quick answer.

 

Bug Reporting
Please report bugs in our bug reporting forum with as much specificity as possible. If you can provide screenshots, that's awesome!
We've also compiled a list of known issues so that you can skip those when reporting bugs.

 

Fly safe!
-Descendent Studios




Photo

Proving Grounds Balance Pass - Build 1644

Posted by Pixley , 22 January 2016 · 905 views

Proving Grounds Balance Pass - Build 1644

Thank you for your help testing 1630!
This Proving Grounds test includes all the cool stuff from 1601 and 1630, but we've made another balance pass to bring it more in line with what we have planned for the roles and tech trees coming up.

 

New Stuff and patch notes! (build 1644)

  • The Shaman is now available for testing! This UFO-shaped healer ship comes equipped with the Repair Tool, which spends energy to restore allies' hull. Finally, you can restore more than shields!
    • The way the Repair Tool works is that if you're looking in the general direction of a hull-damaged ally within a couple of blocks, a restorative beam will latch on and persist as long as you hold down your tool button and are continuing to look at the target. When the target exits your range or is fully healed, the beam will automatically shut off. It will not target allies without hull damage or enemies, so your energy is never wasted.
    • Repair Tool beam and particles are temporary art. I'm going to be up-front with you guys and tell you that the beam right now is just a recolor of the Mining Laser.
  • The Shrapnel Cannon (official name pending) has been reworked slightly, with a minor damage reduction, major increase in rate of fire, and a doubling of its spread. Also, the means by which spread is produced has been adjusted to be a bit less samey between shots.
    • The projectile particles have been changed to look a lot less of a silly shade of blue and also less like Pixley squished down the Laser 4 particles. They look much more like streaks of molten metal.
    • A HUD voice cue has been added for the Shrapnel Cannon.
  • All other weapons have had their damage adjusted.
  • Miniature adjustments and stomping of bugs.
Upcoming
  • HOT ROCK will be bouncing into testing!
  • Energy Converter for the Shaman is on the way!
  • XMPP chat interface will be rolling out to everyone as soon as possible.
  • Tech trees are being designed for all of the ships.
  • Player progression is being designed, along with the in-game economy.
Known Issues
  • Some powerups are using similarly-colored frames for the time being.
  • Private matches can launch even if some players are marked "not ready".
  • Users who typed in chat and have since logged out before you log in may appear as "Unknown User".
  • Chat is not reliably working in Linux clients.
  • Objects near the corners of the screen can experience "barrel distortion" because of the way that 3D objects are mapped to a 2D screen. Using the Field-of-View slider can help with this in some cases.
  • Linux appears to not be detecting joysticks in many cases.
  • Many joysticks do not yet have a default mapping and need to be configured manually.
  • Our guard bots in "Vs. Bots" mode did not pass the Drone Efficiency Recruitment Protocol and need more education.
  • A mysterious "Powerup Yet To Be Named" occasionally appears to slay unsuspecting powerups.
  • Controller customization issues are still being experienced by some users.
    • Control customization needs a "reset to defaults" button.
  • Installer & Launcher need to be run as administrator on Windows.
    • Streaming software may need to run as administrator to capture video.
  • Standalone (non-Steam) launcher not correctly self-updating on Linux and Macintosh.
  • If you are having mouse issues, please make sure that your Mouse axes and sensitivities are NOT inverted and reversed at the same time.
  • Uninstalling & reinstalling the launcher may not properly clear settings.
  • Some placeholder sound effects were accidentally replaced with a different set of placeholder sounds.
  • Matchmaking sometimes puts players into matches that are about to end.
  • The axis configuration menu is somewhat clunky and uses too-technical language (power changes the curvature of the sensitivity curve, factor scales the sensitivity curve)
  • All servers are currently in the U.S., other locations are planned.
  • There may be a wait for matches at times. While our all-backer player base is much larger than the beta pool, player play times can vary. Matches won't begin until they have at least 2 players, and will not add players if there are 8 players.
  • Collisions are more sticky than they are supposed to be, sometimes causing ships to catch on smooth surfaces.
  • The Drone Bay has too many lights and so some people are getting poor performance while admiring their ships.
  • Multiple joysticks of the same model cannot currently be distinguished by the game.
  • Steam Overlay doesn't work on Mac and Linux.
  • The game is not launching correctly on Steamboxes.
  • Auto-start timer in the match lobby doesn't yet display. Keep an eye on the status shown above for an indication as to how soon your match will start.
  • When switching active chat windows (esp. entering a lobby from the default drone bay interface), messages sent to the previous window do not currently carry over to the new one.
General Information
If you need to get the installer, CLICK HERE for instructions.
This test is public and we are happy if people want to share on Twitch and YouTube. In the event that something is running poorly or breaks while you're streaming, please let your viewers know that this is an Early Access build that you are playtesting. It is NOT the final retail release.
If you do plan to stream, you may need to run your streaming software as an administrator.
Our game manual is a series of forum posts:
http://descendentstu...46-game-manual/
If you have any questions about downloading or playing the game, that's the best place to check for a quick answer.

 

Bug Reporting
Please report bugs in our bug reporting forum with as much specificity as possible. If you can provide screenshots, that's awesome!
We've also compiled a list of known issues so that you can skip those when reporting bugs.

 

Fly safe!
-Pix




Photo

Three Stones With One Bird: Fixing Movement

Posted by Pixley , 19 November 2015 · 1,489 views

Three Stones With One Bird: Fixing Movement

Attention Windows 10 users: If you have been experiencing the launcher crash-on-start, the recent major update to Windows 10 has fixed it. Please update Windows 10, and then you should finally be able to play Descent: Underground.

 

Your regularly-unscheduled blog follows.

 

It's always a fun day for a programmer when something gets fixed by accident. Even better when two things get fixed by accident. And even better when you hadn't even realized that second thing was actually a problem and that it was a core issue.

 

What was broken?

 

Before I answer that, I'm going to talk about how I found it. Recently, I've been working on the afterburner system. Seems simple, yes? Hold a button - go forward faster. I ended up banging my head against it for a while, because, though it was a fairly simple implementation, I couldn't get it to work consistently. I would stare at my Blueprints, dig through print statements, and spend a fair amount of time staring at the ceiling.

 

Come Tuesday morning, when I finally resumed work on it after having broken off to work on some other things in preparation for near-future releases. I tried to zip up and down the long 2x1 hall in Valor. It was there I discovered something peculiar: I could only use the afterburner when moving towards the Mega's side of the map (or, in the positive-X direction). I didn't even have to be facing that direction either. I could be strafe afterburning, as long as I was strafing in the positive-X direction.

 

Understandably, I was somewhat suspicious. And I did what all good programmers do in a situation like this: test in the most basic of cases. So I built a quick map that had tunnels leading out from the center in the six cardinal directions (+X/forward, -X/reverse, +Y/right, -Y/left, +Z/up, -Z/down). And I labelled them with floating words so as to not get all turned around. I needed to know which direction I was going. It was on that map I discovered something peculiar: the max speeds in the various directions weren't being enforced relative to the drone. They were being enforced relative to the world!

 

World Space vs Local Space

 

In games, there is the concept of world and local spaces. This applies to location, rotation, and scale. Say I have two cubes. One (cube 1) at the world origin (0,0,0), and one (cube 2) 10 meters to the right of world origin (0, 10, 0). The coordinates I've given are in world space. They're relative to the center of the world. Now, if we were to shift so that we see the world from cube 2's local space, those coordinates will change. In local space, cube 2 is now (0,0,0), and cube 1 is now at (0,-10,0). Everything is now relative to cube 2.

 

Like I said, this also applies to rotation. If I'm playing Capture the Core in Apophis, and I'm flying "upside down", that means I'm inverted in world space, but, in my local space, the core would be the thing that's upside down, not me. I'm always right-side up relative to myself.

 

Now, this also applies to velocity. This is important, as this is where the crux of the problem lies. Say I'm flying a Predator full speed straight towards Phil, who is also flying a Predator at full speed towards me. In world space, we're each going 66 meters per second (subject to change), but, in my local space, I'm motionless. I don't move relative to myself. Phil, however, in my local space, is screaming towards me at 132 meters per second. Velocity has direction, which is the most important part here.

 

The directionality of velocity when it comes to world space is particularly important when it comes to trichording. For example, say I'm in a level that is nothing but a long tunnel along the X axis. For all intents and purposes, relative to the world, I can only go forward and backward. Now, I'm a busy person with important things to do, and I want to get through that tunnel fast, so I trichord. So now, I'm facing down and to the left and am slightly rolled. When I begin to trichord, in local space, my velocity is basically at a 45-degree angle up and to the right from my forward direction. I'm still in that tunnel, though, which means that, in world space, I'm merely going along the positive X direction. Seems a lot different, huh?

 

It's like how if I'm sitting here in the Descendent office, I'm motionless relative to the surface of the North American tectonic plate. Relative to some other tectonic plate, I'm moving at a few cm/year. Relative to the sun, though, I'm moving at 30 km/s. Big difference, huh?

 

Hopefully, you guys have a decent understanding of the difference between the two. I'd provide drawings, but I'm bad at that. Now that we have that out of the way, let's get down to the problem:

 

Velocity Maxima in World Space

 

Wouldn't it be weird if you played an FPS, but, no matter which way you faced, W was north, S was south, A was west, and D was east? That'd be really confusing, right? Of course, that isn't how DU works, or has ever worked. That would be silly. We wouldn't release that. In fact, Unreal even accounts for this by translating input into world space for you before it even gets to the Pawn (object in the game that is controlled by a player or AI). Great, right? The problem here is that the translation is done assuming a couple of things:

  • You're controlling a humanoid character.
  • Your character moves at the same speed in all directions.
We thought we did a pretty good job of taking care of those two assumptions in our ship code. But we forgot/didn't notice that we were getting the processed input back in world space. And then we were applying the maneuverability stats to that world space input. Which meant that rather than having a specific forward speed, you'd have a specific speed on the world's positive X axis.

 

Luckily, it's easy to change vectors (a 3D value indicating direction and magnitude in which our input is provided) to and from local space. So we took the input, flipped it to local space, did the movement stat processing in local space, found the acceleration we needed in local space, then flipped that acceleration back to world space. Now, afterburner worked, no matter which direction you were facing, but only in your forward direction.

 

So, What Else Was Fixed?

 

Well, now the different speeds in different directions for the different ships (the Panzer having low forward speed but good slide speed) will actually be the case. If you've ever felt like you were going different speeds in different places, that's what it was, and that's fixed now. Kind of made the stats garbage, and that would be a huge problem in balance. Fundamental property of the game: fixed.

 

However, what I'm sure will be a bigger deal to a lot of you, is that this fixed trichording! Yes, we said that it was a low-priority issue, but we fixed it by accident. You're welcome.

 

When are You Going to Fix the Mouse All the Way?

 

Soon.

 

Until next time,
Pixley




Photo

Dr. Strangepitch - or - How I Learned to Stop Worrying and Fix the Mouse

Posted by Pixley , 21 October 2015 · 3,658 views

Dr. Strangepitch - or - How I Learned to Stop Worrying and Fix the Mouse

Now that 995's been out in the wild for a while now, and I haven't seen any complaints about mouse control that haven't been resolved by removing the double-invert setting, I think it's about time I talk about the process I went through for fixing it. People seemed to be interested, and we haven't put out a blog in a few days, so I figured I'd sit down and write this up instead of staring at Buildbot.

 

The Issue

 

The mouse control for pitch and yaw was, in a word, borked. Any mouse of a higher DPI than the crummy old Dell office mouse I had would respond in such a way as to be completely unplayable. Turning down the sensitivity factor/scale (don't ever touch the power/curve for mouse - you want linear mouse response) helped for some, but, again, only at lower DPI. I think the default ended up being .25 or something like that, and that was for my crap mouse.

 

But what were people actually experiencing, you may ask. There were three primary problems:

  • Mouse control was so sensitive as to make precision aiming impossible.
  • The ship would yaw in a wobbly fashion as it pitched, even if the player was moving the mouse straight up and down.
  • The mouse seemed to respond differently as framerate changed (which was the most apparent across levels)
This was, of course, unacceptable. I was actually very frustrated to learn that people were having these problems, since they weren't showing up on my end. Scale of 1 was a little touchy, sure, but it wasn't showing the wobble.

 

Tools of the Trade

 

My first meager attempt was to simply scale down the mouse response in-code, such that 1 became an acceptable level for more people. Unfortunately, since I had such a low-quality mouse, this made the game unplayable for me, as my control became sluggish. We couldn't have the default settings be unplayable for all but a small segment of gaming mice.

 

So Elfindreams, champion of men that he is, donated a Razer Ouroboros to the cause. As soon as I plugged it in and launched a game session in-editor, I finally saw the issues I had only heard about from other people (especially Erio). Now I had a good starting point for fixing this.

 

No Fixes from Old Games

 

Some of you might remember my thread in General Chat about this, but I decided to start off by seeing how D1/2 handled the problem. I wanted to see how the same motion felt. To my great surprise, it turns out that the exact same behavior existed in those games. So I booted up D3. Same there! That's when I posted the thread asking how people dealt with it for those games, since we have folks in the community who have intimate knowledge of how Descent works, but there didn't end up being any conclusive comment saying "this is how I avoid this". I was on my own.

 

Axis Dominance

 

The technical side of the issue was this: mice in Unreal do not behave in the same way that joystick and gamepad axes do. Controller axes are reported as floating point numbers from -1 to 1. However, mice report in integers. The smallest movement that would register on your mouse would give +/- 1. So, of course, the reasonable thing to do is to scale everything down. However, due to the large range of DPI possibilities out there, there wasn't a one-size-fits all solution. I had to be more creative.

 

The solution would have to be something that would ignore or greatly reduce the influence of the "nondominant" axis. For example, if I'm pitching straight up, the Y-axis (up and down) of the mouse would be dominant over the X-axis (left and right). First attempt was to implement a dynamic dead zone, where if the non-dominant axis was less than, say, 10 percent of the dominant axis, it would be ignored. It was better, but it resulted in it feeling kind of stiff, especially in cases where you'd mostly yaw, but want a little pitch, which is a common combat move, even against stationary targets. So a dead zone was out.

 

I needed smoothness. I needed the influence to be greatly reduced when extremely different, but not at all impact what would be normal flight. Most of all, I needed it to be completely unnoticeable without having print statements coming out telling you that there's a difference.

 

--Warning: Math Ahead--

 

Enter the natural log. For those of you who aren't math-types, the natural log of a number is the exponent to which you'd have to raise e in order to produce that number. e is a special number (~2.71828) that makes a lot of math things easy, and natural log (log-base-e), as opposed to the common log-base-10 you'd see in high school, was the blueprint node that was available to me. But why use a logarithm in the first place? Because logs increase in such a way that they squish orders of magnitude. For example, log10(10) (log-base-10 of 10) is 1 (101 = 10). log10(100) is 2 (102 = 100). log10(1000) is 3. But log10(11) is barely greater than 1 (~1.04139). log10(12) is barely greater than that. So small differences produce even smaller logs. So, then, if I divide by the log, that means that the number will be imperceptibly lessened. But when the two axes are orders of magnitude apart, then the division really kicks in.

 

So then I divide the non-dominant axis by the natural log of the difference between the dominant and the non-dominant. And, with a little bit of downscaling, those results were good. The wobble went away, and it felt smooth as butter. And, with this change, the game felt as good on a crap Dell mouse as on the Ouroboros, even at crazy-high DPI that no sane person would even be able to navigate their desktop with. But one issue still remained: framerate dependency.

 

Declaration of Framerate Independence

 

Why was the mouse movement framerate dependent? The answer is in two parts here. The first is that Unreal reports mouse axes by their change in position, so if I move my mouse three units to the right, it gives me a 3 on the X-axis. This is fine for most shooters, where you can whip around as quickly as you can throw your mouse across your desk. Descent is different, though. Turn rates are capped. There's acceleration involved. This means that if you're moving your mouse for a low-speed turn, and then you get a major frame drop for some reason, your turn will suddenly be quickened, even though you're moving your mouse at the same speed.

 

So frames need to be taken out of the equation. We get mouse input essentially as pixels per frame. If we divide by the framerate, which is seconds per frame, we suddenly have input in pixels per second. No frames! What have we turned our input into, though? We divided delta position by time, which is velocity. That's right! Mouse movement is now interpreted by the velocity along its axes. Totally framerate independent, giving you smooth, consistent rotation, regardless of your framerate.

 

By Your Powers Combined, I am Playable!

 

Putting mouse velocity and dynamic axis scaling together was easy, but not trivial. I had to divide the difference between the axes by 100 in order for the natural log to spit out a reasonable value. Here's the core scaling formula as it currently stands:

 

Attached Image: MouseScalerImage.PNG

 

I'm really happy with how you guys have received the change. I'm also happy that certain people on the team have stopped complaining at me. :P

 

So, until next time, fly safe. Even with the mouse.




Photo

Tech Underground - 14 Sept 2015

Posted by Pixley , 14 September 2015 · 1,173 views

Tech Underground - 14 Sept 2015

Hey, everyone! I, Pixley, am doing the blog today (because neither Rob nor Jason could be bothered :P). First off, let me give you today's Tech: Underground, featuring Jason and me as we discuss the brand-new Power-Up Manager (or PUM, as I like to say, because it sounds silly). If you have a few minutes you seriously have nothing better to do with, go ahead an give it a watch. However dry the content may seem, I am happy to be working on it.

 

 

 

Of course, that was recorded on Thursday, so things have developed since then. As of this writing, I am getting pretty close to finishing implementation. I'm currently in a battle with respawn delays on the missile and gadget power-ups. It's really annoying, especially since I'm trying to use a feature new to UE4.9 to get it to work, but functionality I'm trying to use is working the same way it did in prior versions. No matter. I'll figure out a way to do it eventually. It's like my 7th grade Algebra teacher once said: there's more than one way to skin a cat. That expression applies to programming as much as it did to Algebra way back when.

 

But, anyway, hopefully, by the time you folks read this, I'll have the new system implemented, and our issues with power-ups will be greatly ameliorated (pro-tip: never claim that something will fix everything).

 

Also, since this is the first time I've had the opportunity to publicly discuss it, I'd like to show my appreciation for everyone that posted in the (sigh...) Pixley's Pixels Fan Club thread. Despite the name triggering all sorts of uncomfortable memories from middle school, it makes me very happy that you guys appreciate what I do. This is seriously a dream job for me, and to get this kind of support inside and outside the office has been nothing short of wonderful.

 

If you have feedback on the show or topics you'd like to see covered in future Tech: Undergrounds, please let us know in the comments. I love talking at people about my job.

 

And now, back into Blueprint. Until next time!

 

Pixley