Pages

Tuesday, November 23, 2010

Flexible development of indie commercial games

[Update: The Rotten Fish Games presentation during the 3rd Hellenic Game Developers Conference, 9-10 October 2010, Athens, Greece on how the Space Debris game is being developed is available here. The presentation is in greek language.]


During the 3rd Hellenic Game Developers Conference, 9-10 October 2010, the Rotten Fish Games team presented the way that it chose to develop its first Apple iPhone game. The presentation is available here but the topic was discussed at Thinking gamer's blog by Kostas Anagnostou, too. After many friends' comments I decided to postpone the next article to write about how to develop indie commercial games working in a flexible way like the one that Rotten Fish Games uses.

The Rotten Fish Games team founded in 2010 comprised of capable, experienced game-makers who thrive both on creative expression and crafting addicting gameplay experiences. Now they joined forces for an innovative and productive team. They come from various backgrounds and possess a depth of experience.

About the game

The first game is called Space Debris and is developed for iPhone by the following team members:
* Kostas Anagnostou: Original idea, Script, Programming, Design
* Georgios Chiotis: Production, Design, Programming, Promotion
* Nick Larin: Graphics, SFX
* Vicky Fysika: Music scores
* Dimitris Fragkos: Graphics Supervision
* Michael Fragos: Design Concepts

Space Debris for iPhone has the following development plan:
* May: The core team was formed based on the original idea
* June: Development starts and necessary hardware is bought
* July: Prototype versions with the selected engine
* August: Game design is finalised and graphics are being introduced
* September: Game design amendments and alpha[not beta as in presentation] version build
* October: First release candidate (1 stage[not level as in presentation])
* November: Beta testing [not AppStore as in presentation]

Participations: GameCon (Sep 2010), IGF (Oct 2010) [we did not manage to participate], etc

The Space Debris game is a shoot-em-up (SMUP) that has 3 stages with 5 levels in each stage, 5 bosses of variant difficulty one for each stage and a total of 15 min play for each stage. With 4 difficulty settings that means a total of 60 levels of 180 min gameplay for each game mode of the 3 available. Except the innovative (asteroids manipulation) weapon mechanics the classic SMUP experience is offered with 3 different play modes. Finally, there are two types of ships to choose from multiplying by 3 the entertainment options of the player. Two players co-operative mode is offered, too.

You can read more about the team and the game using the following links:
* Twitter: @rottenfishgames
* Facebook: Rotten Fish Games

The game development model

A videogame is usually being developed by a company, a team or a single person alone. Each entity has its own pros and cons regarding the game development process.

A company can spend more money and human resources than an individual to develop a game and usually a business plan is a necessity with the related risk like any other investment. A company, depending on its size and portfolio, is able to produce up to multi-million game titles.

A team usually has more time than money to spend developing a videogame depending on its members professional or business activities. Althoug, a production plan is more suitable for a team instead of a business plan the risk to leave the project incomplete is greater since it depends on the members devotion instead of a publisher's requests. Without the legal constrains of a company even the publisher's involvement is not enough to save a project. It is imminent that no multi-million game can be developed by a team of any size.

An individual should have plenty of time to start with and maybe some money should be well-welcomed but it is not likely to develop the next best-selling shooter. However, it is profound that this individual should be talented enough to cover various skills like coding, graphics, sounds, music, writing, design etc. It is an one-man-show most of the times but sometimes a partnership with other people is necessary to complete the game. In that case the 3rd party involvement is the minimum needed.

A game is being developed for either a commercial or no-commercial purpose in mind. Commercial purpose aims for sales profit and to get some real money to support my company, my team or myself in order to be able to develop more videogames in the future without financial constrains. I may choose to develop for non-commercial purpose in order to gain more knowledge as a team, e.g. research and development for a a new platform, or as an individual, e.g. to help me go for the next dream job of mine.

An independant developer or indie for short is somebody (company or team or individual) that develops a videogame expressing himself and it is not a 3rd party outsourced and paid project or a publisher's idea.

How to develop as a team

For a team that develops a videogame to get money by selling it, without taking into account a company or an individual that works for the same purpose, the following work model offers the possibility to develop a game that a publisher will release, promote and sell covering any legal obstacles of the team's formation if no member of the team can cover that.

Since only personal time and money are being given to develop the videogame the risk is somehow smaller that a company's but not propotionally different most of the times. Member of the team can spend their time in creative work and not everyday routine like e-mails to sell the game. Each member of the team can work in autonomous mode and not like a company's employee having to follow tasks from above the hierarchy. Each developer is able to work on its own pace and partnering with other developers around the world is also important.

Modern technology frees from old-time problems and issues of a similar team few years ago. There is no space constraint or time constraint since each team's member can work conveniently from each own place whatever time he/she wants. There is no limitation of available resources and applications, too. There is no need to relocate to another place to work. Of course many companies offer the capability to work remotely but there are other issues needed to be solved in that case.

Also, there should be deliverables and deadlines but there is no real need for SLAs except the necessity to sign an NDA as a minimum precaution if the members are not knowing each other well. Each developer can work with the applications he/she knows well and he/she does not need to learn a company's specific toolset to work with.

