【问题标题】:generic grid framework implementation通用网格框架实现
【发布时间】:2017-03-18 23:07:30
【问题描述】:

我正在开发一个在我的视频游戏中使用的通用网格框架,其中网格由接口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&lt;GridCell,IGrid&lt;GridCell&gt;&gt;"

但我希望我的 BasicPositionRule 专属于 RectGrid2D,但这会引发错误:“Argument #2' cannot convertBasicPositionRule' 表达式以键入 `IGridPositionRule>'”

可以通过不将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 
    }
}

但我真的想弄清楚如何将定位规则限制为特定的网格类型。

【问题讨论】:

  • 抛出的错误cannot convert BasicPositionRule expression to type SM.Grid.IGridPositionRule在哪里?您更改了哪个方法的哪个参数来修复它?
  • @zwcloud 错误位于 GridExample 的第 28 行,在 BasicPositionRule 中将两个 RectGrid2D 定义更改为 IGrid 可修复错误
  • 请编辑代码并注释 line 28...
  • @zwcloud 我不太确定如何在此处执行此操作?但是第 28 行是指 GridExample 中网格实例上的 Generate 方法,所以:grid.Generate(gridCell, new BasicPositionRule(), (x, y, cell) =>
  • 最好提供可编译的代码。我试图让你的代码在 Unity3D 中工作,但失败了。很抱歉,由于代码包含的类型太多,您的描述仍然不清楚。见mvce

标签: c# generics unity3d architecture software-design


【解决方案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
        });
    }
}

【讨论】:

  • 我希望找到一种方法将构建规则与网格分开,如果构建规则在网格类中完成,则不需要存在。
【解决方案2】:

在评论中您说您想要一种将构建规则与网格类分开的方法。但是,由于您只希望某些规则仅在某些网格中使用,因此您实际上是在耦合它们。此外,您正在为使用矩形定位规则的特定情况创建一个类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);
    }
}

【讨论】:

    猜你喜欢
    • 2016-08-30
    • 1970-01-01
    • 2020-06-17
    • 2014-04-19
    • 1970-01-01
    • 1970-01-01
    • 2015-03-24
    • 1970-01-01
    • 2014-12-16
    相关资源
    最近更新 更多