Page1, Page 2, Page 3, Page 4, Page 5, Page 6, Page 7

Game Programming Tutorial (Page 6)

This is page 6 of step by step guide to programming a 2D side-scrolling game with some 3D. We should add some non-procedurally animated tiles (we'll do special effects later)

STEP 72:

First let's make a waterfall image..

Go File>>New>>600x600 - save as waterfall_rough1.psd
Go Filter>>Render>>Fibers - maybe variance 18, strength 3

STEP 73:

Reduce brightness -25 and go image>>adjustments>>color balance and set it to more blue and a bit more cyan for shadows, midtones and highlights - until it looks like a nice brightly colored waterfall kinda thingy - then go filter>>blur>>gaussian blur - and blur by say 3 pixels... It should look pretty good but for a little added effect... copy the layer(Ctrl+A, Ctrl+C, Ctrl+V) [you should now have 2 layers in your layers window] Now go Filters>>Distort>>Ocean Ripple>>Ripple_Size=10, Magnitude=5 - ok - and adjust the opacity of the new layer so it's partly see-thru until it looks good to you(ie: 50)..

waterfall1

STEP 74:

Merge visible and make sure to put a new empty layer under this layer(for transparency purposes).
Use the eraser tool and opacity to 20 and flow to say 45 and maybe brush size of 65.. Using a wavey motion - lightly erase little bits along the left and right sides of the waterfall to make the edges sort-of fade to mist. You can use the blur tool(size 65) to smoothen out the edge fade a bit.. (you should see a bit of that checker pattern along the edges which show the see-thru parts)

wfall2

STEP 75:

We need to make sure this will work as a repeating animated tile so first copy the image and start a new image: File>>New>>600x600(background: transparent), Ctrl+V to paste.. and move(while holding shift) the pasted image half way down. Paste again and move the second image halfway up so the bottom of it touches the top of the other one exactly.

wfall3

STEP 76:

Merge the 2 layers... Now using the smudge tool and stamp tool (as we did before) we can make this seamless. (Remember to pick a spot near either side of the edge by using the ALT key when using stamp)..

wfall4

STEP 77:

Save as: waterfall1.psd

There are a couple different ways we could animate this. The best way would probably be to set the tile to scroll in place like we did with the background. We'd set the tile's animation attribute to SCROLL_DOWN and set the timer to be the motion speed instead of the animation timing. For a frame based animation we'd simply prepare some scrolled tile frames that appear to loop when played 1-3 or 1-6.. and set the tile's animation timing property to be the wait time between each animation frame. I think this time I'll use a tile-scrolling method for this tile -- but the water-splash at the bottom of the fall will need to be a frame based animation (unless we choose to make a particle-effect splash -- maybe later...).

STEP 78:

Watersplash! Go File>>New>>600x600...
Go File>>New>>500x500 (transparent background)
Select the brush tool brush and choose a brush preset that looks a bit like water splash (bunch of dots):

splash brush (I chose size 145 - but whatever works -- best to start with the color white)

(to see the white speckles better - you might want to temporarily place a dark brown layer behind your working layer for now).
Splatter a few of them around the middle. Reduce the opacity to say 75 and splash a couple around it a bit. Change splash brushes, size, and opacity to make some variety of speckles here and there..
Once you're happy with how it looks - right click over the image and click blending options... Go into inner glow and change the color to a light blue. Change blend-mode to normal. Play around with the settings(mainly size and perhaps opacity) until you like how the inner glow looks. Click outer glow to activate it and go into its settings. Choose blend-mode normal and a medium blue color. Again - play with size(ie: 18) and other controls a bit if you like until it looks good to you. You should get something like this:

splash piece 1

Create a couple more pieces like this and save each piece - splash_part1.psd, splash_part2.psd, splash_part3.psd. (easiest way to do it is save this file, eraser out this layer and paint white dots into it again and the effects will be added automatically)..

STEP 79:

For each image - use magic eraser on the brown layer to make a transparent background - then merge visible - and go image>>image size>>256x256 and save as spash_mini1.psd, etc...
Make a new image and place 2 copies of splash 1 in the bottom middle and make 1 upside down. Use the move tool's scaling to make sure each layer is a perfect square with the center right on the bottom of the image. Do the same for splash 2 except place it to the left(not too far tho - so that if you rotate it, pieces don't fall off the side of the image) -- do the same sort of thing for splash 3 on the right side.
For each layer, rotate the layer by about 45 degrees or so... We're basically rotating them all to make an animation effect of splashing water. Save each frame: ie: splash_f1.psd, etc...Merge visible in each file.

