Отправляет email-рассылки с помощью сервиса Sendsay
  Все выпуски  

Уроки по Visual C++ (MFC, платформа .NET)


Информационный Канал Subscribe.Ru

Visual C++. MFC. Уроки по программированию с сайта http://progs.biz

Рассылка №39

Visual C++. MFC. Примеры. Lines. Урок 7. Добавление ресурсов.
Visual C++. MFC. Примеры. Lines. Урок 8. Пишем OnDraw - метод для рисования.
Visual C++. MFC. Примеры. Lines. Урок 9. Обработчик щелчка мыши.
Visual C++. MFC. Примеры. Lines. Урок 10. Метод MoveBall.
Visual C++. MFC. Примеры. Lines. Урок 11. Метод IsPath.
Visual C++. MFC. Примеры. Lines. Урок 12. Метод Is5InLine.

Полные версии уроков (с картинками) можно найти по адресам
Урок 7. Добавление ресурсов.
Урок 8. Пишем OnDraw - метод для рисования.
Урок 9. Обработчик щелчка мыши.
Урок 10. Метод MoveBall.
Урок 11. Метод IsPath.
Урок 12. Метод Is5InLine.

Игра Lines (Visual C++. MFC). Урок 7. Добавление ресурсов

Сейчас при запуске нашей программы ничего на поле не появляется. Давайте исправим это. Сначала нам надо подсоединить к нашему проекту несколько картинок с изображениями шариков (кроме семи картинок с шариками разных цветов будет еще одна - для пустого поля). Их размеры сделайте 24 на 24. Файлы назовите так: yellow.bmp, red.bmp, black.bmp, green.bmp, rose.bmp, blue.bmp, brown.bmp и no_color.bmp (для пустого поля). Перенесите эти файлы в подпапку res для вашего проекта.

Далее в Visual C++ заходим в меню Insert, далее выбираем Resource. Появится окно Insert Resource, в котором нажимаем на Import.

Появится диалог Import Resource, в котором перейдите в паппку res, в Files of type выберите All Files, выберите все необходимые файлы и затем нажмите на кнопку Import.

При этом возможно, что появится окно с предупреждением, что используется более 256 цветов. В этом случае просто нажмите OK.

Сейчас наши картиннки имеют не очень информативные идентификаторы (типа IDB_BITMAP1 и т. п.). Переименуйте их в нечто более осмысленное, например, в IDB_BLACK, IDB_BLUE, IDB_BROWN, IDB_GREEN, IDB_RED, IDB_ROSE, IDB_YELLOW и IDB_NO_COLOR. Теперь на нашей вкладке ResourceView должны появиться новые ресурсы.

С добавлением ресурсов все, переходите к следующему уроку.

Наверх

Игра Lines (Visual C++. MFC). Урок 8. Пишем OnDraw - метод для рисования

После подсоединения ресурсов займемся непосредственно рисованием. Для этого добавьте в метод OnDraw класса CLinesView следующийй код:

void CLinesView::OnDraw(CDC* pDC)
{
    CLinesDoc* pDoc = GetDocument();
    ASSERT_VALID(pDoc);
    // TODO: add draw code for native data here
    CBitmap bitmap;
    BITMAP bmp;
    for(int j=0; j<pDoc->m_iNumOfRows; j++){
        for(int i=0; i<pDoc->m_iNumOfRows; i++){
            bitmap.DeleteObject();
            switch(pDoc->m_field[i][j]){
            case 1:
                bitmap.LoadBitmap(IDB_BLACK);
                break;
            case 2:
                bitmap.LoadBitmap(IDB_BLUE);
                break;
            case 3:
                bitmap.LoadBitmap(IDB_BROWN);
                break;
            case 4:
                bitmap.LoadBitmap(IDB_GREEN);
                break;
            case 5:
                bitmap.LoadBitmap(IDB_RED);
                break;
            case 6:
                bitmap.LoadBitmap(IDB_ROSE);
                break;
            case 7:
                bitmap.LoadBitmap(IDB_YELLOW);
                break;
            default:
                bitmap.LoadBitmap(IDB_NO_COLOR);
                break;
            }
            bitmap.GetObject(sizeof(BITMAP), &bmp);

            pDC->DrawState(CPoint(24*i,24*j),
                CSize(bmp.bmWidth,bmp.bmHeight),bitmap, DST_BITMAP);
        }
    }
}

Запустите программу. Вы должны увидеть поле с пустыми полями.

Наша программа понемногу оживает.

