繁体   English   中英

当值为 Object 时,如何在 Unity 中使用 C# 字典通过值查找键

[英]How do I find key through value with C# dictionary in Unity when value is an Object

我花了几天时间试图找到一种方法来访问我在 Unity 中制作的游戏的棋子所在的方块。 我想要做的是点击一个棋子并将其正方形返回给我。 我目前正在尝试通过获取我单击的正方形的坐标并检查字典中的值来做到这一点。 然而,每次它都会返回 null。我的课程如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;

public class Game : MonoBehaviour
{
    public GameObject white_king, white_queen, white_rook, white_bishop, white_knight, white_pawn;
    public GameObject black_king, black_queen, black_rook, black_bishop, black_knight, black_pawn;
    public static bool whiteMove;
    public static bool blackMove;


    public string[] squareNames = {"a1", "a2", "a3", "a4", "a5", "a6", "a7", "a8",
    "b1", "b2", "b3", "b4", "b5", "b6", "b7", "b8",
    "c1", "c2", "c3", "c4", "c5", "c6", "c7", "c8",
    "d1", "d2", "d3", "d4", "d5", "d6", "d7", "d8",
    "e1", "e2", "e3", "e4", "e5", "e6", "e7", "e8",
    "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8",
    "g1", "g2", "g3", "g4", "g5", "g6", "g7", "g8",
    "h1", "h2", "h3", "h4", "h5", "h6", "h7", "h8"};

    public static Dictionary<string, Coordinates> squares = new Dictionary<string, Coordinates>();
    public static Dictionary<Coordinates, string> square_references = new Dictionary<Coordinates, string>();

    private double x = -3.94;
    private double y = -3.92;

