Статьи

Создание игры в блэкджек в Corona — реализация игрового процесса

В предыдущей части этой серии мы соединили наш интерфейс для игры в Блэкджек и создали колоду карт. В этой части руководства мы добавим необходимую игровую логику в блэкджек. Давайте начнем!


Нам нужен способ хранить деньги игрока между игровыми сессиями, и для этого мы будем использовать простой текстовый файл. Добавьте следующий код ниже функции createDeck() .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function createDataFile()
local path = system.pathForFile( «data.txt», system.DocumentsDirectory )
 
local fh, errStr = io.open( path, «r» ) — r means read mode
 
if fh then
    print(«DataFile Exists Already») — already exists so we just return from this function
   return
else
   print( «Reason open failed: » .. errStr ) — display failure message in terminal
 
   — create file because it doesn’t exist yet
   fh = io.open( path, «w» ) — w means write mode
 
   if fh then
       local money = 500
       fh:write(money)
   else
        print( «Create file failed!»..errStr )
   end
   io.close(fh)
end
end

Здесь мы создаем файл с именем « data.txt » и записываем 500 в него. Игрок начнет игру с $ 500,00 . Важно убедиться, что вы всегда вызываете io.close() когда завершаете свои операции.

Вы можете узнать больше о создании этого файла данных в документации на сайте Corona.


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

01
02
03
04
05
06
07
08
09
10
11
function readMoney()
local path = system.pathForFile( «data.txt», system.DocumentsDirectory )
local fh, errStr = io.open( path, «r» )
    if fh then
    local theMoney = fh:read( «*n» )
    return theMoney
    else
     print( «Reason open failed: » .. errStr ) — display failure message in terminal
    end
   io.close(fh)
end

Мы открываем файл тем же методом, затем используем read("*n") чтобы получить значение из текстового файла. « * N » означает читать как число.


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

01
02
03
04
05
06
07
08
09
10
function saveMoney(money)
local path = system.pathForFile( «data.txt», system.DocumentsDirectory )
local fh, errStr = io.open( path, «w» )
    if fh then
        fh:write(money)
    else
     print( «Reason open failed: » .. errStr ) — display failure message in terminal
    end
    io.close(fh)
end

Здесь мы открываем файл для записи, как обозначено » w » в методе open() . Затем мы записываем money в файл, который был передан в качестве параметра.


Теперь нам нужно создать начальный баланс при первом запуске игры. Добавьте следующее в начало Setup() .

1
2
3
4
5
6
7
8
function Setup()
    createDataFile();
    setupCoins();
    setupButtons();
    setupTextFields();
    setupGroups();
    createDeck();
end

Если вы откроете терминал Corona и дважды запустите приложение, вы увидите распечатанный на терминале файл DataFile уже существует . Я оставил сообщения print() в коде обработки файлов, чтобы вы могли видеть шаги и любые ошибки. Если все работает хорошо, не стесняйтесь удалять их.


Итак, теперь, когда мы установили баланс, давайте покажем его в нашем приложении. Измените следующий код в функции setupTextFields() .

1
2
3
4
5
6
7
8
function setupTextFields()
    instructionsText = display.newText( «Place Your Bet», 300, 300, native.systemFont, 30 );
    instructionsText:setTextColor( 0,0,0)
    bankText = display.newText(«Your Bank:$ «..readMoney(),10,905,native.systemFont, 30 );
    bankText:setTextColor(0,0,0)
    betText = display.newText(«»,650,906,native.systemFont,30);
    betText:setTextColor(0,0,0);
end

Обратите внимание, что мы добавляем остаток в « Ваш банк: $ », вызывая readMoney() .


Теперь, когда у нас есть деньги, мы можем добавить код в нашу betHandler() . Мы создали эту функцию в предыдущей части руководства, поэтому убедитесь, что вы добавили ее, а не переопределяли!

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
local betHandler = function( event )
    local theMoney = readMoney();
    if event.phase == «began» then
        local t = event.target
    if(bet + t.betAmount > theMoney)then
           print(«Trying to bet more than have»);
           print(«Money is»..theMoney);
           return;
        else
        bet = bet + t.betAmount
        local tempImage = display.newImage(«money»..t.betAmount..».png»);
        local randomX = (math.random() * 150);
        local randomY = (math.random() * 100);
        tempImage.x = randomX;
        tempImage.y = randomY;
        coinContainer:insert(tempImage);
        dealBtn.isVisible = true;
        instructionsText.text = «»;
        betText.text = «Your Bet: $»..bet;
        end
     end
