feat: 重构棋盘逻辑和游戏流程,将引擎版本提升至4.3

- 重构了 VirtualBoard 类,优化了棋盘操作和事件处理
- 重写了 ChessPiece 类,增加了事件处理和虚拟棋子逻辑
- 更新了 ChessGame 控制器,增加了结束回合功能和对话框
- 调整了场景布局和资源引用
- 清理了冗余代码和注释
This commit is contained in:
ZZY 2024-11-02 21:18:53 +08:00
parent abace89813
commit 7e2ca3948d
17 changed files with 2984 additions and 503 deletions

17
.gitignore vendored
View File

@ -1,4 +1,19 @@
# Godot 4+ specific ignores # Godot 4+ specific ignores
.godot/ .godot/
.*/
# Godot-specific ignores
.import/
export.cfg
export_presets.cfg
# Imported translations (automatically generated from CSV files)
*.translation
# Mono-specific ignores
.mono/
data_*/
mono_crash.*.json
# MyProject specific ignores
.vscode/
bin/ bin/

View File

@ -1,4 +0,0 @@
[gd_resource type="Theme" format=3 uid="uid://c53fg2fg071yp"]
[resource]
default_font_size = 30

2295
Asserts/defaultTheme.tres Normal file

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,4 @@
<Project Sdk="Godot.NET.Sdk/4.2.2"> <Project Sdk="Godot.NET.Sdk/4.3.0">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<TargetFramework Condition=" '$(GodotTargetPlatform)' == 'android' ">net7.0</TargetFramework> <TargetFramework Condition=" '$(GodotTargetPlatform)' == 'android' ">net7.0</TargetFramework>

View File

@ -1,7 +1,7 @@
[gd_scene load_steps=3 format=3 uid="uid://boa4od72355o4"] [gd_scene load_steps=3 format=3 uid="uid://boa4od72355o4"]
[ext_resource type="Script" path="res://Main.cs" id="1_h4cv2"] [ext_resource type="Script" path="res://Main.cs" id="1_h4cv2"]
[ext_resource type="Theme" uid="uid://c53fg2fg071yp" path="res://Asserts/defalutTheme.tres" id="2_8jwne"] [ext_resource type="Theme" uid="uid://intlbeu8h82r" path="res://Asserts/defaultTheme.tres" id="2_afihr"]
[node name="Main" type="Node2D"] [node name="Main" type="Node2D"]
script = ExtResource("1_h4cv2") script = ExtResource("1_h4cv2")
@ -9,7 +9,7 @@ script = ExtResource("1_h4cv2")
[node name="Control" type="Control" parent="."] [node name="Control" type="Control" parent="."]
layout_mode = 3 layout_mode = 3
anchors_preset = 0 anchors_preset = 0
theme = ExtResource("2_8jwne") theme = ExtResource("2_afihr")
[node name="SignlePlayer" type="Button" parent="Control"] [node name="SignlePlayer" type="Button" parent="Control"]
layout_mode = 0 layout_mode = 0
@ -29,21 +29,21 @@ text = "多人"
[node name="Label" type="Label" parent="Control"] [node name="Label" type="Label" parent="Control"]
layout_mode = 0 layout_mode = 0
offset_left = 210.0 offset_left = 191.0
offset_top = 312.0 offset_top = 265.0
offset_right = 520.0 offset_right = 501.0
offset_bottom = 498.0 offset_bottom = 451.0
theme_override_font_sizes/font_size = 48
text = "中国象棋" text = "中国象棋"
horizontal_alignment = 1 horizontal_alignment = 1
vertical_alignment = 1 vertical_alignment = 1
[node name="Setting" type="Button" parent="Control"] [node name="Setting" type="Button" parent="Control"]
visible = false
layout_mode = 0 layout_mode = 0
offset_left = 249.0 offset_left = 239.0
offset_top = 1034.0 offset_top = 967.0
offset_right = 432.0 offset_right = 450.0
offset_bottom = 1201.0 offset_bottom = 1150.0
text = "设置" text = "设置"
[connection signal="pressed" from="Control/SignlePlayer" to="." method="GoToSignlePlayer"] [connection signal="pressed" from="Control/SignlePlayer" to="." method="GoToSignlePlayer"]

View File

@ -1,7 +1,8 @@
[gd_scene load_steps=3 format=3 uid="uid://g40y10iaf7qb"] [gd_scene load_steps=4 format=3 uid="uid://g40y10iaf7qb"]
[ext_resource type="Script" path="res://Scripts/Controllers/ChessGame.cs" id="1_3x8ac"] [ext_resource type="Script" path="res://Scripts/Controllers/ChessGame.cs" id="1_3x8ac"]
[ext_resource type="PackedScene" uid="uid://b1tx7v3230wab" path="res://Scenes/Entities/Chessboard.tscn" id="1_yheur"] [ext_resource type="PackedScene" uid="uid://b1tx7v3230wab" path="res://Scenes/Entities/Chessboard.tscn" id="1_yheur"]
[ext_resource type="Theme" uid="uid://intlbeu8h82r" path="res://Asserts/defaultTheme.tres" id="3_rcfhx"]
[node name="ChessGame" type="Node2D"] [node name="ChessGame" type="Node2D"]
script = ExtResource("1_3x8ac") script = ExtResource("1_3x8ac")
@ -10,29 +11,47 @@ script = ExtResource("1_3x8ac")
position = Vector2(360, 660) position = Vector2(360, 660)
scale = Vector2(2.5, 2.5) scale = Vector2(2.5, 2.5)
[node name="Undo" type="Button" parent="."] [node name="Control" type="Control" parent="."]
offset_left = 200.0 layout_mode = 3
offset_top = 50.0 anchors_preset = 0
offset_right = 300.0 offset_right = 40.0
offset_bottom = 150.0 offset_bottom = 40.0
theme_override_font_sizes/font_size = 32 theme = ExtResource("3_rcfhx")
text = "撤回"
[node name="ReInit" type="Button" parent="."] [node name="Over" type="Button" parent="Control"]
offset_left = 450.0 layout_mode = 0
offset_top = 50.0 offset_left = 270.0
offset_right = 550.0 offset_top = 1105.0
offset_bottom = 150.0 offset_right = 449.0
theme_override_font_sizes/font_size = 32 offset_bottom = 1209.0
text = "重开" text = "结束回合
(提示对方结束)"
[node name="Home" type="Button" parent="."] [node name="Home" type="Button" parent="Control"]
offset_left = 310.0 layout_mode = 0
offset_top = 1108.0 offset_left = 55.0
offset_right = 412.0 offset_top = 56.0
offset_bottom = 1226.0 offset_right = 157.0
offset_bottom = 164.0
text = "返回主页" text = "返回主页"
[connection signal="pressed" from="Undo" to="." method="Undo"] [node name="Undo" type="Button" parent="Control"]
[connection signal="pressed" from="ReInit" to="." method="ReInit"] layout_mode = 0
[connection signal="pressed" from="Home" to="." method="GoHome"] offset_left = 319.0
offset_top = 58.0
offset_right = 419.0
offset_bottom = 158.0
text = "撤回"
[node name="ReInit" type="Button" parent="Control"]
layout_mode = 0
offset_left = 578.0
offset_top = 55.0
offset_right = 678.0
offset_bottom = 155.0
text = "重开"
[connection signal="pressed" from="Control/Over" to="." method="BtnOver"]
[connection signal="pressed" from="Control/Home" to="." method="GoHome"]
[connection signal="pressed" from="Control/Undo" to="." method="Undo"]
[connection signal="pressed" from="Control/ReInit" to="." method="ReInit"]