Except the need to form the team in order to develop a commercial game that will lead to a future transformation to a company or a partnership with a company or whatever, the team should answer three basic questions: "What", "How" and "Where".

Has the team all the necessary members to finish the game? Is there any writer able to write the next top-selling adventure or the team should select another genre like a platform game for start? What tools can its members use? What is the most appropriate platform for the videogame to develop to? What is the suitable engine for the videogame? In what system the game is going to be released? Is the team able to work, let's say, for PSN or PSP? Why to develop for consoles, PCs or mobiles? Can the team afford to buy the hardware that is needed to work or for a console's SDK?

It is important for a team to target the international market and not only the local one, e.g. why to develop in greek if no one would ever buy it in Greece? Except the case the game will cover its development costs being released in a local language the team should support at least the English language and then another one since English can be understood globally. The selection of the target platform is important, too, e.g. why to develop for a console with a joypad while the game requires huge amount of keystrokes?

Finally, the team should communicate the new game when it is near its completion. If the team is not able to find a business partner to sell the game before the game finishes it is impossible to get any profit or a single sale unless the game is the next best-seller! Normally, a team should show similar dedication to a company in order to finish the game but it is somehow different the way of doing things.

The game must finish

No matter how close to the initial design the final game is, it should finish in order the team has a potential to get some sales and survive. It is beneficial for each member, too, since a finished videogame can be easily added to member's portfolio. What impression a future partner will get if the team did not finish the videogame it started to develop?

Nevertheless, usually a videogame development does not finish at all. Even in that case the benefits for the team members are great. It is obvious that they get experience they would not have been able to get and sometimes the incomplete game can be the base for the next dream project or be sold to another team or company etc.

Human beings learn through their mistakes, that's for sure, so, the path you follow to develop the videogame is the way that leads you to your destination. If you do not test yourself you do not know if you are able to work in that field no matter how you love it. We start, we fail and we start again till we succeed.

Team and videogame promotion

Nowadays, there is a variety of media for a team to use to promote itself and its creations namely the videogames it develops. Internet and the web can reach many people around the globe and the sale channels today are changing to offer more downloadable content than ever before. Do not forget that there is no limit for a man's fantasy and you can develop videogames even for a device with a fixed characters green LCD screen and then sell it to whoever is willing to pay for that entertainment.

Also, there are so many free alternatives to build your site and your videogame's site using a wordpress or blogspot or other similar service that there is no need to hire a specialist like a web designers and programmers to build your page if you cannot affort him. Members and team portfolio can be placed there, too, increasing the career advancement opportunities. Game development is a creative process, so, creative people can build a creative site as well.

The videogame should have a separate site and no matter how the game evolves during the development phase it is important to create anticipation for the game or find a publisher even if the game can be sold through a site on the internet. The sooner the better as they say.

Additionally to promotion, a game's site can serve as the on-line shop for that game with free or relatively cheap solutions being available either supporting credit cards payments or mail delivery. Many manufacturers offer their own marketplace like Apple's AppStore.

Rotten Fish Games is not the only team in Greece that works that way since there are many more teams developing successful and commercial videogames. To get more info about Rotten Fish Games and its first iPhone game Space Debris visit the following links:

* Twitter: @rottenfishgames
* Facebook: Rotten Fish Games

Friday, August 20, 2010

Archive: Putting all together: a sample Game

Sample Game 
Our application is nearly a game now. We have a map, some tiles, and we can scroll. Now, let's add a story, more objects, a little design, and improved game techniques. 

Story 
Bluedra is a small blue dragon. He is alone in a mythical world. He shoots fire out of his mouth and is in a bad mood until he finds his parents. Some blue magic squares were spread in his land. These were put there by a witch and they do not let him fly. Bluedra has to destroy these magic squares before he continues his quest to find his parents... 

Game Design 
Bluedra, will be a 2-sprite animated character. Ground will have two layers that scroll at different speeds to display the parallax effect. Square objects will be placed before starting the game in the map area. Our character is shown facing either left or right even if he moves up or down.

Players can press the "FIRE" button to make Bluedra use fire to destroy the magic squares. Direction keys move Bluedra. However, he is displayed always in the center, which means that the map scrolls while the sprite stays still.

If Bluedra touches a square the game ends and a message appears on screen. If the player selects yes the games restarts otherwise it terminates. 

Graphics 
Sprites: In the gfx folder we have to create 4 bitmaps that describe Bluedra, the blue dragon, in 2 different states facing either left or right. There will also be a bitmap for the fire sprite. Finally, we create a bitmap for the magic squares. Simple!

Tiles: We will use 3 still and 4 animated tiles for the back map layer and one for the front layer as follows:
- 1 (still back layer)
- 2 (still back layer)
- 3 (still back layer)
- 4 (still front layer)
- 5 1st animated
- 6 2nd animated
- 7 3rd animated
- 9 4th animated

The animated tiles will show a small swirl. All the above mentioned graphics can be found in the gfx folder and can be downloaded as well. So, let's move on. 

