简体   繁体   English

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

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

I have spent days trying to find a way to access the square my chess pieces are on for a game I am making in Unity.我花了几天时间试图找到一种方法来访问我在 Unity 中制作的游戏的棋子所在的方块。 What I want to do is click on a chess piece and have its square returned to me.我想要做的是点击一个棋子并将其正方形返回给我。 I am currently trying to do this by getting the coordinates of the square that I click on and checking the value in a dictionary.我目前正在尝试通过获取我单击的正方形的坐标并检查字典中的值来做到这一点。 Every time, however, it returns null. My classes are as follows:然而,每次它都会返回 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);
    }

    


}

In this instance Debug.Log(getSquare()) gives me null no matter which pieces I click on.在这个例子中,无论我点击哪个部分,Debug.Log(getSquare()) 都会给我 null。 I have tried using square_references to get the the key through mykey = Game.square_refenreces[new Coordinates[transform.position.x, transform.position.y] but it also returns null. I have tried many other things but nothing seems to work.我尝试使用 square_references 通过 mykey = Game.square_refenreces[new Coordinates[transform.position.x, transform.position.y] 获取密钥,但它也返回 null。我尝试了很多其他方法,但似乎没有任何效果。 Does anyone know why or what I can do to make this work?有谁知道为什么或我能做些什么来完成这项工作?

Issue A - Floating point precision问题 A - 浮点精度

In general never use == for comparing floating point formats like double or float or decimal .通常永远不要使用==来比较浮点格式,如doublefloatdecimal Something like if(5 * 0.2 / 10 == 1f) might fail due to floating point precision and might actually be 0.99999999 or 1.000000001 ! if(5 * 0.2 / 10 == 1f)之类的东西可能由于浮点精度而失败,实际上可能是0.999999991.000000001

Usually you would rather use an approximation like if(Math.Abs(ab) <= double.Epsilon) where double.Epsilon is basically the smallest value a double can differ from 0 .通常你宁愿使用像if(Math.Abs(ab) <= double.Epsilon)这样的近似值,其中double.Epsilon基本上是 double 可以不同于0的最小值。

Issue B - Reference equality for Dictionary问题 B - 字典的引用相等性

Further, if a type does not override GetHashCode then by default for reference types Dictionary uses reference equality on the given keys.此外,如果类型覆盖GetHashCode ,则默认情况下对于引用类型Dictionary在给定键上使用引用相等性

You do你做

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

where you generate two different instances of Coordinates !在这里你生成两个不同Coordinates实例!

And as you said you also tried then to do正如你所说,你也曾尝试过

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

where again you create a new instance .您又在哪里创建了一个新实例 This new instance and the two ones in the dictionary are not reference equal so the dictionary can not find the given key.这个新实例和字典中的两个实例引用不相等,因此字典找不到给定的键。


(Preferred) Solution: Simply Use Vector2! (首选)解决方案:只需使用 Vector2!

However, looking at you values... do you really need double at all?然而,看看你的价值观……你真的需要double吗? Why not using float since transform.position (and basically everything else in Unity anyway only uses float )?为什么不使用floattransform.position (基本上 Unity 中的其他所有东西都只使用float )?

And then in your use case for actual coordinates why not simply use Vector2 which already provides such an approximation (even better): Vector2 == has an precision of 0.00001 .然后在实际坐标的用例中,为什么不简单地使用Vector2已经提供了这样的近似值(甚至更好): Vector2 ==的精度为0.00001

And additionally it also already implements GetHashCode as you can see in the source code此外,它还已经实现了GetHashCode ,如您在源代码中所见

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

which directly allows you to use it as a key in maps (= Dirctionary)!这直接允许您将其用作地图(= 字典)中的键!

And finally there already exists an implicit conversion forth and back between Vector2 and Vector3 so you can directly use the return value for the instantiate and the current position as the key.最后, Vector2Vector3之间已经存在隐式来回转换,因此您可以直接使用实例化的返回值和当前的 position 作为键。

So you could simply do所以你可以简单地做

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

and then use eg然后使用例如

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

or simply directly since internally Vector2 already implements mentioned GetHashCode which the dictionary will use to define equality between given key and the existing keys.或者直接因为内部 Vector2 已经实现了提到的GetHashCode ,字典将使用它来定义给定键和现有键之间的相等性。

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

(Alternative) Solution - Use IEquatable/Custom == operator or GetHashCode (备选)解决方案 - 使用 IEquatable/自定义 == 运算符或 GetHashCode

So if for some reason you want to stick to your custom type I would make it implement mentioned IEquatable and GetHashcode like因此,如果出于某种原因你想坚持你的自定义类型,我会让它实现提到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();
        }
    }
}

