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で動作するようにソースを修正