风暴幻想游戏-家园系统设计

之前做过一个家园系统,今天和大家分享的是,关于在做家园系统的时候遇到的一些问题和当时的解决的方法。

遇到的几个问题:

1. 地图层问题。

    (1)这些东西如何在场景中摆放:地面,围墙,小窝,人,未解锁区域等等。

    (2)地图大小限制和地图最小单位的大小等因素。

    (3)我们需要保存的数据有哪些。

      当时的解决方法:

      1)幻想的地图层,把地图分成好几块加载的。策划根据地图编辑器生成了对应的文件,底层加载地图的时候会加载这些地图。但是家园地图是一个全新的地图。而且不是地图编辑器编辑的。但是底层有不提供修改支持。因此原先那几块地图就用了空的文件进行修改。相当于进入这个场景的时候,是一个黑的世界。

       2)地面,围墙,小窝,人,未解锁区域等,所有的家园场景的东西,我们通过不同的层加载到场景里。写了一个HomeMapDrawObject.lua 下面是它的初始化函数

 1 function HomeMapDrawObject:__init()
 2     HomeMapDrawObject.number = HomeMapDrawObject.number + 1
 3     self.core_pos = cc.vec2(0, 0)
 4 
 5     --集合
 6     self.coreNodes = {}
 7  
 8     --保持对象应用,方可访问其成员方法
 9     self.spriteLayers = {}
10     -- self.spriteLayers[cc.RenderQueue.GRQ_SHADOW] = {}
11     -- self.spriteLayers[cc.RenderQueue.GRQ_BUILD_AND_PLAYER] = {}
12 
13     --草地
14     local gressNode = cc.Node:create()
15     self.coreNodes[HomeMapDrawObject.Layer.GRESS] = gressNode
16     HandleRenderUnit:GetCoreScene():addToRenderGroup(gressNode, cc.RenderQueue.GRQ_SHADOW)
17 
18     --阴影
19     local shadowNode = cc.Node:create()
20     self.coreNodes[HomeMapDrawObject.Layer.SHOADOW] = shadowNode
21     HandleRenderUnit:GetCoreScene():addToRenderGroup(shadowNode, cc.RenderQueue.GRQ_SHADOW + 1)
22 
23     --身体
24     local bodyNode = cc.Node:create()
25     self.coreNodes[HomeMapDrawObject.Layer.BODY] = bodyNode
26     HandleRenderUnit:GetCoreScene():addToRenderGroup(bodyNode, cc.RenderQueue.GRQ_BUILD_AND_PLAYER - 2)
27 
28     --其他
29     local otherNode = cc.Node:create()
30     self.coreNodes[HomeMapDrawObject.Layer.OTHER] = otherNode
31     HandleRenderUnit:GetCoreScene():addToRenderGroup(otherNode, cc.RenderQueue.GRQ_BUILD_AND_PLAYER - 1)
32 
33 end
View Code

  我们将家园场景中的对象都称做家园碎片,用一个lua去维护。对于不同的类型,不同的点击效果做不同的处理等等。

下面是HomeMapPiece.lua

 1 PriorityQueue = PriorityQueue or BaseClass()
 2 
 3 function PriorityQueue:__init(infoVo)
 4     self.list     = {}
 5     self.hash     = {}    
 6     self.L         = bit.lshift
 7     self.And     = bit.band
 8     self.R         = bit.rshift
 9 
10     if infoVo then
11         self:Push(infoVo)
12     end
13 end
14 
15 function PriorityQueue:__delete()
16     self.list = nil
17     self.hash = nil
18 end
19 
20 function PriorityQueue:Push(infoVo)
21     table.insert(self.list, infoVo)
22 
23     local child = #self.list
24     local root = self.R(child, 1)
25     while child > 1 and self.list[root].F > self.list[child].F do
26         self.list[root], self.list[child] = self.list[child], self.list[root]
27 
28         self:SetHash(self.list[root].value, root)
29         self:SetHash(self.list[child].value, child)
30         child = root
31         root = self.R(child, 1)
32     end    
33 end
34 
35 function PriorityQueue:Pop()
36     local firstInfoVo = self.list[1]
37     local length = #self.list
38     --特判
39     if not self.list[length] then return firstInfoVo end
40     if length == 1 then
41         self.list = {}
42         return firstInfoVo
43     end
44 
45     local lastInfoVo = self.list[length]
46     table.remove(self.list, #self.list)
47     length = length - 1
48 
49     local root, child = 1, 2
50     while child <= length do
51         --找出左右最小的值
52         if child + 1 <= length and self.list[child + 1].F < self.list[child].F then
53             child = child + 1
54         end
55         if lastInfoVo.F < self.list[child].F then
56             break
57         else
58             self.list[root] = self.list[child]
59             self:SetHash(self.list[root].value, root)
60 
61             root = child
62             child = self.L(child, 1)
63         end
64     end
65     self.list[root] = lastInfoVo
66     self:SetHash(self.list[root].value, root)
67 
68     return firstInfoVo
69 end
70 
71 function PriorityQueue:SetHash(value, root)
72     self.hash[value] = root
73 end
74 
75 function PriorityQueue:GetInfoVo(index)
76     return self.list[self.hash[index]]
77 end
78 
79 function PriorityQueue:IsEmpty()
80     if self.list[1] then
81         return false
82     end
83     return true
84 end
View Code

下面是HomePieceVo.lua

  1 --家园地图碎片vo
  2 
  3 HomePieceVo = HomePieceVo or BaseClass()
  4 
  5 function HomePieceVo:__init()
  6     self.insId = 0                             --家具唯一id
  7     self.type = HomeMapManager.TYPE.GRASS     --碎片类型
  8     self.typeId = 0                            --类型id
  9     self.name = ""                             --家具名字
 10     self.resId = 0                            --形象id
 11     self.sideType = 0                        --布置区域(1室内2室外3任意)
 12     self.width = 0                            --
 13     self.height = 0                            --
 14     self.direction = 3                         --方向
 15     self.logicPosX = 0                         --逻辑坐标点x(和服务端交互的)
 16     self.logicPosY = 0                        --逻辑坐标点y
 17 
 18     self.leftNum = 0                         --左边的数字
 19     self.rightNum = 0                        --右边数字
 20     self.upNum = 0                            --上方数字
 21     self.bottomNum = 0                        --下方数字
 22 
 23     self.pointIndex = 0                     --x,y转化成的数字
 24     self.containPos = {}                    --覆盖的坐标点
 25     self.shadowPos = {}                     --阴影区的坐标点(当人物走到这个点的时候,他会显示成半透明)
 26 
 27     self.caveType = 1                        --类型1坐骑/2宠物/3精灵
 28     self.animalTypeId = 0                      --牲口id
 29     self.caveResId = 0                        --资源id
 30     self.masterId = 0                         --牲口主人id
 31     self.foodId = 0                            --口粮id
 32     self.feedRoleId = 0                        --饲养员id
 33     self.harvestTime = 0                     --收获时间
 34     self.expReward = 0                        --经验奖励
 35     self.propArr = {}                         --道具奖励
 36 end
 37 
 38 function HomePieceVo:__delete()
 39 end
 40 
 41 function HomePieceVo:ReadFromProtocal()
 42     self.insId,
 43     self.typeId,
 44     self.name,
 45     self.resId,
 46     self.logicPosX,
 47     self.logicPosY,
 48     self.direction,
 49     self.height,
 50     self.width 
 51     = UserMsgAdapter.ReadFMT("IIsICCCHH")
 52         
 53     local length = UserMsgAdapter.ReadFMT("h")
 54 
 55     --处理占据的点
 56     for i = 1, length do
 57         local vo = {}
 58         vo.x,
 59         vo.y 
 60         = UserMsgAdapter.ReadFMT("cc")
 61         self.containPos[i] = vo
 62     end
 63 
 64     --功能建筑部分(小窝)
 65     self.caveType,            --类型1坐骑/2宠物/3精灵
 66     self.animalTypeId,      --牲口(坐骑/宠物/精灵)id
 67     self.caveResId,            --牲口资源id
 68     self.masterId,             --牲口主人id
 69     self.foodId,            --口粮id
 70     self.feedRoleId,        --饲养员id
 71     self.harvestTime,         --收获时间
 72     self.expReward             --经验奖励
 73         = UserMsgAdapter.ReadFMT("ciiiiiii")
 74     length = UserMsgAdapter.ReadFMT("h")
 75     for i = 1, length do
 76         local tmp = {}
 77         tmp.propId,            --道具id
 78         tmp.propNumber,     --道具数量
 79         tmp.bind             --绑定
 80             = UserMsgAdapter.ReadFMT("iic")
 81         self.propArr[i] = tmp
 82     end
 83 
 84     local configPiece = Config.HomeFurniture[self.typeId]
 85     --处理阴影区的点
 86     local shadowHeight = configPiece.hight or 0
 87     for i = 1, shadowHeight do
 88         for k = 1, #self.containPos do
 89             local vo = {}
 90             vo.x = self.containPos[k].x
 91             vo.y = self.containPos[k].y - i
 92 
 93             table.insert(self.shadowPos, vo)
 94         end
 95     end
 96     local homeMapManager = HomeMapManager:getInstance()
 97     --家具类型
 98     if configPiece then
 99         self.type = homeMapManager:getPieceType(configPiece.type)
100     end
101     --寻路的时候构图
102     self.pointIndex = homeMapManager:getPointIndex(self.logicPosX, self.logicPosY)
103 end
104 
105 --[[
106     进入场景初始化处理草地信息
107 ]]
108 function HomePieceVo:sceneInitDispose(configVo)
109     self.logicPosX = configVo[1]
110     self.logicPosY = configVo[2]
111     self.typeId = configVo[3]
112     self.insId     = configVo[4]
113 
114     local configPiece = Config.HomeFurniture[self.typeId]
115     self.resId = configPiece.res_id
116     self.sideType = configPiece.sideType
117     self.width = configPiece.width
118     self.height = configPiece.length
119     self.name = configPiece.name
120 
121     local homeMapManager = HomeMapManager:getInstance()
122     --碎片类型
123     self.type = homeMapManager:getPieceType(configPiece.type)
124     self.pointIndex = homeMapManager:getPointIndex(self.logicPosX, self.logicPosY)
125     
126     --占据的格子
127     local posVo = {}
128     posVo.x = self.logicPosX
129     posVo.y = self.logicPosY
130     self.containPos[1] = posVo
131 end
132 
133 function HomePieceVo:getHomeLogicPos()
134     return self.homeLogicPosX, self.homeLogicPosY
135 end
136 
137 function HomePieceVo:setHomeLogicPos(x, y)
138     self.homeLogicPosX = x
139     self.homeLogicPosY = y
140 end
141 
142 function HomePieceVo:getLogicPos()
143     return self.logicPosX, self.logicPosY
144 end
145 
146 function HomePieceVo:setLogicPos(x, y)
147     self.logicPosX = x
148     self.logicPosY = y
149 end
150 
151 function HomePieceVo:getVar(var)
152     return self[var]
153 end
154 
155 function HomePieceVo:setVar(var, value)
156     if not self[var] then
157         print("ERROR HomePieceVo no has var:", var)
158         return
159     end
160     self[var] = value
161 end
View Code

  3)地图大小限制和地图最小单位的大小等因素。当时策划的需求是最小的地图是3600*3600,以后可能会扩大道7200*7200的地图。

 幻想的原先的地图的逻辑块大小是60*30代表一个格子。所有如果在原先基础上进行寻路和拾取就变成了60*120, 120*240的地图。

   当时美术那边提出说,要使用菱形的地板,从美术角度来说,看起来的效果会好一些。最后使用的方案是这样的:

 图中的坐标系是服务器的坐标系,一个菱形面积 等于 二个坐标系格子面积。这样的摆放,通过观察会发现一些规律。

 规律1:例如第一排的点A, B 他们的坐标分表是(1, 1),(3,1);第二排的点C,D 他们的坐标分别是(2,2),(4,2);x,y满足奇偶性。

  因为我们摆放的点不存在例如 (2,3),(5,6)类似这样的点。

规律2:因为我们需要进行拾取判定,我们的点击可能落在任意坐标系格子内。但是可分成8种情况进行讨论(一个菱形区域中)。

  下面讨论其中的1种情况。

  1.例如图中E点,根据图中可以看出,当我们点击E点的时候,实际上我们要算出,当前我们点击的是(7,3)这个点的菱形区域。

  E点的周围4个点分别是(min_x, min_y), (min_x, max_y), (max_x, min_y), (max_x, max_y)。(这些点都可以通过e点向上取整或者向下取整得出)

  这4个点中的一个点就是当前我们想求出的菱形区域的中心点

  但是此时,连接的是(min_x, max_y), (max_x, min_y)这两个点,构成了菱形的一条边。

  经过观察,我们知道

  E点的min_x = 6, max_y = 3, 和 E点相类的点F ,它的情况是 min_x = 7, max_y = 4, 可再根据几个点进行观察总结。

  我们可以得出

  当 min_x, max_y 不是同奇数或者同偶数的时候,连接的是 (min_x, max_y), (max_x, min_y)这两个点,构成了菱形的一条边。

  当 min_x, max_y 是同奇数或者同偶数的时候,连接的是 (min_x, min_y), (max_x, max_y)这两个点,构成了菱形的一条边。  

  2.由于1.得出的判断,我们可以排除2个点(因为已经构成了菱形的一条边了),剩下2个点,我们要如何判断呢?

  使用线性规划,根据1.求出的这2个点,我们构建直线,判断E点是在这条线段上方还说下方。这样,我们就可以得出需要找到的那个点了。

  剩下的几种情况类似,同理进行讨论。

  通过这样,我们就可以进行对拾取和放置场景碎片(草地,小窝等)进行操作。

  4)我们需要保存和维护的数据有哪些呢?

1     self.pieceList = {}    --家园碎片对象(草地上的东西等)
2     self.map = {}         --家园地图
3     self.roadMap = {}     --可行走的区域维护
4     self.shadowMap = {} --阴影区(人行走在这个区域会调整透明度)
5     self.tmpList = {}   --临时展示的区域
6     self.lockList = {}  --解锁格子信息

2.拾取问题。

       如果通过模拟点击能判断到是具体哪一个地图最小元素。

下面代码是 通过服务端逻辑坐标,计算出当前点击的是哪一个地图逻辑块的中心位置的坐标

 1 --[[
 2     @brief: 逻辑坐标转像家园地图坐标
 3 
 4     @param: logic_x        逻辑横坐标
 5             logic_y        逻辑纵坐标
 6             scale_size  缩放比例
 7     @return: 逻辑坐标
 8 ]]
 9 function TerrainHelper:ToHomeMapPos(logic_x, logic_y, scale_size)
10     --对特殊点的判断。
11     local home_logic_x, home_logic_y = self:logicToHomePos(logic_x, logic_y, scale_size)
12 
13     local min_x, max_x = math.floor(home_logic_x), math.ceil(home_logic_x)
14     local min_y, max_y = math.floor(home_logic_y), math.ceil(home_logic_y)
15 
16     if GameMath.getEvenAndOdd(min_x) == GameMath.getEvenAndOdd(max_y) then
17         local k = (max_y - min_y) / (max_x - min_x) --斜率
18         local b = max_y - k * max_x
19 
20         local value = k * home_logic_x + b
21 
22         if home_logic_y < value then --在线性规划区域下方
23             home_logic_x = max_x
24             home_logic_y = min_y
25         else                     --在线性规划区域上方
26             home_logic_x = min_x
27             home_logic_y = max_y
28         end
29     else
30         local k = (max_y - min_y) / (min_x - max_x)
31         local b = max_y - k * min_x 
32         local value = k * home_logic_x + b
33 
34         if home_logic_y < value then
35             home_logic_x = min_x 
36             home_logic_y = min_y
37         else
38             home_logic_x = max_x
39             home_logic_y = max_y
40         end
41     end
42     return home_logic_x, home_logic_y 
43 end
View Code

下面代码是 家园中对场景拾取操作的处理

1 function HomeController:homePiecePick(location)
2     local pos = HandleRenderUnit:ViewToWorld(location)
3     pos = HandleRenderUnit:WorldToLogic(pos) --服务端的逻辑坐标
4     local scaleSize = HomeMapManager:getInstance():getScaleSize() --缩放比
5     local homeLogicX, homeLogicY = self.handleTerrainHandler:ToHomeMapPos(pos.x, pos.y, scaleSize)
6 
7     self:modelHander(homeLogicX, homeLogicY)
8 end
View Code

3.寻路问题。

    (1)使用一个怎么样的寻路算法。迪杰斯特拉,或者A*, 或者其他方式。

    (2)如何维护一个动态地图。

  首先要解决的是一个动态维护的过程。其实按照前面的铺垫,这个动态地图的维护并不难,等同于维护一个可行走区域。当加入一个逻辑快的时候,

我们会对这个数据进行分析,加入的是什么。是草地,还说小窝或者是其他什么东西。这里就可以需要添加一个判定函数,这个东西是行走有否。

然后分别对这个区域,和这个区域的相邻区域进行设置。这样加入和删除一个地图里的元素,我们都可以动态处理。

下面是动态删减的维护代码,其中SCENE_DIRECTION是当前地图最小格子,周围8个方向的table

 1 --[[
 2     维护可行走的区域信息
 3     pointIndex: x, y转化后的序号
 4     x, y:    逻辑坐标x, y
 5     types:      类型  1 添加 2 删除
 6 ]]
 7 function HomeMapManager:updateRoadMap(pointIndex, x, y, types)
 8     local index = pointIndex
 9     --无信息又要删除
10     if types == 2 and not self.roadMap[index] then
11         return
12     end
13 
14     if types == 1 then --添加
15         self.roadMap[index] = {}
16         for i = 1, #SCENE_DIRECTION do
17             local newIndex = self:getPointIndex(SCENE_DIRECTION[i][1] + x, SCENE_DIRECTION[i][2] + y)
18             if self.roadMap[newIndex] then
19                 self.roadMap[index][newIndex] = true
20                 self.roadMap[newIndex][index] = true
21             end
22         end
23     elseif types == 2 then --删除
24         for newIndex, _ in pairs(self.roadMap[index]) do--删除和当前有关的信息
25             if self.roadMap[newIndex] then
26                 self.roadMap[newIndex][index] = nil
27                 local length = 0
28                 for _, _ in pairs(self.roadMap[newIndex]) do
29                     length = length + 1
30                     break
31                 end
32                 if length == 0 then
33                     self.roadMap[newIndex] = nil
34                 end
35             end
36         end
37         self.roadMap[index] = nil
38     end
39 end
40 
41 function HomeMapManager:getPointIndex(x, y)
42     return bit.lshift(x, HomeMapManager.PointLineBitLen) + y
43 end
View Code

  有了动态地图后,就是对地图进行搜索。我们需要快速计算出,从一个坐标(x, y)到另一个坐标(x1,y1)的行走路径。

原先使用的方案是迪杰斯特拉。刚刚说起,这个最小的地图是3600*3600,然后根据我们铺的菱形他的大小是120*60的。所以最小的行走地图是

30*60 = 1800 个点。但是大多数情况下这个地图并不是稀疏的地图。极端情况可能存在路径是1800*8个方向 = 14400条路径。每一次行走,我们需要形成1800个点的最小生成树,这样消耗比较大。这仅仅是3600*3600的地图。后面扩展成7200*7200,会更明显。

  A*算法。详细的这里不介绍了。详细请谷歌百度一下。

  A*的实现

  1 --[[
  2     A*寻路          
  3     map:            地图
  4     startPoint:     开始点    
  5     endPoint:         结束点
  6 ]]
  7 
  8 AStar = AStar or {}
  9 AStar.INF = 0xffffffff
 10 
 11 function AStar.findWay(map, startPoint, endPoint)
 12     local preList = AStar.BFS(map, startPoint, endPoint)
 13     local tmpList = {}
 14     local x = endPoint
 15     while(preList[x] and preList[x] ~= 0 ) do
 16         table.insert(tmpList, x)
 17         x = preList[x]
 18     end
 19     table.insert(tmpList, startPoint)
 20 
 21     local pointList = {}
 22     for i = #tmpList, 1, -1 do
 23         table.insert(pointList, tmpList[i])
 24     end
 25     -- PrintTable(pointList)
 26     return pointList
 27 end
 28 
 29 function AStar.BFS(map, startPoint, endPoint)
 30     local hash = {}        --标记已经被用过的点
 31     local preList = {}  --标记前继点
 32 
 33     local Queue = PriorityQueue.New(AStar.getTable(0, 0, startPoint, 0, 0)) --优先队列
 34 
 35     local homeManager = HomeMapManager:getInstance()
 36     local endx, endy = homeManager:getPointXY(endPoint)
 37 
 38     while not Queue:IsEmpty() do
 39         local nowInfoVo = Queue:Pop()
 40         local tmpPoint = nowInfoVo.value
 41 
 42         if tmpPoint == endPoint then
 43             Queue:DeleteMe()
 44             return preList
 45         end
 46 
 47         if not hash[tmpPoint] then
 48             hash[tmpPoint] = true
 49             local x, y = homeManager:getPointXY(tmpPoint)
 50             if map[tmpPoint] then
 51 
 52                 --枚举可以由当前点走向的点
 53                 for point, _ in pairs(map[tmpPoint]) do 
 54                     if not hash[point] then
 55                         local x1, y1 = homeManager:getPointXY(point)
 56                         local H = AStar.max(AStar.abs(endx - x1), AStar.abs(endy - y1))
 57                         local G = nowInfoVo.G
 58                         if x1 == x or y1 == y then
 59                             G = 2 + G
 60                         else
 61                             G = 1 + G
 62                         end
 63 
 64                         --不同方向权重增加(鼓励尽可能走直线,不要走弯曲的路线)
 65                         local dirx = x - x1
 66                         local diry = y - y1
 67                         if nowInfoVo.dirx ~= dirx or nowInfoVo.diry ~= diry then
 68                             G = G + 1.41
 69                         end
 70 
 71                         local tmpInfoVo = Queue:GetInfoVo(point)
 72                         if not tmpInfoVo then
 73                             local infoVo = AStar.getTable(G,H,point,dirx,diry)
 74                             Queue:Push(infoVo)
 75                             preList[point] = tmpPoint
 76                         else
 77                             --如果当前坐标的F值小于已经压入队列里面的F值
 78                             if tmpInfoVo.F > G + H then
 79                                 local infoVo = AStar.getTable(G,H,point,dirx,diry)
 80                                 Queue:Push(infoVo)
 81                                 preList[point] = tmpPoint
 82                             end
 83                         end
 84                     end
 85                 end
 86             end
 87         end
 88     end
 89     Queue:DeleteMe()
 90     return preList
 91 end
 92 
 93 function AStar.init(Queue, startPoint)
 94     local infoVo = AStar.getTable(0, 0, startPoint, 0, 0)
 95     Queue.Push(infoVo)
 96 end
 97 
 98 function AStar.getTable(g, h, point, x, y)
 99     return {G = g, H = h, F = g + h, value = point, dirx = x, diry = y}
100 end
101 
102 function AStar.abs(a)
103     if a >= 0 then
104         return a
105     else
106         return -a
107     end
108 end
109 
110 function AStar.max(a, b)
111     return a > b and a or b
112 end
View Code

  最小堆

 1 PriorityQueue = PriorityQueue or BaseClass()
 2 
 3 function PriorityQueue:__init(infoVo)
 4     self.list     = {}
 5     self.hash     = {}    
 6     self.L         = bit.lshift
 7     self.And     = bit.band
 8     self.R         = bit.rshift
 9 
10     if infoVo then
11         self:Push(infoVo)
12     end
13 end
14 
15 function PriorityQueue:__delete()
16     self.list = nil
17     self.hash = nil
18 end
19 
20 function PriorityQueue:Push(infoVo)
21     table.insert(self.list, infoVo)
22 
23     local child = #self.list
24     local root = self.R(child, 1)
25     while child > 1 and self.list[root].F > self.list[child].F do
26         self.list[root], self.list[child] = self.list[child], self.list[root]
27 
28         self:SetHash(self.list[root].value, root)
29         self:SetHash(self.list[child].value, child)
30         child = root
31         root = self.R(child, 1)
32     end    
33 end
34 
35 function PriorityQueue:Pop()
36     local firstInfoVo = self.list[1]
37     local length = #self.list
38     --特判
39     if not self.list[length] then return firstInfoVo end
40     if length == 1 then
41         self.list = {}
42         return firstInfoVo
43     end
44 
45     local lastInfoVo = self.list[length]
46     table.remove(self.list, #self.list)
47     length = length - 1
48 
49     local root, child = 1, 2
50     while child <= length do
51         --找出左右最小的值
52         if child + 1 <= length and self.list[child + 1].F < self.list[child].F then
53             child = child + 1
54         end
55         if lastInfoVo.F < self.list[child].F then
56             break
57         else
58             self.list[root] = self.list[child]
59             self:SetHash(self.list[root].value, root)
60 
61             root = child
62             child = self.L(child, 1)
63         end
64     end
65     self.list[root] = lastInfoVo
66     self:SetHash(self.list[root].value, root)
67 
68     return firstInfoVo
69 end
70 
71 function PriorityQueue:SetHash(value, root)
72     self.hash[value] = root
73 end
74 
75 function PriorityQueue:GetInfoVo(index)
76     return self.list[self.hash[index]]
77 end
78 
79 function PriorityQueue:IsEmpty()
80     if self.list[1] then
81         return false
82     end
83     return true
84 end
View Code

最后我们得到了一组行走的序列。(坐标集合),传给原先幻想的处理行走的逻辑部分,然后人物就可以走动了。

4.其他问题。

    (1)进入家园场景的时间比较短,如何生成和维护这些大量的对象。

  因为家园场景中对象太多,进入场景的时候,一次创建会卡帧。体验很不好。有一台比较老的安卓机子,等了好久才加载完这个场景。后面采取的方式是分帧加载。一帧创建几个对象。然后在x秒后能够完成创建完这个场景。这里的x需要各种尝试,应该多大,肯定不行,玩家进入场景的时候明显感觉到到周围是黑色的。如果过小也不行,代表一帧需要加载的数量太多,会卡帧。而且不同的手机性能不同,也会有不一样的效果。

 (2)编辑模式下的各种操作。

原文地址:https://www.cnblogs.com/tom987690183/p/7250003.html