The code 
We keep the myfirst.mak and system.h files from the previous tutorials, the other files will change. Generally, in order to create a mophun game that will be published after a well-defined certification process, the resource file (res.txt in our case) should have a specific layout:

[res.txt]
METAINFO
PERMANENT, CERTIFICATION, PACKED RESOURCES
SECTION DATA

In our case, we add METAINFO and declare the new resources as well.

INFO METAINFO
{
  "IMEI" : "00000000000000"
  "Title" : "CellDev Sample"
  "Help" : "Use '2','8','4','6' to move, '*' to select, '#' to   
  cancel - read the in-game help for more information. Thank you for playing."
  "Copyright info" : "(c) Chiotis Georgios, 2003"
  "Program version": "1.0"
}

SECTION DATA
TUTORIALFONT FONT 6 8 FORMAT IND2 "fnt/TutorialFont.BMP" "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"
MYTILES TILESET 8 8 FORMAT RGB332 "gfx/mytiles.bmp"
MYSPRITE_N SPRITE FORMAT RGB332 "gfx/mysprite.bmp"
MYSPRITE1_N SPRITE FORMAT RGB332 "gfx/mysprite1.bmp"
MYSPRITE_R SPRITE FORMAT RGB332 "gfx/myspriter.bmp"
MYSPRITE1_R SPRITE FORMAT RGB332 "gfx/mysprite1r.bmp"
SPRITEFIRE SPRITE FORMAT RGB332 "gfx/fire.bmp"
OBJECT SPRITE FORMAT RGB332 "gfx/object.bmp"

Note that "IMEI", "Title", "Help", "Copyright info" and "Program version" will appear when you see the game-info in you phone. For a final product more have to be added like icons etc. See the morc(=MOphun Resource Compiler) document for more details.

MYSPRITE_N,MYSPRITE1_N are Bluredra 2 sprites facing right, MYSPRITE_R, MYSPRITE1_R are Bluedra 2 sprites facing left, SPRITEFIRE is the sprite for fire and OBJECT is the magic square.

First of all we have to add another map header and tilemap for the front layer. So, we modify the map.h file as follows:

[map.h]
...
MAP_HEADER bghdr_b, bghdr_p;//headers
...
uint8_t *tilemap_b, *tilemap_p;

The tilemap pointer will not be initialised in the InitMap routine, but we will allocate the memory needed and fill the map data. Let's see the InitMap routine now.

void InitMap(void)
{
  int32_t map_size=map_x_tiles*map_y_tiles*2;
  uint8_t x, y;
  tilemap_b=vNewPtr(map_size), //dynamic maps
  tilemap_p=vNewPtr(map_size); //will be drawn
  memset(tilemap_b,0x00,map_size); //allocate memory
  memset(tilemap_p,0x00,map_size); //for the maps

Here we calculate the memory needed for the tilemap (xtiles*ytiles*2). Then allocate the memory for each one. We do so because we are going to draw the maps dynamically. In the previous tutorials we had each tile defined. Both ways are fine as long as they give the desired result. Again, each game has specific needs.

  ...
  bghdr_b.mapoffset= tilemap_b;
  bghdr_b.tiledata = MYTILES; // Tile Gfx
  bghdr_b.format = VCAPS_RGB332; // Graphics format RGB332
  vMapInit(&bghdr_b); // Init the Map Header (b)

We initialise the first map (back). Let's draw it as follows:

  uint8_t res; //used for drawing
  for (x=0;x<map_x_tiles;x++)
  for (y=0;y<map_y_tiles;y++)
  {
    res=vGetRandom()%4;
    /* if 0 then animation will be drawn (tiles 5,6) */
    /* else the tile the res indicates (tiles 1..3) */
    switch (res)
    {
      case 0: vMapSetTile(x,y,0x05); //draw tile 5
        vMapSetAttribute(x,y,0x80); //set attribute to animated
        break;
      case 1:
      case 2:
      case 3: vMapSetTile(x,y,res); //draw tiles 1-3 randomly
        vMapSetAttribute(x,y,0x00); //attribute to not-animated
        break;
    }
}

How it works: Fill the area randomly with tiles: 1,2, and 3. If you get a zero use the animation tiles. vGetRandom() % 4 suits us because it returns 0..3 in our case. Many people are apposing the use of vGetRandom and they use their own random number generators. You can decide for yourself, but we have no problem using vGetRandom here. We have a 4-tile animation, so, in attribute we put the 0x80 value instead of 0x40 from the previous tutorial. If you do not understand why please review the previous tutorial.

bghdr_p=bghdr_b;
bghdr_p.mapoffset = tilemap_p;
vMapInit(&bghdr_p);

We "copy" the header to the second one (front map) and initialize 
it as well.

for (x=0;x<map_x_tiles;x++)
  for (y=0;y<map_y_tiles;y++)
  {
    (!(vGetRandom()%4))?
    vMapSetTile(x,y,0x04):vMapSetTile(x,y,0x00);
    vMapSetAttribute(x,y,0x00);
  }
}

