I'm Chris. I'm making games and I'm writing tools for different things. Such as games ;)

RSS My Blogs  (0 - 10 of 43)

Event and Unity

damagefilter Blog

Events!

Events, events. Unity and events. It's a gravely undervalued tool in your toolbox. There are many ways to go about events. Today I've read an article about it in the context of notGDC and I felt inspired to expand on the topic. Lets look at a very powerful and flexible approach to events. To understand this, you need to have a good understanding of generic types. That is not essential to using it though.

Lets see how one might approach events, given that we're looking for some sort of central event pumping facility because spreading this all over the project would be a maintenance nightmare. You'd have a place that can raise events. A dispatcher of sorts. You'd have another place that uses the dispatcher to raise events and yet another that will listen to events. So how about something like this:

// The thing which knows how to raise events.
class EventDispatcher {
	// we need the delegate definition and ...
	public delegate void OnEnemyDeath(Enemy killedEnemy);
	// ... we need to use it to subscribe listeners to it
	public static OnEnemyDeath EnemyDeathCallback;
	
	// and another pair
	public delegate void OnEnemySpawn(Enemy spawningEnemy);
	public static OnEnemySpawn EnemySpawnCallback;
	// and so forth
}

// The spawner will raise the respawn event
class EnemySpawner : MonoBehaviour {
	public int maxEnemies;
	public int currentEnemies;
	
	private void Update() {
		if (currentEnemies < maxEnemies) {
			// Do the spawning logic and then:
			EventDispatcher.EnemySpawnCallback(theFreshspawnedEnemy);
		}
	}
}

// This will listen to that event
class RandomStatisticsCollector : MonoBehaviour {
	private int monstersSpawnedTotal;
	private int witchesSpawnedTotal;
	
	private void Awake() {
		// register for the spawn event. we wanna count some stuff!
		EventDispatcher.EnemySpawnCallback += EnemySpawned;
	}
	
	private void OnDestroy() {
		// don't forget to remove listener when we're gone or else!
		EventDispatcher.EnemySpawnCallback -= EnemySpawned;
	}
	
	// here's the listener that acts on the event that was raised by the EnemySpawner
	private void EnemySpawned(Enemy e) {
		// I'm making things up here, but something like that maybe :)
		if (e.Type == EnemyType.Monster) {
			monstersSpawnedTotal++;
		}
		else {
			witchesSpawnedTotal++;
		}
	}
}

Okay, something like that. Now, there's nothing wrong with that at first glance but from the maintenance and flexibility standpoint, this is very rigid. That EventDispatcher could grow really quickly and you'll be having a whole lot of delegates which you will need to keep track of. What if the requirements change and a signature for one or more of them must be changed? Welp. That can possibly amount to a whole lot of work. The compiler will tell you everything you need to know of course but it would be nice if such a breakage could be avoided alltogether. You also need to know about the EventDispatcher instance in the places you raise events. Imagine that could be avoided. If you could minimise the spots where the EventDispatcher is actually used. Worth it, I would say.

Well then. Here comes my approach. If you don't feel like reading, here's the source with examples.

At first we need to tuck away round about 119 lines of boilerplate code that's gonna be the EventDispatcher. I put a version without comments here, it's shorter. For the actual code, refer to the link to the source, above :)

// Defines the callback for events
public delegate void Callback<in T>(T hook);

internal interface IEventContainer {
	void Call(IEvent e);
	void Add(Delegate d);
	void Remove(Delegate d);
}

internal class EventContainer<T> : IEventContainer where T : IEvent {
	private Callback<T> events;
	public void Call(IEvent e) {
		if (events != null) {
			events((T)e);
		}
	}

	public void Add(Delegate d) {
		events += d as Callback<T>;
	}

	public void Remove(Delegate d) {
		events -= d as Callback<T>;
	}
}

public class EventDispatcher {
	private static EventDispatcher instance;

	public static EventDispatcher Instance {
		get {
			if (instance == null) {
				instance = new EventDispatcher();
			}
			return instance;
		}
	}

	private Dictionary<Type, IEventContainer> registrants;

	private EventDispatcher() {
		registrants = new Dictionary<Type, IEventContainer>();
	}


