Original Post

I have an interesting idea for a collaborative project. You are probably familiar with the Wario Ware games, in which the player has a limited time to complete several small “microgames”:

Planet Virtual Boy should develop a collaborative Wario Ware game for the virtual boy, in which programmers can submit a small independent microgame and add it to the game.

The idea is that the main game has a common include file, but authors write games in their own private c files and submit them. The common interface has things like the key presses, and the time remaining.
We use a struct to give the main loop the necessary info to run the game at random:

struct MICROGAME{
const char*instruction;
const char*author;
void(*init)(void);
void(*microGameLoop)(void);
void(*draw)(void);
enum time time{SHORT, LONG};
};
The “instruction” field is the message that pops up before starting a game.
The “author” field will be used in credits.
Each function pointer is called by the main game loop to simulate the game.
Finally, time specifies to the main loop how long the game should be active.

The author writes their game in their own .c file, keeping all state for that game static to the .c file they wrote it in, including any libraries they will use. The author exposes the struct with their own header file, and we place a pointer to it in an array. A random function chooses a game from this array, and ends the game when time is up.

struct MICROGAME* game_list[]={

};

If everyone designs one simple microgame, we could have a great community-driven Wario Ware homebrew. The design will be expandable, all we need to do to add a new microgame is add a new pointer to the array and recompile.
Would anyone be interested in something like this?

29 Replies

As someone who would only be playing this and not contributing to it (I don’t have the skills, but am so thankful to those of you who do!)

Even if you don’t have skill with programming, it might still be possible for you to contribute. Are you any good with graphics?

If we were to use inheritance from the base game, each minigame would get unnecessary copies of the main game state. Only one minigame will ever run at a time, so the copies of main game state don’t need to be a part of the minigame at all. They can exist in one place independent of the minigames that don’t need to access it.

Let me explain. The VUEngine has one singleton class called Game that coordinates all the VB’s resources, it is the “Operative System”. It has a StateMachine that holds a stack of states. In our games, each game screen is a GameState that is pushed into that stack, each with their own stage of game entities, and the Game only updates its current or top most state. Our suggestion is to use GameStates because the state machine pattern is a nice way to swap functionality by using a common interface, and because states generalize common phases liking initialization, execution and exiting. We didn’t suggest to inherit from Game (it is not possible in the VUEngine), but from GameState, and we didn’t suggest to add the stats to the class that inherits from GameState, but to use a manager, that is a singleton class that holds such values. So no, even in the impossible case of inheriting from the Game class, inheritance doesn’t imply that there will be redundant copies of any data.

The purpose of a classes and inheritance is to create a template that you can create instances of. However, only one instance of a game will ever exist, so why make it a class?
If we are trying to encapsulate, we can encapsulate at the file level. If you make a variable STATIC inside a .c file, its the same as making a variable PRIVATE inside of a class: it can’t be accessed, not even with EXTERN.

No, the purpose of classes is to model software in the same way that we conceive our phenomenical experience which consists of objects that have properties that determine their behavior, and by doing that, problems that are complex to solve using structural programming become easier under the object oriented programming paradigm. The purpose of inheritance is to reuse code and, by polymorphism, to allow for scalability by means of generic programming (among many other things). The fact that there only needs to be one instance of a single class doesn’t nullify those advantages.

Yeah, you can achieve something analogous to encapsulation by the use of “static” in C, but you still cannot achieve polymorphism. Still, what this has to do with the suggestion to avoid global variables for the game’s stats?

There isn’t really a difference between:
globals.time
vs self.time?

The difference is in the semantics of the program. Globals allow unrestricted modification of the program’s state, while classes’ attributes restricts those modifications to parts of the program. This is important because the comprehension of the meaning of a class attribute restricts itself to the class’ definition, while the meaning of a global variable requires a comprehension of the program as a whole, a much harder task.

Plus, self.time indicates multiple time variables (one per minigame), when in reality we only need one time variable that is applied to all minigames. A global variable makes more sense here because the one parameter would affect everything.

You’re confusing classes instances with classes attributes. Inheritance, classes, or OOP in general for that matter, do not imply that all the minigames need to be loaded at the same time in work RAM. Since there is only one minigame active at any time, there needs to exists only the singleton instance of that minigame class in work RAM, therefore only one time variable. When it ends, the instance is deleted and the instance of the new minigame class is created..

I agree, delta time sounds like it would allow for a much more gradual speedup. Should we use fixed-point as the delta? I would imagine floats would be too slow.

Yeah, fixed math should be preferable since floats are very slow on the VB. In any case, they come with caveats in the way of precision, and their resolution (the number of bits destined to hold the decimal part of the number) needs to be carefully planned.

We use 19 bits for the integer part and 13 bits for the decimal part in our engine for spatial and time calculations, with good enough results, but different games could need different values. In any case, other bit distributions are not difficult to implement.

The game won’t be running all 50 at a time, it just runs 1 at a time. You could have as many minigames as the memory allows, and performance will only matter at the level of the individual minigame code itself.

Well performance does indeed matter only at the level of the individual minigame’s code itself, but if you don’t provide a variable delta time at the level of your “primary game” then you’re defeating yourself in your goal of avoiding to reimplement the same solution to the same problem in each miningame, since variable time delta won’t affect the minigames that are light enough to run at a locked frame rate, but will allow for those which aren’t to adapt their simulations based on the CPU’s load. While if you fix the time delta, the heavy minigames, if there are any, will be penalized unnecessarily, forcing the programmer to have to deal with timer interrupts himself if he wants a stable simulation.

