通用网格框架实现

时间:2017-03-12 20:05:05

标签: c# generics unity3d architecture software-design

我正在研究在我的视频游戏中使用的通用网格框架,其中网格由接口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>>"

但是我希望我的BasicPositionRuleRectGrid2D是唯一的,但会抛出错误:“参数#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 
    }
}

但我真的想弄清楚如何将定位规则约束到特定的网格类型。

2 个答案:

答案 0 :(得分:1)

RectGrid2D同时实施IGridIGridPositionRule怎么样?

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);
    }
}