For my first contribution to the Chop Chop! open project, I wish to program the UI fader to black feature. It will be a great opportunity to practice coding the UI system and won’t put too much of a burden on me time-wise. I do want to program this feature correctly, so if this is approved, I will publicly brainstorm a possible implementation and plan the feature to allow it to be as smooth and bug-free as possible, both from the programmer’s end and the user’s end.
Thank you, and have a nice day.
Hey @SteelcladLandship , welcome to the project.
Feel free to work on this task. Just keep in mind that the way we do things is not to “assign” a task, but to let people come up with solutions and, if one is good, we pull it in. This doesn’t mean that it needs to be a rush, so take your time.
I also made this thread the official discussion for this task and linked it to the task card.
I am doing this brainstorming for feedback on my ideas. I don’t want to start forming a plan that makes no sense and can’t be implemented. The suggested implementation also neglects precise details on how the fadeout will be programmed.
Brainstorming:
Fadeouts can and likely will be used for all sorts of purposes, including cutscenes and room transitions. As such, the fadeout manager should be available for anything to use. While multiple usages of the fadeout would be nonsensical, there may be select circumstances where having multiple fadeouts would be useful. As such, the fadeout object(s) itself and the fadeout manager should be separate. If multiple fadeouts attempt to happen at the same time, the fadeout manager will be tasked with resolving the conflict.
Fadeout Object:
The fadeout object should have one persistent fadeout rectangle managed by two separate operations, one that fades in the rectangle and one that fades out the rectangle. The object itself would not do anything on its own, it’s operation will be managed by the fadeout manager or directly by other parts of the project. It will also not render anything on its own, that will be primarily handled by the fadeout manager. The fadeout object must also be destroyable without disrupting anything that attempts to interact with it, even if the fadeout is currently in progress.
Properties: (The names may change depending on what’s most intuitive)
Color: The color of the fadeout. Will conventionally be black, but could also be white or any other color.
Fade-in duration: How long it takes for the rectangle to become fully opaque.
Fade-out duration: How long it takes for the rectangle to become fully transparent.
Hold duration: How long the rectangle remains opaque after fade-in. Only used if fade-out happens immediately after fade-in.
Conflict resolution: How the fadeout object should be handled if another fadeout is currently being handled.
Theoretical Process:
A fadeout object will be created by the game, and its properties will be set.
When needed, the fadeout object will be added to the list of existing fadeout objects within the fadeout manager.
The game will communicate to the fadeout object to fade in, out, or do both and use the hold duration.
The fadeout object will communicate details to the fadeout manager, referencing its own properties and the clock.
When the fadeout is complete, the fadeout object will be removed from the existing fadeout objects list. It can then be deleted safely or reused.
Fadeout Manager:
Controls the UI fadeout elements. It should be able to handle multiple fadeout objects and their deletion. I’m not sure if the fadeout manager should be called directly by other parts of the script or exist within the Update() loop, but either way, the fadeout manager will receive updates and lack any internal loops otherwise.
Properties:
Fadeout Objects: List of fadeout objects relevant to the fadeout manager. This will not be every fadeout object, only the fadeout objects about to be used.
Theoretical Process:
The fadeout manager will exist at the beginning of the game, will never be destroyed, and only has one instance.
Fadeout objects will attempt to be rendered by the fadeout manager, but how they are handled is determined by the fadeout’s properties.
If the fadeout object’s conflict resolution is:
Skip (default): The manager will ignore the new fadeout object
Queue: The manager will add the fadeout object to the fadeout objects list, and postpone handling it until the current fadeout is done
Override: The manage will end handling the current fadeout object and handle the new one instead.
The manager will receive information from the fadeout object it’s currently handling, and will control the opacity and color of the fadeout rectangle. This will be done statically without loops or coroutines.
Edit: After thinking about the fadeout object some more, it is not actually necessary to have. The fadeout manager can simply be interacted with directly.
If there are selected objects to fade out, we have to ask what should happen with the other objects. Presumably they should remain visible or even appear after the object in front of them has faded out. So in my opinion objects don’t fade into a color but into transparency.
To realize a fadeout to black (or any other color) one would fade in a black (or any other color) screen in front of everything.
I guess it can be done with a shader which takes the current “degree of fading” as parameter. The fadeout manager would change the parameter.
I wanted to share my idea before I open a Pull-Request mainly so that I don’t do anything that someone else is already doing
So my idea is to keep it very simple:
-
Add a UI Canvas that has a Rectangle
-
always scaled to the full screen size
-
default color is black
-
Add FadeInChannelSO and FadeOutChannelSO
-
has Parameters
duration
andcolor
but color gets defaulted to black so you don’t have to specify black every time -
you could combine these just into FadeChannelSO and pass a bool for fade-type
-
Add a TrainsitionManager to the PersistentManagers-Scene
-
has a reference to the Rectangle
-
acts as the EventListener for the FadeChannelSOs
-
has a Coroutine for FadeIn and one for FadeOut that
-
changes the color of the rectangle
-
turns the alpha value of the rectangle up or down every frame for the specified duration
-
keeps track whether it is currently doing a fade and skips every incoming fade-request while one is in progress (with a bool for example)
Other MonoBehaviours can then just Raise the FadeIn or FadeOut event and put in the desired duration and if they want the desired color and a fade will start that can’t be interrupted.
Your implementation is, well, far better than what I was gonna do. I’ll try implementing this as a first task.
I have completed programming the fader to black! Now it’s time to comment the code and create a pull request.
Singing fade to black in the background
I wanted to participate to this thread, I just want to ask about the “Concurrent” Fade In/out . What are the cases where we would have these kind of situation ?
@SteelcladLandship did you implement your or my solution?
If you implemented yours then I’m going to put mine into a PR as well
EDIT: I have found the PR, thanks for crediting me
You chose to remove the ability to change the FadeOut-Color.
What If you fade into black, then you play a pre-rendered cutscene that ends on a white screen, so you want to fade out from white?
Or something similar happens between the FadeIn and FadeOut so that a different color would be more suitable
Removing the ability completely removes developer freedom in that case, you could just default to the last used color if the given color is null or something like that
I don’t know if it’s more appropriate to talk about code in the PR or here but you could for example still pass a color as a parameter and after getting the oldcolor just do if (color == default) color = oldColor;
I wrote a more detailed comment in the Pull Request
also you can give parameters a default value, I will show you an example in the PR, that cleans up code
It could be cool if you could either get the progress of the fade or if you could attach an action that would be completed after the fade
although this might not be needed at all, depending on how the fade is going to be used
That wouldn’t be too hard to implement, for the former you’d just need to assign a global variable that is updated by the fadeout manager, but I’d rather not make too many changes at this point. I wanna wait for the pull request to be accepted so I can finally say I’ve contributed to the project.
Yeah you could probably just make the bool that tracks if it’s currently fading public like public bool IsInProgress {get; private set;}
and that should be it really
Understandable, I mean I would love to expand on this Fade-System myself as well
Hey, as I said on Github on the PR:
Thanks for collaborating on this
I like it, I just have a couple of comments which I’m going to fix before merging (no action needed from you, just tell me if I misunderstood something and my changes will break it)
For instance, in the manager you have two functions which are 99% identical (FadeIn and FadeOut), so I unified them.
I’ll merge for now. We will need to test it, but also, implement a mechanism so that the scene loading actually doesn’t begin until the first fade out has fully finished.
So basically: Disable gameplay controls > Request fade out > Begin scene loading > Scene loading finishes > Begin fade in > Enable gameplay again
I’ll put a new task up soon to do that. It will require some careful changes to the SceneLoader system.
Actually I made it on a whim: https://open.codecks.io/unity-open-project-1/decks/15-code/card/1au-delay-loading-to-accomodate-the-duration-of-fade-out
Hi! I’d like to contribute to the project and I got an idea how to implement fading with scene loading. So I see it like that:
-
SceneLoader has a reference to the inputReader
-
SceneLoader has a private variable _finishedFading
-
FadeManager rises a void event channel when it finishes fading and SceneLoader has a reference to it _fadeComplete
-
SceneLoader has new HandleFade coroutine that works like that:
-
set _finishedFading to false
-
the coroutine subscribes to the FadeManager’s _fadeComplete event
-
then it request Fade
-
waits in the while loop for until _finishedFading is true
-
if it is unsubscribe from _fadeComplete
-
if was fading in LoadNewScenes()
-
if was fading out enableGameplayInput
So the workflow is:
-
SceneLoader sets _finishedFading to true when _fadeComplete is raised by the FadeManager
-
After unloading previous scenes disable input
-
Start HandleFade() coroutine for fading in
-
Coroutine calls LoadNewScenes()
-
LoadingProcess() starts HandleFade() coroutine for fading out
-
Coroutine enables gameplay input
I actually wrote the code and it works, but I wanted to know what the community thinks
We could also add option to specify if we want to fade during scene load, but it probably would require changing the event that calls scene loading.
Please let me know what you think and if I should make a PR
I think the point of using events is that you don’t have to wait in a while loop. You subscribe to the event with the method you want to be called. Like this:
public void OnEnable()
{
globalEvents.gameStartEvent += onGameStart; //adding your method to the event listeners
}
public void OnDisable()
{
globalEvents.gameStartEvent -= onGameStart; //removing your method from the event listeners
}
It’s important unregister at the end to avoid problems when you play for the second time.
Okay, I see your point. So basically we don’t need a coroutine, we just continue scene loading process when _fadeCompmete is raised.