The purpose of inheritance is to reuse code

The purpose of inheritance is to reuse DATA. While polymorphism is a feature of many OOP languages that use inheritance, it’s not exclusive to inheritance or OOP. Function pointers could be used for polymorphism and many other tasks that aren’t tied to objects.

The real code reuse comes from functions, where I can run the same code in multiple places. If the function is pure, I can also be certain I’m not modifying any global state when I call it.

Yeah, you can achieve something analogous to encapsulation by the use of “static” in C

It’s not “analogous” to encapsulation, it IS encapsulation. It’s restricting the scope of your data, which makes the program easier to understand by breaking it down. Even a block {} is a form of encapsulation.

the purpose of classes is to model software in the same way that we conceive our phenomenical experience which consists of objects that have properties that determine their behavior.

But minigames aren’t real world objects. They have a lot of data that has nothing to do with the real world, and everything to do with the hardware. So it doesn’t make anything easier to understand, all the same code needs to be there in some way or another. You end up with classes that are nothing like the real world.

The purpose of inheritance is to reuse code and, by polymorphism, to allow for scalability by means of generic programming (among many other things). The fact that there only needs to be one instance of a single class doesn’t nullify those advantages.

Inheritance doesn’t automatically make scaleable software, abstraction does. But abstraction is not exclusive to OOP or inheritance. A function is a form of abstraction.

Globals allow unrestricted modification of the program’s state, while classes’ attributes restricts those modifications to parts of the program

Global state can be a problem, but at the same time, all games need it. It’s crazy to work in languages that forbid global state. But if we really wanted to avoid accessing it directly, we could pass parameters. Maybe we just pass it to a function then as a const parameter?
That way, the programmer gets a list of all the vars they need,
and they cannot modify it, therefore not tampering with any global state:

void step( const unsigned time );

In that way, it’s not global and its scoped in the function.

Since there is only one minigame active at any time, there needs to exists only the singleton instance of that minigame class in work RAM, therefore only one time variable. When it ends, the instance is deleted and the instance of the new minigame class is created..

If we are trying to reuse memory space for game structs, why not use a union? It’s a language feature instead of a macro.

  • This reply was modified 7 years, 8 months ago by syncophono.
  • This reply was modified 7 years, 8 months ago by syncophono.

If we are trying to reuse memory space for game structs, why not use a union? It’s a language feature instead of a macro.

We could also just have each minigame run a function which creates its data on the stack. That way we don’t even need to make an allocator, or an init function. ie,:

condition game(){
int create, my, data, here;
game_loop{
return WIN;
}
}

Our suggestion is to use GameStates because the state machine pattern is a nice way to swap functionality by using a common interface, and because states generalize common phases liking initialization, execution and exiting.

What functionality needs to be swapped? We don’t want people to override an important initialization function or something, because if it’s overridden, the base behavior won’t be performed( unless the macros have a super keyword?) If each minigame is unique, what are we swapping?

Inheritance doesn’t automatically make scaleable software, abstraction does. But abstraction is not exclusive to OOP or inheritance. A function is a form of abstraction.

And inheritance is a form of abstraction too. But since abstraction makes scalable software, according to you, then inheritance makes scalable software.

I didn’t say that there were no other means of making scalable software, nor did I say that abstraction is exclusive to OOP, don’t put words in my mouth.

Maybe we just pass it to a function then as a const parameter?

void step( const unsigned time );

I don’t see what would be the purpose of adding “const”, besides self documentation, to a functions’ argument that is not a pointer, since you can copy the variable’s value into a non constant variable and modify it. But the point wasn’t self documentation.

Global state can be a problem, but at the same time, all games need it. It’s crazy to work in languages that forbid global state…

If we are trying to reuse memory space for game structs, why not use a union? It’s a language feature instead of a macro.

You’re using a straw man argument and moving the goalposts. I didn’t say that globals are to be forbidden, I just implied that they preferably are to be avoided if possible. And I was only responding to your concern about how classes attributes “indicates multiple time variables”, which is false, since it is not necessarily the case.

The purpose of inheritance is to reuse DATA…

I don’t want to derail the thread, whose initial idea is very good, into a discussion about programming paradigms and concepts. If you think that there is no real advantage in the usage of OOP features or libraries that abstracts part of the problem of providing a single interface to work with multiple game mechanics, you’re more than welcome to pursue your project as you see fit.

I’m out.

I don’t want to derail the thread, whose initial idea is very good, into a discussion about programming paradigms and concepts. If you think that there is no real advantage in the usage of OOP features or libraries that abstracts part of the problem of providing a single interface to work with multiple game mechanics, you’re more than welcome to pursue your project as you see fit.

I’m out.

I don’t really see how the thread is derailed. The discussion of the common interface is important for the development of this project, because it needs to be established before anyone makes games. I think the discussion went pretty far, we shared many of our ideas on programming. Sorry if you viewed the discussion as a debate on paradigms, that was not my intent. I would encourage you stick around if you are interested. But its up to you. I’ll try not to get wrapped up in my opinions on programming.

My goal is to start a project that everyone can enjoy working on at the minigame level, regardless of programming style.

Ultimately, the implementation of the main interface doesn’t matter as much as the implementation of the minigames themselves, which could be in any paradigm spanning any number of files and including any libraries they need to use.

 

Write a reply

You must be logged in to reply to this topic.