1.はじめに
このプロジェクトは、モバイルアプリケーションでGoogle App Engineを使用する簡単な例です。
サーバー部分は、JSON形式のペンギンのリストを提供します。 モバイルクライアントは、HTTPまたはHTTPSを介してこのリストを要求します。
また、サーバー側はデータベースに特定のイベント、つまり特定のペンギンへの訪問数とボタンを押した回数を記録します。つまり、魚に餌を与え、おなかを掻きます。
各ペンギンには、
Name
、
Bio
およびcounterフィールドの説明フィールドがあります。
2.翻訳の微妙さ
ペンギン保育シミュレータをロシア語に翻訳する方法を考えましたが、「幼稚園」は「保育」や「保育」としても適していません。 したがって、翻訳せずに残った。
3.準備
Google App Engine Go SDKがインストールされていない場合は、 Google App Engineリンクをクリックし、「今すぐ試す」をクリックして、すべてのポイントに従います。 プロジェクトに名前を付け、[Go]を選択し、SDKをダウンロードしてインストールします。 環境変数(
PATH
、
GOROOT
、
GOPATH
、
APPENGINE_DEV_APPSERVER
)が
APPENGINE_DEV_APPSERVER
いることを確認してください。これにより、
goapp
コマンドが表示されます。 今後は、単純なプロジェクトをGAEサーバーにアップロードして実行
goapp deploy
は、プロジェクトディレクトリで
goapp deploy
コマンドを実行する必要があると
goapp deploy
ます。 彼女は、プロジェクトを配置するGoogleアカウントのメールを尋ねます。 プロジェクト名がapp.yamlとサイトで一致することが重要です。 ただし、このプロジェクトでは、モジュールが使用され、ロードプロセスが多少異なります。
GoのIDEとしてLiteIDEを、LuaおよびCorona SDKのZeroBrane Studioをお勧めします。 Corona SDKはWebサイトからダウンロードできます。
4.クリントサーバー
次の図は、クライアント(左)とサーバー(右)の間の非常に複雑な通信スキームを示しています。
ご覧のとおり、クライアントはペンギンのリストのみを要求し、3つのイベントのみを送信します。 通信はHTTP経由ですが、HTTPSは無料で使用できます。 これは、GAEを使用する利点の1つに起因する可能性があります。SSL証明書に支払いをして、それで作業を構成する必要はありません。
すべてがHTTPを介して機能するため、特別なクライアントを使用せずにブラウザーでリクエストを直接実行できます。
penguin-daycare-simulator.appspot.com
モバイルクライアントでは使用されない単純な挨拶ですが、サービスが機能しているかどうかを確認できます。 httpをhttpsに置き換えて、これも機能することを確認できます。
penguin-daycare-simulator.appspot.com/penguins
これは最も重要なリクエストです。 モバイルクライアントは、その助けを借りて、現在監視下にあるすべてのペンギンのリストを受け取ります。
このデータをより便利に表示するには、ChromeのJSONview拡張機能をお勧めします。
penguin-daycare-simulator.appspot.com/stat/visit
penguin-daycare-simulator.appspot.com/stat/fish
penguin-daycare-simulator.appspot.com/stat/bellyrub
これらの3つのクエリは、ペンギンの対応するカウントをインクリメントします。 ペンギン
Id
POSTパラメーターとして渡されます。 サーバーは応答で何も返しませんが、必要に応じて、文字列「OK」または操作の成功の別の信号を応答に追加できます。
5.より多くのスクリーンショット、より多くのスクリーンショット!
記事の公開前に、私はこのペンギンについて思い出しました:
ポジティブを見る
6.サーバー-Google App Engine
これで、コードに直接アクセスできます。 Goプロジェクトのファイル構造を考慮してください。
PenguinDaycareSimulatorServer/ ├── default/ │ ├── app.go │ ├── default.yaml │ └── penguins.json ├── static/ │ ├── favicon.ico │ └── static.yaml └── dispatch.yaml
default
および
static
はモジュールです。 GAEのプロジェクトはモジュールに分割することも、モジュールなしで機能することもできます。 この場合、
app.yaml
ファイルは
app.yaml
、
app/app.go
および
penguins.json
3つだけです。 元々これは私のプロジェクトのケースでした(GitHubで最初のコミットを見ることができます)が、アプリケーションの1つのインスタンスが処理できる同時リクエストの数を担当する
max_concurrent_requests
設定を追加したかったのです。 デフォルト値は10のみです。Goは明らかにそれ以上の機能を備えています。 最大値は500です。負荷が増加してこの値を超えると、アプリケーションの追加のコピーが起動され、それらの間で負荷が分散されます。 GAEの無料のクォータのみに収めたい場合は、この設定を使用することを強くお勧めします。 アプリケーションがこのような負荷に対応していない場合は、この値を減らして有料請求に切り替えます。
したがって、この設定はモジュールでのみ使用できます。 また、GAEがモジュールと見なすには、アプリケーションに少なくとも2つのモジュールが必要です。
static
は非常にシンプルなモジュールであり(上記のGAEの制限がない場合)、そのタスクは
favicon.ico
ファイルを静的に提供することだけです。
default
-すべての作業を行うメインモジュール。
*.yaml
ファイルは設定と説明です。 各モジュールに1つと、どのモジュールがどのURLを処理するかを記述する1つの
dispatch.yaml
ファイル。
dispatch.yaml
application: penguin-daycare-simulator dispatch: - url: "*/favicon.ico" module: static - url: "*/" module: default
static.yaml
application: penguin-daycare-simulator module: static version: 1 runtime: python27 api_version: 1 threadsafe: true handlers: - url: /favicon.ico static_files: favicon.ico upload: favicon.ico
default.yaml
application: penguin-daycare-simulator module: default version: 1 runtime: go api_version: go1 automatic_scaling: max_concurrent_requests: 500 handlers: - url: /.* script: _go_app
static.yaml
ランタイムでは、GoではなくPythonが指定されていることに注意してください。 これは、実際にGoファイルなしでGoにモジュールをロードしようとすると、GAEが誓うからです。 しかし、彼はこの状況でPythonとPHPを誓いません。
オフトピック
ここの注意深い読者は、「PHPは静的ファイルのレンダリングに関してPythonよりも悪い」と主張し、聖戦を解き放とうとしますが、Pythonは個人的に私に近いので、それを選んだのです。 誰でもこの目的でPHPを使用できます。 もちろん、このプロセスにはPythonもPHPも関与していないため、これはすべて無意味です。
default.yaml
handlers
は、特定のURLが処理する実行可能ファイルを示します。 この場合、app.goはすべての着信リクエストを処理します(
dispatch.yaml
を考慮に入れます)。 URLの説明は非常に柔軟で、正規表現を使用します。 ただし、PythonとPHPで異なるファイルを使用して同じモジュール内の異なるURLを処理できる場合、Goでは「_go_app」として指定された単一のファイルである必要があります。 さらに、既にGoプログラム内で、必要に応じて、異なるURLのハンドラーを選択し、アプリケーション全体を複数のファイルに分割できます。
設定とyamlファイルの詳細については、 こちらをご覧ください 。
penguins.json
使用されているすべてのペンギンの名前と説明を含むJSONファイル。
penguins.json
[ {"id": "1", "name": "Tux", "bio": "Beloved Linux mascot" }, {"id": "2", "name": "Skipper", "bio": "Small combat squad leader" }, {"id": "3", "name": "Lolo", "bio": "Russian adventurer" }, {"id": "4", "name": "Gunter", "bio": "The darkest character in Adventure Time" }, {"id": "5", "name": "The Penguin", "bio": "Na, na, na, na, na, na, na, na, na, na... The Penguin! " } ]
このファイルを介してペンギンの追加、編集が行われます。
アプリケーション全体の
app.go
ます。 完全なリストは、GitHub- app.goで見るのに便利です。
このファイルの単純化された構造:
package app . import (...) : Id, , , . type penguin struct {...} () . var penguins []penguin . type penguinEntity struct {...} . func init() {...} penguins.json penguins. func loadPenguinsJson() {...} / - . func rootHandler(w http.ResponseWriter, r *http.Request) {...} /penguins - JSON. func penguinsHandler(w http.ResponseWriter, r *http.Request) {...} /stat/visit - . func visitHandler(w http.ResponseWriter, r *http.Request) {...} /stat/fish - . func fishHandler(w http.ResponseWriter, r *http.Request) {...} /stat/bellyrub - . func bellyrubHandler(w http.ResponseWriter, r *http.Request) {...}
アプリケーションが起動すると、最初にinit()関数が起動されます。この関数は、penguins.jsonファイルから読み取り、クライアントからのさまざまな要求に応答する関数を設定します。 記事の冒頭のリンクで既に使用できます。
penguinsHandler()
は、
json.Marshal()
関数を使用してペンギンスライスをJSON形式に
json.Marshal()
し、
json.Marshal()
を介してクライアントに提供します。
visitHandler()
、
fishHandler()
、
bellyrubHandler()
は同じロジックに従って動作します。データベースからペンギンを取り出し、対応するパラメーターを1つ増やしてデータベースに書き戻します。 データベース- データストア -はSQL互換ではありません。つまり、NoSQLソリューションです。 彼女の作品の説明は別の記事に値します。
データストアへのアクセスなど、GAEの多くの操作は個別に課金されるため、リソースの過剰な使用は避けてください。 そのため、たとえば、すべてのペンギンの統計を要求する場合、関連データを提供することは完全にオプションです。 このリクエストは、たとえば10分のキャッシュ有効期間でキャッシュできます。 これを行うために、追加の変数
lastUpdateTime
を導入しました。これは、
penguins
スライスの最後の更新のタイムスタンプです。 そして、各
/penguins
リクエストで
updatePenguinsStatistics()
関数を呼び出します。この関数は、キャッシュの有効期限が切れているかどうかを確認し、サイクルで
penguins
スライス内の各ペンギンのカウンターを更新します。
手動で更新を強制するために、追加の要求/更新と対応する
updateHandler()
ハンドラーを導入しました。
各要求は独自のゴルーチンで処理されるため、
penguins
スライスを記録中の同時書き込みまたは読み取りから保護する必要があります。 このために、
RWMutex
が使用されます-読み取りまたは書き込み用のミューテックス。 その使用は、単純な
Mutex
よりも効果的です。
リソースの支払済み消費を回避するために、データベースに遅延レコードを入力して、受信したすべてのイベントの値を蓄積することもできます。
プロジェクトをGAEサーバーにアップロードするには、プロジェクトディレクトリのターミナルで3つのコマンドを実行する必要があります。
goapp deploy default/default.yaml goapp deploy static/static.yaml appcfg.py update_dispatch .
将来、app.goを変更するときは、
goapp deploy default/default.yaml
を実行するだけです。
結論として、無料の制限を増やすには、有料の請求書を接続することをお勧めしますが、同時に1日あたりの最大費用を1ドルに設定することをサーバー側についてお話します。 同時に、一部の無料割り当てが増加していますが、まだ何も使用していません。
7.クライアント側-Corona SDK
Corona SDKは、Android、iOS、Windows Phone(近日公開予定)、HTML5(開発中)用のモバイルアプリケーションを開発するためのクロスプラットフォームフレームワークです。 私はこの製品をかなり長い間使用しており、フリーランサーとしてのクライアントと私自身のためにゲームを書いています。 適切な速度とアプリケーションの作成速度に注意してください。
プロジェクトのファイル構造から始めましょう。 主にアイコンと写真のために、ここにさらにファイルがありますので、私はスポイラーの下でそれらをきれいにします。
ファイル構造
PenguinDaycareSimulator/ ├── images/ │ ├── penguins/ │ │ ├── 1.png │ │ ├── 1@2x.png │ │ ├── 2.png │ │ ├── 2@2x.png │ │ ├── 3.png │ │ ├── 3@2x.png │ │ ├── 4.png │ │ ├── 4@2x.png │ │ ├── 5.png │ │ └── 5@2x.png │ ├── background.jpg │ ├── background@2x.jpg │ ├── button-over.png │ ├── button-over@2x.png │ ├── button.png │ ├── button@2x.png │ ├── dot-off.png │ ├── dot-off@2x.png │ ├── dot.png │ ├── dot@2x.png │ ├── fish.png │ ├── fish@2x.png │ ├── hand.png │ ├── hand@2x.png │ ├── popup.png │ └── popup@2x.png ├── lib/ │ ├── api.lua │ ├── app.lua │ └── utils.lua ├── scenes/ │ ├── choose.lua │ ├── menu.lua │ └── penguin.lua ├── Default-568h@2x.png ├── Icon-60.png ├── Icon-60@2x.png ├── Icon-72.png ├── Icon-72@2x.png ├── Icon-76.png ├── Icon-76@2x.png ├── Icon-Small-40.png ├── Icon-Small-40@2x.png ├── Icon-Small-50.png ├── Icon-Small-50@2x.png ├── Icon-Small.png ├── Icon-Small@2x.png ├── Icon-hdpi.png ├── Icon-ldpi.png ├── Icon-mdpi.png ├── Icon-ouya.png ├── Icon-xhdpi.png ├── Icon-xxhdpi.png ├── Icon.png ├── Icon@2x.png ├── build.settings ├── config.lua └── main.lua
今のところ、Luaファイルにのみ注意を払うことができます。
config.lua
、
build.settings
-Corona SDKのプロジェクト設定ファイル。 ポートレートビューまたはランドスケープビューに、アプリケーション、参照画面の解像度、ズーム方法、その他のさまざまな設定があることを示します。 Corona SDKが初めての場合は、今のところこれらのファイルに注意を払うことはできません。
また、ルートにはiOSとAndroidのアイコンが
Default-568h@2x.png
、iPhone 5で正常に動作するために
Default-568h@2x.png
があります。画像/ディレクトリ内には、通常のファイルと2倍のHDバージョン
@2x
ます。 原則として、iPhone 3GSのような画面を持つデバイスはサポートできなくなりました。その割合は非常に小さいですが、それでもゼロではありません。 iPad Retinaを完全にサポートするには、すでに
@4x
ファイルと
config.lua
行が必要ですが、ほとんどのゲームは問題なく動作します。
Corona SDKは
main.lua
ファイルからアプリケーションを起動し、必要なライブラリがそれに接続され、いくつかの変数が宣言され、シーンが「デイケアに入る」ボタンで切り替えられます。 アプリケーションのすべてのシーン(画面)は異なるファイルに保存され、
scenes/
ディレクトリに収集され、すべてのユーザーライブラリを
lib/
に配置しました。 開発者はこれらのファイルを好きなように自由に配置できます。
lib/
app.lua
と
utils.lua
が含まれています-これは一緒にCorona SDKを操作するための便利な関数のコレクションです。
app.lua
、画像、テキスト、ボタンなどを表示するための標準Corona SDK関数の便利なラッパーを実装します。
main.lua
から
scenes/menu.lua
main.lua
への移行
scenes/menu.lua
、ラインを介して行われます
storyboard.gotoScene('scenes.menu')
ここで、ペンギン要求は既にサーバー上で実行されています。
menu.lua
主要なコードを
menu.lua
ます。
function scene:createScene (event) local group = self.view app.newText{g = group, text = 'Penguin Daycare', size = 32, x = _CX, y = _CY - 150} app.newText{g = group, text = 'Simulator', size = 32, x = _CX, y = _CY - 110} local pleaseWait = app.newText{g = group, text = 'Please Wait', size = 16, x = _CX, y = _CY} local button = app.newButton{g = group, x = _CX, y = _CY, text = 'Enter the Daycare', onRelease = function() storyboard.gotoScene('scenes.choose', {effect = 'slideLeft', time = app.duration}) end} button.isVisible = false app.api:getPenguins(function() pleaseWait.isVisible = false button.isVisible = true end) end
3行のテキストと1つのボタンが作成されます。 サーバーから応答を受け取るまで、ボタンは非表示になります。 要求自体は
app.api:getPenguins()
関数によって実行され、引数としてコールバック関数があります。
ボタンをクリックすると、ペンギンの選択シーンが
choose.lua
ます
choose.lua
ファイルのコードの主要部分のみを
choose.lua
ます。
function scene:createScene(event) local group = self.view self.backButton = app.newButton{g = group, x = _L + 10, y = _T + 10, w = 48, h = 32, rp = 'TopLeft', text = 'Back', fontSize = 14, onRelease = function() storyboard.gotoScene('scenes.menu', {effect = 'slideRight', time = app.duration}) end} local function gotoPenguin(ind) storyboard.gotoScene('scenes.penguin', {effect = 'slideLeft', time = app.duration, params = ind}) end local slideView = newSlideView{g = group, x = 0, y = _CY, dots_y = 180, onRelease = gotoPenguin} for i = 1, #app.api.penguins do local p = app.api.penguins[i] local slide = display.newGroup() app.newImage('images/popup.png', {g = slide, w = 300, h = 335}) app.newImage('images/penguins/' .. p.id .. '.png', {g = slide, w = 200, h = 256}) app.newText{g = slide, x = 0, y = -140, text = p.name, size = 18, color = 'white'} app.newText{g = slide, x = 0, y = 140, text = p.bio, size = 14, color = 'white', w = 220, align = 'center'} slideView:addSlide(slide) end slideView:makeDots() slideView:gotoSlide(1) end
ここで、
newSlideView()
は、ペンギンのスライドをスクロールできるシンプルなウィジェットを作成する関数です。 このウィジェットのコードは、ファイルの先頭の
choose.lua
にあります。
ペンギンごとにスライドが作成されます。 ペンギンの画像はアプリケーション内に保存され、ペンギンのIDに対応します。 これは、GAEサーバーなどに画像を保存することで修正できます。 ネットワークから画像をダウンロードするために、Corona SDKには
display.loadRemoteImage()
関数または下位レベルの
network.download()
ます。
スライドをクリックすると、
gotoPenguin()
関数が
gotoPenguin()
、受信したすべてのペンギンの配列(テーブル)内のペンギンの番号(
Id
ではない)を取得します。 この関数は最終的な
penguin.lua
シーンにジャンプし、この引数を同じシーンに渡します。
penguin.lua
function scene:createScene(event) local group = self.view local background = app.newImage('images/background.jpg', {g = group, w = 384, h = 640, x = _CX, y = _CY}) self.backButton = app.newButton{g = group, x = _L + 10, y = _T + 10, w = 48, h = 32, rp = 'TopLeft', text = 'Back', fontSize = 14, onRelease = function() storyboard.gotoScene('scenes.choose', {effect = 'slideRight', time = app.duration}) end} local ind = event.params local p = app.api.penguins[ind] local visitsLabel = app.newText{g = group, x = _CX, y = _T + 20, text = 'Visits: ' .. p.visit_count, size = 18, color = 'white'} local fishLabel = app.newText{g = group, x = _CX, y = _T + 40, text = 'Fish: ' .. p.fish_count, size = 18, color = 'white'} local bellyrubsLabel = app.newText{g = group, x = _CX, y = _T + 60, text = 'Belly rubs: ' .. p.bellyrub_count, size = 18, color = 'white'} local penguin = app.newImage('images/penguins/' .. p.id .. '.png', {g = group, w = 200, h = 256, x = _CX, y = _CY - 25}) app.newButton{g = group, x = _CX - 80, y = _B - 50, w = 128, h = 48, text = 'Fish', fontSize = 14, onRelease = function() local fish = app.newImage('images/fish.png', {g = group, x = penguin.x, y = penguin.y + 200, w = 512, h = 188}) fish.alpha = 0.8 transition.to(fish, {time = 400, alpha = 1, y = penguin.y, xScale = 0.1, yScale = 0.1, transition = easing.outExpo, onComplete = function(obj) transition.to(fish, {time = 400, alpha = 0, onComplete = function(obj) display.remove(obj) end}) end}) app.api:sendFish(p.id) p.fish_count = p.fish_count + 1 fishLabel:setText('Fish: ' .. p.fish_count) end} app.newButton{g = group, x = _CX + 80, y = _B - 50, w = 128, h = 48, text = 'Belly rub', fontSize = 14, onRelease = function() local hand = app.newImage('images/hand.png', {g = group, x = penguin.x - 40, y = penguin.y + 30, w = 80, h = 80, rp = 'TopLeft'}) transition.to(hand, {time = 1200, x = penguin.x + 40, transition = easing.swing3(easing.outQuad), onComplete = function(obj) display.remove(obj) end}) app.api:sendBellyrub(p.id) p.bellyrub_count = p.bellyrub_count + 1 bellyrubsLabel:setText('Belly rubs: ' .. p.bellyrub_count) end} app.api:sendVisit(p.id) p.visit_count = p.visit_count + 1 visitsLabel:setText('Visits: ' .. p.visit_count) end
penguin.lua
では、背景画像が読み込まれ、選択したペンギンの画像が表示され、いくつかのテキストラベルと2つのアクションボタンが表示されます。 それらをクリックすると、アクションがアニメーション化され、リクエストが関数
app.api:sendFish()
および
app.api:sendBellyrub()
介してサーバーに送信されます。 また、
app.api:sendVisit()
は、シーンが作成された直後に呼び出されます。 これらの各関数を呼び出した後、インターネットがない場合でも、対応するテキストラベルが更新されます。 これは、各リクエストに対してサーバーからの応答のチェックを入力し、コールバック関数を提供することで修正できます。
最後に、すべてのサーバー作業は
lib/api.lua
行われ
lib/api.lua
。
api.lua
local _M = {} local app = require('lib.app') _M.hostname = 'http://penguin-daycare-simulator.appspot.com' function _M:getPenguins(callback) local url = '/penguins#' .. math.random(1000, 9999) network.request(self.hostname .. url , 'GET', function (event) if not event.isError then local response = json.decode(event.response) if response then self.penguins = response callback() end end end) end function _M:sendVisit(id) local url = '/stat/visit' local request = {body = 'id=' .. id} network.request(self.hostname .. url , 'POST', function (event) if event.isError then app.alert('Network error') end end, request) end function _M:sendFish(id) local url = '/stat/fish' local request = {body = 'id=' .. id} network.request(self.hostname .. url , 'POST', function (event) if event.isError then app.alert('Network error') end end, request) end function _M:sendBellyrub(id) local url = '/stat/bellyrub' local request = {body = 'id=' .. id} network.request(self.hostname .. url , 'POST', function (event) if event.isError then app.alert('Network error') end end, request) end return _M
ご想像のとおり、サーバーの操作は簡単なPOSTリクエストで行われます。
getPenguins()
場合、サーバーからの応答は
json.decode()
関数によってJSON形式から配列(テーブル)に変換され、モジュールの(プロパティ)フィールドに配置されます。
ご覧のとおり、Corona SDKでのPOSTリクエストの送信とその応答への応答は非常に簡単です。 したがって、Google App Engine自体との統合は非常に簡単になりました。 私は各行が何をするかをペイントしません。構文が直感的であることを望みます。
8.参照
ソースは私のGitHubにあります:
- Google App Engine-github.com/Lerg/PenguinDaycareSimulatorServer
- Corona SDK-github.com/Lerg/PenguinDaycareSimulator
Android 2.3.3以降にクライアントパーツをインストールできます。これはAPK ( ミラー )です。
または、Corona SDKをダウンロードし、GitHubからソースをダウンロードして、Corona Simulatorで実行します。
この記事の執筆を手伝ってくれたM0sTH8に感謝します。
Twitter @SergeyLergでフォローしてください
それだけです ご清聴ありがとうございました!