This time in motion.

blake kathryn
occasionally subtle

Product Placement
I'd rather be in outer space 🛸
Three Goblin Art

Discoholic 🪩

if i look back, i am lost
Acquired Stardust

Andulka

titsay
Cosimo Galluzzi
art blog(derogatory)

No title available
cherry valley forever

pixel skylines
Jules of Nature
Alisa U Zemlji Chuda
No title available

Origami Around
wallacepolsom
seen from United States
seen from United States

seen from Germany
seen from United States

seen from Germany

seen from South Korea
seen from Türkiye

seen from United States

seen from Australia
seen from Brazil
seen from United States
seen from United States

seen from Malaysia
seen from China

seen from Germany
seen from United States

seen from United States

seen from Türkiye

seen from Türkiye

seen from Netherlands
@grozzler
This time in motion.
Recording some combat footage this afternoon. Here’s a selection of shots that I think turned out quite nice.
On this episode of me procrastinating on the work I’m supposed to be doing, I added a texture to the missile trail. I’m really happy with how this turned out. It’s exactly the look I aiming for.
I’m starting to feel like I need a jungle map. Maybe some napalm too.
QueryableMonoBehaviour : SQL-like syntax for Unity behaviours
Ever wished you could query MonoBehaviours like they were a database to easily filter and sort through?
Unity, fetch me a list of all goblin monsters in this hero's attack range ordered by lowest health.
Using this ListQuery class from an earlier post, we can build a base MonoBehaviour class that enables exactly that.
using System.Collections.Generic; using UnityEngine; public abstract class QueryableMonoBehaviour<T> : MonoBehaviour where T : QueryableMonoBehaviour<T> { private static List<T> _instances = new List<T>(); // bookkeeping: protected virtual void Awake() { _instances.Add((T)this); } protected virtual void OnEnable() { if (_instances.Contains((T)this) == false) _instances.Add((T)this); } protected virtual void OnDisable() { _instances.Remove((T)this); } protected virtual void OnDestroy() { _instances.Remove((T)this); } // query begin: public static IListQueryAfterSelect<T> Select() { return ListQuery<T>.Create(_instances).Select(); } }
The class itself is simple. It maintains an internal list of objects as they're created/enabled and destroyed/disabled. The magic part is the static Select() function at the bottom that begins our ListQuery. Again, you need the ListQuery class from an earlier post to use this. Inherit your own class from it class Target : QueryableMonoBehaviour<Target> and you're good to go.
If for no other reason, use it to replace all of your calls to GameObject.FindObjectsOfType<T>.
Yikes. Those numbers (especially garbage) are inflated because I had 2000 objects in my list, but you get the idea. Both functions are a single line of code too:
private void GameObjectFindTargets() { Target[] targets = GameObject.FindObjectsOfType<Target>(); } private void QueryFindTargets() { List<Target> targets = Target.Select().All(); }
But a QueryableMonoBehaviour allows you to do so much more. We'll start simple. Let's find all monsters that are goblins:
List<Monster> goblins = Monster.Select() .Where(monster => monster.type == Monster.Type.Goblin) .All();
If you're confused about the => bit, it's called the lambda operator and it creates lambda expressions. You'll want to read up on them before continuing if you're unfamiliar.
We can of course apply more than one conditional to our where clause. The player just used their super cool mineral deposit scanner, and now you want to highlight all the resources in the area that are harvestable:
List<Resource> resources = Resource.Select() .Where(resource => resource.IsHarvestable() && Vector3.Distance(resource.transform.position, player.transform.position) <= scannerRange) .All(); // highlight them
The OrderBy clause allows us to add sorting functionality to the list of results. Here we prepare the UI before a multiplayer match starts by collecting our teammates and ordering them by name:
List<Hero> heroes = Hero.Select() .Where(hero => hero.team == myTeam) .OrderBy((hero1, hero2) => hero1.playerName.CompareTo(hero2.playerName)) .All();
OrderBy takes a comparison delegate rather than a lambda expression. All basic types include a CompareTo method, but you can also write your own comparison if you need to order by custom objects or multiple fields.
Here's a cheeky way to abuse OrderBy to find specific objects, like the monster AI finding the weakest target in range:
Hero weakestTarget = Hero.Select() .Where(hero => Vector3.Distance(hero.transform.position, this.transform.position) <= attackRange) .OrderBy((hero1, hero2) => hero1.health.CompareTo(hero2.health)) .One();
The catch is that OrderBy sorts the entire collection, which is less than ideal if you're just trying to pick a single object, but for small datasets like a list of heroes it should be fine.
Every query will generate ~32 bytes of garbage regardless of what it finds, so while I wouldn't recommend you use it every single frame, it's not a big deal if you do.
Hope you like it.
ListQuery : SQL-like syntax for List<T>
Here's a handy utility class that enables querying a generic List<T> with SQL-like syntax without affecting the original. It's similar to Linq (which is a fantastic tool), but has a more memory efficient OrderBy clause, as it does the sorting in place. Other functions offer comparable or improved performance.
using System; using System.Collections.Generic; public class ListQuery<T> : IListQuerySelect<T>, IListQueryWhere<T>, IListQueryOrderBy<T>, IListQueryResult<T>, IListQueryAfterSelect<T>, IListQueryAfterWhere<T> { private List<T> _original; private List<T> _results; private ListQuery(List<T> list) { if (list == null) throw new ArgumentException("list"); _original = list; } public IListQueryAfterSelect<T> Select() { return this; } public IListQueryAfterWhere<T> Where(Func<T, bool> predicate) { if (predicate == null) throw new ArgumentNullException("predicate"); LinkedList<T> matches = new LinkedList<T>(); for (int i = 0; i < _original.Count; ++i) { if (predicate(_original[i]) == true) matches.AddLast(_original[i]); } _results = new List<T>(matches); return this; } public IListQueryResult<T> OrderBy(Comparison<T> comparison) { if (comparison == null) throw new ArgumentNullException("comparison"); if (_results == null) _results = new List<T>(_original); _results.Sort(comparison); return this; } public List<T> All() { if (_results == null) _results = new List<T>(_original); return _results; } public T One() { if (_results != null) return (_results.Count > 0) ? _results[0] : default(T); else if (_original.Count > 0) return _original[0]; else return default(T); } public T Random() { if (_results != null) return (_results.Count > 0) ? _results[UnityEngine.Random.Range(0, _results.Count)] : default(T); else if (_original.Count > 0) return _original[UnityEngine.Random.Range(0, _original.Count)]; else return default(T); } public static IListQuerySelect<T> Create(List<T> list) { return new ListQuery<T>(list); } } // fluent interface definitions: public interface IListQuerySelect<T> { IListQueryAfterSelect<T> Select(); } public interface IListQueryWhere<T> { IListQueryAfterWhere<T> Where(Func<T, bool> predicate); } public interface IListQueryOrderBy<T> : IListQueryResult<T> { IListQueryResult<T> OrderBy(Comparison<T> comparison); } public interface IListQueryResult<T> { List<T> All(); T One(); T Random(); } public interface IListQueryAfterSelect<T> : IListQueryWhere<T>, IListQueryOrderBy<T>, IListQueryResult<T> { } public interface IListQueryAfterWhere<T> : IListQueryOrderBy<T>, IListQueryResult<T> { }
Here are some examples of how you use it:
List<Target> targets = // populate the list however // find targets in a certain range: List<Target> closeTargets = ListQuery<Target>.Create(targets) .Select() .Where(t => (t.transform.position - position).sqrMagnitude <= range * range) .All(); // find a random wounded target: Target randomWounded = ListQuery<Target>.Create(targets) .Select() .Where(t => t.health <= 10) .Random(); // order targets by their names for the UI or something: List<Target> sortedTargets = ListQuery<Target>.Create(targets) .Select() .OrderBy((a, b) => (a.name.CompareTo(b.name))) .All();
If you're unfamiliar with lambda expressions, you can find more information here.
Keep in mind those numbers are overly inflated from having thousands of objects in the demo list. Realistic use-cases of ~200 items are unnoticeable in the profiler.
I’ll show off how I use it inside Unity next tutorial.
Within reach of finalizing the sector lightning and environments. It’s a touch on the soft side, but I think it offers a lot of depth to the overall scene.
Captain Sydney Miller.
Although lacking raw experience, her exemplary leadership and navigation skills earned her command of the Dauntless after graduating from the naval academy.
Some recent work from the character generator for Remnant.
Spent the entire day working on the new lighting and post processing stacks. Hopefully it’s obvious which is old and which is new or I’ve made a terrible mistake. The gif should help show the subtlety of the new volumetric lighting system.
I think it looks a lot more professional now.
Final pass on the new projectile system. 50,000+ active projectiles with full collision and impact fx.
I’m playing around with the idea of dynamically deconstructing ship modules into procedurally generated debris when they’re destroyed. Here are a few early tests at different contact angles using the mesh’s voxelized point data as the debris -- the real system would generate more interesting debris chunks based on the breaking point.
I’m very pleased with the new projectile management and instancing system I’ve been working on. Here’s an example of 150 ships firing ~6,000 projectiles a second, all with real-time collision and impact visual fx.
Lately I’ve been tackling projectile rendering and management. Here’s a torture test of my new system with 200,000 projectiles. Solid 60+ FPS while drawing over 30 million tris.
I’ve been limiting the count of active projectiles to around 200 for performance reasons, but with this new system I’ll be able to expand that dramatically. The battles in Remnant are going to be quite the light show now.
Very happy with the results!
Working on the Agent portraits for Remnant.
Tiny Combat Redux demo has been released!
Tiny Combat Redux is an almost complete rebuild from the ground up of Tiny Combat. The original game started as one thing, but as I built it out, it became something completely different. Redux is the game that Tiny Combat, but built with that purpose, and those game mechanics, in mind.
This very, very early demo includes 4 planes and a small arena to dogfight enemies of different difficulty levels.
Check it out on Itch.io
Here’s the new missile barrage. Almost all of the weapons and visual FX have been updated at this point. I’ll make a battle montage in the new few days after some final tweaks.
Had some fun recreating A New Hope’s opening sequence in Remnant for Star Wars Day!
New explosion fx for Remnant.