	public void Register<T>(Callback<T> handler) where T : IEvent {
		var paramType = typeof(T);

		if (!registrants.ContainsKey(paramType)) {
			registrants.Add(paramType, new EventContainer<T>());
		}
		var handles = registrants[paramType];
		handles.Add(handler);
	}

	public void Unregister<T>(Callback<T> handler) where T : IEvent {
		var paramType = typeof(T);

		if (!registrants.ContainsKey(paramType)) {
			return;
		}
		var handlers = registrants[paramType];
		handlers.Remove(handler);
	}

	public void Call<T>(IEvent e) where T : IEvent {
		if (registrants.TryGetValue(typeof(T), out var d)) {
			d.Call(e);
		}
	}
}

Holy skateboarding hamster! There's some generic types going on in there. And a lot of that has a so called type constraint on it (where T : IEvent). We limit the type T so that it must be drived from an interface IEvent. IEvent is but an empty interface to get C# to comply with my weird mind. One could leave it out and use plain old object type instead. But I like the readability and that wee bit extra type safety so I cannot just throw random stuff into the EventDispatcher.

Lets continue with the Callback delegate. That is the one and only delegate we will ever need. The generic parameter will determine which kind of argument the concrete callback receives. This is then tucked away in the EventContainer - what I'm doing here is I'm basically passing on the generic type. Packing it up like that will save us some typecasting later on. The EventContainer uses the generic type to cast the event to the correct type and pass it into the delegate. If that's all too much weirdness for you, do not worry. You can understand this class as a big blackbox - it's not gonna break. You don't need to look at it all that often, if at all. But I shall explain further.

Going forward, lets see how events are registered with that one and only single event delegate type.

public void Register<T>(Callback<T> handler) where T : IEvent {
	var paramType = typeof(T);

	if (!registrants.ContainsKey(paramType)) {
		registrants.Add(paramType, new EventContainer<T>());
	}
	var handles = registrants[paramType];
	handles.Add(handler);
}

You notice there's a generic type again. We use it to identify the event type, key it into a dictionary and assign the handler to the corresponding EventContainer. That's basically it. Not a whole lot of magic going on here. Unregistering works about the same way. If you do not see the sense in all of this yet, do not worry. We'll get to it. But basically, what we have at this stage is a dictionary which we can query. We can say: I want the delegate that knows how to handle my "EnemySpawnEvent". And we'll get it.

Lets look at the Call method. That is the integral part. It is where the events are raised, where delegates are invoked.

public void Call<T>(IEvent e) where T : IEvent {
	if (registrants.TryGetValue(typeof(T), out var d)) {
		d.Call(e);
	}
}

It doesn't get any shorter than that. It's querying the dictionary for the type T and gets the EventContainer for it. Then passes the IEvent into the EventContainer. That, on the other hand, will do the calling. Here:

public void Call(IEvent e) {
	if (events != null) {
		events((T)e);
	}
}

The EventContainer, as you know, has a generic parameter, it fits with the definition of the IEvent that was passed in. We made sure of that when we called Register(). As such we can savely cast e to the specified target type T (feeling like explaining a mathematical formula right now).

You'll notive that we use the IEvent object for two things. For one, we use its type to identify which delegate, which EventContainer, we want to use. And for another, we pass it to the listeners as argument. That way, should we ever need to change what kind of data is passed on to listeners, we change the IEvent implementation but we do not have to touch the listeners. At all. Additionally, it can act as state. As it's a reference type, changes made to it by one listener will be seen by the next. Due to the nature of delegates however, the order of the invocation list is not guaranteed.

So that's the EventDispatcher done. Lots of boilerplate but from here on out, we can forget about it all. Lets do some events!

// That's actually all IEvent is. But hang on!
public interface IEvent {}

// Whot
public abstract class Event<T> : IEvent where T : IEvent {
	
	public void Call() {
		EventDispatcher.Instance.Call<T>(this);
	}

	public static void Register(Callback<T> handler) {
		EventDispatcher.Instance.Register(handler);
	}

	public static void Unregister(Callback<T> handler) {
		EventDispatcher.Instance.Unregister(handler);
	}
}

Okay here it gets juicy. We now, finally, have defined the IEvent interface. As promised. It's just an empty interface for some extra typesafety measures. So that you can't just throw anything in as event type. You probably could if you wanted to though. Interesting point is the abstract Event. I've added a type constraint to T here that states, you can pass in any T that derives from IEvent. The same rule applies everywhere in the EventDispatcher.

