feat: 重构多人游戏界面和代码,使用文件存储游戏配置,重构底层代码
This commit is contained in:
0
Scripts/Src/ChineseChess/ChessAdvisor.cs
Normal file
0
Scripts/Src/ChineseChess/ChessAdvisor.cs
Normal file
0
Scripts/Src/ChineseChess/ChessCannon.cs
Normal file
0
Scripts/Src/ChineseChess/ChessCannon.cs
Normal file
0
Scripts/Src/ChineseChess/ChessChariot.cs
Normal file
0
Scripts/Src/ChineseChess/ChessChariot.cs
Normal file
0
Scripts/Src/ChineseChess/ChessElephant.cs
Normal file
0
Scripts/Src/ChineseChess/ChessElephant.cs
Normal file
0
Scripts/Src/ChineseChess/ChessGeneral.cs
Normal file
0
Scripts/Src/ChineseChess/ChessGeneral.cs
Normal file
0
Scripts/Src/ChineseChess/ChessHorse.cs
Normal file
0
Scripts/Src/ChineseChess/ChessHorse.cs
Normal file
0
Scripts/Src/ChineseChess/ChessPawn.cs
Normal file
0
Scripts/Src/ChineseChess/ChessPawn.cs
Normal file
20
Scripts/Src/ChineseChess/ChessTypes.cs
Normal file
20
Scripts/Src/ChineseChess/ChessTypes.cs
Normal file
@ -0,0 +1,20 @@
|
||||
// 帅/将 (General) - 代表双方的最高统帅。
|
||||
// 子类名:ChessGeneral
|
||||
|
||||
// 仕/士 (Advisor) - 保护帅/将的近身侍卫。
|
||||
// 子类名:ChessAdvisor
|
||||
|
||||
// 相/象 (Elephant) - 行动受限,走田字格,不能过河。
|
||||
// 子类名:ChessElephant
|
||||
|
||||
// 車/车 (Chariot) - 横竖移动,威力巨大。
|
||||
// 子类名:ChessChariot
|
||||
|
||||
// 馬/马 (Horse) - 走日字形,跳跃式移动。
|
||||
// 子类名:ChessHorse
|
||||
|
||||
// 砲/炮 (Cannon) - 需要隔子才能吃子,直线移动。
|
||||
// 子类名:ChessCannon
|
||||
|
||||
// 兵/卒 (Pawn) - 最基础的棋子,过河后可横移。
|
||||
// 子类名:ChessPawn
|
0
Scripts/Src/ChineseChess/ChineseChess.cs
Normal file
0
Scripts/Src/ChineseChess/ChineseChess.cs
Normal file
125
Scripts/Src/ChineseChess/Player.cs
Normal file
125
Scripts/Src/ChineseChess/Player.cs
Normal file
@ -0,0 +1,125 @@
|
||||
using Vector2 = Godot.Vector2;
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
public class Player {
|
||||
private readonly VirtualBoard board;
|
||||
private readonly SelectedPiece selectedNode;
|
||||
private readonly MoveRecords<VirtualPiece> moveRecords;
|
||||
public EventHandler<VirtualBoard.MoveEventArgs> OnMove;
|
||||
public ArrayList pieces = new();
|
||||
|
||||
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);
|
||||
|
||||
OnMove?.Invoke(this, new VirtualBoard.MoveEventArgs { From = fromPos, To = toPos });
|
||||
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 readonly 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;
|
||||
}
|
||||
}
|
||||
}
|
99
Scripts/Src/VirtualBoard.cs
Normal file
99
Scripts/Src/VirtualBoard.cs
Normal file
@ -0,0 +1,99 @@
|
||||
using System;
|
||||
using Vector2 = Godot.Vector2;
|
||||
|
||||
public class VirtualBoard {
|
||||
private readonly int Rows;
|
||||
private readonly int Cols;
|
||||
private readonly VirtualPiece[,] pieces;
|
||||
|
||||
public class SetPiecePosEventArgs : EventArgs
|
||||
{
|
||||
public VirtualPiece OldPiece { get; set; }
|
||||
public VirtualPiece NewPiece { get; set; }
|
||||
}
|
||||
|
||||
public class MoveEventArgs : EventArgs
|
||||
{
|
||||
public Vector2 From { get; set; }
|
||||
public Vector2 To { get; set; }
|
||||
}
|
||||
|
||||
public event EventHandler<SetPiecePosEventArgs> OnSetPiecePos;
|
||||
public event EventHandler<VirtualPiece> OnInsert;
|
||||
public event EventHandler<VirtualPiece> OnRemove;
|
||||
public event EventHandler<MoveEventArgs> OnMove;
|
||||
|
||||
public VirtualBoard(int rows, int cols) {
|
||||
this.Rows = rows;
|
||||
this.Cols = cols;
|
||||
pieces = new VirtualPiece[rows, cols];
|
||||
}
|
||||
|
||||
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;
|
||||
return pieces[(int)arrayPos.X, (int)arrayPos.Y];
|
||||
}
|
||||
|
||||
public VirtualPiece SetPiecePos(VirtualPiece piece, Vector2 arrayPos) {
|
||||
if (ArrPosOutOfRange(arrayPos)) return null;
|
||||
|
||||
VirtualPiece oldPiece = pieces[(int)arrayPos.X, (int)arrayPos.Y];
|
||||
pieces[(int)arrayPos.X, (int)arrayPos.Y] = piece;
|
||||
|
||||
OnSetPiecePos?.Invoke(this, new SetPiecePosEventArgs { OldPiece = oldPiece, NewPiece = piece });
|
||||
|
||||
piece?.Move(arrayPos);
|
||||
|
||||
return oldPiece;
|
||||
}
|
||||
|
||||
public bool InsertPiece(VirtualPiece piece, Vector2 arrayPos) {
|
||||
if (GetPiece(arrayPos) != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
OnInsert?.Invoke(this, piece);
|
||||
SetPiecePos(piece, arrayPos);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool MovePiece(Vector2 from, Vector2 to) {
|
||||
if (ArrPosOutOfRange(to) || ArrPosOutOfRange(from)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (GetPiece(to) != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
OnMove?.Invoke(this, new MoveEventArgs { From = from, To = to });
|
||||
|
||||
SetPiecePos(SetPiecePos(null, from), to);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool RemovePiece(Vector2 pos) {
|
||||
VirtualPiece piece = GetPiece(pos);
|
||||
if (piece == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
OnRemove?.Invoke(this, piece);
|
||||
|
||||
return SetPiecePos(null, pos) != null;
|
||||
}
|
||||
|
||||
public void Clear() {
|
||||
for (int i = 0; i < Rows; i++) {
|
||||
for (int j = 0; j < Cols; j++) {
|
||||
RemovePiece(new Vector2(i, j));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
38
Scripts/Src/VirtualPiece.cs
Normal file
38
Scripts/Src/VirtualPiece.cs
Normal file
@ -0,0 +1,38 @@
|
||||
using Vector2 = Godot.Vector2;
|
||||
using System;
|
||||
|
||||
public class VirtualPiece {
|
||||
private Vector2 pos; // 注意这个坐标的非像素坐标而是棋盘坐标
|
||||
|
||||
private readonly 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;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user