Straight Out Of Furlough

So the pandemic is still at large, and despite being one of many employees that had to take a pay cut in March to keep businesses afloat and people with jobs at the company I work for, a number of people had to be furloughed in May as customers remained closed, I myself being one of them. Unfortunately, it’s happening everywhere where it’s non-essential, and the silver lining with mine was that it was only for three weeks (and I had it in writing despite my families concerns). Today is my last day of furlough and tomorrow will be my first day back at work, from home because as far as myself and my company is concerned, whatever excuse the British Government is giving to lifting lockdown is not good enough.

Even so, it feels weird to think about getting back to work. Getting into routines is a slow process for me in general, working from home daily basis took a while for me to adjust to, as well as the first week of furlough. I just hope my first week back at work will be smooth, I’ve worked in games programming for years so it’s just a case of not getting rusty.

So what have I been doing in May? Well, the week before furlough started I wanted to practice some personal game development by working on a clone of a game genre I usually don’t approach, puzzle games. I decided on Tetris since it’s the most popular, and I remember back when writing games in XNA trying (and failing) to make a Tetris clone, so it would be a good progressive piece.

Using the Vigilante Framework, I didn’t have any source code to go off of, although I did use the official Design Guidelines from Tetris Holdings so despite going with the visual aesthetic of the earliest known build of Tetris (written by Alexey Pajitnov for the Electronika60 computer) it functions as a modern version of Tetris with hold-pieces and fast drop.

Collision handling was the most tedious part of developing the game. It was built using tilemap objects, with the main game board using a large tilemap while the individual pieces use a separate tilemap object of either a 4×4 or 5×5 grid. The built-in collision system couldn’t be used as the horizontal and vertical movement had to behave separately, so collision handling and response had to be handwritten.

Every movement and rotation had to be checked for collisions by seeing if a block from the piece overlaps with a block from the board (including walls and the floor). If there is an overlap, the piece moves and collision is checked once again. In most cases, the first response is to move horizontally before doing any future collision checks, which the response would be to move upward until there are no more collision responses. The only exception is if the piece goes downwards, where collision is only checked vertically before putting the piece in a momentary lock state followed by setting the piece on the board and generating a new piece. It was a tedious case of trial-and-error to get right as you had to deal with pieces getting stuck in places they shouldn’t be.

bool Tetramino::AlignH()
{
sf::Vector2i dir = PiecePos - LastPiecePos;
for (int y = 0; y < mapHeight; y++)
{
for (int x = 0; x < mapWidth; x++)
{
char pieceTile = GetTileID(x, y);
char boardTile = board->GetTileID(PiecePos.x + x + 1, PiecePos.y + y);
if (pieceTile == '#' && (boardTile != '.' && boardTile != -1))
{
if (dir.x < 0) //Left
{
MoveH(true);
while (AlignV()) {}
return true;
}
else if (dir.x > 0) //Right
{
MoveH(false);
while (AlignV()) {}
return true;
}
else //Assume Rotation
{
if (x < 2) //Left-Side
{
MoveH(true);
while (AlignV()) {}
return true;
}
else //Right-Side
{
MoveH(false);
while (AlignV()) {}
return true;
}
}
}
else if (pieceTile == '#' && PiecePos.x + x + 1 == 0)
{
MoveH(true);
while (AlignV()) {}
return true;
}
else if (pieceTile == '#' && PiecePos.x + x + 1 == 11)
{
MoveH(false);
while (AlignV()) {}
return true;
}
}
}
return false;
}
bool Tetramino::AlignV(bool landing)
{
sf::Vector2i dir = PiecePos - LastPiecePos;
for (int y = mapHeight - 1; y >= 0; y--)
{
for (int x = 0; x < mapWidth; x++)
{
char pieceTile = GetTileID(x, y);
char boardTile = board->GetTileID(PiecePos.x + x + 1, PiecePos.y + y);
if (pieceTile == '#' && (boardTile != '.' && boardTile != -1))
{
MoveV(false);
return true;
}
if (landing) //Check the tiles underneath the piece are either blocks or the floor before confirming a land.
{
char boardTileBelow = board->GetTileID(PiecePos.x + x + 1, PiecePos.y + y + 1);
if (pieceTile == '#' && (boardTileBelow != '.' && boardTileBelow != -1))
{
return true;
}
}
}
}
return false;
}

Another fun challenge was the RNG for setting the new pieces. The early versions of Tetris had completely randomised pieces, the equivalent of picking a number between 0 and 6 on every turn. More modern versions of Tetris instead use what’s known as either a 7-bag approach, where you’d imagine all seven pieces are jumbled in a bag and then picked out one-by-one until the bag is empty and you repeat the process.

I could have achieved this simply by having an array of numbers from 0 to 6, and shuffle them every at the end of every seventh move, however, I also wanted to have a visual display of what the next piece would be. To make the preview appear seamless, I instead had an array of 14, initialised so it would count up from 0 to 6 twice. These would then get shuffled separately so the jumbled sequence would appear to remain as two bags of seven pieces, then the game would use the first bag of pieces until the seventh move, where the second bag would get used (and the first bag reshuffled), then on the 14th move the first bag would be used and the second bag is reshuffled.

bagPos = (bagPos + 1) % 14;
if (bagPos == 7)
{
//Rebag the first seven pieces.
for (unsigned int i = 0; i < 7; i++)  {   bagRot[i] = VGlobal::p()->Random->GetInt(3);
}
for (unsigned int i = 0; i < 100; i++)  {   unsigned char a = VGlobal::p()->Random->GetInt(6);
unsigned char b = VGlobal::p()->Random->GetInt(6);
if (a == b)
continue;
bag[a] = bag[a] ^ bag[b];
bag[b] = bag[a] ^ bag[b];
bag[a] = bag[a] ^ bag[b];
}
}
else if (bagPos == 0)
{
//Rebag the second seven pieces.
for (unsigned int i = 0; i < 7; i++)  {   bagRot[i + 7] = VGlobal::p()->Random->GetInt(3);
}
for (unsigned int i = 0; i < 100; i++)  {   unsigned char a = 7 + VGlobal::p()->Random->GetInt(6);
unsigned char b = 7 + VGlobal::p()->Random->GetInt(6);
if (a == b)
continue;
bag[a] = bag[a] ^ bag[b];
bag[b] = bag[a] ^ bag[b];
bag[a] = bag[a] ^ bag[b];
}
}

After getting feedback from Quang from Asobi.tech (for his experience as a game developer and competitive Tetris player), I published the game on the 5th May under the name RECTRIS (REcreated teTRIS) and it went pretty well with Tetris players. There were a few audio issues that I, unfortunately, couldn’t iron out because of how streamed audio works in OpenAL, but I’m very happy with how it came out.

Not much else I can really say in terms of game development, as I’ve mostly preoccupied my time with some personal writing, nature walks, preparing for and presenting a talk on old anthropomorphic art, and playing lots of classic arcade games (and Animal Crossing) on the Nintendo Switch.

At least, until earlier this week when I wanted to start something new. I’ve planned it out (somewhat) to be a CAVE style vertical scrolling shmup, since as much as I fear my game development career will be one of the retro arcade game devs, I feel as though out of all the kind of shmups I make (from twin-stick shooters, tube shooters, and run ‘n guns), the last time I probably worked on a new scrolling shmup was the very first game I made (unless you count its remake). How well it’ll all go, it’s hard to say now that I’ve only been working on it for a week.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s