With the generic parameters again. Friggin hell! But bear with me, we tuck them away soon. Basically, this abstract Event is a comfort layer between you and the EventDispatcher. You see it has 2 static methods that can handle registering and unregistering at the dispatcher. Because of the generics, we can have this in here and it will work on all derived types automatically. Late static binding ftw!

I said we want to do some events. Lets do them! I'll pick the EnemySpawn example from earlier!

// Here, finally, the generic type T becomes concrete!
public class EnemySpawnEvent : Event<EnemySpawnEvent> {
	public Enemy SpawnedEnemy {
		get;
		set;
	}
}

There's the event. For our purposes that is all we need for it. Yours can have anything in them you wish to transport along. You see, the T parameter became concrete now. Looking back, this information is passed down right to the event registry to identify callbacks that use it. You can define this event anywhere you like. Lets wrap it up and put this to use by the first example code, way above.

// The spawner will rise the respawn event
class EnemySpawner : MonoBehaviour {
	public int maxEnemies;
	public int currentEnemies;
	private readonly EnemySpawnEvent spawnEvent = new EnemySpawnEvent();
	
	private void Update() {
		if (currentEnemies < maxEnemies) {
			// Do the spawning logic and then:
			spawnEvent.SpawnedEnemy = freshSpawnedEnemy;
			spawnEvent.Call();
		}
	}
}

// This will listen to that event
class RandomStatisticsCollector : MonoBehaviour {
	private int monstersSpawnedTotal;
	private int witchesSpawnedTotal;
	
	private void Awake() {
		// register for the spawn event. we wanna count some stuff!
		EnemySpawnEvent.Register(EnemySpawned);
	}
	
	private void OnDestroy() {
		// don't forget to remove listener when we're gone or else!
		EnemySpawnEvent.Unregister(EnemySpawned);
	}
	
	// here's the listener that acts on the event that was raised by the EnemySpawner
	private void EnemySpawned(EnemySpawnEvent e) {
		// I'm making things up here, but something like that maybe :)
		if (e.SpawnedEnemy.Type == EnemyType.Monster) {
			monstersSpawnedTotal++;
		}
		else {
			witchesSpawnedTotal++;
		}
	}
}

There we go. The major differences being that, for registering and unregistering you virtually only work on your Event type. You do not need to throw in the EventDispatcher all over your project. It's used only in one spot, the base event. Events are both, data transport and a way to identify corresponding listeners. If you want new events, just make them and use them, no further maintenance steps required.

Just remember. For every Register() you need to have an Unregister(). Keep your event listeners clean at all times! In Unity, a good way to go about this is to register in Awake and Unregister OnDestroy, as rule of thumb.

And that concludes my approach to doing events. In this case, specifically for Unity but you'll have noticed, you can use this kind of code about anywhere you want. In any project that would lend itself to the usage of events.

Leave me a comment if you like. I'm also keen on knowing, if this, maybe, is something incredibly terrible and that's why nobody does it like that :D

Ah yes, again. Here's the source with examples :).

Hey folks!

Thanks for sticking around my profile for so long. You guys are mostly silent but I know you're there :P

And I wish to thank you for that. I got so far with The Vrennman Case now that it's actually beneficial to do a real game page on indiedb. You can find it here: Indiedb.com

From now on you can find all the news and media there (there's new stuff to see, actually). The articles will be written by John who's teaming up with the programmer person, the graphics guy and the sound dude to re-create his adventures in the form of a game (The Vrennman Case).

These all seem to be the same person but John pays them separate anyway so there's that.

So, again. Thanks for sticking around and reading at least a bit of my mumbo jumbo here.

See you over there!

Cheers

-d

The Vrennman Case

damagefilter Blog

Howdy.

So as mentioned in the last Firewalker Dev Ramblings I've been working on a new project. One with smaller scale and more realistic goals - something a one-man show can actually pull off without working on it forever. One where there's a fixed feature set and the game mechanics are known and implementable as to prevent most of the feature creep.

And today I'll present a bit of that. It's the work of most of the last year.

The full title is "John Amry in: The Vrennman Case". Or short "The Vrennman Case" or even shorter "VC". As the full title would suggest it's a story you're going to play. Like with most adventures, really. You'll have a ghost companion that'll help you on your way to stop the Vrennman clan from summoning the six evil lord demons and raise hell on earth.

Here's a bit of 2D lightmap testing and Ghostmode. Which is very cheaty because A) In that scene you actually can't have ghost mode and B) With the ghost you can walk through walls (which is intended).

