最近要做一套自动拼接地图,网上看了各种实现方法,主要是rpgmaker和tile map的实现,发现要支持多种地形的话,并不是一件容易的事。主要是不同地形之间都有可能相接,这样就需要出多套过渡素材,比如草地、土地、雪地、水面四种地形,过渡的素材需要考虑到任意组合,当地形增多的时候图量就会成几何倍数增加。
后来我看了http://gad.qq.com/article/detail/16530的实现,它是只支持两种地形的,但是实现方法讲解的很清晰,我在其基础上扩展了一下,支持了多种地形。
和其他算法不同的是,我用的是带透明缺口的图片,即
tiles.png
每一行是16种形状,6种颜色对应不同的地形,所以当多种地形拼接的时候,则是由多张小图片组合在一起绘制的(避免效率问题,可以在程序里合并在一张Texture上,即人为的生成过渡素材)
地图画出来是这样的:
image.png
图虽然比较恶心,但是基本实现了多地形的问题,而且美术素材较少。
PS:【4,3】处有一个交叉地形的bug,可以在算法里处理掉,我就不写了。
代码的话,只贴一个MainScene吧,用"cocos new -l lua"命令新建demo,然后替换掉里面的就可以了。
local MainScene = class("MainScene", cc.load("mvc").ViewBase)
function MainScene:onCreate()
self.tileWidth = 32 -- 图块宽
self.tileHeight = 32 -- 图块高
self.mapColumn = 20 -- 地图列
self.mapRow = 14 -- 地图行
self.mapData = nil -- 地图数据
self.selectedTerrainType = 0 -- 当前选择的地形
-- 背景层
self.bgLayer = cc.LayerColor:create(cc.c3b(128, 128, 0))
:addTo(self)
-- 地图显示层
self.mapLayer = cc.LayerColor:create()
:setIgnoreAnchorPointForPosition(false)
:setContentSize(self.tileWidth * self.mapColumn, self.tileHeight * self.mapRow)
:move(display.center)
:onTouch(handler(self, self.onTouch))
:addTo(self)
-- 光标
self.highlight = cc.Sprite:create("res/highlight.png")
:setLocalZOrder(999)
:move(0, 0)
:addTo(self.mapLayer)
-- tile素材
local file = "res/tiles.png"
self.tileTexture = cc.Director:getInstance():getTextureCache():addImage(file)
-- 地形类型选择按钮
local btns = ccui.RadioButtonGroup:create()
:addTo(self)
btns:addEventListener(function(btn, index)
self.selectedTerrainType = index
self.highlight:setTexture(btn.imageFile)
end)
for i=1,self.tileTexture:getContentSize().height / self.tileHeight do
local file = "res/radio_select_" .. (i - 1) .. ".png"
local btn = ccui.RadioButton:create(file, file)
:move(i * 32 + 200, 60)
:addTo(self)
btn.imageFile = file
btns:addRadioButton(btn)
end
btns:setSelectedButton(1)
-- 初始化
self:initMap()
end
-- 初始化地图数据
--[[
value格式为{1,2,3,4}分别对应四个角的tile类型,即:
4 3
2 1
]]
function MainScene:initMap(value)
self.mapData = {}
self.mapTiles = {}
value = value or {0, 0, 0, 0}
for i=0,self.mapRow-1 do
self.mapData[i] = {}
self.mapTiles[i] = {}
for j=0,self.mapColumn-1 do
self.mapData[i][j] = clone(value)
end
end
self:updateMap()
end
-- 根据传进来的data:{1,2,3,4}生成对应的地块图,是由1~4张小图片组合起来的
-- TODO 拼接完后可以生成Texture缓存起来,可以优化绘制效率
function MainScene:getTile(data)
local tmp = data
assert(#tmp == 4)
local foos = {}
for i,terrainType in pairs(tmp) do
local cornerType = math.pow(2, 4 - i) -- 2的(4-i)次方
foos[terrainType] = (foos[terrainType] or 0) + cornerType
end
local ret = cc.Node:create()
for terrainType,cornerType in pairs(foos) do
local x = cornerType * self.tileWidth
local y = terrainType * self.tileHeight
local tile = cc.Sprite:createWithTexture(self.tileTexture,
cc.rect(x, y, self.tileWidth, self.tileHeight))
:setAnchorPoint(0, 0)
:addTo(ret)
end
return ret
end
-- 更新地图
function MainScene:updateMap(x1, y1, x2, y2)
x1 = math.max(x1 or 0, 0)
y1 = math.max(y1 or 0, 0)
x2 = math.min(x2 or self.mapColumn-1, self.mapColumn-1)
y2 = math.min(y2 or self.mapRow-1, self.mapRow-1)
for i=y1,y2 do
for j=x1,x2 do
if(self.mapTiles[i][j]) then
self.mapTiles[i][j]:removeFromParent()
end
self.mapTiles[i][j] = self:getTile(self.mapData[i][j])
:move(j * self.tileWidth, (self.mapRow - i - 1) * self.tileHeight)
:addTo(self.mapLayer)
end
end
end
-- 处理点击事件
function MainScene:onTouch(event)
local pos = self.mapLayer:convertToNodeSpace(event)
local size = self.mapLayer:getContentSize()
local x = math.floor((pos.x + self.tileWidth / 2) / self.tileWidth)
local y = math.floor((pos.y + self.tileHeight / 2) / self.tileHeight)
if(x < 0 or y < 0 or x > self.mapColumn or y > self.mapRow) then
return false
end
self.highlight:move(x * self.tileWidth, y * self.tileHeight)
y = self.mapRow - y
local POS = {
{y-1, x-1},
{y-1, x},
{y, x-1},
{y, x}
}
for i,v in pairs(POS) do
local r, c = v[1], v[2]
if(self.mapData[r] and self.mapData[r][c]) then
self.mapData[r][c][i] = self.selectedTerrainType
end
end
self:updateMap(x-1, y-1, x, y)
return true
end
return MainScene
编译好的exe放网盘里了,里面还有res和src
链接: https://pan.baidu.com/s/1G0IIuCJ8L1J63rAAMMN7nA 密码: 94ti











网友评论