Lessons Learned (Notes To My Future Self) & Open Questions
Documenting a few things I've learned and some issues that I still don't quite understand (so developed work-arounds). These are basically notes to my future-self for when I run into issues that ‘I know I’ve dealt with this before…’. Maybe others will find these notes useful as well.
These descriptions contain pseudo-code simply to describe/illustrate how the features are logically constructed and do not necessarily match the actual/functioning syntax.
This game jam submission was the first time I've used grid/tilemap tools.
Collider Trigger Sequence Not Consistent Between Win/Mac/Linux Versus WebGL Build Settings
I have a collider placed where the ladder is broken. This collider prevents the player from continuing to move up. For the player, OnTriggerEnter2D, other.GameObject.tag = “LadderStop”, then if input.GetAxis(“Vertical”) > 0, inputVertical = 0; preventing further upward movement while still allowing the player to move down the ladder.
When the player has a ladder segment (pickup), the ladder segment is placed and the ladderStop collider is then relocated to the next position where the ladder is broken. In this way the player is now able to climb where the ladder has been repaired and is not stopped at the next break. The conditional preventing further positive vertical movement is cleared when the collider moves away OnTriggerExit2D.
>> Logic Sequence: Player moves to top of ladder, OnTriggerEnter2D can no longer climb, ladder is placed, collider moves to new position causing OnTriggerExit2D, player can now climb above the newly placed ladder segment.
I originally developed the game with build setting of Win/Mac/Linux. The game loop was working as well as could be expected, while still incomplete. Many hours before I would make my final jam submission, I decided to do a WebGL build and upload a ‘test build’ to itch.io to ensure no issues.
>> The Problem: When I played the game on the itch.io game page, the character would not climb past the repaired ladder point. I could climb down, but not up even from the lower position; my vertical position was still locked by my condition forcing input.GetAxis(“Vertical”) > 0, inputVertical = 0.
I ran the game in focused mode to verify the status on player state Booleans (isGrounded, isClimbing, isAtTopOfLadder…). I spent a good amount of time scouring through my code to see if any of the changes I had made could have inadvertently allowed the climb up function to be locked. I was still unable to track down the issue. Of course, anything like this in the final day of the jam is VERY annoying as you’re spending time debugging instead of ‘finishing’ the myriad of items on your ‘to do’ list.
In my source control, I discarded the changes since last commit (to clear any changes I made while debugging…and my WebGL build setting). Upon reloading, everything worked as intended. I was quite surprised I changed the build setting to WebGL and played, now once again after entering the ladderStop collider, the player was stuck unable to move up even after climbing down. Now I was both confused and relieved, because, after a long time trying to figure out what was going wrong, I knew the issue was being introduced by the build setting change.
By inserting a few Debug.log reports to console, I was able to determine that the collisions under WebGL build setting were occurring OnTriggerExit2D first, and then OnTriggerEnter2D second! For Win/Mac/Linus build setting, collisions occurred as expected OnTriggerEnter2D first, then OnTriggerExit2D second.
>> My Workaround: I had no understanding about why this was happening or how to fix it so the build would result in the same behavior for each platform. Instead I quickly implemented a workaround whereby I allow for the OnTriggerExit2D to happen first, when this trigger happens, I disable the collider, so that it cannot register the OnTriggerEnter2D event. I then turn the collider on WaitForSeconds(0.2f) later so that it will properly stop the player who does not have a ladder pickup from continuing to climb past the broken ladder segment. My solution worked!
I still have no idea why the builds result in different collision behaviors (my solution did not address root cause).
>> Lesson Learned: This example reinforces the wise instructions to ‘build early, build often’; imagine finding this closer to or at the jam submission deadline. While I was frustrated at the time lost debugging this issue, I was grateful I made and uploaded a test build 10 hours before the deadline and that I now had a working WebGL (and Win/Mac/Linux) build that I could continue to develop.
Do you know what the root cause is between these differing build settings results? Please drop a comment if you’ve run into this and understand a root cause fix. That said, I’m sure there are many ways the ladder/movement logic sequence could have been refactored; my quick jam design was likely not in line with best practices, that aside…my interest is in understanding why the build settings gave different results….and less focused on how I could have avoided the issue with different design choices. While I’m happy to learn better/different design practices, in the end and in the future, I’m still left to debug issues that arise in the code that I’ve generated.
OnTriggerEnter2D Activated When Objects Are Far Apart (Colliders Don’t Appear to Make Contact)
When the player is in the clouds scene, a set of wings (pickup) drifts down from above. The wings’ falling effect is an animation using transform properties (coordinates and rotation).
Originally, the player could intercept the wings as they fell. OnTriggerEnter2D if other.gameObject.tag = “pickupWings” would deactivate the pickup gameObject and activate the animated wings attached to the player.
>> The Problem: When the wings pickup is floating down, a collision is triggered while the wings are far above the character, their colliders are nowhere near each other, much less touching.
I carefully went through all the colliders to ensure they were properly tagged. Checked hierarchy to ensure no object parenting where colliders were inherited. Checked to see if player/pickup were touching any other colliders that could somehow ‘bridge’ a collision between the two gameObjects. Changed OnTriggerEnter2D (Collider2d collision) to OnTriggerEnter2D (Collider2D other). Started game as ‘Play Focused’ in editor so I could highlight the pickup and player to verify position of colliders relative to each other….they just don’t appear to touch, even though Debug.Log does reports a collision.
This problem seemed to occur when the wings were directly overhead. I could run toward the pickup from the side, and the OnTriggerEnter2D events would only occur one I ran into and touched the wings pickup.
Here is what the colliders on each object look like.
Here is a picture showing the distance between the player and the wings pickup moments before an apparent collision would occur. In this picture the wings pickup is moving from the right side of the screen to the left side…when they get above the player, the trigger happens, and they are picked up.
I tested to make sure the player colliders were not overlapping/touching the ladder colliders.
>> My Workaround: With the jam deadline pressing, I gave up on debugging root cause and instead disabled the collider on the wingsPickup until the falling animation was complete. Once on the ground, the wings pickup collider is enable and the pickup glow is SetActive. The player is no longer able to intercept the wings in the air and instead must wait until the wings pickup is on the ground.
Please share if you know what the root cause of this may be? Perhaps colliders on an animated game object somehow extended or are oriented in a way that is different than the real time orientation the game object appears to be throughout its animation?
Return to Main Menu from Pause Screen Not Working Properly
This was the first time I implemented a pause screen (which I’ve planned to in other games but simply turned into ‘Nice to Have’ instead of ‘Must Have’ for other games…plus the time pressure of game jams). In Ascent, because the game is a never-ending loop, I figured the pause to allow exiting via Main Menu rose to a must-have feature. During development and before submitting, I did detect that ‘Esc’ was a poor choice of key for pause while playing in a browser; ‘Esc’ returns focus to the browser tab the game is running in. I changed the pause key to ‘P’.
What I didn’t detect in time was that the ‘pause > return to main menu > start’ feature didn’t work! In the first loop, it appears to work because you are returned to the Main Menu. However, if you try to ‘Start’ again, the screen is black. In the second loop (if you are past the cloud stage), pause > return to menu yields a black screen with the #cycles text still on screen. This behavior is different in the second loop because the grid and canvas used for the hellscape were late additions to the game and I forgot to turn these objects off as part of the OnClick() button actions when returning to main menu.
In my implementation, there is only one scene and I turn on/off different canvases to show different parts of the UI navigation (menu, credits, pause).
>> The Problem: Even after properly SetActive(true/false) different canvases based on the UI navigation, ‘Pause > Return to Menu > Start’ was not working, appropriate canvases were not being shown.
>> The Cause: I found that this error occurs because I did not treat ‘return to main menu’ as an exit path from the game paused state. This means that I did not restore Time.timescale =1 when selecting ‘return to main menu’ the way I do for ‘return to game’.
>> Lesson Learned: If pausing via Time.timescale = 0, ensure all navigation paths allowed from the pause screen have paths that restore timescale.
Even with Time.timescale = 0, it’s a little strange that ‘return to main menu’ does show the main menu screen in loop 1 (covered up by the hellscape canvas in loop 2+). Prior to my fix of setting Time.timescale back to 1, my return to menu method cleared the number of loops completed for my ascent/cycle counter, then reloaded the scene. It’s not clear to me which actions are framerate independent…setting the canvas gameObject.isActive(true) must be independent because this part worked? (From Unity Documentation: ‘When timeScale is set to zero your application acts as if paused if all your functions are frame rate independent.’)
Virtual Camera Orthographic Size Will Not Change to New Value at Runtime
(Update with root cause included below)
As suggested in Game Jam comments, I’ve made improvements to a post-game-jam version that allows the used to toggle the zoom between original (near) or zoomed out like loop2+ (far). I am using Cinemachine so the orthographic size I’m concerned about is the value on the virtual camera.
- I have ‘using Cinemachine’ namespace at top of script
- I have a reference to my virtual camera
- In case Cinemachine required my script to check for the ‘active virtual camera; I tried accessing the orthographic size by getting the CinemachineBrain component on the main camera to then set myCinemachineVirtualCamera = brain.ActiveVirtualCamera as CinemachineVirtualCamera (I’m not sure this successfully returned a reference to the virtual camera, so this investigation path may still hold an answer)
- In these attempts I was trying to change the value of myCinemachineVirtualCamera.m_Lens.OrthographicSize = someValueForMyZoom
>> The Problem: I implemented the zoom toggle on a UI button, set the OnClick(), appropriately set and update a private bool for isZoomed to control entry into the appropriate action. And the result was the orthographic size did not change.
Debug.Log(myCinemachineVirtualCamera.m_Lens.OrthographicSize) messages were added after changing the value and in Update(). Based on these log messages to the Console, I could see that the orthographic value was updated, but then immediately overridden by the original value. Outside of my OnClick() method, I don’t believe I had any other code that explicitly set the orthographic size.
>> My Workaround: As I already did for the original zoom out after the Clouds scene, I created animations using the virtual machine orthographic size property. With animation parameters set, the OnClick() button toggle can switch between two animations to zoom in and out.
>> UPDATE: While writing this section, an idea for the root cause of this issue came to mind…and after some testing…now I know what the issue was. I’ll leave the original problem description and workaround in case other people encounter the same issue.
>> The Cause: m_Lens.OrthographicSize = someValueForMyZoom was in fact working! As I indicated in my original description of the problem, Debug.Log() clearly did show the value was changed, and then overridden back to the original value. The reason for this is that I was controlling the original zoom out via animation. Even after explicitly setting a value for the orthographic size, the first frame of the animation state is setting the orthographic size back to the value when the animation starts.
Because of this, I can have all zoom actions via animations (as I implemented before writing this update), or I think it would be possible to disable the animator on the virtual camera so that a m_lens.Orthographic size can be set explicitly.
UI Button Activated with Spacebar
As mentioned above, during my updates to include in post-jam update, after the player is returned to the mineshaft and the camera zooms out to reveal the hell-scape that the ascent loop takes place in, I’ve added a UI button to allow the player to toggle between near or far camera zoom.
>> The Problem: After clicking on the ‘toggle zoom’ button, the camera zoom responds correctly, but then each time the player jumps the toggle zoom activate again.
>> The Cause: The Button (Script) component includes a ‘Navigation’ option that has ‘Automatic’ as default. This allows for the player to activate any button and then navigate to other buttons on the UI (such as with directional keys or tab to next button). When the ‘toggle zoom’ button is pressed, the button remains active (in focus for further navigation). With the navigation set to automatic, ‘spacebar’ acts as ‘submit’ (as in, activate this button). So each time pressing spacebar to make the player character jump, it was also ‘submitting’ ‘toggle zoom’ button action.
>> The Solution: In my case, I do not need to allow for navigation between a bunch of on screen buttons, so I set the navigation field to ‘None’ and this fixed the issue; spacebar is not a ‘submit’ key when button navigation is set to none. Alternatively, I understand that you could reassign the submit button in the Input Manager ( Edit > Project Settings > Input Manager > Submit: Set the positive and alt positive button) and set an alternate input. I think there is also a solution that after the player clicks the ‘toggle zoom’ button on the UI, code can then deselect the current selected object so that navigation will not occur since the button will no longer be ‘selected/in focus’.
Tilemaps – Rotate and Flip Tiles
My understanding is that these work for non-rule-tiles. You can find these setting in Unity under Edit/Shortcuts/Grid Painting
- Clockwise [
- Anti-Clockwise ]
- Flip X Shift + [
- Flip Y Shift + ]
I knew you could edit the tile palette, copy and rotate an individual tile on the palette…but this process is very slow. Instead, I would take my sprite sheet into a graphic program and rotate the entire map (maybe in all four rotations) and then add these to a new palette. This resulted in A LOT of tileset assets being created.
While making updates to my hell-scape for a post-jam update, I found that you could rotate and flip tiles while painting them on the tilemap!
Tilemaps – Could Not Get 2D Tilemap Extras Layers Brush Installed from GitHub
(Update below, got it working)
While developing my game map I use multiple tilemaps at different sort orders so I could have items in foreground, background, etc. On occasion I wanted to move an entire area of the map but had to manually move sections from individual tilemaps one at a time ensuring they were lined up and relocated correctly.
I searched for a tool that could move tiles independent of what tilemap they were are on, and found this link. https://github.com/Unity-Technologies/2d-extras/blob/3f264c91443b9a38313521898886a2d68211674c/Editor/Brushes/LayerBrush/LayerBrush.cs
To add a LayerBrush which edits all tilemaps sharing the same Grid.
From the GitHub I did Code > Download to ZIP
Followed the instructions to add the download (which I extracted) to a new folder com.unity.2d.tilemap.extras in the Packages folder of my project. (myProject/Packages/ com.unity.2d.tilemap.extras/(the contents of the extracted ZIP file)), but no new brushes were added to tile palette brushes.
I’m not sure if I set up the folder structure incorrectly, or maybe I didn’t download the correct code. I’m still not adept at extracting what I want from GitHub repositories.
>> UPDATE: I got it working!
I made a new script in my Assets folder, named is LayerBrush, opened this script and cleared all text (namespaces, Start(), Update() etc….made the entire page blank)
Copied the script from GitHub and pasted the contents into my empty LayerBrush script.
That’s it!
Now under the tile palette window there is a Layer Brush that can select tiles across all tilemaps on the same grid. It only moves tiles from active tilemaps, so if there’s something you don’t want to move you can just deactivate that tilemap while making your move/edits.
This is going to help a lot for future tilemap work!
>> UPDATE 2: I did find that I needed to delete my LayerBrush script in order to build the game. I was having an error "The type or namespace name 'GridBrush' could not be found (are you missing a using directive or an assembly reference?)". I understand that this is related to some 'using' namespace missing in the project...but I didn't know what file/location to edit or exactly what reference I would need to add. I was only able to get the project to build after deleting the script. This isn't horrible, as I will still be able to use this script while developing future projects, I will just need to remove the script before building. If anyone knows exactly what reference I'm missing and where to add this reference (need to break it down Sesame Street style for me), would be grateful to learn.
Leave a comment
Log in with itch.io to leave a comment.