There's a lot of work already done here. I've got save games, controller friendly menus (an in-game controls of course) and a pretty advanced game-event system I dubbed CSP. The initial version of it was devised during the Firewalker development and is sort of a Unity port of the Source engines I/O system.

Vrennman Case Menus

You interact with the world via the Actions menu. There you have one to three options based on the thing you're interacting with. This door here can be kicked or it can be opened. Both actions have an effect, more or less. This system, on the technical side, is reusable and it scales nicely. This together with the CSP system allows for complex puzzle building and fast iterations. So that puzzle design should be quick and efficient.

Okay. So basically the foundations are all down and ready.

Currently I'm tiding up a little. Make sure the first level is rigged properly, the asset placeholders are, for the bigger part, replaced with real assets and colliders are in place where they need to be.

Current status in percent, I'd say is 10%. I'm working on the first couple of scenes to get the player to know John, his companion and the enemies they are facing.

And that's it for today. I might, finally, get around making an actual game page for this project. We shall see.

Have a good one, folks!

-df

Hello folks!

Today, more than 12 months after the last DevRambling about Firewalker, I've come to put it on ice.

On Ice is not cancelled.
But...

Personally I've got a bitter taste of the past about it, thinking about how Prison Island fell apart like a card house in the wind.
I do know the situation is a little different today then it was many many moons ago when I had to cancel the Prison Island project but still. I really want to do this project one day.

At any rate, here's my reasoning.
Firewalker is, and that seems to be very typical for me, a project with a scope which is simply too large for the limited time I have for making games. There are six dungeons and a whole city with (surprise) six districts. The workload to produce all the required assets alone is daunting.

However, the most important factor is, I am at this time, simply not up for the job. I was refactoring code every other day because I kept learning new things. I broke one system after another, wiring them up again, leaving a mess of unused code and undocumented features and corner cases. My knowledge when I started the project was simply not sufficient to really pull it off.

However, there are some really (probably) good systems going on in the Firewalker codebase and I didn't want to let them catch dust. There are savegames, a Source engine like IO system, behaviour trees and a corresponding editor ... these kind of things.
So I pulled it all apart and created a library of code that is independent from any game it may be used in.
It can be found here: Github.com
This is my Firewalker-takeaway so far and it's okay.

During the past 2 years I have been working on smaller projects, learning new stuff and actually making games. Mostly in 72h gamejams. I've been using the code of that library in these jam games. I've been extending the library with my real-life requirements when making games and prototypes. I will delve into a couple of smaller projects with this codebase in my virtual backpack and gather new knowledge about making games.
I might even get to release something a little bigger than a 72h game at some point.

Eventually I will come back to Firewalker. It wil probably not be titled Firewalker anymore but it will be the top-down slashing beasts and solving dungeons thing I wanted it to be.
Hopefully, by then, I'll be able to actually pull off a project with such a scale.

So there's that.

Cheers,
-df

PS: If you feel like reviewing / using / forking / contributing to that library, please do :)

Hello there!

It has been a few weeks now without a word of us so here I am again. A lot of stuff has happened outside the game that consumed most of the time spent, however, it was all part of starting work on milestone 2.

The most important bits: I have re-done the website. Along with it, I ordered a new server on which I set up all the infrastructure for developing the game once again. That was a very time consuming task but now we have a shiny new and clean box at our disposal. For those of you knowing me from the CanaryMod project: That is why the canarymod.net site is currently down. I'm awaiting a response from the new domain owner.

Anyway. Have a look: Playblack.net

Now to gather some proper in-game screenshots.

Development wise, we also started on milestone 2 tickets.

Since we're using Unity 5 now, I re-wired the savegame system dramatically so it would load prefabs via the AssetBundle API instead of the Resources API. That means we don't have to load all the things into memory all at once and as added bonus, loading of save-games is now asynchronous, which means it feels more fluid.