To test out the animation:
Create a new file 500x500 and copy and paste each frame(splash_f1-splash_f4) to each layer. You should have 4 or so frames - each on a different layer. Go Window>>Animation and click animation_button (corner of animation window)... and select: Make frames from layers.. Click the little arrow thingy under each frame and change the delay to 0.2 sec -- click play to see how the animation will look.. (It's probably actually easier to edit animation using something like Ulead's Gif Animator or flash and then export results to whatever format)
animated_splash

STEP 80

Now we need to add some actual water... This time, I'm just going to use a plain 64x64 blue square with opacity of say 50%... It's almost pointless to save this in a seperate file.. but what the heck - let's call this water_plain.psd

water_plain

STEP 81:

Let's add a 3 frame water sparkle to the surface of the water. Just save the plain water as a new image - water_sparkle1.psd -- add a second layer, select it and:
Using dotted brushes or just dotting in with normal brush - use medium blue just along the top of the image.. Do the same with bits of white to add sparkles. Use techniques as described earlier to make sure the tile can be repeated seamlessly. Just make a couple more frames of this - modifying the image just slightly(maybe increase contrast by 4 each time too).. We might make this animation yo-yo 1-3-1... maybe
animated_water

 

Ok so eventually I'm going to come back to this and show how to build the editor.
I'd like to include the following features:

- scroll-in-place tile animation
- tile image layers (for a more 3D look)
- tile frame animator (using snap-mouse to select each frame and set a timing)
- allow more than one image to associate with a tile(with offset, scale, rotation, and hue properties for each)
- allow bump map association (for lighting effect [if we actually want to use them])
- particle/animation generator ID (what type of particle effect - might use as seperate map for particle textures) [will use enum FIRE_PARTICLE, STEAM_PARTICLE, MAGIC_PARTICLE, etc...
- has tile type: PASSABLE, SOLID, LIQUID, BOUNCE, BUTTON, etc...
- tile effect types: SLIPPERY, DAMAGING, FALLS_OUT, BREAKS, DOOR, etc...
- allow item association or hidden items inside(if BREAKS or is DOOR or something)
- tile depth: FRONT_MOST, IN_FRONT, BEHIND, FAR_MOST

Tiles will be given a default behavior in the editor and then when painting tiles into the game-map, we can modify properties of specific tiles. IE: make tile of type SOLID into type PASSABLE or change from BEHIND to INFRONT or add a particle effect to emerge from it or something like that..

How the game map scrolling works?

As the gamemap scrolls up-down, left-right - we update an x_offset and y_offset for the onscreen tiles. If each tile in memory is set to a standard of 64x64 pixels, then this is how we'd do it:

Hero.x+=hero.velocity.x; //(actually we'd use a temp variable to find out if this movement will be legal - do a vector with tiles collision check)
Hero.y+=hero.velocity.y;
Now the player's position has been updated in the map (pretending the map doesn't actually move)
However -- to see what's around the player - we'll need different variables to show what should be seen:

gamemap.offset.x-=hero.velocity.x;
gamemap.offset.y-=hero.velocity.y;

This is because the gamemap obviously moves in the opposite direction we want to travel.

If (gamemap.offset.x>=64.0f) {gamemap.offset.x-=64.0f; gamemap.tile.x--;}
If (gamemap.offset.x<0.0f) {gamemap.offset.x+=64.0f; gamemap.tile.x++;}

Notice we'll also need to make sure the hero doesn't collide with a tile along the way(careful about speeds exceeding tile widths - will need to use a collision test)
Also notice that we'll need to check if the tile.x is less than 0 or greater than TILEMAP_WIDTH...

I allow tolerance of images of about half the screen width and half the screen height - so if the width is 800 - then half is 400 and 400/64=6.25, so I would start testing visible tiles for rendering at 6 tiles left of the screen - which ends up being about -12.5... so we'd test to render from -13 to +13 horizontally and (with height 600) -10 to +10...

Something like this:

b=0;
do {
    a=-13;
    tiley=gamemap.tile.y+b;
    do {
        tilex=gamemap.tile.x+a;
        if (tile_visible(tilex,tiley)) draw_tile(tilex,tiley);
        a++;
    }while(a<=13);
    b++;
}while(b<=10);

Where tile_visible would be a function which uses the associated image widths and heights to decide whether to render the tile.

Hmmm... It might be a good idea to set up an array manager for debug purposes that will prevent and report any unexpected problems along the way.

 

Some other things I'd like to set up when I get some free time:

Some classes for managing textures, sounds, and so on to prevent problems with resource use. I will keep them EXTREMELY simple and minimal - they auto-initialize to NULL, check for redundancy, prevent resource memory leaks, etc...

OK - so I've made a lot of changes to how the code works - in the following tutorial updates - I'll show you the additions and how they explain work. There are 2 different ways you can mix 2D and 3D --- you can do it entirely in OBJECTSPACE or do it by drawing 3D stuff with 2D written over top of it (which so far I think I prefer).

