Jump to content
  • Advertisement
  • entries
    9
  • comments
    0
  • views
    598

About this blog

After completing my little Pong project, I'm moving forward to trying my hands on a "Snake" clone.  I'll be trying out SFML for managing windows, graphics, input, and sound.  The graphics and gameplay will be simple to start, and then I plan to go through a number of improvements to make it feel semi-professional (as much as I'm capable on my own!).  As I build it, I'll keep all the source code over on my GitHub account.  And of course, I'll blog about my experiences here along the way!

Entries in this blog

 

Snake - Loading assets asynchronously

Since my last update, I've primarily been working on a way to load all the assets of a level in an asynchronous manner.  Having the assets get loaded into memory (or the GPU for textures) in a separate thread allows the game to display the loading status while the player waits.  This obviously isn't terribly important for a game like this, where there will only be a dozen or so assets being loaded.  But I wanted to work through it for my own curiosity and for future reference. When you start a "campaign" (ie. story mode) in The Garden of Eating, or when it loads assets for a new level, the loading screen above will appear for a moment. This is how I have it working at a high level: The game loop keeps track of the current status (or mode) as it pertains to loading the game assets for a level.  I use an enum class that progresses from "LOAD_CAMPAIGN" immediately into "LOAD_LEVEL", and then "WAIT_TO_START" where we start accepting user input. During the "LOAD_LEVEL" mode, the "update" phase of the game loop queries the asset loader for its status. If the asset loading status indicates it's still loading, the mode stays at "LOAD_LEVEL". During the "LOAD_LEVEL" mode, the "render" phase of the game loop also queries the asset loader for status information.  It receives back additional information about how far along it is, and which file is currently being loaded. When the main loop (which is running in the main thread) queries the status, it needs to briefly block access to the status variables using a mutex.  I used the SFML Thread and Mutex classes for this, but I guess I could've used the C++ STL classes that were introduced in C++ 11. I'm quite happy with the results at this stage, though I did have one interesting gotcha moment. I was keeping the list of floor textures and barrier textures each in their own std::unordered_map<int, sf::Texture> structure.  At first I had code that looked like so: void StoryLevelAssetBundle::loadFloorTextureMap(const StoryMapDefn& mapDefn) { for (auto const& currFloorDefnPair : mapDefn.floorDefnMap) { this->indicateLoadingFilename(currFloorDefnPair.second.filename); sf::Texture currFloorTexture; if (currFloorTexture.loadFromFile(r3::snake::StoryLoaderUtils::resolveImageFilePath(this->campaignFolderName, currFloorDefnPair.second.filename))) { currFloorTexture.setRepeated(true); this->floorTextureMap[currFloorDefnPair.first] = currFloorTexture; this->incrementLoadedAssetCount(); } else { this->failedFilenameList.push_back(currFloorDefnPair.second.filename); } } } The line that copies the texture from the "currFloorTexture" variable into the map was causing a peculiar warning message to show up in the console: An internal OpenGL call failed in Texture.cpp(98). Expression: glFlush() Error description: GL_INVALID_OPERATION The specified operation is not allowed in the current state. When I first encountered this message, I was just testing the "StoryLevelAssetBundle" class directly -- outside of the game running.  So I assumed it was because I didn't have a valid OpenGL context created.  But the message persisted once I'd hooked it into the game. Eliminating the local "currFloorTexture" variable, and instead loading directly into the map, prevented this: void StoryLevelAssetBundle::loadFloorTextureMap(const StoryMapDefn& mapDefn) { for (auto const& currFloorDefnPair : mapDefn.floorDefnMap) { this->indicateLoadingFilename(currFloorDefnPair.second.filename); this->floorTextureMap[currFloorDefnPair.first] = sf::Texture(); if (this->floorTextureMap[currFloorDefnPair.first].loadFromFile(r3::snake::StoryLoaderUtils::resolveImageFilePath(this->campaignFolderName, currFloorDefnPair.second.filename))) { this->floorTextureMap[currFloorDefnPair.first].setRepeated(true); this->incrementLoadedAssetCount(); } else { this->failedFilenameList.push_back(currFloorDefnPair.second.filename); } } }   When I looked around for information on this message, I kept coming across the acronym RAII, which stands for Resource Acquisition Is Initialization.  This is one of those things that I feel like I understand conceptually, but then when I read articles or people's opinions on how to use it properly in C++, I start to get confused.  I'll see people saying that there's no need to ever use "new" and "delete", for example.  I still use it, for example, to store a pointer to the current "StoryLevelAssetBundle" above in my game loop.  I feel like that class has enough state that I want it to get allocated on the heap, rather than the stack.  Am I just a dinosaur who remembers too much what it was like working with the 8086 memory segments and 64kb stack size?  Heh...

rileyman

rileyman

 

Snake - Map and level loaders

Not a particularly exciting update, but since it's been a week since my last post I felt the need to check in.  I've completed the initial version of the JSON loaders for map files and campaign / level files.  I wanted it to be easy for myself (and anyone else looking to create their own levels) to diagnose problems in their map / campaign files.  So I spent a fair bit of time writing code to validate that a map or level is defined properly.  Inside the game itself, if you try loading a level that isn't correctly defined, it'll just show a general loading error but refer you to a log file.  The log file will have all the details about what properties are missing, are of the wrong type, or have values that are out of bounds, etc. One thing I keep messing up when writing *.json files is forgetting to enclose property names in quote characters.  I'm so used to coding in JavaScript / TypeScript in my day job, where you don't have to! Having these loaders in place may not make for a particularly exciting update, but it was certainly necessary for getting an easily modifiable story mode going in the game!  This upcoming week, I'll finally be able to get started on the game engine for the story mode. 😀

rileyman

rileyman

 

Snake - Story mode design, and Json parser woes

I started up a wiki over on my GitHub account describing the story mode design for The Garden of Eating.  I'm looking forward to implementing all of that, but the first step in getting there is dealing with the file system and JSON parsing. I was originally thinking I'd just have a "campaigns" folder, where each sub-folder would represent a campaign that the player would pick somewhere on the menu.  I quickly realized that C++ is still rough around the edges in supporting certain file system operations -- like enumerating the list of files or folders at some given path: I was hesitant to use the new std::filesystem.  So far, my project is C++11 compliant; this namespace was added in C++17. I was hesitant to use this Boost library I'm reading so much about.  It's another thing to learn, and to try to build or integrate into my project.  Maybe next time... I don't want to use operating-specific calls.  I still have the thought that it would be nice to be able to take all my code and build it for a Mac or Linux machine. So instead of enumerating the sub-folders, I'm just going to require that there be a "campaigns-list.json" file found in the "campaigns" folder.  This file must be a JSON array of strings, listing the folder names.  It seemed like a small price to pay, in order to keep my dependencies to a minimum, and to keep my build environment flexible. And then I started trying out some of the various free / open source C++ Json libraries out there... Nlohmann JSON for Modern C++ I first tried the latest version of Nlohmann JSON for Modern C++.  At a glance, it seemed like the simplest library to get going.  It has a single header file you can include in your project, and away you go.  Unfortunately, I struggled to get it to parse a JSON file, and felt the need to try a different library.  This stackoverflow question didn't solve my issue either.  The library's approach for parsing JSON from a file has been deprecated and changed multiple times over the library's short lifespan, and trying to use this static "parse" method with the 3.7 version in VS 2019 was just not compiling.  Maybe I'm just a dumb-dumb, but if I can't get something like this to work within a half-hour, it's time to move on. The main readme for the library still shows parsing from a file using the 1.X version approach.  If this could be updated with whatever the 3.7 version is using, I presumably wouldn't have had an issue, and my JSON library evaluation would have ended there.  Unless the 3.7 version is indeed using the static "parse" function, in which case I'd have to be of the belief that not a single person is successfully using this library because I'm always right when I code things. 🤣 // read a JSON file std::ifstream i("file.json"); json j; i >> j; // My note: this was deprecated a long time ago! RapidJSON Next I tried RapidJSON.  I was able to get this to work, but there were a few red flags that pushed me away from it: As of today, the Build Status on their front page is failing for both Linux and Windows.  This didn't instill me with confidence. The licence file mentions the MIT, BSD, and JSON licences.  On further reading, this wouldn't have been an issue, as I wouldn't be including the portion that is under the JSON licence.  But I like to keep things simple. It's a header-only library as well, but there are LOTS of header files.  I wanted to see if I could find something with fewer files. JsonCpp The JsonCpp library is where I've ended up.  To get this integrated into my project, I did have to install Python and run the "amalgamate.py" script.  This produces a distribution folder with two header files, and a single *.cpp source file.  The library's API is just fine for my purposes, and I was able to parse and navigate some sample Json files without any trouble. So, JsonCpp it is.

rileyman

rileyman

 

Snake - The basic game is complete!

As of last night, I managed to complete everything I wanted for the basic game.  The menu system is in place, all the game options are working, there's a full screen option, and I worked through some minor issues that I wanted to address.  Full source code, and the 1.1 release, are available on my GitHub account. That puts me on track to start "phase 2", where I'll be working on a story mode with level progression.  I think I have some neat-o ideas: Different types of fruits / veggies that have different effects on the snake. Each level will have its own design (ie. not just a rectangular playing area) and new types of barriers. A fun / silly story line where Adam has trapped the snake (you!) in his maze, to get revenge for tricking Eve into eating from the tree of knowledge of good and evil.  I don't want to call them "cutscenes" really, but there will be some voiced scenes between each level as Adam throws increasingly difficult dangers into the mix. Here are some notes that might be of interest: While I used Photoshop to create the images used for the icon, there's a rather nice free Icon editor out there called Greenfish Icon Editor Pro.  This is what I used to combine the various icon sizes, and export as the *.ico file. I wanted to create a setup / installer program for the game.  I used Inno Setup for this.  I was hoping this would automatically solve the issue of Windows security blocking the program from running by default.  It looks like I'll need to investigate a "signing tool" to achieve this.  For now, Windows 10 users have to manually unblock the program if they want to run it. One of the issues I tackled was related to the rendering of the grass and shrubs that make up the playing field.  Originally, this was done using individual draw calls for every single tile.  When I pulled the grass and shrub textures out into their own separate *.png files, and turned on texture repeating, this allowed me to render the entire grass with one draw call; and the shrub barriers with four draw calls.  This was a significant performance improvement, shaving off about 3 milliseconds per rendering frame.  Given that you only have 16.67 milliseconds to get everything done in one frame, that's pretty huge!

rileyman

rileyman

 

Snake - The game menu

The game menu is coming along quite well.  You can interact with the menu items with the keyboard or mouse.  It highlights the focused item, and shows some descriptive text along the bottom.   I'm actually quite pleased with the way it's structured as well...  though I'll admit to having quite a few frustrating moments fighting with the C++ STL classes. 🤣  For the game options, I'm going to implement a simple slider UI -- that'll be the next thing to tackle.  Once I have that, I should be able to blaze through the rest of the game options. There's a whole lot I could say about how I structured the menu, but as I have to head off for family stuff right away I'll just let the latest GitHub commit do the talking!

rileyman

rileyman

 

Snake - Moving on to game options

The most basic version of my SFML "Snake" clone is now playable, including background music and some rudimentary sound effects.  (The "eating an apple" sound was literally just me making a mouth sound.  I guess that makes me an "ASMRtist"?) I tagged this as "snake_1_0_0" over on my Github repository. Now I'll be moving along to adding game options.  Everything in the image below is still part of my "phase 1".  I'm happy with where things are at, but also slightly bummed because it'll be back to my regular job in a few days. 😥   Some things I feel I'll need to improve upon when I get to "phase 2" (the story mode with level progression)... Loading and managing assets.  So far I've done this with member variables for music, sound, and textures.  If I want to be able to improve the graphics, and perhaps include animations, I feel I'll need some kind of asset management structures. Spawning game objects.  Spawning of the apples is also controlled by two member variables:  "appleExistsFlag" and "applePosition".  That approach definitely won't scale well if I want to have, say, multiple apples or a row of carrots pop onto the field. The "QuickGame::update" method is kind of getting out of control.  Now that I'm typing this, I think I'll add an issue for that over on Github so I don't forget...  

rileyman

rileyman

This snake can move

Here's our snake, zipping around the field waiting for an apple to appear. 🐍 Keeping track of the snake's body as it moves and turns was the trickier part of this, so I think I'm over the hump as far as the basic game goes.  Now I just need to add apples, and keeping track of the snake length on the screen.  Then I can get to adding some of the nicer features like music, sound effects, and game options.  I feel like I have a decent shot at getting the basic game done before the weekend is out. 😀 Some of the things I wanted to take note of: I feel like it's sometimes not responding to all key press events when the player presses two keys in quick succession.  I think the cause is that I'm gathering all events in one "update" call, and going with only the last one.  If the player presses two keys quickly enough, it only uses the last one to determine the input for the next frame.  I'm going to want to look into creating a queue of inputs, where each "update" call uses up the next input from the queue. Right now, the grass texture is part of the same tile-set texture as all the other game elements.  I think this should be split out, so that I can render all of the grass in a single call; rather than width * height calls with the same sprite (there are 1250 total tiles on the playing field).  It's not slowing the game down anywhere near the 60fps range, but I do prefer to be more efficient than less efficient...  I'll be curious to see if this improves rendering performance at all. For drawing the snake, I created the SFML "sf::Sprite" objects on-the-fly.  This doesn't appear to be a performance issue.   Very much looking forward to getting the basic game done so that I can start working on "phase 2"! 🐍

rileyman

rileyman

I never claimed to be an artist!

I've worked out the initial artwork for my "Snake" clone, and it doesn't look at all how I imagine it in my head. 🤣  But that's okay for now -- it puts me in a place where I can get to work coding the game play, and seeing how it looks on the screen. I drew the snake body segments in Photoshop while looking at a picture of a garter snake (we have plenty of those in the country-side here in Manitoba).  The "shrub" barrier is just green with a grain filter and emboss border.  The apple is hand-drawn.  The grass comes from a texture website. A good portion of my day yesterday was just going around looking at various sources for music, fonts, and textures.  These are some of my go-to's: For "The Garden of Eating", I'll be using several pieces of music from the "African" genre over on incompetech.com.  Kevin MacLeod has a ton of great music for games, Youtube content, etc. There are lots of great free textures over at goodtextures.com.  Their "Seamless Textures" category is particularly good for tiling. And lots of great free fonts over at fontsquirrel.com.  I used a font named "Flavors" for the title screen, and will probably be using "Source Sans Pro" for the user interface. These three sites in particular avoid loading your browser up with popups and other obscene behaviour that makes you want to throw your computer out the window. 😝 So far, the actual game is just a splash screen with the "At the Shore" music playing.  Time to get to work on the actual game play!

rileyman

rileyman

 

Getting Started with SFML

So far, things are looking just fine trying out SFML.  (Though I'm wondering why there isn't a tag for it here...) The Visual Studio setup tutorial over on their website strongly cautions that you need to match the download version to your version of Visual C++.  As of this moment, their latest download version is labeled "Visual C++ 15 (2017)".  So far, I have had no problem getting this to work with Visual Studio 2019.  I chose to use static linking, meaning I won't need to include the SFML DLL's in my release.  The only catch to this is with the audio module, which does require "openal32.dll" (one of SFML's dependencies on an external library). To allow the use of a regular "main" function (instead of "WinMain" or whatever Windows wants these days), there are two steps: You need "sfml-main.lib" (or "sfml-main-d.lib") as a linker input. To ensure that the console doesn't appear, change "SubSystem" to "Windows (/SUBSYSTEM:WINDOWS) under Linker -> System. Once I get all of the project setup done, I was able to do a few simple tests creating windows, drawing graphics, playing sound, and accepting user input.  I'm feeling ready to go for my "Snake" clone! 😀

rileyman

rileyman

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!