ユーザ用ツール

サイト用ツール

wiki:defold2:tips:003
startSectionEdit: $data "plugin_wrap_start" is NOT an array! One of your plugins needs an update.
startSectionEdit: $data "plugin_wrap_end" is NOT an array! One of your plugins needs an update.

003 公式チュートリアル War battles で学習する

概要

Defold公式にあるwar-battles でDefoldの基本的な使い方を学習してみます。

Editor の見方

  • Assets ビュー スクリプトやリソースなど、全てのファイルはAssetsビュー以下に配置します。
  • Changed Files ビュー ファイルの更新差分が確認できます。
  • Scene ビュー Outline シーンに配置されたゲームオブジェクトが確認できます。
  • OutLine ビュー Sceneに配置するゲームオブジェクトはこちらに配置します。
  • Properties ビュー Outlineビューで選択しているゲームオブジェクトの詳細を確認できます。

綺麗な空のウインドウの表示

  • main.collection. ファイルを開く
  • outline ビューに配置されているlogo game object を選択して削除します。
  • main/images/logo.png ファイルを削除します。
  • main/images/logo.atlas ファイルを削除します。
  • Project → Build で何も表示されない空のウインドウが起動します。


ゲームマップの表示

  • war-battles のサンプルリソースダウンロード/展開します。
  • war-battles-assets以下のファイルを全てAssets ビューにドラッグ&ドロップで配置します。


  • Assets ビューのmainフォルダを右クリック → New → 「Tile source」を選択。名前は「map.tilesource」にします。
  • map.tilesource のProperties ビューにあるImageにmap.png を指定します。

  • map.tilesourceにmap.pngのテクスチャが設定されました。ile Source のプロパティにはタイルのサイズや余白、コリジョンデータが設定できます。

タイルマップにタイルを配置する

  • Assets ビューのmainフォルダを右クリック → New → 「Tile Map」を選択。名前は「map.tilemap」にします。
  • 「map.tilemap」プロパティのTile Source に先ほど作成したmap.tilesource を設定します。
  • Outlineのlayer1を選択した状態で、メニューの「Scene」→ Tile Map → Show Pallete を選択すると、タイルパレットが表示されます。

タイルパレットから配置したい地面を選択し、タイルマップに塗りマップを作成します。 タイルマップ上で、Shift + マウスドラッグすることでタイルマップの範囲コピーが可能なので、よく似たマップは便利な機能を使ってサクサク作りましょう。

完成したマップ。配置は自由。


GameObject の追加

  • 「main.collection」を開いて、Outline ビューで右クリック → Add Game Object から空のGameObjectを追加します。
  • 空のGameObjectを右クリック → Add Component File から map.tilemap を追加します。
  • この状態でメニューバーのProject ー> Build からアプリをビルドするとウインドウが立ち上がり、編集結果を確認することが出来ます。




  • また、ウインドウサイズはAssets → game.project を開いたdisplay のwidth とheight から切り替えることが出来ます。


キャラクターのアニメーションAtlasの作成

  • Assets ビューのmainフォルダを右クリック→ New Folder からAtlasフォルダを作成します。
  • Atlasフォルダを右クリック ー> New → Atlas からアトラスを作成。名前はplayerとします。

からっぽのAtlasが作成されたので、中身を埋めていきます。

Spriteアニメーショングループの作成

  • player.atlas を開いて、OutLine ー> Atlas を右クリック → AddAnimation Group を作成する。名前は「player-down」。
  • そこにAdd Images を選択。AtlasにまとめるImageを選択します。
  • 複数選択はShiftを押しながら選択すると出来ます。
  • 今回はInfantry/down/1〜4を選択しました。
  • アニメーションのプレビューは、Animation Groupを選択した状態でSpaceキーを押下するか、メニューバーの Scene → Play から実行出来ます。



プレイヤーキャラの配置

  1. main.collection.を開く
  2. 右クリック → Add GameObject でGameObjectを追加。名前をplayerに変更。
  3. player Gameobject を右クリック → Add Component → Sprite コンポーネントを追加。
  4. Sprite コンポーネントのImageに player.atlas を設定
  5. Default Animation に player-down を選択。
  6. アニメーション切り替え速度はFPSを調整することで可能。8を設定しました。