Note:
I updated tutor1 to match the current code and I will provide the source code for download.

First I'll go over a few additional header files and c++ files that will be useful for the rest of this tutorial.

STEP 82:

Add a new header file to the project called editor_vars.h

The ALLOW_EDIT definition can be defined or commented out (in main.cpp) to switch between editor enabled compile and release-only mode compile.

  
  
  #ifdef ALLOW_EDIT
    //editor vars
    void get_editor_input(); //prototype
    TEXTURE         tile_types_texture;
    TEXTURE         edit_menu_texture;  
    TEXTURE         grid;
    LPD3DXLINE      line = NULL;
    
You may be wondering what TEXTURE is - it's a class that automatically sets up and destroys textures..
(which I'll show you in a bit - it will be in graphics.h)
get_editor_input -- is a prototype for a function we'll make later for getting editor input from user
tile_types_texture -- a texture holding graphics that indicate tile type (used in editor) 
edit_menu_texture -- graphics for edit menu
grid -- holds an image for a background grid (visual aid for setting up tiles)
line -- used for drawing selection box (rect of 4 lines) 
    
    bool            g_gamemap_changed=false;
    char            g_custom_file[100]="";
    int             g_reference_maker=0; //used for making unique reference #'s for tiles
        

g_gamemap_changed -- used for determining whether to prompt user to save existing game map
g_custom_file -- will be used to allow user to use a non-level based name for the level/gamemap they want to save
g_reference_maker -- if a tilemap change - we may need to match things up from old gamemap

    #define EDITOR_TEXTURE_LOADING \
    D3DXCreateLine(g_gpu, &line); \
    grid.LoadTexture("Graphics/grid.dds"); \
    tile_types_texture.LoadTexture("Graphics/tile_types.dds"); \
    edit_menu_texture.LoadTextureNonPow2("Graphics/editor_menu1.dds");

EDITOR_TEXTURE_LOADING creates the line and loads all textures used for editor images

    // I N I T  E D I T O R  V A R S
    #define INIT_EDITOR_VARS \
    int a=0,b=0; \
    SetNumLock(TRUE); \
    TEST_ARRAY(tile,520); \
    a=0; SAFE_DO; \
        ZeroMemory(&tile[a], sizeof tile[a]); \
        tile[a].depth=INFRONT; tile[a].effect=NONE; \
        tile[a].frame_direction=1; \
        VEC_ZERO(tile[a].offset); \
        SetRectEmpty(&tile[a].r); \
        tile[a].type=SOLID; tile[a].wait_time=0; \
    a++;}while(a<520); \
    TEST_ARRAY2(g_tiles,LEVEL_WIDTH+10,LEVEL_HEIGHT+10); \
    b=0; SAFE_DO; \
        a=0; SAFE_DO; \
            g_tiles[a][b].color=0xFFFFFFFF; g_tiles[a][b].type=-1; \
        a++;}while(a<LEVEL_WIDTH+10); \
    b++;}while(b<LEVEL_HEIGHT+10); 

Just made this to reduce code in main.cpp a bit... turns on the num lock key (see modified tutor1)
TEST_ARRAY is not necessary(just be careful) - you can remove it if you like - I have my own define for testing to prevent illegal memory access on arrays - ie: tile[n] where n=561 is illegal if tile is allocated as tile[520] and may cause computer to freeze

tile -- holds attributes of a tile type - such as dimensions, solid, liquid, passable, animation, etc... 
g_tiles -- is the actual gamemap itself - it holds info about what tiles go where in a 2D array(LEVEL_WIDTH x LEVEL_HEIGHT) 
I'll show the structs for these below. 

    #define RELEASE_EDITOR_STUFF \
    SAFE_RELEASE_FINAL(grid.tx); \
    SAFE_RELEASE_FINAL(tile_types_texture.tx); \
    SAFE_RELEASE_FINAL(edit_menu_texture.tx); \
    SAFE_RELEASE_FINAL(line); \
    SetNumLock(FALSE);

    #endif

The above just releases editor stuff and turns numlock off again. Numlock was turned on during editor use automatically so that numpad arrows could be used with my numpad input defs. 


STEP 83: 

Add a game_structs.h file to your header files. 

Here's what we add to it:


#define BEGIN_SCENE(p) g_gpu->Clear(0,NULL,D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, g_background_color , 1.0f, 0); g_gpu->BeginScene(); if (g_begin_scene) REPORT_S("begin scene already started ",p); g_begin_scene=true;   
#define END_SCENE(p) g_gpu->SetTexture(0,NULL); g_gpu->EndScene(); ghr=g_gpu->Present(NULL,NULL,NULL,NULL); if(ghr==D3DERR_DEVICELOST) {DeviceLost = true; } if (!g_begin_scene) REPORT_S("scene not began ",p); g_begin_scene=false;

