目次

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

概要

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

Editor の見方

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


ゲームマップの表示


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

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

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


GameObject の追加




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

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

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



プレイヤーキャラの配置

  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を設定しました。

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

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




プレイヤーキャラの移動




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

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




ロケットの実装

  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ファイルを設定します。
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 を生成するスクリプトになっています。


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

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で移動処理を実装しています。

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




爆発エフェクトの追加

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


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

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

終わり

  1. 公式チュートリアルはここで終了しています。
  2. なんどもトライ&エラーを繰り返してコツを掴むことが大切です。