Minimal abstract interface for simple games
Miloslav Ciz b50a854909 Add man page for script | 2 months ago | |
---|---|---|
examples | 1 year ago | |
media | 1 year ago | |
tools | 2 months ago | |
GAMES.md | 1 year ago | |
LICENSE | 3 years ago | |
README.md | 1 year ago | |
TODO.txt | 1 year ago | |
logo.png | 3 years ago | |
palette_help.png | 3 years ago | |
saf.h | 2 months ago |
tiny public domain suckless portable console for small games
in browser games: MicroTD, Minigames
We now have a list of SAF games!
If you make a game with SAF, I'll be glad if you let me know :-)
SAF wants to provide you with a distilled joy of simple nostalgic game programming with as little friction and frustration as possible (no complicated APIs, no bloat, no legal conditions, ...), while also supporting creation of ethical games that can run even on very simple devices, games that people can easily study, share and improve without frustration or fear, games that will survive forever, games that are here for you and not any corporation's profit. SAF follows and promotes the philosophy of peace, social anarchy, free software and mainly of "less is more", which is related to technology minimalism, suckless, Unix and KISS philosophies. SAF is taking a step back to the times at which technology was still sane, so that we can step forward in a new, better direction.
SAF is a very minimal C interface for programming mainly small games that will run on many small gaming consoles, but SAF will also run on PCs and other "big" platforms, and can be used to create not just games but also other "toy" programs. You can look at SAF in different ways:
How exactly does this work? SAF is wholly implemented in a single C header file: saf.h. In it the library functions (such as shape drawing) are implemented, as well as all supported backends (i.e. platform specific code such as writing pixels to the screen). Which backend implementation is used is chosen with a define before including saf.h, the code for other than the chosen backend will then be ignored. The game code along with saf.h then together form source code that can be compiled for the chosen platform, just as any other program for that platform.
Why such low specs? You may ask why SAF doesn't support a higher resolution, color depth, more advanced sound effects, networking and similar features. This is on purpose: sometimes less is more, and the decision for lower specs comes with many advantages and benefits. For example, the joy of programming increases as you, the programmer, can deal with less complexity, fewer headaches from complex APIs and libraries, smaller friction, you finish your projects faster (and, in fact, actually finish them), you can create assets super quickly, see the results very early. You don't deal with resizeable windows, mouse cursor, opening sockets and similar things, you just write the game with focus on its pure essence, a fun gameplay. We currently have an abundance of big, feature-heavy engines and frameworks, but very few small, minimalist and lightweight ones, which is why I created SAF. Another reason is portability -- since SAF aims to run on many simple platforms, its set of features must be the lowest common denominator of these platforms (basically every console has at least the 64x64 etc.).
When to use this? If you want to create a pretty simple platform independent game and don't mind giving up the full control and performance of the specific HW.
But can SAF power something more advanced than "tetris"? Yes, technically you are only limited by the few hardcoded parameters of SAF, i.e. the hardcoded number of buttons, display resolution etc. Computing power is not limited, so you can in theory create a ray traced 3D game with advanced physics simulation in SAF, and nothing is stopping you from using big 3rd party libraries, even if that's not recommended. In this case the small consoles with simple HW will probably not be able to run your game, which a little bit kills the purspose of SAF, but nothing is stopping you from going wild and hacking this in ways that go against recommendations, if that is what you want.
Why choose this over a framework like Godot? A game written in a "big" engine will always be burdened by many dependencies and bloat which makes it too much dependent on the engine and unable to run on very simple HW, even if the game is extremely simple and could otherwise run on it, the heavyweight engine underneath will discriminate against many platforms, and will kind of hold you project "hostage", you need to believe a third party will for a long time continue to develop and maintain a very expensive engine for you. This way your game may have a minimalistic look, but it will never be beautiful and efficient on the inside, you will always drag along a huge burden. With SAF you can create a game that's not only art from the player's point of view, but also from the engineer's, you keep all the advantages of having simple internals: speed, portability, low HW demands, independence, hackability, simplicity of compilation and so on, and you can also actually understand and change anything in the code of SAF itself.
When NOT to use this? If you're creating something that needs all the power and capabilities of a specific HW, when pushing the console towards its limits or needing some of the specs of "big" computers, such as high resolution and HQ sound. Choosing to program with the platform's specific library instead of SAF is similar to choosing to program in assembly instead of a higher level language: you get the full control and power for the price of portability, comfortable programming, safety and other things.
So is this a fantasy console? Kinda yes but also not strictly. The goal of SAF primarily isn't to create an imaginary HW platform but more to unify, simplify, standardize and help porting, to free games of specific HW dependencies, to find a common denominator platform. That actually goes along well with the idea of a fantasy console, and SAF in fact does intentionaly artificially limit some parameters such as resolution or number of buttons, just like fantasy consoles do, however it doesn't limit the HW power (memory, CPU frequency etc.) and it doesn't strictly enforce things like exclusion of 3rd party libraries, it doesn't create any sandboxes or virtual environments, the goal is to produce lightweight programs that run natively.
Can I ignore your philosophy and just "steal" this for my proprietary commercial project? Yes, you can, I've willingly given up the option to enforce any "rights" over this, and I wouldn't do it even if I could, I strongly reject the idea of intellectual property. If you profit from this, that's great, if you send me something back I'll be glad, if you credit me I'll be glad, but if you don't I don't care, it's purely your business, do what you think is right.
SAF does not guarantee portability to all of its supported platforms as the program resource usage (memory, CPU, ...) isn't limited and the use of non-portable libraries isn't enforced. SAF is a tool that will help you achieve very high portability (along with other advantages) if you use it correctly.
SAF game running on many hardware platforms:
Example of auto-converting a game from color to monochrome and applying pixel art upscaling:
This explains the basics of creating a SAF game (or another program). It shows the typical and recommended process, however SAF doesn't enforce anything so it is up to you whether and to what degree you follow it.
For programming in SAF you need to be familiar with basic C programming. SAF games are written in C (following a C99 or C89 standard is recommended for maximum portability). Even though C++ can also be used, it is not recommended because it's not as portable as C, its bloated nature goes against the philosophy of SAF and for simple programs its extra features are completely unnecessary anyway.
Let's now write a simple "hello" program in SAF:
Firstly we create a source file for the program, let's name it hello.c
. In it
we write the source:
#define SAF_PROGRAM_NAME "hello"
#define SAF_PLATFORM_SDL2
#include "saf.h"
void SAF_init(void)
{
}
uint8_t SAF_loop(void)
{
SAF_clearScreen(SAF_COLOR_WHITE);
SAF_drawText("HELLO!",4,15,
SAF_frame() & 0x04 ? SAF_COLOR_RED : SAF_COLOR_BLACK,2);
return 1;
}
At the beginning we define some things for the SAF: SAF_PROGRAM_NAME
tells SAF
the name of our program (to e.g. show in the window title), SAF_PLATFORM_SDL2
tells it which frontend implementation to use, in this case SDL2 (you can try
different ones). These values must be defined before including saf.h
!
There are also other optional settings you can set like this (they are
documented in the saf.h
file itself).
Next we are required to implement two functions (this is similar to how Arduino
works): SAF_init
in which we initialize our program (in our case we don't need
any initialization so the function is left empty) and SAF_loop
, a function
that will be called by SAF once every game frame -- this is like the "main" body
of our program. SAF automatically handles calling the loop function at the right
FPS, so you don't have to deal with this.
Note that we do not write the main
function like in "normal" C program,
that is implemented inside the SAF library. Of course, you can define any
additional functions for your program besides these required two.
It is recommended you don't include anything else than saf.h
, not even
standard libraries such as stdio.h
(there is one exception to which I will
get). Including libraries decreases portability and introduces complexity,
linking etc. If only a little possible, try to just use the functions that SAF
offers. All of these functions are documented in the saf.h
file itself. Now
there is an exception: including simple "header only" libraries that aren't
compiled separately and can be distributed along in source form are okay, so
you can use these (just beware of their dependencies).
Inside the loop function we just clear screen and write out some text using the
functions that SAF provides. You can find all of these functions documented in
the saf.h
file itself.
Now we have our program complete. The two files hello.c
and saf.h
together
form a source code that can be compiled, and you compile it the same way as you
would any other SDL program (since we're using the SDL frontend). For example on
GNU/Linux you may compile this program with gcc -o hello hello.c -lSDL2
, then
run it with ./hello
. If you're using another system, compiler or frontend,
just look up how to compile such program (brief details about compilation are
mentioned under each platform section in saf.h
).
Congratulations, you should now have completed your first SAF program!
There is one more pro tip for you: it may be more comfortable to write the game
in a header file (.h), e.g. game.h
instead of game.c
, and without defining
any SAF_PLATFORM_...
in it: this will be a frontend neutral source file of
your game. Then you can create files such as main_sdl.c
in which you simply
define SAF_PLATFORM_SDL2
and then include game.h
, and you will be compiling
this C file to get the SDL frontend. For another platform, let's say Arduboy,
you will create a similar file main_arduboy.ino
in which you similarly define
SAF_PLATFORM_ARDUBOY
and include game.h
. And so on for each platform. But
the organization of source code is ultimately up to you.
SAF doesn't enforce its recommendations, it relies on you not programming badly. This is a feature, not a bug. The following advice will help you make your programs portable, deterministic and with accordance to the spirit of SAF. Of course, it is only up to you whether you follow it or not. Hacking is a a valid option too.
millis()
or PROGMEM
. If you really really need to use it, use macros (#ifdef
s) to
only allow these on the specific platform.Everything in this repository is released under CC0 1.0 (public domain, https://creativecommons.org/publicdomain/zero/1.0/) + a waiver of all other IP rights (including patents and trademarks).
I've written the code and created all the assets completely myself, from scratch.
This project is made out of love and to be truly helpful to everyone, not for any self interest. I want it to forever stay completely in the public domain, not owned by anyone.
This is not mandatory but please consider supporting free software and free culture by using free licenses and/or waivers.
If you'd like to support me or just read something about me and my projects, visit my site: www.tastyfish.cz.
The additional waiver of all IP rights follows:
The intent of this waiver is to ensure that this work will never be encumbered by any exclusive intellectual property rights and will always be in the public domain world-wide, i.e. not putting any restrictions on its use.
Each contributor to this work agrees that they waive any exclusive rights, including but not limited to copyright, patents, trademark, trade dress, industrial design, plant varieties and trade secrets, to any and all ideas, concepts, processes, discoveries, improvements and inventions conceived, discovered, made, designed, researched or developed by the contributor either solely or jointly with others, which relate to this work or result from this work. Should any waiver of such right be judged legally invalid or ineffective under applicable law, the contributor hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to this right.