これで、シーンにプレイヤーが配置されました。

プレイヤーキャラがマップより後ろの表示されてしまう場合

  • player GameObjectのz座標を1.0に設定すると、マップより手前に表示されます。Z座標で表示優先度が決定する。




プレイヤーキャラの移動

  • Assets ビューの /input/game.input_binding ファイルを開く。
  • Input Buildings のKey Triggerに矢印キーの上下左右のキー入力を追加し、Action名「up」「down」「left」「right」を割り当てます。




プレイヤーキャラの移動スクリプトの実装

  • Assts → main → scripts → New… → Script を追加。名前は「player.script」に設定。
  • Scriptには雛形となるコードが記述されているので、これを改造して実装する。
function init(self)
	msg.post(".",  "acquire_input_focus")
	
    self.moving = false     
    self.input  = vmath.vector3() 
    self.dir    = vmath.vector3(0, 1, 0) 
    self.speed  = 50    
end

function final(self)
	msg.post(".",  "release_input_focus")
end

function update(self, dt)
    if self.moving then
        local pos = go.get_position() 
        pos = pos + self.dir * self.speed * dt 
        go.set_position(pos) 
    end
    
    self.input.x = 0 
    self.input.y = 0
    self.moving = false	
end

function on_message(self, message_id, message, sender)
end

function on_input(self, action_id, action)
    if action_id == hash("up") then
        self.input.y = 1
    elseif action_id == hash("down") then
        self.input.y = -1
    elseif action_id == hash("left") then
        self.input.x = -1
    elseif action_id == hash("right") then
        self.input.x = 1
    end
        
    if vmath.length(self.input) > 0 then
        self.moving = true 
        self.dir = vmath.normalize(self.input) 
    end	
end

function on_reload(self)
end
  • init で変数の初期化。on_input で入力処理を、update で移動処理を実装している。
  • このスクリプトをplayerのGameObjectにAdd Component File から追加します。
  • ビルドして実行確認すると上下左右のキー入力でプレイヤーキャラの移動が確認できます。




ロケットの実装

  1. player.atlas にAnimation Group を追加。名前は「rocket」
  2. rocket を右クリック Add Images から turret rocket/1〜3を追加します。FPSは適度に調節。20にしました。
  3. Flip Horizontal にチェックを入れます。ロケットの向きを調整します。これにチェックが入っていないとロケットの上下が逆さまに表示されてしまいます。
    1. このロケットは最初からマップに表示される訳ではないので配置はしません。
  4. player を右クリック → Add Component → Factory から生成用コンポーネントを追加。名前はrocketfactoryとします。
  5. Assets/main/ にGameObjectを作成。 名前はrocket.goとします。
  6. rocketfactory のprototypeにrocket.goファイルを設定します。
  • /input/game.input_binding. を開いて、space キーを押下した時に”fire” actionが実行される様に設定します。
  • player.scriptにスクリプトを記述します。
function init(self)
	msg.post(".",  "acquire_input_focus")
	
    self.moving = false
    self.firing = false	-- add
    self.input  = vmath.vector3() 
    self.dir    = vmath.vector3(0, 1, 0) 
    self.speed  = 50    
end

function update(self, dt)
    if self.moving then
        local pos = go.get_position() 
        pos = pos + self.dir * self.speed * dt 
        go.set_position(pos) 
    end

    -- add    
    if self.firing then
        local angle = math.atan2(self.dir.y, self.dir.x) 
        local rot = vmath.quat_rotation_z(angle) 
        local props = { dir = self.dir } 
        factory.create("#rocketfactory", nil, rot, props)
    end    
    
    self.input.x = 0 
    self.input.y = 0
    self.moving = false	
    self.firing = false -- add
end

