本文主要是介绍Unity 家居摆放系统设计 (一),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
之前看到明日方舟的宿舍挺有趣的,可以摆放大小不一的家具在一个矩形体的六面墙上。之前写了没时间弄博客现在做下记录。
本次设计点:
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 {}
endfunction __onOpenRoomPanelHandler(self, args)self:__addFrameHandler()self:__openRoomScene()room.RoomManager:setMainUICameraVisible(false)if(not self.mRoomPanel)thenself.mRoomPanel = room.RoomPanel.new()self.mRoomPanel:addEventListener(View.EVENT_VIEW_DESTROY, self.onDestroyRoomPanelHandler,self)endself.mRoomPanel:open()
endfunction 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)
endfunction __removeFrameHandler(self)LoopManager:removeFrame(self, self.__frameUpdate)
endfunction __frameUpdate(self)if(gs.Input:GetMouseButton(0))thenlocal sceneCamera = room.RoomManager:getSceneCamera():GetComponent(ty.Camera)local hitInfo = gs.UnityEngineUtil.RaycastByUICamera(sceneCamera, "", 100)if(hitInfo) thenlocal transform = hitInfo.transformif(transform)thenif(string.find(transform.name, "WALL_") ~= nil)thenlocal needUpdate = falselocal dirType = room.getDirType(transform.name)if(self.mCurDirType ~= dirType)thenneedUpdate = truelocal oldIndex = self.mCurIndexlocal oldDirType, newDirType = self.mCurDirType, dirTypeself.mCurDirType = dirTypeself:changeTileMaterial(oldDirType, nil)local thing = self.mThingDic[oldDirType][oldIndex]self.mThingDic[oldDirType][oldIndex] = nilthing.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 = indexif(not self.mThingDic[newDirType])thenself.mThingDic[newDirType] = {}endself.mThingDic[newDirType][self.mCurIndex] = thingelselocal 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)thenneedUpdate = truelocal oldIndex = self.mCurIndexself.mCurIndex = indexlocal thing = self.mThingDic[self.mCurDirType][oldIndex]self.mThingDic[self.mCurDirType][oldIndex] = nilself.mThingDic[self.mCurDirType][self.mCurIndex] = thingendend-- 直接跟随鼠标坐标if(needUpdate)thenlocal 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() / 2self.mThingDic[self.mCurDirType][self.mCurIndex].transform.localPosition = localPosself:changeTileMaterial(self.mCurDirType, self.mCurIndex)end-- 直接跟随鼠标坐标对应的格子if(needUpdate)thenlocal 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)endendendendend
endfunction __initSceneData(self)-- 房间场景的各个物体字典self.mWallDic = {}self.mOriginalNodeDic = {}self.mThingNodeDic = {}self.mTileNodeDic = {}-- 房间场景的所有格子字典self.mTileIndexDic = {}-- 房间场景的所有物件字典self.mThingDic = {}
endfunction __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 dolocal 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 dolocal 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_BOTTOMself.mCurIndex = 1self:addThingToWall(self.mCurDirType, self.mCurIndex)
endfunction changeTileMaterial(self, dirType, index)if(index)thenlocal tileX, tileY = self:getTilePos(dirType, index)local thingX, thingY = tileX, tileYlocal 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.001local thingMaxX = thingX + thingSizeX / 2 - 0.001local thingMinY = thingY - thingSizeY / 2 + 0.001local 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) dolocal _row, _col = self:getRowColByIndex(dirType, _index)if((_row >= minRow and _row <= maxRow) or (_col >= minCol and _col <= maxCol))thenlocal renderer = tile:GetComponent(ty.MeshRenderer)gs.GoUtil.ChangeShader(renderer, 1, true)elselocal renderer = tile:GetComponent(ty.MeshRenderer)gs.GoUtil.ChangeShader(renderer, 1, false)endendelse-- 刷全部格子颜色至默认local indexDic = self.mTileIndexDic[dirType]for _index, tile in pairs(indexDic) dolocal _row, _col = self:getRowColByIndex(dirType, _index)local renderer = tile:GetComponent(ty.MeshRenderer)gs.GoUtil.ChangeShader(renderer, 1, false)endend
endfunction addTileToWall(self, dirType)if(not self.mTileIndexDic[dirType])thenself.mTileIndexDic[dirType] = {}endlocal tileSizeX, tileSizeY = room.RoomManager:getTileSize()local tileRowCount, tileColCount = self:getRowColNum(dirType)local sideDepth = room.RoomManager:getSideDepth()local index = 0for row = 1, tileRowCount dofor col = 1, tileColCount doindex = index + 1local 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] = tileendend
endfunction addThingToWall(self, dirType, thingId)if(not self.mThingDic[dirType])thenself.mThingDic[dirType] = {}endlocal 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
endfunction getRowColNum(self, dirType)local roomSizeX, roomSizeY, roomSizeZ = room.RoomManager:getRoomSize()local tileSizeX, tileSizeY = room.RoomManager:getTileSize()local col = 0local row = 0if(dirType == room.DIR_LEFT or dirType == room.DIR_RIGHT)thencol = math.floor(roomSizeZ / tileSizeX)row = math.floor(roomSizeY / tileSizeY)elseif(dirType == room.DIR_TOP or dirType == room.DIR_BOTTOM)thencol = math.floor(roomSizeX / tileSizeX)row = math.floor(roomSizeZ / tileSizeY)elseif(dirType == room.DIR_FRONT or dirType == room.DIR_BEHIND)thencol = math.floor(roomSizeX / tileSizeX)row = math.floor(roomSizeY / tileSizeY)endreturn row, col
endfunction getRowColByIndex(self, dirType, index)local tileRowCount, tileColCount = self:getRowColNum(dirType)local temp = index % tileColCountlocal col = temp == 0 and tileColCount or templocal temp = math.ceil(index / tileColCount)local row = temp == 0 and tileColCount or tempreturn row, col
endfunction 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 = 0if(dirType == room.DIR_LEFT or dirType == room.DIR_RIGHT)thencol = math.ceil((localPosX + roomSizeZ / 2) / tileSizeX)elseif(dirType == room.DIR_TOP or dirType == room.DIR_BOTTOM)thencol = math.ceil((localPosX + roomSizeX / 2) / tileSizeX)elseif(dirType == room.DIR_FRONT or dirType == room.DIR_BEHIND)thencol = math.ceil((localPosX + roomSizeX / 2) / tileSizeX)endreturn col
end-- 如果坐标处在tile边界,获取会不准,需自己提前判断
function getRowByPosY(self, dirType, localPosY)local roomSizeX, roomSizeY, roomSizeZ = room.RoomManager:getRoomSize()local tileSizeX, tileSizeY = room.RoomManager:getTileSize()local row = 0if(dirType == room.DIR_LEFT or dirType == room.DIR_RIGHT)thenrow = math.ceil((localPosY + roomSizeY / 2) / tileSizeY)elseif(dirType == room.DIR_TOP or dirType == room.DIR_BOTTOM)thenrow = math.ceil((localPosY + roomSizeZ / 2) / tileSizeY)elseif(dirType == room.DIR_FRONT or dirType == room.DIR_BEHIND)thenrow = math.ceil((localPosY + roomSizeY / 2) / tileSizeY)endreturn row
endfunction getTilePos(self, dirType, index)local roomSizeX, roomSizeY, roomSizeZ = room.RoomManager:getRoomSize()local tileSizeX, tileSizeY = room.RoomManager:getTileSize()local halfTileSizeX = tileSizeX / 2local halfTileSizeY = tileSizeY / 2local startX = 0local startY = 0if(dirType == room.DIR_LEFT or dirType == room.DIR_RIGHT)thenstartX = (- roomSizeZ / 2) + halfTileSizeXstartY = (- roomSizeY / 2) + halfTileSizeYelseif(dirType == room.DIR_TOP or dirType == room.DIR_BOTTOM)thenstartX = (- roomSizeX / 2) + halfTileSizeXstartY = (- roomSizeZ / 2) + halfTileSizeYelseif(dirType == room.DIR_FRONT or dirType == room.DIR_BEHIND)thenstartX = (- roomSizeX / 2) + halfTileSizeXstartY = (- roomSizeY / 2) + halfTileSizeYendlocal row, col = self:getRowColByIndex(dirType, index)local tileX = startX + (col - 1) * tileSizeXlocal tileY = startY + (row - 1) * tileSizeYreturn tileX, tileY
endfunction getWallNodeName(self, gos, type)return gos["WALL_".. type]
endfunction getOriginalNodeName(self, gos, type)return gos["ORIGINAL_NODE_".. type]
endfunction getTileNodeName(self, gos, type)return gos["TILE_NODE_".. type]
endfunction getThingNodeName(self, gos, type)return gos["THING_NODE_".. type]
endfunction __closeRoomScene(self)self.mWallDic = nilself.mOriginalNodeDic = nilself.mThingNodeDic = nilself.mTileNodeDic = nilif(self.mTileIndexDic)thenfor dirType, indexDic in pairs(self.mTileIndexDic) dofor index, tile in pairs(indexDic) dogs.GameObject.Destroy(tile)endendself.mTileIndexDic = nilendif(self.mThingDic)thenfor dirType, indexDic in pairs(self.mThingDic) dofor index, tile in pairs(indexDic) dogs.GameObject.Destroy(tile)endendself.mThingDic = nilendgs.GameObject.Destroy(self.mRoomScene)self.mRoomScene = nil
endreturn _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()
endfunction __init(self)
end-- 面的厚度(用于墙面厚度和格子厚度)
function getSideDepth(self)return 0.0001
end-- 获取格子大小
function getTileSize(self)local x, y = 1, 1return x, y
end-- 房间宽高厚均为格子宽高的公倍数
function getRoomSize(self)local x, y, z = 15, 5, 10return x, y, z
endfunction getThingConfigVo(self, id)local configVo = room.RoomThingConfigVo.new()configVo:setConfigId(id)configVo:setSize(1, 1, 1)configVo:setRotation(0, 0, 0)return configVo
endfunction getThingIdByIndex(self, index)local thingId = 1return thingId
endfunction setMainUICameraVisible(self, visible)if(not self.mMainUICamera)thenself.mMainUICamera = gs.GameObject.Find("[SCamera]")endself.mMainUICamera:SetActive(visible)if(not self.mSceneCamera)thenlocal childGods, childTrans = GoUtil.GetChildHash(gs.GameObject.Find("[CAMERAs]"))self.mSceneCamera = childGods["[SCENE_CAMERA]"]endif(visible)thenself.mSceneCamera:SetActive(false)gs.TransQuick:Pos(self.mSceneCamera.transform, -8, 4, -2)elseself.mSceneCamera:SetActive(true)gs.TransQuick:Pos(self.mSceneCamera.transform, 0, 500, 0)end
endfunction getSceneCamera(self)return self.mSceneCamera
endreturn _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))thenreturn room.DIR_TOPelseif(string.find(str, room.DIR_BOTTOM))thenreturn room.DIR_BOTTOMelseif(string.find(str, room.DIR_FRONT))thenreturn room.DIR_FRONTelseif(string.find(str, room.DIR_BEHIND))thenreturn room.DIR_BEHINDelseif(string.find(str, room.DIR_LEFT))thenreturn room.DIR_LEFTelseif(string.find(str, room.DIR_RIGHT))thenreturn room.DIR_RIGHTendreturn ""
end
模拟的格子数据结构
module("room.RoomThingConfigVo", Class.impl())function setConfigId(self, cusConfigId)self.mConfigId = cusConfigId
endfunction getConfigId(self)return self.mConfigId or 0
endfunction setRotation(self, rotationX, rotationY, rotationZ)self.mRotationX = rotationXself.mRotationY = rotationYself.mRotationZ = rotationZ
endfunction getRotation(self)return self:getRotationX(), self:getRotationY(), self:getRotationZ()
endfunction getRotationX(self)local rotation = self.mRotationX or 0return rotation
endfunction getRotationY(self)local rotation = self.mRotationY or 0return rotation
endfunction getRotationZ(self)local rotation = self.mSizeZ or 0return rotation
endfunction setSize(self, sizeX, sizeY, sizeZ)self.mSizeX = sizeXself.mSizeY = sizeYself.mSizeZ = sizeZ
endfunction getSize(self)return self:getSizeX(), self:getSizeY(), self:getSizeZ()
endfunction getSizeX(self)local size = self.mSizeX or 0return size
endfunction getSizeY(self)local size = self.mSizeY or 0return size
endfunction getSizeZ(self)local size = self.mSizeZ or 0return size
endfunction getDirType(self)return room.DIR_BOTTOM
endfunction getIndex(self)return 0
endreturn _M
module("room.RoomThingVo", Class.impl(room.RoomThingConfigVo))function setId(self, cusId)self.mId = cusId
endfunction getId(self)return self.mId or 0
endreturn _M
这篇关于Unity 家居摆放系统设计 (一)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!