Dev Blog F - I Don't Know Computers

Good morning/evening/night. My name is Brandon, and I have a secret to tell you. You might find this hard to believe, but I have been… playing videogames recently. Scandalous, I know! But I simply cannot help myself. My latest guilty pleasure has been Celeste. After clearing all the game’s content, I’ve taken an interest to speedrunning it.

Its level design and difficulty progression are of particular interest to me; I believe that Celeste is absolutely master class in these fields. There’s a lot you can learn by studying how Celeste organizes its areas and extra content.

This is actually a very thought provoking component of game design, I find. It’s important as a game designer to draw your ideas from many different places. Not just games you’ve played, but everywhere! Films, books, places you’ve been and things you’ve done. You will be able to solve problems in many different and better ways with that large breadth of experience and knowledge. Never stop learning and growing!

Let’s get into the update.

We have some new blood on the team!!!

I’d like to welcome Scrap651 and PseudonymousBN to our team as level designers. Here’s a couple of their very first creations!

scrap level

pseudonymous level

They have a grand amount of prior experience in level design, and I think that they will provide a much needed change of pace to my own designs. I’m hoping that our individual styles will shine through in the final game.

Additionally, in anticipating of us getting some new game mechanics implemented, I have been designing some levels that will use them.

tilt gravity

Gravity floors on seesaws. Pretty awesome.

Thanks to the terrific Laurence, we’ve also got some concept art coming down the pipeline for some of the special objects that will be appearing throughout our levels. A first that I’d like to share is our bumper design!

buper

This is just one of many different kinds of special objects. There’s also wormholes, mines, switches… you get the idea. We’ll be sharing those as they come along!

Up next is a writeup by the wonderful and talented CraftedCart. Take it away!


Reflection part 2? or 3? Something like that…

Yet another follow up to dev update 10 here, one I perhaps should have seen coming. Recall how we had a property system going on, with the idea that you could take a string (or a “data path” as we call them) from a script or from a stage config, such as transform.position.x, and we’d be able to convert that string to a series of memory addresses in C++-land? That works fine for types that we have control over: since we used inheritance to handle properties, we can extend a class from FPropertized to add property powers to it.

1
class FSceneNode : public FPropertized

As you may imagine, this breaks down when it comes to adding properties to types outside of our control, such as the transform and vector types provided by Unreal Engine. I can’t just go ahead and modify those classes, not unless I feel like bashing my head a bit and then bashing every other developer’s head some as I ask them to re-compile the engine (and Unreal Engine can take a few hours to compile and render a machine unusable during that time). So what could we do to add reflected property support to these objects?

1: Don’t, and just hardcode access to such properties at points in our codebase where we can

No.

2: Get frustrated that the reflection system has to be reworked some more again

Yup

3: Propose an awful kludge

A dev chat

Transcription:

1
2
3
4
5
6
7
8
9
CraftedCart: thinking about `FPropertized`, and how we'd be able able to get properties from unreal's core objects (like `FTransform` and `FVector` and what not)
CraftedCart: Rust-style traits would sure make this easy, but you can't add member functions to classes in C++..
CraftedCart: *or can you*
CraftedCart: this would be quite the hack, but if we inherit... `class FPropertizedTransform : public FTransform, public FPropertized`
CraftedCart: and don't add any new data to the class (and `FPropertized` only creates static data)
CraftedCart: we should be able to `static_cast` a plain 'ol `FTransform` to an `FPropertizedTransform`, right?
CraftedCart: this sounds horrible but I'm not sure of a good way to do this in a more-sane way hmmm...
CraftedCart: not unless I want to mess around with extracting FPropertized stuff out into free-functions, and dealing with function overloading, and statically initializing some data per-class maybe
ComplexPlane: ~~*buries head in sand**~~

I haven’t tested this one, so who knows if you’ll get nasal demons or not!

4: Pinch Take inspiration from someone else’s code

Thankfully we also have a CraftSpider on our dev team. They’ve actually made their own reflection system for C++ before, partially based off of the type tag system that we have in Rolled Out! already (so that makes integrating this easier, though that still doesn’t stop my head from hurting when working on this). With regards to properties, instead of using anything like the FPropertized concept we currently have, they have properties stored in the type tag object itself. This makes… oh y’know what this is confusing enough trying to explain this through text, have some diagrams. You’ll have to excuse my handwriting - it’s tricky to write when I’m not looking at my hands.

Our current property system

This is what we currently have for our property system. Note the arrow pointing outwards from the FSceneNode box, meaning adding properties requires modifying the code for FSceneNode.

How our current property system doesn't work with engine code

When it comes to types built-in to the engine, we can’t just simply modify the objects to add properties.

The new system

So the plan is to move properties out to type tags, which can reference the data inside types, without needing to modify the type code itself. For now, we have a few macros to handle defining properties…

1
2
3
4
5
R_DEFINE_META_TYPE(FVector);
R_DEFINE_MEMBER_FIELD_TYPE(FVector, float);
R_DEFINE_MEMBER_FIELD(FVector, X, TEXT("x"));
R_DEFINE_MEMBER_FIELD(FVector, Y, TEXT("y"));
R_DEFINE_MEMBER_FIELD(FVector, Z, TEXT("z"));

…this’ll make FVector known to our reflection system, defines some storage for float fields (since static variables in templates can’t be trusted - see the last blog post), and marks FVector fields X, Y, and Z as properties (and by convention, these are renamed to all lowercase and snake_case, such that they’ll fit in with Lua code style).

Anyways, this whole detour came from refactoring animation code. It’s all nice and well being able to define animation curves and evaluate them and what not, but the idea was animation channels would store a data path (like the aforementioned transform.position.x) and we could just use the reflection system to resolve the path, rather than hardcoding if-statements to resolve them.


Still some very interesting stuff happening behind the scenes. I wish I could say more! I will soon be able to, hopefully.

Thanks for reading! See you on March 15th.