Chapter 6 — Implementing High Scores / Глава 6 – Внедрение лучших результатов

Chapter 6 — Implementing High Scores / Глава 6 -  Внедрение лучших результатов

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

Композитор-доступных данных

По большей части, когда вы используете Composer, сцены остаются самостоятельно выделенными , а это означает , что переменные, функции и объекты , которые вы создаете в пределах данной сцены изолированы на этой конкретной сцене. Например, livesи scoreпеременные , которые мы используем в пределах game.luaсвязаны только с этой сценой.

Это , как правило , хорошо для поддержания приложения чистой и организованной, но иногда вам нужно получить доступ к переменным / объектов в одной сцене из другой сцены , что это по сути не знают об их существовании. В этой главе, например, мы должны получить доступ счет игрока ( scoreс game.lua) внутри нашей предстоящей highscores.luaсцены.

Сам Lua предоставляет различные способы, чтобы пройти и доступа к данным между модулями, но к счастью, композитор делает его еще проще с помощью следующих двух команд:

  • composer.setVariable() - Устанавливает переменную, объявленную в одной сцене, чтобы быть доступным на протяжении всего приложения Composer.
  • composer.getVariable()- Позволяет получить значение любой переменной набора через composer.setVariable().

Вооруженные этими командами, давайте обновим endGame()функцию. Откройте game.luaфайл, найдите функцию, и добавить еще одну команду перед остальными:

local function endGame()
    composer.setVariable( "finalScore", score )
    composer.removeScene( "highscores" )
    composer.gotoScene( "highscores", { time=800, effect="crossFade" } )
end

Эта новая команда, composer.setVariable( "finalScore", score )создает Composer доступную переменную finalScoreс заданным значением scoreпеременной. При этом на месте, мы сможем извлечь значение из высоких баллов сцены.

Вы можете передать любой стандартный тип переменной Lua, включая таблицы, как значение composer.setVariable(). Вы даже можете использовать его для создания локальной функцией от одной сцены доступны в другой. Эта гибкость делает composer.setVariable()и composer.getVariable()два из наиболее полезных API - интерфейсов в рамках Composer набора инструментов.

 

Лучшие результаты сцены

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

Во- первых, сделайте копию стандартного scene-template.luaфайла, входящий в состав этой главы исходных файлов . Переименовать эту копию highscores.lua, поместите его в вашей StarExplorerпапке проекта и откройте его с помощью выбранного текстового редактора.

Как обычно, мы начнем с инициализации некоторых переменных. Поместите следующий код в сюжетном доступной области кода:

-- Initialize variables
local json = require( "json" )

local scoresTable = {}

local filePath = system.pathForFile( "scores.json", system.DocumentsDirectory )

С первой командой, мы загружаем новый ресурс для нашего приложения:. Библиотеку JSON JSON является удобным форматом для работы с данными. Если вы не знакомы с JSON, не волнуйтесь - для целей этого приложения, вам просто нужно научиться использовать следующие встроенные команды Corona:

  • json.encode()
  • json.decode()

Эти команды в основном позволяют взять таблицу Lua, хранить ее содержимое в хорошо изученной формате, а затем перевести эти данные обратно в таблицу Lua.

Следующая команда, local scoresTable = {}просто создает пустую таблицу , которая в конечном счете содержать извлекаемые баллы, или обновленный список оценок , чтобы сэкономить.

Последняя строка создает абсолютный путь к файлу (JSON scores.json) , который мы будем использовать , чтобы сохранить десять наибольшее количество баллов. Не волнуйтесь , что этот файл не на самом деле еще существует - эта команда создаст файл и инициировать ссылку на него в соответствии с переменной filePath.

Важный

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

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

Для того, чтобы убедиться , что scores.jsonфайл находится в постоянном месте, мы указываем в system.DocumentsDirectoryкачестве второго параметра команды мы только что ввели. Это говорит Corona , чтобы создатьscores.jsonфайл в каталоге внутреннего приложения "документы". В то время как есть и другие места , которые мы могли бы поместить файл, мы не будем вдаваться в подробности о них здесь - просто помните , что каталог документов, на который ссылается константа system.DocumentsDirectory, это единственное место , которое обеспечивает поистине постоянное хранилище для файлов, которые создаются внутри приложение.Другими словами, даже если игрок выходит приложение и не откроется снова , пока через месяц, то scores.jsonфайл будет по- прежнему существует.

 

Загрузка данных

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

Непосредственно следующие команды, которые уже добавлены к сцене доступной области кода, включают в себя следующие функции:

local function loadScores()

    local file = io.open( filePath, "r" )

    if file then
        local contents = file:read( "*a" )
        io.close( file )
        scoresTable = json.decode( contents )
    end

    if ( scoresTable == nil or #scoresTable == 0 ) then
        scoresTable = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
    end
end

При работе с файлами , содержащими данные, первый шаг , чтобы подтвердить , что файл существует. Первая команда внутри этой функции local file = io.open( filePath, "r" ), попытки открыть файл scores.jsonвsystem.DocumentsDirectoryпапке (помните , что мы устанавливаем переменную , filePathчтобы указать на этот файл и папку). В этой команде также отметить второй параметр,  "r". Это говорит Corona , чтобы открыть файл счтения только доступа, но этого достаточно здесь , потому что нам просто нужно прочитать содержимое файла.

В условном блоке , следующем, если файл существует, его содержимое будет сброшен в локальную переменную contents. После того как мы его содержимое, мы закрываем файл с io.close( file ). Затем, с помощью команды json.decode(), мы декодировать contentsи сохранять значения в scoresTable - в основном, он json.decode()преобразует файл JSON в таблицу Lua , которые могут быть использованы в нашем приложении.

В окончательном условного блока, только в том случае , scores.jsonфайл пуст или не существует, мы относим scoresTableдесять значений по умолчанию , 0так что сцена имеет что - то работать.

Если вы хотите, чтобы сделать вещи более интересным, вы можете начать игру с баллами десять по умолчанию, что "компьютер" забил и бросить вызов игроку, чтобы победить их! Например:

        scoresTable = { 10000, 7500, 5200, 4700, 3500, 3200, 1200, 1100, 800, 500 }

 

Сохранение данных

Сохранение данных так же просто , как чтение данных. После предыдущей функции в сюжетном доступной области кода, добавьте эту функцию:

local function saveScores()

    for i = #scoresTable, 11, -1 do
        table.remove( scoresTable, i )
    end

    local file = io.open( filePath, "w" )

    if file then
        file:write( json.encode( scoresTable ) )
        io.close( file )
    end
end

Первый шаг , чтобы очистить любые избыточные баллы от scoresTable. Потому что нам нужно только сохранить самые высокие десять баллов, ничего кроме этого может быть отброшен. Используя forпетлю, мы делаем шаг назад по таблице от ее общего количества ( #scoresTable) , чтобы 11, эффективно удаляя все , кроме десяти баллов.

Затем мы открываем scores.jsonфайл. В отличие от нашего io.open()звонка внутри loadScores(), здесь мы указываем в "w"качестве второго параметра. Это говорит Corona для создания (записи) новый файл, или перезаписать файл , если он уже существует.

После того как файл успешно открыт, мы называем file:write()записать scoresTableданные в файл, преобразуется в формат JSON с помощью json.encode()команды. Наконец, мы закрываем файл с io.close( file ).

 

Показано лучшие результаты

Перед тем , как показывают оценки к игроку, мы должны манипулировать scoresTableслегка таблицу. В частности, нам нужно добавить последнюю оценку к столу , а затем отсортировать записи в таблице от самого высокого до самого низкого. Все работы на этой сцене может быть сделано в пределах scene:create(), так что давайте сосредоточим наше внимание на эту функцию:

function scene:create( event )

    local sceneGroup = self.view
    -- Code here runs when the scene is first created but has not yet appeared on screen

    -- Load the previous scores
    loadScores()

    -- Insert the saved score from the last game into the table, then reset it
    table.insert( scoresTable, composer.getVariable( "finalScore" ) )
    composer.setVariable( "finalScore", 0 )

    -- Sort the table entries from highest to lowest
    local function compare( a, b )
        return a > b
    end
    table.sort( scoresTable, compare )

    -- Save the scores
    saveScores()

    local background = display.newImageRect( sceneGroup, "background.png", 800, 1400 )
    background.x = display.contentCenterX
    background.y = display.contentCenterY

    local highScoresHeader = display.newText( sceneGroup, "High Scores", display.contentCenterX, 100, native.systemFont, 44 )

    for i = 1, 10 do
        if ( scoresTable[i] ) then
            local yPos = 150 + ( i * 56 )

            local rankNum = display.newText( sceneGroup, i .. ")", display.contentCenterX-50, yPos, native.systemFont, 36 )
            rankNum:setFillColor( 0.8 )
            rankNum.anchorX = 1

            local thisScore = display.newText( sceneGroup, scoresTable[i], display.contentCenterX-30, yPos, native.systemFont, 36 )
            thisScore.anchorX = 0
        end
    end

    local menuButton = display.newText( sceneGroup, "Menu", display.contentCenterX, 810, native.systemFont, 44 )
    menuButton:setFillColor( 0.75, 0.78, 1 )
    menuButton:addEventListener( "tap", gotoMenu )
end

Там очень много происходит в этом коде, так что давайте пошагово каждого аспекта:

  1. Первая новая команда просто вызывает нашу loadScores()функцию , чтобы загрузить предыдущие оценки.
  2. Далее мы вводим самый последний счет игрока в scoresTableтаблице. Обратите внимание на то, что мы получаем его значение путем вызова composer.getVariable()с единственным параметром "finalScore". Это демонстрирует мощь composer.setVariable()команды, которую мы добавили к game.lua, для установки Composer доступную переменную , которая теперь мы можем получить доступ в этой сцене ( highscores.lua). После этого мы сразу же сбросить его значение , 0так как мы не будем нуждаться в дальнейшем данные о нем.
  3. На данный момент, мы не знаем , где наша оценка входит в число существующих высоких баллов. Он не может даже побить самый низкий балл среди десяти максимально высокий! Чтобы проверить это, мы отсортироватьзначения таблицы от самого высокого до самого низкого. Это автоматически разместит наш счет в правильном порядке. Если это не достаточно высоко , чтобы сделать первую десятку, он будет оставаться в 11 - слот, но так как мы будем показывать только десять баллов, вы не увидите его на экране.Для сортировки таблицы, мы используем Lua table.sort()команду. Чтобы это работало, необходимо предоставить его с таблицей для сортировки и ссылки на функцию сравнения ( compare()) , который определяет , если элементы нужно поменять местами. Здесь мы закодировал compare()функцию непосредственно внутри , scene:create()поскольку он не должен быть доступен в других странах.compare()Сама функция принимает два значения , которые table.sort()предоставляет. Так как мы рассортировать таблицу чисел, два параметра , aи bбудут численные значения. Функция сравнивает эти два значения ,как в "это aбольше , чем b?" Если это правда, то она возвращает trueи table.sort()знает , что ему нужно поменять местами значения. По существу, когда table.sort()заканчивается процесс, нашиscoresTableзначения будут отсортированы от самого высокого до самого низкого.
  4. С помощью таблицы теперь сортируются, мы сохраняем данные обратно в scores.jsonпозвонив по телефону saveScores()функцию.
  5. Вслед за этим, мы не создадим фон сцены и текстовый объект - ничего особенного здесь; теперь вы являетесь экспертом в этом!
  6. Далее мы выводим баллы с помощью простого forцикла от 1до 10. Для каждого счета, мы сначала установить предполагаемую у позиции путем вычисления локальной yPosпеременной. Это заставит баллы бежать вниз по экрану, равномерно расположенных друг от друга.

Для каждой линии надреза, мы будем отображать два текстовых объекта. Слева будет ранг число от 1) до 10) . Непосредственно справа будет фактическая оценка. Большая часть этого кода должен быть простым в этой точке, но мы вводим новое важное понятие в якорей . По умолчанию Corona позиционирует центр любого объекта отображения на х и у координаты даны. Тем не менее, иногда вам нужно выровнять ряд объектов по краям - вот, список баллов будет выглядеть лучше , если каждый номер ранга выравнивается по правому краю , и каждый балл выравнивается по левому краю .

Для достижения этой цели , мы можем установить anchorXсвойство каждого объекта. Это свойство , как правило , колеблется в пределах 0 (слева) и 1 (справа), значение по умолчанию 0.5 ( в центре). Так как мы хотим , чтобы каждое число ранга быть выровнен по правому краю с другими, мы устанавливаем , anchorXчтобы 1, и для каждого числа баллов, мы устанавливаем , anchorXчтобы 0на выравнивание по левому краю.

Естественно, Corona также поддерживает вертикальные опорные точки с anchorYсобственностью. Подобно своей горизонтальной аналог, это свойство обычно находится в диапазоне между 0 (сверху) и 1 (внизу), значение по умолчанию 0.5 ( в центре). Якоря могут даже быть заданы вне 0в 1диапазоне, хотя это использование реже. Установка либо anchorXили anchorYдо значений меньше 0или больше , чем 1поставит точку привязки концептуально где - то в пространстве за пределами края границы объекта, которые могут быть полезны в некоторых случаях.

  1. Наконец, мы создаем кнопку (текстовый объект) , чтобы вернуться в меню. Это так же , как текстовые кнопки , которые вы создали в сцене меню, и его "tap"событие слушатель будет вызывать основную gotoMenu()функцию , которую мы будем писать дальше.

 

Оставив сцену

Резервное копирование в сюжетном доступной области кода, мы должны включить функцию , которая обрабатывает "tap"событие для menuButtonобъекта. Это достаточно просто:

local function gotoMenu()
    composer.gotoScene( "menu", { time=800, effect="crossFade" } )
end
Действие!

Это завершает наш высокий балл сцены! Сохраните измененные highscores.luaи game.luaфайлы , а затем повторно запустить Simulator. Теперь вы должны быть в состоянии играть в эту игру и увидеть ваши баллы сохраняются / сортируются в высокие баллы сцены.

 

Глава Концепции

Мы рассмотрели еще несколько важных понятий в этой главе:

Команда / Недвижимость Описание
system.pathForFile () Создает абсолютный путь , используя определенные системой каталогов в качестве базы.
io.open () Открывает файл для чтения или записи.
io.close () Закрывает открытый дескриптор файла.
Файл: чтение () Читает файл, в соответствии с заданными форматами, которые определяют, что читать.
файл: написать () Записывает значение каждого из аргументов в файл.
json.decode () Декодируется JSON-закодирован структуру данных и возвращает таблицу Lua с данными.
json.encode () Конвертирует таблицу Lua в JSON-закодирован строки.
composer.setVariable () Устанавливает переменную, объявленную в одной сцене, чтобы быть доступным на протяжении всего приложения Composer.
composer.getVariable () Позволяет получить значение любой переменной набора через composer.setVariable () .
table.sort () Сорта элементы таблицы в заданном порядке.
object.anchorX Имущество , которое позволяет контролировать выравнивание экранного объекта вдоль х направлении.
object.anchorY Имущество , которое позволяет контролировать выравнивание объекта отображения вдоль у направлении.

YoGo
Language