Sign in to follow this  

Tetris for beginners help!

This topic is 3200 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I have recently looked into the article about tetris for beginners, http://www.gamedev.net/community/forums/topic.asp?topic_id=192483&PageSize=25&WhichPage=1 and I use Dev-C++, and I have included the "blocks.bmp" file in the project along with the others... and it compiles and runs fine, except its just a black screen...help!

Share this post


Link to post
Share on other sites
//FALLING BLOCK GAME!
//main.cpp
//tell compiler to not include many unneeded header files.
#define WIN32_LEAN_AND_MEAN
//need this for windows stuff.
#include <windows.h>
//need this for srand and rand
#include <stdlib.h>
//now let's include our bitmapobject definitions
#include "bitmapobject.h"

//let's give our window a name
#define WINDOWCLASS "FallingBlockGame"
//let's give our window a title...er caption.
#define WINDOWTITLE "A Falling Block Game!"

//since we're using square blocks, let's only use a single size.
const int TILESIZE=16;
//now for the map...
const int MAPWIDTH=10;
const int MAPHEIGHT=30;
const int GREY=8;

const int TILENODRAW=-1;
const int TILEBLACK=0;
const int TILEGREY=1;
const int TILEBLUE=2;
const int TILERED=3;
const int TILEGREEN=4;
const int TILEYELLOW=5;
const int TILEWHITE=6;
const int TILESTEEL=7;
const int TILEPURPLE=8;

bool GameInit(); // game initialization function
void GameLoop(); //where the game actually takes place
void GameDone(); //clean up!
void DrawTile(int x, int y, int tile); //coordinates & tile type
void DrawMap(); //draw the whole map.. render function, basically
void NewBlock(); //create a new block!
void RotateBlock(); //rotate a block.. if you can!
void Move(int x, int y); //coordinates to move.
int CollisionTest(int nx, int ny); //test collision of blocks
void RemoveRow(int row); //remove a row.. that would be the 'x'.
void NewGame(); //make a new game!

HINSTANCE hInstMain=NULL; //main app handle
HWND hWndMain=NULL; //main window handle

int Map[MAPWIDTH][MAPHEIGHT+1]; //the game map!

struct Piece {
int size[4][4];
int x;
int y;
};

Piece sPrePiece; //preview piece.
Piece sPiece; //the 's' prefixes indicate this is a 'structure'

DWORD start_time; //used in timing
bool GAMESTARTED=false; //used by NewBlock()

//map for the program
BitMapObject bmoMap;
//block images
BitMapObject bmoBlocks;

LRESULT CALLBACK TheWindowProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
//which message did we get?
switch(uMsg)
{
case WM_KEYDOWN:
{
//check for escape key
if(wParam==VK_ESCAPE)
{
DestroyWindow(hWndMain);
return(0);//handled message
}
else if(wParam==VK_DOWN) //check for down arrow key
{
Move(0,1);
return(0);//handled message
}
else if(wParam==VK_UP) //check for up arrow key
{
RotateBlock();
return(0);//handled message
}
else if(wParam==VK_LEFT) //check for left arrow key
{
Move(-1,0);
return(0);//handled message
}
else if(wParam==VK_RIGHT) //check for right arrow key
{
Move(1,0);
return(0);//handled message
}
}break;
case WM_DESTROY://the window is being destroyed
{

//tell the application we are quitting
PostQuitMessage(0);

//handled message, so return 0
return(0);

}break;
case WM_PAINT://the window needs repainting
{
//a variable needed for painting information
PAINTSTRUCT ps;

//start painting
HDC hdc=BeginPaint(hwnd,&ps);

//redraw the map
BitBlt(hdc,0,0,TILESIZE*MAPWIDTH+TILESIZE*GREY,TILESIZE*MAPHEIGHT,bmoMap,0,0,SRCCOPY);

//end painting
EndPaint(hwnd,&ps);

//handled message, so return 0
return(0);
}break;
}

//pass along any other message to default message handler
return(DefWindowProc(hwnd,uMsg,wParam,lParam));
}

int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nShowCmd)
{
//assign instance to global variable
hInstMain=hInstance;

//create window class
WNDCLASSEX wcx;

//set the size of the structure
wcx.cbSize=sizeof(WNDCLASSEX);

//class style
wcx.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;

//window procedure
wcx.lpfnWndProc=TheWindowProc;

//class extra
wcx.cbClsExtra=0;

//window extra
wcx.cbWndExtra=0;

//application handle
wcx.hInstance=hInstMain;

//icon
wcx.hIcon=LoadIcon(NULL,IDI_APPLICATION);

//cursor
wcx.hCursor=LoadCursor(NULL,IDC_ARROW);

//background color
wcx.hbrBackground=(HBRUSH)GetStockObject(BLACK_BRUSH);

//menu
wcx.lpszMenuName=NULL;

//class name
wcx.lpszClassName=WINDOWCLASS;

//small icon
wcx.hIconSm=NULL;

//register the window class, return 0 if not successful
if(!RegisterClassEx(&wcx)) return(0);

//create main window
hWndMain=CreateWindowEx(0,WINDOWCLASS,WINDOWTITLE, WS_BORDER | WS_SYSMENU | WS_CAPTION| WS_VISIBLE,0,0,320,240,NULL,NULL,hInstMain,NULL);

//error check
if(!hWndMain) return(0);

//if program initialization failed, then return with 0
if(!GameInit()) return(0);

//message structure
MSG msg;

//message pump
for( ; ; )
{
//look for a message
if(PeekMessage(&msg,NULL,0,0,PM_REMOVE))
{
//there is a message

//check that we arent quitting
if(msg.message==WM_QUIT) break;

//translate message
TranslateMessage(&msg);

//dispatch message
DispatchMessage(&msg);
}

//run main game loop
GameLoop();

}

//clean up program data
GameDone();

//return the wparam from the WM_QUIT message
return(msg.wParam);
}

bool GameInit()
{
//set the client area size
RECT rcTemp;
SetRect(&rcTemp,0,0,MAPWIDTH*TILESIZE+TILESIZE*GREY,MAPHEIGHT*TILESIZE);//160x480 client area
AdjustWindowRect(&rcTemp,WS_BORDER | WS_SYSMENU | WS_CAPTION| WS_VISIBLE,FALSE);//adjust the window size based on desired client area
SetWindowPos(hWndMain,NULL,0,0,rcTemp.right-rcTemp.left,rcTemp.bottom-rcTemp.top,SWP_NOMOVE);//set the window width and height

//create map image
HDC hdc=GetDC(hWndMain);
bmoMap.Create(hdc,MAPWIDTH*TILESIZE+TILESIZE*GREY,MAPHEIGHT*TILESIZE);
FillRect(bmoMap,&rcTemp,(HBRUSH)GetStockObject(BLACK_BRUSH));
ReleaseDC(hWndMain,hdc);

bmoBlocks.Load(NULL,"blocks.bmp");
NewGame();

return(true);//return success
}

void GameDone()
{
//clean up code goes here
}

void GameLoop()
{
if( (GetTickCount() - start_time) > 1000)
{
Move(0,1);
start_time=GetTickCount();
}

}

void NewGame()
{
start_time=GetTickCount();
GAMESTARTED=false;

//start out the map
for(int x=0;x< MAPWIDTH;x++)
{
for(int y=0;y< MAPHEIGHT+1;y++)
{
if(y==MAPHEIGHT) //makes Y-collision easier.
Map[x][y]=TILEGREY;
else
Map[x][y]=TILEBLACK;
}
}
NewBlock();

DrawMap();
}


void DrawTile(int x,int y,int tile)//put a tile
{
//mask first
BitBlt(bmoMap,x*TILESIZE,y*TILESIZE,TILESIZE,TILESIZE,bmoBlocks,tile*TILESIZE,TILESIZE,SRCAND);
//then image
BitBlt(bmoMap,x*TILESIZE,y*TILESIZE,TILESIZE,TILESIZE,bmoBlocks,tile*TILESIZE,0,SRCPAINT);
}

void DrawMap()//draw screen
{
int xmy, ymx;

//place the toolbar
for(xmy=MAPWIDTH; xmy< MAPWIDTH+GREY; xmy++)
for(ymx=0; ymx< MAPHEIGHT; ymx++)
DrawTile(xmy, ymx, TILEGREY);

//draw preview block
for(xmy=0; xmy<4; xmy++)
for(ymx=0; ymx<4; ymx++)
if(sPrePiece.size[xmy][ymx] != TILENODRAW)
DrawTile(sPrePiece.x+xmy, sPrePiece.y+ymx, sPrePiece.size[xmy][ymx]);

//draw the map
//loop through the positions
for(xmy=0;xmy< MAPWIDTH;xmy++)
for(ymx=0;ymx< MAPHEIGHT;ymx++)
DrawTile(xmy,ymx,Map[xmy][ymx]);

//draw moving block
for(xmy=0; xmy<4; xmy++)
for(ymx=0; ymx<4; ymx++)
if(sPiece.size[xmy][ymx] != TILENODRAW)
DrawTile(sPiece.x+xmy, sPiece.y+ymx, sPiece.size[xmy][ymx]);

//invalidate the window rect
InvalidateRect(hWndMain,NULL,FALSE);
}


void NewBlock()
{
int newblock;
int i,j;
// 0 1 2 3 4 5 6
// X These
// X XX X XX XX XX XX are
// X XX XXX XX XX X X block
// X X X types

//begin game! make generate a block and then one in preview.

srand(GetTickCount());


//initialize the piece to all blank.
for(i=0; i<4; i++)
for(j=0; j<4; j++)
sPiece.size[ i ][j]=TILENODRAW;

sPiece.x=MAPWIDTH/2-2;
sPiece.y=-1;

//let's see if the game's started yet
if(GAMESTARTED == false)
{
//guess not..
//Generate a piece right off.
//From now on, use previous preview block.
GAMESTARTED=true;

newblock=rand()%7;

switch (newblock)
{
case 0: //Tower!
{
sPiece.size[1][0]=TILERED;
sPiece.size[1][1]=TILERED;
sPiece.size[1][2]=TILERED;
sPiece.size[1][3]=TILERED;
sPiece.y=0;
}break;
case 1: //Box!
{
sPiece.size[1][1]=TILEBLUE;
sPiece.size[1][2]=TILEBLUE;
sPiece.size[2][1]=TILEBLUE;
sPiece.size[2][2]=TILEBLUE;
}break;
case 2: //Pyramid!
{
sPiece.size[1][1]=TILESTEEL;
sPiece.size[0][2]=TILESTEEL;
sPiece.size[1][2]=TILESTEEL;
sPiece.size[2][2]=TILESTEEL;
}break;
case 3://Left Leaner
{
sPiece.size[0][1]=TILEYELLOW;
sPiece.size[1][1]=TILEYELLOW;
sPiece.size[1][2]=TILEYELLOW;
sPiece.size[2][2]=TILEYELLOW;
}break;
case 4://Right Leaner
{
sPiece.size[2][1]=TILEGREEN;
sPiece.size[1][1]=TILEGREEN;
sPiece.size[1][2]=TILEGREEN;
sPiece.size[0][2]=TILEGREEN;
}break;
case 5://Left Knight
{
sPiece.size[1][1]=TILEWHITE;
sPiece.size[2][1]=TILEWHITE;
sPiece.size[2][2]=TILEWHITE;
sPiece.size[2][3]=TILEWHITE;
}break;
case 6://Right Knight
{
sPiece.size[2][1]=TILEPURPLE;
sPiece.size[1][1]=TILEPURPLE;
sPiece.size[1][2]=TILEPURPLE;
sPiece.size[1][3]=TILEPURPLE;
}break;
}
}
else
{
for(i=0; i<4; i++)
for(j=0; j<4; j++)
sPiece.size[ i ][j]=sPrePiece.size[ i ][j];

}

newblock=rand()%7;

for(i=0; i<4; i++)
for(j=0; j<4; j++)
sPrePiece.size[ i ][j]=TILENODRAW;

sPrePiece.x=MAPWIDTH+GREY/4;
sPrePiece.y=GREY/4;

switch (newblock)
{
case 0: //Tower!
{
sPrePiece.size[1][0]=TILERED;
sPrePiece.size[1][1]=TILERED;
sPrePiece.size[1][2]=TILERED;
sPrePiece.size[1][3]=TILERED;
}break;
case 1: //Box!
{
sPrePiece.size[1][1]=TILEBLUE;
sPrePiece.size[1][2]=TILEBLUE;
sPrePiece.size[2][1]=TILEBLUE;
sPrePiece.size[2][2]=TILEBLUE;
}break;
case 2: //Pyramid!
{
sPrePiece.size[1][1]=TILESTEEL;
sPrePiece.size[0][2]=TILESTEEL;
sPrePiece.size[1][2]=TILESTEEL;
sPrePiece.size[2][2]=TILESTEEL;
}break;
case 3://Left Leaner
{
sPrePiece.size[0][1]=TILEYELLOW;
sPrePiece.size[1][1]=TILEYELLOW;
sPrePiece.size[1][2]=TILEYELLOW;
sPrePiece.size[2][2]=TILEYELLOW;
}break;
case 4://Right Leaner
{
sPrePiece.size[2][1]=TILEGREEN;
sPrePiece.size[1][1]=TILEGREEN;
sPrePiece.size[1][2]=TILEGREEN;
sPrePiece.size[0][2]=TILEGREEN;
}break;
case 5://Left Knight
{
sPrePiece.size[1][1]=TILEWHITE;
sPrePiece.size[2][1]=TILEWHITE;
sPrePiece.size[2][2]=TILEWHITE;
sPrePiece.size[2][3]=TILEWHITE;
}break;
case 6://Right Knight
{
sPrePiece.size[2][1]=TILEPURPLE;
sPrePiece.size[1][1]=TILEPURPLE;
sPrePiece.size[1][2]=TILEPURPLE;
sPrePiece.size[1][3]=TILEPURPLE;
}break;
}

DrawMap();
}