Наверх

Игра Lines (Visual C++. MFC). Урок 9. Обработчик щелчка мыши

Сейчас мы с вами добавим обработчик для щелчка левой кнопкой мыши. Это - самый важный метод в классе CLinesView. Именно благодаря ему программа и будет обращать внимание на действия пользователя (перемещать и уничтожать шарики и т. п.).

Вызовите ClassWizard (Ctrl+W), перейдите на вкладку Message Maps, выберите класс CLinesView в списке Object IDs выберите CLinesView и в списке Messages выберите WM_LBUTTONUP.

Нажмите на Add Function и затем - на Edit Code.

В появившейся заготовке для нашего метода напечатайте следующий код:

void CLinesView::OnLButtonUp(UINT nFlags, CPoint point)
{
    // TODO: Add your message handler code here and/or call default
    int x=(int)point.x/24;//координата по горизонтали
    int y=(int)point.y/24;//координата по вертикали
    CString str;//строка с координатами
    static bool bMove;   //щёлкаем ли во второй раз
    static int x0;      //координаты выбранного шарика
    static int y0;
    CLinesDoc* pDoc=GetDocument();
    if(pDoc->m_field[x][y]!=0){//т.е. если щёлкаем на шарике
        bMove=true;
        x0=x;//запоминем выбраный шарик
        y0=y;
    }
    else{//т. е. щелкаем не на шарике
        if(bMove){//если шарик выбран
            if(/*pDoc->IsPath(x0, y0, x, y)*/true){ //ставим временную заглушку,
                                                    //т. е. считаем, что путь всегда существует
                //pDoc->MoveBall(x0, y0, x, y);
                bMove=false;
                //если нет пяти в ряд
                if(/*!pDoc->Is5InLine(x,y,true)*/!false){   //ставим временную заглушку,
                                                            //т. е. считаем, пяти (или более) шариков  в ряд не существует
                    //true-очки прибавляем
                    //смотрим, сколько свободных полей осталось
                    //и кидаем шарики
                    if(pDoc->m_iNumOfRows*pDoc->m_iNumOfRows
                        -pDoc->m_iNumOfBalls>=3){
                        pDoc->Throw3Balls(3);
                    }
                    else{
                        pDoc->Throw3Balls(
                            pDoc->m_iNumOfRows*pDoc->m_iNumOfRows
                            -pDoc->m_iNumOfBalls);
                    }
                    //Проверяем, есть не все ли клетки заняты
                    //Если да, то заканчиваем игру
                    if(pDoc->m_iNumOfRows*pDoc->m_iNumOfRows
                        ==pDoc->m_iNumOfBalls){
                    }
                }
            }
            else{
                MessageBeep(0xFFFFFFFF);
            }
        }
        else{
            MessageBeep(0xFFFFFFFF);
        }
    }
    //показываем в заголовке окна кол-во шариков
    str.Format("%d balls, %d points",
        pDoc->m_iNumOfBalls, pDoc->m_iNumOfPoints);
    SetWindowText(str);

    CWnd* wnd = AfxGetApp()->GetMainWnd();
    wnd->SetWindowText(str);
    Invalidate(FALSE);

    CView::OnLButtonUp(nFlags, point);
}

В алгоритм вдаваться особенно не будем (комментарии как-то послужат вам для его понимания). Пока только отметим, что некоторые методы у нас закомментированы (например, IsPath, MoveBall или Is5InLine). Это означает, что их у нас пока нет. На последующих уроках мы их введем, тогда и раскомментируем соответствующие места.

Пока же можете просто запустить нашу программу. Выглядеть она будет как и в конце предыдущего шага, только при щелчке на клеточках будет раздаваться звуковой сигнал.

Наверх

Игра Lines (Visual C++. MFC). Урок 10. Метод MoveBall

Добавьте в класс CLinesDoc новый метод MoveBall. Для этого на вкладке ClassView щелкаете на имени класса правой кнопкой мыши, в контекстном меню выбираете Add Member Function, и появивщемся диалоге задаете параметры для нового метода - Тип созвращаемого значения - void и объявление функции - MoveBall(int xFrom, int yFrom, int xTo, int yTo).

Для добавленного метода внесите следующий код:

void CLinesDoc::MoveBall(int xFrom, int yFrom, int xTo, int yTo){
    //обмен шарика и пустого места
    int iTmp;
    iTmp = m_field[xTo][yTo];
    SetField(xTo, yTo, m_field[xFrom][yFrom]);
    SetField(xFrom, yFrom, iTmp);
}

Запускаем программу. Ничего нового нет. Внесем теперь изменения в написанный ранее код. Перейдите в код метода OnNewDocument для класса CLinesDoc. Добавьте в конце его следующие строки:

BOOL CLinesDoc::OnNewDocument()
{
    ...
    Throw3Balls(5);//кидаем пять начальных шарика
    CString s1;
    s1.Format("5 balls, 0 points");
    return TRUE;
}

И еще одно изменение. Раскомментируйте строку в методе OnLButtonUp класса CLinesView:

            ...
            if(/*pDoc->IsPath(x0, y0, x, y)*/true){ //ставим временную заглушку,
                                                    //т. е. считаем, что путь всегда существует

                pDoc->MoveBall(x0, y0, x, y);
                bMove=false;
                ...

Запустите программу. Появились шарики, и их уже можно по полю двигать!

Наверх

Игра Lines (Visual C++. MFC). Урок 11. Метод IsPath

На этом уроке мы добавим в наше программу метод IsPath класса CLinesDoc. Он имеет тип bool и отвечает на вопрос, есть ли путь между двумя некоторыми клеточками, или его нет.

Добавьте в класс CLinesDoc заготовку для нашего метода. Прототип у неё следующий:

bool IsPath(int xFrom, int yFrom, int xTo, int yTo);

В полученную заготовку вставьте следующий код:

bool CLinesDoc::IsPath(int xFrom, int yFrom, int xTo, int yTo)
{
    bool bPathIsFound=false;
    int iField[iNumOfRows][iNumOfRows];
    for(int j=0;j<iNumOfRows;j++){
        for(int i=0;i<iNumOfRows;i++){
            iField[i][j]=m_field[i][j];
        }
    }
    int iMaxPathLength=iNumOfRows*iNumOfRows+1;
    iField[xFrom][yFrom]=-1;//начальная точка
    iField[xTo][yTo]=iMaxPathLength;//конечная точка
    int iNextSearch=-1;
    bool bContSearch = true;
    while(bContSearch){
        bContSearch=false;
        for(j=0;j<iNumOfRows;j++){
            for(int i=0;i<iNumOfRows;i++){
                if(iField[i][j]==iNextSearch){//найдно очередное поле для перемещения
                    //начинаем обследовать его соседей
                    bool bIsLeft=(i==0);//поле располжено слева
                    bool bIsRight=(i==(iNumOfRows-1));//поле располжено справа
                    bool bIsTop=(j==0);//поле располжено сверху
                    bool bIsButtom=(j==(iNumOfRows-1));//поле располжено снизу
                    if(!bIsLeft){
                        if(iField[i-1][j]==iMaxPathLength){//если это искомое поле
                            bPathIsFound=true;
                            return bPathIsFound;

                        }
                        if(iField[i-1][j]==0){//если поле слева свободно
                            iField[i-1][j]=iNextSearch-1;
                            bContSearch = true;
                        }

                    }
                    if(!bIsRight){
                        if(iField[i+1][j]==iMaxPathLength){//если это искомое поле
                            bPathIsFound=true;
                            return bPathIsFound;
                        }
                        if(iField[i+1][j]==0){//если поле справа свободно
                            iField[i+1][j]=iNextSearch-1;
                            bContSearch = true;
                        }
                    }
                    if(!bIsTop){
                        if(iField[i][j-1]==iMaxPathLength){//если это искомое поле
                            bPathIsFound=true;
                            return bPathIsFound;
                        }
                        if(iField[i][j-1]==0){//если поле сверху свободно
                            iField[i][j-1]=iNextSearch-1;
                            bContSearch = true;
                        }
                    }
                    if(!bIsButtom){
                        if(iField[i][j+1]==iMaxPathLength){//если это искомое поле
                            bPathIsFound=true;
                            return bPathIsFound;
                        }                        if(iField[i][j+1]==0){//если поле снизу свободно
                            iField[i][j+1]=iNextSearch-1;
                            bContSearch = true;
                        }
                    }
                }
            }
        }
        iNextSearch--;

    };

    return bPathIsFound;
}

Теперь внесите небольшие изменения в метод OnLButtonUp класса CLinesView. Для этого найдите следующую строку:

    ...
    if(/*pDoc->IsPath(x0, y0, x, y)*/true){ //ставим временную заглушку,
    ...

и замените ее на

    ...
    if(pDoc->IsPath(x0, y0, x, y)){
    ...

Запускайте программу. Теперь при перемещении путь будет искаться правильно - если он есть, то шарик будет перемещаться, а если нет, то шарик перемещаться не будет.

При желанни можете разобраться с алгоритмим поиска пути. Его неофицальное название - метод водопроводчика. Приблизительно он работает так - в той клетке, откуда вы двигаетесь, вы открываете водопроводный кран, и смотрите, дойдет вода до нужной вам клетки или нет.

Наверх

Игра Lines (Visual C++. MFC). Урок 12. Метод Is5InLine

Сейчас в нашей игре если пять или более шариков одного цвета оказываются расположенными подряд, то они не исчезают. Начнем исправлять это. Сейчас мы напишем метод Is5InLine класса CLinesDoc, который будет отвечать на вопрос, есть ли ряд из пяти или более одноцветных шариков.

Добавьте метод со следующим прототипом

    bool Is5InLine(int x, int y, bool bAddPoints);

в заловочный файл класса CLinesDoc. В тело метода внесите следующий код:

bool CLinesDoc::Is5InLine(int x, int y, bool bAddPoints){
    //проверяет, есть ли 5 или более шариков подряд
    //int x1=x, y1=y;//координаты поля справа, слева и т.п.
    int i;//смещение относительно точки (x, y)
    bool bSeeFuther;//смотрим ли далее
    int iO=0, iW=0, iN=0, iS=0;//число совпадений вправо, влево, вверх и вниз
    int iNO=0, iNW=0, iSO=0, iSW=0;//число совпадений вправо-вверх, влево-вверх, вправо-вниз и влево-вниз
    bool bRes =false;//возвращемое значение

    //смотрим вправо
    bSeeFuther=true;
    i=1;
    while(x+i<iNumOfRows && bSeeFuther){//если поле справа существует
        bSeeFuther=false;
        if(m_field[x+i][y]==m_field[x][y]){//и его цвет совпадает с цветом (x,y)
            iO++;//увеличиваем счётчик совпадений вправо
            bSeeFuther=true;
        }
        i++;
    };

    //смотрим влево
    bSeeFuther=true;
    i=1;
    while(x-i>=0 && bSeeFuther){//если поле справа существует
        bSeeFuther=false;
        if(m_field[x-i][y]==m_field[x][y]){//и его цвет совпадает с цветом (x,y)
            iW++;//увеличиваем счётчик совпадений вправо
            bSeeFuther=true;
        }
        i++;
    };

    //смотрим вниз
    bSeeFuther=true;
    i=1;
    while(y+i<iNumOfRows && bSeeFuther){//если поле справа существует
        bSeeFuther=false;
        if(m_field[x][y+i]==m_field[x][y]){//и его цвет совпадает с цветом (x,y)
            iS++;//увеличиваем счётчик совпадений вправо
            bSeeFuther=true;
        }
        i++;
    };

    //вмотри вверх
    bSeeFuther=true;
    i=1;
    while(y-i>=0 && bSeeFuther){//если поле справа существует
        bSeeFuther=false;
        if(m_field[x][y-i]==m_field[x][y]){//и его цвет совпадает с цветом (x,y)
            iN++;//увеличиваем счётчик совпадений вправо
            bSeeFuther=true;
        }
        i++;
    };
    /////////////проверки диагоналей/////////////
    //смотрим вправо-вниз
    bSeeFuther=true;
    i=1;
    while(x+i<iNumOfRows && y+i<iNumOfRows && bSeeFuther){//если поле справа внизу существует
        bSeeFuther=false;
        if(m_field[x+i][y+i]==m_field[x][y]){//и его цвет совпадает с цветом (x,y)
            iSO++;//увеличиваем счётчик совпадений вправо
            bSeeFuther=true;
        }
        i++;
    };

    //смотрим вверх-влево
    bSeeFuther=true;
    i=1;
    while(x-i>=0 && y-i>=0 && bSeeFuther){//если поле слева вверху существует
        bSeeFuther=false;
        if(m_field[x-i][y-i]==m_field[x][y]){//и его цвет совпадает с цветом (x,y)
            iNW++;//увеличиваем счётчик совпадений вправо
            bSeeFuther=true;
        }
        i++;
    };

    //смотрим вправо-вверх
    bSeeFuther=true;
    i=1;
    while(x+i<iNumOfRows && y-i>=0 && bSeeFuther){//если поле справа вверху существует
        bSeeFuther=false;
        if(m_field[x+i][y-i]==m_field[x][y]){//и его цвет совпадает с цветом (x,y)
            iNO++;//увеличиваем счётчик совпадений вправо
            bSeeFuther=true;
        }
        i++;
    };

    //смотрим вниз-влево
    bSeeFuther=true;
    i=1;
    while(x-i>=0 && y+i<iNumOfRows && bSeeFuther){//если поле слева внизу существует
        bSeeFuther=false;
        if(m_field[x-i][y+i]==m_field[x][y]){//и его цвет совпадает с цветом (x,y)
            iSW++;//увеличиваем счётчик совпадений вправо
            bSeeFuther=true;
        }
        i++;
    };

    //запоминаем, сколько шариков было до удаления
    int iWasNumOfBalls = m_iNumOfBalls;
    if(iS+iN+1>=5){//есть 5 в ряд
        AfxMessageBox("5 в ряд по вертикали", NULL, NULL);
        //Delete5InLine(x,y-iN,x,y+iS); (пока закомментируем)
        bRes=true;
        //return true;
    }
    if(iO+iW+1>=5){//есть 5 в ряд
        AfxMessageBox("5 в ряд по горизонтали", NULL, NULL);
        //Delete5InLine(x-iW,y,x+iO,y); (пока закомментируем)
        bRes=true;
        //return true;
    }

    //есть 5 в ряд по диагонали слева-сверху справа-снизу
    if(iNW+iSO+1>=5){
        AfxMessageBox("5 в ряд по диагонали слева-сверху справа-снизу", NULL, NULL);
        //Delete5InLine(x-iNW,y-iNW,x+iSO,y+iSO); (пока закомментируем)
        bRes=true;
        //return true;
    }

    //есть 5 в ряд по диагонали справа-сверху слева-снизу
    if(iSW+iNO+1>=5){
        AfxMessageBox("5 в ряд по диагонали справа-сверху слева-снизу", NULL, NULL);
        //Delete5InLine(x+iNO,y-iNO,x-iSW,y+iSW); (пока закомментируем)
        bRes=true;
        //return true;
    }

    if(bAddPoints){
        //m_iNumOfPoints+=CalcPoints(iWasNumOfBalls-m_iNumOfBalls);//(пока закомментируем)
    }
    return bRes;//нет 5 в ряд
}

Обратите внимание, что наш метод вызывает другой метод - Delete5InLine, который удаляет пять или более шариков, идущих подряд. Этот метод мы пока не написали, поэтому пока соответствующие места у нас закомментированы. Но для того, чтобы было видно, что наш метод все-таки обнаруживает несколько идущих подряд шариков, мы вставили строчки типа

    ...
    AfxMessageBox("5 в ряд по вертикали", NULL, NULL);
    ...

которые просто будут показывать соответствующий MessageBox. Потом мы эти строчки уберем за ненадобностью.

Аналогично с методом CalcPoints в строке

    ...
    if(bAddPoints){
        //m_iNumOfPoints+=CalcPoints(iWasNumOfBalls-m_iNumOfBalls);//(пока закомментируем)
    }

Этот метод нужен для подсчета очков. Его мы тоже напишем позже.

Теперь найдите в методе OnLButtonDown класса CLinesView заглушку

                ...
                //если нет пяти в ряд
                if(/*!pDoc->Is5InLine(x, y, true)*/!false){   //ставим временную заглушку,
                ...

и замените ее на

                ...
                //если нет пяти в ряд
                if(!pDoc->Is5InLine(x,y,true)){
                ...

Значение последнего параметра (true) в методе Is5InLine означает, что игроку будут начислятся очки.

И осталось еще мелкое изменение. Понятно, что шарики могут изчезать не только при ходе игрока, но и при ходе компьютера. Найдите метод Throw3Balls класса CLinesDoc и внесите в него следующие изменения:

            ...
            iColor=rand()%m_iNumOfColors+1;
            SetField(x,y,iColor); //ставим шарик нужного цвета
            Is5InLine(x, y, false);
        }
        ...

Значение последнего параметра (false) означает, что очки игроку начислятся не будут.

Если в теперь запустите программу, то при образовании ряда из пяти (или более) шариков одного цвета по горизонтали (или по другим направлениям) возникнет соответствующий MessageBox.

Правда сами шарики при этом исчезать не будут. Но этим мы займемся позже.

Наверх

Copyright Алексеев Игорь, 2002

Copyright cайт progs.biz, 2002



http://subscribe.ru/
E-mail: ask@subscribe.ru
Отписаться
Убрать рекламу

В избранное