我正在研究在我的视频游戏中使用的通用网格框架,其中网格由接口IGrid <T>
定义,T被约束为GridCell
。网格必须实现一个Generate
方法来实例化单元格,然后GridPositioniningRule
负责处理每个单元格在网格中的位置。这是我遇到麻烦的地方,我正在处理RectGrid2D
并且我有一个规则BasicPositionRule
,它只能与RectGrid2D
一起使用,因为它需要知道网格列+行但我无法弄清楚如何将我的规则约束到RectGrid
。
到目前为止,这是我的代码库:
using System.Collections.Generic;
using System;
using System.Linq;
using UnityEngine;
public interface IGrid<T> where T : GridCell
{
Dictionary<GridCoordinate, T> grid { get; set; }
/// <summary>
/// populates the grid with cell type t + a shiny callback to handle each cell after instantiation
/// </summary>
void Generate(T t, IGridPositionRule<T, IGrid<T>> positionRule, System.Action<int, int, T> callback);
/// <summary>
/// returns the cell at given grid coordinate
/// </summary>
T Fetch(GridCoordinate coord);
}
public partial class RectGrid2D <T> : MonoBehaviour, IGrid<T> where T : GridCell
{
public int rows;
public int columns;
public Dictionary<GridCoordinate, T> grid { get; set; }
public void Generate(T t, IGridPositionRule<T, IGrid<T>> positionRule, Action<int, int, T> callback)
{
grid = new Dictionary<GridCoordinate, T>();
for (int x = 0; x < rows; x++)
{
for (int y = 0; y < columns; y++)
{
var coord = new GridCoordinate(x, y);
var cell = GameObject.Instantiate(t);
grid.Add(coord, cell);
positionRule.PositionCell(cell, this, x, y);
System.Action<int, int, T> _callback = callback;
if (_callback != null)
{
_callback(x, y, cell);
}
}
}
}
/// <summary>
/// returns the cell at given grid coordinate
/// </summary>
public T Fetch(GridCoordinate coord)
{
return grid[coord];
}
/// <summary>
/// Returns a grid cell given T
/// </summary>
public GridCoordinate CoordinateFromCell(T cell)
{
return grid.FirstOrDefault(x => x.Value == cell).Key;
}
}
public struct GridCoordinate
{
public int x;
public int y;
public GridCoordinate (int _x, int _y)
{
this.x = _x;
this.y = _y;
}
}
public class GridCell : MonoBehaviour
{
}
public interface IGridPositionRule <T, U> where T : GridCell where U : IGrid <T>
{
void PositionCell(T cell, U grid, int x, int y);
}
public class BasicPositionRule : IGridPositionRule<GridCell, RectGrid2D<GridCell>>
{
public void PositionCell(GridCell cell, RectGrid2D<GridCell> grid, int x, int y)
{
// do math to position cells for rect grid type pattern
}
}
public partial class GridExample : MonoBehaviour
{
[SerializeField] private GridCell gridCell; // prefab
private void Awake()
{
RectGrid2D<GridCell> grid = GetComponent<RectGrid2D<GridCell>>();
grid.Generate(gridCell, new BasicPositionRule(), (x, y, cell) => // ERROR HERE
{
// do something with grid cell
});
}
}
现在会抛出错误"Argument #2 cannot convert BasicPositionRule expression to type IGridPositionRule<GridCell,IGrid<GridCell>>"
但是我希望我的BasicPositionRule
对RectGrid2D
是唯一的,但会抛出错误:“参数#2' cannot convert
BasicPositionRule'表达式,以键入`IGridPositionRule&gt;'”
错误可以通过不将BasicPositionRule
限制为RectGrid2D
来解决
喜欢:
public class BasicPositionRule : IGridPositionRule<GridCell, IGrid<GridCell>>
{
public void PositionCell(GridCell cell, IGrid<GridCell> grid, int x, int y)
{
// do math to position cells for rect grid type pattern
}
}
但我真的想弄清楚如何将定位规则约束到特定的网格类型。
答案 0 :(得分:1)
让RectGrid2D
同时实施IGrid
和IGridPositionRule
怎么样?
using System.Collections.Generic;
using System;
using System.Linq;
using UnityEngine;
public interface IGrid<T> where T : GridCell
{
Dictionary<GridCoordinate, T> grid { get; set; }
/// <summary>
/// populates the grid with cell type t + a shiny callback to handle each cell after instantiation
/// </summary>
void Generate(T t, System.Action<int, int, T> callback);
/// <summary>
/// returns the cell at given grid coordinate
/// </summary>
T Fetch(GridCoordinate coord);
}
public partial class RectGrid2D<T> : MonoBehaviour, IGrid<T>, IGridPositionRule<T> where T : GridCell
{
public int rows;
public int columns;
public Dictionary<GridCoordinate, T> grid { get; set; }
public void Generate(T t, Action<int, int, T> callback)
{
grid = new Dictionary<GridCoordinate, T>();
for (int x = 0; x < rows; x++)
{
for (int y = 0; y < columns; y++)
{
var coord = new GridCoordinate(x, y);
var cell = GameObject.Instantiate(t);
grid.Add(coord, cell);
PositionCell(cell, x, y);
System.Action<int, int, T> _callback = callback;
if (_callback != null)
{
_callback(x, y, cell);
}
}
}
}
/// <summary>
/// returns the cell at given grid coordinate
/// </summary>
public T Fetch(GridCoordinate coord)
{
return grid[coord];
}
/// <summary>
/// Returns a grid cell given T
/// </summary>
public GridCoordinate CoordinateFromCell(T cell)
{
return grid.FirstOrDefault(x => x.Value == cell).Key;
}
public void PositionCell(T cell, int x, int y)
{
//define your position rule
}
}
public struct GridCoordinate
{
public int x;
public int y;
public GridCoordinate (int _x, int _y)
{
this.x = _x;
this.y = _y;
}
}
public class GridCell : MonoBehaviour
{
}
public interface IGridPositionRule <T> where T : GridCell
{
void PositionCell(T cell, int x, int y);
}
public partial class GridExample : MonoBehaviour
{
[SerializeField] private GridCell gridCell; // prefab
private void Awake()
{
RectGrid2D<GridCell> grid = GetComponent<RectGrid2D<GridCell>>();
grid.Generate(gridCell, (x, y, cell) =>
{
// do something with grid cell
});
}
}
答案 1 :(得分:0)
在评论中,您说您想要一种将构建规则与网格类分开的方法。但是,由于您只希望某些规则仅用于某些网格,因此实际上是将它们耦合在一起。此外,您正在为特定情况创建一个类RectGrid2D
,使用矩形定位规则。所以你实际上是在定位依赖于网格而不仅仅是某些外部规则。
所以我看到两种可能性:网格和定位规则应该完全解耦或完全耦合。
在第一种情况下,RectGrid2D
类可能是多余的:只存在基类Grid
类并使用外部定位规则。该规则应包含特定于几何的所有内容(布局,查找邻居等)
在第二种情况下,您不需要定位规则,您可以将所有内容放在特定的网格类中,如下所示:
public class GridCell : MonoBehaviour
{
public int x;
public int y;
}
public abstract class Grid : MonoBehaviour
{
public abstract void Generate(GridCell prefab);
public abstract GridCell GetAt(int x, int y);
public abstract List<GridCell> GetNeighboursOf(GridCell cell, List<GridCell> result);
}
public class RectGrid : Grid
{
public int rows;
public int columns;
public RectGrid(int r, int c)
{
rows = r;
columns = c;
}
public override GridCell GetAt(int x, int y) { return null; }
public override List<GridCell> GetNeighboursOf(GridCell cell, List<GridCell> result)
{
// You can change this in an hex grid
result.Add(GetAt(cell.x + 1, cell.y));
result.Add(GetAt(cell.x - 1, cell.y));
result.Add(GetAt(cell.x, cell.y + 1));
result.Add(GetAt(cell.x, cell.y - 1));
return result;
}
// RectGrid knows how to layout its cells
public override void Generate(GridCell prefab)
{
for (int x = 0; x < columns; x++)
{
for (int y = 0; y < rows; y++)
{
// Instantiate prefab
}
}
}
}
public partial class GridExample : MonoBehaviour
{
[SerializeField]
private GridCell gridCell; // prefab
private void Awake()
{
RectGrid grid = GetComponent<RectGrid>();
grid.Generate(gridCell);
}
}