2011/11/23

Corona SDKでのDrag & Drop基本サンプル

新しく取りかかり始めたアプリでタッチ操作によるDrag&Dropを実装しようと思い、以前にclipしておいたこのページのDrag&Dropのサンプルコードを動かしてみました。
-- create object
local myObject = display.newRect( 0, 0, 100, 100 )
myObject:setFillColor( 255 )

-- touch listener function
function myObject:touch( event )
  if event.phase == "began" then
    self.markX = self.x    -- store x location of object
    self.markY = self.y    -- store y location of object
  elseif event.phase == "moved" then
    local x = (event.x - event.xStart) + self.markX
    local y = (event.y - event.yStart) + self.markY
    self.x, self.y = x, y    -- move object based on calculations above
  end
  return true
end

-- make 'myObject' listen for touch events
myObject:addEventListener( "touch", myObject )
実際に動かしてみるとわかるのですが、タッチ操作(指の動き)にオブジェクトが追従してきません。最初はfpsが低いのかなとも思ったのですが、60fpsに設定しても変わりませんでした。

ソース的には何も間違っていないような印象があったのですが、別のサンプルコードを見つけて納得しました。上記のコードでは指の動きが少しでも早くなると、オブジェクトに対するイベントがすぐに外れてしまうため、操作に対する追従が悪かったのです。

実践的というか使えるコードとしては以下の様になります。
-- create object
local myObject = display.newRect( 0, 0, 100, 100 )
myObject:setFillColor( 255 )

-- touch listener function
function myObject:touch( event )
  local t = event.target
  local phase = event.phase
  if("began" == phase) then
    display.getCurrentStage():setFocus(t)
    t.isFocus = true
    t.x0 = event.x - t.x
    t.y0 = event.y - t.y
  elseif(t.isFocus) then
    if("moved" == phase) then
      t.x = event.x - t.x0
      t.y = event.y - t.y0
    elseif("ended" == phase or "cancelled" == phase) then
      display.getCurrentStage():setFocus(nil)
      t.isFocus = false
    end
  end
  return true
end

-- make 'myObject' listen for touch events
myObject:addEventListener( "touch", myObject )
オブジェクトに対する操作(イベント)が終わるまでフォーカスが外れないようになっています。
これで指をグリグリ動かしてもオブジェクトが追従してくれるようになりました。

せっかくタッチパネル向けにアプリをつくるなら、ボタンをタップするだけじゃなくて、指で操作させたい(したい)ですよね。

2011/09/17

Director ClassとAndroidのKEYイベント[Corona SDK]

Corona SDKでAndroidのKEYイベントを取得し、Backキーでひとつ前の画面に戻ったり、Menuボタンで設定画面に遷移したりといった処理を行おうとした際に、Director Classを使っている場合には、ちょっとした注意が必要です。(もちろん私もハマりましたよw)

先日リリースした「Cutie Horoscope」でもAndroidではキー操作によって“戻る”と“メニュー画面を表示”を行っています。
※実際の動きは、アプリをDLして確認ください。

最初はアンスカのAPI解説ページを見て、取得したKEYイベントにあわせてdirector:changeScene()で画面遷移できるんだろうと思って、そのようなコードを書いてみたのですが、画面遷移のアニメーションが効かないという状況に。。。

あれ?

この時すでにライセンスを購入済みだったのでForumを検索してみると、あっ、あった!
「Android device back button utilizing Director class」というタイトルのトピックが立てられていました。

あー、色々と小細工的な処理が必要なようです。
※Forum見られる方はそちらへ。サンプルのソースが載ってます

下記に「Cutie Horoscope」のソースを抜粋して解説します。





new = function ( params )
  
  --(略)--
  
  local localGroup = display.newGroup()
  
  --ポイント1--
  local backButtonPushed = false
  local menuButtonPushed = false
  
  --(略)--
  
  --ポイント2--
  local animate = function( event )
    if backButtonPushed == true then
      backButtonPushed = false
      os.exit()
    elseif menuButtonPushed == true then
      menuButtonPushed = false
      director:changeScene( "setting", "overFromBottom" )
    end
  end

  local onKeyEvent = function( event )
    local phase = event.phase
    local keyName = event.keyName
    
    if phase == "up" and (keyName == "back") then
      backButtonPushed = true
    elseif phase == "up" and (keyName == "menu") then
      menuButtonPushed = true
    end
    
    return true
  end
  
  --(略)--
  
  --ポイント3--
  function clean ( event )
    Runtime:removeEventListener( "key", onKeyEvent )
    Runtime:removeEventListener( "enterFrame", animate )
    backButtonPushed = nil
    menuButtonPushed = nil
  end
  
  local initVars = function ()
    
    --(略)--
    
    --ポイント2--
    Runtime:addEventListener( "key", onKeyEvent )
    Runtime:addEventListener( "enterFrame", animate )
    
    --(略)--
    
  end
  
  initVars()
  
  return localGroup
  
end

ポイントは3つ
1.ボタンが押されたことを判別するフラグを用意する
2.enterFrameイベントでそのフラグを判定して遷移等の処理を行う。KEYイベントはどのキーが押されたか、フラグを書き換えるだけ
3.cleanメソッドでイベントリスナーとフラグの解除を行う

上記の様にしないと、遷移時のエフェクトが掛かりません。そして、イベントの解除等をしないと、最初に設定したイベントが有効のままになってしまうようで、Backキーを押した時に戻る画面が違って何故?ってことになります。

動かし方がわかれば、後はキーと遷移時のエフェクトの組み合わせをうまくやれば、ネイティブ・アプリ(Javaで開発したアプリ)のような動作が可能になります。

2011/09/16

Corona SDKでアプリをリリースする際に起こった「えっ!?」ってこと

Corona SDKでのアプリ開発では、色々と問題も起きます。今回はちょっとキビシイ現実を書きます。

下記は「Cutie Horoscope」のbuild.settingsファイルの中身です。

settings =
{
  iphone =
  {
    plist =
    {
      MinimumOSVersion="4.0",
    },
  },
  android =
  {
    versionCode="2",
    versionName="1.1",
    installLocation="preferExternal",
  },
}

まず、iOS向けに「MinimumOSVersion="4.0"」が設定してあります。
これはiTunes Connectにファイルを送信し終えた時に、たまたまBinary Detailsをクリックすると、なんということでしょう「Minimum OS Requirements」が「3.1」に、そして「Supported Architecture」が「armv6, armv7」になっているではありませんか!
「Coronaは4以上で固定ではなかったのかよ〜」と、あわててファイルを削除し、「MinimumOSVersion="4.0"」を加えることによって回避することにしました。実際に最初にUPした設定のファイルをオーガナイザ経由で第1世代のiPod touch(iOS 3.1.3、armv6)の端末にインストールすることが可能でした。(起動はしますが、表示が崩れるとか使えない状態だったので、気付かなかったら、非常に危ないところでした)
Daily Build版を使用しているので、たまたまデグレが起こったのかも知れませんが、今後「MinimumOSVersion」は必ず設定しようと決めました。

次にAndroid向けに「installLocation="preferExternal"」が設定してあります。
これもおかしな現象なのですが、デバッグ・ビルドにおいては、端末にインストールしたアプリはSDカードに移動可能でしたので、デフォルトでそうゆう設定になっているのだろうと思っていたのですが、いざリリースしてみると、SDカードに移動できません。なんでやねん!ってことで、すぐさま修正版をリリースすることに。
※アプリの動作には関係ないですが、アプリのファイル容量が大きいので必ずやっておきたい設定です

まあこんな感じで、Coronaでの初リリースで色々とトラブル?に見舞われました。もしこのトラブルがだれか他の方に起こり「Corona使えねぇよ!クソがっ!」って評価をされていても、しょうがないと思います。実際半分ぐらい私もそう思っています。
※これは評価されるタイミングに用意できてない方が悪いので

あと「versionName="1.1"」ってなってますが、マーケットでの表示は「1.0」のママです。中の人はあまり細かいことには関心がないのかも知れません。

今回は否定的な事も含め、こうゆう現実があることも知っていて欲しいと思い、起こったことを書きました。
しかしながら、Corona SDKが私にとって作りたいと思ったアプリを一番作りやすかった開発環境であることには変わりませんので、この状態が続く限り、Coronaを使っていると思います。

この記事が、同じように躓いたりハマったりすることがあった方に、少しでも役に立てたら嬉しいです。

※私はアンスカのまわし者ではないですし、正直Coronaを広めたいとか考えていません
※人に「ぜひ使うべき」とか「勧めやすい」とは思っていないという意味です

タブレット向けにアプリ作りたいなぁ。