2024-09-30 - Minor clean-up day
In the conclusion of my previous post, I wrote that I felt the need to do a bit of clean-up before proceeding with the smart particles.
So for today I'm just going to do some minor restructuring of the recent code, and of the Unity scene.
I did many minor improvements here and there, nothing worth discussing at length.
I also removed some old scripts which weren't useful anymore, and removed a few test nodes from the Unity scene.
I feel like it's always worth doing a bit of refactoring after getting something working, resisting the temptation to just move forward. The project stays more manageable and you exploit the fact that the reasoning behind it it's still fresh: you take the opportunity to make the code easier to understand for when "future you" is going to go back to make some changes, having forgotten lots of details in the meantime.
Is everything perfect after one afternoon of tidying up? Not by far. There's still a lot to do, especially about the portal management, but you gotta start somewhere.
2024-10-01 - Portal subsystem refactoring
Yesterday I decided to skip refactor and clean-up for things related to the portal system. I noticed things that weren't in the right place, but the changes needed didn't feel "minor", so they called for a dedicated day. Which is today.
While working with the enemies and the avatar, I have structured the code into a nice set of elements which deal with different responsibilities of an entity.
I have the concepts of state provider and state presenter, and around that I also built the test behaviours which let me test the visualization via inspector. Additionally, I have separated the "debug visualization" operations.
For example, in the case of an enemy, I have
In the state management layer
`
EnemyStateManager
` and the related data types (`Enemy.Data
`, `Enemy.EState
`...)
In the presentation layer
`
EnemyBhv
` handling the presentation of an enemy, using other sub-components as needed`
DebugEnemyBhv
` handling the presentation of an enemy in debug mode (e.g. showing the health bars)`
TestEnemyBhv
` allowing the test of the presentation via inspector
I did the portal subsystem before getting to this architectural organization, so it's a bit messier than this. Refactoring time!
I moved some transient data from the `PlayerStateManager
` to the `PortalStateManager
`, which was long overdue.
After making `PortalStateManager
` self-sufficient in terms of the information needed to drive the portal presentation behaviour, I updated `PortalBhv
` so that is a state presenter for the portal entity. Along the way, I removed a `PortalManagerBhv
` which had no reason to exist anymore (I didn't write it, but yesterday, doing a similar process, I got rid of `PlayerManagerBhv
`).
Abstractions help in keeping things manageable when they make sense, but sometimes one introduces some unnecessary ones, that provide no real semantic value. So, good riddance!
Tomorrow I'll continue the refactoring work dealing specifically with the portal surface rendering.
2024-10-02 - From PortalRenderer to CameraManager
Something that has always bothered me a little is that in the `PortalRendererBhv
` I'm doing things which are not only related to portals. What am I doing that doesn't fit? Well, I'm changing the cameras settings, both in terms of culling layers and of rendering background.
I did it there because `PortalRendererBhv
` was where I took care of positioning the "portal camera", so it was quick to do there other "more global" things too. Quick, but dirty.
Today I'd like to revisit `PortalBhv
` and `PortalRendererBhv
` and shuffle things a bit.
Ok, it wasn't easy, but I did a step in the right direction.
Unfortunately I had forgotten many details about this part of the code (which probably is one of the most complicated so far), so it took me a while to figure out what to do.
Until now, `PortalRendererBhv
` was operated by `PortalBhv
`, which had a direct reference to it.
`PortalBhv
` passed the portal surface `MeshRenderer
` to `PortalRendererBhv
`, which took care of updating the material (setting the render textures and changing the proximity level parameter, for example).
Then, `PortalRendererBhv
` also took care of managing the cameras, and not only the "portal cameras" that update the render textures, but also the main cameras (and the skyboxes).
In today's refactoring, I changed `PortalRendererBhv
` to `CamerasManagerBhv
`, now working as an independent subsystem. The part which worked on the portal surface `MeshRenderer
`, updating its material, got moved into the main `PortalBhv
`, where it belongs, as it's working on a child component of the portal node managed by `PortalBhv
`.
`PortalBhv
`, during its initialization, queries `CamerasManagerBhv
` to get references to the render textures that it must assign to its surface `Material
`.
There's a definite architectural improvement, because `CamerasManagerBhv
` doesn't directly modify the portal, but just the cameras. `PortalBhv
` now handles the `Material
` and remains in charge of configuring the transforms which indicate entrance and exit of the portal, which `CamerasManagerBhv
` uses to update the portal cameras poses (and, in turn, the render textures which get displayed on the portal surface).
Part of me wants to generalize all this so that the "portal cameras" are instantiated and moved directly by `PortalBhv
`, but that would be worth it only if many portals could be opened at the same time, each with its cameras. I'm designing the game avoiding that possibility (which would definitely kill the framerate), so I'm going to contain myself and don't do it. At least for now!
Talking about moving portal cameras, I think I know what I'm going to do tomorrow...
2024-10-03 - Portal camera movement
Last week, on 2024-09-25, I wrote:
You might notice a problem, which I already spotted a long time ago but haven't faced yet. The "content" shown in the "distant" bridge portal is wrong. It doesn't feel like the portal is coming forward, but like it is a strange window into another space, which feels right only when completely opened. The effect is even worse in the headset, because of stereoscopy: you see the "close" mirrored avatar, but confined in the "far" hexagon, and your brain and your eyes complain.
Considering that yesterday I "refreshed" my understanding of the portal camera management, it should be a good moment to fix this.
Ok, I had to take pen and paper for a minute, but I did what I wanted.
The difference is subtle for the normal portals, but quite meaningful for the "bridge" portals which stay opened at a distance.
Let's try and see what I changed with a diagram, showing "before and after" the fix...
The yellow camera is attached to the player. The other is the "portal camera", which draws to the render texture which is then displayed on the portal surface. The portal starts appearing from the destination platform, and while scaling up to full size, it comes forward to the player platform. In the case of bridge portals, there's no scaling: the "distant" portal is always visible at full scale, and only comes forward.
The positioning of the portal camera is calculated on the basis of the player camera and of the current portal. I'm simplifying a bit, because the real setup involves more cameras, to take care of stereoscopy: right eye and left eye see two different textures rendered on the portal surface.
Anyway, in the upper part of the diagram (before the fix), the portal camera was always in the "destination" position, even when the portal was distant, which caused the problem described at the beginning, because you saw something that was supposed to be far as if it was "near" (cropped in the portal area).
Now, instead, I animated the portal camera so that, at the beginning of the animation, it matches the position of the player camera, and at the end, it's in the correct position to show the viewpoint as if the player were on the destination platform. Basically, the camera moves together with the portal, but in the opposite direction.
How does the result look? Let's see it with the portal opening and closing speed slowed down by a factor of ten.
And here's a short in-editor capture that shows what happens behind the scenes:
I added a red cube to make it easier to understand. In the bottom right corner, there's the portal camera render preview. In the scene view, you can see how the camera node moves together with the player, and then animates to the destination. When the player crosses the portal and gets teleported, the player camera matches what was shown by the portal camera and the magic trick is complete.
I like the resulting effect, but a little problem showed up: with this new camera behaviour, when there's a bridge portal that can be crossed both sides, the "cover" for the other sides is shown in the portal. Something that I had not anticipated, but that is completely correct considering how they are currently implemented and positioned. Here's a short clip showing the problem:
It doesn't show in the clips, but the new effect doesn't really work well with the portal scaling of the "non-bridge" portals. So I decided to disable it, at least for now. I'm going to do a different "appearance" animation. Between this and the need to fix the "covers", I guess I'll be pretty busy tomorrow.
2024-10-04 - Refactoring portal covers
Ok then, time to refactor the portal covers and fix the issue shown yesterday.
As first step, I'm going to rename a few things currently named "bridge portal" to "portal cover", because that's what it currently is: the bridge portal themselves are handled like all the other portals, with a couple variations handled in place for "bridge" and "mirror" portals.
So the red/green elements that show where a bridge portal is, and disappear when they're ready to open, are basically "covers" in front of the actual portal.
Well, that was a lot of renaming, but at least now the concept of bridge portal (a portal connecting two zones of the game world) and that of portal cover (an element visualizing if a bridge portal is available and the target zone is loaded) are well separated.
And now, the real work begins...
I started by aligning the "portal cover" with the other entities I recently worked on, so I restructured `PortalCoverBhv
` as a state presenter and added a `TestPortalCover
` behaviour that allows me to test via inspector the different states and the animations of the component.
This was also a good way to refresh my memory about how this thing worked and how it interacted with the gameplay simulation logic.
It was also a good opportunity to change the obnoxious red/green colors that I had used as test color to indicate the locked/unlocked state. Still temporary colors, but maybe they won't ruin all my screenshots and videos anymore.
About the actual modification, I think the easiest fix is setting the portal cover to the same state that I set when the portal is opened (so, when the cover becomes invisible). In theory, I should only need to set this state on the `PortalCoverManager
` that handles "the other side" of the bridge portal... let's try!
Well, it's not pretty, but did the job:
if (portalDstLoc.HasValue
&& portalDstLoc.Value.m_zoneId != playerLoc.m_zoneId) {
PortalCoverStateManager rOtherSidePortalCover =
getWorldStateManager()
.getPlatformStateManager(portalDstLoc.Value)
.getPortalCoverStateManager();
if (rOtherSidePortalCover != null) {
rOtherSidePortalCover.setLocked(false);
rOtherSidePortalCover.setLoadingState(true, true);
}
}
So basically "if there's currently a portal bringing to a platform in a zone which is not the player zone, try to fetch the `PortalCoverStateManager
` of the destination platform. If you find one, set it in a way that makes the cover disappear".
The `setLoadingState()
` call is awful. I don't remember why I did it that way and not using two enumerations and not two booleans, like I usually do to keep things more readable. Well, that's a change for another day...
End of week video capture!
Ok then, thanks for reading yet another entry of the Particular Reality DevLog.
I managed to post each week for three consecutive weeks, but now I'm afraid I'm going to take a break for a week or two, and take care of a few things on my backlog.
As soon as I can, I'll be back at work, and I think I'm going to finally start development of the smart particles subsystem mentioned in week 25.