Emotions, storytelling and Heavy Rain

I wept. A few times in fact. Before I get there, let me bring you to the beginning. No, not so far as the Big Bang…

[Note: Strictly speaking, this is not a game review. Go somewhere else if you are looking for that. And there are spoilers ahead…]

Curiosity brought me to YouTube

I was browsing the racks in a game store. Even though I’m not a game fanatic anymore, I like browsing to see what kind of games there are. I believe game development (and the demoscene) pushes software development to the extreme.

So. I saw this game called Heavy Rain. Hmm… multiple endings, multiple story branching. Interesting. But it wasn’t “I must buy this game and run home so I can play this game for the next 72 hours!” interesting.

I was sort of in the middle of playing another game (Bayonetta if you must know…), and I kind of suck at it. I mean I completed the game on easy mode, but really wanted to do more in it. Then I remembered YouTube might have, uh, playthrough videos of the game. So I surfed YouTube.

Anyway, I found someone who totally pwned that game, and finished watching the stuff I had missed out in the game. Then I looked at what else he created. There it was, a Heavy Rain playthrough. But it wasn’t complete. So I looked and found another YouTuber who did complete the game and uploaded the entire video series.

Where the horror and sadness started

It was a Saturday early afternoon. I sat down comfortably in front of my computer, and proceeded to watch the game playthrough. And the story unfolded…

About 95% of the game occurred in the rain. And the rendering was exceptional. You could see the spattering of rain drops on top of the car, on rain puddles on the road, on the human characters in the game. There’s a sinister link of the wet weather to the story (hence the title).

What I was also amazed with, was the human facial expression. During loading screens, the face of the character you’re about to play was on screen. I mean the head fills up the entire screen. You could see the pores on the face. What’s more awesome than the realistic facial texture, was the realistic facial animation. There was cheek twitching. The head turned left and right seemingly spontaneously. The eyes wiggled this way and that.

How far had game development gone in terms of motion capture?

So with realistic human body movement and facial expressions, the game creators went on to create the most compelling game story I had ever experienced. There were no stats such as your strength, your health points and so on. You had no inventory. You had no need of skills such as climbing, swinging, jumping and other acrobatic manoeuvres.

The game started out slow, with simple tutorials to teach you how to play the game. You perform actions such as brushing your teeth, showering, turning on radios. It was your main character’s older son’s birthday, and you played with your two children, and helped your wife in setting the table. It was like a crash course in family bonding.

And I had no idea how insidiously deep I was in the clutches of the game creators and storytellers…

there are some things which just have to happen even if you don’t want them to… – Ethan Mars

That quote by your main character was like a foreshadowing of how the entire game was like. *sniff* I’m, I’m sorry.

In the video above, the sequence on the playing with your son also trains you when future quick time events, in particular fights, occur. In the later part of the video, your son got separated from you. The music turns sinister. The crowd keeps bumping into you, keeping you away. It’s like the worst thing ever. Note the frantic search for the red balloon.

And at the end of that video, a most tragic thing happened.

I wept.

A game that made me cry. Because I identified with the character, and his loss somehow transferred to me.

Now I have to say, I’m already susceptible. I’m enamoured with Caucasians, have a predilection for the Western culture, and have dreams of building a family. If I didn’t know any better, I would say the game creators were out to get me. Making me feel for the character and then take away something so important to him (and thus me).

Entrepreneur support

One cannot underestimate the amount of support and encouragement an entrepreneur needs.

Then there was my current situation. I had a lot of conflicting constraints. Emotions run high. Doubts surround me. My mind was in turmoil.

I mean, I’m working out to gain the physique of a Calvin Klein underwear model, then I have to eat more to gain muscle mass, but I have to be careful of my food and other expenses, which is hard because sometimes I need to invest in business education, which means I have less time to do the things I enjoy, but I freaking quit my job so I can enjoy them…

Uh, what was I talking about again?

So, I want you to take note of how differently the game creators wrap you into their story. Broad generalisation, typically games allow you to control something or someone. They give you a rich environment to explore, hoping to cloud you from realising the illusion that there are limits to the game world, terrain wise or action options wise.

They feed you nuggets of information such as the type of guns available, types of magic, any special items to boost your abilities, the little pieces of conversation from non-player characters (NPCs), the realistic rendering of foliage, the uncannily true physics of a car crashing. All to take you away from the “real” world and into their world. That’s what games are supposed to do, right?

Heavy Rain makes you perform activities that you normally do in your life. There are no immense environments to explore, because you’re exploring the character you’re roleplaying. You’re immersed in the game, you’re invested in the game, you’re totally and completely and absolutely dying to know what’s going to happen next because

The prison of the mind is the strongest prison of all

The game creators made you feel for the character, and thus captured your mind.

And you know what else? There’s no inventory, no stats to optimise for, no weapons, no armour, and minimal world exploration. What’s left? Choices. Your choices. You are keenly aware of your choices because they are about the only thing you can do in the game. And your choices sometimes affect the story so much that consequences are huge. Characters can die. That’s what your choices can do.

To quickly run through the story to my next point, the main character Ethan’s other son was kidnapped by the Origami killer. The Origami killer only kills boys, and there’s always an origami figure and an orchid on or beside the body of the boy. And the killer only kills in the fall, when the rain is falling *cue sinister music*

During story splitting to cut into another character’s event, there would be some text such as “Tuesday, 9:52 PM, 1.394 inches”. I could understand the purpose of the date and time. But the length escaped me. Until an in-game character, an agent from one of those American agencies (I can’t state the 3 letter acronym of the agency because apparently there was some law about the use of the 3 letter acronym. I’ll give you a hint: Mulder and Scully used to work there.) figured out that the victim died when the rainfall was about 6 inches. As the story progressed, that length (or more correctly, the height) kept increasing. And my sense of urgency kept rising.

“Please, please, please let them be in time!”

I mean, of course you’ll be in time. All the feel-good movies end so. But this is a story branching game. There is most probably an ending where Ethan’s son was never saved. *sniff*

So the Origami killer gave Ethan 5 tasks. If he could complete them, Ethan would know the location where his son was kept. I’ll show you the video for the 3rd task, because… I’ll just let you judge for yourself.

[Warning: the event in the video might be upsetting to you. You’ve been warned.]

I wept.

After I saw that happen, I had this intense urge to find the Origami killer, jam my hand into his throat and rip his heart out. While he’s alive. That scene tore me up at a fundamental level.

I know it’s a game. I know it’s fake. But the game creators really messed me up badly with that. And thank goodness I’m not playing the game, because I might be torn up by the choices I had to make. Yes, I know it’s a game. Yes, I know it’s fake. *deep breath* I’m fine. Really.

The moral of the story

What have we been talking about? A game. Fundamentally a program with text, graphics, music, and a whole bunch of polygons moving around.

People crave stories. Whether it’s real or imagined, it doesn’t matter. Sometimes they want an improved version of whatever life they’re leading. Sometimes they want to be in a world where it’s nothing like their life. Books, movies, and games allow people to escape. Exchanging stories with friends and family allow people to step into other people’s lives.

Programming a game has gone up many levels. You need the art of storytelling.

And the theme music haunted me for days after…

Developer Stories

Paparazzi red carpet

I must be getting famous. I was contacted by a social media marketing manager at M80. His company is working with Microsoft to promote Visual Studio 2010. Ok, I think I’ve fulfilled the necessary disclosure requirements. And no, I don’t get anything out of this. And I doubt I’m really that famous, but give me a few seconds to savour my short-lived fame anyway.

Microsoft wants to get feedback, and I believe it doesn’t have to be about Visual Studio 2010 (or Visual Studio at all). They’ve created a YouTube channel called Developer Stories. And they want to know why you are a developer.

If you’re a video kind of person, please go ahead and upload a video of you telling your story. Forget about your biases and opinions about the companies involved. I believe a polymath programmer should be above that. If nothing else, I want to hear your story too.

Since I’ll stutter in front of a video camera, and thank goodness I don’t have one (which is just an excuse, since my bedroom is a lousy backdrop), I’ll have to tell you my story in blog post format.

Once upon a time…

Actually, I stumbled upon programming. Sure I joined the computer club when I was in junior college (about 17 or 18 years old), but I didn’t understand the point of all the arcane Pascal lines of code. When I got into university, I didn’t have the credits (nor background) for the computer science track. So I took up applied mathematics as a major, and computational science as a minor.

In my first semester of my freshman year, I took C programming (which was a requirement for the computational science minor). Variables, assignments, loops, algorithms. It was fun. I also couldn’t understand why some of my classmates had difficulty wrapping their heads around what I perceived as simple ideas. They couldn’t understand some programming concepts, and they had some trouble understanding how to apply and change and break down a problem into programmable, solvable parts. I mean, you calculate something, add it to a temporary variable, go to next iteration, calculate with different input, add to temporary variable, go to next iteration until done. That’s summation. What’s so hard?

It was then that I realised that some people just weren’t made for programming. I’m not saying I’m a genius at it. I may just have a knack for breaking down problems so a programmatic solution is possible. That’s what programming is, not the lines of code and algorithms and what-not. Well, I was good enough at it that I decided to upgrade my computational science minor to a major.

Programming is about solving problems

What’s so special about computational science? And what’s the difference between it and computer science? I’m not sure. Computational science is more about solving (scientific) problems using programming, rather than the programming itself. I’ll leave you to compare that with your understanding of what’s computer science.

And what do I mean by solving problems using programming? I drew Sierpinski triangles. I used Newton-Raphson method to find roots. I solved a gigantic set of 100 equations with a 100 by 100 matrix. And most of the problems were based in science or mathematics.

So my background is about breaking down problems and translating that into programmable parts. I didn’t learn about software development cycles, software management practices and all those complicated stuff. I was trained in the solving problems, not the meta stuff around it. I’m not saying those complicated stuff aren’t useful. Just be aware of what you’re doing.

Here’s a suggestion. Learn about your business processes and work flow. What does your company do? What does your company sell? Which industry? Because your value as a software developer goes up exponentially if you can solve a business problem, not that clever obfuscated one-liner of yours.

Don’t just be a programmer. Be a problem solver.

So after I graduated, even with a math background, I went for a software development career. I like solving problems, and programming is one method. This guy just about sums it up:

So that’s a summary of why I’m a software developer. And now for some free advertisement for Visual Studio. I like C# and Visual Studio. Probably because of my C background. There’s Intellisense, a well documented library of the .NET Framework’s functions, and… it feels “clean”. I’m not sure how to explain that to you. I’m a simple man. I don’t need a lot of what is called developer productivity tools. Maybe I haven’t a problem to solve that requires them.

[update]
And I only have the Visual Studio Express version, not the paid one (but I bought VS2005 way back if that counts). The professional version’s a little steep in price, you know, considering my recent foray into entrepreneurship.

So what’s your story? Tell me in a comment, a blog post, or a video response.

[image by Ad Hatcher. Videos taken from Developer Stories YouTube channel]

What ketchup bottle manufacturers can learn from a drinks stall uncle

Pouring milk
[image by Nicholas Moore]

My posterior hurt. It was a slow sultry afternoon, and my friends and I were hanging out at a hawker centre. We had dinner there and continued to chat. Then I couldn’t stand it any more and stood up. The seat was hard, and I was starting to lose sensation in my gluteus maximus.

I needed a drink anyway, so I went to a drinks stall to order teh si siu dai.

[digression]
In the Hokkien dialect, teh means tea with condensed milk and sugar (when ordered from a typical drinks stall in Singapore. Otherwise it just literally translates to “tea”.). teh si means tea with evaporated milk (and sugar). Evaporated milk is less sweet than condensed milk, so sugar is typically added to the tea. siu dai means less sugar. So, say it with me, teh si siu dai means tea with evaporated milk and less sugar.
[end digression]

The drinks stall uncle found that his current can of evaporated milk ran out. (In Singapore, “uncle” can be used to address any male who’s older than you. If you really want to get into the Singapore mode of speech, pronounce it as “unk-uh”, without curling your tongue to form the “l” sound.) So he brought out a new can of evaporated milk, and what appeared to be an ice pick.

He placed the sharp end of the ice pick at one edge of the top of the can. He held it in place with one hand, then with his other hand, hammered the handle of the pick, and bored a hole into the can in one smooth motion.

My first thought was, “I would have used a can opener”.