    public void Start()
    {
      whiteMove = true;
      blackMove = false;
        for (int i = 0; i < 64; i++)
        {
        if (i % 8 == 0 && i > 0)
        {
            y = -3.94;
            x += 1.1;
        }
        squares.Add(squareNames[i], new Coordinates(x, y));
        square_references.Add(new Coordinates(x, y), squareNames[i]);
        y += 1.1;

        }

        Instantiate(white_queen, new Vector3((float) squares["d1"].getX(), (float) squares["d1"].getY(), -2), Quaternion.identity);
        Instantiate(white_king, new Vector3((float) squares["e1"].getX(), (float) squares["e1"].getY(), -2), Quaternion.identity);
        Instantiate(white_rook, new Vector3((float) squares["a1"].getX(), (float) squares["a1"].getY(), -2), Quaternion.identity);
        Instantiate(white_rook, new Vector3((float) squares["h1"].getX(), (float) squares["h1"].getY(), -2), Quaternion.identity);
        Instantiate(white_bishop, new Vector3((float) squares["c1"].getX(), (float) squares["c1"].getY(), -2), Quaternion.identity);
        Instantiate(white_bishop, new Vector3((float) squares["f1"].getX(), (float) squares["f1"].getY(), -2), Quaternion.identity);
        Instantiate(white_knight, new Vector3((float) squares["b1"].getX(), (float) squares["b1"].getY(), -2), Quaternion.identity);
        Instantiate(white_knight, new Vector3((float) squares["g1"].getX(), (float) squares["g1"].getY(), -2), Quaternion.identity);
        Instantiate(white_pawn, new Vector3((float) squares["a2"].getX(), (float) squares["a2"].getY(), -2), Quaternion.identity);
        Instantiate(white_pawn, new Vector3((float) squares["b2"].getX(), (float) squares["b2"].getY(), -2), Quaternion.identity);
        Instantiate(white_pawn, new Vector3((float) squares["c2"].getX(), (float) squares["c2"].getY(), -2), Quaternion.identity);
        Instantiate(white_pawn, new Vector3((float) squares["d2"].getX(), (float) squares["d2"].getY(), -2), Quaternion.identity);
        Instantiate(white_pawn, new Vector3((float) squares["e2"].getX(), (float) squares["e2"].getY(), -2), Quaternion.identity);
        Instantiate(white_pawn, new Vector3((float) squares["f2"].getX(), (float) squares["f2"].getY(), -2), Quaternion.identity);
        Instantiate(white_pawn, new Vector3((float) squares["g2"].getX(), (float) squares["g2"].getY(), -2), Quaternion.identity);
        Instantiate(white_pawn, new Vector3((float) squares["h2"].getX(), (float) squares["h2"].getY(), -2), Quaternion.identity);
        Instantiate(black_king, new Vector3((float) squares["d8"].getX(), (float) squares["d8"].getY(), -2), Quaternion.identity);
        Instantiate(black_queen, new Vector3((float) squares["e8"].getX(), (float) squares["e8"].getY(), -2), Quaternion.identity);
        Instantiate(black_rook, new Vector3((float) squares["a8"].getX(), (float) squares["a8"].getY(), -2), Quaternion.identity);
        Instantiate(black_rook, new Vector3((float) squares["h8"].getX(), (float) squares["h8"].getY(), -2), Quaternion.identity);
        Instantiate(black_bishop, new Vector3((float) squares["c8"].getX(), (float) squares["c8"].getY(), -2), Quaternion.identity);
        Instantiate(black_bishop, new Vector3((float) squares["f8"].getX(), (float) squares["f8"].getY(), -2), Quaternion.identity);
        Instantiate(black_knight, new Vector3((float) squares["b8"].getX(), (float) squares["b8"].getY(), -2), Quaternion.identity);
        Instantiate(black_knight, new Vector3((float) squares["g8"].getX(), (float) squares["g8"].getY(), -2), Quaternion.identity);
        Instantiate(black_pawn, new Vector3((float) squares["a7"].getX(), (float) squares["a7"].getY(), -2), Quaternion.identity);
        Instantiate(black_pawn, new Vector3((float) squares["b7"].getX(), (float) squares["b7"].getY(), -2), Quaternion.identity);
        Instantiate(black_pawn, new Vector3((float) squares["c7"].getX(), (float) squares["c7"].getY(), -2), Quaternion.identity);
        Instantiate(black_pawn, new Vector3((float) squares["d7"].getX(), (float) squares["d7"].getY(), -2), Quaternion.identity);
        Instantiate(black_pawn, new Vector3((float) squares["e7"].getX(), (float) squares["e7"].getY(), -2), Quaternion.identity);
        Instantiate(black_pawn, new Vector3((float) squares["f7"].getX(), (float) squares["f7"].getY(), -2), Quaternion.identity);
        Instantiate(black_pawn, new Vector3((float) squares["g7"].getX(), (float) squares["g7"].getY(), -2), Quaternion.identity);
        Instantiate(black_pawn, new Vector3((float) squares["h7"].getX(), (float) squares["h7"].getY(), -2), Quaternion.identity);
       
    }





}

public class Coordinates
{
  public double x;
  public double y;

  public double getX()
  {
    return this.x;
  }

  public double getY()
  {
    return this.y;
  }

  public Coordinates(double x, double y)
  {
    this.x = x;
    this.y = y;
  }



}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using System.Linq;

public class Piece : MonoBehaviour
{
    public GameObject moveSquare;
    private bool isDragging;
    Vector2 startPosition;
    string square; 

    void Start()
    {
        startPosition = transform.position;
    }

    private void OnMouseDown()
    {
        if((getColour().Equals("white") && Game.whiteMove) || ((getColour().Equals("black") && !Game.whiteMove)))
        {
            isDragging = true;
            CreateMoveSquare("f5");
            Debug.Log(getSquare());
        
        }
        
    }

