I spent the last two development weeks by building elements that now, finally, I can use to implement the "tutorial" sequence I described in week 25. Here we go!
2025-01-06 - Limbo zone logic
Let's start from the beginning, the limbo platform where you need to enter Particular Reality and embody the avatar. I wrote (and sketched):
I need to push the player roughly near one edge of the hexagonal platform, and facing the opposite edge. This way, the portal to cross, which is going to "split" the platform in two halves when it appears, will open in front of the player, well visible.
Having some kind of arrow stamped on the platform would be effective, but too crude for my taste. What I'm thinking is having a dynamic hint system, based on floating particles, that goes into the player field of view and moves trying to push him into a certain spot.
From a high level perspective, the logic of the limbo zone should be:
the zone is initialized with the mirror portal closed
if the mirror portal is closed
If the player is not on the bottom half of the platform, facing towards the top half, bring them in that status with the hint system
otherwise, open the mirror portal
if the mirror portal is opened
check the proximity of the player with its mirrored double, and if crosses a certain threshold, cross the mirror portal and embody the avatar (this part is already implemented, even if needs to be refactored)
optional: if a certain time has passed and the player is not approaching the portal, maybe use the hint system to encourage them to do that
According to my experience, the best way to think about gameplay logic is by having states activated by arbitrarily complex predicates.
I'm going to do a bit of refactoring, making it easier to check the predicates needed without duplicating the logic I already have to check if the player is in the correct position to open a portal.
I did the refactoring I wanted, or at least part of it.
I also wrote the "tentative" code for the limbo zone logic.
Without thinking too much about where this is currently placed, here's the snippet:
if (!isPlayerReadyToOpenPortalTowards(Hex.EDir.N)) {
RTSpot mirrorPortalOpeningSpot = new RTSpot(
WorldStateManager.getStartupLoc(),
Hex.EDir.S
);
rSmartParticlesSM.setPortalHint(mirrorPortalOpeningSpot);
} else if (rPortalSM.getState() == Portal.EState.opened) {
const float fCROSSPORTAL_HINT_WAIT_SECS = 5f;
float fPortalBeenOpenedSecs =
rPortalSM.getCurrStateElapsedTime();
if (m_fTimeAtCurrLoc > fCROSSPORTAL_HINT_WAIT_SECS) {
RTSpot portalDestinationSpot = new RTSpot(
WorldStateManager.getMenuStartLoc(),
Hex.EDir.N
);
rSmartParticlesSM.setGotoSpotHint(portalDestinationSpot);
}
mirrorPortalHandling();
}
The idea is that, at this level, I simply set what kind of hint I want depending on the situation.
Then, in the `SmartParticlesStateManager
`, one layer below, I will appropriately use the features we implemented in the last weeks to guide the player.
I'm talking, of course, of the implementation of the `setPortalHint()
` and `setGotoSpotHint()
` methods, which are currently empty.
2025-01-07 - Improving visual debugging
I think that, before proceeding, it makes sense to revisit and improve the debug visualization features related to the player movement/target platform selection.
That way, I'll be able to easily understand if the conditions that should activate a certain hint are true at a specific time.
I haven't much time to write today, so I'm going to keep it brief.
I'm refactoring the old `DebugPlayerBhv
` and a couple of other scripts so that they follow the pattern of the `DebugSmartParticlesBhv
` I recently discussed. This avoid some code duplication and improves the organization of the scene, removing unnecessary interdependencies.
Things are gradually getting a bit tidier, and that's great, even if it's taking a bit more than expected. Can't escape Hofstadter's law.
I added a couple of timers indicating how long one has been on a certain platform, and on a specific hexagonal slice on that platform.
I also drafted a small panel following the player on the ground, showing the timer values and other information that might get useful when testing the hint system.
It's not worth a video, but here's an image.
2025-01-08 - Restructuring hint state management
I realized that to quickly test the hint visualization, in the last weeks, I cheated a bit and calculated the world transform for the hint sphere accessing directly the player tracking data from the Unity scene. That doesn't sit right. I must do the work and have the state management layer not dependant on the presentation layer. This calls for some more preliminary refactoring which won't carry visible results.
Ok, I was able to mostly fix what I wanted, but I had to do it in a hacky way.
To fix it properly, I will need to do some more in-depth restructuring.
Long story short, in the state management layer, at the moment, I only deal with zone-relative coordinates: while in the presentation layer, when two zones are loaded, I can do calculations involving platforms and elements from both zones, I can't really do that in the state management layer.
If I want to be able to have logic involving an adjacent zone, that you are maybe peeking into from an opened bridge portal, I will need to fix that.
For now, I did the barely minimum I needed to be able to have the `guide_move
` state working when the destination spot is not only on the current zone (which was the "easy" case) but also on the single platform of the adjacent zone that is on the other end of the current bridge portal, if any.
As the "mirror" portal is a special case of bridge portal, connecting the limbo zone to the menu zone, this should allow me to implement the hint telling the player to move into the portal, if they don't figure that out after a while. I will just need to set the target spot for the `guide_move
` state to the "other end" ot the portal.
2024-01-09 - Hint modes design
In the zone logic code written Monday, I called the `setPortalHint()
` and `setGotoSpotHint()
` stubs: it's time to fill them in, defining how the low level states of the smart particles subsystem (that we already implemented) are meant to be used to give players hints in specific contexts.
I'm going to introduce the notion of hint mode to describe the "high level" hints I want to have readily available to the zone logic code. Each hint mode will have its custom path in the smart particles FSM. Maybe, I will then factor this out to properly nested state machines, if it's worth it.
public enum EHintMode {
none,
portal,
gotoSpot
}
So, let's define a simple (hopefully) FSM for each of these two modes we need for the limbo zone, and assume that calling `setPortalHint()
` and `setGotoSpotHing()
` activates each one of them.
Let's start from `EHintMode.portal`.
To be able to open a portal one must be
in the correct spot
facing the correct direction
So the hint particles should
catch the player attention (`
captivate
`)if they are not at the right spot, send them there (`
guide_move
`)if they are at the right spot, but are not facing the correct direction, make them look that way (`
guide_look
`)
The system must be robust, meaning that if after some time the player fails to follow the hint, it should go back to `captivate
` their attention and try again.
To make the diagram readable, let's assign some one letter identifiers to the predicates we are going to need:
`
C
` : player "captivated", meaning that the hint sphere is in their FOV`
P
`: player "positioned", meaning they're in the correct spot to open the portal`
L
`: player "looking" towards the portal (more precisely: properly rotated to open it)`
T
`: timeout, some specified time has passed
I added a `done
` state just to have the diagram flowing better. And we're going to add some extra state like that to take care of the appearance/disappearance of the hint particles anyway.
Let's move to the `EHintMode.gotoSpot
` FSM, which should be a bit simpler.
To send the player to a specific spot, we shouldn't need `guide_look
`: we just catch their attention with `captivate
` and then use `guide_move
`. If the player fails to reach the destination spot in a certain time, and they're not looking at the hint sphere (meaning that the player attention is not captivated), we go back to `captivate
`, repeating the loop until the player reaches the spot.
Tomorrow I'll try to implement and test all this.
2024-01-10 - Hint modes implementation
As first step, I'm going to implement some utility functions to evaluate the predicates I described yesterday, which will probably be the most delicate part.
Then, I will map the logic defined by the diagrams to the code, which should be straightforward.
Ok, my time is over for now, but let's say that I implemented what I designed yesterday, and it somewhat works.
There's plenty of details to take care of, and polishing to be made, but for this week, this is it:
The worst thing is how the hint abruptly disappears as soon as I'm in the correct direction to open the portal. Then, it sometimes intersects platforms, and it's not clear where does it come from when, after opening the portal, it suggests go cross it by guiding to the target platform.
Well, you gotta start somewhere!