The second map is simpler to draw. We keep the %4, not because we will use 4 tiles again, but to spread the tile to a larger area. If you get zero we put a transparent tile (0x00). Always remember that tile number 0 is transparent and your own tiles start from 1.

We created the 2 maps but we said during design time that the player can play again if the game finishes. Each time we allocate the memory we need for the tilemaps it will result in run-time errors. So, we use another routine to free the memory allocated.

Another way this can be done is to allocate the memory once and use it during the game. This can be done because we just change its contents drawing the map not the size! I leave that method as an exercise for you. I will use the DisposeMap routine:

void DisposeMap(void)
{
  vDisposePtr(tilemap_b);
  vDisposePtr(tilemap_p);
}

Easy!

[sprite.h]
Here, we have a problem. We displayed the sprite in the previous tutorial using the vSpriteSet routine. Now we want to display a animated sprite instead of a static sprite(non animated). We will use a sprite array to do this! Let's see how.

#define LEFT 0
#define RIGHT 1

Sometimes it is more convenient to represent the data as we understand, especially if we use the relevant code many times. That's why we define 0 to be the Bluedra facing left, 1 the Bluedra facing right.

#define SPRITEOFFS 2 //0,1-facing right, 2,3 facing left
SPRITE *spriteanim[4]; //four sprites 2+2 animation sprites

We said something about sprite array. There are 4 bitmaps for the Bluedra animated sprite, so we have 2 bitmaps for each side facing. We can use 2 different arrays of 2 elements or just one with 4 elements. In the second case we have to know where the second face sprites start.

SPRITEOFFS indicates just that:
0 and 1 elements are the Bluedra facing right, 2 and 3 elements are the Bluedra facing left.

struct {
  int16_t spr_x, spr_y; //position of spite (pixel 0,0)
  uint8_t spr_pos_anim; //animation position
  uint8_t face; //left-right
  uint8_t onfire;
} sprite;

This structure describes Bluedra. We keep it's x and y position (on screen not in the map!!! ); spr_pos_anim is either 0 or 1 for the first or the second sprite representation (2 for each side), face indicates where Bluedra looks and onfire indicates if fire shot out of Bluedra's mouth.

We have to add some more variables if we want Bluedra to interact with his environment.

struct {
  int16_t obj_x, obj_y;
  uint8_t hit;
} object[10];

The magic square representation is more simple. Just the location of the square and one other variable that indicates if it was destroyed by our dragon!

void InitSprite(void)
{
  sprite.spr_x=(scrwidth-MYSPRITE_N_WIDTH) /2;
  sprite.spr_y=(scrheight-MYSPRITE_N_HEIGHT) /2;
  sprite.spr_pos_anim=0;
  sprite.face=RIGHT;
  sprite.onfire=0;
}

Just initial values for Bluedra (0=FALSE/OFF, 1= TRUE/ON inthe caseof flags like onfire, this is basic boolean representation with numbers you should know that!).

void InitSpriteAnim(void)
{
  spriteanim[0]=&MYSPRITE_N;
  spriteanim[1]=&MYSPRITE1_N;
  spriteanim[2]=&MYSPRITE_R;
  spriteanim[3]=&MYSPRITE1_R;
}

Next we initialize the sprite array. When displaying the sprite on screen we will use the spriteanim[index] instead of &SPRITENAME.

void InitObjects(void)
{
  uint8_t i;
  for (i=0;i<10;i++)
  {
    object[i].obj_x=50+vGetRandom() % (map_x_tiles*8-150);
    object[i].obj_y=50+vGetRandom() % (map_y_tiles*8-150);
    object[i].hit=0;
  }
}

Then randomly spread the squares around (imagine them as boxes). Again the initial state for hit is FALSE.

Now, we are ready to start our game:
[level.c]
#include <vmgp.h>//the "must-include" mophun header file
#include "res.h" //resource header
#include "system.h" //caps routines
#include "map.h" //map routines
#include "sprite.h" //sprite routines

uint8_t game_finished= 0, //game over
bg_x_b=0,bg_x_p= 0, //map's offset position - x
bg_y_b=0,bg_y_p= 0; //map's offset position - y

After the necessary includes we place our global variables. game_finished will be used to end the game loop. The initial value is FALSE. The other four are the previous bg_x variable we had used for horizontal scrolling. Now, we need 2 for x and y times 2 for the back and front map we use.

void InitGame(void)
{
  /* Initialize and get device capabilities */
  InitTutorialFont();
  GetDeviceScreenSize();
  /* whole screen will be used */
  vSetClipWindow(0,0,scrwidth,scrheight);
  /* Initialize the map */
  InitMap(); //Set the header
  /* Initialize the sprite animation */
  InitSpriteAnim();
  /* Initialize the sprite */
  InitSprite();
  /* Initialize objects */
  InitObjects();
  vSpriteInit(12);
}

InitGame routine contains all the initializations necessary (which moved here from main). Note the InitSpriteAnim routine added to initialize the sprite animation. Also, the vSpriteInit uses 12 slots instead of 1. The slots will be used as follows:
  0 - Bluedra
  1 - fire
  2..12 - magic squares

