Here is a quick look at the games I’ve been playing recently. I’ll try to post some updates on progress now and then.
This time around, I finished Metal Gear Solid (1998), some World of Warcraft, Destiny 2, and Diablo 3.
Metal Gear Solid (1998)
I finally completed Metal Gear Solid after many deaths from the Metal Gear mech and the fist fight with Liquid Snake.
MGS is a game I watched being played and beaten. Though it’s been so long I could only remember pieces of it. Needing to transform the card key between rooms was one of those pieces, though I thought it was in a different game in the series.
Finishing it myself was gratifying. There were a lot of frustrating and tiring moments. I have the Metal Gear Solid HD Collect (MGS 2, 3, Peace Walker) to finish at some point.
World of Warcraft
I jumped on WoW and ran an instance of “The Motherlode!”, which was interesting experience with other players. I was playing with a new tank (but not new to the instance) and unfortunately I kept aggroing mobs when I chased bubbles from the Azerite armor talent Secrets of the Deep. I may need to switch that out. The dungeon map for “The Motherlode!” is pretty open with branching pathways. If you meander or lag behind, it’s easy to accidentally pull enemies. So far this is my least preferred Dungeon, strangely, this makes me want to play it more so that I will learn how to do it right. My favorite Dungeon so far is Waycrest Manor.
Global Game Jam 2018 took place at the end of January. It was hosted locally here in Sacramento at Square One Clubs. I participated as much as I could, but unfortunately I had some family obligations that prevented me from making it down to the site.
For my game, I created a first person puzzler in a 3D world. I used it as an opportunity to get more familiar with Blender and Unreal Engine. With this being a learning experience, I didn’t get as far as I would with tech I was already familiar with. Overall, it felt like a success. I am now much more comfortable making shapes and applying textures in Blender. I also learned what is lost while exporting a blender project into Unreal, such as shaders that are closely tied to the rendering engine.
Alright, so lets take a peek and go over some stuff!
My game takes place in a testing grounds with various platforms and standing orbs. In this first iteration, it takes place in space, but after building out the platforms that idea didn’t seem to pan out so well, for reasons I’ll go into shortly. The skybox texture was created using a premade texture from SpaceScape, a free tool for creating space skyboxes.
Unreal is a very powerful engine with some very useful tools in prototyping a scene. You can create shapes in the scene on the fly. Unfortunately (at least as far as I know) those custom shapes, known as brushes, are their own object type and I couldn’t find an easy way to convert them into components for reuse. I imagine it’s possible, the answer just wasn’t easily at hand. I created the octagon platforms and the orb with stand using brushes.
I started developing a slots game using the Cocos2d C++ SDK. Cocos2d is an opensource game development framework that support C++, Objective-C, and JavaScript. Eventually, this game will evolve into a slots RPG, like King Cashing and King Cashing 2. For now it uses a pretty generic reels and a match 3 mechanic that matches on the same row as well as cells on adjacent rows. For score, it keeps experience points, since this will transition to the RPG slots.
Source for the project can be found here. As of this writing, it’s in early development.
Like many game frameworks, Cocos2d has many helper functions that allow for quick game prototyping. Scenes are easy to construct, and assets, sprites, and audio can be added using built in Cocos2d objects. It even supports the ability to add custom shaders.
Extending Sprites
I found quickly that I needed to create custom objects that extended sprites. In this project there two classes that extend cocos2d::sprite ; the reel, and the HUD.
Grouping elements within sprites helped with organizing code and separation of concern. I did run into strange memory errors when trying to add certain objects, such as cocos2d::Label , directly to the scene while also having a pointer to it in the scene.
Autorelease
The Cocos2d C++ framework uses a smart-pointer technique to automatically destroy dynamically allocated objects when its internal reference count is 0. This relieves pressure in remembering to destroy objects and worrying about pointer ownership. Though cyclical dependencies still need to be avoided.
The built-in Cocos2d objects are automatically added to the autorelease pool, so there is no need to use the new keyword. In my project, I have an object that extends the Cocos2d sprite. So there’s some boilerplate code that I needed to add so my object would be added to the autorelease pool.
ReelSprite* ReelSprite::create(const std::string& filename, std::vector<int> _cells)
{
ReelSprite* mainSprite = new ReelSprite();
mainSprite->init(filename);
mainSprite->autorelease(); // <-- ADD TO AUTORELEASE POOL
mainSprite->cells = _cells;
return mainSprite;
}
ReelSprite::create is a static method that follows the Cocos2d convention of constructing an object and adding it to the autorelease pool. mainSprite->autorelease() is the line that actually adds the object to the autorelease pool, so that it does not have to be manually destroyed.
I needed a map editor with more features than what I saw included in TILED back in August, so I decided to try my own hand at creating a map editor. It’s just an interactive grid, right? Not quite. At least in the approach I took.
I started writing the tile editor with C++ and SDL. Implementing drag functionality was pretty easy since that was baked in the SDL API, however, I didn’t want to build the UI widgets from scratch. Unfortunately, the existing UIs I found weren’t compatible with SDL, so I had to pivot and use straight OpenGL and matrix math.
Because I was ditching the SDL framework, I had to implement my own drag logic, which is what I will discuss in this post.
Moving Objects with Mouse Picking
I needed the ability to select objects in 3D space, which lead me to a technique called mouse picking. This technique utilizes ray casting, which is how you detect if a line (a ray) intersects with something else.
The article “Mouse Picking with Ray Casting” by Anton Gerdelan helped explain the different planes/spaces and what they represented.
In order to move the objects in 3D space at a distance that matched the mouse movement, I had to transform the coordinates between screen and world spaces. When working with 3D coordinates, there are several spaces or planes that have their own coordinates.
A very simplified list of these spaces are:
Screen Space > Projection (Eye) Space > World Space > Model Space.
Fully understanding the transformation formula was a challenge for me. Normalization and calculating the inverse Projection Matrix tripped me up due to a combination of confusion and erroneous input.
The Solution
Here are some code examples of the final working solution.
Initialization of Projection, View, Model
Projection = glm::perspective(1.0f, 4.0f / 3.0f, 1.0f, 1024.1f);
GlobalProjection = Projection;
CameraDistance = 10.0f;
GlobalCameraDistance = CameraDistance;
View = glm::lookAt(
glm::vec3(0, 0, CameraDistance), // Camera location
glm::vec3(0, 0, 0), // Where camera is looking
glm::vec3(0, 1, 0) // Camera head is facing up
);
GlobalView = View;
Model = glm::mat4(1.0f);
GlobalModel = Model;
MVP = Projection * View * Model; // Matrix multiplication
View to World Coordinate Transformation
// SCREEN SPACE: mouse_x and mouse_y are screen space
glm::vec3 Application::viewToWorldCoordTransform(int mouse_x, int mouse_y) {
// NORMALISED DEVICE SPACE
double x = 2.0 * mouse_x / WINDOW_WIDTH - 1;
double y = 2.0 * mouse_y / WINDOW_HEIGHT - 1;
// HOMOGENEOUS SPACE
glm::vec4 screenPos = glm::vec4(x, -y, -1.0f, 1.0f);
// Projection/Eye Space
glm::mat4 ProjectView = GlobalProjection * GlobalView;
glm::mat4 viewProjectionInverse = inverse(ProjectView);
glm::vec4 worldPos = viewProjectionInverse * screenPos;
return glm::vec3(worldPos);
}
At first this algorithm felt a bit magical to me. There were things going on I wasn’t entirely wrapping my head around, and when I stepped through the algorithm I got lost at the inverse matrix multiplication. In addition, the “Mouse Picking” article normalizes the world space values, which we don’t need.
This is part of a series for the Out of Phase game project that reflects on various stages, covering pros and cons of the creative process and implementation of the game. The first post can be found here.
Deciding what type of game you’re creating can be difficult while the concept is in its infancy. There’s so many ideas you want to use, and many of those may conflict with each other at first, requiring some compromise in order for them to gel
In Out of Phase, there was a struggle to combine puzzle and action mechanics.I was trying to integrate elements from the two genres that I knew were successful by themselves, but incompatible with each other. To resolve this, I had to consider what experience I wanted to give the player and how the mechanics from each genre would contribute to that experience.
Some ideas I had to let go of, as they were too far from the vision, and other ideas had to be reworked to fit the core game concept. Here’s a reflection of that journey.
Puzzles
First I’ll start off with what kind of puzzler this is not, but what I originally thought it was going to be.
Some of my favorite games are point-and-click puzzlers, such as Myst or Escape the Room games such as Crimson Room. In these types of games, the player can progress at their leisure. While there may be action sequences, they usually don’t require interaction from the player, though there are some exceptions where the player must make a timed decision during the action sequence. The timing is typically pretty lax however.
Games like Myst, Crimson Room, or even The 7th Guest are what I would consider leisure puzzlers. They are typically slower paced compared to an action game and focus more on immersion and an experience. Death in these games are pretty rare, and are based off of a decision and not action, so the player is given a high level of safety while exploring the worlds. The focus of these games is on immersing the player into a fantasy world, and death or abrupt twitch mechanics tend to draw the player out.
While these games are fun, they weren’t the style I was looking for. Instead, I wanted to go with something more real time and physical, like Maniac Mansion or the more recent Ib. I wanted to give the player a different feeling of tension that they may need to react fast to avoid getting injured or killed, which deviated from leisure puzzlers.
This was a point of conflict in my early design brainstorming, because I liked the pacing and immersion of the leisure puzzlers. However, every time I tried to settle on removing action from the game, it felt incomplete. So I moved onto a different type of puzzler, one which was more physical and time sensitive.
In the first prototype I started with a very basic series of chambers and hallways that contain puzzles. This is what was implemented at the the Global Game Jam, and I had the beginnings of what was like a Portal clone, with pressure plates and objects that could be pushed onto them.
Where it differed from Portal (besides no portals!) is some objects and parts of the maps would be different between the players, in some cases requiring the players to communicate and discover the difference in order to complete the puzzle.
There would be furnished rooms with interactive objects, like a record player that would play music, light switches, and paintings. The player would need to interact with certain objects and in some cases complete a sequence in order to progress through the game.
With these ideas, this game was becoming more like Ib, where core gameplay involved adventuring through the levels and discovery. There would be some action sequences, but the player had to evade dangers, opposed to attacking those dangers.
The game concept already sounded fun, and there were so many possibilities for puzzles. Yet, there were some things that didn’t feel right. I didn’t want the player to be totally defenseless, I wanted to let them fight back. I also needed something that gave the game some replay value after the puzzles were figured out, so my focus began to shift.