    private void OnMouseUp()
    {
        if((getColour().Equals("white") && Game.whiteMove) || ((getColour().Equals("black") && Game.blackMove)))
        {
                isDragging = false;
                DestroyMoveSquares();
                
            if(getColour().Equals("white"))
            {
                Game.whiteMove = false;
                Game.blackMove = true;
            }
            else
            {
                Game.whiteMove = true;
                Game.blackMove = false;
            
            }
        }
        
        
        
    }

    void Update()
    {
        if(isDragging)
        {
            Vector2 mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition) - transform.position;
            transform.Translate(mousePosition);
        }
    }

    private string getColour()
    {
        
        switch (this.name)
        {
            case "black_queen 1(Clone)": return "black";
            case "black_knight(Clone)": return "black"; 
            case "black_bishop(Clone)": return "black"; 
            case "black_king(Clone)": return "black"; 
            case "black_rook(Clone)": return "black"; 
            case "black_pawn(Clone)": return "black"; 
        }

        return "white";


    }

    private string getSquare()
    {
        string myKey = Game.squares.FirstOrDefault(x => x.Value.x == (double) transform.position.x && x.Value.y == (double) transform.position.y).Key;
        return myKey;           
    }

    public void DestroyMoveSquares()
    {

        GameObject[] movePlates = GameObject.FindGameObjectsWithTag("MoveSquare");
        for (int i = 0; i < movePlates.Length; i++)
        {
            Destroy(movePlates[i]); 
        }
    }

    public void CreateMoveSquare(string square)
    {
        GameObject ms = Instantiate(moveSquare, new Vector3((float) Game.squares[square].getX(), (float) Game.squares[square].getY(), -3), Quaternion.identity);
    }

    


}

在这个例子中,无论我点击哪个部分,Debug.Log(getSquare()) 都会给我 null。 我尝试使用 square_references 通过 mykey = Game.square_refenreces[new Coordinates[transform.position.x, transform.position.y] 获取密钥,但它也返回 null。我尝试了很多其他方法,但似乎没有任何效果。 有谁知道为什么或我能做些什么来完成这项工作?

问题 A - 浮点精度

通常永远不要使用==来比较浮点格式,如doublefloatdecimal if(5 * 0.2 / 10 == 1f)之类的东西可能由于浮点精度而失败,实际上可能是0.999999991.000000001

通常你宁愿使用像if(Math.Abs(ab) <= double.Epsilon)这样的近似值,其中double.Epsilon基本上是 double 可以不同于0的最小值。

问题 B - 字典的引用相等性

此外,如果类型覆盖GetHashCode ,则默认情况下对于引用类型Dictionary在给定键上使用引用相等性

你做

squares.Add(squareNames[i], new Coordinates(x, y));
square_references.Add(new Coordinates(x, y), squareNames[i]);

在这里你生成两个不同Coordinates实例!

正如你所说,你也曾尝试过

mykey = Game.square_refenreces[new Coordinates(transform.position.x, transform.position.y)];

您又在哪里创建了一个新实例 这个新实例和字典中的两个实例引用不相等,因此字典找不到给定的键。


(首选)解决方案:只需使用 Vector2!

然而,看看你的价值观……你真的需要double吗? 为什么不使用floattransform.position (基本上 Unity 中的其他所有东西都只使用float )?

然后在实际坐标的用例中,为什么不简单地使用Vector2已经提供了这样的近似值(甚至更好): Vector2 ==的精度为0.00001

此外,它还已经实现了GetHashCode ,如您在源代码中所见

 // used to allow Vector2s to be used as keys in hash tables public override int GetHashCode() { return x.GetHashCode() ^ (y.GetHashCode() << 2); })

这直接允许您将其用作地图(= 字典)中的键!

最后, Vector2Vector3之间已经存在隐式来回转换,因此您可以直接使用实例化的返回值和当前的 position 作为键。

所以你可以简单地做

public static Dictionary<string, Vector2> squares = new Dictionary<string, Vector2>();
public static Dictionary<Vector2, string> square_references = new Dictionary<Vector2, string>();

private float x = -3.94f;
private float y = -3.92f;

public void Start()
{
    whiteMove = true;
    blackMove = false;
    for (int i = 0; i < 64; i++)
    {
        if (i % 8 == 0 && i > 0)
        {
            y = -3.94f;
            x += 1.1f;
        }

        squares.Add(squareNames[i], new Vectro2(x, y));
        square_references.Add(new Vectro2(x, y), squareNames[i]);
        y += 1.1f;
    }

    Instantiate(white_queen, squares["d1"] - Vector3.forward * 2), Quaternion.identity);
    ....
}
   

