Solved

# Direct Draw, Movement problem.

Posted on 2004-09-10
583 Views
Hi i'm currently programming a small game (similar to radian 2). and have come across a small problem with moving the ship.

1. I can't move the ship North-west while shooting.
2. I can't move the ship south-west while shooting.

The keys i have used for moving are:

Forward    - W
backward  - S
Left          - A
Right        - D
Shoot      - Space.

I can move forward,backward,left,right,north-east & south east without any problems.

I have tried other keys for shoot, e.g. i tried F1 for shoot and the ship moves in all directions with no problems.

Can someone please tell me why i'm having this problem.

Note: If you see any problems with my code, e.g. style or bad programming practises please let me know =)
Also the way i've gotten the ship to shoot is probably pretty crude :) (didn't put much thought into it, Just wanted to check ways in which i could get the ship to shoot).

#include<stdlib.h>
#include<windows.h>
#include<ddraw.h>
#include<stdio.h>
#include<conio.h>

#define WIN32_LEAN_AND_MEAN
#define WINDOW_WIDTH 640
#define WINDOW_HEIGHT 480
#define BPP 16               //bits per pixel

#define KEYDOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0)
#define KEYUP(vk_code)   ((GetAsyncKeyState(vk_code) & 0x8000) ? 0 : 1)

#define DDRAW_INIT_STRUCT(ddstruct) {memset(&ddstruct,0,sizeof(ddstruct)); ddstruct.dwSize = sizeof(ddstruct);}

#define _RGB16BIT565(r,g,b) ((b & 31) + ((g & 63) << 5) + ((r & 31) << 11))

#define MAX_BULLET 15

#define LEFT      0x41
#define RIGHT      0x44
#define UP            0x57
#define DOWN      0x53
#define SHOOT      0x20

HWND Global_hWnd;

LPDIRECTDRAW                  Lpdd = NULL;
LPDIRECTDRAW7                  Lpdd7 = NULL;
PALETTEENTRY                  Palette[256];
LPDIRECTDRAWPALETTE            LpddPalette;
DDSURFACEDESC2                  ddsd;
LPDIRECTDRAWSURFACE7      LpddsPrimary;
LPDIRECTDRAWSURFACE7      LpddsBack;
DDPIXELFORMAT                  ddpixel;

void ShootLeftBullet(int,int,int);
void ShootRightBullet(int,int,int);
void ShootMiddleBullet(int,int,int);

int Window_Closed      = 0;

RECT rMovement;
RECT rLeftBullet[16];
RECT rRightBullet[16];
RECT rMiddleBullet[16];

int MovementXposition = 310;
int MovementYposition = 420;
int BulletVelocity        = 5;
int RateOfFire;
int BulletNumber;

LRESULT CALLBACK WinProc(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam)
{
PAINTSTRUCT pS;
HDC                  hDc;

switch(Msg)
{
case WM_CREATE:
break;

case WM_PAINT:
hDc = BeginPaint(hWnd,&pS);

EndPaint(hWnd,&pS);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProc(hWnd,Msg,wParam,lParam);
}

int Game_Main(void *parms = NULL, int num_parms = 0)
{
DDBLTFX ddbltfx;   //Used for the bliter

if(Window_Closed)
return(0);

if(KEYDOWN(VK_ESCAPE))
{
PostMessage(Global_hWnd,WM_CLOSE,0,0);
Window_Closed = 1;
}

//Lock the back buffer Note: this is done so you can write to the back buffer.
LpddsBack->Lock(NULL,&ddsd,DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT, NULL);

UCHAR *Buffer = (UCHAR*)ddsd.lpSurface;

DDRAW_INIT_STRUCT(ddbltfx);

if(ddsd.lPitch == WINDOW_WIDTH)
{
memset(Buffer,0,640*2*480);
}
else
{
UCHAR* Buffer2 = Buffer;

for(int Count = 0; Count<WINDOW_HEIGHT;Count++)
{
memset(Buffer2,0,WINDOW_WIDTH*2);
Buffer2+=ddsd.lPitch;
}
}

LpddsBack->Unlock(NULL);

rMovement.left      = MovementXposition;
rMovement.right      = MovementXposition+20;
rMovement.top      = MovementYposition;
rMovement.bottom= MovementYposition+50;

ddbltfx.dwFillColor = _RGB16BIT565(255,255,255);
LpddsBack->Blt(&rMovement,
NULL,
NULL,
DDBLT_COLORFILL | DDBLT_WAIT,
&ddbltfx);

ddbltfx.dwFillColor = _RGB16BIT565(255,0,0);
for(int count = 0; count<MAX_BULLET;count++)
{

LpddsBack->Blt(&rLeftBullet[count],
NULL,
NULL,
DDBLT_COLORFILL | DDBLT_WAIT,
&ddbltfx);

LpddsBack->Blt(&rRightBullet[count],
NULL,
NULL,
DDBLT_COLORFILL | DDBLT_WAIT,
&ddbltfx);

LpddsBack->Blt(&rMiddleBullet[count],
NULL,
NULL,
DDBLT_COLORFILL | DDBLT_WAIT,
&ddbltfx);
}

LpddsPrimary->Flip(NULL,DDFLIP_WAIT);

if(KEYDOWN(LEFT))
{
if(MovementXposition>4)
{
MovementXposition-=4;
}
}
if(KEYDOWN(RIGHT))
{
if(MovementXposition<615)
{
MovementXposition+=4;
}
}
if(KEYDOWN(UP))
{
if(MovementYposition>2)
{
MovementYposition-=4;
}
}
if(KEYDOWN(DOWN))
{
if(MovementYposition<425)
{
MovementYposition+=4;
}
}

if(RateOfFire>=10)
{
if(KEYDOWN(SHOOT))
{
ShootLeftBullet(MovementXposition,MovementYposition,BulletNumber);
ShootRightBullet(MovementXposition,MovementYposition,BulletNumber);
ShootMiddleBullet(MovementXposition,MovementYposition,BulletNumber);

RateOfFire=0;
if(BulletNumber<MAX_BULLET)
{
BulletNumber++;
}
else
{
BulletNumber=0;
}
}
}

for(int Count1=0;Count1<MAX_BULLET;Count1++)
{
if(rLeftBullet[Count1].top>0)
{
rLeftBullet[Count1].top-=8;
rLeftBullet[Count1].bottom-=8;

rRightBullet[Count1].top-=8;
rRightBullet[Count1].bottom-=8;

rMiddleBullet[Count1].top-=8;
rMiddleBullet[Count1].bottom-=8;
}
else
{
rLeftBullet[Count1].top                  = 0;
rLeftBullet[Count1].bottom            = 0;

rRightBullet[Count1].top            = 0;
rRightBullet[Count1].bottom            = 0;

rMiddleBullet[Count1].top            = 0;
rMiddleBullet[Count1].bottom      = 0;
}
}

RateOfFire++;

Sleep(10);

return(1);
}

void ShootLeftBullet(int Xpos,int Ypos,int BulletNumber)
{
rLeftBullet[BulletNumber].left            = Xpos;
rLeftBullet[BulletNumber].right            = Xpos+1;
rLeftBullet[BulletNumber].top            = Ypos;
rLeftBullet[BulletNumber].bottom      = Ypos+10;
}

void ShootRightBullet(int Xpos,int Ypos,int BulletNumber)
{
rRightBullet[BulletNumber].left            = Xpos+19;
rRightBullet[BulletNumber].right      = Xpos+20;
rRightBullet[BulletNumber].top            = Ypos;
rRightBullet[BulletNumber].bottom      = Ypos+10;
}

void ShootMiddleBullet(int Xpos,int Ypos,int BulletNumber)
{
rMiddleBullet[BulletNumber].left      = Xpos+9;
rMiddleBullet[BulletNumber].right      = Xpos+11;
rMiddleBullet[BulletNumber].top            = Ypos;
rMiddleBullet[BulletNumber].bottom      = Ypos+10;
}

int Game_Init(void *parms = NULL, int num_parms = 0)
{
srand(GetTickCount());

ShowCursor(false);

if(FAILED(DirectDrawCreate(NULL,&Lpdd,NULL)))
{
return(0);
}

if(FAILED(Lpdd->QueryInterface(IID_IDirectDraw7,(LPVOID*)&Lpdd7)))
{
return(0);
}

Lpdd->Release();
Lpdd = NULL;

if(FAILED(Lpdd7->SetCooperativeLevel(Global_hWnd,
DDSCL_FULLSCREEN |
DDSCL_EXCLUSIVE |
DDSCL_ALLOWREBOOT|
DDSCL_ALLOWMODEX)))/*Note: i don't think that it is required
to addd DDSCL_ALLOWMODEX, as this only allows
you to use 320*240, & no one uses this....*/
{
return(0);
}

Lpdd7->SetDisplayMode(WINDOW_WIDTH,WINDOW_HEIGHT,BPP,0,0);

DDRAW_INIT_STRUCT(ddsd);

ddsd.dwFlags      = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;

ddsd.dwBackBufferCount = 1;

ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_FLIP |DDSCAPS_COMPLEX;

if(FAILED(Lpdd7->CreateSurface(&ddsd,&LpddsPrimary,NULL)))
{
return(0);
}

ddsd.ddsCaps.dwCaps = DDSCAPS_BACKBUFFER;

LpddsPrimary->GetAttachedSurface(&ddsd.ddsCaps,&LpddsBack);

return(1);
}

int Game_Shutdown(void *parms = NULL, int num_parms = 0)
{
if(LpddsPrimary)
{
LpddsPrimary->Release();
LpddsPrimary = NULL;
}

if(LpddPalette)
{
LpddPalette->Release();
LpddPalette = NULL;
}

if(Lpdd7)
{
Lpdd7->Release();
Lpdd7 = NULL;
}
return(1);
}

int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nShowCmd)
{
WNDCLASSEX WndClass;
HWND hWnd;
MSG Msg;

WndClass.cbSize                  = sizeof(WNDCLASSEX);
WndClass.hbrBackground      = (HBRUSH)GetStockObject(BLACK_BRUSH);
WndClass.cbClsExtra            = 0;
WndClass.cbWndExtra            = 0;
WndClass.hInstance            = hInstance;
WndClass.lpszClassName      = "Class1";
WndClass.lpfnWndProc      = WinProc;
WndClass.style                  = CS_DBLCLKS | CS_VREDRAW |
CS_HREDRAW | CS_OWNDC;

if(!RegisterClassEx(&WndClass))
{
MessageBox(NULL,"Could not Register Class.","Error",MB_OK |MB_ICONERROR);
return(-1);
}

/*This will create the window, and defines window properties*/
if(!(hWnd = CreateWindowEx(NULL,
"Class1",
"Practise Window",
WS_POPUP | WS_VISIBLE,
350,
150,
WINDOW_WIDTH,
WINDOW_HEIGHT,
NULL,
NULL,
hInstance,
NULL)))
{
MessageBox(NULL,"Could not Create Window.","Error",MB_OK|MB_ICONERROR);
}

Global_hWnd = hWnd;

Game_Init();

while(1)
{
if(PeekMessage(&Msg,NULL,0,0,PM_REMOVE))
{
if(Msg.message==WM_QUIT)
{
break;
}
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}

Game_Main();

}
Game_Shutdown();

return Msg.wParam;
}

0
Question by:cpu4ghz

LVL 17

Accepted Solution

davebytes earned 100 total points
Simplest answer: there are certain bits used to store current keyboard state, and you can't always rely on getasync to get you the 'real' state when you have more than two keys held at once -- or at least, my brain is recalling that to be the issue.

Have you tried switching to directinput instead?  Or use WM_KEYZZZ messages, and track the keyboard state yourself.  I believe the higher-level systems all have 'work arounds', though I can't recall offhand.

-d
0

LVL 2

Assisted Solution

cmreichl earned 100 total points
Most games will not continuely poll a state, they create 2 event triggers for each key they are using.

Key up and key down...  This way you use flags to set forward movement to true if the corrisponding key is pressed, and forward movement false when the key up event is processed.  Much more efficient and clean method for handling keyboard events.

-Chris
0

LVL 3

Assisted Solution

str_ek earned 100 total points
well if you wanna stick to try GetKeyboardState... it's better than pooling buch of getasyncs, nad you only trace what you want ;)

and bout the blockin... havent tried to compile your code.. yet... 'cos there is one other reason that sort of thing happens, and it's the idea some keabords are build on... internal sturcture has nothing in common with the serial transmision standard, or kb protocl...there is microchip that handles the interfaceing...

in most office style keybord there is tendency to design them with assumption that they are to be used for typing, and typing is pressing only one key at once (most, special keys are considered as separate lines), so they design the layout so that a keay  X generates something like 1001000 (internal signal - remember the interface chip reinterprets it!) and key Y 1011000, so pressed together gives the same feedback like Y only.... you may ask and what if Z and W together gives i.e. C , but it's no prob, cos reduceing direct lines by paring keys is already 1/2 of costs! they dont push it to far, and you may design a codepattern that complays theoty ...

there is one other method of keybord-makeing based on like' coordinates system... you get keys set to 0 ( low voltge for logical 0) , nad colums, nad rows to 1 (high volt.) and to microprocessor directly, and when the key is presed it touches the corsing of one of col/row makeing current to sink directly and forcing a 0 logical state on micreoprocessor input, and from the coordinates it decodest the key and generates special seialport (ie ps2) message
thsi way you need only 2*X^(1/2) (square root) lines to code X keys... it's realy widely spread technology, and makes a little problem when ther are presed multiple keys that uses the same lines,
i.e.
1   1   0
|   |   |
-----------  1
|   |   |
-----------   1
|   |   |
-----------   0
|   |   |

may be

1   1   0
|   |   |
--X--X----  1
|   |   |
--X-------   1
|   |   |
-----------   0
|   |   |

or

1   1   0
|   |   |
--X-------  1
|   |   |
--X--X----   1
|   |   |
-----------   0
|   |   |

or
1   1   0
|   |   |
---X--X----  1
|   |   |
------X----   1
|   |   |
-----------   0
|   |   |

etc.....

so ther are combinations that are underminable.... but cos of it adventage (witch is double root of lines needed to code the keys) you can actulay code 256 kays with only 32 lines!!! and the price difference between microcontrolers with 32 and with 256 input lines are huge!

anyway...

chech the high-level fuctions, winapi is treacherus, and lieks to teas people... ;) and if it dosnt help jsut dont worry it's you're keybord :) check another brand and price-level for other pattern of cols and rows ;)
0

## Featured Post

Artificial Intelligence comes in many forms, and for game developers, Path-Finding is an important ability for making an NPC (Non-Playable Character) maneuver through terrain.  A* is a particularly easy way to approach it.  I’ll start with the algor…
As game developers, we quickly learn that Artificial Intelligence (AI) doesn’t need to be so tough.  To reference Space Ghost: “Moltar, I have a giant brain that is able to reduce any complex machine into a simple yes or no answer. (http://www.youtu…
Polish reports in Access so they look terrific. Take yourself to another level. Equations, Back Color, Alternate Back Color. Write easy VBA Code. Tighten space to use less pages. Launch report from a menu, considering criteria only when it is filled…
You have products, that come in variants and want to set different prices for them? Watch this micro tutorial that describes how to configure prices for Magento super attributes. Assigning simple products to configurable: We assigned simple products…