Jump to content
  • Advertisement


  • Content Count

  • Joined

  • Last visited

Community Reputation

73 Neutral

1 Follower

About jb-dev

  • Rank

Personal Information

  • Role
    3D Artist
    Game Designer
  • Interests


  • Twitter
  • Github
  • Steam

Recent Profile Visitors

The recent visitors block is disabled and is not being shown to other users.

  1. jb-dev

    Vaporwave Roguelite

  2. Last week wasn't concretely visual or anything... It was mainly an integration one. There was also some graphical updates plus I've also made part of the options menu. Graphics First, let's talk about those graphics. Previously, the skybox was of a solid colour. This was a placeholder skybox. However, I thought about it and came to the conclusion that it was about time to have a better one. I've searched on the internet and found this lovely gradient skybox, so I've added it to the game but with a bit of pizzazz. Basically, I've added a screen-positioned grid pattern and make a big dot in it representing the sun. I've loved it so much that I've decided to add it to my levels too! Here's how it looked previously: And here's how it looks now: I personally like how this looks. Also, previously the directional light that lights the scenes weren't following the colour palette, so I've added a bit of script to help with that, hence the different lighting... Integrations I've mainly had to link up the menus and the actual first level of the game. To do this I've used the Scene loading functions of Unity. I've added a nice loading screen with a working loading bar that fills up to the loading process' actual progression. There is, however, a bit of a hiccup while loading the level. The game window actually freezes and seems like it stopped working... This might be related to the loading process of the Music Player I've previously made: while the process loads in a different thread, the first load is done on the main process. This makes the game hang up until the first song actually loading... This is something I'm currently working on. Options Secondly, I've also added a bunch of drivers to the options screen. Let's take a look at each tab... General This is where general options will be. Right now, there's only a dropdown to switch languages, but eventually, there might be more options if it's needed. Graphics This is where the meat is. Here, the player can set many types of graphical settings, like resolution and quality settings. There are six quality presets: Very Low, Low, Medium, High, Very High and AESTHETIC (Ultra). There's also a seventh one, called Custom, where the player can set each graphical settings separately. As of now, this setting doesn't do anything, as it is only a driver. Audio This is where the player can set all its audio configs. Mainly this is where the player can adjust any volume sliders. There's also another section called "Others" where other audio settings can be set. As of right now, there's only a toggle indicating whenever or not the VaporMaker is active. There might be more options in the future. That's about it for options: the other tabs are still in development. Minor Updates Reflection Probes! Now there's a bunch of reflection probes in special rooms. This means that lights will feel more natural and specular materials will look better. Shader optimizations. I've optimized some shaders and the game builds quicker. (Speaking of build...) I've created a really dirty private build of the game. While still building, there was a lot of bugs and isn't really presentable. The important thing is that it builds properly and without any hiccups. I've fixed some bugs with state loading and overall scene progression. Previously the level scene was completely separate from the menu screen, meaning that a lot of states appeared twice between the scenes. Because these are supposed to be singletons, many of the level's controller were removed, breaking everything. Next week Next week is going to be the continuation of the integration. I mainly need to fix my loading screen hiccup problems and many other things. Afterwards, I might need to make an actual progression between each level. As of right now, there's only one level with no exits. Afterwards, it's gonna be boss time! And then after it's the usual suspects... I gotta say, with all that integration, I'll be soon ready to actually publish some kind of demo or test build by the end of December.
  3. jb-dev


    If that would be the case then I think that Blizzard should have not have announced it at Blizzcon (IDK if Blizzcon-like event exists in Asia, though) Banjo-Kazooie Nuts and Bolts, anyone? 🙃 I've just looked at the Google Play Store top popular games list, and most of them were either strategy or puzzle games (there might have been one or two arcade games at best). While it's true that the market IS bigger on mobile it's only bigger within certain genera. Right now Blizzard is literally trying to port Diablo's genera and overall gameplay to the mobile which is statistically not really viable. As a rule of thumb, if you use virtual buttons or joysticks in mobile then you should really go back to the drawing board and really think about the UX... For example, let's take arguably one of the most popular mobile game ever : Angry Birds. The game uses intuitive controls that are simple to understand, which makes the game easy to play in short bursts. But most importantly, there's no virtual buttons nor virtual joysticks (aside for really simple things like pausing and such) So If the controls are too complex to understand in a fraction of a second then your mobile game is doomed to fail, and that even if the possible revenue is 15x bigger on phones than on PCs. (And so far, Immortal's screenshots aren't promising...)
  4. jb-dev


    I kinda think that this wasn't the time nor the place to announce this. For what I've heard many of these people attending the event were die-hard PC gamer, so this was just distasteful of Blizzard to do that. It's a bit like talking about Xbox to a Sony crowd. (Personally I think that making a mobile game need a whole lot of consideration and UX knowledge. You can't just plop a virtual joystick and call it a day)
  5. Last week started with some kind of epiphany: I've kinda forgotten the idea of the iterative top-down development of Agile development, which is sad. So after a bit of reflexion, I've decided that it was time to start building stuff like menus, save files and playable characters. This way I could ship a playable alpha quicker, meaning that I could start getting monetary funds sooner. But anyway, let's get right to it! New Shader Beforehand, let me show you my newest shader. This one is supposed to mimic Memphis-like designs. The name "Memphis" comes from the "Memphis Group", an Italian designer group that tried to design THE design of the decade (which coincidently was the 80's). This design was mainly expressed through furniture, although it was still used in many other media. Here are some examples: (Fun fact: most furniture made by the group was actually bought by David Bowe for some reason. By far he had the most exhaustive collection.) But anyway, here's the shader: The shader is supposed to represent a rough texture typically found in many Memphis designs like so: As for the actual shader, I've first experimented with that kind of noise while making splash screens mockups in Blender. Here's the original mockup: (Notice the green bar on the left) This was achieved by using the Musgrave cycles node. The main gist is to use a Perlin noise to which we apply different types of filter to change their appearance to match the desired texture. So I've figured that if I wanted to translate this shader in Unity I needed to take a look at blender's source code, which is coincidently open. Here's the final shader code: float4 mod(float4 x, float4 y) { return x - y * floor(x / y); } float4 mod289(float4 x) { return x - floor(x / 289.0) * 289.0; } float4 permute(float4 x) { return mod289(((x*34.0)+1.0)*x); } float2 fade(float2 t) { return t*t*t*(t*(t*6.0-15.0)+10.0); } float4 taylorInvSqrt(float4 r) { return (float4)1.79284291400159 - r * 0.85373472095314; } // Classic Perlin noise float cnoise(float2 P) { float4 Pi = floor(P.xyxy) + float4(0.0, 0.0, 1.0, 1.0); float4 Pf = frac (P.xyxy) - float4(0.0, 0.0, 1.0, 1.0); Pi = mod289(Pi); // To avoid truncation effects in permutation float4 ix = Pi.xzxz; float4 iy = Pi.yyww; float4 fx = Pf.xzxz; float4 fy = Pf.yyww; float4 i = permute(permute(ix) + iy); float4 gx = frac(i / 41.0) * 2.0 - 1.0 ; float4 gy = abs(gx) - 0.5 ; float4 tx = floor(gx + 0.5); gx = gx - tx; float2 g00 = float2(gx.x,gy.x); float2 g10 = float2(gx.y,gy.y); float2 g01 = float2(gx.z,gy.z); float2 g11 = float2(gx.w,gy.w); float4 norm = taylorInvSqrt(float4(dot(g00, g00), dot(g01, g01), dot(g10, g10), dot(g11, g11))); g00 *= norm.x; g01 *= norm.y; g10 *= norm.z; g11 *= norm.w; float n00 = dot(g00, float2(fx.x, fy.x)); float n10 = dot(g10, float2(fx.y, fy.y)); float n01 = dot(g01, float2(fx.z, fy.z)); float n11 = dot(g11, float2(fx.w, fy.w)); float2 fade_xy = fade(Pf.xy); float2 n_x = lerp(float2(n00, n01), float2(n10, n11), fade_xy.x); float n_xy = lerp(n_x.x, n_x.y, fade_xy.y); return 2.3 * n_xy; } /* Noise Bases */ float safeNoise(fixed4 p) { return cnoise(p); } float noiseMusgraveHeteroTerrain(fixed4 p, float H, float lacunarity, float octaves, float offset) { float value, increment, rmd; float pwHL = pow(lacunarity, -H); float pwr = pwHL; int i; /* first unscaled octave of function; later octaves are scaled */ value = safeNoise(p) - offset; p *= lacunarity; for (i = 1; i < (int)octaves; ++i) { increment = (safeNoise(p) + offset) * pwr * value; value += increment; pwr *= pwHL; p *= lacunarity; } rmd = octaves - floor(octaves); if (rmd != 0.0) { increment = (safeNoise(p) + offset) * pwr * value; value += rmd * increment; } return value; } fixed4 frag(v2f IN, UNITY_VPOS_TYPE screenPos : VPOS) : SV_Target { half4 color = (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * IN.color; fixed2 g_Resolution = _ScreenParams.xy; float factor = -IN.texcoord.y + 1; float dimension = max(_NoiseDimensions, 1e-5); float octaves = clamp(_NoiseDetails, 0.0, 16.0); float lacunarity = max(_NoiseLacunarty, 1e-5); fixed4 p = screenPos; p = p / _NoiseScale; float intensity = 1.0; float offset = smoothstep(1, 0,factor); color.rgb = lerp(color.rgb, _NoiseColor.rgb, saturate(intensity * noiseMusgraveHeteroTerrain(p, dimension, lacunarity, octaves, offset))); return color; } The Menus Then the next big thing was to create different menus in the game. In order to achieve a somewhat proper menu navigation, I've first created a simple navigation flow chart that tells what is the flow between two screens. For example, the player firstly goes through the Title screen, then goes to the main menu and so on. As for the actual implementation, I've decided to use the State design pattern. This way I can easily create simple flows between two screen. First, I've got an AbstractMenuState class: using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.UI; public abstract class AbstractMenuState : MonoBehaviour { public AbstractMenuState m_nextMenu; public AbstractMenuState m_previousMenu; public Animator m_menuAnimator; protected GameObject m_menu; public GameObject menu { get { if (!m_menu) { m_menu = transform.GetChild(0).gameObject; if (!m_menuAnimator) { m_menu.GetComponent<Animator>(); } } return m_menu; } } // Show the menu public virtual bool ShowMenu() { menu.gameObject.SetActive(true); m_menuAnimator.SetBool(MenuState.instance.openParameterId, true); selected = firstSelectable; return true; } // Hide the menu public virtual bool HideMenu() { m_menuAnimator.SetBool(MenuState.instance.openParameterId, false); StartCoroutine("DisableMenuDelayed"); return true; } // Things to do when we procede to the next menu public virtual void OnNext() { } // Things to do when we return to the previous menu public virtual void OnPrevious() { } IEnumerator DisableMenuDelayed() { bool closedStateReached = false; bool wantToClose = true; MenuState instance = MenuState.instance; // We're disableing the menu once its animation transition is done while (!closedStateReached && wantToClose) { if (!m_menuAnimator.IsInTransition(0)) { closedStateReached = m_menuAnimator.GetCurrentAnimatorStateInfo(0).IsName(instance.closedStateName); } wantToClose = !m_menuAnimator.GetBool(instance.openParameterId); yield return new WaitForEndOfFrame(); } if (wantToClose) { m_menu.gameObject.SetActive(false); } } public GameObject firstSelectable { get { // We search for the first selectable child Selectable[] selectables = m_menu.gameObject.GetComponentsInChildren<Selectable>(true); for (int i = 0, length = selectables.Length; i < length; ++i) { Selectable selectable = selectables[i]; if (selectable.IsActive() && selectable.IsInteractable()) { return selectable.gameObject; } } return null; } } public GameObject selected { set { //Select the GameObject. EventSystem.current.SetSelectedGameObject(value); //If we are using the keyboard right now, that's all we need to do. var standaloneInputModule = EventSystem.current.currentInputModule as StandaloneInputModule; if (standaloneInputModule == null) { //Since we are using a pointer device, we don't want anything selected. //But if the user switches to the keyboard, we want to start the navigation from the provided game object. //So here we set the current Selected to null, so the provided gameObject becomes the Last Selected in the EventSystem. EventSystem.current.SetSelectedGameObject(null); } } } } Then I've also got a MenuState MonoBehavoir that is composed of an AbstractMenuState instance: using System; using System.Collections; using System.Collections.Generic; using UnityEngine; // This is a Singleton public class MenuState : MonoBehaviour { public static MenuState instance; private bool isReady = false; public AbstractMenuState m_currentMenu; /// <summary> /// Awake is called when the script instance is being loaded. /// </summary> void Awake() { // Setup singletion if (instance == null) { instance = this; } else if (instance != this) { Destroy(gameObject); } DontDestroyOnLoad(gameObject); isReady = true; } const string OPEN_TRANSITION_NAME = "Open"; const string CLOSED_STATE_NAME = "Closed"; private int m_openParameterId; public int openParameterId { get { return m_openParameterId; } } public string closedStateName { get { return CLOSED_STATE_NAME; } } /// <summary> /// This function is called when the object becomes enabled and active. /// </summary> void OnEnable() { //We cache the Hash to the "Open" Parameter, so we can feed to Animator.SetBool. m_openParameterId = Animator.StringToHash(OPEN_TRANSITION_NAME); //If set, open the initial Screen now. if(m_currentMenu) { m_currentMenu.ShowMenu(); } } // Proceding to the next menu public void NextMenu() { AbstractMenuStrategy next = m_currentMenu.m_nextMenu; if (next != null) { m_currentMenu.OnNext(); m_currentMenu.HideMenu(); next.ShowMenu(); m_currentMenu = next; } } // Returning to the previous menu public void PreviousMenu() { AbstractMenuStrategy previous = m_currentMenu.m_previousMenu; if (previous != null) { m_currentMenu.OnPrevious(); m_currentMenu.HideMenu(); previous.ShowMenu(); m_currentMenu = previous; } } } When the player clicks an OK button (for example), then the NextMenu() function of the MenuState is called. This internally calls the Show/Hide function of the needed AbstractMenuState and so on. The same thing is true for any "Cancel" type of button, but using the PreviousMenu() function instead of the NextMenu() ones. Because I'm using Unity, I can then just drag and drop each AbstractMenuState instance the m_previousMenu and m_nextMenu fields to recreate my flowchart. I can even technically set these dynamically based on which button was pressed (like in the main menu screen for example) So let's get right into each menu then! Title Screen This is the title screen. The player just presses any keys to pass right through it and go straight to the main menu. Main Menu This is the main menu. The player can navigate to different screens by pressing one of the menu's buttons. Let's see what each button do. Quick Play This button is quite simple; it quickly prepares a game by reusing previously used parameters. This way the player can quickly get in the game with just one click. This automatically picks the last used savefile and character and puts the player directly at the confirm screen. If it's the first time the game is launched, then the first savefile and the default character are used. This is kinda useful. You only need two clicks and you're ready to go! Play This one is the long version of the Quick Play button. The player then needs to pick a save file from 4 different saves. Right after that, the player chooses a playable character and is then put right at the confirm screen. Options This is self-descriptive. It puts the player at the options screen. Exit This button spawns a quitting prompt. If the player chooses "Yes", then the game quits. Otherwise the prompt closes. Savefile Screen This screen is where the player can choose which save files to use. A savefile holds many different types of pertinent statistics and information (Things like unlocks, run statistics and game completion). It also holds things like the last used character and so on. At this screen, the player will be able to do many different types of savefile manipulation like copy and erase. As of now, these operations aren't implemented, so only the OK button works. Before any operation buttons are enabled, the player must select a save file by clicking (or selecting if you use a controller) a specific save file. A selected savefile will have a green checkmark on its upper-right corner. When the player enters the screen the last used savefile is highlighted. If the game is fresh then the first savefile is used. Each save file also shows things like a progression mark and a last used date. I have yet to precise how the progression percentage is calculated, though I have some ideas on how this can be done (maybe thought unlocks and achievements, but I'm not sure yet). The player can also go back to the main menu using the "Cancel" button. Once the savefile is chosen, the screens translate to the character selection screen. Character Selection Screen This screen is where the player chooses its characters. Each character comes with different stats, focus and equipment. Each of these characteristics can be inspected through the white panel in the lower half of the screen. Like how these components behave in the pause menu, some of these also have tooltips. The characters themselves are laid in a circle formation. The layout system is dynamic enough to have n playable characters, and it's using the game object's children to do all of the layings. The currently selected playable character has a thick outline and it often the one closest to the camera. Also, the currently selected character is determined by the selected savefiles' last used character. If the data is missing then it goes to the default character. In order to change the currently selected character, the player simply uses both left and right arrows at both sides of the screen. This simply turns the contraption by a given angle (calculated from the amount of object present in the root game object). This changes the outline so that it'll follow the currently selected character. Again, the player can either go back to the save file screen or proceed to the confirmation screen. Confirmation Screen This is the last screen before starting the run. In this screen, the player can review its run parameters (like the used save file and the selected character). We can also set the generation seed at this point. As of now if the player chooses to go back then he goes all the way back to the main menu... I'm not sure if it's a better idea if we store from which screen the player came from and use this to send it back to the relevant screen. Options Screen This is where the options will be displayed. I didn't have the time to complete it yet, but it's still accessible. There will be different types of settings typical of any modern games, like graphical settings and whatnot. I've also planned to show statistic here. However, I'm not sure if I should have different tabs/view for each saves file... Technically I could place the save file selection right before the main menu and only show one statistics screen, but I'm not sure yet. That's about it for screens. Minor upgrades Because of the new shader, I've also changed some of the pause menus to use the new shader: There are also some GUI elements that also use these, like the aesthetics lines and health bars... I cant show them just yet because the menu kinda blocks the access to levels right now... I've implemented a playable character class that stores any character specific things like equipment and such; Also, save files are in. There's a simple serialization/deserialization going on with these: I'm not sure when to save though. Right now it's after each savefile changes (like when the player uses a crystal and whatnot). I've also added a GUI shader that uses a special "Hard Light" blend mode when dealing with GUI element textures. Normally Unity uses chromatic multiplication to blend GUI textures with colours. This, however, makes any pure white pixels on the texture take the actual GUI colour; (Hard Light on the left Left, Multiplication on the right) I've added a bunch of textures on buttons and panels. This really looks AESTHETIC now. I've added a normal mapped variant of the AtlasShader. This is quite useful for the menus' statues. Speaking of which these are actually still technically low poly (under 600 tris each!). Their normal maps were baked from high poly models with over 2M verts! Needless to say that Blender crashed a bunch of times while manipulating these... I've finally switched some GUI components to used the Unity's Graphic class rather than coding everything by hand and manually calling the CanvasRenderer. This makes the GUI element's code much cleaner and readable! Next Week If everything goes to plan the game will be in a playable state by beginning 2019 (or so I hope). There's still some work to do with menus... For example, the options screen isn't fully there and so far no runs can be started. I didn't complete the actual level completion, and this might be a good thing to do afterwards... Afterwards, I think I'll be able to code at least one dummy boss, with a somewhat simple attack pattern and whatnot. After it's the usual suspects: Relics, Capacities, Activated items, Equipment... The list goes on.
  6. Hello there, welcome the 18th edition of your favourite weekly update! 😃 Last week was really intense. A lot of things were going on at once, so without any further ado, let's cut to the chase! Pieces of equipment First, there's now pieces of equipment in the game. Players can now stumble upon t-shirts, runnings shoes, gloves and baseball caps during their run. Here's a picture of pieces of equipment in malls: In essence, pieces of equipment give the player unitary stat bonuses (things like +1 or +3) when worn. Pieces of equipment can also have a focus alignment: when worn by the player, his focus will be progressively pulled towards its focus alignment. There'll also be additional stats bonuses (and even capacities) when his focus is aligned with a piece of equipment. This means that managing your equipment is critical to have a good run. The player can hold different kinds (or slots if you want) of pieces of equipment. These are: Headware (caps, hats and whatnot); Neckwear(necklaces, neck chains, etc.) Chestwear (T-shirts, chest plates and so on); Handwear (Gloves, rings, etc.;); Footwear (boots, shoes and whatnot). The player can only hold one piece of equipment per slot. He can, however, replace his equipment by simply grabbing another one. GUI refactored Second, I've also done a big GUI overall. Although often forgotten by both gamers and developers, the GUI has an important role in games: they are often the primary way for games to tell their player critical pieces of information like their current health, money or current stats. The GUI was actually a copy-paste from the game's previous iteration with Lemur and JMonkeyEngine. With this refactoring, I've tried to use Unity's GUI components at their fullest. I personally think I've cut the mustard with this one. Status Screen Let's take a look at the status tab of the pause menu: As you can see, everything was kinda bare. There were no ways to tell where everything was, and quite frankly it was all over the place. Here's a look at the newly redesigned one: Now that's prettier! Things are nicely labelled, aligned and positioned. There are even components that were previously hidden, like the Relics section and the Focus section. There's even an Equipment section, along with a new Item section. Also, the status screen is more interactive than ever before! Some components have tooltips, showing useful things like name, description and many more. Relics Let's take a closer look at the relic section: This is where all collected relics go. If no relics were collected yet, a placeholder text is displayed. Each relic is displayed using a nice pictogram from my custom Open Type font. As a bonus, the section is scrollable. This means that if there's a whole lot of relics the view can be truncated and controlled using a scrollbar. (Keep in mind that I've enlarged the icons sizes to show the scroll bar) Equipment The display is quite simple: it's a minimal silhouette of a human body. Each equipment slots are placed according to their type (e.x. the headwear is on the head, etc.) When a slot is empty, a pale "X" is displayed. Otherwise, a glyph representing the grabbed piece of equipment is displayed. Each of these has tooltips attached to them, showing the pieces' name, description and quality. Focus This is a barycentric coordinate component rendered as a triangle representing the player's focus. The only thing new is that it's now properly aligned and labelled... (Now look how happy it is with its proper title!) Item This component shows the currently hold activatable item. Not unlike equipment slots, having no held activatable items yields a pale "X". It also has a tooltip displaying the item's name and description. Foods Previously, foods timers were rather obnoxious: their timers were displayed right in the middle of the screen. If many foods were eaten at once then all their timers would overlap. Now, food timers are properly displayed with tooltips. When the player eats a food item it will place itself to the right of the pause menu in a vertical stack. This is not unlike how Minecraft deals with its potions effects. Once a food timer runs out, the component is removed. Dialogue box Previously, dialogue boxes were of static sizes. They were really big with a whole lot of empty space everywhere. Now that I use Unity's GUI layout at their fullest, dialogue boxes can be resized according to their content. Gone are the static sizes! Here's how it looked before: As you can see, there was a lot of wasted space. It also arbitrarily breaks its text for no other reason than to keep its dimensions. And now look at the new one: Now there's almost no wasted space. Everything fits snuggly now and feels more believable. Restroom I've previously added a restroom room but it wasn't functional yet... With the inclusion of pieces of equipment, the toilet can now fulfil its role as intended. The idea is that the player can discard one of his equipment by flushing it down the drain. The toilet will obviously clog afterwards, as generally speaking toilets aren't made for this kind of stuff, making it a one-time use. Flushed pieces won't appear later on in landfills so this is permanent. Here's a look at its dialogue box: Within the dialogue there are some similar components to those used in the pause menu's Equipment section, but with a twist. Each of these components acts as toggles (or radio toggles if you want). When the player chooses one of his equipment to flush a visual queue is displayed within. Below that, there's a bit of text that indicates which piece is currently selected. After everything is said and done the player can then click the "OK" button at the bottom of the box. This will spawn a confirmation popup: The player can then just confirm everything by pressing "yes", after which the equipment is flushed and the toilet is clogged. Minor Updates Random collectables are now chosen by asserting how much the player needs a particular collectable: We then can do a luck test to see if the player actually gets to have what he needs or not: Yes, this is cruel for some, but also rewarding to others. Fixed some shader stuff; Optimized the GUI (Dhu 🙃😞 Previously I wanted to reuse dialogue boxes. This was however kind of hard and cumbersome, especially for dialogue boxes that were rarely used at all: It was also a pain to deal with this with the Restroom's dialogue (because the whole process uses TWO boxes). Abstracted GUI window controller classes: Horray for abstraction! 🙏 Re-added the Clothes Mall, which was removed due to lack of equipment; Added a bunch of placeholder notification everywhere: These would be polished later on. foreshadowing??? 😶 Added a whole lot of localized text: Now begone "Localized text not found"! 😉 New Music I've also had the time to compose some stuff during last week. Take a look: I'm not quite sure where to use this, even if I titled it as "Tutorial", I'm not even sure if there's gonna be one. Next Week Boy, this was a mouthful! But when it rains it pours, and this was really a necessary evil. I doubt the next one would be as filled to the rim as last one. However, there are still things to do. It ain't over till the fat lady sings. I think I'll work on notifications next: They are in a dire need of polish. I also need to continue the polishing of GUI: I didn't walk the whole nine yards. Afterwards, it's the usual suspects: Rooms, Relics, Capacities, etc. But then again I have bigger fishes to fry. 😉
  7. Last week was really productive. There were a lot of changes and refactoring and it was really worthwhile. Regular rooms Firstly, regular rooms got a major uplift. Before, there were only two types of hardcoded rooms. Neither of which were interactable as they were placeholders. I've had a blog post on how these work, but in essence, we place several prop formations at anchor point around the room. We then create those props using those and by removing props outside the room. If you want to learn more, click here! Props Secondly, there are now new types of props. Because of the major room refactoring, I need better types of props than just ferns. Rocks These are regular rocks. There are three kinds of shape which are selected randomly. These are solid and cannot be walked over. The player can, however, use a bomb to remove these. There's also a chance that when a rock breaks common consumable items can drop (things like money, bombs, keys, etc.). I've also planned to have special types of rocks that can spawn uncommon items (like foods, activated items and whatnot) through it's not implemented yet... Boxes These are solid wooden boxes that the player can break using normal attacks. Each box has a certain damage threshold, so the stronger the attack, the quicker the breaking occurs. If the attack isn't strong enough, multiples attacks could be needed. Everything that can deal damage can be used to break those, including bombs and laser beams, although some attacks are better than others. In the future, there would be a visual indicator that tells how much damage the box took. Like rocks, a broken box can drop common items. I also think that enemies could sometimes spawn out of these. I'm not sure however if there could be special boxes that could drop special items... (Rocks DO fill that role anyway...) Minor Upgrades Some special rooms, such as the Gym, can also spawn with props. Props that are inside room specific props, such as the gym mat in Gyms, are also removed... These special rooms include: Starting rooms; Ending rooms; Gym rooms ; Landfill rooms. There could be more types of special rooms with those in the future. Ferns now spawns using the new room layout algorithm, just like rocks and boxes Palm trees don't spawn anymore as of now. Not sure if they're gonna be used as background props or actually used inside rooms... Sometimes chrysanthemum plants can be spawn rather than ferns. I don't think that these could fit within a level... These could be used as fallbacks, though... Many new types of shaders have been created. There's a serious need of refactoring though... Next week I feel that next week will be about refactoring and upgrading UI elements like the pause menu and such. I really think that it's also about time to add at least a piece of equipment to test, even if it's a placeholder. After the GUI is done, back to relics and capacities. Now that regular rooms are nicely generated, about 25% of the game is done. Things are progressing, and bit by bit (literally or not if you would... 🙃) the whole picture is taking form.
  8. jb-dev

    Flexible Room Layout algorithm

    UPDATE: After thinking about it for a while, the checkered pattern was kinda removed. Instead, it's a property of each prop formation. This means that we can now have rooms that have checkered circles and diamond among other shapes...
  9. While making a roguelike game, procedural generation have to be quick and yet intriguing enough for the generated level to be fun to just pick up and play. There are many ways to generate and laying out procedurally generated rooms. In The Binding Of Isaac, for example, you have many different types of regular room presets. The generator just picks a preset based on things like room placement and size. Because those rooms are always of fixed size, this is a nice compromise. By having handmade presets the generated level is somewhat believable (i.e. there are no gaps or obstacles below a room door or secret room and whatnot). Another example would be Nuclear Throne. The game takes a different approach to procedural generation by keeping it relatively simple. Because it's not room-based like The Binding Of Isaac, there are more things like caves and large open area. The gameplay also plays into this, as the player needs to eliminate every enemy to spawn a portal to the next level. Because my game is somehow more inspired by The Binding of Isaac, the right way to procedurally generate rooms would be to use presets, and this is how I make special rooms. However, there's a big difference between The Binding Of Isaac and my game: my regular rooms aren't always the same size. This means that rather than having presets of regular rooms as well as special rooms I need something more flexible and, most importantly, dynamic.. The anatomy of a Room In my game, as I've said in a previous post, levels are big two-dimensional arrays from which the level geometry is generated. Every room of a level is made using a BSP tree. I won't go in details much on how rooms are generated, but in essence, we create a grid from which we trace a path between two rooms and sparsely attach bonus rooms along the way. Because I already have rooms sizes and whatnot with that level generation, I could reuse the same idea for room layouts. Within rooms, I've also set special anchor points from which props (or more precisely, prop formations, more on that later...) could be generated. Basic Layouts The idea here is to have room layout presets. Within those, presets are an array of prop formations, and each of these formations is linked to a specific anchor point. A formation has a two-dimensional boolean array that indicates whenever or not there should be a prop here. Let's take, for example, a diamond array: The dimension of the array depends on its rooms' dimensions. Here's how it's done: \( size = \left \lceil \frac{2(max(RoomSize_{x},RoomSize_{y}))) }{ 3 } \right \rceil\) In order to change the array's content we actually use common image manipulation algorithms... Bresenham's Line Algorithm The first used algorithm is the Bresenham's Line Algorithm. Its purpose is to simply render a line describe by two bitmap points onto a raster image. To put it simply, we get the deviation (delta, or "d" for short) in both X and Y of each point of the described line and compare both of them. Depending on the biggest, we simply incremate the point on that axis and colour it in. Here's the implementation: public void TraceLine(Vector2Int p0, Vector2Int p1) { int dx = Mathf.Abs(p1.x - p0.x), sx = p0.x < p1.x ? 1 : -1; int dy = Mathf.Abs(p1.y - p0.y), sy = p0.y < p1.y ? 1 : -1; int err = (dx > dy ? dx : -dy) / 2, e2; while (true) { m_propArray[p0.x][p0.y] = true; if (p0.x == p1.x && p0.y == p1.y) { break; } e2 = err; if (e2 > -dx) { err -= dy; p0.x += sx; } if (e2 < dy) { err += dx; p0.y += sy; } } } Midpoint Circle Algorithm The midpoint circle algorithm is an algorithm used to render a circle onto an image. The idea is somehow similar to Bresenham's Line Algorithm, but rather than drawing a line we draw a circle. To do this we also need, for simplicity sakes, to divide the circle into 8 pieces, called octants. We can do this because circles are always symmetric. (It's also a nice way to unroll loops) Here's the implementation: private void TraceCircle(Vector2Int center, int r, AbstractPropFormation formation) { int d = (5 - r * 4) / 4; int x = 0; int y = r; do { // ensure index is in range before setting (depends on your image implementation) // in this case we check if the pixel location is within the bounds of the image before setting the pixel if (IsValidPoint(center + new Vector2Int(x,y)) { formation.m_propArray[center.x + x][center.y + y] = true; } if (IsValidPoint(center + new Vector2Int(x,-y)) { formation.m_propArray[center.x + x][center.y - y] = true; } if (IsValidPoint(center + new Vector2Int(-x,y)) { formation.m_propArray[center.x - x][center.y + y] = true; } if (IsValidPoint(center + new Vector2Int(-x,-y)) { formation.m_propArray[center.x - x][center.y - y] = true; } if (IsValidPoint(center + new Vector2Int(y,x)) { formation.m_propArray[center.x + y][center.y + x] = true; } if (IsValidPoint(center + new Vector2Int(y,-x)) { formation.m_propArray[center.x + y][center.y - x] = true; } if (IsValidPoint(center + new Vector2Int(-y,x)) { formation.m_propArray[center.x - y][center.y + x] = true; } if (IsValidPoint(center + new Vector2Int(-y,-x)) { formation.m_propArray[center.x - y][center.y - x] = true; } if (d < 0) { d += 2 * x + 1; } else { d += 2 * (x - y) + 1; y--; } x++; } while (x <= y); } Flood Fill Algorithm This is quite a classic, but it's still useful nevertheless. The idea is to progressively fill a section of an image with a specific colour while The implementation is using a coordinate queue rather than recursion for optimization sakes. We also try to fill the image using west-east orientation. Basically, we fill the westmost pixel first, eastmost second and finally go north-south. Here's the implementation: public void Fill(Vector2Int point) { Queue<Vector2Int> q = new Queue<Vector2Int>(); q.Enqueue(point); while (q.Count > 0) { Vector2Int currentPoint = q.Dequeue(); if (!m_propArray[currentPoint.x][currentPoint.y]) { Vector2Int westPoint = currentPoint, eastPoint = new Vector2Int(currentPoint.x + 1, currentPoint.y); while ((westPoint.x >= 0) && !m_propArray[westPoint.x][westPoint.y]) { m_propArray[westPoint.x][westPoint.y] = true; if ((westPoint.y > 0) && !m_propArray[westPoint.x][westPoint.y - 1]) { q.Enqueue(new Vector2Int(westPoint.x, westPoint.y - 1)); } if ((westPoint.y < m_propArray[westPoint.x].Length - 1) && !m_propArray[westPoint.x][westPoint.y + 1]) { q.Enqueue(new Vector2Int(westPoint.x, westPoint.y + 1)); } westPoint.x--; } while ((eastPoint.x <= m_propArray.Length - 1) && !m_propArray[eastPoint.x][eastPoint.y]) { m_propArray[eastPoint.x][eastPoint.y] = true; if ((eastPoint.y > 0) && !m_propArray[eastPoint.x][eastPoint.y - 1]) { q.Enqueue(new Vector2Int(eastPoint.x, eastPoint.y - 1)); } if ((eastPoint.y < m_propArray[eastPoint.x].Length - 1) && !m_propArray[eastPoint.x][eastPoint.y + 1]) { q.Enqueue(new Vector2Int(eastPoint.x, eastPoint.y + 1)); } eastPoint.x++; } } } } Formation Shapes Each formation also has a specific shape. These shapes simply define the content of the formation array. We can build these shapes using the previously mentioned algorithms. There are 9 different types of shapes as of now. Vertical line A simple vertical line of a width of one Horizontal line A simple horizontal line of a width of one Diamond A rather nice diamond shape, especially pretty in corners Circle The circle is rendered using the Midpoint circle algorithm. Especially pretty in the center of rooms Cross A simple cross shape, i.e a vertical and horizontal line align at the center. X Shape An "X" shaped cross, i.e two perpendicular diagonal lines align at the center. Triangle An Isocele triangle. Square A solid block. Every cell of the formation is essentially true. Checkers A nice variation of the square shape. Every other cell is false. There might be more types of shapes as time goes by, but it's about it for now. Placing props Once the array is set, we simply need to place the actual props in the room. Each formation is of an actual type, i.e. rocks, ferns, etc. (For simplicity sakes, let's say that every prop is a 1x1x1m cube. This would simplify future steps.) In order to find their position, we simply align the array's center to the formations' specified anchor point. For each prop formation, we then instantiate props for each true cells while checking whenever or not the prop would be outside its room. Afterwards, we do a precise position check to make sure no props are either partially or fully outside a room. Finally, we make sure every room connections aren't obstructed with props. And voilà, we have a nicely decorated room In Game Screenshots Here's a couple of screenshots of what it looks like in-game
  10. Last week, there was a lot of thinking going on... There's still is least one new room plus many different refactoring. The Spa Firstly, there's a new room: the spa. This is a relaxing and zen room filled with classical Vaporwave aesthetics. I've based it on really generic Vaporwave images around the internet. The gist is that the player can cure its status effect by interacting with either massage table in the back. You may or may not have seen the fountain in the middle of the room. Its water is purely refractive with a solid, almost emissive colour when perpendicularly facing its surface (like some kind of Fresnel). This shader is part of Unity's Standard Asset, so it wasn't a hassle to get it up and running. The water is also being used in the restroom's toilet. There might be other places where it might pop up in future rooms. Minor Changes I've modelled and imported a generic spherical light that makes lighting a tad more tangible and credible. Most lights are now coloured using blackbody colour temperatures, adding a little bit more reality to most rooms. Changed the palette texture. Colours are now more distinct from each other and more contrasting for at least the default palette. This is due to some feedback saying that the palette was too much pink... Changed how most solid colours meshes are being rendered so that they rely more on the palette texture. This means that changing the current sub-pallette or even the texture itself will dynamically change the colour of those meshes as well. Made the palette initializing run within the Unity Editor. Now there's no need to run the game only to look how models are shown. This really speeds up room designs and model previews, mainly because I previously needed to compile the game and regenerate the levels until the wanted room was generated. Refactored the RNG. Now each level has its own isolated RNG state. This means that actions taken in one level won't influence the next one anymore. This also means that a given seed at a given chance (or luck if you fancy) stat will always produce the same level with the same loot. There's still some tweaking to do, but overall the isolated RNG system is in place. Many bugs were corrected, particularly with shaders. Next Week Most significant rooms are now in the game. There are still some minor rooms left, but these can wait: those might not even make it into the game so my energy could be better used on more critical stuff. Right now, normal rooms are in dire need of polish. Like in The Binding of Isaac, there will be different types of regular rooms. Each room would have different types of decoration in them. For example, some might have loads of rocks while others won't. There are currently only two placeholders kind of regular room... I do not know how many kinds of disposition there'll be: all of this needs research, sketches and design... There's a lot of work ahead. The good news is that heavy modelling is momentarily stopped. The following week will be fundamentally a coding one...
  11. Right now the goal is a big Ethernet port at the end of the level. It'll eventually lead to a boss fight. And yes, the Vapor Maker can take any .mp3 file and make vaporwave out of it: Vaporwave is known first for its music, and I know that it have a whole lot of different sub-genera... I don't want to lock the soundtrack to a particular genera, so that my way to please everybody.
  12. Like last week, the room development is still in progress. While there are two new rooms I've also had time to tweak the lighting a bit here and there. New Lights Basically, I've tried to change the lighting of most rooms. First thing first, In order to properly shade the inside rooms I've used invisible shadow casters. At the time, it was the cheapest and quickest way to deal with it when I've started. I did find out that another quick way to do this was with layers and light culling masks. Basically, every game objects that are inside sealed rooms is given a specific layer. That layer, in particular, was made to ignore the global directional light altogether. This means that there were no more need of these shadow casters (except on some opened rooms like the temple; the directional light can actually illuminate the room if the angle is good enough) I've also tried to fix the lighting in most rooms. Although not completely finished, it is getting prettier: The VIRTUAL Clinical Research Center As one of the newest rooms, this one represents a fictional clinical research facility named VIRTUAL Clinical Research. While in this room, the player can actually take part in a dodgy clinical trial involving either a strange cream, pills of unknown content and glowing fluids inside syringes. Each test takes away part of the player's health in exchange for cold hard cash. The ratio of health and cash is actually one to one as of yet. Taking the cream gives 5% of damage, the pills are 10% and the syringes are 25%. This room is quite useful if you're in dire need of cash, just like in real life. (except you don't get hurt as often in real life...) The Restroom This room is quite special. It a rather small room that is actually a normal public restroom, complete with a toilet, a sink, a real-time working mirror, a hand dryer, etc. Although not functional right now, the idea is that the player can flush down one of its piece of equipment down the drain. You can only flush one piece of equipment per restroom because, well, toilets aren't really made to be able to flush down metal armours really... For those who don't know, a piece of equipment acts like a relic but is actually set to a specific equipment slot. You can only have one piece of equipment of a specific slot (for example, the player can have only one pair of gloves because it would be overpowered otherwise). Like most RPG, different pieces of equipment have different types of stats bonuses. Each piece of equipment also has a focus alignment. This means that while the player is wearing those, its focus will progressively be drawn to whatever alignment the equipped piece is. There will also be additional stats bonuses that are applied if the player's focus matches a worn piece of equipment. But anyways, the reason the restroom isn't functional is quite simple: there's no piece of equipment yet. So it's something I'll have to get back to once there's at least one piece of equipment in the game... Next week The game is progressively coming together. Especially when it comes to rooms. There's still a lot of relics and capacities to add. There are also some rooms to add an maybe add different types of enemies and whatnot. I still want to work on rooms as of right now. The thing is that those rooms are really modelling heavy, and I really want to get those out of the way as soon as possible. The rest won't be as heavy as those, but once they're out of the way it will be a pretty big chunk of modelling that will be done. If there's time, maybe I'll work on capacities and relics...
  13. Last week was another modelling one, although there were some coding to be made in the last 2 days. Basically, I've continued the implementation of the pawnshop. Also, I had time to add a new room. The Pawnshop Although already functional, the pawnshop still needed some details props here and there. Here's a bunch of pictures showing the (almost) final version: As of now, the pawnshop's stock mainly consist of either prop from previously added rooms or even models of previous blender scene that went nowhere (take it as recycling I guess) I also want to point out that the cash register is modelled after classical mechanical cash registers like these ones: There are also various guitars hanged on the back wall... Here's the actual reference I've used for the whole room: There's still the lighting to take care of... Also, keep in mind that I want to add more props from other rooms in the shop. So I still need to keep on moving along and get back to it when new props were added in the game. The Casino There's also a whole new room: the casino. This room acts like a giant slot machine for stats. Like traditional slots machines, the player simply pulls its lever to stop the currently active slot. There are 3 slots: one that states which stats the bonus applies, another one stating whenever it's actually positive or negative and finally the actual bonus amount. I won't lie: I kinda wanted to mimic Mario Party's mythical Chance Time. For those who aren't familiar, Chance Time was an event in the Mario Party games in which two players are forced to give/exchange specific items between each other. The player involved and the actual items are given by a slot machine-ish process. Here's a compilation of Chance Time events: As for the casino, I'm not quite sure whenever the player needs to buy in before the slots start spinning. What I'm sure of is that this process is only once per casino and that the bonus/malus is permanent. Also, the room is not fully decorated nor is lighted, but as of right now the main goal is to debug the actual slot mechanic. Next week Like before, next week is going to be room-centric again. There are also bugs with those slots that need to be fixed. They don't always line up correctly and sometimes even decides to give the player a completely different outcome. Once most rooms are either fully or partially implemented, then maybe the next step would be to either fix animations or complete AIs. Afterwards, we still need capacities and relics... Some code refactoring wouldn't hurt either.
  14. I've recently released on YouTube a new track part of the game's original soundtrack. I have yet to choose how this track is going to be used, but chances are that this track will be used in a cave-like level (mainly due to its title) If you got any type of feedback, don't be shy! I'm always willing to get better and am always open to criticism.
  15. Last week was a modelling one. There aren't a whole lot of new mechanics but it was still a productive week nevertheless. Custom Font Firstly, I've previously talked about creating a custom font to display some of the GUI icons. Well, with the use of FontForge, we were able to add vectorial art in a TrueType font. For those who don't know, FontForge is an open source font-making app. It's pretty advance and can actually make good looking fonts. There's a pretty acute learning curve though. If you're using it on Windows, you'll need to fiddle around with the setting though. Otherwise, it can really run with a whole lot of hiccups and crashes. With FontForge, you can actually import vectorial graphics right into the font. Because I've already had SVG files of most of my used icons, it was just a matter of a couple of clicks and everything was imported. However, it wasn't a piece of cake: although imported, we still need to properly align those graphics up so they could be properly displayed. With FontForge you can export the custom font to different file formats. Thankfully, we can export in TrueType, which is exactly the type of font file Unity uses. Once the TrueType file is created, we can then use Unity's dynamic font rendering to display our GUI icons at any resolution we need without rescaling and rerendering any texture. However, there's a big drawback: fonts are always monochromatic. If we want a coloured icon then we'll have no other option besides using a traditional bitmap texture. (Colour fonts do exits... However, their support isn't really widespread) But anyway, here's how it looks: New rooms Secondly, there are also two new rooms. All of these rooms are linked to crystals. Now that the player can know the number of crystals they currently have, those rooms can safely be integrated and tested without any hiccups. The Temple When visiting a temple, players can donate their crystals to gain back health points. To do this, the player simply needs to interact with the box at the back of the room while holding a crystal. Temples are modelled after Japanese Shinto temple/shrine. I've taken some liberties here and there but the overall theme is Japan. They are also much more open compared to other rooms. When the sun is right, the lighting can be quite charming. The Pawnshop The pawnshop isn't finished yet, but it's functional nevertheless. The player can exchange their crystals for a small amount of money by interacting with the yet-to-be-modelled cash register. Once finished, the pawnshop will have some fancy props here and there much like a typical pawnshop: things like guitars, jewellery and, of course, crystals. But for now, the room is kinda bland and boring... Minor updates There are also some new models and code refactors. For once, the diner now has a nice sign up on its roof: Aside from that, there aren't a whole lot of minor differences. Next week Like I've stated before, a lot of rooms can be added into the game now that most gameplay mechanics are coded. And there's still a whole lot of rooms to implement. Of course, I still need to model out the pawnshop and add its details. There might be some polishing to do with many gameplay mechanics and maybe a refactor or two. There's a lot of work ahead of me.
  • Advertisement

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!