c# - 当值是对象时,如何在 Unity 中使用 C# 字典通过值查找键
问题描述
我花了几天的时间试图找到一种方法来访问我在 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 - 浮点精度
通常从不用于==
比较浮点格式,如double
orfloat
或decimal
。if(5 * 0.2 / 10 == 1f)
由于浮点精度,类似的东西可能会失败,实际上可能是0.99999999
or 1.000000001
!
通常你宁愿使用一个近似值,比如if(Math.Abs(a-b) <= double.Epsilon)
wheredouble.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
吗?为什么不使用float
since transform.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); })
这直接允许您将其用作地图中的键(= 目录)!
最后,在 and 之间已经存在隐式转换Vector2
,Vector3
因此您可以直接使用实例化的返回值和当前位置作为键。
所以你可以简单地做
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/Custom == 运算符或 GetHashCode
因此,如果由于某种原因你想坚持你的自定义类型,我会让它实现提到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();
}
}
}
现在你可以使用
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
由于浮点精度,这种方式很可能仍然会失败。
推荐阅读
- javascript - 使用匹配的对象值查找和替换图像 src
- sql - SQL 通配符 - 尝试在字符串中查找一系列字符/数字
- node.js - nodemon ignore 无法使用 package.json
- ios - 在 Swift3 iOS 中播放多个视频时,AVPlayer 显示黑屏
- animation - 如何在 Flutter 中制作两个 GridView 元素的切换动画
- amazon-web-services - 如何使用 Route53 将子域指向同一个 S3 存储桶?
- python-3.x - 单词建议 Python
- r - R:使用 gsub() 函数没有替换任何内容
- java - JUnit 5 参数化测试:将 CSVFileSource 与 Enclosed.Class 一起使用
- magento2 - 每个实例的 Magento 2 Varnish 缓存