How to check that a player's PC meets your requirements
You've created the next call of duty. In excitement, someone downloads your game and starts it. If their PC isn't the latest and greatest, are they met with a cryptic error message or crash, or something nicer? Read this article to find out how to do it the right way.
Introduction
Generally, when you write your game, very little thought will initially be given to system specifications. The usual train of thought might be "well it runs on my system, so i'll publish this as the minimum or recommended specs in a readme file, or on my website". However, what will your game do if the player's PC fails to meet your expectations? This article will outline what sort of things you should be checking for, and when. There is a decidedly windows and DirectX slant to this article as these are my platforms of choice. The concepts are transferable to other platforms and frameworks, however, the source code i give here is not.Why should i even attempt to detect system specifications?
It gives a good impression
Checking for the user's system specification will ensure that all users who can run your game will run it without issue, and those who cannot run it will be presented with something useful. A game which crashes or even worse, takes down the player's system when they try to start it, with no reason or rhyme as to why will discourage them from trying again, and what's worse they might even go onto Twitter and disparage your game's name. One player spreading bad news about your game is enough to deter many other potential players.It cuts down on support issues
If the player receives a well thought out and instructive error message in the event of a failure, they will know who to contact, and how. The error message could even advise them on what they need to do next before they call you, e.g. to purchase a better graphics card, or delete some programs to free up space, or even to change their display resolution. If they aren't told this beforehand, they will have to contact someone. That someone might be you, and this is your time they will take up which is better spend producing more games.It helps with system stability
Checking for the correct capaibilities beforehand will cut down on the amount of crashes that a player might encounter if their machine isn't quite up to par. As outlined above, a game which crashes is a bad thing, but worse than that, a complete system crash (e.g. by switching to full screen mode with no easy way out) might risk other data on the user's machine, causing damage as well as annoyance.How and when should i detect system specifications?
You should attempt to detect system specifications whenever your game starts. This should preferably be done before any systems are properly initialised, so that windows is still in a state where the user can properly click any error messages away and get back to what they were doing before trying to run your game. In my own game, I have split system specifications detection into several classes, each of which is responsibile for detecting the state of one subsystem. Each is called in turn, with the simplest checks done first as some depend on others for success. It is best to leave the code which checks for system specifications till last in your game, as you won't know what specifications your game needs until this point and are likely to go back and change it repeatedly, otherwise. Important subsystems to check are:- System RAM - is there enough to run the game?
- CPU speed - is the CPU fast enough? Is it multi-core?
- Hard disk space - is there enough for save game files and other data you might put there?
- Hard disk speed - will your game fall over when streaming assets from disk?
- GPU speed and video RAM - Is the graphical performance of the machine sufficient?
- Network connectivity - Is there a network connection? Is the internet reachable, e.g. to look for updates?
- Windows version - Is the version of windows up to date enough to do what you need to do?
Checking system RAM size
You can check the system RAM size on windows using the GlobalMemoryStatusEx() function, which will tell you amongst other things the amount of free and total RAM, and the amount of free and total pagefile: const ONE_GIG = 1073741824; MEMORYSTATUSEX status; ZeroMemory(&status); status.dwLength = sizeof(status); GlobalMemoryStatusEx(&status); if (status.ullTotalPhys < ONE_GIG) { MessageBox(0, "You don't have enough RAM, 1GB is needed", "Epic Fail", MB_OK); exit(0); } Checking video RAM size
You can check the video RAM size using DXGI, and then based upon this you could load lower resolution textures to cut down on memory usage, or you could outright tell the player to get a new graphics card. I prefer the first of these two options wherever possible, as it gives a more friendly experience. Only once you have exhausted all possibilities should you give up. The code to detect video RAM is relatively simple: #include <iostream> #include <string> #include <d3d11.h> int main() { HRESULT hr; D3D_FEATURE_LEVEL FeatureLevel; // Create DXGI factory to enumerate adapters CComPtr<IDXGIFactory1> DXGIFactory; hr = CreateDXGIFactory1(__uuidof(IDXGIFactory1), (void**)&DXGIFactory); if(SUCCEEDED(hr)) { CComPtr<IDXGIAdapter1> Adapter; hr = DXGIFactory->EnumAdapters1(0, &Adapter); if(SUCCEEDED(hr)) { CComPtr<ID3D11Device> Device; CComPtr<ID3D11DeviceContext> Context; hr = D3D11CreateDevice(Adapter, D3D_DRIVER_TYPE_UNKNOWN, nullptr, D3D11_CREATE_DEVICE_BGRA_SUPPORT, nullptr, 0, D3D11_SDK_VERSION, &Device, &FeatureLevel, &Context); if(SUCCEEDED(hr)) { DXGI_ADAPTER_DESC adapterDesc; Adapter->GetDesc(&adapterDesc); std::wstring Description = adapterDesc.Description; INT64 VideoRam = adapterDesc.DedicatedVideoMemory; INT64 SystemRam = adapterDesc.DedicatedSystemMemory; INT64 SharedRam = adapterDesc.SharedSystemMemory; std::wcout << L"***************** GRAPHICS ADAPTER DETAILS ***********************"; std::wcout << L"Adapter Description: " << Description; std::wcout << L"Dedicated Video RAM: " << VideoRam; std::wcout << L"Dedicated System RAM: " << SystemRam; std::wcout << L"Shared System RAM: " << SharedRam; std::wcout << L"PCI ID: " << Description; std::wcout << L"Feature Level: " << FeatureLevel; } } } } The FeatureLevel variable is also useful here, as it will show you which of the graphics card features the PC actually supports. Detecting the windows version
Detecting the windows version may be important if you only wish to support certain types of installation. For example, you might not want the user to run your game on a server, or you might want to ensure, before your game even tries to access DirectX, that they are not running windows XP or earlier if this will have an impact on your game. Detecting the version information of windows is very simple and should be done using the GetVersionEx win32 function: WindowsVersion::WindowsVersion() : Checkable("windows/version") { OSVERSIONINFOEX vi; ZeroMemory(&vi, sizeof(OSVERSIONINFOEX)); vi.dwOSVersionInfoSize = sizeof(vi); GetVersionEx((LPOSVERSIONINFO)&vi); vMajor = vi.dwMajorVersion; vMinor = vi.dwMinorVersion; spMajor = vi.wServicePackMajor; spMinor = vi.wServicePackMinor; Build = vi.dwBuildNumber; Platform = vi.dwPlatformId; ProductType = vi.wProductType; } bool WindowsVersion::IsServer() { return (ProductType == VER_NT_DOMAIN_CONTROLLER || ProductType == VER_NT_SERVER); } bool WindowsVersion::IsGreaterThanXP() { return (Platform == VER_PLATFORM_WIN32_NT && vMajor >= 6); } Please note, however, that there is an important gotcha to this function call. You cannot use it to detect if the user is running windows 8.1, only version 8.0. This is because the call will only return the newer version number if your executable embeds the correct manifest. If you want to detect this, you should use the newer Version helper API from the Windows 8.1 SDK instead. If all you want to do is detect XP, or anything older than windows 8.0, then GetVersionEx will do fine. Detecting hard disk space
Detecting hard disk space is relatively simple, and can be done via the GetDiskFreeSpaceEx function. You should always avoid the simpler GetDiskFreeSpace function, which operates in number of clusters rather than number of free bytes, taking more work to get a simple answer rather than just returning a simple 64 bit value you can check. Using this function is very simple: INT64 userbytes; INT64 totalbytes; INT64 systembytes; BOOL res = GetDiskFreeSpaceEx(L".", (PULARGE_INTEGER)&userbytes, (PULARGE_INTEGER)&totalbytes, (PULARGE_INTEGER)&systembytes); std::cout << "Your disk has " << userbytes << " bytes available for use."; Note the difference between userbytes and systembytes in the example above. The userbytes value is the amount of disk space available to the current user, as the disk might be limited by a quota. The systembytes is the total space ignoring quotas, available to all users. Therefore, you should usually check the first result field. Detecting CPU speed
There are many ways to detect the CPU speed. Some of the more common ones are:- Using WMI to read the Win32_Processor information - my personally preferred method
- Using the machine code CPUID instruction via inline assembly - less portable, but accurate
- Using a busy loop to calculate CPU - mostly deprecated as this is extremely hard to get right on multi-tasking operating systems, and not recommended outside of kernel level code
Conclusion
The advice above should help you detect your player's specifications effectively. This is of course just the tip of a very big iceberg, and once you start detecting various metrics from a player's system, you will keep thinking of other things you really should check. Don't get carried away though, as it is easy to get distracted trying to detect potential edge cases, rather than just carrying on with the game.Article Update Log
17 Mar 2014: Initial releaseRelated Tutorials
Balancing Game Development and Creative Direction in Indie Production
A practical look at how indie developers can balance creative direction with hands-on game development. This article co…
My Unreal Engine Development Process: From Core Idea to Playable Build
A practical overview of my Unreal Engine development process, covering how I move from a core game idea to a playable b…
Introducing LaneGraph: The Ultimate Road Network Solution for Unity
Discover the power of LaneGraph, a lightweight and flexible lane-based navigation system for Unity. LaneGraph makes it…
Retargeting Mixamo Characters with Root Motion In Unreal Engine 5.4.
I have always found Retargeting Mixamo Characters To have Root Motion is a serious lengthy Tast, Recently I stumbled up…
How To Make A SIMPLE Main Menu In Unity
In this tutorial for unity, i go over how to make a simple main menu for unity, it's an unlisted video because i do not…
Guide to Gameplay Balance
A perspective on competitive gameplay balance, from a background of "shooter" sandbox design.
Discussion
More from Craig Edwards
Billboarded Foliage in Unreal Engine 4
Learn how to render forests of potentially millions of trees in Unreal Engine 4, without bogging down your hardware.
Everything You Ever Wanted to Know About Authenticode Signing
Learn everything you need to know about how and why you should digitally sign your games.
Automated Deployment of Your Game
Learn how to bundle your game in an installer with very little effort, to sign your installer with a digital signature,…
Discussion