View File

@ -1,7 +1,7 @@
[gd_scene load_steps=3 format=3 uid="uid://cl8nkm6j8s8jh"] [gd_scene load_steps=3 format=3 uid="uid://cl8nkm6j8s8jh"]
[ext_resource type="Script" path="res://Scripts/Controllers/Menu.cs" id="1_chiyi"] [ext_resource type="Script" path="res://Scripts/Controllers/Menu.cs" id="1_chiyi"]
[ext_resource type="Theme" uid="uid://c53fg2fg071yp" path="res://Asserts/defalutTheme.tres" id="1_sg40w"] [ext_resource type="Theme" uid="uid://intlbeu8h82r" path="res://Asserts/defaultTheme.tres" id="1_mkeko"]
[node name="Menu" type="Control"] [node name="Menu" type="Control"]
layout_mode = 3 layout_mode = 3
@ -10,7 +10,7 @@ anchor_right = 1.0
anchor_bottom = 1.0 anchor_bottom = 1.0
grow_horizontal = 2 grow_horizontal = 2
grow_vertical = 2 grow_vertical = 2
theme = ExtResource("1_sg40w") theme = ExtResource("1_mkeko")
script = ExtResource("1_chiyi") script = ExtResource("1_chiyi")
metadata/_edit_horizontal_guides_ = [141.0] metadata/_edit_horizontal_guides_ = [141.0]
@ -63,9 +63,10 @@ offset_bottom = 1198.0
[node name="ItemList" type="ItemList" parent="Server"] [node name="ItemList" type="ItemList" parent="Server"]
layout_mode = 0 layout_mode = 0
offset_top = 120.0 offset_left = 15.0
offset_right = 663.0 offset_top = 214.0
offset_bottom = 965.0 offset_right = 678.0
offset_bottom = 1059.0
[node name="ConnectServer" type="Button" parent="Server"] [node name="ConnectServer" type="Button" parent="Server"]
layout_mode = 0 layout_mode = 0
@ -113,10 +114,10 @@ size = Vector2i(200, 118)
[node name="URL" type="LineEdit" parent="."] [node name="URL" type="LineEdit" parent="."]
layout_mode = 0 layout_mode = 0
offset_left = 22.0 offset_left = 37.0
offset_top = 1166.0 offset_top = 290.0
offset_right = 662.0 offset_right = 677.0
offset_bottom = 1247.0 offset_bottom = 371.0
[connection signal="pressed" from="Home" to="." method="goToHome"] [connection signal="pressed" from="Home" to="." method="goToHome"]
[connection signal="pressed" from="Name/Button" to="." method="EnterName"] [connection signal="pressed" from="Name/Button" to="." method="EnterName"]

View File

@ -5,7 +5,10 @@ public partial class ChessGame : Node2D
{ {
ChessBoard board; ChessBoard board;
Global global; Global global;
ConfirmationDialog dialog;
private bool isSession = false; private bool isSession = false;
private Vector2 from;
private Vector2 to;
// Called when the node enters the scene tree for the first time. // Called when the node enters the scene tree for the first time.
public override void _Ready() public override void _Ready()
@ -13,6 +16,12 @@ public partial class ChessGame : Node2D
// Init.Call(); // Init.Call();
global = GetNode<Global>("/root/Global"); global = GetNode<Global>("/root/Global");
board = GetNode<ChessBoard>("Chessboard"); board = GetNode<ChessBoard>("Chessboard");
dialog = new ConfirmationDialog {
DialogAutowrap = true,
MinSize = new Vector2I(400, 200),
Position = new Vector2I(200, 400),
};
AddChild(dialog);
// GetNode<Button>("Undo").Connect("pressed", Callable.From(board.Undo)); // GetNode<Button>("Undo").Connect("pressed", Callable.From(board.Undo));
// GetNode<Button>("ReInit").Connect("pressed", Callable.From(board.ReInit)); // GetNode<Button>("ReInit").Connect("pressed", Callable.From(board.ReInit));
// GetNode<Button>("Home").Connect("pressed", Callable.From(this.GoHome)); // GetNode<Button>("Home").Connect("pressed", Callable.From(this.GoHome));
@ -28,6 +37,11 @@ public partial class ChessGame : Node2D
}; };
board.Set("Hello", Callable.From(() => {GD.PrintErr("hello");})); board.Set("Hello", Callable.From(() => {GD.PrintErr("hello");}));
board.Set("chessMoveFunc", Callable.From((Vector2 newPos, Vector2 fromPos) => { board.Set("chessMoveFunc", Callable.From((Vector2 newPos, Vector2 fromPos) => {
if (from.X == fromPos.X && from.Y == fromPos.Y && to.X == newPos.X && to.Y == newPos.Y) {
return true;
}
from = fromPos;
to = newPos;
var res = global.RPClient.SendSessionToAll(global.sessionId, new Dictionary{ var res = global.RPClient.SendSessionToAll(global.sessionId, new Dictionary{
{"type", "move"}, {"type", "move"},
{"from", fromPos}, {"from", fromPos},
@ -50,23 +64,56 @@ public partial class ChessGame : Node2D
} }
private void SessionMsgHandle(Dictionary msg) { private void SessionMsgHandle(Dictionary msg) {
// GD.PrintErr($"session msg: {msg}"); GD.PrintErr($"session msg: {msg}");
switch (msg["type"].AsString()) { switch (msg["type"].AsString()) {
case "move": case "over":
Vector2 to = new(GD.StrToVar(msg["toX"].ToString()).AsInt32(), if (global.RPClient.GetUserId() == msg["id"].AsString()) {
GD.StrToVar(msg["toY"].ToString()).AsInt32());
Vector2 from = new(GD.StrToVar(msg["fromX"].ToString()).AsInt32(),
GD.StrToVar(msg["fromY"].ToString()).AsInt32());
board.MoveAndRecord(to, from);
break;
case "undo":
board.Undo();
break;
case "reInit":
board.ReInit();
break; break;
} }
dialog.Title = "Opponent Finished";
dialog.DialogText = "Turn On You\n";
dialog.Visible = true;
break;
case "move":
Vector2 _to = new(GD.StrToVar(msg["toX"].ToString()).AsInt32(),
GD.StrToVar(msg["toY"].ToString()).AsInt32());
Vector2 _from = new(GD.StrToVar(msg["fromX"].ToString()).AsInt32(),
GD.StrToVar(msg["fromY"].ToString()).AsInt32());
if (_to.X == to.X && _to.Y == to.Y && _from.X == from.X && _from.Y == from.Y) {
return;
}
to = _to;
from = _from;
board.playerSelf.MoveAndRecord(to, from);
break;
case "undo":
_Undo();
break;
case "reInit":
_ReInit();
break;
}
}
private void _Undo() {
board.playerSelf.Undo();
}
private void _ReInit() {
board.playerSelf.ReInit();
board.InitChessBoard();
}
private void BtnOver() {
GD.PrintErr($"BtnOver {isSession}");
if (isSession == false) {
return;
}
global.RPClient.SendSessionToAll(global.sessionId, new Dictionary{
{"type", "over"},
{"id", global.RPClient.GetUserId()},
});
} }
public void GoHome() { public void GoHome() {
@ -79,9 +126,7 @@ public partial class ChessGame : Node2D
public void Undo() { public void Undo() {
GD.PrintErr($"Undo {isSession}"); GD.PrintErr($"Undo {isSession}");
if (isSession == false) { if (isSession == false) {
GD.PrintErr($"Undo ??"); _Undo();
board.Undo();
return;
} }
global.RPClient.SendSessionToAll(global.sessionId, new Dictionary{ global.RPClient.SendSessionToAll(global.sessionId, new Dictionary{
{"type", "undo"}, {"type", "undo"},
@ -91,8 +136,7 @@ public partial class ChessGame : Node2D
public void ReInit() { public void ReInit() {
GD.PrintErr($"ReInit {isSession}"); GD.PrintErr($"ReInit {isSession}");
if (isSession == false) { if (isSession == false) {
board.ReInit(); _ReInit();
return;
} }
global.RPClient.SendSessionToAll(global.sessionId, new Dictionary{ global.RPClient.SendSessionToAll(global.sessionId, new Dictionary{
{"type", "reInit"}, {"type", "reInit"},

View File

@ -0,0 +1,127 @@
using Godot;
public class Player
{
private VirtualBoard board;
private SelectedPiece selectedNode;
private MoveRecords<VirtualPiece> moveRecords;
public enum PlayerType {
Human,
AI
}
public Player(VirtualBoard board, PlayerType type = PlayerType.Human)
{
this.board = board;
this.selectedNode = new SelectedPiece(board);
this.moveRecords = new MoveRecords<VirtualPiece>(onUndoRecordCallback: (newNode, oldNode, newPos, oldPos) => {
GD.Print("Undo: ", newNode, "->", oldNode, ":", newPos, "->", oldPos);
VirtualPiece newPiece = newNode;
VirtualPiece oldPiece = oldNode;
this.board.MovePiece(newPos, oldPos);
if (newPiece != null) {
this.board.InsertPiece(newPiece, newPos);
}
});
}
public void HandleBoardPosClick(Vector2 clickPos) {
if (board.ArrPosOutOfRange(clickPos)) return;
GD.Print($"VirtualBoard {clickPos} clicked");
VirtualPiece clickChess = board.GetPiece(clickPos);
if (!selectedNode.HasSelected()) {
// Select piece
if (clickChess == null) {
// selectedNode.Clear();
return;
}
selectedNode.SetPos(clickPos);
} else if (clickChess == selectedNode.GetPiece()) {
// Unselect piece
selectedNode.Clear();
} else {
// Move piece
GD.Print("default MoveFunc Move: ", selectedNode.GetPos(), "->", clickPos);
MoveAndRecord(clickPos, selectedNode.GetPos());
}
}
public void MoveAndRecord(Vector2 toPos, Vector2 fromPos) {
GD.Print($"{fromPos} move to {toPos}");
VirtualPiece toChess = board.GetPiece(toPos);
VirtualPiece fromChess = board.GetPiece(fromPos);
fromChess?.Selected(false);
VirtualPiece NowNode;
if (toChess != null) {
NowNode = toChess;
board.RemovePiece(toPos);
} else {
NowNode = toChess;
}
moveRecords.AddRecord(NowNode, fromChess, toPos, fromPos);
board.MovePiece(fromPos, toPos);
selectedNode.Clear();
}
public void Undo() {
// ChessPiece selected = selectedNode.GetPiece();
// selected?.DeSelected();
selectedNode.Clear();
moveRecords.Undo();
}
public void ReInit() {
moveRecords.Clear();
board.Clear();
selectedNode.Clear();
// board.InitChessBoard();
}
private class SelectedPiece
{
// Called when the node enters the scene tree for the first time.
private Vector2 selectedNodePos = Vector2.Inf;
private VirtualPiece piece;
private VirtualBoard board;
public SelectedPiece(VirtualBoard board)
{
this.board = board;
}
public void Clear()
{
if (selectedNodePos != Vector2.Inf) {
selectedNodePos = Vector2.Inf;
piece.Selected(false);
}
}
public void SetPos(Vector2 pos)
{
// piece = board.GetNodeFromBoard(pos) as VirtualPiece;
selectedNodePos = pos;
piece = board.GetPiece(selectedNodePos);
piece.Selected(true);
}
public VirtualPiece GetPiece()
{
return piece;
}
public Vector2 GetPos()
{
return selectedNodePos;
}
public bool HasSelected()
{
return selectedNodePos != Vector2.Inf;
}
}
}

View File

@ -2,27 +2,19 @@
using Godot; using Godot;
public partial class ChessBoard : Node2D { public partial class ChessBoard : Node2D {
VirtualBoard Board = null; public VirtualBoard board = null;
MoveRecords Records = null; public Player playerSelf = null;
// Called when the node enters the scene tree for the first time.
public Vector2 selectedNodePos = Vector2.Inf;
public delegate bool ChessMoveFunc(Vector2 toPos, Vector2 fromPos); public delegate bool ChessMoveFunc(Vector2 toPos, Vector2 fromPos);
public Callable chessMoveFunc { get; set; } public Callable chessMoveFunc { get; set; }
public override void _Ready() { public override void _Ready() {
Board = new VirtualBoard(this as Node); board = new VirtualBoard(9, 10);
Records = new MoveRecords(onUndoRecordCallback: (newNode, oldNode, newPos, oldPos) => { playerSelf = new Player(board);
GD.Print("Undo: ", newNode, "->", oldNode, ":", newPos, "->", oldPos); board.OnRemove += OnRemove;
ChessPiece newPiece = newNode as ChessPiece; board.OnInsert += OnInsert;
ChessPiece oldPiece = oldNode as ChessPiece; board.OnMove += OnMove;
Board.MoveNode(oldPos, newPos); InitChessBoard();
if (newPiece != null) {
Board.InsertNode(newPiece, newPos);
}
});
Board.InitChessBoard();
// this.AddChild();
} }
public override void _Input(InputEvent @event) { public override void _Input(InputEvent @event) {
@ -31,85 +23,286 @@ public partial class ChessBoard : Node2D {
mouseEvent.ButtonIndex == MouseButton.Left) { mouseEvent.ButtonIndex == MouseButton.Left) {
// HandleMouseClick(GetGlobalTransformWithCanvas().AffineInverse() * mouseButton.Position); // HandleMouseClick(GetGlobalTransformWithCanvas().AffineInverse() * mouseButton.Position);
HandleMouseClick(GetLocalMousePosition()); HandleMouseClick(GetLocalMousePosition());
// ActionPos(
// (PosTrans.transArrToPix.AffineInverse() *
// GetLocalMousePosition()).Round()
// );
} }
} }
public void ActionPos(Vector2 newPos) {
}
public void MoveAndRecord(Vector2 toPos, Vector2 fromPos) {
GD.Print($"{fromPos} move to {toPos}");
ChessPiece toChess = Board.GetNodeFromBoard(toPos) as ChessPiece;
ChessPiece fromChess = Board.GetNodeFromBoard(fromPos) as ChessPiece;
if (fromChess != null) {
fromChess.DeSelected();
}
Node NowNode;
if (toChess != null) {
GD.Print("nowchess", toChess);
NowNode = toChess.Duplicate();
Board.RemoveNode(toPos);
} else {
NowNode = toChess as Node;
}
Records.AddRecord(NowNode, fromChess, toPos, fromPos);
Board.MoveNode(toPos, fromPos);
selectedNodePos = Vector2.Inf;
}
public void Undo() {
Records.Undo();
}
public void ReInit() {
Records.Clear();
Board.Clear();
selectedNodePos = Vector2.Inf;
Board.InitChessBoard();
}
private void HandleMouseClick(Vector2 clickPosition) { private void HandleMouseClick(Vector2 clickPosition) {
Vector2 newPos = (PosTrans.transArrToPix.AffineInverse() * Vector2 clickBoardPos = (PosTrans.transArrToPix.AffineInverse() *
clickPosition).Round(); clickPosition).Round();
if (VirtualBoard.ArrPosOutOfRange(newPos)) return; playerSelf.HandleBoardPosClick(clickBoardPos);
GD.Print($"{newPos} mouse clicked");
ChessPiece NowChess = Board.GetNodeFromBoard(newPos) as ChessPiece;
ChessPiece SelChess = Board.GetNodeFromBoard(selectedNodePos) as ChessPiece;
if (Vector2.Inf.Equals(selectedNodePos)) {
if (NowChess == null) {
return;
}
NowChess.Selected();
selectedNodePos = newPos;
} else if (selectedNodePos == newPos) {
if (SelChess != null) {
SelChess.DeSelected();
}
selectedNodePos = Vector2.Inf;
} else {
if (chessMoveFunc.Delegate != null) {
GD.Print("chessMoveFunc Move: ", selectedNodePos, "->", newPos);
if ((bool)chessMoveFunc.Call(newPos, selectedNodePos)) {
MoveAndRecord(newPos, selectedNodePos);
}
} else {
GD.Print("default MoveFunc Move: ", selectedNodePos, "->", newPos);
MoveAndRecord(newPos, selectedNodePos);
} }
// MoveAndRecord(newPos, selectedNodePos); void OnMove(Vector2 from, Vector2 to) {
// GD.Print($"End: {selectedNodePos} {SelChess} move to {clickArrPos} {NowChess}"); chessMoveFunc.Call(to, from);
} }
void OnRemove(VirtualPiece piece) {
// (piece.data as Node).QueueFree();
if (piece?.data != null)
RemoveChild(piece.data as Node);
}
void OnInsert(VirtualPiece piece) {
ChessPiece chess = piece.data as ChessPiece;
AddChild(chess);
}
public void InsertNode(ChessPiece node, Vector2 arrayPos) {
AddChild(node);
VirtualPiece piece = node.GetVirtualPiece();
// piece.Move(vector);
board.SetPiecePos(piece, arrayPos);
} }
// using Godot;
// public enum ChessType
// {
// Carriage,
// Horse,
// Elephant,
// Advisor,
// General,
// Cannon,
// Pawn
// }
// public class VirtualBoard
// {
// // ...其他原有成员变量...
// public VirtualBoard(Node root = null)
// {
// BoardRoot = root ?? GetTree().CurrentScene; // 如果未提供root默认为当前场景
// }
// public void InitChessBoard()
// {
// // 定义黑色和红色棋子的初始位置和类型
// var positions = new (Vector2 position, ChessType type)[]
// {
// // 黑方棋子初始化...
// // 示例省略具体位置和类型,你需要根据实际情况填写
// // (new Vector2(x, y), ChessType.Pawn),
// // 红方棋子初始化...
// // 同上
// };
// // 初始化棋子
// foreach (var (position, type) in positions)
// {
// var color = position.Y == 0 ? new Color("black") : new Color("red"); // 根据行判断颜色
// var piece = CreateChessPiece(type, color);
// InsertChess(piece, position);
// }
// }
// private ChessPiece CreateChessPiece(ChessType type, Color labelColor)
// {
// // 根据ChessType创建对应的棋子实例并设置Label和颜色
// // 这里需要你实现具体的逻辑例如switch case或映射表来决定创建哪种棋子
// // 返回创建的棋子实例
// }
// // ...其他原有方法...
// }
public void InitChessBoard() {
InsertNode(new ChessPiece
{
PieceLabel = "车",
LabelColor = new Color("black"),
}, new Vector2(0, 0));
InsertNode(new ChessPiece
{
PieceLabel = "马",
LabelColor = new Color("black"),
}, new Vector2(1, 0));
InsertNode(new ChessPiece
{
PieceLabel = "象",
LabelColor = new Color("black"),
}, new Vector2(2, 0));
InsertNode(new ChessPiece
{
PieceLabel = "士",
LabelColor = new Color("black"),
}, new Vector2(3, 0));
InsertNode(new ChessPiece
{
PieceLabel = "将",
LabelColor = new Color("black"),
}, new Vector2(4, 0));
InsertNode(new ChessPiece
{
PieceLabel = "士",
LabelColor = new Color("black"),
}, new Vector2(5, 0));
InsertNode(new ChessPiece
{
PieceLabel = "象",
LabelColor = new Color("black"),
}, new Vector2(6, 0));
InsertNode(new ChessPiece
{
PieceLabel = "马",
LabelColor = new Color("black"),
}, new Vector2(7, 0));
InsertNode(new ChessPiece
{
PieceLabel = "车",
LabelColor = new Color("black"),
}, new Vector2(8, 0));
InsertNode(new ChessPiece
{
PieceLabel = "炮",
LabelColor = new Color("black"),
}, new Vector2(1, 2));
InsertNode(new ChessPiece
{
PieceLabel = "炮",
LabelColor = new Color("black"),
}, new Vector2(7, 2));
InsertNode(new ChessPiece
{
PieceLabel = "卒",
LabelColor = new Color("black"),
}, new Vector2(0, 3));
InsertNode(new ChessPiece
{
PieceLabel = "卒",
LabelColor = new Color("black"),
}, new Vector2(2, 3));
InsertNode(new ChessPiece
{
PieceLabel = "卒",
LabelColor = new Color("black"),
}, new Vector2(4, 3));
InsertNode(new ChessPiece
{
PieceLabel = "卒",
LabelColor = new Color("black"),
}, new Vector2(6, 3));
InsertNode(new ChessPiece
{
PieceLabel = "卒",
LabelColor = new Color("black"),
}, new Vector2(8, 3));
// -----------------------------------
InsertNode(new ChessPiece
{
PieceLabel = "车",
LabelColor = new Color("red"),
}, new Vector2(0, 9));
InsertNode(new ChessPiece
{
PieceLabel = "马",
LabelColor = new Color("red"),
}, new Vector2(1, 9));
InsertNode(new ChessPiece
{
PieceLabel = "象",
LabelColor = new Color("red"),
}, new Vector2(2, 9));
InsertNode(new ChessPiece
{
PieceLabel = "士",
LabelColor = new Color("red"),
}, new Vector2(3, 9));
InsertNode(new ChessPiece
{
PieceLabel = "将",
LabelColor = new Color("red"),
}, new Vector2(4, 9));
InsertNode(new ChessPiece
{
PieceLabel = "士",
LabelColor = new Color("red"),
}, new Vector2(5, 9));
InsertNode(new ChessPiece
{
PieceLabel = "象",
LabelColor = new Color("red"),
}, new Vector2(6, 9));
InsertNode(new ChessPiece
{
PieceLabel = "马",
LabelColor = new Color("red"),
}, new Vector2(7, 9));
InsertNode(new ChessPiece
{
PieceLabel = "车",
LabelColor = new Color("red"),
}, new Vector2(8, 9));
InsertNode(new ChessPiece
{
PieceLabel = "炮",
LabelColor = new Color("red"),
}, new Vector2(1, 7));
InsertNode(new ChessPiece
{
PieceLabel = "炮",
LabelColor = new Color("red"),
}, new Vector2(7, 7));
InsertNode(new ChessPiece
{
PieceLabel = "卒",
LabelColor = new Color("red"),
}, new Vector2(0, 6));
InsertNode(new ChessPiece
{
PieceLabel = "卒",
LabelColor = new Color("red"),
}, new Vector2(2, 6));
InsertNode(new ChessPiece
{
PieceLabel = "卒",
LabelColor = new Color("red"),
}, new Vector2(4, 6));
InsertNode(new ChessPiece
{
PieceLabel = "卒",
LabelColor = new Color("red"),
}, new Vector2(6, 6));
InsertNode(new ChessPiece
{
PieceLabel = "卒",
LabelColor = new Color("red"),
}, new Vector2(8, 6));
}
} }

View File

@ -11,42 +11,38 @@ public partial class ChessPiece : Sprite2D {
private Vector2 textureSize; private Vector2 textureSize;
private Label labelOfChessName; private Label labelOfChessName;
private VirtualPiece piece;
private Vector2 arrPos = new Vector2(); // 注意这个坐标的非像素坐标而是棋盘坐标 public VirtualPiece GetVirtualPiece() {
return piece;
public void movePos(Vector2 newPos) {
this.arrPos = newPos;
this.Position = PosTrans.transArrToPix * newPos;
} }
public Vector2 getPos() { private void OnMove(Vector2 newPos) {
return arrPos; Position = PosTrans.transArrToPix * new Vector2(newPos.X, newPos.Y);
}
public void OnSelected(bool isSelected) {
if (isSelected) {
GD.Print($"{piece.Pos()} is selected");
Transform *= transToSeleted;
} else {
GD.Print($"{piece.Pos()} is deselected");
Transform *= transToSeleted.AffineInverse();
}
} }
// 是否被选中
public bool isSelected = false;
Transform2D transToSeleted = new Transform2D( Transform2D transToSeleted = new Transform2D(
new Vector2(1.2f, 0), new Vector2(1.2f, 0),
new Vector2(0, 1.2f), new Vector2(0, 1.2f),
new Vector2(0, 0) new Vector2(0, 0)
); );
public void Selected() { public ChessPiece(string name = "", Vector2 pos = new Vector2()) {
if (isSelected) { PieceLabel = name;
return; piece = new VirtualPiece(name, pos);
} piece.OnMove += OnMove;
GD.Print($"{arrPos} is selected"); piece.OnSelected += OnSelected;
this.Transform *= transToSeleted; piece.data = this;
this.isSelected = true;
}
public void DeSelected() {
if (!isSelected) {
return;
}
GD.Print($"{arrPos} is deselected");
this.Transform *= transToSeleted.AffineInverse();
this.isSelected = false;
} }
// Called when the node enters the scene tree for the first time. // Called when the node enters the scene tree for the first time.
@ -56,8 +52,8 @@ public partial class ChessPiece : Sprite2D {
private void InitLabel() { private void InitLabel() {
// this.Texture.ResourcePath = "res://Asserts/ChesspieceBase.tres"; // this.Texture.ResourcePath = "res://Asserts/ChesspieceBase.tres";
this.Texture ??= (Texture2D)ResourceLoader.Load("res://Asserts/ChesspieceBase.tres"); Texture ??= (Texture2D)ResourceLoader.Load("res://Asserts/ChesspieceBase.tres");
textureSize = this.Texture.GetSize(); textureSize = Texture.GetSize();
Vector2 labalPosition = new( Vector2 labalPosition = new(
-textureSize.X / 2, -textureSize.X / 2,
-textureSize.Y / 2); -textureSize.Y / 2);
@ -72,8 +68,4 @@ public partial class ChessPiece : Sprite2D {
// labelOfChessName.SetAnchorsPreset(Control.LayoutPreset.FullRect); // labelOfChessName.SetAnchorsPreset(Control.LayoutPreset.FullRect);
AddChild(labelOfChessName); AddChild(labelOfChessName);
} }
interface IVarify {
bool CanMove(Vector2 newPosition);
}
} }

View File

View File

@ -1,23 +1,25 @@
// using System.Numerics;
using Godot;
using System.Collections.Generic; using System.Collections.Generic;
using System; using System;
using Godot;
using System.Linq;
public class MoveRecords { public class MoveRecords<T> {
private readonly LinkedList<MoveRecord> records = new LinkedList<MoveRecord>(); // 使用队列替换栈 private readonly LinkedList<MoveRecord> records = new LinkedList<MoveRecord>(); // 使用队列替换栈
private readonly int maxRecords; // 记录上限 private readonly int maxRecords; // 记录上限
private Action<Node, Node, Vector2, Vector2> onAddRecordCallback; // 添加记录时的回调 private Action<T, T, Vector2, Vector2> onAddRecordCallback; // 添加记录时的回调
private Action<Node, Node, Vector2, Vector2> onUndoRecordCallback; // 撤销记录时的回调 private Action<T, T, Vector2, Vector2> onUndoRecordCallback; // 撤销记录时的回调
public MoveRecords( public MoveRecords(
Action<Node, Node, Vector2, Vector2> onAddRecordCallback = null, Action<T, T, Vector2, Vector2> onAddRecordCallback = null,
Action<Node, Node, Vector2, Vector2> onUndoRecordCallback = null, Action<T, T, Vector2, Vector2> onUndoRecordCallback = null,
int maxRecords = 32) { int maxRecords = 32) {
this.maxRecords = maxRecords; this.maxRecords = maxRecords;
this.onAddRecordCallback = onAddRecordCallback; this.onAddRecordCallback = onAddRecordCallback;
this.onUndoRecordCallback = onUndoRecordCallback; this.onUndoRecordCallback = onUndoRecordCallback;
} }
public void AddRecord(Node newNode, Node oldNode, Vector2 newPos, Vector2 oldPos) { public void AddRecord(T newNode, T oldNode, Vector2 newPos, Vector2 oldPos) {
// 达到记录上限时,移除最远的记录(队首元素) // 达到记录上限时,移除最远的记录(队首元素)
if (records.Count >= maxRecords) { if (records.Count >= maxRecords) {
records.RemoveFirst(); records.RemoveFirst();
@ -42,16 +44,16 @@ public class MoveRecords {
} }
private class MoveRecord { private class MoveRecord {
public Node NewNode { get; } public T NewNode { get; }
public Node OldNode { get; } public T OldNode { get; }
public Vector2 NewPos { get; } public Vector2 NewPos { get; }
public Vector2 OldPos { get; } public Vector2 OldPos { get; }
public MoveRecord(Node newNode, Node oldNode, Vector2 newPos, Vector2 oldPos) { public MoveRecord(T newNode, T oldNode, Vector2 newPos, Vector2 oldPos) {
this.NewNode = newNode; NewNode = newNode;
this.OldNode = oldNode; OldNode = oldNode;
this.OldPos = oldPos; OldPos = oldPos;
this.NewPos = newPos; NewPos = newPos;
} }
} }
} }

View File

@ -1,316 +1,74 @@
// using System.Numerics;
using Godot; using Godot;
using System;
class VirtualBoard { public class VirtualBoard {
private static readonly int boradRows = 9; private readonly int Rows;
private static readonly int boradCols = 10; private readonly int Cols;
private readonly Node BoardRoot; private readonly VirtualPiece[,] pieces;
private readonly Node[,] nodesBorad = new Node[boradRows, boradCols];
public static bool ArrPosOutOfRange(Vector2 arrayPos) { // <oldPiece, newPiece>
return arrayPos.X < 0 || arrayPos.X >= boradRows || arrayPos.Y < 0 || arrayPos.Y >= boradCols; public event Action<VirtualPiece, VirtualPiece> OnSetPiecePos;
public event Action<VirtualPiece> OnInsert;
public event Action<VirtualPiece> OnRemove;
public event Action<Vector2, Vector2> OnMove;
public VirtualBoard(int Rows, int Cols) {
this.Rows = Rows;
this.Cols = Cols;
pieces = new VirtualPiece[Rows, Cols];
} }
public Node GetNodeFromBoard(Vector2 arrayPos) { public bool ArrPosOutOfRange(Vector2 arrayPos) {
return arrayPos.X < 0 || arrayPos.X >= Rows || arrayPos.Y < 0 || arrayPos.Y >= Cols;
}
public VirtualPiece GetPiece(Vector2 arrayPos) {
if (ArrPosOutOfRange(arrayPos)) return null; if (ArrPosOutOfRange(arrayPos)) return null;
return nodesBorad[(int)arrayPos.X, (int)arrayPos.Y]; return pieces[(int)arrayPos.X, (int)arrayPos.Y];
} }
public void SetNodeToBoard(Vector2 arrayPos, Node node) { public VirtualPiece SetPiecePos(VirtualPiece piece, Vector2 arrayPos) {
if (ArrPosOutOfRange(arrayPos)) return; VirtualPiece ret = null;
nodesBorad[(int)arrayPos.X, (int)arrayPos.Y] = node; if (ArrPosOutOfRange(arrayPos)) return ret;
ret = pieces[(int)arrayPos.X, (int)arrayPos.Y];
pieces[(int)arrayPos.X, (int)arrayPos.Y] = piece;
OnSetPiecePos?.Invoke(ret, piece);
piece?.Move(arrayPos);
return ret;
} }
public VirtualBoard(Node root = null) { public bool InsertPiece(VirtualPiece piece, Vector2 arrayPos) {
//?? //GetTree().CurrentScene; // 如果未提供root默认为当前场景 if (GetPiece(arrayPos) != null) {
BoardRoot = root;
}
public bool MoveNode(Vector2 newPos, Vector2 oldPos) {
if (ArrPosOutOfRange(newPos) || ArrPosOutOfRange(oldPos)) {
return false; return false;
} }
if (GetNodeFromBoard(newPos) != null) { OnInsert?.Invoke(piece);
return false; SetPiecePos(piece, arrayPos);
}
if (GetNodeFromBoard(oldPos) is ChessPiece chessPiece) {
chessPiece.movePos(newPos);
}
SetNodeToBoard(newPos, GetNodeFromBoard(oldPos));
SetNodeToBoard(oldPos, null);
return true; return true;
} }
public bool RemoveNode(Vector2 arrayPos) { public bool MovePiece(Vector2 from, Vector2 to) {
if (ArrPosOutOfRange(arrayPos)) return false; if (ArrPosOutOfRange(to) || ArrPosOutOfRange(from)) {
if (GetNodeFromBoard(arrayPos) is ChessPiece chessPiece) { return false;
chessPiece.QueueFree();
} }
SetNodeToBoard(arrayPos, null); if (GetPiece(to) != null) {
return false;
}
OnMove?.Invoke(from, to);
SetPiecePos(SetPiecePos(null, from), to);
return true; return true;
} }
public void InsertNode(ChessPiece chess, Vector2 arrayPos) { public bool RemovePiece(Vector2 pos) {
chess.Position = PosTrans.transArrToPix * arrayPos; OnRemove?.Invoke(GetPiece(pos));
SetNodeToBoard(arrayPos, chess); return SetPiecePos(null, pos) != null;
BoardRoot.AddChild(chess);
} }
public void Clear() { public void Clear() {
foreach (var node in BoardRoot.GetChildren()) { for (int i = 0; i < Rows; i++) {
if (node is ChessPiece chessPiece) { for (int j = 0; j < Cols; j++) {
RemoveNode(chessPiece.getPos()); RemovePiece(new Vector2(i, j));
chessPiece.QueueFree();
} }
} }
} }
// using Godot;
// public enum ChessType
// {
// Carriage,
// Horse,
// Elephant,
// Advisor,
// General,
// Cannon,
// Pawn
// }
// public class VirtualBoard
// {
// // ...其他原有成员变量...
// public VirtualBoard(Node root = null)
// {
// BoardRoot = root ?? GetTree().CurrentScene; // 如果未提供root默认为当前场景
// }
// public void InitChessBoard()
// {
// // 定义黑色和红色棋子的初始位置和类型
// var positions = new (Vector2 position, ChessType type)[]
// {
// // 黑方棋子初始化...
// // 示例省略具体位置和类型,你需要根据实际情况填写
// // (new Vector2(x, y), ChessType.Pawn),
// // 红方棋子初始化...
// // 同上
// };
// // 初始化棋子
// foreach (var (position, type) in positions)
// {
// var color = position.Y == 0 ? new Color("black") : new Color("red"); // 根据行判断颜色
// var piece = CreateChessPiece(type, color);
// InsertChess(piece, position);
// }
// }
// private ChessPiece CreateChessPiece(ChessType type, Color labelColor)
// {
// // 根据ChessType创建对应的棋子实例并设置Label和颜色
// // 这里需要你实现具体的逻辑例如switch case或映射表来决定创建哪种棋子
// // 返回创建的棋子实例
// }
// // ...其他原有方法...
// }
public void InitChessBoard() {
InsertNode(new ChessPiece
{
PieceLabel = "车",
LabelColor = new Color("black"),
}, new Vector2(0, 0));
InsertNode(new ChessPiece
{
PieceLabel = "马",
LabelColor = new Color("black"),
}, new Vector2(1, 0));
InsertNode(new ChessPiece
{
PieceLabel = "象",
LabelColor = new Color("black"),
}, new Vector2(2, 0));
InsertNode(new ChessPiece
{
PieceLabel = "士",
LabelColor = new Color("black"),
}, new Vector2(3, 0));
InsertNode(new ChessPiece
{
PieceLabel = "将",
LabelColor = new Color("black"),
}, new Vector2(4, 0));
InsertNode(new ChessPiece
{
PieceLabel = "士",
LabelColor = new Color("black"),
}, new Vector2(5, 0));
InsertNode(new ChessPiece
{
PieceLabel = "象",
LabelColor = new Color("black"),
}, new Vector2(6, 0));
InsertNode(new ChessPiece
{
PieceLabel = "马",
LabelColor = new Color("black"),
}, new Vector2(7, 0));
InsertNode(new ChessPiece
{
PieceLabel = "车",
LabelColor = new Color("black"),
}, new Vector2(8, 0));
InsertNode(new ChessPiece
{
PieceLabel = "炮",
LabelColor = new Color("black"),
}, new Vector2(1, 2));
InsertNode(new ChessPiece
{
PieceLabel = "炮",
LabelColor = new Color("black"),
}, new Vector2(7, 2));
InsertNode(new ChessPiece
{
PieceLabel = "卒",
LabelColor = new Color("black"),
}, new Vector2(0, 3));
InsertNode(new ChessPiece
{
PieceLabel = "卒",
LabelColor = new Color("black"),
}, new Vector2(2, 3));
InsertNode(new ChessPiece
{
PieceLabel = "卒",
LabelColor = new Color("black"),
}, new Vector2(4, 3));
InsertNode(new ChessPiece
{
PieceLabel = "卒",
LabelColor = new Color("black"),
}, new Vector2(6, 3));
InsertNode(new ChessPiece
{
PieceLabel = "卒",
LabelColor = new Color("black"),
}, new Vector2(8, 3));
// -----------------------------------
InsertNode(new ChessPiece
{
PieceLabel = "车",
LabelColor = new Color("red"),
}, new Vector2(0, 9));
InsertNode(new ChessPiece
{
PieceLabel = "马",
LabelColor = new Color("red"),
}, new Vector2(1, 9));
InsertNode(new ChessPiece
{
PieceLabel = "象",
LabelColor = new Color("red"),
}, new Vector2(2, 9));
InsertNode(new ChessPiece
{
PieceLabel = "士",
LabelColor = new Color("red"),
}, new Vector2(3, 9));
InsertNode(new ChessPiece
{
PieceLabel = "将",
LabelColor = new Color("red"),
}, new Vector2(4, 9));
InsertNode(new ChessPiece
{
PieceLabel = "士",
LabelColor = new Color("red"),
}, new Vector2(5, 9));
InsertNode(new ChessPiece
{
PieceLabel = "象",
LabelColor = new Color("red"),
}, new Vector2(6, 9));
InsertNode(new ChessPiece
{
PieceLabel = "马",
LabelColor = new Color("red"),
}, new Vector2(7, 9));
InsertNode(new ChessPiece
{
PieceLabel = "车",
LabelColor = new Color("red"),
}, new Vector2(8, 9));
InsertNode(new ChessPiece
{
PieceLabel = "炮",
LabelColor = new Color("red"),
}, new Vector2(1, 7));
InsertNode(new ChessPiece
{
PieceLabel = "炮",
LabelColor = new Color("red"),
}, new Vector2(7, 7));
InsertNode(new ChessPiece
{
PieceLabel = "卒",
LabelColor = new Color("red"),
}, new Vector2(0, 6));
InsertNode(new ChessPiece
{
PieceLabel = "卒",
LabelColor = new Color("red"),
}, new Vector2(2, 6));
InsertNode(new ChessPiece
{
PieceLabel = "卒",
LabelColor = new Color("red"),
}, new Vector2(4, 6));
InsertNode(new ChessPiece
{
PieceLabel = "卒",
LabelColor = new Color("red"),
}, new Vector2(6, 6));
InsertNode(new ChessPiece
{
PieceLabel = "卒",
LabelColor = new Color("red"),
}, new Vector2(8, 6));
}
} }

View File

@ -0,0 +1,40 @@
// using System.Numerics;
using Godot;
using System;
public class VirtualPiece {
private Vector2 pos; // 注意这个坐标的非像素坐标而是棋盘坐标
private string name;
private bool isSelected;
public object data;
public event Action<Vector2> OnMove;
public event Action<bool> OnSelected;
public void Move(Vector2 pos) {
this.pos = pos;
OnMove?.Invoke(pos);
}
public Vector2 Pos() {
return pos;
}
public void Selected(bool isSelected) {
if (this.isSelected != isSelected) {
OnSelected?.Invoke(isSelected);
this.isSelected = isSelected;
}
}
public bool IsSelected() {
return isSelected;
}
public VirtualPiece(string name = "", Vector2 pos = new Vector2()) {
this.name = name;
this.pos = pos;
}
}

View File

@ -2,7 +2,7 @@
name="Windows Desktop" name="Windows Desktop"
platform="Windows Desktop" platform="Windows Desktop"
runnable=false runnable=true
dedicated_server=false dedicated_server=false
custom_features="" custom_features=""
export_filter="all_resources" export_filter="all_resources"
@ -185,7 +185,7 @@ permissions/install_location_provider=false
permissions/install_packages=false permissions/install_packages=false
permissions/install_shortcut=false permissions/install_shortcut=false
permissions/internal_system_window=false permissions/internal_system_window=false
permissions/internet=false permissions/internet=true
permissions/kill_background_processes=false permissions/kill_background_processes=false
permissions/location_hardware=false permissions/location_hardware=false
permissions/manage_accounts=false permissions/manage_accounts=false

View File

@ -12,7 +12,7 @@ config_version=5
config/name="Chinese_Chess" config/name="Chinese_Chess"
run/main_scene="res://Main.tscn" run/main_scene="res://Main.tscn"
config/features=PackedStringArray("4.2", "C#", "Mobile") config/features=PackedStringArray("4.3", "C#", "Mobile")
config/icon="res://Asserts/icon.svg" config/icon="res://Asserts/icon.svg"
[autoload] [autoload]
@ -23,7 +23,6 @@ Global="*res://Scripts/Global.cs"
window/size/viewport_width=720 window/size/viewport_width=720
window/size/viewport_height=1280 window/size/viewport_height=1280
window/size/always_on_top=true
window/stretch/mode="viewport" window/stretch/mode="viewport"
window/handheld/orientation=1 window/handheld/orientation=1