and now you can either use现在你可以使用

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

or directly use或直接使用

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

Note though that I'm pretty sure that the GethashCode way would most probably still fail due to floating point precision.请注意,尽管我很确定GethashCode方法很可能仍会由于浮点精度而失败。

Before diving through your code which is quite long at a first glance I'll make a couple of comments in case they're helpfull.在深入了解您的代码(乍一看很长)之前,我会发表一些评论,以防它们有帮助。 There are casts in your code from double to float and viceversa.您的代码中有从doublefloat的强制转换,反之亦然。 Carefull with those, and the equality comparer == as casting to a lower precision type might make inccur in precision loss, and that might give problem finding back a previously kept value.小心那些,相等比较器==因为转换为较低精度的类型可能会导致精度损失,并且这可能会给找回以前保存的值带来问题。 You can check this for further understanding.您可以检查以进一步了解。 From there "Calculated values that follow different code paths and that are manipulated in different ways often prove to be unequal" with examples.从那里“遵循不同代码路径并以不同方式操作的计算值通常被证明是不相等的”示例。 So even if there is no casting involved, comparing floats is tricky.因此,即使不涉及转换,比较浮点数也很棘手。

Another comment I can make is that to find retrieve your values, if I was not familiar/sure of Linq, I would try to find my value with a for loop, and debug the looped through data structure.我可以做的另一个评论是,如果我不熟悉/不确定 Linq,要找到检索你的值,我会尝试用for循环找到我的值,并调试循环数据结构。 Debugging you can check "manually" if your data is ther and you can narrow down the problem to check if the problem is that the data is not in the data structure to look in, or if there is a mismatch in the criteria (testing for equality).调试你可以“手动”检查你的数据是否存在,你可以缩小问题范围以检查问题是否是数据不在要查找的数据结构中,或者标准是否不匹配(测试平等)。 After that problem is solved, I would go and try to have the neat linq line that provides what I want directly.解决该问题后,我会 go 并尝试使用整洁的 linq 行直接提供我想要的内容。 Meaning that it might be useful to slice down your problem into smaller ones:)这意味着将您的问题分解成更小的问题可能会有用:)

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

相关问题 当值是C#中的对象列表时,如何查找字典的键? - How to find the key of a dictionary when the value is a list of objects in C#? 如何从包含值元组作为 C# 键的字典中提取值? - How do I extract a value out of a dictionary that contains a value tuple as the key in C#? 通过目录中的键访问值 <system.String, system.Object> -C#Unity字典 - Accessing value by key in Directory<system.String, system.Object> - C# Unity Dictionary 如何使字典按值查找对象键 - How to make Dictionary find object key by value C#如何根据字典的键将字典的值添加到变量中? - C# How do I add the Value of a Dictionary to a variable based on its Key? 如何根据C#.NET Silverlight中Dictionary中的键值对字典列表进行排序? - How do I sort a List of Dictionaries based off of a value in a key within the Dictionary in C# .NET Silverlight? Unity,C#如果值是列表,如何获取字典的值 - Unity, c# how to get value of dictionary if the value is a list C# - 如何在具有属性的枚举中找到键值? - C# - how do I find a key value inside an enum with a property? C# - 使用键从字典中获取值 - 返回对象的属性? - C# - Get value from dictionary with key - return property of object? 如何从C#中的Dictionary(key,value)中检索特定值 - How to retrieve the specific value from Dictionary(key,value) in c#
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM