Game Design, Programming and running a one-man games business…

Gratuitous Space Shooty Shield tweaks

Maybe there is an easier way to code this, and I’m sure 99% of devs would just unity and copy paste some asset store effect, but I like to code these things myself becausae I like the intellectual challenge, and it gives you total freedom and zero dependencies so here we go:

I just finished coding a ‘shield impact effect’ for Gratuitous Space shooty game, and thought I’d explain how its done. The idea is that the ships are surrounded by energy fields, with various grid patterns, and when laser bullets hit them, they generate a sort of encapsulating ‘wash’ effect over that portion of the shield. To pull this off, I need two things: It needs to be clear where the impact hit the ship, and it needs to feel like it wraps around the target in 3D.

How do you do that in a 2D engine?

The first challenge is getting a graphic that looks like a ship shield in 3D. This is easy. You just get any pattern you want, such as a hex-link pattern:

Then you use the photoshop sphereize distortion filter to give you a nice alpha channel map of a cool space shield:

If I just blap that on top of a spaceship thats hit by lasers, you get a nice effect, but the problem is its non directional, and its pretty clear that its just one image placed on top of another. What I need to do, is to draw this image, but only bits of it at a time, and to wash over the image, revealing it over time.

The way I found was best to do this was to enlarge that texture a lot so it had a ton of empty space, and then use it at twice the size. The reason for this will become apparent, but I’m not working with this:

What I then do, for my shield effect is to use a sort of 2D mesh to wrap around that image over time. That wrap-around effect will originate from the point where the bullet hits the shield. To do this, I get the bullet position, then get the angle from shield center to bullet center, and travel the exact shield radius along that angle from the shield center. That gets me an EXACT location on the perimeter of the shield, even if the bullet is super fast and has already moved inside the perimeter. This is important!

So I now have the impact location, and I want to kind of ‘wash’ the image of the sphere into the players view over time like this:

The red dot is the impact point, the yellow rectangle gets thicker over time as it washes over the sphere. To do this, I ‘virtually’ place the sphere texture on the screen, but do not render it. Its there just as a placeholder for where the ‘full’ sphere would be if it was all revealed. I then calculate how to position that rectangle, and over time I stretch it so it completely covers the target sphere. In order to render the composite image, I render the yellow rectangle, but when I get the UV values of each vertex in the rectangle, I actually look up where they are in relation to my oversized virtual sphere and use that UV value, but the texture of the sphere.

This is why I had to enlarge the canvas the sphere is on, so that those UV values make sense and are still 0 to 1 in both directions. When I do this, I get a cool effect, but its basically a watermelon (shown here with early rubbish shield texture)

To fix that, I just need to set the color values of the ‘inner’ vertices at the top of the rectangle to be fully transparent, and I then get a nice fade effect over the sphere. To be honest, that looked fine, and I was happy with it, but even though NOBODY would notice, something annoyed me…

When you imagine something wrapping around a sphere like this, from a top-down view, you would notice that the speed at which the ‘frontier’ washes over the sphere is non-linear. In other words, I’m not taking any account of the curvature of the sphere itself. To do things right, the speed at which that rectangle washes overs the sphere should vary along the length of the top of the rectangle. To do THAT, I need the rectangle to actually be a tri-strip, so I can curve the top edge. And not only that, the speed at which the center point of that edge moves needs to be a nice curve defined as a sine wave…

In other words I need to achieve this:

That wireframe thing is my yellow triangle. Over time it will wash over the whole shield right to the back. here is two overlapping impacts:

You can see whats going on quite easily in the wireframe version there. The game is way too fast and gratuitous for anyone to notice, and TBH I am still tweaking it.

One of the really fiddly bits was getting that curve just right. I had to start at the left hand top of my yellow triangle, and go across the top edge, noting my progress along there. I then interpolated between the extent to which the ‘height’ of the rectangle came from a fixed point (I picked the top left at the ‘back’ of the shield’) or from my sine-eave inspired non-linear curve of the point that represented the center of the rectangle.

At the end of all that I then basically have a single tri-strip. I then run some separate code to derive UVs from the ‘virtual’ sprite, and just render it. For those who care about the details, here is the code for what I’ve described:

void GUI_ShieldRipple::CalculateVerts()
{
	//place relative to the shield
	float radius = PParent->GetGlowSprite()->Width / 2;

	//we have a tristrip here that is angled at RippleAngle and whose bottom center is at
	//InitialImpactOffset from the current shield center;
	float shieldcenx = PShip->GetWorldPosition().X;
	float shieldceny = PShip->GetWorldPosition().Y;

	//derive bottom center
	float radangle = D3DXToRadian(RippleAngle);
	float cosangle = COS(radangle);
	float sinangle = SIN(radangle);
	//note this is the inverse because the angle is from offset to cen and we want the reverse
//	WorldPos.X += (sinangle * ourspeed);
//	WorldPos.Y -= (cosangle * ourspeed);

	float botcenx = shieldcenx - (sinangle * radius);
	float botceny = shieldceny + (cosangle * radius);

	//now turn 90 degrees to the left to go to the start
	float angletostart = RippleAngle - 90;
	if (angletostart < 0) angletostart += 360;
	radangle = D3DXToRadian(angletostart);
	cosangle = COS(radangle);
	sinangle = SIN(radangle);

	float botleftx = botcenx + (sinangle * radius);
	float botlefty = botceny - (cosangle * radius);

	//invert for right
	float botrightx = botcenx - (sinangle * radius);
	float botrighty = botceny + (cosangle * radius);

	//how wide is each block of the tri-strip
	int prims = (MAXVERTS - 2);

	float chunkwidth = PParent->GetGlowSprite()->Width / prims;

	//now deduce chunkheight, which is basically our travel over the shield at the center point
	//we have non linear progress here, and we deduce that from a cosine wave curve imagined from 
	//Pi to 2xPi
	float cosinput = D3DX_PI + (D3DX_PI * Progress);
	float adjusted_progress = cos(cosinput);
	//now convert to 0 to 1 instead of -1 to 1
	adjusted_progress = (adjusted_progress + 1) / 2;

	//this gives us the center value, of the middle of the sphere
	float chunkheight = radius * 2 * adjusted_progress;

	//get top of the strip above the botleft
	radangle = D3DXToRadian(RippleAngle);
	cosangle = COS(radangle);
	sinangle = SIN(radangle);

	float innerx = botleftx + (sinangle * chunkheight);
	float innery = botlefty - (cosangle * chunkheight);

	//start botleft then up, then to 1 along and down...then up
	float currx = botleftx;
	float curry = botlefty;
	
	float offsetx = (botrightx - botleftx) / (prims/2);
	float offsety = (botrighty - botlefty) / (prims/2);

	unsigned long fullcolor = RGBA_MAKE(20, 234, 221, (int)(255.0f * Intensity));
	unsigned long nocolor = RGBA_MAKE(20, 234, 221, 0);
	for (int n = 0; n < MAXVERTS; n+=2)
	{
		Verts[n].dvSX = currx;
		Verts[n].dvSY = curry;
		Verts[n].color = fullcolor;

		//to get innerx we need to adjust between the full corner value and the middle adjusted
		//value based on our progress
		float progress = n / (float)prims;
		//convert that progress to a nice curve
		progress = sin(progress * D3DX_PI);

		//thats the extent to which we get the adjusted value rather than the base value of   chunkheight being radius
		float newheight = (radius * 2 * (1.0f - progress)) + (chunkheight * progress);
		float newinx = currx + (sinangle * newheight);
		float newiny = curry - (cosangle * newheight);

		Verts[n + 1].dvSX = newinx;
		Verts[n + 1].dvSY = newiny;
		Verts[n + 1].color = nocolor;

		currx += offsetx;
		curry += offsety;
	}
}

Its a lot of C++ to just generate that effect, but I love coding this stuff. I might change it so that the top left and right points of that rectangle are in fact the mid point, so the curve goes flat then inverts. I think that may look better. Also I’ll fiddle with render states and lightmaps to make it fizz more. In the meantime I uploaded a video to twitter showing it with, and without the wireframe. There is a lot going on, and there can be multiple overlapping effects at once. Thats another reason I needed to use a virtual shield texture for UVs, this way everything lines up perfectly, even with multiple impacts:

Gratuitous Space Shooty Game will be on sale on itch soon

Your corporate communication policy is bollocks

I’ve had the misfortune recently of having to deal with two gargantuan and completely terribly run clusterfucks of inefficiency recently here in moving my mothers TV network from one company to the other. I will not bore you with the details, but the process of quitting company A and enabling company B was, at a conservative estimation, 100x as difficult, and costly (to them) as it should be assuming a simple baseline of a competently run company.

At the same time, I am also running my little one man games business, AND overseeing the construction of a solar farm, which involves at least 5 different companies including mine. Having all this happen at once makes for an interesting comparison, and its even more interesting to do this while I’m also reading the Elon Musk biography, with lots of discussion of Twitter/SpaceX/Tesla etc.

Its no great revelation that big companies are often run badly. I think the situation has got worse, especially in the UK (or is that just the western world in general?) in recent years. We seem to have completely forgotten about free markets and competition, and the role of governments in ensuring that oligopolies and monopolies do not exploit their market position, and this has made things much much worse.

Having dealt directly with both google and facebook, I can tell you both companies are incredibly obtuse, bureaucratic disasters with absolutely no idea how to do anything any more. Its impossible to communicate with them, or get anything fixed. From the point of view of a customer (in this case for ads), both companies seem 100% computer-run, with no humans at all. At any level.

Running a company is about efficient communication. Companies that cannot communicate with their employees, or customers degenerate into inefficiency. The most obvious example is when something is fundamentally broken, but you, as the CEO of a company do not know about it. With my mothers TV, there was a simple mistake on their part. They had set a limit of phone call costs at which point the TV and phone would be disconnected. You could pay the costs to re-enable the account, but their website failed if you tried to pay in the first month, before the first bill. So there was an infinite loop on non payment, and non-connection.

In any sane organization, there would be actual humans who would respond, understand the problem, and fix it immediately. Instead it took 3 days and about 6 hours of my time, numerous furious phone calls, dozens of emails, a complaint to the government regulator, you name it. I was not a happy customer… But me being an extremely angry customer, and therefore the complete destruction of any potential goodwill is only 1% of the cost to the company. They now have to respond to, and deal with an official complaint with a government regulator. They have tried to call me to apologize 6 times, and left messages and sent emails. This has taken them time, effort, money. In theory the regulator could fine them, costing even more time, effort, money.

The fix is obvious, but unfashionable. You simply hire staff to deal with complaints. The obvious rebuttal is that this would cost money, but the cost of NOT dealing with a complaint will always be higher than the cost of dealing with one.

The compounding error for large organizations is that even when their staff do communicate with customers, they are not empowered to do anything. Nobody you ever speak to on a phone call to a large company has any discretion, authority or power to do anything. They have to fob you off with excuses or use tools like long wait times, annoying hold music and no ringback, and infuriating AI chatbots pretending to be typing… all to hide the fact that even if you get to talk to a human, they cant fix the problem.

My email address is cliff at positech dot co dot uk. I make no secret of it, and give it freely to people who ask. I don’t put ANY barriers between me and people who want to talk to me. Its my decision whether I reply, and some emails I do decide not to respond to, but the idea of making it hard for customers and potential customers to contact you, when you SELL things, is just insane. Amazingly, I am not overwhelmed by email, nor annoyed by it. Many, many times, I have got emails that have been VERY helpful, or profitable, that I may never have got if I tried to screen my contact with the general gaming public.

One of the best sizes for a company is one person. This doesn’t just mean ‘smaller than 2’, its an actual qualitative difference. There is something special about a company where every role is handled by the same person. My game designer and game programmer and engine programmer and QA lead are all me. That means we can have a 4 way meeting to evaluate the potential of a idea while I’m making coffee. We communicate between us at the speed of thought, and there are no egos. Sometimes the game designer gets their way, sometimes the QA dude. Its all me.

This is absolutely fundamental, and something very few people seem to really understand. Going from a one person team to a two-person team not only doesn’t make things much faster, it may actually make everything slower. Maybe much slower. Even if there are no egos and both people get along, and they never interrupt each other and never disagree, communication moved from the speed of thought to the speed of speech, or typing. Incredibly slow.

Communication between everyone working towards a common goal is absolutely vital. The idea that people in dept A cannot talk to people in dept B is lunacy. Strict unskippable hierarchies and org-charts are nuts. In so many companies, barriers to internal and external communication are put up by managers who want to be left alone. The impact on the company’s success is colossal, but individual managers in huge megacorps are not invested in the success of the business so rarely care.

It should, in theory be absolutely impossible for a single individual like me to commercially survive making and selling video games when huge companies like blizzard and epic exist. And yet here I am. I think a huge part of this is due to the huge huge gains in efficiency when a company is basically a single individual. It MIGHT be possible to replicate it with an extremely driven, loyal team of super-like-minded people led by someone who is super-inspiring, but thats amazingly rare. Maybe space/tesla are one example.

So whats the conclusion? Corporate communication is critical. Its almost impossible for it to work in large companies, unless you fight hard to ensure information flows freely. If you are thinking of growing your company think very hard if thats the right decision. And communication with customers is vital. You want to hear when there is a problem immediately, so its fixed immediately. Putting barriers between you and your customers is insane.