Consider this the first formal announcement of the result of my work over the past nine months.
That’s right. I’ve been missing because I’ve literally been busy. Go figure.
After months of reading through textbooks on image processing, noise generation, papers by the likes of Perlin and Worley, and procedural development techniques—along with a little on fluvial and tectonic geomorphology—I felt I was ready for an actual application of it.
The Way of the Bee!
The Way of the Bee is a game about being a beehive. You are the queen, you are the colony. You fight against real threats to bees, such as fungal and pathogenic threats, overheating or freezing, resource starvation, predators, and weather and climactic threats.
Journey as a colony swarm to your chosen location. Establish a hive, and send out sentries to find floral groves and gather nectar to bring back to it.
Be wary of threats from birds to lizards and frogs to fish to bears, navigating a procedurally generated terrain to find the sustenance you need to grow the hive. Be mindful of getting winded, staying adequately warm, and dodging inclement weather that might otherwise cut your journey short.
Return to the hive after finding substantial groves and setting a marker, then communicate your find to the other bees with a dance. Get it right, and a swarm will depart for the nectar and automate the process… but be mindful of threats to their well-being!
Gather enough resources before winter, and you will have enough bees to form a second colony swarm, to go to a new environment, further progressing in the game and facing new threats in doing so. If you don’t have enough, weather the winter with casualties, and begin again in the spring with your remnant bees.
Along the way, you will encounter heat waves that require dedicated bees to cool the hive, frosts which will requires bees to expend calories to warm it, parasites and hive beetles, full-on attacks by wood peckers and bears, and even hornets. Learn the way bees can defend their hive, from massive stinging (at the cost of the bees that sting), overheating the enemy, and even mummifying it in propolis.
The choice, and the price to pay, is yours alone; but think quickly.
Experience the fear of losing your queen, and desperately expending precious resources to hatch a new one. See badgers, skunks, and birds and with untold terror. Sacrifice everything in the name of your hive and its survival. Understand the need to sacrifice sick bees who are not recovering, and send them away to die alone; or invest calories and workers in medical care until enough of the bees recover.
The colony must survive.
Coming soon, opening platforms will be Steam and Itch.io.
You’ll find it here. Every now and then I’ll get a little mentally tired with whichever piece I’m working on, and I’ll just doodle for a bit. Every now and then, now and then, I’ll come up with something brilliant while doing it.
Time to turn them into t-shirts and mugs!
So I’ve got two items so far and probably a few more coming through the rest of the week—the one featured to the right of this paragraph is a Christmas tree made out of fractal constructs, which admittedly is the kind of thing you either get or you don’t… which also makes it funnier.
And to the left of this paragraph, we have my take on Spider-Man: No Way Home. (Also a play on “Magneto Was Right” / “Thanos Was Right” / “Mysterio Was Right”) I’ll give it a seven out of ten, it was a darned good movie; there were only a few things that bugged me and if we’re going to be realistic, they specifically bugged me, so I can’t complain. My wife and I had a lot of fun and I’m looking forward to Morbius later in the coming year.
We’ve all been there. We’re hammering away at a project invigorated by not just mastery of resources, but raw passion for it. We have ideas for art, story, song, all of the core components of our planned piece; we have mathematical precision and the comprehension of how to use it to build the framework that will give it life.
Then, out of the blue, for perhaps the most innocent of reasons, something terrible happens to us. We become ill, we capture a pathogen from the earth and air and people around us; we become weak and oppressed by a force of nature.
In my case, it was a particularly notorious bat virus.
Not that I haven’t gotten the vaccines; my family has a long history of autoimmune diseases to begin with, and experience has shown me that I am not an exception to this. So, when everybody started dropping around me, I got that vaccine the moment it was available. I hid from public spaces for longer than I thought I could bear. I got the second shot of the vaccine. I’ve had three boosters now, one each year, for the new variants.
And let me tell you, thank God I did; because that still felt like a particularly ugly head cold. It took me about a week to get over it, during which I made only marginal progress on my swarm AI. It was a setback I would have preferred to avoid, but it’s the hand I was dealt, as they say; and now, I face the uphill battle of getting back into the zone.
It is a zone, of course; much as sports players, gamers, and any other form of skilled craftsman has a zone. It’s an ideal state of mind for the task, on the threshold of being too hard and too easy. “Too hard” has been offset for me, as I’m still recovering; it’s much less difficult to get overwhelmed now, and I am faced with a gradual recovery until I’m at my former strength and acuity.
So, in lieu of my usual update, I’m chronicling advice I have for anyone who faces the same problem.
Understand that it isn’t your fault.
Diseases happen, much as rain storms, droughts, black outs, blizzards, and taxes do. Last week, wishing I felt better would not make me feel any better. It would expend my mental resources, and time which would be better spent resting, staying hydrated, and recovering, would instead be wasted on a vain mental effort.
No one created this virus on their own. If it isn’t the coronavirus, no one gave you your genes on their own, or created the weather intentionally; certainly not you. Disease saps our strength and makes us lean into depression, particularly as we strain ourselves; but the disease is not your fault.
This is not to say that it would not have been convenient if I was at full strength over the last week; only that by realizing I was not, I was capable of addressing the pertinent problem of my illness, and could properly prepare for the moment when it passed and I was at full strength again.
Beating oneself up over a problem which could come about because of the most innocent of things never leads to anything productive. In truth, such incidents need to be planned for early in the project—they will happen—and time should be allotted to account for them. Not everyone always does this, particularly on personal endeavors, but this is an effort everyone should consider making.
Recognize that it will come to an end, and prepare your notes for your recovery.
For the coronavirus, given you are fully vaccinated, the CDC recommends about five to ten days of isolation. Once this period of isolation is over, your body will still be clearing out all of the debris from fighting the virus and attempting to stockpile strength and energy.
Once you know that you are ill, given that the disease has not hit you in force, I recommend taking notes for yourself about everything you’ve been doing. Technical notes, on the state of the project, where it needs to go, and all general strategies for getting there.
If you happen to have a change log and a UML diagram together already, great! Use them! However, it’s also good to give yourself a gentle note, no more than two or three paragraphs long, on what needs to be done next. Use simple sentences—simpler than you think that you will need—that focus on the core actions that need to be done. It’s likely that after the incident hits, you will have fewer mental resources and less patience, so write kindly and clearly.
Bring all of your work to a closing point, the closest semantically complete block you’re comfortable with; and leave comments on the purpose of anything new you write. You want to tie this up before the symptoms hit you in full.
Afterward, I recommend grabbing any supplies you will need—groceries, tissues, cold medicine, whatever it may be—and preparing to stay hydrated and comfortable for the duration of the disease. Again, go easy on yourself, and have faith that you’ll shake it and return to your former glory.
Ease yourself back into the Zone using simpler projects.
This is a big one. Time has passed, your head or stomach are clearing up, and you’re ready to get back to work; but your mind is like stone and concrete. You don’t remember what you were doing, you’re still a little weak, but you’re optimistic.
The last thing you want to do here is to pretend that you’re at the same strength you were before the sickness; you’re still gaining momentum again. I recommend easing yourself back into the project by taking on something parallel, like studying an API you’ve been curious about.
Grab something that relates to your project, but is simpler. For me, it was re-learning Qt 5. I’ve had a book on it for some time now, which is written and edited in a manner just-dodgy-enough to keep me on my toes. In the past three days, I’ve gone through four chapters, and may finish the book before the end of next week.
Meanwhile, I touch on something involving the swarm AI I was working on before every day. This way, the project becomes fresh in my mind again. By spending a couple of hours every morning simply reviewing, and experiencing, some of the latent bugs, I become familiar with the code again.
Think of it like returning to a gym after a two month break. You need to ease yourself back into the routine, with steady progression and confrontation of the tasks. It’s all too easy to simply quit and walk away forever; but then we would never see our projects bear fruit.
If there is a vaccine, get vaccinated. Prepare for infection.
There’s been a disappointing, and alarming, amount of dissent on this. I can tell you for a fact that the mRNA vaccines are the result of several centuries of research into combating disease, and they work. In most parts of the world, your government will give it to you for free. It will be all of fifteen to twenty minutes; less, if you lose patience and sneak out early after the shot.
It’s really no big deal. However, that shot can make the difference between something truly life threatening which may haunt you for months even if you survive it; and something that only takes a few days to get over. Familiarize your immune system with the infection, before you get infected.
Then, have some supplies on hand. If you can’t cook, have some canned soup around; soup helps keep you hydrated and nourished, and tends to taste pretty reasonable even in the midst of influenza. Have tissues, keep a water bottle around, and maybe some cough drops and cough suppressant.
Don’t lose faith, don’t lose vision.
That awesome idea you had, whether it’s for a game, or an animation, or anything else, will be a reality. If you’re committed, then you have nothing to worry about. The trick is to stay committed.
The truth is, we feel like hell after disease. We’re tired, lethargic, and injured. There’s a decent chance you won’t want to think about the math, or even the art, at all; and that’s OK, sometimes you’re just knowing yourself and you’re right. Maybe you do need time to recover.
However, you should never lose the dream that’s fueling this effort. It’s a question of persistence and gradual perturbation of the project, until your health is back and its familiarity has returned to you.
This week’s work has primarily been about getting in-game swarms to follow paths set by the scout bee. It’s going well. That said, I look back on it and feel like the past week could have been a couple of days.
Maybe. However, I was also sick over the past week, and that’s just a part of life. Over the coming handful of days, I intend to recover my momentum and get back into pounding this game out. I still intend to have a releasable preview available before the end of the year.
This is how I approach my return to that state of mind and body, and I hope it helps those among you who worry, perhaps rightly, that you too may come down with a malady. Keep on building!
In this particular instance, I’m dealing with a flaw on Disney+, which, as a die-hard Marvel fan, I have been a member at for some time now. I love those shows, want them to continue, and do not want to have to restart my entire desktop and lose all of my work just so I can watch them. (Not to mention the sheer span of classic animation available.) As of recently, a flaw in their page is preventing videos from loading for me, due to a dependence on specific user agent strings. It’s inconvenient, but no biggie; I just need to override a few things.
Whenever this happens, it’s important to remember that it wasn’t personal. Sometimes a design flaw, often involving the User Agent string from your browser, will keep a service like (but not limited to) Disney+ from functioning on your OS. This is absurd, as it’s a central point of web browser security to isolate everything from the OS. Otherwise it would be dangerous! However, some new designers just don’t know better, and they’ll test the User Agent string for the browser but keep the part about the OS.
I still insist that that operating system part never should have been there in the first place, but I’ll come back to that after my instructions.
Similar actions are probably possible in other browsers like Chrome, but I’ll be detailing Firefox as an example.
Setting Up a Custom Profile
The problem with changing a user agent string is that it will, generally, cause other sites to malfunction or perform improperly. (We’ll come back to how to set that string in the next section.) To do this, from Firefox, open about:profiles.
Here, you will be presented with existing profiles, which are collections of custom settings. You may already have a few available, but we’re creating a new one. Click on “Create A New Profile”.
Firefox has a wizard for this (whatever our opinion of wizards may be), but it is rather straightforward. You will need a memorable name (I recommend against using spaces or characters like “/”, “\”, “:”, or any other that may have specific meaning in your terminal). The root and local directories don’t need to be changed in most situations. Once you have it, switch to that profile by clicking on “Launch Profile in New Browser”.
It’s helpful to check and see that your typical browser profile is still the default. Click on the “Set as Default Profile” button if you have to.
Note that the two browser profiles effectively function as separate programs.
Setting a Custom User Agent String
Once you have your browser open, I suggest giving it a default color different from your typical browser, so you can tell them apart. The theme color of the site you’re trying to access is usually a good bet, it’s unwise to use this for any other site anyway.
We’ll now be adding a custom property to this profile. It is very important that you perform the next steps using your new profile, not your old one.
From the browser launched with your new profile, open about:config. This is how you’ll set configuration preferences for your profile. As of at least Firefox 106.0.2, you’ll have a search bar at the top of the screen.
This is a little more than a search bar, as it also allows you to set new, custom properties which can be accessed from scripts. Look up this property:
It isn’t typical to have it set, but if you’re using your new browser profile, there shouldn’t be any harm in overwriting it. Press the “+” button next to the string to do so, after verifying your spelling and ensuring you are adding precisely that property.
The user agent was once heavily used to determine the idiosyncrasies of different browsers, and to ensure that a page (along with its style sheets and scripts) loads properly on that browser. Sometimes it still is, though the browsers aren’t locked in the same kind of war they were in the 1990s.
Sometimes, someone will accidentally use this as a security check to ensure that a non-custom-browser is not being used, which is unwise on so many levels; but we’ve all been naive once and it’s no reason to give them flak, it’s just something we need to work around.
Modern user agent strings specify both the browser kernel and the operating system it’s built for, which really should never have entered the question, but I digress. The truth is, we didn’t know what we were doing back then, or where it would go. By overriding this string, you can fake your browser and OS. You can request pages that might typically only be visible to Chrome from Firefox, or see how a page would look on MacOS from Linux… given that your browser is capable of properly rendering the page.
Today, we’ll be using a specific custom string to convince the site at the other end that we’re running on Windows.
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.246
Once you have added this string to your profile, sites will be convinced that you are running your browser from the Windows operating system (or at least, using something based on Windows NT 10.0). It should work immediately for the profile you wrote it into.
This is of course likely to change with time; and more modern user agent strings can be found on a number of sites, for me most typically www.WhatIsMyBrowser.com. Feel free to experiment with them! Just keep it in that custom profile.
If your user agent string is correct, your browser will receive the exact same data as your chosen operating system. This is, with good reason for both sides, as much as the server ever knows about your specific computer. If it knew more, it could potentially execute arbitrary code that affects your host environment, and the internet as we know it, streaming media and all, would collapse on itself.
Creating a Custom Launcher
Now that we have this custom profile, we’re doing good; we can access our subscription service and everything. However, it would be nice to just double-click an icon on the desktop and immediately get to it, wouldn’t it?
I’ll walk you through this, again using my example of Disney+.
Create a launcher on your desktop, or if this option is unavailable to you, a shell script. Name it appropriately (I suggest the name of the service you’re accessing), and give it the following launch command:
firefox -p <your profile name> www.disneyplus.com
Where <your profile name> is the name of the new profile which you provided. firefox -p allows you to run Firefox with the profile name specified in the next argument. Here, I assume you want to start the launcher at Disney+, but you can replace that string with the address of the host you’re attempting to reach, or simply remove it and navigate manually. After all, this could (and will) happen to anyone.
If you would like a custom icon to your launcher, I suggest using a site like icons8.com and I personally prefer SVG over PNG; but that’s up to you.
Using that launcher will take you to your streaming service, ready to go!
Advice for Web Developers
It’s admittedly been a long time since I was a web developer, and the web has changed quite a bit since then. That said, user agent strings, back in the day, were used to work with incompatibilities between browsers, with the assumption that the operating system would play a part in that.
There were a few mistakes in that assumption. For starters, desktop environment ≠ operating system. On this machine alone I regularly switch between KDE Plasma (like living in a penthouse) and XFCE (like living economy). They both handle things entirely differently, but the user agent string will betray you, as in either case it will say “Linux”.
It’s just not enough to be helpful.
So, rather than depending on this poorly placed identifier, I suggest sanitizing it with a regular expression, and completely ignoring the operating system. It is the goal of any browser to behave the same on every OS that supports it, and there are a heck of a lot of them out there. Believe me, you would not like your job better if you had to build your pages and services to check for every single Windows Update or personalization setting that changed the desktop environment.
Let’s take a moment to remember that all policies exist for a reason, and it is important to ensure that whatever you are doing is not ultimately in violation of your contract. (Not that I intend to tell you how to live your life.) For legal reasons, it is important to consider that while a browser setting is within your control, this should not be used in any manner that would violate intellectual property rights.
That seems unlikely here, but it bears repeating that animators and film makers are still trying to make a living and deserve our respect.
I would like to make it clear that I do not endorse theft, piracy, unauthorized access of a streaming service, or anything in violation of contract or law; this is purely advice on customizing your browser settings to enjoy an optimal experience on your choice of operating system and desktop environment.
If you must, contact your streaming service to find out what their policy is on making such changes. Or, you know, don’t; but if there’s trouble of some kind, don’t come back to me with it. I’m an animator myself, after all; and it isn’t that expensive. Pay people for their hard work, they did it for you!
Sometimes it isn’t possible to get a service to work via user agent alone; perhaps some other detail is in the way. Maybe it depends on a plugin that has not been made, or does not have an equivalent, for your operating system—super rare these days, sure, but not impossible.
On occasions like this, I recommend one of two things: a compatibility layer, like WINE for Linux, BSD, Macintosh, and others for emulating Windows; or a virtual machine like VMWare or VirtualBox.
Hardware virtualization has progressed by leaps and bounds over the years. A CPU is now capable of furnishing two separate operating systems—effectively different machines—with 64-bit processing when virtualization is on. (For MSI motherboards like mine, that’s IOMMU and SVM enabled in the BIOS/UEFI settings; other motherboards have similar settings you’ll have to look up.)
This allows programs like VirtualBox to create virtual hard drive files, of dynamic size if you would like, and run 64-bit Windows 10/11 on it, or any other operating system you can get an install disk and a license for. This is a major component in server technology at this point, and we are a long way away from the days of simply treating a virtual machine as “one computer pretending to be another”.
I’m not going to delve into the details of these here, but in the instance of running Linux, you can install something like Chrome or Firefox for Windows with WINE and run the service through that; or you can install Windows 10/11 as a whole on a VM, and just watch it through Edge in that.
Hopefully this post has given you a number of inspirations on how to semi-permanently get any service available to you on your chosen work operating system.
I’ve got a prototype trailer up for Way of the Bee, and it’s been a ride making it.
From the terrain generator, to the art, to the custom AI, this has been all-me; which made it a work out. Of course, I have bottomless thanks to the people on the KDE team, the Blender team, the Unity developers, and many more for making the environment which I used to produce this, as while it’s my dream, it would be a much smaller one without them.
Additional shout outs to Perlin, Worley, Peachy, Musgrave, & Ebert for “Texturing and Modeling: A Procedural Approach”, which has opened up many new facets out of the world of procedural generation for me; Toady One for showing me that a dream can become an icon with enough effort and time; the Microsoft .NET team for starting with an overt Java knock-off but turning it into an incredibly respectable and efficient language to build this with, Ton Roosendaal for establishing a standard for 3D modeling that isn’t just influential, but open; and so many others.
Well, I’m getting back to it. It should be purchasable by the end of the year, as a minimum viable game—complete enough to make it through, and preferably enjoyable. Then, we start dressing it up!
After considerable deliberation, arguing, researching, pounding out, arguing, bending, prying, polishing, and welding, I have a navmesh-free situation-aware basic AI going for my bird predators. I’m finally satisfied with it.
Birds follow a relatively smooth path to their opponent, avoid obstacles like the terrain, do not turn on a dime (unless I tell them they can), and circle around gradually to face targets which aren’t within an angle window in front of them. Physics and animation are whole different modules from path finding, so the animation should always match the behavior.
I remind you that in this game, you are the 🐝. The birds are not your friends… But, having a player-vs-player mode in which one player does control a bird sounds like a lot of fun. So I may come back to that!
Most navigation uses a clever concept called a navigation mesh (or in some cases, a navigation volume); which divides traversable terrain into a set of linked convex polytopes. Each pair of connected polytopes can be walked across, and as long as an agent is bound to it, they will behave quite normally.
This is usually great, and is supported by freakin’ everything! However, it isn’t always enough. As an example, for my bird here, we need to worry about altitude, too. One way to do that would be a navigation volume (using polygons instead of edges for meeting points between convex polyhedra), but I would be building that from scratch, it would take a while, and I want this playable and enjoyable before the end of December. So instead, I fell back on a few other navigation techniques and tuned the amount of error I’m comfortable with it having.
Most of this stuff actually comes from robotics, where collision detection (or hopefully anticipation!) is really essential. You can theoretically make a game that doesn’t need collisions, but nobody wants to drive their robot into a wall. The bird’s doing pretty well in that department!
A lot of people are ready to talk about Bézier curves, as they’re a pretty fundamental part of solid geometry. Let’s be honest, if you’ve ever used so much as MS Paint and wanted a smooth curve, you’ve likely been introduced to these.
I’m working with them right now in Way of the Bee, as a matter of fact, in an effort to make by bird flights look a little more realistic and less obviously bound to 3D vectors. Grabbing a little bit of back knowledge—since it can be mildly dangerous to assume that you’re too much of an expert to learn—I noticed a lot of mysticism about them.
You have four points (at least for a cubic Bézier), and it forms a curve, efficiently, between the first point and the last, but what the heck are these other two points? Shifting them adjusts the curve, but how so? Can you make a circle with it?
I’m going to address all of this here; but let’s start with a little history.
A Little History
Béziers were around before Pierre Bézier, but he did popularize them. They’re what’s called Bernstein polynomials, which have been well documented since the early twentieth century. What the heck is a Bernstein polynomial? They’re linear combinations—for Unity users, LERPs—of basis polynomials.
Technically, a solid year before Bézier documented their use in computer graphics, they were used by another mathematician, Paul de Casteljau. (de Casetjau’s algorithm is still frequently referred to when arbitrarily splitting a Bézier curve.) de Casteljau established IP protections in France, but not internationally, as Bézier unknowingly did a year later; there was never any evidence of hostility between the two.
Bézier himself was working for Renault as an automotive engineer, and needed to find a way to accurately describe the surface of a car without reducing it to a flat polyhedron. This is exceptionally important for automotive building, as flat planes can introduce terrible aerodynamic resistances.
He was no stranger to mathematics himself and was an absolutely brilliant solid geometry engineer, but he noticed that Bernstein polynomials offered a way to arbitrarily specify a curve at any point without reducing down to jagged polygons. He had a winner, he shared it, and it’s used in virtually all forms of industrial design, from blenders to airplanes, to this day.
If it’s been a while since you’ve been in a math class, a polynomial is an equation which consists of several terms added together.
Linear interpolation, commonly Lerping, is interpolating in a linear way between one vector and another.
By linear I technically mean “without any bumps, curve, or mathematical shenanigans” in the curve. And yes, if you look at it, you’ll note that between zero and one it covers a line between vector one and vector two; but it doesn’t have to.
I would like you to consider that vectors are also effectively polynomials. It’s just a question of expression. If we have the point <4, 5, 7>, we can just as easily express it as:
So, what does it mean to create a Bernstein polynomial using these vectors as a basis? If we just had the first point and the last, it would again be a line; but if we have a curve then we’re departing from lines in a specific way—we aren’t just interpolating between two points, we’re also interpolating between two directions. The control points define the vectors, and magnitudes, of these directions.
This is why moving either control point will not change the initial or terminal points of the curve, but will totally change how they get there. We are interpolating between both P1 and P4, and the directions of P1P2 and P3P4.
The best way to understand any algorithm used in construction is to think about its constraints! So, to reiterate, we want it at a certain place, facing a certain direction, at the beginning; at the end, we want it at a different place, facing a different direction.
The first step in evaluating a Bézier curve is to find the line between P1 and P2, and the line between P3 and P4, as equations of t. This is a basic linear interpolation. We have two lines, now, which carry our initial and final directions.
We want to shift between the two, so we’ll be interpolating between them, too. How do you do that? Well, I’m going to introduce yet another line, but this one is a bit more flexible. As we increase t from zero to one, we’ll be describing points on P1P2 and on P4P3, let’s call them Q1 and Q2.
We’ll define L as a line between Q1 and Q2.
As we watch L evolve with the increase of t, we might notice something from this graph. It intersects the Bézier curve perfectly, begins at P1 facing along P1P2, and end at P4 facing along P4P3. We’ll call that interpolation, along the shifting line L, by value t, the point QL.
That’s exactly how it’s done. QL perfectly traces out the constraints of the Bézier curve.
In the instance of a non-cubic but quadratic Bézier curve, where there are only three points, you simply use P1P2 as your first vector, and P2P3 as your second.
Like all Bernstein polynomials, Bézier curves lend themselves to tesselation, or division into simpler subsets, very well. However, they don’t have to do so destructively; the information of the curve at each point is still well defined.
So Why Can’t it be a Proper Circle?
I’m going to rough-and-tumble this one.
That can be proved quickly, with just a little math—though you should note that Béziers can get damned close to a circle. The equation of a circle is well known, but let’s start with that.
Here, x and y are coordinates, and r is the radius of the circle. In order to be identical to a circle, a Bézier curve would have to be reducible, for some parameters, to this. So, for the first time in this article, let’s consider the completely-unfolded double-linear-interpolation of the cubic Bézier curve.
As you can see, it’s called cubic because it has a cube in it. The equation of a circle, along with any of its forms, does not have a cube in it. To some this will be obvious, but we don’t all speak math that well; but there are no parameters that reduce any part of this to a consistent square. You can get very close, but never identical; they two equations occupy different numerical spaces.
To push this further, quadratic Bézier curves describe parabolas, not circles. Cubics describe parabolas between parabolas, in a sense. They are incredibly versatile, and they are (at least for cubics and quadratics) computationally efficient, but they are not universal.
I’ve turned down the number of grass blades from 16-per-unit to 2-per-unit, for the sake of everyone’s graphics cards and frame rate; it actually looks a little bit better to me that way.
As you can see, the hive develops as nectar is delivered to it right now; that won’t always be the case. Way of the Bee is largely about economics; nectar is converted into honey for consumption and storage. It drives the growth of larvae, which become bees. The bee count drives the creation of wax and building of the colony; while different defenses will require various resources to perform. Cooking a hornet to death, as an example, will tap into honey storage because of the sheer amount of calories necessary.
It’s going to be an interesting fusion between an adventure game and a real-time-strategy game.
We’re all loving that satisfying crunch toward the end, which actually came from a vegetable. In any case it’s definitely not a sound you would want your body to be making…
The current goal is to reach a minimum viable game, which is enjoyable to play. Then, we’ll have it available for early access on Itch, and later on Steam. Later targets could include any number of platforms, but only after the game has reached a more mature state.
OK, let’s get one thing out of the way. Multimedia and game development is hard work. It’s fulfilling work, it’s often (but not always) enjoyable work, but it is an absolute mountain of a task. It often requires extreme focus, thorough organization, and mindful physical and mental health.
That said, the recent supreme court rulings, including (but not limited to) the overturn of the half-century-settled Roe vs. Wade, have been an enormous stressor. I’m fortunate, in that I live in New Mexico where actions are regularly taken by our government to protect citizens from these rulings, particularly the out-of-the-blue and in this person’s opinion, unconstitutional, ones; but it does little for the stress.
Texans are my countrymen, in that we are brothers. Texan women are also my countrymen, and they are my sisters. Florida, West Virginia, Missouri, Wyoming… some of these places, I once lived in myself. There’s a divisiveness in my country right now, with which it has become too easy to point fingers at the other side of the line and claim that, on account of some mistake legislatively made, all of the people living there are the same, or are even in support of it (or even in majority support of it). That’s just not true.
It isn’t that I’m not loaded down with anger right now, I am. A very house of government is beyond a legitimacy crisis. The right in my nation’s politics has become the extreme-far-right, siding with unity over reasonability as though, if everyone sinks together, there will be some merit over changing sides and fixing the leak. For nearly ten years, the UN has classified lack of access to abortion care as torture. It reiterated on that recently, after Roe vs. Wade was overturned. It’s right, childbirth is both painful and dangerous, and should be forced on no one.
There’s been another case recently where the court stripped the EPA, and sibling agencies such as OSHA, the FDA, and even the FBI, of their ability to effectively go about their core purpose. This has lead us into a concerning potential treaty violation with China, among others, and I can’t help but feel that the aim of this court has been to break down all forms of federal control.
The six justices who consistently vote conservative on these issues were all put in place, for an exasperating lifetime position, by presidents who lost the popular vote. Three of them were from a president who was an insurrectionist after losing the election. One of them is married to someone who was actively involved at the January 6th attempted insurrection in 2021.
I wish I didn’t have to write a piece like this, but it is not just affecting my workflow, it’s affecting the software and game industry. Texas has been a game development hub for years, and many of us are women. Many of us are minorities, or homosexual. We’re in this whether we like it or not.
In the future, right now will be classified, in retrospect, as the second American civil war. But war has changed; during the first Civil War we didn’t have cruise missiles or a strong navy. The air force wasn’t even a concept. The only automatic weapons we had were stationary Gatling guns, which weren’t in wide deployment. So we fought, and we beat each other in the mud and the blood until one side had to step down. This was only a hundred and fifty years ago.
This claim is not without precedent. In the winter of 1861, after Confederate soldiers had bombarded Fort Sumter and shots had already been fired, even the best and brightest in the country refused to believe what was happening. In fact, South Carolina senator James Chestnut—one of the ultimate instigators—promised to drink all of the blood spilled in the conflict, popular belief at the time maintaining that it would be “not a thimble”. No one wanted a war, so no one wanted to believe that it was already happening.
I take sole satisfaction in knowing that we can’t regress as much as this Supreme Court would have us regress, as that ship has indeed already sunk. You cannot survive in the twenty first century with nineteenth century ideals and methods.
I will continue to write my algorithms, my books, and my games and programs; I will continue to publish. I have to. Even in Ukraine, development of S.T.A.L.K.E.R. 2 is continuing right now, in spite of the fact that most of the developers are firing actual assault rifles at intruding Russian forces during the day. Why? Because life is not a singular task, and this presents a sense of normalcy to people who need it.
Summarily, this is a message of love and solidarity. It’s a reminder that this is neither the first nor the last storm we will weather, and we have overcome the previous ones. It’s a reminder that the best and brightest in this country know that this outlandish violation of basic human rights, even if it should stand for a while, will perish in the end under the steel-toed boot of the righteous.
For those who happen to be on the other side of the argument, know that I will forgive you, in spite of everything; but first, we both need you to step down. We’re going to be mired down for the next decade, sure; but one way or another, this won’t work in the end.
To provide background, I’ve been working on a project lately, involving generating random terrain in Unity based on a combination of Worley noises and Voronoi noises (cell noise), with a few matrix-based operations in them. This is a logically straightforward task (given that you understand how your noises work!) but ultimately very computationally expensive.
If I was using something like my-main-man C, I wouldn’t be so concerned; but Unity requires C#, which is both a blessing and a curse. C# is managed code, which makes it much harder to slip and have a memory leak. It’s also a mostly1 well-maintained language and is relatively easy to learn, without being abstracted to a crippling point.
That is, unless you’re doing something very close to the hardware, or very computationally intensive. The truth is, managing code comes with a price. If I were using C or Assembly, my code would be moving (literally) a hundred times faster (in most cases). Breakneck speeds through a lattice of severe safety pitfalls. That’s not usually a big deal, as modern processors are blisteringly fast and programming is not an easy skill—sometimes it’s more economical to have the job done fast than it is to have the completed job run in a fraction of the time, especially if you’re only saving milliseconds.
It’s my personal opinion that object-oriented programming (OOP), for all its legitimate benefits elsewhere, has contributed exceedingly little to the multimedia (and game development) scene. It has a tendency to turn people into champion office-software programmers, when loop-driven programs like games follow a completely different set of rules. Object pooling, as an example, and data-driven code, become foreign concepts, when they can make all of the difference in the world!
So, enter ECS. ECS stands for the Entity Component System, which is, I think, a very vague and obfuscated name for it. It involves data-driven development instead of object-oriented programming. Enter the Unity Jobs system, which we’ll be discussing here. Also enter the Burst Compiler, an equally poorly (but at least fashionably) named extension to Unity. All of them get around these obstructions in some way, without having to part from the comfort of C#—at least, no more than is strictly necessary.
Jobs bring in a specific new feature. Unity is a single-core program, in other words, it uses only a single processor thread. This isn’t unique to it, the vast majority of programs are single-core. You may be working with a 4.0 GHz Ryzen processor with sixteen threads, but an individual program usually only uses one. Exceptions typically include web browsers—Chrome tries to keep a different thread for each tab, as I understand it—and programs which are attempting to perform many tasks simultaneously, like some renderers.
The drawback of this is that, in classic Unity, every line of code you type ends up being queued on the same thread, henceforth the “main thread”. You can’t run more than one instruction at once, you’re just running them blisteringly fast. Since we’re usually still going around 120 frames per second, that isn’t typically a problem, until you have an extreme operation that does require more time than the main thread can spare.
This is where the job system enters. Jobs, unlike their ancestral coroutines, can run on their own thread. When accelerated with the Burst compiler, they can run exceedingly fast. (Remember that hundred-fold factor I was talking about? Here it is.) Additionally, unlike when using C, you can readily compile to any platform supported by Unity with minimal work.
So the naive thought from here is, why don’t we use it on everything? Well, therein lies the catch—only certain data types can be used, specifically blittable data types. Blitting is short for block transferring (just as bit was short for binary digit—programmers love these things). It means that a streamed chunk of data of this type can be copied, en mass, from lower memory to processor caches. Since, compared to the speed of your processor, your memory is wrapped in architectural red tape and moving at a snail’s pace, this is critical stuff.
Non-blittable types are typically pointers to locations in memory where the actual data is. Sometimes, they’re pointers to lists of pointers. There’s a lot of he-said-she-said reference tracing when using them, but they open up a lot of new possibilities like OOP in their use; and I’m not bad-mouthing OOP, I’m just saying where it’s out of place. If you only need a few of them, then that’s not a problem. If you need a bunch of them, even hundreds or thousands or even millions, then it’s going to cripple your operation.
Blittable types are, in the context of C#, defined as types that have the same representation in managed and unmanaged memory. The program’s marshaller, or the module that converts types between managed and unmanaged memory, does not touch them. That’s about as close to the hardware as I’ve seen anyone get with C#. It also means that they can be block-transferred to a new thread.
Threads, by design, cannot know what each other are doing. This is part of where the speed-up from using multiple threads comes from. Because of this, following reference (non-blittable) types becomes very difficult. By ensuring that every type is a blittable type, and all are present on the thread before it runs, we avoid this; in fact it’s required for many parts of the Unity Job System and Burst Compiler.
Additionally, there are functions which cannot be called from these jobs, some of which are surprisingly useful to have around. While many, such as Monobehaviour, are unsurprising and not that impeding, we unfortunately also lose access to virtually all parts of the UnityEngine namespace, including Random.
In my case, I was attempting to seed the random number generator with a coordinate location’s hash value, and then acquire feature points from it. Since that is no longer possible, we can follow the easiest and most controllable solution and simply roll our own LCG (linear congruential generator), a type of random number generator!
Inside your job—and that’s important, it needs to be local to it—feel free to insert this function and definition.
This is technically a word-for-world (aside from C# translation) copy of the BSD random number generator, which is an LCG where multiplier a = 1103515245, increment c = 12345, and modulus m = 231. It’s converted to a floating-point (Single) and then divided by its modulus so we can get a value between zero and one, which is what I needed. It’s a BSD license and is very well known.
Let’s break down that return line for clarity, as it’s a little complicated.
We’ll start with LcgSeed = . That is an assignment, but the assignment operator has a useful side effect of returning the assigned value. We need it, because we want LcgSeed to update with each random value, to prevent repeats. What it’s set to be equal to, (LcgSeed * 1103515245 + 12345) & RAND_MAX, is the basic LCG equation with parameters from BSD. It’s finally divided by its maximum possible value in an effort to limit it to, for me, the unit cube; but there are many occasions when a random value between zero and unity is of value!
Replace your typical InitState call, which is part of the UnityEngine package and inaccessible from the job, with a simple assignment to LcgSeed. Since I’m generating vectors which ideally need to be identical for each call at adjacent locations, I just use the Vector3’s hash value; but that’s much more complicated than the rest of this and will likely be addressed in a book, maybe cursorily on the website.
Remember that you will need to initialize LcgSeed before you call it, as another side effect of IJob, on account of it being a struct instead of a class, is that it can’t have initializers in field declarations. However, since in many cases we don’t want a truly random value so much as a one-way hash from a location, this is almost an advantage.
Replace each Job-internal call to Random.value with rand(), or your variation on it, and you will do just fine for both performance and noise generation on an IJob.
1There’s some concern about dirtying the language by introducing SQL syntax, which also bothers me a bit; but this is optional and relatively small.