Wednesday 15 November 2023

Godot - Rotating a Camera in 3d space

For the past couple of years I've been dabbling about in Unity, but due to their recent PR disaster, I've shifted over into dabbling with Godot instead.

The project I'm trying to create is a turn-based strategy game, which would include terrain. As such, units might be hidden behind terrain, so the user should be able to rotate the camera.

To this end I've been following the Godot tutorials, creating a 3d plane, and putting a box in one corner so that I can see how the whole thing would rotate (if it were a plain plane, I wouldn't be able to tell it rotated). I didn't bother putting in a player character yet (as the concept is quite different to the example in the tutorial), but I did follow the steps for implementing a camera.

I wanted the camera to rotate by increments of 90 degrees (so there would effectively be 4 positions). Unfortunately, most of the maths in the documentation is about radians, which doesn't seem useful to me for this case, as it makes the maths more complicated (and less precise due to pi having [as far as we know] infinite numbers after the decimal)

Eventually I found that I could use "rotation_degrees", giving me the exact precision I wanted and without requiring more complex calculations. I didn't want the transition to be instantaneous, so I used "lerp" to make the camera rotate over a series of frames (apparently this is an abbreviation for "linear interpolation", which makes sense) - I got this from one of the Godot samples (but it didn't have much annotation)

I also wanted to make it so that inputs are ignored during rotation, that the user can only do one rotation at a time. To this end, I wrote the code so that when it starts rotating it sets a flag, then when finished it resets it. If it receives another instruction to start, while that flag is set, that instruction is ignored.

Below is the code I had for that point, before things get particularly interesting.

extends Marker3D

const ROT_SPEED = 10
var rotating = false
var targetRotation = 0

func _ready():
	targetRotation = rotation_degrees.y

func rotate_left():
	targetRotation = targetRotation - 90
	if(targetRotation < -360):
		targetRotation = targetRotation + 360
	rotating = true
func rotate_right():
	targetRotation = targetRotation + 90
	if(targetRotation > 360):
		targetRotation = targetRotation - 360
	rotating = true
func _process(delta):
		rotation_degrees = Vector3(rotation_degrees.x,lerp(rotation_degrees.y,targetRotation,ROT_SPEED*delta),rotation_degrees.z)
		if (rotation_degrees.y == targetRotation):
#Re-enable when finished rotating = false

However, this didn't work as expected - the rotation would stop, but not finish, before reaching a 90 degree change. Since the starting rotation was 45 degrees, if the target was then 135 (45 + 90), the rotation would stop at 134.999969482422 degrees (which changed to 135.000015258789 when I moved the window)

This meant that I had to round the current rotation value to compare to the target, and rather than an equals comparison use a "greater than or equal to", in case it rotated slightly too far.

As you can see, the "rotation_degrees" property is a Vector3, which is a structure that uses 3 floats as the values. But all number types (int, double, float etc) all have a maximum value (which differs for each type) - once that is reached, if you add 1 to that number, the result is then the minimum possible value (this is called "overflow"). What would happen when this number is reached for the Vector3? I have no idea, but with the code as above it is possible to just keep rotating until you reach that point. Since I don't know what would happen, and the numbers could be absurdly huge, it makes sense to restrict it to smaller numbers to avoid this and make debugging in future far easier.

The next step was to adjust the values so that if it goes below -360 degrees or above 360 is then adds or subtracts 360 to keep it within a reasonable boundry.

When testing this out, though, it looked like it was acting like a coiled spring - when the target value got above 360 (which would be 405, so it would then be set to 45) it would suddenly "spring" back rather than smoothly animating. Putting in debugging, it seemed to be rotating twice at once.

This is apparently from using the "Input.is_action_pressed" function, as it seems to keep executing the action repeatedly. Perhaps, despite the flag trying to prevent this, it was picking up the instructions across two frames? Maybe there is a race condition, and the function was executing asynchronously? Regardless, switching it to "Input.is_action_just_pressed" seems to restrict it to only one press at a time.

However, still the change is rather sudden, the final change not having any animation to it.

The ultimate solution was to allow it to overflow for the purposes of animation, and then to adjust the value after the rotation has finished. At least for rotating right, where the numbers are increasing. When decreasing, because of the check (greater than or equal to) it meant that the rotation was finishing early. So the comparison needed to change depending on a switch with 3 states - an enum rather than a boolean flag.

The final code for this is below.

extends Marker3D

enum RotationType {NONE, LEFT, RIGHT}
const ROT_SPEED = 10
var rotating = RotationType.NONE
var targetRotation = 0

func _ready():
	targetRotation = rotation_degrees.y
	print("Starting, current rotation is " + String.num(targetRotation))

func rotate_left():
	targetRotation = targetRotation - 90
	rotating = RotationType.LEFT
	print("Setting target rotation to " + String.num(targetRotation))
func rotate_right():
	targetRotation = targetRotation + 90
	rotating = RotationType.RIGHT
	print("Setting target rotation to " + String.num(targetRotation))
