之前看到明日方舟的宿舍挺有趣的,可以摆放大小不一的家具在一个矩形体的六面墙上。之前写了没时间弄博客现在做下记录。
本次设计点:
1、层级结构为,根节点下面有六面墙壁,每面墙在代码里会进行大小的缩放,所以这里为保持每面墙内的物体能够按照实际大小摆放,多加了一个反向缩放的原始节点层,在里面就是格子层和家具层
顶部墙面
?
?
?底部墙面
?
?
?
?
?前部墙面
?
?
?
?垂直的面加了四条线以显示边界
?
?
?
?后部墙面
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
2、房间大小、格子大小、家具大小全部动态设置,效果如下面图所示:
格子正方形,房间正方形,家具长方形
?格子长方形,房间正方形,家具正方形
房间长方形,格子正方形,家具正方形
?
?3、目前一个面一个碰撞体共6个碰撞体,为了每一面都可以求出局部坐标进而求出每个格子的坐标再换算出行列;重合检测有房间内的所有家具的边界检测,和所有家具之间的重合检测。目前没时间弄,边界检查只需判断家具靠墙的一面的长宽共4个点是否越出墙的长宽即可;至于家具之前的重合检测,初步考虑用算法,将房间分割成X*Y*Z个小六面体,每个小六面体都存储有投影到六面墙上时每一面墙上对应所有已被其他家具占用的格子,这样每次家具的移动都对n个被波及到的小六面体进行数据增删以及检测重合。或者考虑用八叉树处理优化下。具体有空再试试
4、未精简优化代码:
控制器:
module("room.RoomController", Class.impl(Controller))
--构造函数
function ctor(self, cusMgr)
super.ctor(self, cusMgr)
end
--析构函数
function dtor(self)
end
-- Override 重新登录
function reLogin(self)
super.reLogin(self)
end
--游戏开始的回调
function gameStartCallBack(self)
end
--模块间事件监听
function listNotification(self)
GameDispatcher:addEventListener(room.RoomManager.OPEN_ROOM_PANEL, self.__onOpenRoomPanelHandler, self)
end
--注册server发来的数据
function registerMsgHandler(self)
return {}
end
function __onOpenRoomPanelHandler(self, args)
self:__addFrameHandler()
self:__openRoomScene()
room.RoomManager:setMainUICameraVisible(false)
if(not self.mRoomPanel)then
self.mRoomPanel = room.RoomPanel.new()
self.mRoomPanel:addEventListener(View.EVENT_VIEW_DESTROY, self.onDestroyRoomPanelHandler,self)
end
self.mRoomPanel:open()
end
function onDestroyRoomPanelHandler(self)
self:__removeFrameHandler()
self:__closeRoomScene()
room.RoomManager:setMainUICameraVisible(true)
self.mRoomPanel:removeEventListener(View.EVENT_VIEW_DESTROY,self.onDestroyRoomPanelHandler,self)
self.mRoomPanel = nil
end
---------------------------------------------------------------场景--------------------------------------------------------------------------
function __addFrameHandler(self)
LoopManager:addFrame(1, 0, self, self.__frameUpdate)
end
function __removeFrameHandler(self)
LoopManager:removeFrame(self, self.__frameUpdate)
end
function __frameUpdate(self)
if(gs.Input:GetMouseButton(0))then
local sceneCamera = room.RoomManager:getSceneCamera():GetComponent(ty.Camera)
local hitInfo = gs.UnityEngineUtil.RaycastByUICamera(sceneCamera, "", 100)
if(hitInfo) then
local transform = hitInfo.transform
if(transform)then
if(string.find(transform.name, "WALL_") ~= nil)then
local needUpdate = false
local dirType = room.getDirType(transform.name)
if(self.mCurDirType ~= dirType)then
needUpdate = true
local oldIndex = self.mCurIndex
local oldDirType, newDirType = self.mCurDirType, dirType
self.mCurDirType = dirType
self:changeTileMaterial(oldDirType, nil)
local thing = self.mThingDic[oldDirType][oldIndex]
self.mThingDic[oldDirType][oldIndex] = nil
thing.transform:SetParent(self.mThingNodeDic[newDirType].transform, false)
local localPos = self.mThingNodeDic[newDirType].transform:InverseTransformPoint(hitInfo.point)
local row, col = self:getRowColByPos(newDirType, localPos.x, localPos.y)
local index = self:getTileIndexByRowCol(newDirType, row, col)
self.mCurIndex = index
if(not self.mThingDic[newDirType])then
self.mThingDic[newDirType] = {}
end
self.mThingDic[newDirType][self.mCurIndex] = thing
else
local localPos = self.mThingNodeDic[self.mCurDirType].transform:InverseTransformPoint(hitInfo.point)
local row, col = self:getRowColByPos(self.mCurDirType, localPos.x, localPos.y)
local index = self:getTileIndexByRowCol(self.mCurDirType, row, col)
if(self.mCurIndex ~= index)then
needUpdate = true
local oldIndex = self.mCurIndex
self.mCurIndex = index
local thing = self.mThingDic[self.mCurDirType][oldIndex]
self.mThingDic[self.mCurDirType][oldIndex] = nil
self.mThingDic[self.mCurDirType][self.mCurIndex] = thing
end
end
-- 直接跟随鼠标坐标
if(needUpdate)then
local localPos = self.mThingNodeDic[self.mCurDirType].transform:InverseTransformPoint(hitInfo.point)
local thingId = room.RoomManager:getThingIdByIndex(self.mCurIndex)
local thingConfigVo = room.RoomManager:getThingConfigVo(thingId)
localPos.z = localPos.z + thingConfigVo:getSizeY() / 2
self.mThingDic[self.mCurDirType][self.mCurIndex].transform.localPosition = localPos
self:changeTileMaterial(self.mCurDirType, self.mCurIndex)
end
-- 直接跟随鼠标坐标对应的格子
if(needUpdate)then
local thingId = room.RoomManager:getThingIdByIndex(self.mCurIndex)
local thingConfigVo = room.RoomManager:getThingConfigVo(thingId)
local tileX, tileY = self:getTilePos(self.mCurDirType, self.mCurIndex)
gs.TransQuick:LPos(self.mThingDic[self.mCurDirType][self.mCurIndex].transform, tileX, tileY, thingConfigVo:getSizeY() / 2)
self:changeTileMaterial(self.mCurDirType, self.mCurIndex)
end
end
end
end
end
end
function __initSceneData(self)
-- 房间场景的各个物体字典
self.mWallDic = {}
self.mOriginalNodeDic = {}
self.mThingNodeDic = {}
self.mTileNodeDic = {}
-- 房间场景的所有格子字典
self.mTileIndexDic = {}
-- 房间场景的所有物件字典
self.mThingDic = {}
end
function __openRoomScene(self)
self:__initSceneData()
-- 初始化
self.mRoomScene = AssetLoader.GetGO(UrlManager:getPrefabPath('ui/room/Scene_RoomRoot.prefab'))
local childGods, childTrans = GoUtil.GetChildHash(self.mRoomScene)
local dirTypeList = {}
table.insert(dirTypeList, room.DIR_TOP)
table.insert(dirTypeList, room.DIR_BOTTOM)
table.insert(dirTypeList, room.DIR_FRONT)
table.insert(dirTypeList, room.DIR_BEHIND)
table.insert(dirTypeList, room.DIR_LEFT)
table.insert(dirTypeList, room.DIR_RIGHT)
-- 初始化各个节点
for i = 1, #dirTypeList do
local dirType = dirTypeList[i]
self.mWallDic[dirType] = self:getWallNodeName(childGods, dirType)
self.mOriginalNodeDic[dirType] = self:getOriginalNodeName(childGods, dirType)
self.mThingNodeDic[dirType] = self:getThingNodeName(childGods, dirType)
self.mTileNodeDic[dirType] = self:getTileNodeName(childGods, dirType)
end
-- 场景大小设置
local roomSizeX, roomSizeY, roomSizeZ = room.RoomManager:getRoomSize()
gs.TransQuick:Pos(self.mRoomScene.transform, 0, 500, roomSizeZ / 2)
-- 6面墙的位置
gs.TransQuick:LPos(self.mWallDic[room.DIR_TOP].transform, 0, roomSizeY / 2, 0)
gs.TransQuick:LPos(self.mWallDic[room.DIR_BOTTOM].transform, 0, -roomSizeY / 2, 0)
gs.TransQuick:LPos(self.mWallDic[room.DIR_FRONT].transform, 0, 0, roomSizeZ / 2)
gs.TransQuick:LPos(self.mWallDic[room.DIR_BEHIND].transform, 0, 0, -roomSizeZ / 2)
gs.TransQuick:LPos(self.mWallDic[room.DIR_RIGHT].transform, roomSizeX / 2, 0, 0)
gs.TransQuick:LPos(self.mWallDic[room.DIR_LEFT].transform, -roomSizeX / 2, 0, 0)
-- 6面墙的大小
local sideDepth = room.RoomManager:getSideDepth()
gs.TransQuick:Scale(self.mWallDic[room.DIR_TOP].transform, roomSizeX, roomSizeZ, sideDepth)
gs.TransQuick:Scale(self.mWallDic[room.DIR_BOTTOM].transform, roomSizeX, roomSizeZ, sideDepth)
gs.TransQuick:Scale(self.mWallDic[room.DIR_FRONT].transform, roomSizeX, roomSizeY, sideDepth)
gs.TransQuick:Scale(self.mWallDic[room.DIR_BEHIND].transform, roomSizeX, roomSizeY, sideDepth)
gs.TransQuick:Scale(self.mWallDic[room.DIR_RIGHT].transform, roomSizeZ, roomSizeY, sideDepth)
gs.TransQuick:Scale(self.mWallDic[room.DIR_LEFT].transform, roomSizeZ, roomSizeY, sideDepth)
-- 6面墙缩放后,墙内总结点反向缩放,以使总结点内的所有物体都能按照通用标准大小
gs.TransQuick:Scale(self.mOriginalNodeDic[room.DIR_TOP].transform, 1 / roomSizeX, 1 / roomSizeZ, 1 / sideDepth)
gs.TransQuick:Scale(self.mOriginalNodeDic[room.DIR_BOTTOM].transform, 1 / roomSizeX, 1 / roomSizeZ, 1 / sideDepth)
gs.TransQuick:Scale(self.mOriginalNodeDic[room.DIR_FRONT].transform, 1 / roomSizeX, 1 / roomSizeY, 1 / sideDepth)
gs.TransQuick:Scale(self.mOriginalNodeDic[room.DIR_BEHIND].transform, 1 / roomSizeX, 1 / roomSizeY, 1 / sideDepth)
gs.TransQuick:Scale(self.mOriginalNodeDic[room.DIR_RIGHT].transform, 1 / roomSizeZ, 1 / roomSizeY, 1 / sideDepth)
gs.TransQuick:Scale(self.mOriginalNodeDic[room.DIR_LEFT].transform, 1 / roomSizeZ, 1 / roomSizeY, 1 / sideDepth)
-- 场景添加格子
for i = 1, #dirTypeList do
local dirType = dirTypeList[i]
self:addTileToWall(dirType)
end
-- local id = 0
-- for i = 1, #dirTypeList do
-- id = id + 1
-- local dirType = dirTypeList[i]
-- self:addThingToWall(dirType, id)
-- end
-- 初始加一个在底部
self.mCurDirType = room.DIR_BOTTOM
self.mCurIndex = 1
self:addThingToWall(self.mCurDirType, self.mCurIndex)
end
function changeTileMaterial(self, dirType, index)
if(index)then
local tileX, tileY = self:getTilePos(dirType, index)
local thingX, thingY = tileX, tileY
local thingId = room.RoomManager:getThingIdByIndex(index)
local thingConfigVo = room.RoomManager:getThingConfigVo(thingId)
local thingSizeX, thingSizeY, thingSizeZ = thingConfigVo:getSize()
-- index所在点占据的格子范围包含的最小坐标和最大坐标(加减0.001是取巧做法,基本可以减少坐标点避免刚好落在tile边界上的概率)
local thingMinX = thingX - thingSizeX / 2 + 0.001
local thingMaxX = thingX + thingSizeX / 2 - 0.001
local thingMinY = thingY - thingSizeY / 2 + 0.001
local thingMaxY = thingY + thingSizeY / 2 - 0.001
-- index所在点占据的格子范围包含的最小行列和最大行列
local minCol = self:getColByPosX(dirType, thingMinX)
local maxCol = self:getColByPosX(dirType, thingMaxX)
local minRow = self:getRowByPosY(dirType, thingMinY)
local maxRow = self:getRowByPosY(dirType, thingMaxY)
-- 刷格子对应颜色
local indexDic = self.mTileIndexDic[dirType]
for _index, tile in pairs(indexDic) do
local _row, _col = self:getRowColByIndex(dirType, _index)
if((_row >= minRow and _row <= maxRow) or (_col >= minCol and _col <= maxCol))then
local renderer = tile:GetComponent(ty.MeshRenderer)
gs.GoUtil.ChangeShader(renderer, 1, true)
else
local renderer = tile:GetComponent(ty.MeshRenderer)
gs.GoUtil.ChangeShader(renderer, 1, false)
end
end
else
-- 刷全部格子颜色至默认
local indexDic = self.mTileIndexDic[dirType]
for _index, tile in pairs(indexDic) do
local _row, _col = self:getRowColByIndex(dirType, _index)
local renderer = tile:GetComponent(ty.MeshRenderer)
gs.GoUtil.ChangeShader(renderer, 1, false)
end
end
end
function addTileToWall(self, dirType)
if(not self.mTileIndexDic[dirType])then
self.mTileIndexDic[dirType] = {}
end
local tileSizeX, tileSizeY = room.RoomManager:getTileSize()
local tileRowCount, tileColCount = self:getRowColNum(dirType)
local sideDepth = room.RoomManager:getSideDepth()
local index = 0
for row = 1, tileRowCount do
for col = 1, tileColCount do
index = index + 1
local tileX, tileY = self:getTilePos(dirType, index)
local tile = AssetLoader.GetGO(UrlManager:getPrefabPath('ui/room/Scene_RoomTile.prefab'))
tile.transform:SetParent(self.mTileNodeDic[dirType].transform, false)
gs.TransQuick:Scale(tile.transform, tileSizeX, tileSizeY, sideDepth)
gs.TransQuick:LPos(tile.transform, tileX, tileY, 0)
self.mTileIndexDic[dirType][index] = tile
end
end
end
function addThingToWall(self, dirType, thingId)
if(not self.mThingDic[dirType])then
self.mThingDic[dirType] = {}
end
local thingConfigVo = room.RoomManager:getThingConfigVo(thingId)
local thingSizeX, thingSizeY, thingSizeZ = thingConfigVo:getSize()
local thing = AssetLoader.GetGO(UrlManager:getPrefabPath('ui/room/Scene_RoomThing.prefab'))
gs.TransQuick:Scale(thing.transform, thingSizeX, thingSizeY, thingSizeZ)
gs.TransQuick:LPos(thing.transform, 0, 0, thingSizeY / 2)
thing.transform:SetParent(self.mThingNodeDic[dirType].transform, false)
self.mThingDic[dirType][thingId] = thing
end
function getRowColNum(self, dirType)
local roomSizeX, roomSizeY, roomSizeZ = room.RoomManager:getRoomSize()
local tileSizeX, tileSizeY = room.RoomManager:getTileSize()
local col = 0
local row = 0
if(dirType == room.DIR_LEFT or dirType == room.DIR_RIGHT)then
col = math.floor(roomSizeZ / tileSizeX)
row = math.floor(roomSizeY / tileSizeY)
elseif(dirType == room.DIR_TOP or dirType == room.DIR_BOTTOM)then
col = math.floor(roomSizeX / tileSizeX)
row = math.floor(roomSizeZ / tileSizeY)
elseif(dirType == room.DIR_FRONT or dirType == room.DIR_BEHIND)then
col = math.floor(roomSizeX / tileSizeX)
row = math.floor(roomSizeY / tileSizeY)
end
return row, col
end
function getRowColByIndex(self, dirType, index)
local tileRowCount, tileColCount = self:getRowColNum(dirType)
local temp = index % tileColCount
local col = temp == 0 and tileColCount or temp
local temp = math.ceil(index / tileColCount)
local row = temp == 0 and tileColCount or temp
return row, col
end
function getTileIndexByRowCol(self, dirType, row, col)
local tileRowCount, tileColCount = self:getRowColNum(dirType)
return (row - 1) * tileColCount + col
end
-- 如果坐标处在tile边界,获取会不准,需自己提前判断
function getRowColByPos(self, dirType, localPosX, localPosY)
local col = self:getColByPosX(dirType, localPosX)
local row = self:getRowByPosY(dirType, localPosY)
return row, col
end
-- 如果坐标处在tile边界,获取会不准,需自己提前判断
function getColByPosX(self, dirType, localPosX)
local roomSizeX, roomSizeY, roomSizeZ = room.RoomManager:getRoomSize()
local tileSizeX, tileSizeY = room.RoomManager:getTileSize()
local col = 0
if(dirType == room.DIR_LEFT or dirType == room.DIR_RIGHT)then
col = math.ceil((localPosX + roomSizeZ / 2) / tileSizeX)
elseif(dirType == room.DIR_TOP or dirType == room.DIR_BOTTOM)then
col = math.ceil((localPosX + roomSizeX / 2) / tileSizeX)
elseif(dirType == room.DIR_FRONT or dirType == room.DIR_BEHIND)then
col = math.ceil((localPosX + roomSizeX / 2) / tileSizeX)
end
return col
end
-- 如果坐标处在tile边界,获取会不准,需自己提前判断
function getRowByPosY(self, dirType, localPosY)
local roomSizeX, roomSizeY, roomSizeZ = room.RoomManager:getRoomSize()
local tileSizeX, tileSizeY = room.RoomManager:getTileSize()
local row = 0
if(dirType == room.DIR_LEFT or dirType == room.DIR_RIGHT)then
row = math.ceil((localPosY + roomSizeY / 2) / tileSizeY)
elseif(dirType == room.DIR_TOP or dirType == room.DIR_BOTTOM)then
row = math.ceil((localPosY + roomSizeZ / 2) / tileSizeY)
elseif(dirType == room.DIR_FRONT or dirType == room.DIR_BEHIND)then
row = math.ceil((localPosY + roomSizeY / 2) / tileSizeY)
end
return row
end
function getTilePos(self, dirType, index)
local roomSizeX, roomSizeY, roomSizeZ = room.RoomManager:getRoomSize()
local tileSizeX, tileSizeY = room.RoomManager:getTileSize()
local halfTileSizeX = tileSizeX / 2
local halfTileSizeY = tileSizeY / 2
local startX = 0
local startY = 0
if(dirType == room.DIR_LEFT or dirType == room.DIR_RIGHT)then
startX = (- roomSizeZ / 2) + halfTileSizeX
startY = (- roomSizeY / 2) + halfTileSizeY
elseif(dirType == room.DIR_TOP or dirType == room.DIR_BOTTOM)then
startX = (- roomSizeX / 2) + halfTileSizeX
startY = (- roomSizeZ / 2) + halfTileSizeY
elseif(dirType == room.DIR_FRONT or dirType == room.DIR_BEHIND)then
startX = (- roomSizeX / 2) + halfTileSizeX
startY = (- roomSizeY / 2) + halfTileSizeY
end
local row, col = self:getRowColByIndex(dirType, index)
local tileX = startX + (col - 1) * tileSizeX
local tileY = startY + (row - 1) * tileSizeY
return tileX, tileY
end
function getWallNodeName(self, gos, type)
return gos["WALL_".. type]
end
function getOriginalNodeName(self, gos, type)
return gos["ORIGINAL_NODE_".. type]
end
function getTileNodeName(self, gos, type)
return gos["TILE_NODE_".. type]
end
function getThingNodeName(self, gos, type)
return gos["THING_NODE_".. type]
end
function __closeRoomScene(self)
self.mWallDic = nil
self.mOriginalNodeDic = nil
self.mThingNodeDic = nil
self.mTileNodeDic = nil
if(self.mTileIndexDic)then
for dirType, indexDic in pairs(self.mTileIndexDic) do
for index, tile in pairs(indexDic) do
gs.GameObject.Destroy(tile)
end
end
self.mTileIndexDic = nil
end
if(self.mThingDic)then
for dirType, indexDic in pairs(self.mThingDic) do
for index, tile in pairs(indexDic) do
gs.GameObject.Destroy(tile)
end
end
self.mThingDic = nil
end
gs.GameObject.Destroy(self.mRoomScene)
self.mRoomScene = nil
end
return _M
数据管理器
module("room.RoomManager", Class.impl(Manager))
OPEN_ROOM_PANEL = "OPEN_ROOM_PANEL"
--构造函数
function ctor(self)
super.ctor(self)
self:__init()
end
-- Override 重置数据
function resetData(self)
super.resetData(self)
self:__init()
end
function __init(self)
end
-- 面的厚度(用于墙面厚度和格子厚度)
function getSideDepth(self)
return 0.0001
end
-- 获取格子大小
function getTileSize(self)
local x, y = 1, 1
return x, y
end
-- 房间宽高厚均为格子宽高的公倍数
function getRoomSize(self)
local x, y, z = 15, 5, 10
return x, y, z
end
function getThingConfigVo(self, id)
local configVo = room.RoomThingConfigVo.new()
configVo:setConfigId(id)
configVo:setSize(1, 1, 1)
configVo:setRotation(0, 0, 0)
return configVo
end
function getThingIdByIndex(self, index)
local thingId = 1
return thingId
end
function setMainUICameraVisible(self, visible)
if(not self.mMainUICamera)then
self.mMainUICamera = gs.GameObject.Find("[SCamera]")
end
self.mMainUICamera:SetActive(visible)
if(not self.mSceneCamera)then
local childGods, childTrans = GoUtil.GetChildHash(gs.GameObject.Find("[CAMERAs]"))
self.mSceneCamera = childGods["[SCENE_CAMERA]"]
end
if(visible)then
self.mSceneCamera:SetActive(false)
gs.TransQuick:Pos(self.mSceneCamera.transform, -8, 4, -2)
else
self.mSceneCamera:SetActive(true)
gs.TransQuick:Pos(self.mSceneCamera.transform, 0, 500, 0)
end
end
function getSceneCamera(self)
return self.mSceneCamera
end
return _M
常量定义
room.DIR_TOP = "TOP"
room.DIR_BOTTOM = "BOTTOM"
room.DIR_FRONT = "FRONT"
room.DIR_BEHIND = "BEHIND"
room.DIR_LEFT = "LEFT"
room.DIR_RIGHT = "RIGHT"
room.getDirType = function(str)
if(string.find(str, room.DIR_TOP))then
return room.DIR_TOP
elseif(string.find(str, room.DIR_BOTTOM))then
return room.DIR_BOTTOM
elseif(string.find(str, room.DIR_FRONT))then
return room.DIR_FRONT
elseif(string.find(str, room.DIR_BEHIND))then
return room.DIR_BEHIND
elseif(string.find(str, room.DIR_LEFT))then
return room.DIR_LEFT
elseif(string.find(str, room.DIR_RIGHT))then
return room.DIR_RIGHT
end
return ""
end
模拟的格子数据结构
module("room.RoomThingConfigVo", Class.impl())
function setConfigId(self, cusConfigId)
self.mConfigId = cusConfigId
end
function getConfigId(self)
return self.mConfigId or 0
end
function setRotation(self, rotationX, rotationY, rotationZ)
self.mRotationX = rotationX
self.mRotationY = rotationY
self.mRotationZ = rotationZ
end
function getRotation(self)
return self:getRotationX(), self:getRotationY(), self:getRotationZ()
end
function getRotationX(self)
local rotation = self.mRotationX or 0
return rotation
end
function getRotationY(self)
local rotation = self.mRotationY or 0
return rotation
end
function getRotationZ(self)
local rotation = self.mSizeZ or 0
return rotation
end
function setSize(self, sizeX, sizeY, sizeZ)
self.mSizeX = sizeX
self.mSizeY = sizeY
self.mSizeZ = sizeZ
end
function getSize(self)
return self:getSizeX(), self:getSizeY(), self:getSizeZ()
end
function getSizeX(self)
local size = self.mSizeX or 0
return size
end
function getSizeY(self)
local size = self.mSizeY or 0
return size
end
function getSizeZ(self)
local size = self.mSizeZ or 0
return size
end
function getDirType(self)
return room.DIR_BOTTOM
end
function getIndex(self)
return 0
end
return _M
module("room.RoomThingVo", Class.impl(room.RoomThingConfigVo))
function setId(self, cusId)
self.mId = cusId
end
function getId(self)
return self.mId or 0
end
return _M
?
|