end

Здесь мы сначала читаем, сколько денег игрок имеет. Если они пытаются поставить больше, чем имеют, функция просто возвращается. Я оставил операторы print() в коде, чтобы помочь с отладкой. Мы устанавливаем динамический ключ betAmount , когда устанавливаем деньги. Если они не пытаются ставить слишком много, мы добавляем сумму к переменной bet .

Затем мы создаем tempImage , генерируем два случайных числа, устанавливаем изображения x и y на случайные числа и, наконец, добавляем изображение в контейнер для монет. Вы заметите, что мы используем "money"..t.betAmount..".png" для URL изображения. Наши изображения для денег называются « money10.png », « money25.png » и « money50.png », поэтому все, что мы здесь делаем, это объединяем их вместе, чтобы сформировать строку изображения.

Наконец, мы устанавливаем dealBtn instructionsText и устанавливаем betText равный bet .

Теперь нам нужно добавить addListeners() в наш код Setup() . Добавьте следующий код внизу.

1
2
3
4
5
6
7
8
9
function Setup()
    createDataFile()
    setupCoins();
    setupButtons();
    setupTextFields();
    setupGroups();
    createDeck();
    addListeners();
end

Если вы протестируете приложение сейчас, вы сможете поставить деньги.


Нам нужен способ получить ценность руки игрока и руки дилера. Введите следующее ниже функции createDeck() .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
function getHandValue(theHand)
local handValue = 0;
local hasAceInHand=false;
    for i=1,#theHand do
     local cardsValue = tonumber(string.sub(theHand[i],2,3));
         if (cardsValue > 10) then
        cardsValue = 10;
         end
        handValue = handValue + cardsValue;
        if (cardsValue == 1)then
            hasAceInHand = true;
        end
    end
    if (hasAceInHand and handValue <= 11)then
    handValue = handValue + 10;
    end
    return handValue;
end

Мы устанавливаем переменную hasAceInHand переменную hasAceInHand . Затем мы перебираем theHand который будет либо playerHand либо dealerHand . Мы создаем переменную cardsValue , cardsValue ее к числу, получая подстроку текущей карты. Если cardsValue больше 10, мы устанавливаем его cardsValue 10 . Гнезда, королевы и короли представлены 11 , 12 и 13 . Затем мы добавляем значение в handValue . Если ценность карты равна 1, то мы знаем, что у них в руке туз. Если у них туз и их handValue меньше или равно 11, мы добавляем к нему 10 .


