山水堂
A010.マップスクロール基本
マップの表示、及び画面内に入りきらないマップのスクロール処理の基本です。
http://dxlib.o.oo7.jp/dxprogram.html#N4
DXライブラリを参考にしました!
-- conf.lua function love.conf(t) t.window.width = 640 t.window.height = 480 end
640x480で設計していますので、コンフィグファイルを設定しておきましょう。
-- main.lua -- 基本情報 local MAP_SIZE = 64 -- マップのセルサイズ local MAP_WIDTH = 20 -- マップ幅 local MAP_HEIGHT = 16 -- マップ高 local MOVE_INTERVAL = 0.4 -- 移動にかかる秒数(キーボード入力間隔) -- マップデータ local map_data = { {0, 0, 0, 0, 0, 0, 0, 0, 0, 0 , 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 1, 1, 0, 0, 0, 0, 0, 0, 0 , 1, 1, 1, 1, 1, 1, 1, 1, 1, 0}, {0, 0, 1, 1, 1, 0, 0, 0, 0, 0 , 1, 0, 0, 0, 0, 0, 0, 0, 1, 0}, {0, 0, 1, 0, 1, 0, 0, 0, 0, 0 , 1, 1, 1, 1, 1, 1, 0, 0, 1, 0}, {0, 1, 1, 0, 1, 0, 0, 1, 1, 0 , 0, 0, 0, 0, 0, 1, 0, 0, 1, 0}, {0, 1, 0, 0, 1, 0, 0, 1, 1, 0 , 0, 0, 1, 1, 1, 1, 1, 0, 1, 0}, {0, 1, 0, 0, 1, 0, 0, 1, 1, 0 , 0, 0, 1, 0, 0, 1, 1, 0, 1, 0}, {0, 1, 0, 0, 1, 0, 0, 1, 1, 0 , 0, 0, 1, 0, 0, 1, 1, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 0 , 0, 1, 1, 0, 0, 1, 1, 0, 1, 0}, {0, 0, 0, 0, 1, 0, 0, 1, 0, 0 , 0, 1, 1, 0, 0, 1, 1, 0, 1, 0}, {0, 0, 0, 0, 1, 0, 0, 1, 0, 0 , 0, 1, 1, 0, 0, 1, 1, 0, 1, 0}, {0, 0, 0, 1, 1, 0, 0, 1, 0, 0 , 0, 1, 1, 0, 0, 1, 1, 0, 1, 0}, {0, 1, 1, 1, 1, 0, 0, 1, 1, 1 , 1, 1, 1, 0, 0, 1, 1, 0, 1, 0}, {0, 1, 1, 1, 1, 0, 0, 0, 0, 0 , 0, 0, 0, 0, 0, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 0, 0, 0, 0, 0 , 0, 0, 0, 0, 0, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0 , 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, } -- プレイヤーの位置 local player_x = 2 local player_y = 2 -- 移動関連 local move_x = nil local move_y = nil local move_dt = 0 local keystate = {} function love.keypressed(key, isrepeat) -- キーボードが押された! keystate[key] = true -- エスケープボタンが押されたら終了イベントを起こす if key == "escape" then love.event.quit() end end function love.keyreleased(key) -- キーボードが離された! keystate[key] = false end function love.update(dt) -- 更新処理! local movement_x = 0 local movement_y = 0 -- ①キーボードを解析して移動量を算出 if move_x == nil and move_y == nil then if keystate.left then movement_x = movement_x - 1 end if keystate.right then movement_x = movement_x + 1 end if keystate.up then movement_y = movement_y - 1 end if keystate.down then movement_y = movement_y + 1 end end -- ②移動量から移動先を算出 if movement_x ~= 0 or movement_y ~= 0 then -- 移動させる move_x = player_x + movement_x move_y = player_y + movement_y -- 進入不可能なマップだった場合は移動できない if map_data[move_y][move_x] == 0 then move_x = nil move_y = nil else -- 移動を初期化 move_dt = 0 -- プレイヤーを移動 player_x = move_x player_y = move_y end end -- ③移動後に間隔をあける(キーの長押しに対応) if move_x and move_y then if move_dt >= MOVE_INTERVAL then -- 移動終了なら、値を初期化 move_x = nil move_y = nil else -- 移動インターバル move_dt = move_dt + dt end end end function draw_box( -- DXライブラリ風 left, top, right, bottom, color, fill_flag ) -- なければ、初期セット color = color or {} fill_flag = fill_flag or true -- fillを生成 local fill = "line" if fill_flag then fill = "fill" end -- 色をセット local get_color = { love.graphics.getColor() } love.graphics.setColor(color[1] or 0, color[2] or 0, color[3] or 0, color[4] or 255) -- 描画! love.graphics.polygon( fill, left, top, right, top, right, bottom, left, bottom ) -- 色を復帰 love.graphics.setColor(unpack(get_color)) end function love.draw() -- 描画処理! local num_x = math.floor(love.graphics.getWidth() / MAP_SIZE) + 1 local num_y = math.floor(love.graphics.getHeight() / MAP_SIZE) + 1 local draw_x = player_x - math.floor(num_x / 2) local draw_y = player_y - math.floor(num_y / 2) -- 地形の描画 for y = 1, num_y do for x = 1, num_x do local pos_x = x + draw_x local pos_y = y + draw_y if pos_x > 0 and pos_x <= MAP_WIDTH and pos_y > 0 and pos_y <= MAP_HEIGHT then if map_data[pos_y][pos_x] == 0 then draw_box( (x - 1) * MAP_SIZE, (y - 1) * MAP_SIZE, x * MAP_SIZE, y * MAP_SIZE, {255, 0, 0}, true ) end end end end -- プレイヤーの描画 draw_box( (player_x - draw_x - 1) * MAP_SIZE, (player_y - draw_y - 1) * MAP_SIZE, (player_x - draw_x) * MAP_SIZE, (player_y - draw_y) * MAP_SIZE, {255, 255, 255}, true ) end
カクカクと移動するバージョンです。
カーソルで白色のプレイヤーが移動します。
ソースがちょっと長めですが、マップ表示基本に移動処理が加わったという点では、それほど難しいところは無いと思います。
プレイヤーの位置の所在から計算し、マップ全体をずらして表示しています。
draw_boxは自作の関数です、DXライブラリの引数のままサンプルで作ってみました。
-- main.lua -- 基本情報 local MAP_SIZE = 64 -- マップのセルサイズ local MAP_WIDTH = 20 -- マップ幅 local MAP_HEIGHT = 16 -- マップ高 local MOVE_INTERVAL = 0.4 -- 移動にかかる秒数(キーボード入力間隔) -- マップデータ local map_data = { {0, 0, 0, 0, 0, 0, 0, 0, 0, 0 , 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 1, 1, 0, 0, 0, 0, 0, 0, 0 , 1, 1, 1, 1, 1, 1, 1, 1, 1, 0}, {0, 0, 1, 1, 1, 0, 0, 0, 0, 0 , 1, 0, 0, 0, 0, 0, 0, 0, 1, 0}, {0, 0, 1, 0, 1, 0, 0, 0, 0, 0 , 1, 1, 1, 1, 1, 1, 0, 0, 1, 0}, {0, 1, 1, 0, 1, 0, 0, 1, 1, 0 , 0, 0, 0, 0, 0, 1, 0, 0, 1, 0}, {0, 1, 0, 0, 1, 0, 0, 1, 1, 0 , 0, 0, 1, 1, 1, 1, 1, 0, 1, 0}, {0, 1, 0, 0, 1, 0, 0, 1, 1, 0 , 0, 0, 1, 0, 0, 1, 1, 0, 1, 0}, {0, 1, 0, 0, 1, 0, 0, 1, 1, 0 , 0, 0, 1, 0, 0, 1, 1, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 0 , 0, 1, 1, 0, 0, 1, 1, 0, 1, 0}, {0, 0, 0, 0, 1, 0, 0, 1, 0, 0 , 0, 1, 1, 0, 0, 1, 1, 0, 1, 0}, {0, 0, 0, 0, 1, 0, 0, 1, 0, 0 , 0, 1, 1, 0, 0, 1, 1, 0, 1, 0}, {0, 0, 0, 1, 1, 0, 0, 1, 0, 0 , 0, 1, 1, 0, 0, 1, 1, 0, 1, 0}, {0, 1, 1, 1, 1, 0, 0, 1, 1, 1 , 1, 1, 1, 0, 0, 1, 1, 0, 1, 0}, {0, 1, 1, 1, 1, 0, 0, 0, 0, 0 , 0, 0, 0, 0, 0, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 0, 0, 0, 0, 0 , 0, 0, 0, 0, 0, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0 , 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, } -- プレイヤーの位置 local player_x = 2 local player_y = 2 -- 移動関連 local move_x = nil local move_y = nil local move_dt = 0 local scroll_x = 0 local scroll_y = 0 local keystate = {} function love.keypressed(key, isrepeat) -- キーボードが押された! keystate[key] = true -- エスケープボタンが押されたら終了イベントを起こす if key == "escape" then love.event.quit() end end function love.keyreleased(key) -- キーボードが離された! keystate[key] = false end function love.update(dt) -- 更新処理! local movement_x = 0 local movement_y = 0 -- ①キーボードを解析して移動量を算出 if move_x == nil and move_y == nil then if keystate.left then movement_x = movement_x - 1 end if keystate.right then movement_x = movement_x + 1 end if keystate.up then movement_y = movement_y - 1 end if keystate.down then movement_y = movement_y + 1 end end -- ②移動量から移動先を算出 if movement_x ~= 0 or movement_y ~= 0 then -- 移動させる move_x = player_x + movement_x move_y = player_y + movement_y -- 進入不可能なマップだった場合は移動できない if map_data[move_y][move_x] == 0 then move_x = nil move_y = nil else -- 移動を初期化 move_dt = 0 end end -- ③移動先からプレイヤー位置を算出 if move_x and move_y then if move_dt >= MOVE_INTERVAL then -- 移動終了なら、プレイヤー位置を移動して、値を初期化 player_x = move_x player_y = move_y scroll_x = 0 scroll_y = 0 move_x = nil move_y = nil else -- 移動中なら、スムーズに move_dt = move_dt + dt scroll_x = (player_x - move_x) * math.floor(move_dt / MOVE_INTERVAL * MAP_SIZE) scroll_y = (player_y - move_y) * math.floor(move_dt / MOVE_INTERVAL * MAP_SIZE) end end end function draw_box( -- DXライブラリ風 left, top, right, bottom, color, fill_flag ) -- なければ、初期セット color = color or {} fill_flag = fill_flag or true -- fillを生成 local fill = "line" if fill_flag then fill = "fill" end -- 色をセット local get_color = { love.graphics.getColor() } love.graphics.setColor(color[1] or 0, color[2] or 0, color[3] or 0, color[4] or 255) -- 描画! love.graphics.polygon( fill, left, top, right, top, right, bottom, left, bottom ) -- 色を復帰 love.graphics.setColor(unpack(get_color)) end function love.draw() -- 描画処理! local num_x = math.floor(love.graphics.getWidth() / MAP_SIZE) + 1 local num_y = math.floor(love.graphics.getHeight() / MAP_SIZE) + 1 local draw_x = player_x - math.floor(num_x / 2) local draw_y = player_y - math.floor(num_y / 2) -- 地形の描画 for y = 0, num_y + 1 do for x = 0, num_x + 1 do local pos_x = x + draw_x local pos_y = y + draw_y if pos_x > 0 and pos_x <= MAP_WIDTH and pos_y > 0 and pos_y <= MAP_HEIGHT then if map_data[pos_y][pos_x] == 0 then draw_box( (x - 1) * MAP_SIZE + scroll_x, (y - 1) * MAP_SIZE + scroll_y, x * MAP_SIZE + scroll_x, y * MAP_SIZE + scroll_y, {255, 0, 0}, true ) end end end end -- プレイヤーの描画 draw_box( (player_x - draw_x - 1) * MAP_SIZE, (player_y - draw_y - 1) * MAP_SIZE, (player_x - draw_x) * MAP_SIZE, (player_y - draw_y) * MAP_SIZE, {255, 255, 255}, true ) end
スムーズに動くバージョンです。
上のソースと間違え探し程度の変更ですが、実行してみると、スムーズに位置移動します。
何かのボタンを押しながら移動すると、MOVE_INTERVALの値が半分……とか工夫すれば、ダッシュの処理も出来そうですね!
とりあえずは以上です、お疲れさまでした!
[2016.08.18] 変更:0.10.1で動作するようにソースを修正