Additionally there has been work to account for "fixed time situations". That's, for instance, inside dungeons and houses, where the time should not pass but stand still. Of course this leaves a large gaping hole for a lot of feature creep but I'll do my best not to let it in all that much. But the thought of mechanics to manipulate time is tempting!

Also, the sound system got a bit of refreshment. It too loads its audio clips from asset bundles and can be spoken to via the event system we're using.

Next thing in line: Getting some proper in-game material going. Then it's time for a new indiedb page. I think before that there will not be enough stuff to show off.

That was it for today folks, thanks for reading

-df

Howdy out there!

I had to forcefully take a break on development once more because the usual real-life happenings. It happens. The good news is, I'm still on schedule for the first Firewalker milestone - the technology release. It's actually not a release but the name sounds funky.

This milestone means that all the basic technology that I need (or think I need) in order to make the game is ready. By that I don't mean "all the AI" or "all the entities" but things like "the sound system", "important editor UI", "system to execute scripted sequences", "Entity Signals System (like I/O in Hammer Editor)" ... stuff like that. With these in place I can add more and more logic (or content), as I progress through the further making of Firewalker.

The tech-release will be ready the coming sunday (that's June the 14th) and once this is through I'll start on some PR related work. I'll create a new IndieDB page, perhaps something on facebook, fix up the website and, although only loosely related, I'll re-design the playblack site to something more modern looking. Of course the pages will be updated accordingly with information about what Firewalker actually is. I realise that my personal blog is way too abstract to give the reader a big picture.

Once that is done, I'll progress with the second milestone - which contains first bits of proper gameplay, at least one dungeon and a bunch of mobs. On that occasion I'll also tie up some loose ends in the code that are open from milestone one, if any, and clean up "the code so far".

This time I have no fancy pictures for you, I'm sorry :(

And that's it for today. Rather short but I hear that's a good thing sometimes!

Cheers,

-df

Hello there!
I'm writing again because I have a little progress. First and foremost I went ahead and turned these dynamic shadows I showed off a while ago (https://www.moddb.com/members/damagefilter/videos/dynamic-2d-volumetric-lights#imagebox) into soft shadows.
That looks something like this:
PARTY LIGHT  (Soft Shadows)

Pretty pretty. It's even better when it's moving. I'll have a video for you soon.
The cool thing is, I can have the shadow casters really low-poly now and it wouldn't matter.
That means ... MORE dynamic lights! Can never have enough dynamic lights.

A word on the visual style on the screenshots so far:
This is not the final artwork. The finished or final versions will look darker with more detailed and less like they are straight from a kids game. I'll have some of this for you soon! We're currently developing the city layout and after that it's remaking and creating new sprites.

Now, what took me most of the time the past weeks was the new sequence editor.
It is based on the behaviour tree implementation I also use for the AI and so creating a sequence structure was not the hard part. I have it all there already, including serialisation and save game compatibility.
What took me so long was all the editor code. It went down to an extend where I was wondering if it was worth all the trouble I had.
sequence ed

Turned out it was worth it indeed. You may recognise that pseudo code thing - once again I borrowed from the old RPG Makers. With this sequence editor ready, we can push out story and gameplay progress very rapidly. But of course, it's not 100% functional yet. Some event types are still missing, like Sound triggers.

Generally though, variable operations, switches, messages - all the basics - are in and working.
And that feels good. Seems like I'll make it to my tech-release deadline in a timely manner!

And that's it for today, thanks for reading!

Cheers,
df

Hello there!

It has been a funky week full of hunting regressions while finishing the attribute spreading in the combat system... which is a fancy description for "Weapons can put things on fire now".
Which is essentially what is happening now.
Flame-up!

Generally, weapons can do anything with the attacked entities now for demonstration purposes I chose the classic fireball and letting thigns go up in flames. This was all done to enhance combat mechanics and also to finish up the status display.
That's kind of an indicator about what kind of problems a particular entity may or may not have.
The initial idea was to add little icons above entity heads but then it seemed easier and much better to just introduce actual image effects.
The poison effect makes you go all green, Diablo-style ;)
But first, here's the fireball effect working:

So that was what I've been working on all week.
Thanks for reading and have a nce week!

Cheers,
df

Goood morning, folks!

