játékfejlesztés.hu
FórumGarázsprojectekCikkekSegédletekJf.hu versenyekKapcsolatokEgyebek
Legaktívabb fórumozók:
Asylum:    5448
FZoli:    4892
Kuz:    4455
gaborlabor:    4449
kicsy:    4304
TPG:    3402
monostoria:    3284
DMG:    3172
HomeGnome:    2919
Matzi:    2520

Pretender:    2498
szeki:    2440
Seeting:    2306
Geri:    2188
Orphy:    1893
Joga:    1791
Bacce:    1783
MaNiAc:    1735
ddbwo:    1625
syam:    1491
(C++) Foxi 2D-s programozás leckéi 4. - Billentyű 2007.04.29 08:06


Billentyű I.

Üdvözöllek!

A legjobb, ha másolatot készítesz a projectről! Mert a következő leckében innen folytatjuk.
A mostani leckében a billentyűzet kezeléséről lesz szó. Kétféleképpen oldható meg:
  • Az egyik, hogy a main .cpp -ben a visszatérési függvényben LRESULT CALLBACK WindowProcedure (......) már eleve ott van a billentyű üzenet. Ezt fogjuk most haszálni.
  • A másik pedig egy directx-es input objektumot kell létrehozni. Ez lehet billentyűzet, egér, joystick. Ez pedig a következő lecke.
Joystick-ot még nem próbáltam, de azért megpróbálom. Most a billentyűzet.
A lenyomott billentyű ascII kódja a KEYDOWN wParam változójában van, csak ezt kell átadni a sprite osztálynak, a bábu irányítására. Azonban ez sem ilyen egyszerű, mert a billentyű leütés esemény azonnal bekövetkezik, de az ismétlések nem rendszeresek, tehát a bábunk megindul, megakad, és meint megindul. Ez bizony nem előny a látványban. Kiküszöbölésére egy logikai tömböt hozunk létre, és hozzárendeljük az egyes billentyűket. Ha bill. lenyomás esemény akkor true, ha felengdjük azt a billentyűt, akkor billentyű felengedése esemény false -ra állítja a tömbelemet. A sprite osztály ezek után bármikor kérdezi a billenyű tömböt, mindig aktuális értéket kap vissza.
Sprite .hpp ban a protected alá vegyél fel egy öt elemű logikai tömböt:
bool keystatus[5];
ezt a tömböt a konstruktorban inicializálni kell:
SPRITE::SPRITE()
{
    for(int i=0;i<5;i++){keystatus[i]=false;}
}
A két függvény dekralációja ami beleír ebbe a tömbbe:
public:
    void bill_le(char);
    void bill_fel(char);
a megvalósítása:
static char billkod[]={37,38,39,40,32}; //mivel függvényenkívül van, látja más függvény is ezek a figyelt billentyűk kurzornyilak és space
void SPRITE::bill_le(char bill)
{
    for(int i=0;i<5;i++){ if(bill==billkod[i])keystatus[i]=true;}
}
/**/
void SPRITE::bill_fel(char bill)
{
    for(int i=0;i<5;i++){ if(bill==billkod[i])keystatus[i]=false;}
}
/**/
egyelőre a demohoz írd be a frissit () függvénybe:

    rajz.kiirbetu("10.sprite y",10,50);rajz.kiirszam(sp[9].SP_X,100,50);
    /**/
    if(keystatus[0])rajz.kiirbetu("bal nyíl",10,70);
    if(keystatus[1])rajz.kiirbetu("fel nyíl",10,90);
    if(keystatus[2])rajz.kiirbetu("jobb nyíl",10,110);
    if(keystatus[3])rajz.kiirbetu("le nyíl",10,130);
    if(keystatus[4])rajz.kiirbetu("space ",10,150);
    /**/
    rajz.flip();

A main. cpp -ben módosítsuk a visszatérési függvényt:

LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message) /* vizsgáljuk az üzenet tipusát */
    {
        case WM_KEYDOWN:
            sprite.bill_le(wParam);
            switch(wParam)
            {
                case VK_ESCAPE:fut=!fut;break;

            }
            break;
        case WM_KEYUP:
            sprite.bill_fel(wParam);break;

        ......
        ......

Fordít futtat.....

A billentyűzet most már működik, mint látható. Csak 3 billentyűt figyel egyszerre a windows, de ez nem zavaró a játék alatt. A mozgatáshoz egy egyszerű függvényt kell dekralálni a neve:

Sprite.hpp:
void babu_mozgat();
Megvalósítás:

void SPRITE::babu_mozgat()
{
    int sebesseg =3; //elmozdulás sebessége
    if(!keystatus[0] & keystatus[2])//ha a bal nincs lenyomva,de a jobb igen
    {
        if( sp[0].SP_X < (800-18-sebesseg))sp[0].SP_X+=sebesseg;
    }
    if(keystatus[0] & !keystatus[2])//ha a jobb nincs lenyomva,de a bal igen
    {
        if( sp[0].SP_X > (0+sebesseg))sp[0].SP_X-=sebesseg;
    }
    if(!keystatus[1] & keystatus[3])//ha a fel nincs lenyomva,de a le igen
    {
        if( sp[0].SP_Y < (600-18-sebesseg))sp[0].SP_Y+=sebesseg;
    }
    if(keystatus[1] & !keystatus[3])//ha a le nincs lenyomva,de a fel igen
    {
        if( sp[0].SP_Y > (0+sebesseg))sp[0]SP_Y-=sebesseg;
    }
}

Persze ezt a függvényt is be kell tenni a frissit() -be:

    .......
    anim();
    sp_mozgat();
    babu_mozgat();
}

Fordít futtat. Látható, hogy az egyes dolgok a programban jól szétválaszthatóak, és nem keveredhetünk semmilyen káoszba a programozás során. Ha valami nem stimmel, akkor a gyanús változót bármikor kiirattathatjuk a képernyőre....

A következőben visszaállítjuk a billenyűkezelést, és irunk másikat, amolyan dx-eset.

itt a kód:


Billentyű II.

Most egy directx alapú billentyűkezelést próbálunk létrehozni.
Mindenek előtt töltsd vissza az előző projectet, vagy törüld az összes billentyűzethez kapcsolódó függvényt, változót minden fájlban. (main cpp sprite hpp)
Hozz létre egy új fájl-t Dx_input.hpp néven. Az alaposztály dekralációja:

//
/* INPUT OSZTÁLY */
//

#ifndef _INPUT_
#define _INPUT_
#ifndef DIRECTINPUT_VERSION
#define DIRECTINPUT_VERSION 0x800
#endif
#include <dinput.h> //MSVC dinput8.lib DEVC++ libdinput.a
//
extern bool fut;
extern HINSTANCE hinstance;
//extern bool aktiv;


//

//dekralációk
//