A better way to do this is to use #define for the offsets above because we use them in various routines (display, collision detection etc) and in many places. We will leave it here as it is...

void UpdateGraphics(void)
{
  /* Set and show the b-map */
  vMapHeaderUpdate (&bghdr_b);
  vMapSetXY(bg_x_b,bg_y_b); //Set map's position
  vUpdateMap();
  /* Set and show the p-map */
  vMapHeaderUpdate (&bghdr_p);
  vMapSetXY(bg_x_p,bg_y_p); //Set map's starting position

We use two maps. When it is time to be displayed a trick must   take place. First, we draw the back map. We use vMapHeaderUpdate   to inform the engine what map we use each time. Shortly after the vUpdateMap we call the vMapHeaderUpdate to make the front map active. The front map will be displayed together with sprites later with the vUpdateMapSprite routine.

  /* Show the sprites */
  (sprite.face/*==RIGHT*/)
  ?vSpriteSet(0,spriteanim[sprite.spr_pos_anim],
  sprite.spr_x, sprite.spr_y)
  :vSpriteSet(0,spriteanim[sprite.spr_pos_anim+SPRITEOFFS],
  sprite.spr_x, sprite.spr_y);
  
Here, we instruct the vSpriteSet which sprite to use (of 4 available). If Bluedra faces right it displays one of 0,1 (spr_pos_anim). If he faces left it displays one of 2,3 (spr_pos_anim+SPRITEOFFS).

if(sprite.onfire)
{
  if (sprite.face)
  {
    vSpriteSet(1,&SPRITEFIRE,sprite.spr_x+12,sprite.spr_y+2);
    sprite.onfire=0;
  }
  else
  {
    vSpriteSet(1,&SPRITEFIRE,sprite.spr_x-8,sprite.spr_y+2);
    sprite.onfire=0;
  }
}
else vSpriteSet(1,&SPRITEFIRE,-100,-100);

The onfire flag indicates if the Bluedra used it's fire (player pressed the FIRE button). So, depending on the Bluedra placement we have to display the fire sprite. Mouth is approximately 2 pixels from above (sprite_y + 2). If Bluedra looks right then fire is 12 pixels right from sprite x position (=sprite_x + sprite_width). If Bluedra looks left then fire is 8 pixels left from sprite x position (=sprite_x - fire_width). Again, defines are good to have because sprites usually change sized during development. If Bluedra doesnot fire then we hide the sprite. That's because if first drawn with SpriteSet the sprite is displayed even when we do not use it anymore... Think a little about it.

uint8_t i;
for (i=0;i<10;i++)
  (!object[i].hit)
  ?vSpriteSet(2+i,&OBJECT,object[i].obj_x-bg_x_b,
  object[i].obj_y-bg_y_b)
  :vSpriteSet(2+i,&OBJECT,-100,-100);

If the squares are intact we display them, else if they were hit by fire they are removed from the screen. Another problem we have to solve is the world and screen coordinates. What I mean is that we have to display the objects relevant to map not relevant to screen. So, from obj_x and obj_y coordinates we have to substract the map's position (bg_x_b, bg_y_b).

/* Display the map and sprite */
vUpdateSpriteMap();
/* Refresh display */
vFlipScreen(1); //update the screen
}

Finally, the graphics are displayed :) 

Collision Dectection
void CheckCollisions(void)
{

Another aspect of a game is the collision detection. We thank Mophun technology for providing us the necessary facilities for this. They are fast, too :)

if (vSpriteCollision(0/*sprite*/,2/*1st object*/,11/*10th object*/)!=-1)
  game_finished=1;

So, if Bluedra touches a magic square the game finishes (1=TRUE, remember?). The squares range is from 2 to 11 not from 2 to 12. We count from 0 so the 2 is the first square, 3 is the second,...11 is the twelfth! In your game a situation like this may result to decreasing a life, losing some points, etc...

uint8_t i;
for (i=0;i<10;i++)
  if (vSpriteCollision(1/*fire*/,2+i,2+i)!=-1)
  {
    object[i].hit=1;
  }
}

Next, we check the magic squares against the fire. We have to check one by one in order to decide which is destroyed. Try to compare the two collision parameters of Bluedra and the fire.