Теперь у нас есть все для сделки, поэтому теперь мы будем анимировать карты, чтобы сделать игру более интересной. Эта функция довольно большая, потому что именно там происходит вся логика игры. Мы разделим это на несколько шагов. Добавьте следующий код в функцию deal() вы создали в первой части этой серии.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
money10.isVisible = false;
  money25.isVisible = false;
  money50.isVisible = false;
  dealBtn.isVisible = false;
  local randIndex = math.random(#deck)
  local tempCard = display.newImage(«card_front.png»,630,55);
  table.insert(allCards,tempCard);
  local whichPosition;
  local whichArray={};
  local whichGroup;
   if(dealTo == «player») then
       whichArray = playerHand;
       whichPosition = playerCardsY;
       whichGroup = playerGroup;
   else
       whichArray = dealerHand;
       whichPosition = dealerCardsY;
       whichGroup = dealerGroup;
   end
    
   table.insert(whichArray,deck[randIndex]);
   local xPos = 20+#whichArray*35
   transition.to(tempCard, {time=1000,x=xPos,y=whichPosition,onComplete=function()
        if(dealTo == «dealer» and #dealerHand == 1) then
           firstDealerCard = deck[randIndex];
           dealerGroup:insert(tempCard);
       else
       tempCard:removeSelf();
       tempCard = display.newImage(deck[randIndex]..».png»,xPos-45,whichPosition-60);
       whichGroup:insert(tempCard);
       end
       table.remove(deck,randIndex);
       if(#dealerHand < 2)then
           if(dealTo == «player»)then
               dealTo = «dealer»
           else
               dealTo = «player»
           end
          deal();
          end
   end
   });

Здесь мы устанавливаем наши деньги, чтобы быть невидимыми, а наша кнопка сделки видимой. Затем мы генерируем randIndex из таблицы deck . Затем мы генерируем новое изображение tempCard и вставляем tempCard в таблицу allImages . Мы устанавливаем три локальные переменные: whichPosition , whichArray и whichGroup . Мы проверяем, кому в настоящее время поручено инициализировать эти переменные соответствующим образом.

Затем мы вставляем deck[randIndex] в whichArray , которая является либо playerHand либо таблицей dealerHand . Помните, что наша колода состоит из строк, поэтому колода [randIndex] будет выглядеть как « h5 », « d10 ».

Мы устанавливаем локальную переменную xPos равную 20+#whichArray*35 , которая устанавливает ее равной 20 плюс длина таблицы + 35 . Первый раз в таблице длина будет 1 , так что 20 + 1 * 35 . В следующий раз длина таблицы будет равна 2, поэтому будет 20 + 2 * 35 . Все это позволяет нам равномерно распределять наши карты по оси X.

Мы используем метод coronas transition.to для перемещения tempCard . Когда карта завершает свой переход, мы проверяем, имеем ли мы дело с дилером и имеет ли его длина руки 1 . Если это так, мы устанавливаем firstDealerCard равным deck[randIndex] и вставляем карту в группу dealerGroup . Нам нужна ссылка на первую карту дилера, чтобы мы могли показать ее позже.

Если это была не первая карта дилера, то мы удаляем tempCard , генерируем новое изображение с помощью deck[randIndex] и вставляем ее в соответствующую группу (игрок или дилер). Причина, по которой мы вычитаем 45 и 60 соответственно, заключается в том, что Corona по умолчанию устанавливает опорную точку изображений в центр, и наши изображения имеют размер 90 x 120 . Следовательно, мы берем половину этого.

Наконец, мы удаляем карту в позиции randIndex из deck и проверяем, не dealerHand ли dealerHand 2 . Если это так, мы меняем dealTo на его противоположность (игрок или дилер), а затем снова dealTo сделку.

Наконец, вы можете протестировать приложение, поставить немного денег и получить первые две карты.


Добавьте следующий код ниже того места, где мы вызывали deal() на шаге выше.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
if(#dealerHand < 2)then
   if(dealTo == «player»)then
       dealTo = «dealer»
   else
       dealTo = «player»
   end
   deal();
   elseif(#dealerHand == 2 and #playerHand == 2) then
       if(getHandValue(playerHand)==21 or getHandValue(dealerHand) == 21)then
        doGameOver(true);
      else
           standBtn.isVisible = true;
       hitBtn.isVisible = true;
           end
      end

Здесь мы проверяем, равны ли как dealerHand и playerHand длиной 2 . Если они есть, мы проверим, равны ли их руки 21. Если все их руки равны 21, игра окончена. Мы называем doGameOver(true) который присуждает выигрыши и начинает новую игру. true параметр верен для блэкджека. Иначе это будет ложно.


Функция doGameOver() присуждает выигрыш и запускает новую игру. Мы также будем кодировать эту функцию в несколько шагов. Сейчас мы можем использовать его для проверки части блэкджека. Введите следующий код под функцией deal .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
function doGameOver(hasBlackJack)
    local playerHandValue = getHandValue(playerHand);
    local dealerHandValue = getHandValue(dealerHand);
    local tempCardX = allCards[2].x;
    local tempCardY= allCards[2].y;
    allCards[2]:removeSelf();
    local tempCard = display.newImage(firstDealerCard..».png»,tempCardX-45,tempCardY-60);
    dealerGroup:insert(tempCard);
    tempCard:toBack();
    if(hasBlackJack)then
        if(playerHandValue > dealerHandValue)then
        money = money + bet*1.5;
        instructionsText.text = «You Got BlackJack!»;
        winner = «player»
         else
        money = money — bet;
        instructionsText.text = «Dealer got BlackJack!»;
        winner = «dealer»
        end
    end
end

Здесь мы получаем значение руки игрока и дилера. Мы получаем ссылку на allCards[2] , x и y , которая является первой картой дилера, и затем удаляем ее с дисплея. Затем мы генерируем tempCard с помощью переменной firstDealerCard мы установили ранее. Еще раз вычитаем 45 и 60 . Затем мы вставляем эту новую карту в группу dealerGroup . Когда мы делаем это, она находится на второй карточке, поэтому мы отправляем ее обратно, вызывая toBack() .

Мы проверяем, имеет ли hasBlackJack true, и если это так, то мы проверяем, больше ли рука игрока, чем рука дилера. Если это так, мы присуждаем немного денег, соответственно устанавливаем instructionsText и меняем winner на « игрок ».


Нам нужно помнить, чтобы инициализировать переменную money прежде чем что-либо делать с ней. Добавьте следующее в функцию Setup() .

1
2
3
4
5
6
function Setup()
   createDataFile();
   money = readMoney();
   setupCoins();
   …..
end

Мы находимся в точке, где мы можем проверить, есть ли у игрока или дилера блэкджек. Мы временно изменим код для тестирования, но вернем его обратно. Сначала в функции deal измените следующее.

1
2
3
elseif(#dealerHand == 2 and #playerHand == 2) then
    if(true)then
     doGameOver(true);

Затем в функции doGameOver() измените первые две строки следующим образом.

1
2
local playerHandValue = 21—getHandValue(playerHand);
local dealerHandValue = 18—;getHandValue(dealerHand);

Теперь попробуйте и протестируйте приложение. Вы должны увидеть, что игрок получает блэкджек.

Теперь измените первые две строки внутри doGameOver на следующие

1
2
local playerHandValue = 18—getHandValue(playerHand);
local dealerHandValue = 21—;getHandValue(dealerHand);

Теперь, если вы проверите, вы должны увидеть, что у дилера есть блэкджек.


Теперь, когда мы проверили, мы должны установить наши переменные обратно. В функции deal измените следующее.

1
2
3
elseif(#dealerHand == 2 and #playerHand == 2) then
   if(getHandValue(playerHand)==21 or getHandValue(dealerHand) == 21)then
    doGameOver(true);

Затем в функции doGameOver() первые две строки в их предыдущее состояние.

1
2
local playerHandValue = getHandValue(playerHand);
local dealerHandValue = getHandValue(dealerHand);

Если вы проведете тест еще раз, и ни у вас, ни у дилера не появится блэкджек, кнопка раздачи должна стать невидимой, а кнопки удара и подставки должны стать видимыми.


Мы должны позволить игроку ударить или встать, как только сдаются первые две карты. Введите следующее в функцию hit() введенную вами в предыдущей части этой серии.

1
2
3
4
5
6
function hit(event)
    if(«began» == event.phase)then
    dealTo = «player»;
    deal();
    end
end

Если вы проверите сейчас, вы сможете ударить.

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

1
local canBet=true;

Теперь измените функцию hit() на следующую.

1
2
3
4
5
6
7
8
9
function hit(event)
    if(«began» == event.phase)then
      if(canBet)then
      dealTo = «player»;
      deal();
      canBet = false;
      end
    end
end

В функции deal() добавьте следующую строку кода.

1
2
transition.to(tempCard, {time=1000,x=xPos,y=whichPosition,onComplete=function()
        canBet = true;

Теперь игрок может нанести удар только один раз, карта была сдана.


Далее нам нужно позволить игроку встать. Добавьте следующее в функцию stand() введенную вами в предыдущей части этой серии.

01
02
03
04
05
06
07
08
09
10
11
function stand()
    playerYields = true;
    standBtn.isVisible = false;
    hitBtn.isVisible = false;
    if(getHandValue(dealerHand) < 17)then
    dealTo = «dealer»
    deal();
    else
      doGameOver(false);
    end
end

Здесь мы указываем, что игрок «держит». Установите standBtn и hitBtn в невидимый. Мы проверяем, составляет ли рука дилера меньше 17 , и если это так, мы меняем dealTo на дилера и dealTo сделку. Если его рука не меньше 17 , то мы вызываем doGameOver() . Дилер должен стоять на 17 или больше.

Если вы протестируете сейчас, игрок может получить желаемую руку, а затем нажать «Стенд». Однако есть пара проблем. Если игрок обанкротился, он может продолжить вытягивать карты, и раздача останавливается у дилера. Нам нужно, чтобы дилер продолжал вытягивать карты до тех пор, пока ему не исполнится 17 или более, или пока он не перестанет. Мы исправим эти проблемы, когда закончим нашу функцию deal() в следующие два шага.


Добавьте следующее в функцию deal() .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
if(getHandValue(playerHand)==21 or getHandValue(dealerHand) == 21)then
    doGameOver(true);
       else
         standBtn.isVisible = true;
         hitBtn.isVisible = true;
    end
end
 if( #dealerHand >=3 and(getHandValue(dealerHand) < 17))then
    deal();
        elseif( playerYields and getHandValue(dealerHand)>=17)then
        standBtn.isVisible = false;
        hitBtn.isVisible = false;
        doGameOver(false);
 end

Здесь мы проверяем, является ли длина рук дилера больше или равна 3 и что рука дилера меньше 17 . Если его рука меньше 17, он должен взять карту. В противном случае мы проверяем, дал ли игрок сдачу и имеет ли рука дилера больше или равно 17 . Если так, игра окончена. Дилер может иметь 17 или больше с первыми двумя картами.


Введите следующий код в функции deal() .

01
02
03
04
05
06
07
08
09
10
11
12
if( #dealerHand >=3 and(getHandValue(dealerHand) < 17))then
   deal();
   elseif( playerYields and getHandValue(dealerHand)>=17)then
   standBtn.isVisible = false;
   hitBtn.isVisible = false;
   doGameOver(false);
end
if(getHandValue(playerHand)>21)then
    standBtn.isVisible = false;
    hitBtn.isVisible = false;
    doGameOver(false);
end

Если игрок бьет и берет карту, которая ставит его старше 21 года , игра окончена.


На этом шаге мы продолжим кодирование функции gameOver() . На данный момент функция раздачи определяет, кто выигрывает, когда у игрока или дилера есть блэкджек. Нам нужно обработать все другие возможные результаты. Введите следующее в функции doGameOver() .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
else
   money = money — bet;
   instructionsText.text = «Dealer got BlackJack!»;
   winner = «dealer»
end
else
   if (playerHandValue > 21)then
        instructionsText.text = «You Busted!»;
         money = money — bet;
         winner = «dealer»;
    elseif (dealerHandValue > 21)then
         money = money + bet;
         instructionsText.text =»Dealer Busts. You Win!»;
         winner = «player»;
    elseif (dealerHandValue > playerHandValue)then
         money = money — bet;
         instructionsText.text =»You Lose!»;
         winner = «dealer»
    elseif (dealerHandValue == playerHandValue)then
     money = money — bet;
     instructionsText.text = «Tie — Dealer Wins!»;
     winner = «tie»
    elseif (dealerHandValue < playerHandValue)then
     money = money +bet;
     instructionsText.text=»You Win!»;
     winner = «player»
   end
end

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


Введите следующее в нижней части функции doGameOver()

01
02
03
04
05
06
07
08
09
10
elseif (dealerHandValue < playerHandValue)then
    money = money +bet;
    instructionsText.text=»You Win!»;
    winner = «player»
  end
end
if(money < 10)then
   money = 500
  end
saveMoney(money)

После каждого раунда мы должны экономить деньги игрока. Если их деньги меньше 10, мы считаем их банкротами и обнуляем их до 500 . В качестве упражнения, посмотрите, можете ли вы получить предупреждение, говорящее что-то вроде: « Вы обанкротились, дилер присуждает вам $ 500,00 ».


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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
saveMoney(money)
local tweenTo;
    if(winner == «player»)then
    tweenTo = playerCardsY;
    else
    tweenTo = dealerCardsY
    end
transition.to(coinContainer, {time=1000,y=tweenTo,onComplete=function()
    for i=coinContainer.numChildren,1,-1 do
        local child = coinContainer[i]
        child:removeSelf()
        child = nil;
    end
    timer.performWithDelay( 2000, newGame);
    coinContainer.y = 600;
    end
   });

Здесь мы видим, кто выиграл раунд, а затем мы оживляем монеты для них. Когда анимация завершится, мы удаляем все монеты из coinContainer и устанавливаем их coinContainer nil, так как мы закончили с ними. Наконец, мы вызываем newGame() через две секунды, мы также сбрасываем нашу позицию coinContainer .


Введите следующее под doGameOver() .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
function newGame()
   instructionsText.text = «PLACE YOUR BET»;
   betText.text = «»;
   money10.isVisible = true;
   money25.isVisible = true;
   money50.isVisible = true;
   bankText.text = «Your Bank: $»..readMoney()
 
  for i=dealerGroup.numChildren,1,-1 do
     local child = dealerGroup[i]
     child:removeSelf()
     child = nil;
 end
 
 for i=playerGroup.numChildren,1,-1 do
    local child = playerGroup[i]
    child:removeSelf()
   child = nil;
end
   dealTo = «player»;
   playerHand = {};
   dealerHand = {};
   allCards = {};
   createDeck();
   firstDealerCard=»»;
   playerYields = false;
   winner = «»;
   bet = 0;
   canBet = true;
end

Здесь мы устанавливаем видимые деньги, удаляем карты из групп игроков и дилеров и сбрасываем все наши переменные.


Мы написали забавную и интересную игру в блэкджек, используя Corona SDK. Спасибо за чтение, надеюсь, вы нашли этот урок полезным!