void RotateBlock()
{
int i, j, temp[4][4];

//copy &rotate the piece to the temporary array
for(i=0; i<4; i++)
for(j=0; j<4; j++)
temp[3-j][ i ]=sPiece.size[ i ][j];

//check collision of the temporary array with map borders
for(i=0; i<4; i++)
for(j=0; j<4; j++)
if(temp[ i ][j] != TILENODRAW)
if(sPiece.x + i < 0 || sPiece.x + i > MAPWIDTH - 1 ||
sPiece.y + j < 0 || sPiece.y + j > MAPHEIGHT - 1)
return;

//check collision of the temporary array with the blocks on the map
for(int x=0; x< MAPWIDTH; x++)
for(int y=0; y< MAPHEIGHT; y++)
if(x >= sPiece.x && x < sPiece.x + 4)
if(y >= sPiece.y && y < sPiece.y +4)
if(Map[x][y] != TILEBLACK)
if(temp[x - sPiece.x][y - sPiece.y] != TILENODRAW)
return;

//end collision check

//successful! copy the rotated temporary array to the original piece
for(i=0; i<4; i++)
for(j=0; j<4; j++)
sPiece.size[ i ][j]=temp[ i ][j];

DrawMap();

return;
}

void Move(int x, int y)
{
if(CollisionTest(x, y))
{
if(y == 1)
{
if(sPiece.y<1)
{
//you lose! new game.
NewGame();
}
else
{
bool killblock=false;
int i,j;
//new block time! add this one to the list!
for(i=0; i<4; i++)
for(j=0; j<4; j++)
if(sPiece.size[ i ][j] != TILENODRAW)
Map[sPiece.x+i][sPiece.y+j] = sPiece.size[ i ][j];

//check for cleared row!
for(j=0; j< MAPHEIGHT; j++)
{
bool filled=true;
for(i=0; i< MAPWIDTH; i++)
if(Map[ i ][j] == TILEBLACK)
filled=false;

if(filled)
{
RemoveRow(j);
killblock=true;
}
}

if(killblock)
{
for(i=0; i<4; i++)
for(j=0; j<4; j++)
sPiece.size[ i ][j]=TILENODRAW;
}
NewBlock();
}
}

}
else
{
sPiece.x+=x;
sPiece.y+=y;
}

DrawMap();
}

int CollisionTest(int nx, int ny)
{
int newx=sPiece.x+nx;
int newy=sPiece.y+ny;

int i,j,x,y;

for(i=0; i< 4; i++)
for(j=0; j< 4; j++)
if(sPiece.size[ i ][j] != TILENODRAW)
if(newx + i < 0 || newx + i > MAPWIDTH - 1 ||
newy + j < 0 || newy + j > MAPHEIGHT - 1)
return 1;

for(x=0; x< MAPWIDTH; x++)
for(y=0; y< MAPHEIGHT; y++)
if(x >= newx && x < newx + 4)
if(y >= newy && y < newy +4)
if(Map[x][y] != TILEBLACK)
if(sPiece.size[x - newx][y - newy] != TILENODRAW)
return 1;
return 0;
}


void RemoveRow(int row)
{
int x,y;
int counter=0;

for(x=0; x< MAPWIDTH; x++)
for(y=row; y>0; y--)
Map[x][y]=Map[x][y-1];

}

























//BitMapObject.h
#ifndef BITMAPOBJECT_H
#define BITMAPOBJECT_H
#pragma once
//we need this for windows stuff.
#include <windows.h>

class BitMapObject
{
private:
//memory dc
HDC hdcMemory;
//new bitmap!
HBITMAP hbmNewBitMap;
//old bitmap.
HBITMAP hbmOldBitMap;
//width & height as integers.
int iWidth;
int iHeight;

public:
//constructor
BitMapObject();

//destructor
~BitMapObject();

//loads bitmap from a file
void Load(HDC hdcCompatible,LPCTSTR lpszFilename);

//creates a blank bitmap
void Create(HDC hdcCompatible, int width, int height);

//destroys bitmap and dc
void Destroy();

//return width
int GetWidth();

//return height
int GetHeight();

//converts to HDC
operator HDC();
};

#endif


















//BitMapObject.cpp
#include "bitmapobject.h"

BitMapObject::BitMapObject()
{
hdcMemory=NULL;
hbmNewBitMap=NULL;
hbmOldBitMap=NULL;
iWidth=0;
iHeight=0;
}

BitMapObject::~BitMapObject()
{
//if the hdcMemory hasn't been destroyed, do so
if(hdcMemory)
Destroy();
}

void BitMapObject::Load(HDC hdcCompatible, LPCTSTR lpszFilename)
{
//if hdcMemory isn't null, make it so captain!
if(hdcMemory)
Destroy();

//create memory dc.
hdcMemory=CreateCompatibleDC(hdcCompatible);
//load the bitmap
hbmNewBitMap=(HBITMAP)LoadImage(NULL,lpszFilename,IMAGE_BITMAP,0,0,LR_LOADFROMFILE);
//shove the image into the dc
hbmOldBitMap=(HBITMAP)SelectObject(hdcMemory,hbmNewBitMap);
//grab the bitmap's properties
BITMAP bmp;
GetObject(hbmNewBitMap,sizeof(BITMAP),(LPVOID)&bmp);
//grab the width & height
iWidth=bmp.bmWidth;
iHeight=bmp.bmHeight;
}

void BitMapObject::Create(HDC hdcCompatible, int width, int height)
{
//if hdcMemory isn't null, blow it up!
if(hdcMemory)
Destroy();

//create the memory dc.
hdcMemory=CreateCompatibleDC(hdcCompatible);
//create the bitmap
hbmNewBitMap=CreateCompatibleBitmap(hdcCompatible, width, height);
//shove the image into the dc
hbmOldBitMap=(HBITMAP)SelectObject(hdcMemory, hbmNewBitMap);
//change the width and height.
iWidth=width;
iHeight=height;
}

void BitMapObject::Destroy()
{
//restore old bitmap.
SelectObject(hdcMemory, hbmOldBitMap);
//delete new bitmap.
DeleteObject(hbmNewBitMap);
//delete device context.
DeleteDC(hdcMemory);
//set members to 0/NULL
hdcMemory=NULL;
hbmNewBitMap=NULL;
hbmOldBitMap=NULL;
iWidth=0;
iHeight=0;
}

BitMapObject::operator HDC()
{
//return hdcMemory.
return(hdcMemory);
}

int BitMapObject::GetWidth()
{
//return width
return(iWidth);
}

int BitMapObject::GetHeight()
{
//return height
return(iHeight);
}





blocks.bmp is also saved in the same folder.

all i get is black screen, with a title of falling blocks...



Share this post


Link to post
Share on other sites
O.O

Shortest tetris code ever (by line count, unverified claim I'm making right now):
http://www.matthewleverton.com/misc/20lines.html


I don't really know how long a normal (as in, actually readable) tetris game's code should be, but 750 lines seems a bit long.

Share this post


Link to post
Share on other sites
im sorry, but I really have no idea what that 20 lines of code means...it compiles...

but thats about all I can get from it.

could I have some help on something more on my level?

i got this program from this site...

Share this post


Link to post
Share on other sites
BlueBan007,

It looks to me like the blocks.bmp file is in the wrong directory. There's no error checking in the code, so it won't warn you if the file hasn't been found. I read through the thread and it seems a number of other people had similar problems.

Quote:

If you compiled it, and then executed it, and it comes up with a blank/black background, then the .bmp file isn't in your Projects folder, thus not showing the falling blocks.


I think that's probably right. When you say you've added the file to your project, has it actually copied it to the project's directory or is it just referencing the file from the project?

To the other posters: it's irrelevant how long the code is for this. It's a tutorial which is aimed at beginners. Obfuscated code which you've posted has zero readability so it's not even worth bringing it into this thread.

Share this post


Link to post
Share on other sites
Yes, that does make a difference and is what is causing the problem.

While a shortcut seems like a simple link to us, it is actually a file (a .lnk file, for a shortcut in Windows to a local file, to be specific) - When you click it, Windows knows to look inside for the location of the real file, and to run that file as if you had clicked on the real file instead of the shortcut file.

But your program isnt using the desktop to handle your files, its using its own programming to handle the files. When you tell it to load blocks.bmp it looks for that file - it isnt looking for blocks.lnk, and even if it was it wouldnt know what to do with it to find the real file.

So you need to have the actual file in the directory, not a shortcut.

Share this post


Link to post
Share on other sites
Quote:
Original post by Possumdude0
O.O

Shortest tetris code ever (by line count, unverified claim I'm making right now):
http://www.matthewleverton.com/misc/20lines.html


I don't really know how long a normal (as in, actually readable) tetris game's code should be, but 750 lines seems a bit long.


Doesn't count, technically, you can squez it all into 2 lines.. that doesnt make it 2 line code..

Besides.. I have a platformer demo that is shorter than 750.. X_X

kost.

Share this post


Link to post
Share on other sites
Quote:
Original post by kostiak2
Doesn't count, technically, you can squez it all into 2 lines.. that doesnt make


of couse u could, but what counts is to have only one statement or expression per line.

Share this post


Link to post
Share on other sites
ok, now im getting linker errors...omg.

[Linker error] undefined reference to `BitMapObject::operator HDC__*()'

ld returned 1 exit status

C:\Dev-Cpp\Makefile.win [Build Error] [Project12.exe] Error 1



I think the problem might be the type of project im running...basic windows app?

or the .exe file...

but idk what to do, D:

Share this post


Link to post
Share on other sites
I have to say I support the idea for creating most simple games as beginner, such as tetris.

Quote:
Original post by BlueBan007
I have recently looked into the article about tetris for beginners

I didn't really study the whole code, but seems alot of beginners are having problems with this "easy" article. For what I skimmed through, it seems like a tutorial for Win32 GDI. There is an conversion of the same program to SDL at page 6, after which the complaints in the thread stopped. Maybe have a look at that?

Quote:
Original post by Zahlman
That's a... very misguided attempt to help beginners, and over five years old. :s
It has been so long since I learned the basics, when you wrote stuff directly into video memory, what is the current path for graphics programming? SDL?

Share this post


Link to post
Share on other sites

This topic is 3200 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this