void CheckInput(void)
{

Again, moved from main the input check provides the interaction with the game.

  int32_t buttons=vGetButtonData();
 /* keep the previous anim position */
 uint8_t tmp_spr_pos_anim=sprite.spr_pos_anim;

We need to keep this position in order to hace a succesful animation. If we increase the anim offset by one with the directional keys we would come up with the same animation position every time. Remember we have only 0 and 1 position in the animation. So, moving diagonally we will have one change for left/right (from 0 position to 1) and one change for up/down (for position 1 to 0). See? No animation at all.

/* -scroll the map according to the keys pressed */
/* -decide character's facing */
/* -check the boundaries */
/* -update animation position */
if (buttons&KEY_LEFT)
  if (bg_x_p>0)
  {
    bg_x_b--;
    bg_x_p-=3;
    sprite.face=LEFT;
  }

We move the back map 1 pixel while the front 3. If the step was the same we would not get the parallax effect!

if (buttons&KEY_RIGHT)
  if (bg_x_p<(map_x_tiles*MYTILES_WIDTH-scrwidth-1))
  {
    bg_x_b++;
    bg_x_p+=3;
    sprite.face=RIGHT;
  }
  if (buttons&KEY_UP)
    if (bg_y_p>0)
    {
      bg_y_b--;
      bg_y_p-=3;
    }
  if (buttons&KEY_DOWN)
    if (bg_y_p<(map_y_tiles*MYTILES_HEIGHT-scrheight-1))
    {
      bg_y_b++;
      bg_y_p+=3;
    }

  if (buttons&(KEY_RIGHT|KEY_LEFT|KEY_UP|KEY_DOWN))
    if (tmp_spr_pos_anim==sprite.spr_pos_anim)
      (sprite.spr_pos_anim<1)
      ?sprite.spr_pos_anim++
      :(sprite.spr_pos_anim=0);

So, we change the animation position only one time if the Bluedra moves.

  if (buttons&KEY_FIRE)
    sprite.onfire=1;

If player presses the FIRE button the onfire state becomes TRUE.

  if (buttons&KEY_SELECT)
    game_finished=1;
}

Instead of Bluedra touching a square the game ends if the user presses the "Exit" button.

So, what is left to the main routine?

int main(void)
{
  do
  {
    InitGame();
    do
    {
      UpdateGraphics();
      CheckCollisions();
      CheckInput();
    }while(!game_finished);
    game_finished=0;
    DisposeMap();
    vSpriteDispose();
  }while(vMsgBox(VMB_YESNO,"Play again?")==VMB_YES);
  vClearScreen(vRGB(0,0,64));
  vFlipScreen(1);
  return 0;
}

Let's examine what the main does. 

# Until the user decides to exit
- Initializes the game
- Until the game finishes
* Displays the graphics
* Check the sprite collisions
* Interacts with the input
- Deallocates memory allocated by Inits
# Clears the screen 

That's it! We are finished! Enjoy your game :)

Many things are missing, the code is not optimal, but the main idea has been fully explained. Good luck with your mophun development until the next advanced tutorials! 

Download the source for this tutorial here.

Archive: "Mophun API" - part 3: Tiles and Sprites, Move and Scroll

* Tiles and Sprites
* Move and Scroll

It is time to move on and create something cooler than the previous dots and lines on screen. We will use actual bitmapped images as background tiles and sprites. When we finish we will create a basic game engine to move around in our "level".

Tiles and Sprites theory

Let's explain what "tiles" and "sprites" are. People already familiar with these terms can skip ahead to the Creating Tiles and Sprites section.

To create a level you have to design the map behind it. Imagine the map as the game's area where the player can move and that each level is a small proportion of the map. We can have 1 or more levels per map. Again, we see a small area of the level each time, only what is visible on screen. When we move in a level we are changing the view and a scroll effect is created. Because the game map can vary depending on the game, levels can be large demanding a vast amount of graphics be created. To make things easier we break the level down into small graphical components called tiles. Tiles can then be put together in arrangements that form the level.

With tiles of size NxN pixels, e.g. 8x8, 16x16, 32x32 and so on, we can create huge levels. The only limit we have is the hardware we develop for. For our purposes tiles will be of 8x8 size because that is the current limit in Mophun. We are also able to have levels of up to 255x255 tiles. Nevertheless, a map level of 255x255 tiles of 8x8 pixels results in 2040x2040 pixels. However, this is available only to specific devices (SE P800, NOKIA 3650/7650 etc).

Another aspect is the visible area of each device. For Sony Ericsson T300 with a screen of 101x80 pixels we have to use 13x10 tiles of 8x8 pixels to cover the screen. Other devices can display a larger area so the target device is a decisive factor for our implementation. We will use a generic one which easily can be modified to adapt to other phones as well.

Tiles can be static or animated. Static tiles remain the same where ever they appear in our level map. Animated tiles can be shown "moving" or "changing status" while time passes. Another category are attributed tiles that can be either static or animated but have a special meaning like "block-the-character", "map-boundary", or something else that the developer defines. Animated tiles can be a power of two with a limit of eight (2,4,8). The animation depth is also an attribute but not a user-defined one.

Sprites are similar to tiles (they are both bitmaps) but they are not used to create levels (backgrounds) nor do they have a size limit in pixels (e.g. 8x8 pixels like a tile). Sprites are use for the game objects like the hero, the opponents, the items, etc... that are found in a game.

"The Eel" game located in the SDK's tutorials offer an idea of how to manipulate sprites but no actual implementation exists...

Creating Tiles and Sprites
First of all let's decide what we will create. I believe a small level for a platform game will be a nice start. The level will show an area of a red planet. We will use two constants in our code: max_x_tiles and map_y_tiles for horizontal and vertical tiles count accordingly. map_x_tiles will equal the visible-screen-tiles (vs_tiles) multiple visible-screens-per-level (vs_perlevel), e.g. for SE T300max_x_tiles=13*3= 39 for 3 level screens. map_y_tiles and max_y_tiles are similar for screenheights.