function on_input(self, action_id, action)
    if action_id == hash("up") then
        self.input.y = 1
    elseif action_id == hash("down") then
        self.input.y = -1
    elseif action_id == hash("left") then
        self.input.x = -1
    elseif action_id == hash("right") then
        self.input.x = 1
    -- add
    elseif action_id == hash("fire") and action.pressed then
        self.firing = true
    end
        
    if vmath.length(self.input) > 0 then
        self.moving = true 
        self.dir = vmath.normalize(self.input) 
    end	
end	

add と書かれた部分のコードを追加します。 fire キーが押された時に、rocketfactory を生成するスクリプトになっています。


ロケット用スクリプトの作成

  • scripts 右クリック → New → Script からスクリプトを追加。名前は rocket.script としました。
go.property("dir", vmath.vector3()) 

function init(self)
	self.speed = 200
end

function final(self)
end

function update(self, dt)
    local pos = go.get_position() 
    pos = pos + self.dir * self.speed * dt 
    go.set_position(pos)	
end

function on_message(self, message_id, message, sender)
end

function on_input(self, action_id, action)
end

function on_reload(self)
end

dir プロパティを追加。initでロケットの速度を初期化し、updateで移動処理を実装しています。

ロケット用ゲームオブジェクトの中身

  • Add Component File から rocket.scriot を追加。
  • Add Component ー> Sprite を追加し、player.atlas を設定。Default Animationはrocket にします。




爆発エフェクトの追加

  • player.atlas を開いて Animation Groupを追加。名前は“explosion”とします。
  • FPSは16を設定しました。explosionを選択した状態でスペースキーで動きを確認。
  • playback は爆発は一度だけ再生して終わりたいので「Once Forward」に変更します。

ここまで出来ていればSpriteアニメーションの作り方は覚えて来たかなと思います。


爆発エフェクトのスクリプトを記述

  • rocket.script を編集します。
function init(self)
	self.speed = 200
	self.life = 1 -- 追加。
end

function update(self, dt)
    local pos = go.get_position()
    pos = pos + self.dir * self.speed * dt
    go.set_position(pos)

    self.life = self.life - dt
    
    -- 追加
    if self.life < 0 then
        self.life = 1000
        go.set_rotation(vmath.quat())
        self.speed = 0
        msg.post("#sprite", "play_animation", { id = hash("explosion") })
    end
end

実行するとロケットが飛んで1秒後に爆発エフェクトが再生されます。 しかしこのままでは爆発エフェクトのゴミが残ってしまいます。エフェクトのゲームオブジェクトが削除される様に修正します。

function on_message(self, message_id, message, sender)
    if message_id == hash("animation_done") then
        go.delete()
    end
end

onmessage にスクリプトを追加します。hash値でチェックしている“animationdone” は“playanimation”が呼ばれたたあと送られてくるゲームエンジンのに内包されているメッセージです。この場合、“playanimation”でのSpriteアニメーションの再生が完了時に“animation_done”が呼ばれる形になっています。その時go.delete()関数でゲームオブジェクトを削除します。こうすることで、爆発アニメーションが終了したあと、rocketゲームオブジェクトが削除されます。


敵となる戦車の作成

  1. Assts にtank.go ゲームオブジェクトを作成します。
  2. player.atlasを開いて新たにAnimationGroupを作成します。名前はtank-downとします。
  3. Add Imageからimageを選択します。「tank/down/1.png」「tank/down/2.png」です。
  4. FPSは8を設定しました。
  5. tank.goを開いてAdd Component ー> Sprite を追加。Atlasを選択してDefault Animationを「tank-down」としました。
  6. main.collectionを開いて戦車を配置します。
  7. Outlineを右クリック Add GameObject File.よりtank.goを選択します。フィールドの適当な位置に配置します。
  8. Z座標は1.0に設定して、フィールドとなる地面より手前に表示される様にします。
  9. 3体くらい配置してみましょう。[Cmd(Ctrl) + C]→ [Cmd(Ctrl) + V]複製できます。




ゲームオブジェクトに当たり判定をつける

戦車に当たり判定をつける

  1. tank.go を右クリック → Add Component → Collision Object を追加。
  2. Type は“Kinematic”。
  3. Group を“tanks” に設定。
  4. Mask を“rockets” に設定。
  5. “collisionobject” を右クリック → Add Shape → Box の当たり判定を付けます。
  6. Boxのサイズを戦車の見た目に合わせます。 -