Before I could finish that thought, the uncle took that ice pick, placed the sharp end at the other edge side of the can, and bored a hole there.

Tin can with holes by ice pick (top view)

My next thought was, “Why did he bore that second hole?”

Before I could finish that thought, the uncle held the can up and half-filled a glass with evaporated milk.

In that instant, I answered my own question. It was to let the air flow in, so a vacuum wasn’t created, so the evaporated milk would pour out smoothly.

Side view of bored tin can

And immediately after I got that insight, questions surfaced. “Why didn’t they do it for ketchup bottles? Why are ketchup bottles designed the way they are? Ketchup’s viscous, so why a small opening to make your life miserable? Why… oh thank you uncle.” My teh si siu dai was ready, and I took my drink back to my seat.

Well, I was never much of a condiment kind of guy, so ketchup bottles didn’t bother me much. Then again, in these modern times, I don’t see ketchup bottles in use any more. But just in case

today’s whippersnappers, lacking experience with this task in the home environment, need instruction all the more if they are to avoid a public faux pas and consequent humiliation

I will refer you to a definitive guide on ketchup decantation.

That’s it. Unless you want to know about the different variations of tea in Singapore…

Image rotation with bilinear interpolation and no clipping

I wrote an article on image rotation with bilinear interpolation to calculate the pixel information. Commenter Jahn wanted to know if the resulting image could be obtained without clipping. I replied with the required code changes (it was fairly minimal), then I thought it might be useful to you too.

The code was cleaned up a bit, and here it is:

const int cnSizeBuffer = 20;
// 30 deg = PI/6 rad
// rotating clockwise, so it's negative relative to Cartesian quadrants
const double cnAngle = -0.52359877559829887307710723054658;
// use whatever image you fancy
Bitmap bm = new Bitmap("slantedtreetopsky.jpg");
// general iterators
int i, j;
// calculated indices in Cartesian coordinates
int x, y;
double fDistance, fPolarAngle;
// for use in neighbouring indices in Cartesian coordinates
int iFloorX, iCeilingX, iFloorY, iCeilingY;
// calculated indices in Cartesian coordinates with trailing decimals
double fTrueX, fTrueY;
// for interpolation
double fDeltaX, fDeltaY;
// pixel colours
Color clrTopLeft, clrTopRight, clrBottomLeft, clrBottomRight;
// interpolated "top" pixels
double fTopRed, fTopGreen, fTopBlue;
// interpolated "bottom" pixels
double fBottomRed, fBottomGreen, fBottomBlue;
// final interpolated colour components
int iRed, iGreen, iBlue;
int iCentreX, iCentreY;
int iDestCentre;
int iWidth, iHeight;
int iDiagonal;
iWidth = bm.Width;
iHeight = bm.Height;
iDiagonal = (int)(Math.Ceiling(Math.Sqrt((double)(bm.Width * bm.Width + bm.Height * bm.Height)))) + cnSizeBuffer;

iCentreX = iWidth / 2;
iCentreY = iHeight / 2;
iDestCentre = iDiagonal / 2;

Bitmap bmBilinearInterpolation = new Bitmap(iDiagonal, iDiagonal);

for (i = 0; i < iDiagonal; ++i)
{
	for (j = 0; j < iDiagonal; ++j)
	{
		bmBilinearInterpolation.SetPixel(j, i, Color.Black);
	}
}