We will use 256 colors indexed bitmap format or FORMAT RGB332 in Mophun representation.

1. Paint the tiles.
Create a 256 color indexed bitmap with a size of 8x32 pixels. Tiles are drawn sequentially one below the other, that's why the width is 8. The height of 32 pixels tells us that we are going to use 4 tiles (4x8 pixels). The first two will be static in our level while the two others will represent an animated tile. Use your favourite painting application. Although artistic talent is nice it is not required. Remember to draw the last two tiles in such a way that 2 states are described. Ready? Create a gfx directory under your project directory and save the file as "mytiles.bmp".

2. Paint the sprites.
Again, create a 256 color indexed bitmap with a size of 12x8 pixels. If you want instead of the spaceship I drew in my bitmap paint whatever you like. Save the file under gfx directory as "mysprite.bmp".

3. Declare the resources.
Open the res.txt file and add the following line under SECTION DATA:

MYTILES TILESET 8 8 FORMAT RGB332 "gfx/mytiles.bmp"
MYSPRITE SPRITE FORMAT RGB332 "gfx/mysprite.bmp"

TILESET means that the "gfx/mytiles.bmp" contains tiles, 8 8 is each tile size in pixels, FORMAT RGB332 shows the bitmap format.
SPRITE means that an image object is contained in "mysprite.bmp" file.

4. Write the code.
For our purposes we will create the map.h header file as follows:

[map.h]
#define vs_tiles 13 //13 horizontal tiles per screen
#define vs_htiles 10 //10 vertical tiles per screen
#define vs_perlevel 3 //3 screen per level

MAP_HEADER bghdr; //map header

uint8_t map_x_tiles=vs_tiles*vs_perlevel;//level hor.tiles
uint8_t map_y_tiles=vs_htiles; //level ver.tiles

void InitMap(void)
{
  //the background tiles
  uint8_t tilemap[ ] =
  {
    /* 1 2 3 4 5 6 7 8 9 10 11 12 13 */
    /*1*/
    0x01,0x00, 0x01,0x00, 0x01,0x00, 0x01,0x00, 0x01,0x00,         
    0x01,0x00, 0x01,0x00, 0x01,0x00, 0x01,0x00, 0x01,0x00,  
    0x01,0x00, 0x01,0x00, 0x01,0x00,
    0x01,0x00, 0x01,0x00, 0x01,0x00, 0x01,0x00, 0x01,0x00,  
    0x01,0x00, 0x01,0x00, 0x01,0x00, 0x01,0x00, 0x01,0x00, 
    0x01,0x00, 0x01,0x00, 0x01,0x00,
    0x01,0x00, 0x01,0x00, 0x01,0x00, 0x02,0x00, 0x02,0x00,  
    0x02,0x00, 0x01,0x00, 0x01,0x00, 0x01,0x00, 0x01,0x00, 
    0x01,0x00, 0x01,0x00, 0x01,0x00,
    ...
    ...
    ...
    /*10*/
    0x02,0x00, 0x02,0x00, 0x02,0x00, 0x02,0x00, 0x02,0x00,   
    0x02,0x00, 0x02,0x00, 0x02,0x00, 0x02,0x00, 0x02,0x00,  
    0x01,0x00, 0x01,0x00, 0x01,0x00, 0x01,0x00, 0x01,0x00,   
    0x01,0x00, 0x01,0x00, 0x01,0x00, 0x01,0x00, 0x01,0x00, 
    0x01,0x00, 0x01,0x00, 0x01,0x00, 0x01,0x00, 0x01,0x00,   
    0x01,0x00, 0x03,0x40, 0x03,0x40, 0x03,0x40, 0x03,0x40,  
    0x03,0x40, 0x03,0x40, 0x03,0x40, 0x03,0x40, 0x03,0x40, 
    0x03,0x40, 0x03,0x40, 0x03,0x40, 0x03,0x40,
  };

  bghdr.x = 0; // X position on the map
  bghdr.y = 0; // Y position on the map
  bghdr.flag = MAP_AUTOANIM | MAP_USERATTRIBUTE;// no flags
  bghdr.xpan = 0;
  bghdr.ypan = 0;
  bghdr.width = map_x_tiles; // Width in tiles
  bghdr.height = map_y_tiles; // Height in tiles
  bghdr.animationspeed = 3;
  bghdr.mapoffset = tilemap;
  bghdr.tiledata = MYTILES; // Tile Gfx
  bghdr.format = VCAPS_RGB332; // Graphics format RGB332
  vMapInit(&bghdr);// Init the Map Engine
}

Let's examine the tilemap variable which is initialized into the IniMap routine. Each tile is a combination of two consecutive numbers. The first number is the tile number inside the bitmap file. The second one is the attribute value. More details you can be found in the Mophun's documentation. We will use only the attribute 0x40 for 2-tile animations in our example. Each pair in the tilemap represents a single tile on screen. An attribute value of 0x00 represents the absence of a tile, useful when you put more than one map over the other to create a parallax effect.

The MAP_HEADER structure is easily understood. We set the initial position of the map on screen. These are it's tile positions, the tiles image resource, how many horizontal and vertical tiles are contained, etc...

Sprite related routines will be put in the sprite.h file:

[sprite.h]
struct {
  int16_t spr_x, spr_y;
} sprite;

void InitSprite(void)
{
  sprite.spr_x=(scrwidth-MYSPRITE_WIDTH) /2;
  sprite.spr_y=(scrheight-MYSPRITE_HEIGHT) /2;
}

The structure sprite is declared to hold the sprite's position. We declared the spr_x and spr_y variables as int16_t in order to hold negative values (the sprite is hidden outside visible screen area). The InitSprite routine will set the position of the sprite at the screen's center. The width and height of the sprite are taken into account for proper display.

The system.h file for our purposes has changed to:

[system.h]
uint16_t scrwidth, scrheight;
void GetDeviceScreenSize(void)
{
  VIDEOCAPS videocaps;
  videocaps.size=sizeof(VIDEOCAPS);
  if(vGetCaps(CAPS_VIDEO,&videocaps))
  {
    scrwidth=videocaps.width;
    scrheight=videocaps.height;
  }
}

The scrwidth and scrheight variables were made global for access by the rest of the program. They can be directly initialised by the GetDeviceScreenSize routine.

Now, let's display the map and spite using the level.c file:

[level.c]
#include <vmgp.h>//the "must-include" mophun header file
#include "res.h"

#include "system.h" //caps routines
#include "map.h" //map routines
#include "sprite.h"

int main(void)
{

  /* Initialise and get device capabilities */
  InitTutorialFont();
  GetDeviceScreenSize();

  /* whole screen will be used */
  vSetClipWindow(0,0,scrwidth,scrheight);

  /* Initialise the map */
  InitMap(); //Set the header
  vMapSetXY(0,0); //Set map's starting position
  /* Initialise the sprite */
  InitSprite();
  vSpriteInit(1);
  vSpriteSet(0,&MYSPRITE, sprite.spr_x, sprite.spr_y);

  do{
    /* Display the map and sprite */
    vUpdateSpriteMap();
    /* Refresh display */
    vFlipScreen(1); //update the screen
  }while(!vGetButtonData()); //wait till a key is pressed

  vSpriteDispose();

  return 0;
}

We put the vUpdateSpriteMap and vFlipScreen routines inside the do..while loop in order for the animate effect to be seen. Try to put both routines outside the do..while loop and you will get a totally static screen. Note that the animation speed can be set in the map header.

vSpriteInit and vSpriteDispose routines are used to set the first sprite slot and then free it at the end respectively.

Because we are using more memory we have to change the last line of MyFirst.mak file accordingly:

pip-gcc -o $@ $(OBJS) -mstack=2048 -mdata=4096 -s

Move and Scroll
Ok, we have a level and a sprite, how can we move around in our game? Let's use the input code we have seen in the previous tutorial. Appropriate modifications will be made to level.c file:

[level.c]
int main(void)
{
  ...
  /* Initialise the map */
  InitMap(); //Set the header
  /* Initialise the sprite */
  InitSprite();
  vSpriteInit(1);

  uint8_t bg_x = 0; //map's offset position
  int32_t buttons;
  do{
    /* Set the map */
    vMapSetXY(bg_x,0); //Set map's starting position
    vSpriteSet(0,&MYSPRITE, sprite.spr_x, sprite.spr_y);
    /* Display the map and sprite */
    vUpdateSpriteMap();
    /* Refresh display */
    vFlipScreen(1); //update the screen
    buttons=vGetButtonData();
    if (buttons&KEY_LEFT)
    if (bg_x>0)
      bg_x--;
    if (buttons&KEY_RIGHT)
    if (bg_x<(map_x_tiles*MYTILES_WIDTH-scrwidth))
      bg_x++;
    if (buttons&KEY_UP)
    if (sprite.spr_y>0)
      sprite.spr_y--;
    if (buttons&KEY_DOWN)
    if (sprite.spr_y<scrheight-MYSPRITE_HEIGHT)
      sprite.spr_y++;
  }while(!(buttons&KEY_SELECT)); //wait till a key is pressed

  vSpriteDispose();

  return 0;
}

A new variable (bg_x) was added to indicate the position of the level map. Increasing bg_x we scroll the map left while decreasing it we will scroll right. The left and right direction keys are now affecting these variables. The Up and down direction keys move the sprite because we cannot scroll the map in the Y direction for this game. We use only as many tiles as can be displayed on screen.

However, bg_x cannot take negative values or values bigger than map_x_tiles*tiles_width, otherwise the map will freeze. So we will do bounds checking on bg_x everytime we attempt to change it's value. The same applies to the sprite.spr_y value. In order for updates to be visible the vMapSetXY and vSpriteSetXY routines were put inside the do..while loop.

Now we are ready. Rebuild the sample game and run it in the emulator. A spacecraft (or your replacement sprite) will be displayed in the middle of a red planet with lava rivers running below it. Use the arrow keys to move it around!

Download the source for this tutorial here.