[英]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?
有谁知道为什么或我能做些什么来完成这项工作?
In general never use ==
for comparing floating point formats like double
or float
or decimal
.通常永远不要使用
==
来比较浮点格式,如double
或float
或decimal
。 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.99999999
或1.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
的最小值。
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.
这个新实例和字典中的两个实例引用不相等,因此字典找不到给定的键。
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
)?为什么不使用
float
自transform.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.最后,
Vector2
和Vector3
之间已经存在隐式来回转换,因此您可以直接使用实例化的返回值和当前的 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;
}
So if for some reason you want to stick to your custom type I would make it implement mentioned IEquatable
and GetHashcode
like因此,如果出于某种原因你想坚持你的自定义类型,我会让它实现提到
IEquatable
和GetHashcode
类的
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.您的代码中有从
double
到float
的强制转换,反之亦然。 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.