ロケットに当たり判定をつける

  1. rocket.go を右クリック → Add Component → Collision Object を追加。
  2. Type は“Kinematic”。
  3. Group を“rockets” に設定。
  4. Mask を “tanks”に設定。
  5. “collisionobject” を右クリック → Add Shape → Box の当たり判定を付けます。
  6. Boxのサイズをロケットの見た目に合わせます。




当たり判定に対するリアクションの実装

  1. テスト。
  2. rocket.scriptを編集します。

– 関数化。爆発アニメーションの再生。

local function explode(self)
    self.life = 1000
    go.set_rotation(vmath.quat())
    self.speed = 0
    msg.post("#sprite", "play_animation", { id = hash("explosion") })
end

function update(self, dt)
    local pos = go.get_position()
    pos = pos + self.dir * self.speed * dt
    go.set_position(pos)

    self.life = self.life - dt

    if self.life < 0 then
        explode(self)
    end
end

function on_message(self, message_id, message, sender)
    if message_id == hash("animation_done") then
        go.delete()
    -- Add
    elseif message_id == hash("collision_response") then
        explode(self)
        go.delete(message.other_id)
    end
end

爆発アニメーションをexplode 関数にまとめました。onmessage 関数で“collisionresponse”が送られてきた時に、explode で爆発アニメーションを再生し、対象のGameObjectを削除しています。実行するとロケットが戦車に当たると爆発アニメーションと共に消える様になっています。


UIの実装

スコアの表示や、クリア用のUIを作ります。

  1. Assets/mainフォルダを右クリック → New → Font からフォントを追加。名前は[text.font]とします。
  2. text.font を開いて[04font.ttf]を選択します。
  3. Assets/mainフォルダを右クリック → New → Guiを選択。名前は[ui.Gui]とします。
  4. ui.Guiを開いてOutLineからFonts を選択し右クリック → Add ー> text.font を選択します。
  5. Nodesを選択し右クリック → Add → Text を追加。名前は[score]とします。テキスト内容は“SCORE: 0”を設定しておきます。
  6. Textを画面の左上に配置しておきます。



  1. Assets/mainフォルダを右クリック → New → Gui Scriptを追加。名前は[ui.guiscript] - ui.gui ファイルを開いて、Outlineの“Guiを選択した状態で表示されるプロパティのScriptに先程作成した[ui.guiscript]を設定します。
  2. main.collection.を開いて、Outlineから右クリック → Add Game Object ー> ゲームオブジェクトの名前は”gui“とします。
  3. “gui”ゲームオブジェクトを右クリック → Add Component File から”ui.gui“を選択します。

画面にスコアが表示されました。次はスコアを加算していくためのスクリプトを書いていきます。

スコアの更新

  1. ui.gui_scriptを開きます。
function init(self)
    -- score の変数を定義し0で初期化します。
    self.score = 0
end

function on_message(self, message_id, message, sender)
	
    -- "add_score"メッセージが返された時に
    if message_id == hash("add_score") then 
        -- scoreを加算する。
        self.score = self.score + message.score 
        -- score ノードを取得して、テキストを更新します。
        local scorenode = gui.get_node("score") 
        gui.set_text(scorenode, "SCORE: " .. self.score) 
    end 	
end
  • addscore メッセージを発火するためのスクリプトをrocket.guiscriptに書きます。 <code> function onmessage(self, messageid, message, sender) if messageid == hash(“animationdone”) then go.delete()
    1. - Add

    elseif messageid == hash(“collisionresponse”) then explode(self) go.delete(message.otherid) – スコアを100点追加。 msg.post(”/gui#ui“, “addscore”, {score = 100})
    end end </code> これでロケットで戦車を倒すとスコアが100点加算される様になります。

終わり

  1. 公式チュートリアルはここで終了しています。
  2. なんどもトライ&エラーを繰り返してコツを掴むことが大切です。
Permalink wiki/defold2/tips/003.txt · 最終更新: 2018/04/04 22:56 by step

oeffentlich