Dev Blog šŸ–šŸ– - Math Is Easy When You Have Fingers.

I hope youā€™re doing well! My name is Bites, and Iā€™ll be your host and server for this evening.

Letā€™s see whatā€™s in store for this update!

Iā€™ve been making some party game levels. Would you like to see a couple of them? TOO BAD!

ā€¦Alright, fine.

zoomy
Almost all of the interior floors in this racetrack are gravity floors. Try not to fall downā€¦ or sidewaysā€¦ or upwards!

shapes
A fairly straightforward tag level, but there are plenty of cool ways to optimize your movement and abuse slopes to feint and mislead your opponents.

Laurence has also been hard at work on what might be the coolest background concept Iā€™ve ever seen. (and also, potentially the hardest to model?? why do you do this to me)

Before I show you the concept art, Iā€™d like to remind you of this prototype background we did a little while back.

Here, we were able to create an effect like the stage was moving through the background. This was crucial to the design we had planned, and it worked like a charm. Without further adieuā€¦

train bg concept

I donā€™t even have wordsā€¦ I was personally completely blown away when Laurence showcased this, as were many of the other devs. Itā€™s going to be a challenge to build in 3D, but Iā€™m confident we can do it!

Normally, I try to have a natural lead-in to whatever writeup comes after my personal recap, but Iā€™ve got nothinā€™ for you, sucker! Hereā€™s some cool stuff about reflection in our game code, by CraftedCart.


Again for this post, I donā€™t really have much of interest to show - itā€™s mostly just been various code cleanups, continuations of systems Iā€™ve mentioned in previous blog posts, helping out the physics team a bit with tooling, and shooting myself in the foot (woo for debugging memory mismanagement).

So this post is gonna be yet another tidbit from the codebase. This oneā€™s gonna be a bit shorter and a bit more code-heavy than usual, sorry!

Making C++ more dynamic

This is a sort-of follow up to the previous posts Iā€™ve made on variants and type tags. C++ out of the box is a very rigid language - you canā€™t just look up properties of an object while the program is running, theyā€™re looked up at compile time and thatā€™s all you get out of the box. This is in contrast to languages such as, say, Lua, where you can just look up arbitrary properties while it is running.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
-- Let's say we have a class "SceneNode" that represents an object in the stage
local platform = SceneNode{
name = "platform",
type = "mesh",
position = Vector3(27, 4, 0),
rotation = Quat(0, 0, 0, 0),
scale = Vector3(1, 1, 1),
}

-- Read in the name of a property to modify from the user
local what_to_modify = io.read()

-- Modify the property on the "platform" object
platform[what_to_modify] = Vector3(8, 16, 24)

-- So for example, if the user entered "position", we would change the position property of the scene node, without hardcoding in that we're changing "position"

C++ (with Unreal) for comparisonā€¦

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class FSceneNode
{
private:
FString Name;
FString Type;
FVector Position;
FQuat Rotation;
FVector Scale;

public:
// mmmm, boilerplate
SceneNode(
FString InName,
FString InType,
FVector InPosition,
FQuat InRotation,
FVector InScale
) :
Name(InName),
Type(InType),
Position(InPosition),
Rotation(InRotation),
Scale(Scale)
{}
};

// Create a SceneNode
SceneNode Platform = SceneNode(
TEXT("platform"), // name
TEXT("mesh"), // type
FVector(27, 4, 0), // position
FQuat(0, 0, 0, 0), // rotation
FVector(1, 1, 1) // scale
);

// Read in the name of a property to modify from the user
FString WhatToModify = FString();
std::cin >> WhatToModify;

// Uhh... how do we modify the property?
??? = FVector(8, 16, 24);

I know FNames would make more sense than FStrings in this context, but Iā€™m trying to keep it at least somewhat simple

