В первой части этого урока мы настроили проект и создали интерфейс игры. Также мы создали и реализовали функцию создания колоды карт. Во втором уроке мы создадим игровую логику.
Образец заявки
Если вы хотите запустить пример приложения из этого учебника, обязательно добавьте изображения для автомобилей, как я объяснил в предыдущем учебнике . Не забудьте включить и обновить библиотеку dataSaver, упомянутую в этом руководстве.
1. enableDealButton
Обновите реализацию функции enableDealButton
как показано ниже.
1
2
3
4
5
|
function enableDealButton()
disableDealButton()
dealButton:addEventListener(‘tap’,doDeal)
instructionsText.text = » «
end
|
Сначала мы вызываем disableDealButton
, который удаляет все ранее добавленные прослушиватели, и добавляем прослушиватель doDeal
, который вызывает doDeal
. Метод addEventListener
принимает событие и обратный вызов. Есть ряд событий, которые вы можете прослушивать, в зависимости от контекста, в котором вы их вызываете.
2. disableDealButton
Как я упоминал в предыдущем разделе, в disableButton
мы удаляем все ранее добавленные слушатели.
1
2
3
|
function disableDealButton()
dealButton:removeEventListener(‘tap’,doDeal)
end
|
3. enableBetButtons
В enableBetButtons
мы добавляем прослушиватели betMaxButton
в betMaxButton
и betButton
и даем игроку некоторые инструкции о том, как сделать свою ставку.
1
2
3
4
5
|
function enableBetButtons()
betMaxButton:addEventListener(‘tap’,betMax)
betButton:addEventListener(‘tap’,bet)
instructionsText.text = «Place your Bet or Bet Max ($15)»
end
|
4. disableBetButtons
Как и в disableDealButton
, мы удаляем все ранее добавленные слушатели в disableBetButtons
.
1
2
3
4
|
function disableBetButtons()
betMaxButton:removeEventListener(‘tap’,betMax)
betButton:removeEventListener(‘tap’,bet)
end
|
5. enableHoldButtons
В enableHoldButtons
мы holdButtons
таблицу holdButtons
и добавляем прослушиватель holdButtons
к каждой кнопке.
1
2
3
4
5
|
function enableHoldButtons()
for i=1, #holdButtons do
holdButtons[i]:addEventListener(‘tap’,holdCard)
end
end
|
6. disableHoldButtons
В функции disableHoldButtons
мы также holdButtons
таблицу holdButtons
, но удаляем ранее добавленные прослушиватели вместо добавления новых прослушивателей.
1
2
3
4
5
|
function disableHoldButtons()
for i=1, #holdButtons do
holdButtons[i]:removeEventListener(‘tap’,holdCard)
end
end
|
7. generateCard
Реализация generateCard
требует немного большего объяснения. Сначала мы генерируем случайное число от 1
до длины таблицы deck
. Затем мы создаем временную карту, используя deck["randIndex]..".png"
и сохраняем ссылку в tempCard
. То, что tempCard
deck["randIndex]..".png"
случайного элемента из таблицы deck
, который будет что-то вроде c1
или h5
и добавляет .png
к нему. Поскольку Lua является динамическим языком, мы можем добавлять новые свойства к объектам. В этом примере мы добавляем свойство isHolding
, которое сообщает нам, держит ли игрок карту, свойство cardNumber
, получая подстроку выбранного элемента deck
, и мы делаем то же самое для свойства cardSuit
. Наконец, мы удаляем выбранный элемент из таблицы deck
и возвращаем массив.
01
02
03
04
05
06
07
08
09
10
|
function generateCard()
local randIndex = math.random(#deck)
local tempCard = display.newImage(deck[randIndex]..».png»)
tempCard.anchorX, tempCard.anchorY = 0,0
tempCard.isHolding = false
tempCard.cardNumber = tonumber(string.sub(deck[randIndex],2,3))
tempCard.cardSuit = string.sub(deck[randIndex],1,1)
table.remove(deck,randIndex);
return tempCard;
end
|
8. getCard
В getCard
мы устанавливаем cardPosition
, который является координатой x
первой карты в интерфейсе игры. Мы генерируем карту, добавляем ее в таблицу playerHand
и cardIndex
переменную cardIndex
, которая будет числом от 1
до 5
, представляющим одну из пяти карт. Это позволяет нам расположить карты в правильном порядке в таблице playerHand
. Мы устанавливаем положение каждой карты, используя смещение (93 * (cardIndex - 1))
. Это означает, что карты расположены на расстоянии 93
пикселя друг от друга.
1
2
3
4
5
6
7
|
function getCard(index)
local cardPosition = 199
local tempCard = generateCard()
playerHand[cardIndex] = tempCard
tempCard.x = cardPosition + (93*(cardIndex-1))
tempCard.y = 257;
end
|
9. holdCard
В holdCard
мы сначала получаем ссылку на кнопку, которая была нажата, используя ее свойство buttonNumber
. Это позволяет нам проверить, есть ли карта в таблице playerHand
. Если это так, мы устанавливаем для isHolding
значение false и обновляем координату y
карты. Если карты нет в таблице playerHand
, мы устанавливаем для isHolding
значение true и обновляем координату y
карты. Если игрок выбирает держать карту, его координата y
уменьшается, что означает, что карта немного смещена вверх.
01
02
03
04
05
06
07
08
09
10
|
function holdCard(event)
local index = event.target.buttonNumber
if (playerHand[index].isHolding == true) then
playerHand[index].isHolding = false
playerHand[index].y = 257
else
playerHand[index].isHolding = true
playerHand[index].y = 200
end
end
|
10. resetCardsYPosition
В resetCardsYPosition
мы playerHand
таблицу playerHand
и видим, playerHand
ли какая-либо из карт. Те, которые находятся, возвращаются в исходное положение с помощью библиотеки переходов . Библиотека Transition позволяет очень легко перемещать объекты и изменять их свойства.
1
2
3
4
5
6
7
|
function resetCardsYPosition()
for i=1,#playerHand do
if (playerHand[i].isHolding) then
transition.to(playerHand[i], {time=200,y=257})
end
end
end
|
11. Сохранение данных между сеансами
Мы хотим, чтобы наша игра могла сохранять значения или данные в разных сеансах. Мы можем создать решение самостоятельно, используя библиотеку Corona io , но в этом руководстве мы собираемся использовать стороннее решение. Артур Сосинс создал удобный небольшой модуль для сохранения данных во время игровых сессий.
Загрузите библиотеку и добавьте два файла, которые она содержит, в ваш проект. Чтобы использовать библиотеку, добавьте следующую строку вверху main.lua .
1
|
saver = require(«dataSaver»)
|
Чтобы библиотека работала в нашем проекте, нам нужно внести несколько небольших изменений в dataSaver.lua . Откройте этот файл и измените require "json"
на local json = require "json"
.
Изменить:
1
|
require «json»
|
Для того, чтобы:
1
|
local json = require «json»
|
Второе изменение, которое нам нужно сделать, — это изменить все вхождения system.ResourceDirectory
на system.DocumentsDirectory
как показано ниже.
Изменить:
1
|
system.ResourceDirectory
|
Для того, чтобы:
1
|
system.DocumentsDirectory
|
12. createDataFile
Чтобы настроить хранилище данных, вставьте следующий фрагмент кода ниже функции setupTextFields
. Обязательно включите определение функции, так как мы не заглушали эту функцию в предыдущем уроке.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
function createDataFile()
gameData = saver.loadValue(«gameData»)
if (gameData == nil) then
gameData = {}
gameData.numberOfCredits = 100
gameData.numberOfGames = 0
creditText.text = «100»
gamesText.text = «0»
saver.saveValue(«gameData»,gameData)
else
creditText.text = gameData.numberOfCredits
gamesText.text = gameData.numberOfGames
end
end
|
В createDataFile
мы сначала пытаемся загрузить ключ gameData
из заставки в переменную gameData
. Метод loadValue
возвращает nil
если ключ не существует. Если он не существует, мы инициализируем таблицу gameData
, добавляем свойства numberOfCredits
и numberOfGames
, обновляем соответствующие текстовые поля и сохраняем таблицу gameData
, вызывая saveValue
для saver
. Если ключ существует, то мы уже сделали это и можем заполнить текстовые поля правильными значениями.
На следующем шаге мы createDataFile
функцию createDataFile
функции setup
как показано ниже.
1
2
3
4
5
6
|
function setup()
math.randomseed(os.time())
setupButtons()
setupTextFields()
createDataFile()
end
|
13. betMax
В betMax
мы начинаем с загрузки наших данных в gameData
. Если количество кредитов больше или равно 15
, мы продолжаем и вызываем doDeal
. В противном случае у игрока недостаточно кредитов, чтобы поставить максимум 15
кредитов, и мы показываем ему предупреждение.
01
02
03
04
05
06
07
08
09
10
11
12
13
|
function betMax()
local gameData = saver.loadValue(«gameData»)
local numberOfCredits = gameData.numberOfCredits
if (numberOfCredits >= 15) then
enableDealButton()
betAmount = 15;
betText.text = betAmount
instructionsText.text = » «
doDeal()
else
local alert = native.showAlert( «Not Enough Credits», «You must have 15 or more Credits to Bet Max», { «OK»})
end
end
|
14. bet
В функции bet
мы betMaxButton
кнопку раздачи и удаляем слушателя из betMaxButton
. Поскольку игрок делает обычную ставку, он не может одновременно играть максимальную ставку. Нам нужно проверить, больше или равно ли количество кредитов 5
чтобы убедиться, что игрок не пытается поставить больше кредитов, чем у него осталось. Если betAmount
равен 15
, мы вызываем doDeal
поскольку они сделали максимальную ставку.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
function bet()
enableDealButton()
betMaxButton:removeEventListener(‘tap’,betMax)
instructionsText.text = » «
local numberOfCredits = tonumber(creditText.text — betAmount)
if (numberOfCredits >= 5) then
betAmount = betAmount + 5
betText.text = betAmount
else
doDeal()
end
if (betAmount == 15) then
doDeal()
end
end
|
15. doDeal
Функция doDeal
координирует doDeal
карт. Если это новая игра, мы сдаем начальную руку. В противном случае мы раздаем игроку новые карты.
1
2
3
4
5
6
7
8
|
function doDeal()
if(isNewGame == true) then
isNewGame = false
dealInitialHand()
else
dealNewCards()
end
end
|
16. dealInitialHand
Мы отключаем кнопки ставок в dealInitialHand
и dealInitialHand
кнопки удержания. Мы вызываем getCard
пять раз, который генерирует первые пять карт. Затем мы загружаем gameData
, обновляем currentCredits
, рассчитываем newCredits
, обновляем текстовое поле кредита и сохраняем gameData
.
01
02
03
04
05
06
07
08
09
10
11
12
13
|
function dealInitialHand()
disableBetButtons()
enableHoldButtons()
for i=1, 5 do
getCard(i)
end
local gameData = saver.loadValue(«gameData»)
local currentCredits = gameData.numberOfCredits
local newCredits = currentCredits — betAmount
creditText.text = newCredits
gameData.numberOfCredits = newCredits
saver.saveValue(«gameData»,gameData)
end
|
17. dealNewCards
В dealNewCards
мы проверяем, держит ли игрок карту. Если это так, тогда мы получаем ссылку на текущую карту, вызываем removeSelf
, устанавливаем ее removeSelf
nil
и получаем новую карту, вызывая getCard(i)
.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
function dealNewCards()
disableDealButton()
disableHoldButtons()
for i = 1, 5 do
if (playerHand[i].isHolding == false) then
local tempCard = playerHand[i]
tempCard:removeSelf()
tempCard = nil
getCard(i)
end
end
resetCardsYPosition()
getHand()
end
|
nil
чтобы убедиться, что оно правильно настроено для сборки мусора. 18. getHand
Функция getHand
определяет руку игрока. Как вы можете видеть ниже, функция довольно сложна. Давайте разберемся, если посмотрим, что происходит. Начните с реализации функции getHand
как показано ниже.
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
|
function getHand()
table.sort(playerHand, function(a,b) return a.cardNumber < b.cardNumber end)
local frequencies = {}
for i=1, 13 do
table.insert(frequencies,0)
end
for i=1,#playerHand do
frequencies[playerHand[i].cardNumber] = frequencies[playerHand[i].cardNumber] + 1
end
local numberOfPairs = 0
local hasThreeOfAKind = false
local hasFourOfAKind = false
local winningHand = «Nothing»
local cashAward = 0
local isStraight = true
local isRoyalStraight = false
local isFlush = true
for i=0, #frequencies do
if (frequencies[i] == 2) then
numberOfPairs = numberOfPairs+1
end
if (frequencies[i] == 3) then
hasThreeOfAKind = true
end
if (frequencies[i] == 4) then
hasFour = true
end
end
if (numberOfPairs > 0) then
if(numberOfPairs == 1)then
winningHand = «1 pair»
cashAward = 1 * betAmount
else
winningHand = «2 pair»
cashAward = 2 * betAmount
end
end
if (hasThreeOfAKind) then
winningHand = «3 of A Kind»
cashAward = 3 * betAmount
end
if (hasFour) then
winningHand = «Four of A Kind»
cashAward = 7 * betAmount
end
if (numberOfPairs == 1 and hasThreeOfAKind) then
winningHand = «Full House»
cashAward = 6 * betAmount
end
if (playerHand[1].cardNumber == 1 and playerHand[2].cardNumber == 10 and playerHand[3].cardNumber == 11 and playerHand[4].cardNumber == 12 and playerHand[5].cardNumber == 13 )then
isRoyalStraight = true
end
for i=1, 4 do
if (playerHand[i].cardNumber+1 ~= playerHand[i+1].cardNumber) then
isStraight = false
break
end
end
for i=1, 5 do
if(playerHand[i].cardSuit ~= playerHand[1].cardSuit) then
isFlush = false
break
end
end
if (isFlush) then
winningHand = «Flush»
cashAward = 5 * betAmount
end
if (isStraight) then
winningHand = «Straight»
cashAward = 4 * betAmount
end
if (isRoyalStraight)then
winningHand = «Straight»
cashAward = 4 * betAmount
end
if (isFlush and isStraight) then
winningHand = «Straight Flush»
cashAward = 8 * betAmount
end
if (isFlush and isRoyalStraight) then
winningHand = «Royal Flush»
cashAward = 9 * betAmount
end
awardWinnings(winningHand, cashAward)
end
|
Мы начинаем с вызова table.sort
playerHand
таблицы playerHand
. Метод sort
выполняет sort
на месте, он использует оператор <
чтобы определить, должен ли элемент таблицы располагаться до или после другого элемента. Мы можем передать функцию сравнения в качестве второго аргумента. В нашем примере мы проверяем, меньше ли свойство cardNumber
чем предыдущее. Если это так, то это меняет местами два элемента.
Затем мы создаем таблицу frequencies
, заполняем ее тринадцатью нулями ( 0
), playerHand
таблицу playerHand
и увеличиваем число каждого индекса, если playerHand
содержит это число. Например, если у вас есть два три и три пятерки, то таблица frequencies
будет 0, 0, 2, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0
.
На следующем шаге мы объявляем и устанавливаем ряд локальных переменных, которые нам нужны за несколько минут. Затем мы перебираем таблицу frequencies
и проверяем числа, чтобы увидеть, содержит ли индекс 2
, что означает, что у нас есть пара, 3
, что означает, что у нас есть три вида, или 4
, что означает, что у нас есть четыре из Добрый. Затем мы проверяем количество пар, три в своем роде и четыре в своем роде, и winningHand
cashAward
значения winningHand
и cashAward
.
Чтобы проверить Royal Straight , нам нужно проверить, равна ли первая карта тузу, а оставшиеся карты равны десяти, Джеку, Королеве и Королю. Чтобы проверить наличие обычного playerHand
мы перебираем playerHand
и проверяем, больше ли каждый последующий номер cardNumber
на единицу, чем предыдущий. Чтобы проверить наличие сброса , мы проверяем, все cardSuit
ключи cardSuit
карты были равны ключу cardSuit
первой карты.
В конце getHand
мы вызываем awardWinnings
.
19. awardWinnings
В awardWinnings
мы показываем игроку, какая у него рука, и обновляем настройки gameData
. Мы сохраняем gameData
и вызываем newGame
с задержкой в три секунды.
01
02
03
04
05
06
07
08
09
10
11
12
13
|
function awardWinnings(theHand, theAward)
instructionsText.text = «You got «..theHand
winText.text = theAward
local gameData = saver.loadValue(«gameData»)
local currentCredits = gameData.numberOfCredits
local currentGames = gameData.numberOfGames
local newCredits = currentCredits + theAward
local newGames = currentGames + 1
gameData.numberOfCredits = newCredits
gameData.numberOfGames = newGames
saver.saveValue(«gameData»,gameData)
timer.performWithDelay( 3000, newGame,1 )
end
|
20. newGame
В newGame
мы проходим и сбрасываем все переменные, создаем новую колоду карт и проверяем, равен ли gameData.numberOfCredits
нулю. Если это так, то игрок потратил все свои кредиты, поэтому мы присуждаем им еще 100 кредитов. Наконец, мы обновляем текстовые поля.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
function newGame()
for i=1,#playerHand do
playerHand[i]:removeSelf()
playerHand[i] = nil
end
playerHand = {}
deck = {}
betAmount = 0
isNewGame = true
createDeck()
enableBetButtons()
instructionsText.text = «Place your Bet or Bet Max ($15)»
winText.text = «»
betText.text = «»
local gameData = saver.loadValue(«gameData»)
if (gameData.numberOfCredits == 0)then
gameData.numberOfCredits = 100
saver.saveValue(«gameData»,gameData)
end
creditText.text = gameData.numberOfCredits
gamesText.text = gameData.numberOfGames
end
|
21. Тестирование игры
Обновите функцию setup
, вызвав функцию createDeck
как показано ниже, и проверьте окончательный результат.
1
2
3
4
5
6
7
|
function setup()
math.randomseed(os.time())
createDeck();
setupButtons()
setupTextFields()
createDataFile()
end
|
Вывод
В этом уроке мы создали интересную и интересную игру в покер. Мы еще не использовали кнопку для вывода денег, но вы можете сделать это в своей игре. Я надеюсь, что вы узнали что-то полезное в этом уроке. Оставьте свой отзыв в комментариях ниже.