Posts by Volnaiskra

Welcome to our brand new Clickteam Community Hub! We hope you will enjoy using the new features, which we will be further expanding in the coming months.

A few features including Passport are unavailable initially whilst we monitor stability of the new platform, we hope to bring these online very soon. Small issues will crop up following the import from our old system, including some message formatting, translation accuracy and other things.

Thank you for your patience whilst we've worked on this and we look forward to more exciting community developments soon!

Clickteam.

    The trick is to use MOD:

    Please login to see this picture.


    To make it easier for myself, I have a little system set up for this in my VACCiNE tool, where the following altVals will be 1 at the appropriate intervals (and 0 at all other times):

    Please login to see this picture.

    This way I don't need to repeatedly type the MOD formula out, but just check for the altVal with a very simple condition like this:

    Please login to see this picture.


    Some of the larger numbers (eg. 353, 503) are purposefully prime numbers. My thinking there is that if I make something expensive happen every 500th frame, it may stack with other expensive things that are occurring elsewhere every 2nd/5th/10th/20th frames and potentially cause a microstutter as that particular frame takes significantly longer to process than its neighbours. By making that expensive thing happen every 503rd frame instead, it theoretically lowers the probability of such a microstutter.

    Adaptive Vsync is something you do in your GPU driver settings. So it's something your users can try, but it's not something you can do on the Fusion end.


    If you change the game's native fps to 120, then you can still code certain things to happen at slower intervals, to keep things performant. That's what I do. So I make vital things like my main character's movement code and camera code happen every frame (ie. 120 times per second) to keep things as precise and smooth as possible. But where it won't impact final quality, I make other events trigger every 2nd frame (ie. 60 times per sec) or even less often, like every 3rd, 4th or 10th frame.

    For example, it's a waste to check every single frame whether an enemy has collided with the player, since most of the time the enemy won't even be near the player. Instead, I do a broad check every 10 frames to see whether the enemy is in the general vicinity of the player. Only if that check returns true do I start doing more frequent and precise collision checks. Similarly, many of my cosmetic elements (eg. fog, particles, background critters) are only processed every Xth frame.

    Some things are processed much less frequently than that. I have some 'safety net' error checking routines to check whether, for example, an enemy has somehow gotten stuck inside some geometry. I only run checks like this every 100 or 300 frames. It would be wasteful to run them more often since they are very rare and/or purely hypothetical occurrences.

    Bug.


    Fusion says that altVal names must begin with a letter:

    Please login to see this picture.

    But sometimes it still lets you begin names with a number:

    Please login to see this picture.

    Sometimes it does, and sometimes it doesn't, and I'm not entirely sure why. If I try to name an altVal "123" then it will never work. If I try "123dfgdf" then sometimes it does, sometimes it doesn't. It seems more likely to block you if you've already been blocked trying to rename that same altVal, but the same name may work when used on a different altVal. And it seems less likely to block you if you just append numbers to the start of an existing name rather than typing a whole new name with numbers at the front.

    I guess this isn't a massive deal, though I did find myself with some altVals starting with numbers that I was under the impression were fine (though which I assume would have broken my code). It's only because I had a nagging feeling that something wasn't right that I tried a few other names and discovered that starting numbers indeed weren't supposed to be allowed.

    VACCiNE 2 contains a bunch of useful features that I personally continue to use daily, such as the debugging panel and ticks & rhythms system. In terms of gamepad support, it uses the old Joystick 2 object which has now - thankfully - become obsolete. It is recommended to use the newer SDL object instead, which is less likely to have compatibility problems with Steam.

    There would probably still be value in using VACCiNE's input system if you wanted to use its Unified Input System, which automatically consolidates gamepad and keyboard controls. For example, it gives you a single moveLeft altVal that will automatically be set from 0 to 1 if the user presses left arrow (keyboard), A (keyboard), D-pad left (gamepad), or left-joystick left (gamepad), making it simpler to code your game's movement. You would need to modify the VACCiNE code yourself if you wanted to use this in conjunction with the newer SDL object.

    I will probably migrate VACCiNE from Joystick 2 to SDL myself one day, as I will want SDL in the published version of my game. But it's low on my priority list for now because Joystick 2 still works fine for development purposes.


    I can't say whether volCAMERA is for you, but I'm certainly still very happy with it in my own game. It's based on the standard LERP-style smooth camera motion that many games use, and adds a bunch of useful customisations to it. So while it uses LERP to centre on the character, it can also smoothly veer towards areas that the player most needs to see (eg. in front of the character while moving, or below the character while falling). It's also pretty good at minimising excessive jerky camera movement if the player repeatedly performs rapid jumps and things like that.

    The comment on itchio (which I unfortunately hadn't noticed until now) is most likely because the user doesn't have the 2.5+ DLC version of Fusion, and the MFA uses a feature (custom qualifiers) that requires this DLC. I believe I uploaded an alternate MFA to the Clickstore that doesn't use custom qualifiers, but I must have forgotten to include that file when uploading to itchio. I've uploaded it now, so people without the DLC can now use that alternate version of volCAMERA. Both versions are fully featured - the only difference is that in the DLC version the qualifiers volCAMERA uses are clearly labelled (with appropriate names and icons) while the non-DLC version uses generic Fusion qualifiers.

    The Frame Editor has an option called View>Center to bring the camera automatically to the centre of the game area. Could we please get another option called something like View>Top Left or View>Home or View>Reset that centres the camera on X=0,Y=0? This tends to be a very commonly accessed area because around here is where we'll usually place most objects that aren't meant to be seen in frame (extensions, value holders, game objects that don't spawn until later, etc.). If the frame is full of lots of objects, dragging through the frame can get pretty laggy, so it can take quite a few seconds to get back there each time. Being able to jump there with a hotkey would save a lot of time.

    Oh I know that your levels won' t be 5000x5000 because you mentioned it was all happening on the top left. I just used the full 5000x5000 dimensions as a more extreme example of how important dimensions are to RAM. To show that even though something like 5000x5000 isn't that much bigger an area than something like 640x480, it would end up consuming almost 100 times more RAM.

    Plus the good thing is, i can leave things that will be used actually in frame, away from where any scrolling will be going on as well, so no worries of things getting crazy in terms of losing track of where I am


    Unless you are 100% certain that there are certain areas of your 5000x5000 frame that will never be used (and it sounds like you aren't), it might be a good idea to keep things off the frame area to make sure they won't ever get in the way. There's nothing stopping you safely placing everything to the top and left of the frame, which is what I do. Just make sure "destroy object if too far from frame" is unchecked as needed. You can also see in the below screenshot that most of my game objects are little squares. Wherever possible, I make the first frame of my active a 50x50 'icon' that won't actually be seen in-game. This makes things much easier to organise in the frame editor and - importantly - the square shape makes them much easier to read in the Event Editors.


    Please login to see this picture.

    Well, you definitely don't want to try and make it using 962 separate frames. Any time you found a bug or made a mistake you'd need to update all 962, which would be horrible. Though there would be some ways to mitigate this issue. For example, like putting most game objects in a single frame that is the included frame of all the 962 level frames - that way you can change/add objects in just the included frame only and it'll automatically update the other 962.

    But if the single-frame way you've done it for 4-5 levels is working well, then it will probably work well for 962 levels too, as long as you're disciplined with your organisation.

    The main issue I can imagine cropping up is you running out of RAM, depending on how rich your assets are. A single playthrough that went through all 962 levels would end up loading an awful lot of stuff into RAM, and if the levels are large and/or the enemies/platforms have graphics with large animations, then you may blow your RAM budget. In that case, you may need to build in some sort of mechanism that occasionally (eg. after every 20 levels) jumps to frame out of your main frame then jumps back in, just to force Fusion to purge all the stuff in its RAM from previous levels.

    But it all depends on how bit your assets are. The way to calculate how much RAM something will take is: pixel dimensions x 4 channels (RGB+alpha) x no. of frames.

    So, if your levels are all 640x480, then the RAM cost of your blockout collisions object would be 640*480*4*962 = 1,182,105,600 bytes, or about 1.18 GB

    If your levels are 300x200, then it would be 300*200*4*962 = 230,880,000 bytes, or about 0.23 GB

    If your levels were the full 5000x5000, then it would be 5000*5000*4*962 =96,200,000,000 bytes, or about 96 GB (!)

    As you can see in the examples above, the dimensions of your assets (including your hero/enemy/npc/platform/background animations etc.) makes a big difference. It's up to you to figure out whether the animations you'll throw at Fusion will break your RAM budget or not.



    The problem with arrays is that they're not easily human-readable: the contents sit inside an invisible grid that the computer can see but you can't. It's kind of like cooking a meal with a cupboard full of unlabelled spices, where you can't achieve anything unless you've previously memorised which jars contains which spices. It also doesn't help that the code for using arrays is somewhat ugly and syntax-heavy by Fusion standards. So don't feel bad that you can't wrap your head around arrays - that's normal. The way they're designed means they require an annoying level of cognitive load. And the whole point of a Fusion style of programming is that it keeps the cognitive load low, so that your mind can focus as much as possible on the creative and intelligent side of programming (eg. working out the logic) instead of being preoccupied with the dumb busywork side of it (memorising precise syntax or unlabelled array values)

    But arrays can be useful. Not necessarily for this project, but possibly for some future project of yours. If so, then I recommend mirroring your array with an Excel spreadsheet. Arrays are basically the same as a spreadsheet - a grid where you can store info in the cells of rows and columns. So if you make a spreadsheet where you clearly label the rows and columns, it will help you figure out how best to set up the array at the start, and will help you keep track of it down the line.

    Sounds like an intense time. If I were you, I would go to someone like a counsellor - someone who is basically paid to listen to you for an hour - so that you can debrief about the whole experience. That might give you some well-deserved stress relief.

    I think you'll be fine, but if you want peace of mind, then just create a simple stress test. Use fastloops to create a few thousand arrays and fill them up with some data, and see whether your computer explodes. If it does, buy a new computer, reduce arrays by 10% and try again.8o If it doesn't, proceed confidently with your project.

    IF there is one thing I would love to see in an upcoming release, it is a functional undo/redo, with an undo/redo action list, and with an option to jump to where the change was made

    Some specific improvements that would be nice:

    • Saving MFA doesn't wipe undo history
    • Switching editors doesn't wipe undo history
    • When you delete a group and activate group & deactivate group actions are automatically deleted, and then you undo the deletion of the group, those actions should be undeleted too.
    • Changing properties of objects (eg. rename, check visible at start, etc.) to also be undoable.
    • If you have multiple editors open, undoing affects them globally, rather than only working on the currently active editor.

    To answer your first question (why does it happen), it might help to remember that, if we think of your event as a grammatical sentence, the Active in your code is an object, not a subject.

    For example, take the sentence collars go on dogs. In this sentence, collar is the subject and dogs are the object. And most people would understand this sentence to be very clear about where collars go: they definitely go on dogs. But the sentence is not totally clear about dogs - do all dogs have collars? not necessarily, maybe some dogs do, and some dogs don't. The sentence leaves this matter rather vague.

    However, take the sentence dogs have collars. This time we've switched the subject and object. This time, the sentence is much clearer about dogs. The sentence seems to - if not state, then at least imply - that indeed all dogs do have collars. Meanwhile, this sentence seems more vague about collars. Do all collars go on dogs? Probably not...probably lots of things have collars, just like how saying dogs have eyes doesn't preclude the fact that lots of other things have eyes too.

    So, when you tell Fusion to create an "Object" at "Active", you are being clearly instructive about the subject (which, perhaps ironically, in this case is called "Object") and vague about the object (which in this case is the "Active"). So, Fusion knows that you want to create an "Object" at some "Active". Though it doesn't actually receive any strong hint from you that you want it at all Actives. Maybe you could say that it interprets your 'sentence' as Create an "Object" at an Active, or perhaps Create an "Object" at the Active. So it grabs the closest Active at hand (which is the most recently created one) and creates one there.


    Now, there is a way to reverse the subject and object. You could use the Launch Object action. You would access this from the Active's dropdown, which would make the Active, rather than the "Object" the centre of attention of this action (ie. the object).


    Please login to see this picture.


    Launching objects is usually for bullets and things, but if you launch it at a speed of 0, then it'll be unmoving, if that's what you want. What's interesting is that if you have a speed above 0, then the object will move, even if it doesn't have any "Movement" (eg. bouncing ball) assigned to it. (I assume it automatically turns it into a bouncing ball object?). Anyway, as you can see below, it creates the object at all instances.

    If this approach doesn't work for you, then I would use a forEach loop, as semar suggested.


    Please login to see this picture.

    Does your code include wall collsion code? In other words, what would happen in the below situation - where there is a wall blocking the player's horizontal movement - would the player stop, or would he walk through the wall?


    Please login to see this picture.


    Because it looks to me (albeit without seeing your code) like you have events that block vertical movement if the player hits an obstacle during Y movement, but possibly not events that block horizontal movement during X movement. The problem in your GIF seems to be about the latter. Though it's natural to think of your platforms as floors, in the case of your GIF the platform acts more like a wall, because the player is bumping against the side of it.

    It sounds like you've refined your enemy AI to such a degree that it's almost like the Tyranid Hive Mind (and I'm sure you get the reference), a singular entity doing all the enemy thinking as one, then distributing it around them as appropriate for their environment and the players actions?

    It's a great idea, but a long way off for a guy at my level. I imagine that the knowledge and understanding to create such a thing takes thousands upon thousands of hours of experience, trial and error across many projects before you finally 'get it', but that's where I want to be one day as it sounds like it has huge potential and all manner of gaming applications :/

    Actually, I think (I'm not sure) that my enemy system might be an example of your finite state machine: a singular but multifaceted chunk of code where all the bits are switched on or off as needed, according to the current states of the entity.

    The silver lining is that while it will be a tedious nightmare to redo all that work, you will almost certainly do a better job the second time round, and the project will be better for it.

    But still, use a cloud backup system, like OneDrive, which comes free with Windows.

    On top of that, I highly recommend a program called "OO AutoBackup", which you can set up to silently backup certain key files at regular intervals. I have mine set to do it twice daily, plus into a different location weekly, and a third location quarterly. It means that if something goes wrong with a file, I'll only ever lose a few hours of work max. But if I accidentally broke something 3 weeks ago and only realised today, I also have a much older version available that can help me recover whatever I broke.

    I think lots of variables is usually a good thing. Unless, I suppose, you keep forgetting about them and doubling up with new ones until you have 5 altVals doing the work of 1. But as long as you're well organised, giving everything its own well-named altVal is helpful, in my opinion. Otherwise you run the risk of using the same altVals for too many things at once, so that if you tweak something over here it breaks something over there. Or you get into the habit of using lots of hard-coded, unlabelled numbers everywhere, which will make your code less readable and harder to manage.

    Each of my enemy objects actually uses about 300+ variables: basically the full 260 altVals, plus all 32 flags, plus a few dozen more altVals in child objects such as skins and turrets. Every enemy in my game uses an instance of the same object, no matter the type of enemy - stationary, moving, a flyer, a shooter, etc. To create a new enemy type, all I need to do is tweak some altVals like this:


    Please login to see this picture.


    The rest will take care of itself. If the enemy moves, it will automatically navigate terrain, either flying through the air or walking on floors, walls and/or ceilings, avoiding hazards. It will jump over gaps, with a suitable air-time and jump parabola that takes into account its speed and the width of the gap, and environmental factors such as wind speed.

    If it's aggressive, it will automatically seek out the player, and eventually give up if it can't reach her. It can handle all sorts of shooting behaviour (aiming, multi-pellet bursts, heat-seeking, friendly fire) depending what I set.

    All of its animations and sounds will be managed automatically - all I need to do is provide the graphics/sound assets and put them in pre-determined spots in my game's data folder.

    The whole code is comprised of some 2000 events, but code will be automatically enabled/disabled for each instance, as needed, depending on what the capabilities and requirements of that particular enemy are. Performance is good because the system automatically optimises itself as needed (eg. slow-moving enemies won't check for obstacles as frequently as fast-moving ones)

    My enemy system is one of the things I'm most proud of. Even though I built it a few years ago and have long forgotten exactly how it works under the hood, I can create a new enemy type today, and it works 'out of the box' with little fuss. If needed, I can make small tweaks to some small portion of the system without affecting anything else.

    One of the reasons it works this well is because just about everything is contained in a single active object, with all of its various features neatly segregated amongst its many altVals. I'll go as far as to say that I simply wouldn't have been able to pull this off without 2.5+. With only 26 altVals per object, I think things would have become too fragmented and cumbersome for me to be able to mentally keep up with all the different moving parts.

    The code is too embedded in my game to easily separate out into an example MFA. But I can describe it a little more.

    Yes, fogRotationSpeed is an altVal, as many of the other ones are. There are 3 altVals that will determine rotation:


    Please login to see this picture.


    rotationRhythm is a randomly generated number between 60-68. This will be used to pick a rhythm from my rhythms Active object, because the 60th to the 68th altVals of that object have the following code (executed every tick):


    Please login to see this picture.


    The above altVals will continually generate numbers that gently undulate between 0 and 1. They'll all gently undulate at slightly different intervals, to provide some variation. They do that by applying sin() to the timer (⏱milliseconds is an altVal I use that is more or less the same as Fusion's timer). The one called moderateVariation6 is a bit different: it will have a 'plateau' effect because it will pause at 1 for a period of time each cycle. The one called stationary will not undulate at all, of course - it will always remain at 0.85.

    So every piece of fog is assigned one of these 8 random rhythms, which is stored in rotationRhythm. This is then used in the following event:


    Please login to see this picture.


    So the AltValN("~"), rotationRhythm("~") will just be one of those 8 rhythms from above which, if you recall, is a slowly undulating number between 0-1 (except when it's a stationary 0.85). On its own, this would cause each fog piece to gently swivel 1°, which is not much. So this rotation is multiplied by fogRotationSpeed (which is randomly assigned to 25-40 above...and which I probably should have called fogRotationStrength actually).

    So each fog piece will start at a random angle (determined by fogRotationStartPosition) and slowly swivel back and forth from that angle by some amount between 25°-40°.

    I do a similar thing with the scale:


    Please login to see this picture.

    I do similar things in my notes, anything referring to an upward movement/top colliders/jumping and hitting a ceiling etc being sky blue, downward stuff being brown like earth....

    I hear ya ^^

    Please login to see this picture.



    By the way, I would encourage you to get Fusion 2.5+ (I don't see the icon for it under your avatar), as creating a well-organised MFA is harder without it. Global events can be a great time-saver, but I found them barely usable without the DLC. For me, the DLC's Find all feature is an indispensable upgrade to the old Find feature. Having 260 instead of 26 altVals means you don't have to split them into multiple objects then waste time rummaging through them to find the right altVals. Qualifiers are a great way to streamline and organise your code, and you can integrate them into your project more tightly and clearly once you have the DLC and can give them custom icons and names.


    8| Awesome

    how did you make that panel? :/

    It's mostly comprised of lots of counters/strings all lined up. The framework to set it all up comes as part of Please login to see this link.

    Thanks for all the advice folks, I've actually clocked over 3000 hours practice in Fusion over the last few years (yes I log it) so there's a fair bit I can do now, but I'm getting to the stage where I'm trying to tie it altogether into a properly thought out and structured approach rather than just hot shotting ideas when they come to me.

    I'm feeling like effective coding doesn't come down to being able to do loads of fancy clever tricks, but rather how good you are at pulling together the basics in a consistent, disciplined, logical fashion?

    OK, well, you've had plenty of experience, so my comments about 'getting your feet wet' clearly don't apply. I totally agree with you about the importance of a disciplined and thoughtful approach.

    For what it's worth, I can give you a peek at how I've done things in my game. I don't know if it qualifies as a state machine (probably not, or at least not entirely) and I don't know if it would meet the approval of any programming theorist. I never studied programming in any proper way, and I'm basically self-taught. Which means I made the dubious choice to pick a teacher who literally didn't know what they were doing.

    But Steam says I've spent over 19,000 hours in Fusion, and 95% of that has been on Spryke. In that time, I've (eventually) done a good job of making an engine that works effectively and is easy for me to understand. In the first couple of years I was more haphazard, and by about year 3 I'd ended up with spaghetti code, a mountain of bugs, and had to redo a lot from scratch. Since then, I've been very disciplined with keeping things modular and well-organised. I'm pleased to say that even if I revisit pieces of the engine I made 5 years ago, I'll generally still find my way around pretty comfortably.

    I can't overstate how important I believe it is to keep everything about your MFA organised and clear. Not just the code, but the frame editor, the icons, the naming conventions, the general workflow, and of course you should comment extensively. If you allow yourself bachelor habits, what starts as a few dirty socks in the corner today will eventually become a labyrinth of lost belongings and mysterious smells. It's the same with an MFA.

    I keep my variables organised in relevant objects, and I give those objects 50x50px icons that are easy to read in both the Frame Editor and the Event List Editor. For example, this is my Spryke States object:

    Please login to see this picture.


    Inside it are all the different state-related variables of the player character. I use a clock icon to differentiate those that are timer-based variables, and a flag icon for flags, so that I immediately know what I'm dealing with when I see it in an expression. I group them logically and use other altVals as dividers and headings:

    Please login to see this picture.


    I can keep track of many of these variables during runtime in my debugging panel (the blue section):


    Please login to see this picture.


    In my code, I first set a bunch of parameters for the physics of the movement engine. For example, gravity. The gravity changes depending on various factors in the game. When Spryke jumps normally, it's one value, when she jumps on a special trampoline platform, it momentarily becomes weaker. While she floats in a bubble, it becomes weaker still...and so on. I set all these different gravity values early on, in my "config" section:


    Please login to see this picture.


    Lower down in my code is a group called Spryke States where all the various things that can determine or affect the player's state are processed:


    Please login to see this picture.


    For example, if the player is not on the ground, and is moving downwards, then I determine that the player is falling, which will activate the IF: falling = 1 group (this group is continually closed earlier in the code, so it will only run if it's turned on here, because falling=1):


    Please login to see this picture.


    In this group, I set up various things that need to be set up. For example, I cancel out certain states/variables that clearly don't apply any more (eg. set jumping and isStopped to 0). Some things will be set no matter what (event #5654), while others must only be set if the player is not on a wall (event #5648). I also set the gravity (how much the player will be pulled downwards per frame) and the fallCap (how fast the player is allowed to drop before further acceleration is stopped):


    Please login to see this picture.


    However, some of the things that were set here may yet be overridden. For example, lower down is the Floating section of the code. And if in this section the player has been determined to be floating, then a bunch of different code will execute. Including different gravity and fallCap values that will overide what was set earlier:


    Please login to see this picture.


    Once all of the player's internal states have been processed, we then process various mechanics that can affect the player externally:

    Please login to see this picture.


    For example, if the player is standing on a vertical platform, we make a note of it in the Spryke States object by setting vertFloater to 1, and we record how fast that platform is moving in YmovingPlatMovement:


    Please login to see this picture.


    Notice that the player object hasn't actually been moved in any way yet. There have been no set X(player) to... or set Y(player) to... actions thus far. All I've done is set a bunch of variables. Once the entire Spryke States and Movement-related Mechanics groups play out, the variables will have been set, cancelled out, and overidden as needed, and we arrive at the final states that the player is in. Only then is it time to actually process the movement:


    Please login to see this picture.


    So, for example, we now apply the gravity (insofar as the fallCap will allow). As we saw above, what those values actually are will depend on various factors. But by the time we get to this point, it's all been worked out, and they are what they need to be, depending on the various player states:


    Please login to see this picture.


    After applying gravity to the Y speed, we then adjust it for various other things that may be affecting Spryke. For example, the vertical platform platform we registered earlier. Notice that YmovingPlatMovement will actually be added to Spryke's Y movement every single frame, no matter what. Most of the time, YmovingPlatMovement will be 0 so won't have any effect. But whenever the player is indeed on a vertical platform, YmovingPlatMovement will be set appropriately above, and will be applied here.


    Please login to see this picture.


    We then consolidate everything into a final value yMovePixelPerFrame (second-from-bottom action in above pic) that will be used to actually move the player:


    Please login to see this picture.


    Finally, once the movement has been completed, we reset a bunch of stuff, and perform a battery of tests to see where the player has now ended up (eg. is she now touching a wall? if so, what type of wall?), some of which are shown below.


    Please login to see this picture.


    Spryke has been moved, and the code is totally up to date with her various states. Now, and only now, we ensure the appropriate animation is playing, depending on her various states:


    Please login to see this picture.


    So that's, broadly speaking, the process. Everything happens in due course, in an appropriate part of the code. All the different physics and movement parameters are stored at the top, where I can easily see and modify them. They will then be applied as appropriate by subsequent groups. First we determine the player's various states, then we check whether she's being affected by any external mechanics. Only once we know all this information do we perform any movement. Finally, once we've moved the player and re-measured her relationship with the environment, we update her animation. Here's a snapshop of my Event Manager window:


    Please login to see this picture.


    You will have noticed that I use a lot of graphic characters in my group names. Keeping things clearly labelled and visually segregated helps remove cognitive friction so you can spend more mental energy problem-solving and less on trying to remember what does what. I posted a big list of useful characters Please login to see this link.. I have dedicated buttons on my streamdecks/macropads for my most commonly used ones, and I use a pop-up clipboard manager called Phrase Express for the rest:

    Please login to see this picture.