As you might be able to guess, there are pros and cons to using a rigid static language verses a more dynamic one. Looking up properties at compile time leads to a faster language, as a compiler can just encode memory offsets into the compiled code. Dynamically looking up properties leads to a more flexible language that has the ability to inspect itself, at the cost of some speed.

Now while the extra speed of static languages are nice, this does become a bit of an issue if you want to interface with other more dynamic systems, such as having Lua scripting. Heck, this would be useful even for stage configs - you could have an animation specify a property to animate and we could just figure out where in memory is this property. It would be awfully convenient if these systems could, say, modify transform.position.x for an object without me having to write a bunch of if-elseif-elseif-elseif-else statements on the C++ side of things, to check what property to modify and do type conversions and stuff.

Conveniently, Unreal Engine already has all the boilerplate for handling reflection (that is, the ability for a program to inspect itself while itā€™s running). Inconveniently, thatā€™s part of the UObject system, meaning itā€™s unsafe to use it on anything other than the main game thread; thatā€™s a bit of an issue given we load stages in the background on other threadsā€¦ Well, I guess we have to hand-roll our own system then!

Enter FPropertized, a little class to handle some barebones reflection in C++. This is a simple thing, mapping names to the memory location and types of variables.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class FPropertized
{
private:
TMap<FName, FPVariant> Properties; // The variant does *not* own the data it points to

public:
/**
* @return
* - Ok(FPVariant*) if a property exists with the given name
* - Err(FValueError) if a property does not exist with the given name
*/
TResult<FPVariant*, FGameErrorRef> GetProperty(const FName& Name);

/**
* @return
* - Ok() if the property was set
* - Err(FValueError) if a property does not exist with the given name
* - Err(FTypeError) if trying to set a property where the property type != NewValue type
*/
TResult<void, FGameErrorRef> SetExistingProperty(const FName& Name, const FPVariant& NewValue);

protected:
void AddProperty(const FName& Name, const FPVariant& Variant);
};

Thereā€™s not much to this class, just a way to add properties when an object is created, and a way to get and set properties dynamically afterwards. With this, this means I can rewrite the above example with the SceneNode asā€¦

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
// We extend from FPropertized now
class FSceneNode : public FPropertized
{
private:
FString Name;
FString Type;
FVector Position;
FQuat Rotation;
FVector Scale;

public:
SceneNode(
FString InName,
FString InType,
FVector InPosition,
FQuat InRotation,
FVector InScale
) :
Name(InName),
Type(InType),
Position(InPosition),
Rotation(InRotation),
Scale(Scale)
{
// Register the properties
AddProperty(TEXT("name"), FPVariant::Create<FString>(&Name));
AddProperty(TEXT("type"), FPVariant::Create<FString>(&Type));
AddProperty(TEXT("position"), FPVariant::Create<FVector>(&Position));
AddProperty(TEXT("rotation"), FPVariant::Create<FQuat>(&Rotation));
AddProperty(TEXT("scale"), FPVariant::Create<FVector>(&Scale));
}
};

// Create a SceneNode
SceneNode Platform = SceneNode(
TEXT("platform"), // name
TEXT("mesh"), // type
FVector(27, 4, 0), // position
FQuat(0, 0, 0, 0), // rotation
FVector(1, 1, 1) // scale
);

// Read in the name of a property to modify from the user
FString WhatToModify = FString();
std::cin >> WhatToModify;

// Now we can modify the property, dynamically!
Platform.SetExistingProperty(
FName(WhatToModify),
FPVariant::CreateOwned<FVector>(FVector(8, 16, 24))
);

// And if we want to get the property, we can do...
Platform.GetProperty(FName(WhatToModify)).Unwrap()
// ...which gives up a variant pointing to the property

Huzzah!


Pretty cool update, right? Riiight? ā€¦No? Well, you can shove it! We worked hard on this update! Oh? You meant that it was SUPER cool, not just cool? Alright, but youā€™re on thin ice.

Thanks for reading!
See you all on the 15th.