The last week I was busy finding new ways of lighting up scenes in Firewalker.
That's not putting them on fire, that's setting up point and spotlights and better ambient light.
Simple fake 2D light
I did find a way that seems to be alright. It imposes one extra draw call all in all and so far it seems that it doesn't have any significant impact on the time required to render the scene.

So, it works by dropping a few things into an extra rendering layer (lights, mainly). They can be of any shape and colour. They can even be 2D volumetric light meshes (for dynamic cutout shadows).
That light layer is not rendered by the main camera but by a second camera that is rendering only this one layer.
To fake the ambient light I simply use the clear colour of that camera. At night it clears to some dark blue colour and at day it clears to something that looks like day.
The result is put into a render texture.
I call that a lightmap. Strictly speaking it's not a lightmap but the idea is the same.
The lightmap gets multiplied onto the the image that is rendered by the main camera.
Et voila.
Lights and daylight cycle. All dynamic.
Simple fake 2D light
That was the big thing this week.
I also wanted to implement a combat system but it turned out I already did that.
So that was one free point on my list.
Next thing on my schedule is a status indicator to show the status of the player and enemies.
That means, is something poinsoned or burning or frozen or numb and so on.

There shall be more colours then!
And that was it for this time, thanks for reading!

Cheers,
df

Why, hello there!
It has been a while, hasn't it?
I fell behind my originally planned schedule quite severely ... by 6 months ... *cough*.
That is why I was unable to write anything - there simply was nothing I could write about.
However, these six months didn't go by while I did nothing. I did something but perhaps not as much as I would have liked.

There were 2 major refactorings (because I love kicking my own ass and breaking the code over and over ...?) going on. Following is a very nerdy (more or less technical) rambling about code stuff.

State Machines to Behaviour Trees

I got around to finish the conversion from state machines to behaviour trees for my AI. One would think that is easy, however there is only one BT library out there for Unity that is worth using but I couldn't because it comes with integrated path finding and that path finding does not work on a 2D plane.
At least that is the impression I got while trying it out.

So I made my own ... more or less.
I ported the JBT library - or rather the relevant parts of it - to C# and wrote a node editor for Unity to integrate it. That very editor was a major pain in the neck to write though. The first version I made assumed that the Unity serialiser can serialise anything that falls within the rules (GameObject or annotated with Serializable etc etc). However, it has a maximum nesting level of 7.
Magic constants. Great!
That meant I could only have behaviours with a rather limited complexity.
I supposed that a proper boss battle AI would have more than just 7 nested behaviours to function properly, with all the sensor checks, decorators, composite nodes and an undefined amount of actions a boss can have.
So 7 ... not gonna happen.
For version 2 I used ProtoBufs, the same library in use for the save game system.
This way I can have reference tracking (awesome for having parent nodes as real references instead of copies, which would really break a lot of things) and an unlimited amount of nesting.

So ProtoBuf serialises my behaviour tree into a simple byte array and Unity serialises that byte array back and forth. Job was finally done.
I have implemented a simple wandering behaviour and attack-on-sight behaviour so far.
It works a charme.

The new dialogues

The next big topic on my todo list was re-implementing the dialogue system.
It broke a while ago because it was too interconnected with all kinds of other parts like the input controller and inventory system. So I decided to decouple the whole thing and use my event bus to control it instead.

It can now be requested to do things by events, an "operator" does not need to know anything about the system doing the dialogues, it just makes requests. If there is no system in place, simply nothing will happen.

The dialogue manager itself dispatches events to notify all listeners that something happened. Specifically that a dialogue started or ended.
So that is done now too.

In other news


Unity 5!

The moment it was released I switched to it and it's gooood!
I can now stop loading all the assets into memory at once and work with asset bundles. That means I should probably change the part of the save game system that loads prefabs to populate / re-create a scene. Not that it would make a big difference as all the assets are but a few kilobytes in size but it will probably lower minimum specs so you could run the game with a pile of wooden sticks.

And that was it for todays very text heavy blog entry. Next time there will be more pictures, promise!

Cheers,
df

Level
Avatar
Avatar
Last Online
Country
Germany 🇩🇪
Gender
Male
Friends
Become friends
Member watch
Follow
Blog Statistics
Articles
43
Views
4,198
Views Today
1
Feed
RSS