C, PHP, VB, .NET

Дневникът на Филип Петров


* Елемент от игра с NetBeans

Публикувано на 19 ноември 2015 в раздел УКИ.

В тази статия ще покажем съвсем елементарен фрагмент от игра наподобяваща популярната змия. След като вече сте решили всичко от предишната задача, ще добавим още един графичен обект на екрана - малък триъгълник. Ще направим така, че нашата топка да "яде" триъгълника, с което ще се увеличава краен резултат с една точка.

Реализирайте работния екран в нещо подобно на това:

9

Именовайте JLabel обекта на триъгълника като "theTriangle", а текстовото полето на резултата като "result".

Сега нашето кръгче ще се движи по екрана. Идеята е да направим така, че ако влезе вътре в триъгълника, да увеличаваме резултата с 1, а триъгълника да се премести на произволно място на екрана. Добавете следния метод някъде в тялото на основния клас:

 private void checkIfCircleIsInTriangle(){
  if(theBall.getLocation().x >= theTriangle.getLocation().x
          &&
     theBall.getLocation().y >= theTriangle.getLocation().y
          &&
     (theBall.getLocation().x + theBall.getWidth()) <= (theTriangle.getLocation().x + theTriangle.getWidth())
          &&
     (theBall.getLocation().y + theBall.getHeight()) <= (theTriangle.getLocation().y + theTriangle.getHeight())
    ){
       result.setText(""+(Integer.parseInt(result.getText())+1));
       theTriangle.setLocation((int)(ballPanel.getWidth()*Math.random()), 
                               (int)(ballPanel.getHeight()*Math.random()));
    } 
 }

Проверките в if условието са за това дали топката е изцяло вътре в полето на триъгълника. Ако оставим настрани факта, че на екрана виждаме кръгче и триъгълник, реално картинките са правоъгълници. Това е и причината да не им направя фона прозрачен, а да се вижда бялата рамка - по този начин това се вижда ясно. В тялото на if условието преместваме триъгълника на ново място на екрана. Math.random() е метод, който връща число от 0 до 1.

Сега този добавен от нас нов метод трябва да го извикаме на няколко места. Първото е там, където местим кръгчето с клавиатурата. Добавете следния ред в края на метод formKeyPressed (събитието за натиснат бутон):

checkIfCircleIsInTriangle();

Добавете същия ред и в actionPerformed методите за всеки един от четирите метода за натиснати бутони moveLeft, moveRight, moveUp и moveDown. Готово! Вече вашата топка ще "яде" триъгълниците. От тук нататък можете да фантазирате и доработвате програмата - например да се добавят таймери за състезание кой най-бързо ще достигне резултат 10 и подобни. Чрез многонишково програмиране можем да направим така, че триъгълника да "бяга" от нас. Подобни техники ще покажем по-нататък.

При подобни задачи учениците обикновено задават следния логичен въпрос - не може ли да движим топчето и по диагонал? До този момент то се движи само в една от четирите посоки. Добавянето на бутони за движение по диагонал не е трудно. Направете го. Много по-сложен е момента с "два едновремено натиснати бутона от клавиатурата". Реално ние имаме едно единствено събитие - натиснат бутон. Нямаме второ, което да е "натиснат още един бутон".

Правилният начин да се справим с този проблем е да престанем със сегашната тъй или иначе неправилна тактика и да използваме два Timer обекта, които практически ще местят обектите в отделни нишки. За повече информация вижте края на статията (послеписа) тук: https://www.cphpvb.net/uki/9866-ballmover-netbeans/

Другият начин, който нарочно ще изложим, е да се прави така, че "да помним" кой бутон в момента е натиснат. Това е и подходящ момент за запознаване на учениците с първия тип структура от данни - множеството. Ако премълчим техническите характеристики на множеството (вижте статиите за речници и множества в раздел Java), с тях се работи много лесно.

В началото на класа BallMover добавете следния ред:

private final static Set<Integer> KEYSPRESSED = new HashSet<>();

С това създадохме константа (final) от тип множество с име KEYSPRESSED. Когато натиснем бутон, ще искаме да добавяме кода на бутона (той е число) в това множество. Това става по следния начин - отидете в метода за събитието при натиснат бутон (formKeyPressed) и добавете следния ред в неговото начало:

KEYSPRESSED.add(evt.getKeyCode());

Метод add на множеството (Set) добавя елемент в множеството.

Сега ще проверим дали в множеството има точно определени комбинации от елементи - (наляво, надолу), (наляво, нагоре), (надясно, надолу) и (надясно, нагоре). Други "блокиращи се" комбинации като (наляво, надясно) и (нагоре, надолу), както и повече от два натиснати бутона не ни интересуват. Ако няма такава комбинация, ще извършваме досегашното действие - местене в посока на текущо натиснатия клавиш. Целият метод formKeyPressed ще бъде променен по следния начин:

    private void formKeyPressed(java.awt.event.KeyEvent evt) {                
    KEYSPRESSED.add(evt.getKeyCode());
    int currentBallX = theBall.getLocation().x;
    int currentBallY = theBall.getLocation().y;
    if(KEYSPRESSED.contains(java.awt.event.KeyEvent.VK_LEFT)
            &&
       KEYSPRESSED.contains(java.awt.event.KeyEvent.VK_DOWN)
    ){
       theBall.setLocation(currentBallX-1, currentBallY+1);
    }
    else if(KEYSPRESSED.contains(java.awt.event.KeyEvent.VK_LEFT)
                &&
            KEYSPRESSED.contains(java.awt.event.KeyEvent.VK_UP)
    ){
       theBall.setLocation(currentBallX-1, currentBallY-1);
    }
    else if(KEYSPRESSED.contains(java.awt.event.KeyEvent.VK_RIGHT)
                &&
            KEYSPRESSED.contains(java.awt.event.KeyEvent.VK_DOWN)
    ){
       theBall.setLocation(currentBallX+1, currentBallY+1);
    }
    else if(KEYSPRESSED.contains(java.awt.event.KeyEvent.VK_RIGHT)
                &&
            KEYSPRESSED.contains(java.awt.event.KeyEvent.VK_UP)
    ){
       theBall.setLocation(currentBallX+1, currentBallY-1);
    }
    else{
        switch(evt.getKeyCode()){
        case java.awt.event.KeyEvent.VK_LEFT:
            theBall.setLocation(currentBallX-1, currentBallY);
            break;
        case java.awt.event.KeyEvent.VK_RIGHT:
             theBall.setLocation(currentBallX+1, currentBallY);
            break;
        case java.awt.event.KeyEvent.VK_UP:
             theBall.setLocation(currentBallX, currentBallY-1);
            break;
        case java.awt.event.KeyEvent.VK_DOWN:
             theBall.setLocation(currentBallX, currentBallY+1);
             break;
        }
    }
    checkIfCircleIsInTriangle();
}

Остава да направим последно важно нещо - ако освободим даден клавиш, трябва да го премахнем от множеството. На основната рамка добавете Event от тип KeyReleased. В него добавете следния код:

 private void formKeyReleased(java.awt.event.KeyEvent evt) { 
    KEYSPRESSED.remove(evt.getKeyCode());
 }

Метод remove на множество премахва елемент от него.

Допълнителна задача 1: Добавете четирите бутона за движение по диагонал със стъпка 10

Допълнителна задача 2: Ще забележите, че триъгълничето понякога излиза от екрана. Направете така, че това да не може да се получава. Същото направете и за топчето.

Допълнителна задача 3: При показаното решение ще забележите неприятен ефект - при движение по диагонал и пускане на един клавишите, в някои случаи топчето ще продължава да се движи, а в други случаи ще спира. Например ако първо сте натиснали наляво, а после нагоре (движите се в североизточна посока) и пуснете клавиш нагоре, топчето ще спре. Ако първо сте натиснали първо нагоре, а после наляво (същото движение на североизток), след което пуснете клавиш нагоре, топчето ще продължи наляво (няма да спре). Обсъдете на какво може да се дължи този проблем и предложете решение.

Допълнителна задача 4: Направете така, че да се трупа точка не когато топчето влезе изцяло вътре в полето на триъгълника, а когато само го докосне.

Допълнителна задача 5: Преправете всичко така, че да работите правилно с Таймери. Ще забележите, че кодът в крайна сметка ще се опрости, защото няма да има нужда от HashSet и помнене на това кой клавиш е натиснат - просто ще спирате и пускате два различни таймера за нагоре и надолу.

 



Добави коментар

Адресът на електронната поща няма да се публикува


*