func _process(delta):
	if(rotating == RotationType.NONE):
		rotation_degrees = Vector3(rotation_degrees.x,lerp(rotation_degrees.y,targetRotation,ROT_SPEED*delta),rotation_degrees.z)
		var normalised_y = rotation_degrees.round().y
		if ((normalised_y >= targetRotation && rotating == RotationType.RIGHT) || (normalised_y <= targetRotation && rotating == RotationType.LEFT)):
			#Re-enable when finished
			if (normalised_y > 360):
				normalised_y = normalised_y - 360
			if(normalised_y < -360):
				normalised_y = normalised_y + 360
			rotation_degrees.y = normalised_y			
			targetRotation = normalised_y
			print("Finished rotating, target rotation = " + String.num(targetRotation))
			rotating = RotationType.NONE

Friday 19 May 2023

Kain Amigurumi

This is one I started working on pretty much immediately after finishing my previous doll - the namesake of the Legacy of Kain series: Kain (in his Elder Vampire form)

It's been quite hard to work up the motivation for typing this up, in part because I sort of live-tooted it over on Mastodon

Nevertheless it's been a bit of a long journey - we have a puppy who would probably try to eat whatever I make, so I was only working on it during my lunch breaks when I'm in the office (so at most a couple of hours each week)

Through into that my getting an abscess on my back and getting it surgically removed, there's been a lot to get in the way (up-side though is that I now have a hole in my back that I could hang crochet hooks off of if I were a complete lunatic)

This one used the same base as the others (AmiguruME by Allison Hoffman), but of course, with some adaptations. The flesh was done in a dark green (I couldn't find anything to accurately be the sickly-vomit-green-and-yellow of his flesh in the games, so this was a good abstraction in my opinion) and the legs in plain black.


I used the same technique for the feet as I did with the Janos doll, but used a sort of off-white acrylic yarn to give it a sort of ivory colour. This was also to try to make a clear distinction between the claws and the hair.

The grieves were done right at the end - using the "traverse root" method (just doing a bunch of slip stitches into existing stitches as a basis for more) I made them using some yellow acrylic to make them sort of golden. I wanted to do this to make the legs more interesting. Despite measuring them against one another, I didn't remember the number of rounds/rows, so they ended up slightly different heights. I'm quite annoyed by this whenever I look at it. You could call me a-grieve-d! (I'll get my coat)


As mentioned, the flesh was in a dark green acrylic yarn, changing to a brown alpaca wool for the sort of leather wraps, then finally to black for his gloves.

Following the Commander Sterling doll, I discovered that Allison Hoffman had created a pattern for hands with fingers, which is sold on her shop.

Of course I bought it, so that the fingers would be in better proportion. It took me ages to get around to downloading it. In fact, I had waited too long, so the link was invalid. However, Hoffman re-enabled the link for me once I emailed her about it - top tier customer service.

To make the claws I joined the fingers together into pairs and then used decreasing to make them pointy. I made the thumb twice as wide on the hand and did the same thing to decrease to a point.

Body Accessories

In terms of the body, there were a few accessories to make.

First of all was the cape. I absolutely obsessed over this. I wanted to see if I could get the symbol onto it by doing colour changes, and minimise carrying across stitches (because that looks messy)

I created a tool in ReactJS in order to visualise this... and found that if I were to do it in the size I want, I would have to embroider it instead. Oh well. (Note, I'll make the tool available eventually)

I also made the cape a bit short - in my head, it went to the centre of the chest (under the medallion), and the rest is leather straps, so I worked to that.

Then I decided to use the toy as a reference, and found that the cape should wrap right around the body, and instead the leather strap just sort of floats there, not connected to anything other than the cape. Weird.

So I did the rest of the cape, and the leather strap.

The doll next to the action figure, with the paper pattern for the codpiece

The final part was the "Plunging Codpiece (TM)" - I realised that if I were to place the bottom of this at the top of the leg/waist section, it would be too high. Thankfully, I could use the gold/yellow internal section to sort of hide this, make it look like the waist was lower than it was.

I drew the pattern on paper, and went by that rather than a pattern in terms of stitches and rows. I'm pretty pleased with the result.


The face was relatively normal, following the pattern and using white acrylic for the hair. Looking at the toy I was using for reference, I tried to draw the crests. They looked to be in three parts, but had to be distorted because the doll's head is stretched in comparison to the more realistically proportioned toy. I think it ended up looking a bit strange, and not so much of a scowl, but it did hide issues with the hairline. The earring was a very simple thread with a knot at the end, stitched into the left ear.

The back of the doll, showing that I cut the hair far too short

In terms of the hair, I made it extremely long in order to get it to the right length, and then cut it down. However, when I ended up cutting it, I definitely cut it a bit too short, because some of the strands would have been loose (they were too short). I just have to keep reminding myself that this is an abstraction.

To quote a famous dog that lives in a burning house, "This is fine!"


There are a few changes I would try to make were I to do this again, but overall I'm happy with the result, despite some niggling little issues that annoy me.

I may eventually have the patience to try to make him a Soul Reaver to hold, but I don't know how to make skulls, and I also don't know how to make the kriss blade without making it too floppy.