feat: 重构多人游戏界面和代码,使用文件存储游戏配置,重构底层代码

This commit is contained in:
ZZY
2024-11-06 22:13:03 +08:00
parent d323a0bee7
commit d6cbb5e11d
30 changed files with 715 additions and 262 deletions

View File

@ -7,8 +7,9 @@ public partial class ChessGame : Node2D
Global global;
ConfirmationDialog dialog;
private bool isSession = false;
private Vector2 from;
private Vector2 to;
private Player playerSelf;
private Player playerOpponent;
// Called when the node enters the scene tree for the first time.
public override void _Ready()
@ -16,45 +17,47 @@ public partial class ChessGame : Node2D
// Init.Call();
global = GetNode<Global>("/root/Global");
board = GetNode<ChessBoard>("Chessboard");
playerSelf = new Player(board.board, Player.PlayerType.Human);
playerOpponent = new Player(board.board, Player.PlayerType.Human);
board.OnMouseClicked += (sender, clickPosition) => {
Vector2 clickBoardPos = (PosTrans.transArrToPix.AffineInverse() *
clickPosition).Round();
playerSelf.HandleBoardPosClick(clickBoardPos);
};
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>("ReInit").Connect("pressed", Callable.From(board.ReInit));
// GetNode<Button>("Home").Connect("pressed", Callable.From(this.GoHome));
if (!global.RPClient.GetIsConnected()) {
return;
}
isSession = true;
GD.Print("ws is connected");
global.RPClient.OnPRCSessionExit += (cmd, code) => {
GoHome();
};
// board.Set("Hello", Callable.From(() => {GD.PrintErr("hello");}));
board.board.OnMove += (sender, args) => {
Vector2 newPos = args.To;
Vector2 fromPos = args.From;
if (from.X == fromPos.X && from.Y == fromPos.Y && to.X == newPos.X && to.Y == newPos.Y) {
return;
}
from = fromPos;
to = newPos;
var res = global.RPClient.SendSessionToAll(global.sessionId, new Dictionary{
{"type", "move"},
{"from", fromPos},
{"to", newPos},
{"fromX", fromPos.X},
{"fromY", fromPos.Y},
{"toX", newPos.X},
{"toY", newPos.Y},
board.OnMouseClicked += (sender, clickPosition) => {
Vector2 clickBoardPos = (PosTrans.transArrToPix.AffineInverse() *
clickPosition).Round();
var res = global.RPClient.SendSessionToAll(global.sessionId, new Dictionary {
{"type", "mouseClicked"},
{"X", clickBoardPos.X},
{"Y", clickBoardPos.Y},
});
GD.Print($"chessMoveFunc Callback {fromPos} -> {newPos} {res}");
// GD.Print($"chessMoveFunc Callback {fromPos} -> {newPos} {res}");
};
global.RPClient.OnPRCSessionRecv += (msg) => {
SessionMsgHandle(msg["msg"].AsGodotDictionary());
};
@ -75,18 +78,10 @@ public partial class ChessGame : Node2D
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);
case "mouseClicked":
Vector2 mouseClicked = new(GD.StrToVar(msg["X"].ToString()).AsInt32(),
GD.StrToVar(msg["Y"].ToString()).AsInt32());
playerOpponent.HandleBoardPosClick(mouseClicked);
break;
case "undo":
_Undo();
@ -98,16 +93,16 @@ public partial class ChessGame : Node2D
}
private void _Undo() {
board.playerSelf.Undo();
playerSelf.Undo();
}
private void _ReInit() {
board.playerSelf.ReInit();
playerSelf.ReInit();
board.InitChessBoard();
}
private void BtnOver() {
GD.PrintErr($"BtnOver {isSession}");
GD.Print($"BtnOver {isSession}");
if (isSession == false) {
return;
}
@ -125,22 +120,26 @@ public partial class ChessGame : Node2D
}
public void Undo() {
GD.PrintErr($"Undo {isSession}");
if (isSession == false) {
GD.Print($"Undo {isSession}");
if (isSession) {
global.RPClient.SendSessionToAll(global.sessionId, new Dictionary{
{"type", "undo"},
});
} else {
_Undo();
}
global.RPClient.SendSessionToAll(global.sessionId, new Dictionary{
{"type", "undo"},
});
}
public void ReInit() {
GD.PrintErr($"ReInit {isSession}");
if (isSession == false) {
if (isSession) {
global.RPClient.SendSessionToAll(global.sessionId, new Dictionary{
{"type", "reInit"},
});
} else {
_ReInit();
}
global.RPClient.SendSessionToAll(global.sessionId, new Dictionary{
{"type", "reInit"},
});
}
}

View File

@ -1,48 +1,50 @@
using Godot;
using Godot.Collections;
public partial class Menu : Control
{
Global global = null;
ItemList lists = null;
ConfirmationDialog dialog = null;
LineEdit nameLineEdit = null;
LineEdit urlLineEdit = null;
ColorRect colorRect = null;
public partial class GameLobby : Control {
Global global = null;
Timer timer = null;
// Called when the node enters the scene tree for the first time.
public override void _Ready()
{
global = GetNode<Global>("/root/Global");
nameLineEdit = GetNode<LineEdit>("Name/LineEdit");
urlLineEdit = GetNode<LineEdit>("URL");
urlLineEdit.Text = global.URL;
urlLineEdit.Set("text_submitted", Callable.From(()=>{
global.URL = urlLineEdit.Text;
}));
ItemList lists = null;
ConfirmationDialog dialog = null;
lists = GetNode<ItemList>("Server/ItemList");
string URL;
string userName;
// Called when the node enters the scene tree for the first time.
public override void _Ready() {
global = GetNode<Global>("/root/Global");
lists = GetNode<ItemList>("MarginContainer/VBoxContainer/MarginContainer3/ItemList");
dialog = GetNode<ConfirmationDialog>("Dialogs/ConfirmationDialog");
colorRect = GetNode<ColorRect>("Server/ColorRect");
dialog.DialogAutowrap = true;
URL = global.GlobalConfigDict["server_url"].AsString();
userName = global.GlobalConfigDict["user_name"].AsString();
dialog.MinSize = new Vector2I(400, 200);
dialog.DialogAutowrap = true;
dialog.Canceled += () => {
if (dialog.Title == "Session Created")
global.RPClient.SessionAckCreate(dialog.GetMeta("sessionId").ToString(), false);
};
dialog.Confirmed += () => {
// GD.PrintErr("confirm", dialog.GetLabel().Text);
// goToSignle();
if (dialog.Title == "Session Created")
global.RPClient.SessionAckCreate(dialog.GetMeta("sessionId").ToString(), true);
};
global.RPClient.RegSessionAckCreateCallback((
sessionId,
res,
reqUserId,
reqUserName) => {
global.RPClient.OnOpen += (string eventName, object[] args) => {
global.RPClient.UserInit(userName, "godot chessboard", () => {
global.RPClient.UserRename(userName);
return global.RPClient.RegionAdd("server");
});
};
// global.RPClient.OnOpen += (eventName, args) => {
// global.RPClient.UserInit(userName, "none", () => {
// GD.PrintErr($"User Init Success");
// return true;
// });
// // global.RPClient.UserRename(userName);
// };
global.RPClient.RegSessionAckCreateCallback((sessionId, res, reqUserId, reqUserName) => {
if (reqUserId != null) {
dialog.Title = "Session Created";
dialog.SetMeta("reqUserName", reqUserName);
@ -65,34 +67,43 @@ public partial class Menu : Control
return true;
});
timer = new Timer();
timer = new Timer();
AddChild(timer);
ColorRect colorRect = GetNode<ColorRect>("MarginContainer/VBoxContainer/MarginContainer2/MarginContainer2/ColorRect");
timer.Connect("timeout", Callable.From(() => {
if (global.RPClient.GetIsConnected()) {
colorRect.Color = Colors.Green;
} else {
colorRect.Color = Colors.Red;
}
FlushData();
}));
timer.Start(1);
Connect();
}
public void Connect() {
GD.Print("Connect");
if (global.RPClient.GetIsConnected()) {
return;
}
// global.RPClient.ExitServer();
global.RPClient.ConnectToUrlEx(URL);
global.SetProcess(true);
}
// Called every frame. 'delta' is the elapsed time since the previous frame.
public override void _Process(double delta) {
}
private void OnItemSelected(int index) {
private void OnItemSelected(int index) {
Dictionary item = lists.GetItemMetadata(index).AsGodotDictionary();
GD.Print($"Item {index} selected, {item}");
string[] strings = { item["id"].ToString() };
global.RPClient.SessionCreate(strings);
}
private void FlushData()
{
private void FlushData() {
global.RPClient.RegionInspect("server", (data) => {
GD.Print(data);
lists.Clear();
// GD.Print(data);
lists?.Clear();
foreach (Dictionary<string, string> user in data) {
string userId = user["id"].ToString();
string userName = user["name"].ToString();
@ -110,27 +121,9 @@ public partial class Menu : Control
});
}
public void Connect()
{
GD.Print("Connect");
global.RPClient.ExitServer();
global.RPClient.ConnectToUrlEx(urlLineEdit.Text);
global.SetProcess(true);
}
public void EnterName() {
var newLine = nameLineEdit.Text;
global.RPClient.UserRename(newLine);
nameLineEdit.Text = newLine;
}
private void goToHome() {
private void OnBack() {
global.RPClient.ExitServer();
global.GotoScene("res://Main.tscn");
}
// private void OnItemListItemClicked(int index, Vector2 atPosition, int mouse_button_index)
// {
// GD.Print($"Item {index} clicked at {atPosition} with mouse button index {mouse_button_index}");
// }
}

View File

@ -1,127 +0,0 @@
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

@ -0,0 +1,91 @@
using Godot;
using System;
public partial class Setting : Control
{
Global global;
int font_size;
LineEdit fontOut;
// Called when the node enters the scene tree for the first time.
public override void _Ready()
{
global = GetNode<Global>("/root/Global");
fontOut = GetNode<LineEdit>("BoxContainer/MarginContainer5/HBoxContainer/FontSize");
font_size = (int)global.GlobalConfigDict["font_size"];
GetNode<HSlider>("BoxContainer/MarginContainer6/FontSizeBar")
.Value = font_size;
fontOut.Text = font_size.ToString();
FillingData();
}
private void FillingData()
{
GetNode<LineEdit>("BoxContainer/MarginContainer2/Server/LineEdit")
.Text = global.GlobalConfigDict["server_url"].ToString();
GetNode<LineEdit>("BoxContainer/MarginContainer3/Name/LineEdit")
.Text = global.GlobalConfigDict["user_name"].ToString();
}
// Called every frame. 'delta' is the elapsed time since the previous frame.
public override void _Process(double delta)
{
}
private void OnBack() {
global.SaveConfig();
global.GotoScene("res://Main.tscn", null);
}
void OnNameChanged(string Value)
{
global.GlobalConfigDict["user_name"] = Value;
// global.ConfigFlush();
}
private void OnServerUrlChanged(string Value)
{
global.GlobalConfigDict["server_url"] = Value;
// global.ConfigFlush();
}
private void OnFontSizeChanged(float Value)
{
font_size = (int)Value;
global.GlobalConfigDict["font_size"] = font_size;
global.ConfigFlush();
fontOut.Text = font_size.ToString();
}
private static void OnClearData() {
// ProjectSettings.GlobalizePath("user://");
// DirAccess.CopyAbsolute("res://userdata", "user://");
}
private static void OnClearUserData() {
string path = ProjectSettings.GlobalizePath("user://");
// DirAccess dirAccess = DirAccess.Open(path);
// dirAccess.Remove(path);
if (DirAccess.RemoveAbsolute(path) != Error.Ok) {
FormattableString msg = $"Failed to delete user data at {path}";
OS.Alert(msg.ToString(), "Error");
}
}
private static void OnGetCacheDir() {
OS.Alert(OS.GetCacheDir(), "Cache Dir");
}
private static void OnGetConfigDir() {
OS.Alert(OS.GetConfigDir(), "Config Dir");
}
private static void OnGetDataDir() {
OS.Alert(OS.GetDataDir(), "Data Dir");
}
private static void OnGetUserDataDir() {
OS.Alert(OS.GetUserDataDir(), "User Data Dir");
}
}