It's the Hat Update! Monumental Failure has added 7 new hats available immediately. There is a special 8th hat that only the most skilled builders can unlock.
Based on some feedback, we have changed the way worlds are unlocked. Now, 1 star earned in a world will unlock the next world. This should make it easier for players to see all of Monumental Failure's content. Patch notes:
0 Comments
Hey! Today I wanted to share an editor extension I wrote while developing Monumental Failure. This piece of code looks at a Terrain object and instantiates the Terrain's tree prefabs as their own GameObjects. It also groups these newly instantiated GameObjects based on their location, in a number of groups customizable in the editor. In Monumental Failure, our landscapes are decorated with trees and shrubs rendered in the same low-poly style as the rest of the game. Unity's terrain has the ability to place these trees and shrubs, but because they are regular models, and not Trees, Unity is unable to perform the optimizations and randomizations it does for Trees. This tool lets me use the tree placing feature of the Terrain, and then get randomize the rotation and scale of the trees. An added bonus is a huge reduction in draw calls. The .cs file and raw code is below. Let me know if you find some use for it. Cheers, Spencer
using UnityEditor; using System.Collections; using System.Collections.Generic; public class TerrainTreeReplacer : EditorWindow { [MenuItem("Tools/Terrain Tree Replacer")] public static void ShowWindow() { EditorWindow.GetWindow(typeof(TerrainTreeReplacer)); } Terrain terrain; int treeDivisions = 5; float scaleMin = .9f; float scaleMax = 1.5f; float heightFudge = 0f; void OnGUI() { // The actual window code goes here GUILayout.Label("Replace Trees with Prefabs", EditorStyles.boldLabel); terrain = EditorGUILayout.ObjectField("Terrain:", terrain, typeof(Terrain), true) as Terrain; GUILayout.Label("Tree groups " + treeDivisions); treeDivisions = (int)GUILayout.HorizontalSlider(treeDivisions, 1, 10); GUILayout.Space(10f); GUILayout.Label("Tree small scale " + scaleMin); scaleMin = GUILayout.HorizontalSlider(scaleMin, .5f, 1f); GUILayout.Label("Tree large scale " + scaleMax); scaleMax = GUILayout.HorizontalSlider(scaleMax, 1f, 3f); GUILayout.Space(10f); GUILayout.Label("Height fudge " + heightFudge); heightFudge = GUILayout.HorizontalSlider(heightFudge, -2f, 2f); GUILayout.Space(10f); if (GUILayout.Button("Replace trees!")) { Convert(); } } public void Convert() { if (terrain == null) return; GameObject treeParent = new GameObject("Trees"); List<List<Transform>> treegroups = new List<List<Transform>>(); for(int i = 0; i < treeDivisions; i++) { treegroups.Add(new List<Transform>()); for (int j = 0; j < treeDivisions; j++) { GameObject treeGroup = new GameObject("TreeGroup_" + i + "_" + j); treeGroup.transform.parent = treeParent.transform; treegroups[i].Add(treeGroup.transform); } } TerrainData data = terrain.terrainData; float width = data.size.x; float height = data.size.z; float y = data.size.y; float xDiv = data.size.x / (float)treeDivisions; float zDiv = data.size.z / (float)treeDivisions; foreach (TreeInstance tree in data.treeInstances) { Vector3 position = new Vector3(tree.position.x * width, tree.position.y * y, tree.position.z * height); int xGroup = (int)(position.x / xDiv); int zGroup = (int)(position.z / zDiv); position += terrain.transform.position; float scale = Random.Range(scaleMin, scaleMax); position.y += heightFudge; GameObject newTree = Instantiate(data.treePrototypes [tree.prototypeIndex].prefab, position, Quaternion.Euler (Random.Range (0f,360f) * Vector3.up) ) as GameObject; newTree.transform.localScale = scale * Vector3.one; newTree.transform.SetParent(treegroups[xGroup][zGroup]); } terrain.drawTreesAndFoliage = false; } }
This week, I'm going to write a brief technical devlog on how I have set up volume control options in Monumental Failure. Let's first show our final result. Pretty typical sound options eh? In my implementation, the SFX and music channels can be affected individually, and the master will affect them both. I've also made the decision that 70% volume will be "normal", allowing the user to both decrease and increase volume. Our first step is to create an Audio Mixer. The audio mixer will have 1 group to start, I have named mine "Master". I then create two subgroups called Music and SoundEffects. You of course can create more groups as you need. For each group, you must expose the volume variable to script by right clicking and selecting Expose. I'm not going to give details on how to do this next step, but there are two things you need in your scripts. The first is a reference to each of the AudioMixerGroups in your AudioMixer. This lets you set the volume variable you just exposed. The second is to ensure when your game plays audio, it is routed to the appropriate AudioMixerGroup. I solve these problems by having an audio controller class that stores references to the AudioMixerGroups. When I want to play a sound my scripts call a method in the audio controller which assigns the appropriate AudioMixerGroup. The final step is translating the percentage in the Options menu into a value for volume. You will have noticed that the AudioMixerGroups have volume which can be set from -80 to +20 decibels. Decibels create a problem. Decibels work on a logarithmic scale, which means +10 is loud, and +20 is really really loud. Similarily anything below about -40 is nearly inaudible. How do we choose what decibel values our volume percentage translates to? My solution starts by declaring a minimum DB, maximum DB and normal DB percentage (70% as mentioned above). const float MAX_DB = 10f; Here's where it gets a little ugly. I have a method that passes in a volume percentage as a float between 0 and 1. If that value is greater than my default percentage, I'll linearly interpolate between 0 and my max volume, otherwise, I'll use a circular out interpolation to find a value between 0 and my minimum volume. public void SetMasterVolume(float iVolume) Perhaps if my memory of how logarithmic scales work I could find a method better than this ugly code, but sometimes you just have to go with what works. Let me know if you have a better solution, or if my solution helped you!
Cheers, Spencer This week I worked on adding a cooperative play mode to Monumental Failure. If you have 2, 3, or 4 local players, you can play through any level together, working on a single monument. Fun! I've mentioned before that I've been borrowing from local multiplayer game design, I thought today I'd take a moment to discuss why. There's a game interview podcast I listen to where the host routinely asks the guest "what game has made you laugh?" I think there's a lot of insight into the state of comedy and games contained within this question. Asking it to myself, I'm immediately tempted to list the comedic games I've played. Games with good writing, absurd premises, and silly designs. Games like Just Cause, Octodad, or Jazzpunk. Funny games. While I have huge respect for design and achievements of those games, I feel that if I'm being honest with my answer, I can't really say these games made me laugh. Maybe a chortle here, a snicker there, and plenty of wry smiles, but never the full gut busting laugh. For me, the games that make me laugh most are the games I play with my friends. With my friends, in the same room, specifically. Certainly there are the games designed to elicit comedy, something like Gang Beasts or a Jackbox game. But beyond the explicitly comedic, there is so much comedy to be found in the stories a game tells. Think of the comeback earned by a bullshit weapon pick up, the assured mutual loss ("I can't win and so you won't either"), getting hilariously screwed out of sheep in Catan, or whatever other story the game might tell. In the right context, with the the right friends, you have the potential not just to laugh, but create lasting memories. Monumental Failure is supposed to be funny. From my experience, the way I can guarantee the humour resonates is to encourage playing it with friends. Be it competitive, cooperative, or maybe even asynchronously via a stream or video. Accounting for all the local multiplayer options guarantees more work for me, but I'm confident it will pay off.
Thanks for reading, Spencer Happy Halloween! For this week's devlog I figured I would write the third installment of "Things I Use To Make This Game". Find the first installment here, and the second here. I have really enjoyed highlighting the tools that make my game development life easier, and hopefully it helps other devs too. Kenney Nature Pack. I'd be surprised to learn of a game dev who hasn't seen that great work Kenney is doing, making free game art for anybody to use. As I move forward with locking in the visual aesthetic of Monumental Failure, having a bunch of low poly nature models ready to go is super valuable! Unity3D - Simple Finite State Machine (C#). Finite state machines are extremely useful tools in game development, and a fundamental idea in computer science. It's with great irony that most programming languages don't easily represent state machines. Simple FSM does all the hard work, and you're left with a state machine that follows the same patterns of a standard Unity Monobehaviour. If you're a Unity programmer and need a finite state machine, this is the solution.
Probuilder. Probuilder is a mesh editor that runs inside Unity. Most of Monumental Failure has been prototyped using cubes and cylinders and every other basic shape built in to Unity. Probuilder let's me construct a mesh right on top of my prototypes, right in Unity. Very useful! That's it for today. Maybe you can use one of these tools in your own project. Thanks for reading. -Spencer Last night Monumental Failure passed a significant milestone -- all the levels that will be included in the initial release are now done! Well, sort of... I still have several passes of game play and graphical polish to do, but none the less, we're getting there! Today I'm going to discuss some ideas that initially didn't work, and the solutions I found to make those bad ideas good. I was inspired by the design from this Mario Party 2 mini-game to challenge the player to stay on top of a cylinder. The constant shuffle and adjustment left and right should be an interesting challenge. Well, unfortunately for our physics driven game, once you started to tip a little bit, it wasn't long before you tipped a lot. Loss was too sudden. The solution? Conveyor belts, alternating between pushing you left and right meant you had to readjust, and if you got pushed all the way off, well, you probably saw it coming. I had designed this crazy crane vehicle to be used in the Bayon Temple level, and for a long time it was one of the most confusing and glitchiest aspects of the game. At the start, the characters were physics objects standing within the vehicle. While it seems like physics objects would be in the spirit with the rest of the game, the result was that your character couldn't consistently press the buttons to make the vehicle go, and worse, they could get bounced right out of the vehicle. Oops! The solution was to simply make the characters part of the vehicle, rather than making them physics objects. When building the pyramid, you will have to traverse some crumbly stone columns that tip over, that ultimately help you get to your destination. The initial prototype used some green cylinders to represent these tipping platforms. When Jess tested them out, she didn't realise they were tipping and attempted to hop from one to another, and it didn't work and she was frustrated by it. The solution here was to better visualise that something was happening. Green cylinders got replaced with craggy, wiggling rocks. Particle systems create dust when the tipping starts. Crumbling sounds play, ending with a good thud when the tipping is done. The behaviour was then obvious. Those are just a few of the tweaks I've made up till now, and like I mentioned above, there's bound to be a few more. Given that the level design is inherently antagonistic to the player, it's important that the challenge is clearly communicated, and that failure clearly feels like a fault of the player. Simply put, I need to create a fair game.
Thanks for reading, Spencer The design of Monumental Failure is hugely influenced by the design philosophies of local multiplayer games. That doesn't mean it's exclusively multiplayer, you can play it solo, no problemo! But, let's say you have some friends over, maybe you'll have more fun if you split the screen and fail together! Anyways, one of the design ideas we are appropriating is character customization. In a multiplayer game, getting the player to customize and familiarize themselves with their characters imbues an immediate connection with their characters. This is especially helpful to combat the "I was looking at the wrong character/screen" problem people often experience with local multiplayer games. When I started working on implementing character customization I wasn't sure how I would introduce the user interface required to allow the player to set their customization. I started with examining what the player could be customizing. We have 4 different character bodies, and while it would be possible to let the player pick which one to use, I use the different body types to help clarify gameplay elements within the levels. This means the player won't choose body type and will instead be customizing the whole "team" of characters. The characters have three attributes that can be swapped around, their skin color, their clothes color, and their hat. It felt important to us that the characters' skin colour stay relevant to the culture they are representing. That leaves us with just the hat and clothes colour to customize. Conventional wisdom would suggest that character customisation happens right around the time a player "presses START to join". This doesn't really make sense for Monumental Failure. Loading four characters in to a menu would make for a pretty crowded user interface. Additionally, not knowing your characters skin tone until you load a level would provide ambiguity to the characters finalised look. Not ideal for would-be fashionistas. This implies that character customization should happen after the level has been chosen, and if the screenshots haven't made it obvious, that's exactly what I did! This provided the additional benefit of having a pleasant character vignette to introduce each level, and even better, introduced a "Ready Up" feature. When the level loads, you have to press a button to start instead of being thrust immediately in to the gameplay. Honestly, that sounds like something I should have introduced a while ago :-P I always find it to be a rewarding experience when the solution to a design problem is arrived on by this kind of deduction. Using design constraints and philosophies brought me to a unique solution for Monumental Failure's customization problem. A solution with benefits greater than the problem it solves.
Thanks for reading, Spencer One of the challenges you'll navigate in Monumental Failure is steering characters who have roll around on giant balls. In this blog post, I'm going to walk through the implementation of such a character with a specific focus on how to correlate the character's speed to the ball's rotation. Let's take a look at the final product first. In Monumental Failure, the characters a typically composed from a single rigidbody and a few primitive colliders to represent their physical form. In the case of the ball riders, they are represented by a capsule for the human, and a sphere for the ball. The animated components, the human and the ball, are rotated as child objects while the parent rigidbody has a frozen rotation. When we animate this character, the result should be as follows: the character faces forward, the ball rolls forward, and the character walks backwards, implying they are rolling the ball forward. Facing the character the right way and getting them to walk backwards are trivial problems already implemented in other levels. It took me a bit of trouble to figure out what was the best method to rotate the ball graphics object (remember it is a child of the top level rigidbody). The best way was simply Transform.Rotate, passing Space.World as the relativeTo parameter. The code is something like: public GameObject sphere; And the result is: Not bad right? Take a closer look though, a little slidey maybe? Hmmmm. Be suspicious of my code above, I'm rotating stuff in FixedUpdate and not using Time.fixedDeltaTime. Something must be amiss! At this point we've simply got lucky that our character's speed was close to the amount we need to rotate by, but it's not accurate! We can do better! What we want to do is exactly correlate the character's speed to the rotation of the ball. If we multiply the character's velocity by the fixed delta time, we will get how far the character has traveled in the last frame. To correlate that distance to an amount of rotation, we simply use the circumference of the sphere to figure out how many degrees we need to rotate per unit moved. For example, if our circle has a circumference of 4 units, and our character has moved 1 unit, we would rotate 90 degrees. Easy! public GameObject sphere; Not bad eh? I hope this bit of behind the scenes tech talk was interesting, or maybe even helpful!
Thanks for reading, Spencer |
Archives
March 2017
Categories |