然后使用例如

private string getSquare()
{
    // As said where == uses approximation with a precision of 0.00001
    string myKey = Game.squares.FirstOrDefault(x=> x.Value == transform.position).Key;
    return myKey;  
} 

或者直接因为内部 Vector2 已经实现了提到的GetHashCode ,字典将使用它来定义给定键和现有键之间的相等性。

private string getSquare()
{
    string myKey = Game.square_references[transform.position];
    return myKey;  
} 

(备选)解决方案 - 使用 IEquatable/自定义 == 运算符或 GetHashCode

因此,如果出于某种原因你想坚持你的自定义类型,我会让它实现提到IEquatableGetHashcode类的

public class Coordinates
{
    public double x;
    public double y;

    public double getX()
    {
        return this.x;
    }

    public double getY()
    {
        return this.y;
    }

    public Coordinates(double x, double y)
    {
        this.x = x;
        this.y = y;
    }
    
    public static bool operator  == (Coordinates a, Coordinates b)
    {
        return Math.Abs(a.x - b.x) <= double.Epsilon && Math.Abs(a.y - b.y) <= double.Epsilon;
    }

    public static bool operator !=(Coordinates a, Coordinates b)
    {
        return !(a == b);
    }

    public bool Equals(Coordinates other)
    {
        if (ReferenceEquals(null, other)) return false;
        if (ReferenceEquals(this, other)) return true;

        return this == other;
    }

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        if (ReferenceEquals(this, obj)) return true;
        if (obj.GetType() != this.GetType()) return false;

        return Equals((Coordinates) obj);
    }

    public override int GetHashCode()
    {
        unchecked
        {
            return (x.GetHashCode() * 397) ^ y.GetHashCode();
        }
    }
}

现在你可以使用

private string getSquare()
{
    string myKey = Game.squares.FirstOrDefault(x => x.Value == new Coordinates(transform.position.x,transform.position.y)).Key;
    // or also
    //string myKey = Game.squares.FirstOrDefault(x => x.Value.Equals(new Coordinates(transform.position.x,transform.position.y))).Key;
    return myKey;           
}

或直接使用

private string getSquare()
{
    string myKey = Game.square_references[new Coordinates(transform.position.x,transform.position.y)];
    return myKey;           
}

请注意,尽管我很确定GethashCode方法很可能仍会由于浮点精度而失败。

在深入了解您的代码(乍一看很长)之前,我会发表一些评论,以防它们有帮助。 您的代码中有从doublefloat的强制转换,反之亦然。 小心那些,相等比较器==因为转换为较低精度的类型可能会导致精度损失,并且这可能会给找回以前保存的值带来问题。 您可以检查以进一步了解。 从那里“遵循不同代码路径并以不同方式操作的计算值通常被证明是不相等的”示例。 因此,即使不涉及转换,比较浮点数也很棘手。

我可以做的另一个评论是,如果我不熟悉/不确定 Linq,要找到检索你的值,我会尝试用for循环找到我的值,并调试循环数据结构。 调试你可以“手动”检查你的数据是否存在,你可以缩小问题范围以检查问题是否是数据不在要查找的数据结构中,或者标准是否不匹配(测试平等)。 解决该问题后,我会 go 并尝试使用整洁的 linq 行直接提供我想要的内容。 这意味着将您的问题分解成更小的问题可能会有用:)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM