CGI Сoffee


Revelations of a computer graphics apprentice

The Tide Turns: Unity Abandons the Controversial Runtime Fee

Great news to all Unity devs out there!

Yesterday, Unity published a new post on their blog titled "A message to our community: Unity is canceling the Runtime Fee."

You have to read it, to believe it. After what felt like a catastrophic fiasco of a business model change — initially relying on an install count metric, later revised and replaced with a "runtime" metric that would count the number of Unity-made app instances running on client machines — this change of direction both fills me with delight and sends my mind racing.

Why?

I'm sure you're well aware of just how unpopular their previous decision was. So much so, that many developers announced they'd be migrating to other engines like Godot or Unreal. What's even worse for Unity — many devs actually delivered on that promise! This is especially telling, considering how buggy, incomplete, and limited Godot is compared to Unity, particularly with its build platform support. Not to mention how complicated Unreal Engine development can be due to its reliance on C++, making it less small-team friendly.

Still, I can't help but wonder why the whole Unity team including their sizeable board of directors went with a terribly and obviously flawed and under-cooked proposal from John Riccitiello, and just... Implemented it. I understand that Unity is a business with business goals, but such drastic changes to the Terms of Service should be announced well in advance, such as to not shock and drive your existing users away.

It's not my business to try and estimate how many clients Unity lost during this period of turbulence, but I absolutely welcome the return to the original business model. I'm not a fan of Unity making their 6th version require phoning home at least once every 30 days, but you're free to stick with your previous Unity LTS branch if you're content with its ToS. At least until it reaches its EOS, after which you'll have to upgrade. Other than that — good job, Unity, with getting rid of John Riccitiello, and re-evaluating your strategy moving forward.

Here's a backup of the original blog post in case Unity decides to quietly change it sometime in the future:

Unity blog bost backup

How to Fix Delayed Notifications on a Google Pixel Phone on Android 11, 12, 13, 14 Using ADB via USB (Tethered) or On the Device Itself (Untethered)

Relevant for Google Pixel models:

  • Pixel 4, Pixel 4a, Pixel 4a 5G, Pixel 4 XL
  • Pixel 5, Pixel 5a, Pixel 5a 5G
  • Pixel 6, Pixel 6a, Pixel 6 Pro
  • Pixel 7, Pixel 7a, Pixel 7 Pro
  • Pixel 8, Pixel 8a, Pixel 8 Pro

Android versions:

  • Android 11
  • Android 12
  • Android 13
  • Android 14

Great Cameras, Buggy OS

One of the requirements for a new project of mine was a dedicated Android smartphone with some of the best cameras on the market. According to the latest tests, the Google Pixel smartphone lineup still leads the pack when it comes to reasonably priced camera phones. It also seems like there haven't been any noticeable improvements on the camera hardware in those since the release of the 6th generation of Pixels. So I picked up a shiny new Pixel 6a, created a new Gmail account and signed in.

All was going well until I realized that most, if not all email and instant messaging apps' notifications were delayed. Sometimes for a minute. Usually – for much longer.

At first I shrugged it off, thinking, "Well, what did I expect? It's an Android phone. You have to spend time fiddling with it to get it to behave as needed...". You know the drill.

Therefore, I made sure to try and fix this with the tools presented by the OS:

  • Disabled Battery Saver and Adaptive Battery
  • Lifted any background activity restrictions for Gmail and messaging apps
  • Allowed those apps to have unrestricted access to the internet
  • Updated both Android and the apps to the latest versions

power-saving-disabled-on-pixel-phone-no-effect

Alas, even after completely powering down and restarting the phone, as recommended in some forums, nothing changed. Within about 5 minutes, the phone would still go into some sort of deep sleep mode, which in turn would cause email and instant messaging notifications to be delayed again.

Really? Do I still have to switch into a "Power User" mode and jump through hoops even in 2024 to fix freaking notifications?

Guess some things never change…

gta-here-we-go-again

Power Walking With Weights. A Comprehensive Look at How Weighted Power Walking Can Boost Fitness, Increase Strength, and Enhance Your Quality of Life

"Power walking is better than sex."
— Me

🎉 How Lucky For You!

You've just landed on a blog post where I'll introduce you to the absolute META of fitness exercises. The top of the heap. The cream of the crop. The safest, most efficient, life-changing workout you've never heard of.

Walking. With. Weights.

It's true what they say: "Life is really simple, but we insist on making it complicated".

Find out how the simple act of walking with hand weights can greatly benefit your physical and mental health (with video tutorials by yours truly!)

What IS Walking?

I bet you never considered walking something worth contemplating.

Walking is what humans evolved to do over the span of hundreds of thousands of years. It's the most optimized type of movement the human body can do. Walking, just as standing upright, are so important for survival, we can do them without any conscious thought: our brains actually developed separate areas, specialized at maintaining our sense of balance, Cerebellum being the prime example. That is how important walking is.

However, "unconscious" control over walking is mostly possible only when no extra load is present on the body. That's because the added weight usually noticeably changes the center of gravity of the combined system.

walking-with-box-loop

The good news is that the body and the brain are highly pliable and adaptable. If we keep walking with something moderately heavy regularly, over time we'll get more used to such movement, whereas letting go of the load would make previous weight-free movement easier.

So… If we walk all the time… What if we deliberately introduce challenge to this very activity with the goal of making ordinary walking effortless, and improve our fitness along the way?

That's the idea I'd like to share with you, along with the expected results and explanations, in the hopes that you'll find this activity as compelling and inspiring as I did.

3D Printers Are Awesome! Or — What Is a 3D Printer Actually Good For?

A couple days ago I was doing my kitchen chores and realized I was out of food (or sealing) clips.

food-clips

And I needed those ASAP.

So the choice was to either:

  • stop doing the important stuff (that being actually cleaning up and sorting the inventory) and go out looking for some sealing clips or
  • spend 10-15 minutes setting up a print job and 3D print some clips right at home!

The latter option was so much more convenient and fun, that I obviously went with it. And by the time I was done, the clips were done as well, and I made use of them straight away.

my-3d-printed-sealing-clips

Afterwards I realized that this provided a great opportunity for a brief post on the topic of 3D printing and my experience with it. As well as another look at the age-old question: "What is a consumer 3D printer actually useful for?"

I'll tell you all about it.

Protect Unity Prefabs From Changes With the ISerialization Callback Receiver Interface

Unity prefabs are awesome. They are easy to create, easy to instantiate, and with the current versions of the Unity Editor can be used to create prefab variants and even nest prefabs within prefabs, allowing you to modularize the design of systems and scene objects.

But this simplicity comes at the cost of a higher risk of screwing things up the larger your project or the team grow...

All because of this little "Apply All" Inspector button, which is both a blessing and a curse:

Unity Apply All overrides button

You know what I'm talking about. Imagine having a prefab for an object or an important interactive actor, and scattering it all over the scenes in the project, each one — with its own unique overrides for the position, variable values or even MonoBehavior component scripts attached or removed. Obviously, they are all based on the same shared prefab asset on disk, which is expected to have a certain set of initial values and components, which are then either used "as is" or are changed slightly here and there in the scenes.

The problem is, it only takes one careless press of the "Apply All" button to screw up the prefab for every single instance of that prefab in all the scenes! And chances are, you or a member of your team will eventually press this button and completely change the base prefab on disk, which all scene instances are based upon. And the more prefabs in your project, or members in your team, the higher the chance of this event happening. Sometimes stealthily, which is even worse... Thank Heavens for version control!

Unfortunately, Unity doesn't provide a way to disable this button for certain prefabs (at least I wasn't able to find a way to do so). That's why when designing my previous game, even though I was the only developer and scene builder, I decided to save myself the headache and created a custom bare-bones "ActorLoader" prefab. I would then exclusively use this prefab to place interactive instantiatiables in the scenes. Such a prefab had just a handful of fields which were meant to always be overriden and not to use base properties stored in the shared prefab: a prefab type enum (used to determine which prefab to instantiate from the Resources folder), starting Transform parameters and a couple of modifiers, all overridden right away to ensure that no changes to the base prefab asset would ever break ActorLoaders anywhere in the project. And this approach worked wonderfully.

Such "loaders" are in fact considered standard practice in the industry in contrast to scattering actual prefabs in the scenes, for a lot of reasons, which I won't list here. But this still doesn't solve the original problem of prefabs being modifiable with a press of a button!

A Solution!

Unity prefab is just a serialized object. Which means we can tap into the serialization routine and do something when Unity serializes or deserializes a prefab in the Editor. All you need to do is extend one or several MonoBehavior components attached to the prefab from the ISerializationCallbackReceiver interface, and implement a couple of methods.

Here's how I did it in my current project: inherited a component from MonoBehavior and the interface:

// See: https://docs.unity3d.com/ScriptReference/ISerializationCallbackReceiver.html
public class MyClass : MonoBehaviour, ISerializationCallbackReceiver

And then implemented a custom OnBeforeSerialize() routine, which is executed right before Unity commits any changes to the serialized object, which is a prefab in our case. I've commented it to help you understand how it works. Nothing special, but gets the job done:

#region ISerializationCallbackReceiver
void ISerializationCallbackReceiver.OnBeforeSerialize()
{
    // This can only be done inside UNITY_EDITOR since we can not check prefab status in a build!
    #if UNITY_EDITOR
    // IMPORTANT: EditorApplication checks must be done first.
    // Otherise Unity may report errors like "Objects are trying to be loaded during a domain backup"
    if (EditorApplication.isPlayingOrWillChangePlaymode || EditorApplication.isUpdating) return;
    // Validate the type of your prefab. Useful pre-check.
    if (UnityEditor.PrefabUtility.GetPrefabAssetType(this) != UnityEditor.PrefabAssetType.Regular) return;
    // Override properties only if this is a prefab asset on disk and not any of its scene instances
    if (UnityEditor.PrefabUtility.GetPrefabInstanceHandle(this)) return;
    // Finally, re-set any fields to initial or specific values for the shared asset prefab on disk
    // This protects these fields when "Apply Override" gets called from any of prefab's scene instances
    // Example: enforce default position, rotation and scale values for the prefab
    transform.localPosition = Vector3.zero;
    transform.localRotation = Quaternion.identity;
    transform.localScale = Vector3.one;
    // Example: your variables here
    // myFloat = 1f;
    // myAssetRef = null;
    // myClassInstance.myString = "Default string";
    #endif
}

// OnAfterDeserialize can be left empty
void ISerializationCallbackReceiver.OnAfterDeserialize() { }
#endregion

As you can see, this method makes sure that each serialization call to the shared prefab asset on disk goes through a custom procedure which re-sets values for certain fields and references, and can even check whether specific components are attached to the prefab root or any of its children, and either add them if they're missing, or remove ones that shouldn't be there. This procedure is invoked any time prefab is serialized, which includes the aforementioned "Apply All" button or any other way to apply changes to the original asset.

And there you have it! With this approach each time you create a new prefab you can decide which shared asset fields and references should be protected from accidental overrides, and simply specify default/initial values for them within the ISerializationCallbackReceiver's OnBeforeSerialize() method.

Hello Again, Unity. I Missed You

Some time ago I bashed Unity Game Engine (the Editor in particular) for its instability and lots of quirks and questionable changes that took place over the years following the version 4 release of the Editor.

Fast forward to this September. I'm done with my microcontroller shenanigans and have mostly finished the Design Document for the game I talked about quite a while ago (which took a lot of planning, for I am a project manager after all).

What's the next step then? — Exactly!

Prototyping!

So I've been playing with the latest "LTS" version of the Unity Editor 2021.3.7 and...

Wow!

And I mean a good wow:

  • It's rock solid and hasn't crashed once even when I was importing some of my older code and extension methods from the previous game which I wrote in 2012-2015. And 95% of the code-base ended up being non-obsolete! I guess Unity APIs didn't change that much over the years, huh? How cool is that?
  • Package Manager is amazing. Cutting some of the features of the engine from the binary/default assembly and turning those into officially supported packages with the added convenience of being able to download and update them as you please? What a deal!
  • Unity Addressable Asset system (or "Addressables"). Nuff said. This is a feature Unity devs had been dreaming of for like a decade
  • Nested prefabs. I know, I am a slowpoke, since this is not a new feature, but there is a difference: they work. Compared to my previous experience with nested prefabs in Unity when they would crash the editor like 50% of the time when I was editing them – now we're actually usable! Huzzah!
  • Integration with Visual Studio is very solid, pleasure to work with
  • I looks the same, feels the same and despite adding new features like DOTS and burst compiler, Unity team didn't change the paradigm too much, so being a knuckle-headed brute that I am I can continue using ScriptableObjects and MonoBehaviors for most of my objects and scripts. Yay!
  • C#. Yes, C#. That C#. It's not new of course, but it's C#. It's C#, you see. It's not C or C++. It's C#. I love C#, I guess that's what I'm trying to say
  • Also, Asset Store is still a thing. And can still save hundreds of hours of development for a couple hundred bucks
  • Oh, yeah, it's still free of charge for indie devs. Just sayin'

All in all, the time has come...

Unleash the monkey coder!

monkey-coder

Putting "Smart" Into Smart Devices: Enter the World of Microcontrollers With WiFi and Bluetooth. Learn to Build Your Own Smart Devices Using Off-The-Shelf Components

This post is not a comprehensive dive into the world of microcontrollers, but rather a brief introduction and a compilation of interesting facts I've collected over the months working on a couple projects that made use of contemporary microcontrollers to achieve real and practical goals.

The Harsh Reality of Reality

I'm sure you're well aware of the situation with microelectronics prices. It's been getting worse for a couple years and doesn't look like it will improve any time soon due to the on-going crisis in several areas at once: semi-conductor component shortages, logistics issues, political games, socioeconomic factors and everything in between. For an average technically-minded person this means that one must look for creative ways to achieve their goals or alter those altogether.

We do live in the real world after all, and one must adapt or perish.

pi-4-price

Remember "low-cost single-board PCs" we eagerly watched evolve and get cheaper over the years? All of those Raspberry/Orange/Rock Pi's and "Zeros", Tinker boards and Jetson Nanos? Oh they did evolve all rightInto overpriced 100+ USD boards. So now we're stuck in a reality where even the simplest single-board ARM PC in the form of a Pi Zero W is valued at 100 USD instead of expected $15-20.

pi-zero-price

So… What Do?

There are practical alternatives to single-board PCs:

  • Small x86/x64 PCs
  • Microcontrollers

The first option is blatantly obvious if you're looking for a device with a real multitasking OS and a load of compute power. And used PCs nowadays? They are cheap. For a market price of about $150 for a Raspberry Pi 4 with 4GB RAM you can get a used mini-PC with a 4-core Pentium/Celeron CPU, 4 to 8 gigs of extendable RAM and a 128GB+ SSD system drive with an option of connecting a second drive via SATA or M.2.

mini-pcs

Almost any PC will blow a Pi out of the water performance-wise. Sure, small PCs can't beat Pi boards' dimensions, power consumption and embedded IO-capabilities with over 20 I/O pins exposed for communicating with external devices. So if you're looking to set up a home web/file server, retro emulation station or use the machine as a simple desktop PC, opting for a used mini-PC today would be a no-brainer.

But what if you actually require rich I/O capabilities, small dimensions and low power-drain of a small single-board device?

Enter… Microcontrollers!