// assigning pixels of destination image from source image
// with bilinear interpolation
for (i = 0; i < iDiagonal; ++i)
{
	for (j = 0; j < iDiagonal; ++j)
	{
		// convert raster to Cartesian
		x = j - iDestCentre;
		y = iDestCentre - i;

		// convert Cartesian to polar
		fDistance = Math.Sqrt(x * x + y * y);
		fPolarAngle = 0.0;
		if (x == 0)
		{
			if (y == 0)
			{
				// centre of image, no rotation needed
				bmBilinearInterpolation.SetPixel(j, i, bm.GetPixel(iCentreX, iCentreY));
				continue;
			}
			else if (y < 0)
			{
				fPolarAngle = 1.5 * Math.PI;
			}
			else
			{
				fPolarAngle = 0.5 * Math.PI;
			}
		}
		else
		{
			fPolarAngle = Math.Atan2((double)y, (double)x);
		}

		// the crucial rotation part
		// "reverse" rotate, so minus instead of plus
		fPolarAngle -= cnAngle;

		// convert polar to Cartesian
		fTrueX = fDistance * Math.Cos(fPolarAngle);
		fTrueY = fDistance * Math.Sin(fPolarAngle);

		// convert Cartesian to raster
		fTrueX = fTrueX + (double)iCentreX;
		fTrueY = (double)iCentreY - fTrueY;

		iFloorX = (int)(Math.Floor(fTrueX));
		iFloorY = (int)(Math.Floor(fTrueY));
		iCeilingX = (int)(Math.Ceiling(fTrueX));
		iCeilingY = (int)(Math.Ceiling(fTrueY));

		// check bounds
		if (iFloorX < 0 || iCeilingX < 0 || iFloorX >= iWidth || iCeilingX >= iWidth || iFloorY < 0 || iCeilingY < 0 || iFloorY >= iHeight || iCeilingY >= iHeight) continue;

		fDeltaX = fTrueX - (double)iFloorX;
		fDeltaY = fTrueY - (double)iFloorY;

		clrTopLeft = bm.GetPixel(iFloorX, iFloorY);
		clrTopRight = bm.GetPixel(iCeilingX, iFloorY);
		clrBottomLeft = bm.GetPixel(iFloorX, iCeilingY);
		clrBottomRight = bm.GetPixel(iCeilingX, iCeilingY);

		// linearly interpolate horizontally between top neighbours
		fTopRed = (1 - fDeltaX) * clrTopLeft.R + fDeltaX * clrTopRight.R;
		fTopGreen = (1 - fDeltaX) * clrTopLeft.G + fDeltaX * clrTopRight.G;
		fTopBlue = (1 - fDeltaX) * clrTopLeft.B + fDeltaX * clrTopRight.B;

		// linearly interpolate horizontally between bottom neighbours
		fBottomRed = (1 - fDeltaX) * clrBottomLeft.R + fDeltaX * clrBottomRight.R;
		fBottomGreen = (1 - fDeltaX) * clrBottomLeft.G + fDeltaX * clrBottomRight.G;
		fBottomBlue = (1 - fDeltaX) * clrBottomLeft.B + fDeltaX * clrBottomRight.B;

		// linearly interpolate vertically between top and bottom interpolated results
		iRed = (int)(Math.Round((1 - fDeltaY) * fTopRed + fDeltaY * fBottomRed));
		iGreen = (int)(Math.Round((1 - fDeltaY) * fTopGreen + fDeltaY * fBottomGreen));
		iBlue = (int)(Math.Round((1 - fDeltaY) * fTopBlue + fDeltaY * fBottomBlue));

		// make sure colour values are valid
		if (iRed < 0) iRed = 0;
		if (iRed > 255) iRed = 255;
		if (iGreen < 0) iGreen = 0;
		if (iGreen > 255) iGreen = 255;
		if (iBlue < 0) iBlue = 0;
		if (iBlue > 255) iBlue = 255;

		bmBilinearInterpolation.SetPixel(j, i, Color.FromArgb(iRed, iGreen, iBlue));
	}
}
bmBilinearInterpolation.Save("rotatedslantedtreetopsky.jpg", System.Drawing.Imaging.ImageFormat.Jpeg);

In the original article, the resulting image had the same dimensions as the source image. This meant that after rotation, some pixel information would be lost. To counter that, we make the dimensions of the resulting image “large enough”.

Considering that this is a rotation operation with an arbitrary angle, when all possible rotations of the source image are placed on top of one another, you get a smudgy circle. Not sure if you can visualise that. So the resulting image must be able to contain a circle, and because our images are stored in rectangular format, we need a square. That’s why in the code, I didn’t use a separate X and Y component of the dimensions, because they’re the same.

The width of the square has to be, at the minimum, the diagonal of the source image (which you can use Pythagoras’ Theorem to calculate). I added a buffer (of 20 pixels) to ensure that our square can contain the resulting image.

Here’s the source image:
Slanted treetop and sky

And here’s the resulting image:
Rotated slanted treetop with sky

Alright, have fun with the code.