I moved the above 2 definitions into here to reduce clutter in main.cpp

#define NUM_TYPES 6
#define MAX_LAYER_TYPES 4
#define MAX_TILE_ANIMATION 20
enum TILE_TYPE {PASS, SOLID, LIQUID, BOUNCE, BUTTON, JUMP_PASSABLE} this_tiletype; 
enum TILE_DEPTH {FAR_BEHIND, BEHIND, INFRONT, NEAR_INFRONT} this_tiledepth;
enum TILE_EFFECT {NONE, DAMAGE, SPIKE_UP, SPIKE_DOWN, SPIKE_LEFT, SPIKE_RIGHT, BREAKS, SLIPPERY, DOOR, PORTAL} this_tileeffect;
enum TILE_ANIM {NO_ANIM, SCROLL_X, SCROLL_Y, LOOPING, PING_PONG} this_tileanim;
enum TILE_LAYERING {LAYER_FLAT, LAYER_BOX, LAYER_DEPTH, LAYER_MAX} this_tilelayering;


NUM_TYPES -- how many types as in SOLID, LIQUID, PASSable, etc... 
MAX_LAYER_TYPES -- layer types determine how the graphics are layered. ie: multiple layers, single layer, etc... 
MAX_TILE_ANIMATION -- maximum number of frames an animated tile will hold
TILE_TYPE - is it solid, passable, bouncey, etc? 
TILE_DEPTH - what order should we draw it in so stuff closer is drawn last.. 
TILE_EFFECT - does this tile cause damage, break, cause sliding, is it a door, etc? 
TILE_ANIM - animation type - most are looping or ping_pong(123454321)
Scroll_X for example would scroll horizontally in place(like conveyor belt or something)
TILE_LAYERING - how to render it - flat, 3D box, add depth layers, depth layers with foreground layers


struct stTileType {
    RECT animation[MAX_TILE_ANIMATION+1]; 
    RECT r; 
    RECT layer2, layer3; //set layer.right=0 to set as non-layered  
    VEC offset; 
    int tiles_wide, tiles_high;
    enum TILE_TYPE type;   
    enum TILE_DEPTH depth; 
    enum TILE_EFFECT effect; 
    enum TILE_ANIM animation_type; 
    int layer_type;      
    int frame, num_frames;  //if animated, keep track of current frame      
    int frame_direction; //-1 or +1
    int wait_time, timer; //how long to wait before changing frames     
    VEC scroll_pos;
    RECT o; //for setting up the offsets
    int offx, offy; //for setting up offsets
    int ref; //reference # used for fixing changes to tiles when loading a level
};

The above struct, stTileType, is used to create a type of tile with assigned attributes for how it's rendered( and/or animated)
and to determine from where on the graphics texture, each tile image can be found. When we make a gamemap which will
consist of g_tiles[level_width][level_height] - each tile on the gamemap grid will point to a tile type like this (instead of assigning
attributes to every single gamemap tile). 



RECT animation[] -- a bunch of RECTs which hold source coordinates of each animation frame -- 
when animating something - each frame is rendered from a portion of the loaded level's graphics 
bitmap - each rect determines which portion of the bitmap holds each frame of animation 
RECT r -- the RECT which holds the source coordinates of the image for this tile 
(same as frame 1 if it is animated) 
RECT layer2, layer3 -- hold source coords of foreground layers (if they exist) for this tile

VEC offset -- holds the horizontal and vertical offset we would like it to be drawn at from 
the game-map tile it's associated with. So if we attach this tile to g_tile[10][25] for example 
-- it would normally be rendered exactly where g_tile[10][25] would appear on the screen(if onscreen) 
-- but with the applied offset - it would be moved horizontally and vertically from that 
coordinate based on the tile offset we set up ahead of time. That way I could apply a tree to 
the ground for example so the tile that has a tree attached to it is one right above the ground 
-- but if we drew it there - it draws from the upper-left corner of the image -- and since the 
tree is so large it would be into the ground - which is no good -- so we can set up an offset 
for this tile so it is always draw from up higher and slightly to the left of where we put it 
- so the tree-trunk would then attach to the ground where we put it. 

offset

struct stTile {
    int type; //allows modification of tile type
    int i;  //index of tile type
    int i2; //index of another tile image to use with this tile 
    int depth; 
    VEC offset;  //offset of individual tile (good for making scene look more varied)
    VEC offset2; //allow another tile to be placed on this one (use Z value for depth layering)
    float rotation, scale; 
    float rotation2, scale2;    
    int trigger;  //could be used to trigger a tile event of some type
    int has_item; //tile has had a key used on it or something hidden behind it (has_item = tile of item)
    D3DCOLOR color; //could be useful for some color effects, lighting or just tint variety 
    int m_a, m_b; //indices of the "master-tile" (top-left corner tile which controls the rest of the piece)
    VEC reserved_v; 
}; 

Even though we might not always want to use all these members - it doesn't hurt to have 
them for future use -- sometimes we can use them for things we didn't originally intend 
to use them for.
i -- the index of the type of tile this gamemap tile points to. (refer to stTileType)
i2 -- if we want to put another tile on top or behind this tile - we could use this
depth -- if we want to modify the depth characteristic of this individual gamemap tile 
(otherwise it just uses the default depth assigned to the tile type pointed to)
offset -- usually assigned to the default tile type offset -- but can be changed for 
individual tiles to add variety to how the scene is layed out 
(ie - if you wanted a shorter tree - you could sink it into the ground a bit)
offset2 -- same but for i2
rotation, scale -- to add variety to the scene
(individual gamemap tiles can be rotated and scaled a bit for effect)
has_item -- during gameplay - an individual tile can be assigned a variable or item for whatever reason
color -- tinting for fake lighting effects or whatever
m_a, m_b -- master tile -- topleft corner tile -- to make sure we only render once 
(master tile will be the controlling tile for all tiles this tile-type would occupy)
reserved_v -- could be useful in some animation events 

struct DRAWING_MEMORY{
    int a,b; //store index of tile
    int tn;//,tn2;
    VEC pos,pos2; //store position of actual tile image to be displayed on screen
};

DRAWING_MEMORY - during render operation for the tiles in the scene - we can store 
in an array(of this struct) the tiles, tile types and screen coordinates that were 
used - so that for layering or other purposes - we don't need to recalculate.

struct stBackgroundType{
    VEC pos1, pos2;
    float x, y;
    int width, height;
    RECT r1,r2; 
    TEXTURE texture;
    stBackgroundType() {
        VEC_ZERO(pos1); VEC_ZERO(pos2);
        x=y=0.0f;
        width=height=0;
        SetRectEmpty(&r1); SetRectEmpty(&r2);   
    }
};
stBackgroundType    g_far_background;
stBackgroundType    g_mid_background;

I moved the stBackgroundType struct into here and modified it a bit. 
The Gamescroll function will be different now too. I decided to simplify it a bit so 
that 2 complete background images are rendered side-by-side as they scroll. 
stBackgroundType() is a constructor which initializes the member variables automatically. 

For the 3D cube stuff - we'll need a vertex list of type stMyVertex (as defined in main_defs.h)
to make a cube(which can be scaled to whatever size). 
The first 3 members - x,y,z are the coordinates of each point. 
The next 3 coords represent the normals of each corner (I made them the same for each face)
If you want to make the shading look "rounder" - you could mess with the corner normals making
them an average face normal of surrounding faces... 
The default color for all the faces is 100% opaque and full color (0xFFFFFFFF) 
The texture coordinates are standard and set so each side will have a copy of the texture image. 

stMyVertex g_box_verts[] ={
   {-1.0f,-1.0f,-1.0f,   0.0f, 0.0f,-1.0f,   0xFFFFFFFF, 0.0f, 1.0f },  //Front face
   {-1.0f, 1.0f,-1.0f,   0.0f, 0.0f,-1.0f,   0xFFFFFFFF, 0.0f, 0.0f },  { 1.0f, 1.0f,-1.0f,   0.0f, 0.0f,-1.0f,   0xFFFFFFFF, 1.0f, 0.0f },  { 1.0f, 1.0f,-1.0f,   0.0f, 0.0f,-1.0f,   0xFFFFFFFF, 1.0f, 0.0f }, { 1.0f,-1.0f,-1.0f,   0.0f, 0.0f,-1.0f,   0xFFFFFFFF, 1.0f, 1.0f }, {-1.0f,-1.0f,-1.0f,   0.0f, 0.0f,-1.0f,   0xFFFFFFFF, 0.0f, 1.0f },
   { 1.0f,-1.0f, 1.0f,   0.0f, 0.0f, 1.0f,   0xFFFFFFFF, 0.0f, 1.0f },  //Back face
   { 1.0f, 1.0f, 1.0f,   0.0f, 0.0f, 1.0f,   0xFFFFFFFF, 0.0f, 0.0f },{-1.0f, 1.0f, 1.0f,   0.0f, 0.0f, 1.0f,   0xFFFFFFFF, 1.0f, 0.0f },  {-1.0f, 1.0f, 1.0f,   0.0f, 0.0f, 1.0f,   0xFFFFFFFF, 1.0f, 0.0f },{-1.0f,-1.0f, 1.0f,   0.0f, 0.0f, 1.0f,   0xFFFFFFFF, 1.0f, 1.0f },{ 1.0f,-1.0f, 1.0f,   0.0f, 0.0f, 1.0f,   0xFFFFFFFF, 0.0f, 1.0f },
   {-1.0f, 1.0f,-1.0f,   0.0f, 1.0f, 0.0f,   0xFFFFFFFF, 0.0f, 1.0f },  //Top face
   {-1.0f, 1.0f, 1.0f,   0.0f, 1.0f, 0.0f,   0xFFFFFFFF, 0.0f, 0.0f },{ 1.0f, 1.0f, 1.0f,   0.0f, 1.0f, 0.0f,   0xFFFFFFFF, 1.0f, 0.0f },{ 1.0f, 1.0f, 1.0f,   0.0f, 1.0f, 0.0f,   0xFFFFFFFF, 1.0f, 0.0f },{ 1.0f, 1.0f,-1.0f,   0.0f, 1.0f, 0.0f,   0xFFFFFFFF, 1.0f, 1.0f },{-1.0f, 1.0f,-1.0f,   0.0f, 1.0f, 0.0f,   0xFFFFFFFF, 0.0f, 1.0f },
   { 1.0f,-1.0f,-1.0f,   0.0f,-1.0f, 0.0f,   0xFFFFFFFF, 0.0f, 1.0f },  //Bottom face
   { 1.0f,-1.0f, 1.0f,   0.0f,-1.0f, 0.0f,   0xFFFFFFFF, 0.0f, 0.0f },{-1.0f,-1.0f, 1.0f,   0.0f,-1.0f, 0.0f,   0xFFFFFFFF, 1.0f, 0.0f },{-1.0f,-1.0f, 1.0f,   0.0f,-1.0f, 0.0f,   0xFFFFFFFF, 1.0f, 0.0f },{-1.0f,-1.0f,-1.0f,   0.0f,-1.0f, 0.0f,   0xFFFFFFFF, 1.0f, 1.0f },{ 1.0f,-1.0f,-1.0f,   0.0f,-1.0f, 0.0f,   0xFFFFFFFF, 0.0f, 1.0f },
   {-1.0f,-1.0f, 1.0f,  -1.0f, 0.0f, 0.0f,   0xFFFFFFFF, 0.0f, 1.0f },  //Left face
   {-1.0f, 1.0f, 1.0f,  -1.0f, 0.0f, 0.0f,   0xFFFFFFFF, 0.0f, 0.0f },{-1.0f, 1.0f,-1.0f,  -1.0f, 0.0f, 0.0f,   0xFFFFFFFF, 1.0f, 0.0f }, {-1.0f, 1.0f,-1.0f,  -1.0f, 0.0f, 0.0f,   0xFFFFFFFF, 1.0f, 0.0f },{-1.0f,-1.0f,-1.0f,  -1.0f, 0.0f, 0.0f,   0xFFFFFFFF, 1.0f, 1.0f },{-1.0f,-1.0f, 1.0f,  -1.0f, 0.0f, 0.0f,   0xFFFFFFFF, 0.0f, 1.0f },
   { 1.0f,-1.0f,-1.0f,   1.0f, 0.0f, 0.0f,   0xFFFFFFFF, 0.0f, 1.0f },  //Right face
   { 1.0f, 1.0f,-1.0f,   1.0f, 0.0f, 0.0f,   0xFFFFFFFF, 0.0f, 0.0f },{ 1.0f, 1.0f, 1.0f,   1.0f, 0.0f, 0.0f,   0xFFFFFFFF, 1.0f, 0.0f },{ 1.0f, 1.0f, 1.0f,   1.0f, 0.0f, 0.0f,   0xFFFFFFFF, 1.0f, 0.0f },{ 1.0f,-1.0f, 1.0f,   1.0f, 0.0f, 0.0f,   0xFFFFFFFF, 1.0f, 1.0f },{ 1.0f,-1.0f,-1.0f,   1.0f, 0.0f, 0.0f,   0xFFFFFFFF, 0.0f, 1.0f },
};
#define NUM_BOX_VERTICES (sizeof(g_box_verts)/sizeof(stMyVertex))

NUM_BOX_VERTICES is just a handy way to always have the number of box vertices on hand. 
In the future - for more complex 3D objects - It's probably a good idea to set up code to load
some .X files -- I may show this later.. 

STEP 84:

We'll need a class for setting up textures (auto-initialize via constructor) and for automatically releasing texture resources 
apon program exit to prevent memory leaks. We still need the cleanup graphics function to release all resources though because
if we lose the device - it works best to release everything and reset and reload everything. If the program needs to abort
for whatever reason - it is good to have destructors. 

If you haven't already, add a header file called graphics.h

Add the following code: 


#ifndef GRAPHICS_H
#define GRAPHICS_H

#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include <d3dx9.h>
#include "common.h"
#include "misc.h"

misc.h we'll add after -- it will just have some support functions like: bool FileExists()

extern LPDIRECT3DDEVICE9 g_gpu;
extern int g_width,g_height;
extern "C" char * Problem;
extern HRESULT     ghr;

extern void FatalError(const char * error_msg);

^Just some stuff in the main program we'll need access to. 

void GetScreenShot(LPCTSTR file, int user);

^GetScreenShot could come in handy so the user can take screen shots or for making thumbnails for save points
The TEXTURE class below as you can see is pretty simple. It has a DIRECT3DTEXTURE9 called tx for holding the directX
texture, and has the width and height info handy. 
The constructor, TEXTURE(), just initialized the variables (very important that tx is set to NULL) and the
destructor, ~TEXTURE() releases apon program exit. Normally the destructor won't need to release it unless there's
a missing release in the graphics cleanup function or if something goes wrong and the program must exit. 

class TEXTURE {
    public:
    LPDIRECT3DTEXTURE9 tx;
    int width, height;
    TEXTURE() {
        tx=NULL;
        width=0; height=0;
    }
    ~TEXTURE() {
      if (tx!=NULL) REPORT("Texture released via destructor.");
      SAFE_RELEASE_FINAL(tx); 
    }   
    void LoadTexture(LPCTSTR filename) {
        D3DXIMAGE_INFO info;
        if (tx!=NULL) SAFE_RELEASE(tx); 
        if (FileExists(filename)) {
            ghr=D3DXCreateTextureFromFileEx(g_gpu, filename, D3DX_DEFAULT, D3DX_DEFAULT, 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, D3DX_FILTER_NONE, D3DX_FILTER_NONE, 0, &info, NULL, &tx); 
            if (ghr!=D3D_OK) { REPORT_S("texture file error: ",filename); if (ghr==D3DERR_NOTAVAILABLE) PROBLEM("Can't create texture from file: D3DERR_NOTAVAILABLE"); if (ghr==D3DERR_OUTOFVIDEOMEMORY) PROBLEM("Can't create texture from file: D3DERR_OUTOFVIDEOMEMORY"); if (ghr==D3DERR_INVALIDCALL) PROBLEM("Can't create texture from file: D3DERR_INVALIDCALL") if (ghr==D3DXERR_INVALIDDATA) PROBLEM("Can't create texture from file: D3DXERR_INVALIDDATA \nCan't find file.");   if (ghr==E_OUTOFMEMORY) PROBLEM("Can't create texture from file: E_OUTOFMEMORY"); FATAL("D3DXCreateTextureFromFile",Problem); }
            width=info.Width; height=info.Height;
        } else FatalError("missing or corrupted file - please reinstall game or contact me: contact@AlienScribbleInteractive");
    }
    void LoadTextureNonPow2(LPCTSTR filename) {
        D3DXIMAGE_INFO info;
        if (tx!=NULL) SAFE_RELEASE(tx);
        if (FileExists(filename)) {
            ghr=D3DXCreateTextureFromFileEx(g_gpu, filename, D3DX_DEFAULT_NONPOW2, D3DX_DEFAULT_NONPOW2,
            1, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, D3DX_FILTER_NONE, D3DX_FILTER_NONE, 0, &info, NULL, &tx);
            if (ghr!=D3D_OK) { if (ghr==D3DERR_NOTAVAILABLE) PROBLEM("Can't create texture from file: D3DERR_NOTAVAILABLE") if (ghr==D3DERR_OUTOFVIDEOMEMORY) PROBLEM("Can't create texture from file: D3DERR_OUTOFVIDEOMEMORY"); if (ghr==D3DERR_INVALIDCALL) PROBLEM("Can't create texture from file: D3DERR_INVALIDCALL"); if (ghr==D3DXERR_INVALIDDATA) PROBLEM("Can't create texture from file: D3DXERR_INVALIDDATA \nCan't find file."); if (ghr==E_OUTOFMEMORY) PROBLEM("Can't create texture from file: E_OUTOFMEMORY"); FATAL("D3DXCreateTextureFromFile",Problem); } 
            width=info.Width; height=info.Height;
        } else FatalError("missing or corrupted file - please reinstall game or contact me: contact@AlienScribbleInteractive");     
    }   
};

#endif

As above loading functions are pretty straight forward. Whenever loading graphics for uneven sized bitmaps (ie: 382x255) you should use LoadTextureNonPow2... but for common even textures(ie: 256x256, 512x512 or 1024x1024, etc..) you should just call LoadTexture.

STEP 85:

Add a misc.h file to your project and add the following code:

 


#ifndef MISC_H
#define MISC_H
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include <d3d9.h>
#include <d3dx9.h>
#include "common.h"
#include "math.h"

extern "C" char * Problem;
extern VEC mouse_pos;

bool FileExists(const char* filename);
void UpdateMousePos(LPPOINT xy);
bool mouse_inside(RECT pos);
void MoveRect(RECT &r,LONG x, LONG y);

#endif 

Not sure what all I might add to this yet -- but so far I have a function for checking for a file's existence,
one for updating the mouse_pos variables, one for checking if the mouse is inside a RECT region, 
and one just for moving a RECT. 

STEP 86:

Add the misc.cpp file to the project and add the following code:

#include "misc.h"
//support functions:

// file stuff, mouse stuff, and rect stuff
//If error returned on an attempt to open the file is not 0 then report the problem to debug.txt and return false:
// F I L E  E X I S T S
bool FileExists(const char* filename) {
    FILE* fp = NULL;
    errno_t err;
    err=fopen_s(&fp,filename, "rb");
    if(err==0) {
        fclose(fp);
        return true;
    }
    REPORT_S("The following file does not exist: ",filename); PROBLEM("File not found"); return false;
}

//seems almost pointless to have a function for this - just takes the mouse LPPOINT passed to it and sets the mouse_pos VEC
// U P D A T E  M O U S E  P O S
void UpdateMousePos(LPPOINT xy) {   
    LONG x=xy->x; LONG y=xy->y;
    if (x<0) x=0; if (y<0) y=0;
    mouse_pos.x=(float)x; mouse_pos.y=(float)y;
}


// see if the mouse is within a rectangle region
// M O U S E  C L I C K E D  I N S I D E
bool mouse_inside(RECT pos) {   
    if ((mouse_pos.x>(float)pos.left)&&(mouse_pos.x<(float)pos.right)) {
        if ((mouse_pos.y>(float)pos.top)&&(mouse_pos.y<(float)pos.bottom)) {
            return true;            
        }
    }
    return false;
}


//just moves a rect r by distance x and y
// M O V E  R E C T
void MoveRect(RECT &r,LONG x, LONG y) {
    r.left+=x; r.right+=x; r.top+=y; r.bottom+=y;
}


STEP 87: 

Add graphics.cpp to the project and add the following code:

(so far all I have for graphics support functions in this module is the function for taking a screenshot)


#include "graphics.h"

//----------------------------
// G E T  S C R E E N  S H O T 
//----------------------------
void GetScreenShot(LPCTSTR file, int user=-1) {
HRESULT hr; 
char str[30];
//The front buffer used to get the screenshot   
LPDIRECT3DSURFACE9 buffer = NULL;       
// Create a surface for the screenshot to be rendered to    
if(FAILED(hr=g_gpu->CreateOffscreenPlainSurface(g_width,g_height,   
   D3DFMT_A8R8G8B8, D3DPOOL_SCRATCH, &buffer, NULL))) {REPORT("Unable to create offscreen surface"); return;}
    // Write the front buffer to the surface    
    if( FAILED(hr=g_gpu->GetFrontBufferData(0,buffer))) {
            REPORT("Cannot Acquire front buffer...");
            buffer->Release();                      
            return;
    }
    if (user==-1) D3DXSaveSurfaceToFile(file, D3DXIFF_BMP, buffer, NULL, NULL );
    else {
        sprintf_s(str, _T("%d.bmp"), user);
        D3DXSaveSurfaceToFile(str, D3DXIFF_BMP, buffer, NULL, NULL );
    }
    buffer->Release();
}

So basically what we did here was create a memory area to store the rendered screen image (front buffer) 
into and then transfer the data from the front buffer(from gpu hardware memory) into the surface memory buffer
(on system RAM which we can directly access). 
We could have copied from the backbuffer - provided we're sure it's done drawing to backbuffer(right before buffer swap) - 
hence this is the way I'd prefer to do it. 
We then use the supplied filename to save the file. I was going to make it so that if the user was set to -2, it would save the 
image as a thumbnail for save points(might do this later)  -- otherwise it's just a regular thumbnail (-1) -- 
but if the var user is a positive number - it will save it to (whatever the number is - ie: 1.bmp or 2.bmp) -- 
this is useful for getting a series of shots for whatever reason(like making an animated gif of an event)

STEP 88:

Before moving on - best to make sure all our headers are in headers.h

#include <d3dx9.h>
#include <Dxerr.h>
#include <windows.h>
#include <mmsystem.h>
#include <stdio.h>
#include <time.h>
#include <tchar.h>
#include "resource.h"
#include "main_defs.h"
#include "startup.h"
#include "GameInput.h"
#include "math.h"
#include "graphics.h"
#include "game_structs.h"
#include "debug.h"

On the following page - I'll show you the new main.cpp and discuss what all the code is for and how it works.
Most of the new code is for the gamemap / tilemap editor.
The tile editor allows the user to load a bitmap and assign properties to sections of images that are made into tiles which can later be placed in the gamemap grid (and are normally rendered according to their default tile settings - unless explicitly altered on the gamemap).

Onto the gamemap editor...

Tutorial 7 - Editor

 

Return to Home Page