前一段时间写了一篇 背包大乱斗与俄罗斯方块(设计篇) ,具体的实现思路在这一文中已经讲清楚了,后来我抽空去实现了一版。目前看效果还不错。
已经实现,形状的变换,定位,移动,消除,障碍判定等。
本篇稍微讲一下具体的实现过程,以及如何去优化这个算法。
基于池去实现节点的创建与回收
一开始就基于这个池模板去管理所有数量上较多的对象,后期优化的压力会小一些。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| public interface IReset { void Reset(); }
public interface IPool<T> where T : IReset { Stack<T> nodesPool { get; }
T CreateOne<W>() where W : T, new(); void ReturnOne(T item); }
public class BasePool<T> : IPool<T> where T : IReset { Stack<T> _nodesPool = new Stack<T>(); public Stack<T> nodesPool { get { return _nodesPool; } }
public virtual T CreateOne<W>() where W : T, new() { if (nodesPool.Count > 0) { return nodesPool.Pop(); } return new W(); }
public virtual void ReturnOne(T node) { node.Reset(); nodesPool.Push(node); } }
|
分离算法与表现
我们的算法是需要适应不同的场景的,如果基于一套UI或者3D/2D渲染,混写代码,就会导致这个代码复用性低,迁移起来费时费力。
TileInfo.cs 作为管理单个形状( 物品) 渲染信息的最小单位。我们并不需要在这里书写任何如何去渲染的逻辑。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public class TileInfo : IReset { public List<GameObject> Cubes = new List<GameObject>(); public GameObject Tile; public Color BaseColor;
public void Reset() { BaseColor = Color.white;
if (Cubes.Count > 0) { for (int i = 0; i < Cubes.Count; i++) PrefabPoolManager.GetInstance().PushGameObjectByType(PrefabPoolManager.PrefabType.Cube, Cubes[i]); Cubes.Clear(); } PrefabPoolManager.GetInstance().PushGameObjectByType(PrefabPoolManager.PrefabType.Tile, Tile); Tile = null; } }
|
将这个逻辑放到一个单独的Render脚本中,这样处理现在我们已经将渲染画面的功能完全隔离到了 BlockRender。
如果我们有3D的画面,就可以写一个3DBlockRender 或者是 UIBlockRender ,只需要抽象出接口做新的实现即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class BlockRender { public TileInfo UpdatePreSelectNode(PreSelect node, LogicMap map) { return UpdateTile(node, node.Shape, node.x, node.y, map); }
public TileInfo UpdatePreSelectNode(PreSelect node, IShape shap, LogicMap map) { return UpdateTile(node, shap, node.x,node.y,map); } ...
|
至此我们已经完成了基础逻辑,他包含一个通用的池实现,与一个通用的渲染层。
核心算法
一般来说游戏的业务逻辑复杂度都不高,真要说复杂的,那肯定是渲染逻辑。
这段放置图形的代码,就是背包大乱斗最复杂的业务逻辑了。
通过当前节点的相对点加图形的数据结构中存储的x与y值,就可以推算出逻辑节点的坐标。
注意这边的逻辑节点,需要配置map的信息,比如map的位置信息与缩放进行一个定位,才能换算出真实坐标。
当然下面的代码并没有添加,是否这个位置有阻挡或者已经被占用的判定,由于我们的玩法尚未定型,则将这个判定放到了渲染层,在最后的演示中,你可以看到如果两个图形有重叠部分,重叠部分的区域会变成红色。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| public INode PlaceShape(INode node,IShape shape) { node.Shape = shape;
var data = shape.Data; var rows = data.GetLength(1); var columns = data.GetLength(0);
for (int y = 0; y < rows; y++) { for (int x = 0; x < columns; x++) { if (data[x, y] == 0) continue;
INode usedNode = GetNodeWithXY(node.x + x, node.y + y); usedNode.State = NodeState.CUBE; } }
return node; }
|
演示
通过空格键可以在左下角创建出方块,wasd去移动,qe可以转换方向,再次空格键可以放下方块。
页面有9M大小,加载较慢。演示地址 : 点我转跳
我尝试在下方加载了一个 iframe ,如果能正常显示的话就不用再转跳到上面的链接了。-
现在上面的演示地址失效,我用来放置游戏demo,当然这个demo也是用如上算法,欢迎参考,关注。
本文标题:背包大乱斗与俄罗斯方块(代码篇)
文章作者:Keyle
发布时间:2024-11-21
最后更新:2024-11-26
原始链接:https://vrast.cn/posts/55960/
版权声明:©Keyle's Blog. 本站采用署名-非商业性使用-相同方式共享 4.0 国际进行许可