class INPUT
{
    public:
        INPUT();
        ~INPUT();
        LPDIRECTINPUTDEVICE8 bill;
        bool init_key();
        bool init_mause();
        bool init_joy();
        bool get_key(unsigned char,bool frissit=false);
    protected:
        LPDIRECTINPUT8 lpDI; //input objektum ezen keresztül hozzuk létre a többit
        BYTE key_buf[256];

Akkor ami magyarázatra szorul:
Az input.h headerhez DEVC++-nál a libdinput.a -t kell, az MSVC hez pedig a dinput.lib kell megadni.
A directinput objektum létrehozásához kell egy HINSTANCE típusú valami, amit megkapunk a main() függvény bemenőparaméteréből. A billentyűzetet majd innen kezeljük, ezért át kell adni azt a logikai változót, (bool fut) amit ha false- ra állítunk, (esc) akkor a progink leáll.:

extern bool fut;
extern HINSTANCE hinstance;

A Directinput objektumot csak egyszer kell létrehozni, és ezen keresztül lehet létrehozni a többi objektumot, mint billentyűzet, egér, joystick. Most a billentyűn a sor, de mint látható elő van készítve az egér és a joy is. Szükség van még egy 256 elemű tömbre is, ami eltárolja az egyes bill. kódokat.
Nézzük az init_key() függvényt és a konstruktort, destruktort:

//
//MEGVALÓSÍTÁSOK:
//

INPUT::INPUT()
{
    bill=NULL;    //LPDIRECTINPUTDEVICE8 bill pointer nullázása
}
//
INPUT::~INPUT()
{
    if(bill){bill->Release();bill=NULL;}// felszabadítjuk
    lpDI->Release();lpDI=NULL;        //és az objektumot is törüljük
};
/**/
bool INPUT::init_key()
{
    BOOL inf; //infó,hogy sikerült- e a függvény
    if( !lpDI)//ha még más nem hozta létre az objektumot
    {            //akkor létrehozzuk
        inf=DirectInput8Create( hinstance, DIRECTINPUT_VERSION, IID_IDirectInput8A, (VOID**)&lpDI, NULL);
        if (inf !=DI_OK)return false;//ha nem sikerült
    }
    if(bill){bill->Release();bill=NULL;}//ha már létezett letörüljük,egy új példánynak kell a hely
    inf=lpDI->CreateDevice(GUID_SysKeyboard, &bill, NULL);//kerálunk billentyűt
    if(inf !=DI_OK)return false;
    inf=bill->SetDataFormat(&c_dfDIKeyboard);//billentyűzet forma az adat
    if(inf !=DI_OK)return false;
    inf=bill->SetCooperativeLevel(hwnd,DISCL_FOREGROUND | DISCL_NONEXCLUSIVE);
    //aktiv=true;
    bill->Acquire();//aktiváljuk

    return true;
}

Mivel kommentezve van, nincs mit többet mondani.A billentyűzet használatának dekralációja:
bool get_key(unsigned char,bool frissit=false);
A bemenő paraméter egy 0-256 -ig terjedő szám a billentyűről ad vissza információt, hogy le van-e nyomva, vagy mégse. A másik bemenő logikai paraméter pedig az, hogy kell e frissíteni a táblát, mert a használt GetDeviceState(...) függvény minden billentyű állapotot frissít. Tehát ha egymásután több billentyűt kérdezünk le akkor fölösleges újból kitöltetni a táblázatot. Ha a dekralációban true értékkel inicializáljuk, akkor minden meghíváskor frissíti a táblát.
Megvalósítása:

bool INPUT::get_key(unsigned char adat,bool frissit)
{
    if(frissit)//ha kell, frissíti a táblát
    {
        if(bill->GetDeviceState(256,&key_buf)==DIERR_INPUTLOST)
        {
            init_key(); //amennyiben elveszett az objektum ujraépítjük
            return false; //majd következőre lekérdezzük
        }
    }
    if(key_buf[ DIK_ESCAPE] & 0x80)fut=false;//ez a sor biztosítja az esc-re a kilépést
    return (key_buf[adat] & 0x80) ? true:false;//lenyomva,vagy mégse

}

Hát ez is kommentezve van, nem sok mondani való maradt, csak annyi, hogy NEM ascII kód kell a billentyűzet lekérdezéséhez, hanem a dinput.h -ban vannak dekralálva az egyes billentyűknek a tömbben elfoglalt helyei, mégpedig el vannak nevezve! pl. a bal kurzornyíl : DIK_LEFT.
Sajnos meg kell nyitni a dinput.h-t és a billentyű nevét meg kell nézni. Azért előny is lehet, mert olyan billentyűt is lekérdezhetünk, amit máshogy nehezebb lenne pl.: DIK_VOLUMEDOWN.
Menjünk a Main.cpp- be!
Vegyük fel a Dx_input.hpp-t és hozzuk létre az új osztályunkat:

#include <windows.h>
#include <Rajz.hpp>
#include <Dx_input.hpp>
#include <Sprite.hpp>

//
//
//dekralációk

HINSTANCE hinstance;
RAJZ rajz;
INPUT input;
SPRITE sprite;

Fontos, a sorrend! Mivel a sprite osztály használja majd az input osztályt, ezért ELÉ kell tenni, mert hibaüzit kapnánk....
A következő, hogy van egy HINSTANCE hinstance változó aminek értéket is kell adni:
Módosítsad a WinMain(...) függvényt mindjárt az elején:

int WINAPI WinMain(HINSTANCE hThisInstance,HINSTANCE hPrevInstance,LPSTR lpszArgument,int nFunsterStil)
{

    hinstance=hThisInstance;
    WNDCLASSEX wincl; // ITT FOGLALOD LE A STRUKTÚRÁT
    .......
    .......

Marad az üzenetkezelő függvény bővítése:

    switch (message) /* vizsgáljuk az üzenet tipusát */
    {
        case WM_ACTIVATE: //ha az alkalmazás előtérbe került
            if(input.bill==NULL)break;
            switch(wParam)
            {
                case WA_ACTIVE:
                case WA_CLICKACTIVE:
                    aktiv=true; //későbbi felhasználásra
                    input.bill->Acquire();//aktiváljuk a billentyűzetet
                    break;
                case WA_INACTIVE:
                    aktiv=false;
                    input.bill->Unacquire();
                    break;
            }
            break;
        case WM_DESTROY:
        .......
        .......

Az aktivate üzenetet azért kell most már figyelni, mert ha az ablakunk kisméretbe került, akkor el kell engedni a billentyűzetet (és az egeret, meg a joy-t), ha pedig megint aktív, akkor engedni kell a működést. A működés... Ugrás a Sprite.hpp-be!
Mindjárt az elején:
#ifndef DIRECTINPUT_VERSION
#define DIRECTINPUT_VERSION 0x800
#endif
#include "dinput.h"
#include <Dx_input.hpp>
//
extern RAJZ rajz ;
extern bool aktiv;
extern INPUT input;// az új osztály felvétele

Azért kellett ide is csatolni a dinput.h -t mert nem tudná értelmezni a DIK_LEFT, DIK_RIGHT stb. billentyűneveket. Dekralálni kéne őket.
A protected tagok közé vedd fel a void babu_mozgat(); függvényt.
A megvalósítása:

void SPRITE::babu_mozgat()
{
    int sebesseg=3; //elmozdulás sebessége
    if(!input.get_key(DIK_LEFT,true) & input.get_key(DIK_RIGHT))//ha bal nincs lenyomva de a jobb igen
    {
        if( sp[0].SP_X < 800-18-sebesseg)sp[0].SP_X+=sebesseg;
    }
    if(input.get_key(DIK_LEFT) & !input.get_key(DIK_RIGHT))//ha a jobb nincs lenyomva,de a bal igen
    {
        if( sp[0].SP_X > 0+sebesseg)sp[0].SP_X-=sebesseg;
    }
    if(!input.get_key(DIK_UP) & input.get_key(DIK_DOWN))//ha a fel nincs lenyomva,de a le igen
    {
        if( sp[0].SP_Y < 600-18-sebesseg)sp[0].SP_Y+=sebesseg;
    }
    if(input.get_key(DIK_UP) & !input.get_key(DIK_DOWN))//ha a le nincs lenyomva,de a fel igen
    {
        if( sp[0].SP_Y > 0+sebesseg)sp[0].SP_Y-=sebesseg;
    }
}

Ha nem hagytam ki semmit, akkor mennie kell. Fordít- futtat! AZ eddig elkészülteket megcsináltam DEVC++ fordítóval is, és jól működött..

itt a működő kód:


foxi


Kapcsolódó linkek:

A cikksorozat további részei:

Értékelés: 1.00

Új hozzászólás
Nincs megjegyzés