From 3efabf1c331bef94d27e2818ab734a02dc401dfe Mon Sep 17 00:00:00 2001 From: Loic Guegan Date: Thu, 29 Dec 2022 10:08:22 +0100 Subject: [PATCH] Implement pieces move animations --- src/game_tab/Game.cpp | 2 +- src/game_tab/GameTab.cpp | 16 ++++ src/game_tab/HalfMove.cpp | 10 ++- src/game_tab/HalfMove.hpp | 7 +- src/game_tab/left_panel/GameTabLeftPanel.cpp | 67 +++++++++++---- src/game_tab/left_panel/GameTabLeftPanel.hpp | 10 ++- src/game_tab/left_panel/board/BoardCanvas.cpp | 84 +++++++++++++++---- src/game_tab/left_panel/board/BoardCanvas.hpp | 9 +- .../right_panel/GameTabRightPanel.cpp | 4 +- .../right_panel/editor/EditorCanvas.cpp | 4 +- 10 files changed, 160 insertions(+), 53 deletions(-) diff --git a/src/game_tab/Game.cpp b/src/game_tab/Game.cpp index 1bc1e4c..39605ce 100644 --- a/src/game_tab/Game.cpp +++ b/src/game_tab/Game.cpp @@ -84,7 +84,7 @@ bool Game::Play(std::string move) { std::string fen = GetFen(); arbiter.Setup(fen); if (arbiter.Play(move)) { - HalfMove *m = new HalfMove(arbiter.GetSAN(), arbiter.GetFEN()); + HalfMove *m = new HalfMove(move, arbiter.GetSAN(), arbiter.GetFEN()); char capture = arbiter.GetCapture(); if (capture != ' ') { wxLogDebug("%c", capture); diff --git a/src/game_tab/GameTab.cpp b/src/game_tab/GameTab.cpp index 55d7848..40a1c71 100644 --- a/src/game_tab/GameTab.cpp +++ b/src/game_tab/GameTab.cpp @@ -28,6 +28,22 @@ GameTab::GameTab(wxFrame *parent, std::shared_ptr game) Bind(REFRESH_TAB_TITLE, &GameTab::OnRefreshTabTitle, this, wxID_ANY); Bind(GAME_CHANGE, &GameTab::OnGameChange, this, wxID_ANY); + splitter->Bind(wxEVT_KEY_DOWN, [p=this,bp=board_panel,ed=editor_panel](wxKeyEvent &e){ + if(e.GetKeyCode() == WXK_RIGHT){ + bp->NextMove(true); + } else if(e.GetKeyCode() == WXK_LEFT){ + bp->PreviousMove(true); + } + ed->Notify(); + }); + splitter->Bind(wxEVT_KEY_UP, [p=this,bp=board_panel,ed=editor_panel](wxKeyEvent &e){ + if(e.GetKeyCode() == WXK_RIGHT){ + bp->NextMove(false); + } else if(e.GetKeyCode() == WXK_LEFT){ + bp->PreviousMove(false); + } + ed->Notify(); + }); } void GameTab::OnGameChange(wxCommandEvent &event) { diff --git a/src/game_tab/HalfMove.cpp b/src/game_tab/HalfMove.cpp index 7e18869..3ff627f 100644 --- a/src/game_tab/HalfMove.cpp +++ b/src/game_tab/HalfMove.cpp @@ -1,12 +1,14 @@ #include "HalfMove.hpp" -HalfMove::HalfMove(std::string move) : capture(' ') { - this->move = move; +HalfMove::HalfMove(std::string move_absolute,std::string move_san) : capture(' ') { + this->move_absolute=move_absolute; + this->move = move_san; fen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; } -HalfMove::HalfMove(std::string move, std::string fen) : fen(fen), capture(' ') { - this->move = move; +HalfMove::HalfMove(std::string move_absolute, std::string move_san, std::string fen) : fen(fen), capture(' ') { + this->move_absolute=move_absolute; + this->move = move_san; } HalfMove::~HalfMove() { diff --git a/src/game_tab/HalfMove.hpp b/src/game_tab/HalfMove.hpp index 6ea0c5a..92b0645 100644 --- a/src/game_tab/HalfMove.hpp +++ b/src/game_tab/HalfMove.hpp @@ -21,10 +21,12 @@ class HalfMove : public cgeditor::CGEHalfMove { std::string fen; char capture; void BuildAndVerify(HalfMove *m, std::string fen); + std::string move_absolute; public: - HalfMove(std::string move); - HalfMove(std::string move, std::string fen); + + HalfMove(std::string move_absolute,std::string move_san); + HalfMove(std::string move_absolute,std::string move_san, std::string fen); HalfMove(pgnp::HalfMove *m); ~HalfMove(); @@ -56,6 +58,7 @@ public: void SetFen(std::string fen); void SetCapture(char c); bool IsABlackMove(); + std::string GetAbsoluteMove(){return move_absolute;}; /** * @brief Build current move diff --git a/src/game_tab/left_panel/GameTabLeftPanel.cpp b/src/game_tab/left_panel/GameTabLeftPanel.cpp index 5a0c430..cd3bfe4 100644 --- a/src/game_tab/left_panel/GameTabLeftPanel.cpp +++ b/src/game_tab/left_panel/GameTabLeftPanel.cpp @@ -17,17 +17,33 @@ GameTabLeftPanel::GameTabLeftPanel(wxFrame *parent, std::shared_ptr game) fen_text_field->SetFont(wxFont(*wxNORMAL_FONT).Bold().Larger()); Bind(PLAY_MOVE_EVENT, &GameTabLeftPanel::OnPlay, this, wxID_ANY); - Bind(PREVIOUS_MOVE_EVENT, &GameTabLeftPanel::OnPreviousMove, this, wxID_ANY); - Bind(NEXT_MOVE_EVENT, &GameTabLeftPanel::OnNextMove, this, wxID_ANY); Bind(wxEVT_BUTTON, &GameTabLeftPanel::OnSwap, this, SWAP_BTN); Bind(wxEVT_BUTTON, &GameTabLeftPanel::OnZoomIn, this, ZOOM_IN_BTN); Bind(wxEVT_BUTTON, &GameTabLeftPanel::OnZoomOut, this, ZOOM_OUT_BTN); + Bind(wxEVT_KEY_DOWN, [p=this](wxKeyEvent &e){e.ResumePropagation(1);e.Skip();}); + Bind(wxEVT_KEY_UP, [p=this](wxKeyEvent &e){e.ResumePropagation(1);e.Skip();}); + repeat=false; } -void GameTabLeftPanel::OnPreviousMove(wxCommandEvent &event) { - game->Previous(); - Notify(true); - NotifyEditor(); +void GameTabLeftPanel::PreviousMove(bool isKeyDown) { + if(isKeyDown){ + game->Previous(); + Notify(true,true); + repeat=true; + } else { + repeat=false; + } +} + +void GameTabLeftPanel::NextMove(bool isKeyDown) { + if(isKeyDown){ + game->Next(); + Notify(true,false); + repeat=true; + } + else{ + repeat=false; + } } void GameTabLeftPanel::OnZoomIn(wxCommandEvent &event) { @@ -45,12 +61,7 @@ void GameTabLeftPanel::OnSwap(wxCommandEvent &event) { board_canvas->Swap(); } -void GameTabLeftPanel::OnNextMove(wxCommandEvent &event) { - wxLogDebug("Game tab received NEXT_MOVE_EVENT"); - game->Next(); - Notify(true); - NotifyEditor(); -} + void GameTabLeftPanel::OnPlay(wxCommandEvent &event) { wxLogDebug("Game tab received PLAY_MOVE_EVENT"); @@ -67,7 +78,8 @@ void GameTabLeftPanel::OnPlay(wxCommandEvent &event) { } } -void GameTabLeftPanel::Notify(bool animate) { +void GameTabLeftPanel::Notify(bool animate, bool backward) { + wxLogDebug("Notify"); std::string fen = game->GetFen(); std::map captures; HalfMove *m = game->GetCurrentMove(); @@ -76,12 +88,37 @@ void GameTabLeftPanel::Notify(bool animate) { } if(!animate){ + if(m){ + last_absolute_move=m->GetAbsoluteMove(); + } board_canvas->SetupBoard(chessarbiter::FENParser::Parse(fen).board, game->IsBlackToPlay(), captures); } else{ - board_canvas->Animate(chessarbiter::FENParser::Parse(fen).board, - game->IsBlackToPlay(), captures,"a1","a2"); + if(backward && last_absolute_move.size()>0){ + std::string dst=last_absolute_move.substr(0,2); + std::string src=last_absolute_move.substr(2,2); + board_canvas->Animate(chessarbiter::FENParser::Parse(fen).board, + game->IsBlackToPlay(), captures,src,dst,repeat); + if(m){ + last_absolute_move=m->GetAbsoluteMove(); + } + } + else if(!backward && m){ + std::string new_absolute_move=m->GetAbsoluteMove(); + if(last_absolute_move!=new_absolute_move){ + last_absolute_move=new_absolute_move; + std::string src=last_absolute_move.substr(0,2); + std::string dst=last_absolute_move.substr(2,2); + board_canvas->Animate(chessarbiter::FENParser::Parse(fen).board, + game->IsBlackToPlay(), captures,src,dst,repeat); + } + } + + // If m undefined + if(!m){ + last_absolute_move=""; + } } fen_text_field->SetValue(game->GetFen()); diff --git a/src/game_tab/left_panel/GameTabLeftPanel.hpp b/src/game_tab/left_panel/GameTabLeftPanel.hpp index 5a160d2..d254ace 100644 --- a/src/game_tab/left_panel/GameTabLeftPanel.hpp +++ b/src/game_tab/left_panel/GameTabLeftPanel.hpp @@ -12,14 +12,16 @@ class GameTabLeftPanel : public TabGameLeftPanel { std::shared_ptr game; BoardCanvas *board_canvas; void NotifyEditor(); - + std::string last_absolute_move; + bool repeat; + public: GameTabLeftPanel(wxFrame *parent, std::shared_ptr game); - void Notify(bool animate=false); + void Notify(bool animate=false,bool backward=false); void OnPlay(wxCommandEvent &event); void OnGotoMove(wxCommandEvent &event); - void OnPreviousMove(wxCommandEvent &event); - void OnNextMove(wxCommandEvent &event); + void PreviousMove(bool isKeyDown); + void NextMove(bool isKeyDown); void OnZoomIn(wxCommandEvent &event); void OnZoomOut(wxCommandEvent &event); void OnSwap(wxCommandEvent &event); diff --git a/src/game_tab/left_panel/board/BoardCanvas.cpp b/src/game_tab/left_panel/board/BoardCanvas.cpp index ab1e969..ad6ba65 100644 --- a/src/game_tab/left_panel/board/BoardCanvas.cpp +++ b/src/game_tab/left_panel/board/BoardCanvas.cpp @@ -4,7 +4,7 @@ wxDEFINE_EVENT(PLAY_MOVE_EVENT, wxCommandEvent); BoardCanvas::BoardCanvas(wxFrame *parent) : wxPanel(parent), black_side(false), is_black_turn(true), frozen(false), - lock_square_size(false), t(new Theme()), t_captures(new Theme()) { + lock_square_size(false), t(new Theme()), t_captures(new Theme()), buffer(nullptr) { board = "rnbqkbnrpppppppp PPPPPPPPRNBQKBNR"; is_dragging = false; valid_drag = false; @@ -15,9 +15,12 @@ BoardCanvas::BoardCanvas(wxFrame *parent) ApplyPreferences(); // The following should be called when using an EVT_PAINT handler SetBackgroundStyle(wxBG_STYLE_PAINT); - timer.Bind(wxEVT_TIMER, [p=this](wxTimerEvent &e){p->Refresh();}); - duration=500; - fps=30; + duration=200; + duration_fast=100; + fps=180; + + Bind(wxEVT_KEY_DOWN, &BoardCanvas::OnKeyEvent, this); + Bind(wxEVT_KEY_UP, &BoardCanvas::OnKeyEvent, this); } BoardCanvas::~BoardCanvas() { @@ -52,6 +55,8 @@ void BoardCanvas::OnPaint(wxPaintEvent &event) { boardY = 0; // Setup buffer (later use for animations) + if(buffer!=nullptr) + free(buffer); buffer=new wxBitmap(canvas_size.x,canvas_size.y,32); wxMemoryDC memDC(*buffer); DrawBoard(memDC); @@ -60,12 +65,15 @@ void BoardCanvas::OnPaint(wxPaintEvent &event) { else { // Otherwise reuse buffer and animate. TEST CODE FOR NOW: dc.DrawBitmap(*buffer, 0, 0, true); - // Draw piece - dc.DrawBitmap(*t->Get(piece_moved), 0+frame, 0+frame, false); + double frames=duration/(1000/fps); + double percent=frame/frames; + // Draw moving piece + dc.DrawBitmap(*t->Get(piece_moved), + src.x + frame*(transVect.x/frames), + src.y + frame*(transVect.y/frames), false); // end drawing frame++; - if(frame*fps>=duration){ - timer.Stop(); + if(frame>=frames){ reuseBuffer=false; SetupBoard(final_board, final_is_black_turn, final_captures); } @@ -102,21 +110,56 @@ void BoardCanvas::SetupBoard(std::string board, bool is_black_turn, Refresh(); } -void BoardCanvas::Animate(std::string board, bool is_black_turn, std::map captures, std::string src, std::string dst){ - return; // Shortcut this method for now +void BoardCanvas::Animate(std::string board, bool is_black_turn, std::map captures, std::string src, std::string dst,bool faster){ this->final_board=board; this->final_is_black_turn=is_black_turn; this->final_captures=captures; - this->src=src; - this->dst=dst; std::uint8_t pfile = src[0]-'a'; std::uint8_t prank = src[1]-'1'; - this->piece_moved = this->board[(7 - pfile) + 8 * (7-prank)]; // Piece to move - // Animate piece here + this->piece_moved = this->board[pfile + 8 * (7-prank)]; // Piece to move + + // Now remove the piece that will be moved + this->board[pfile + 8 * (7-prank)]=' '; + SetupBoard(this->board,this->is_black_turn,this->captures); + Update(); // Since refresh in SetupBoard is not synchronous, this call wait the end of Refresh() + + // Now compute piece start position and translation vector (Copy paste from DrawBoard()) + std::uint32_t piece_width = t->GetPiecesSizes(); + std::uint32_t centrer_offset = (square_width - piece_width) / 2; + if (!black_side) { + prank = 7 - prank; + pfile = 7 - pfile; + } + std::uint32_t x = boardX + (7 - pfile) * square_width; + std::uint32_t y = boardY + prank * square_width; + this->src.x = x + centrer_offset; + this->src.y = y + centrer_offset; + // Now dst: + pfile = dst[0]-'a'; + prank = dst[1]-'1'; + if (!black_side) { + prank = 7 - prank; + pfile = 7 - pfile; + } + x = boardX + (7 - pfile) * square_width; + y = boardY + prank * square_width; + transVect.x=x-this->src.x+centrer_offset; + transVect.y=y-this->src.y+centrer_offset; + + // Start animation: reuseBuffer=true; + int duration_backup=duration; + duration=faster ? duration_fast : duration; frame=0; - timer.Start(1000/fps); // in ms; + int frames=duration/(1000/fps); // total number of frames + for(int i=frames;i>0;i--){ + Refresh(); + Update(); + wxMilliSleep((1000/(fps*2))); + } + duration=faster ? duration_backup : duration_backup; + reuseBuffer=false; } void BoardCanvas::DrawBoard(wxDC &dc) { @@ -321,17 +364,22 @@ void BoardCanvas::Swap() { } void BoardCanvas::OnKeyEvent(wxKeyEvent &event) { + event.ResumePropagation(1); +event.Skip(); +return; + wxLogDebug("OnKeyEvent %d",event.GetUnicodeKey()); if (event.GetKeyCode() == WXK_LEFT) { wxCommandEvent previousEvent(PREVIOUS_MOVE_EVENT, GetId()); - previousEvent.SetEventObject(this); + //previousEvent.SetEventObject(this); ProcessEvent(previousEvent); } else if (event.GetKeyCode() == WXK_RIGHT) { wxCommandEvent nextEvent(NEXT_MOVE_EVENT, GetId()); - nextEvent.SetEventObject(this); + //nextEvent.SetEventObject(this); ProcessEvent(nextEvent); } } + void BoardCanvas::SetClockTime(short hours, short min, short sec, bool IsBlack) { if (IsBlack) { @@ -343,4 +391,4 @@ void BoardCanvas::SetClockTime(short hours, short min, short sec, wxBEGIN_EVENT_TABLE(BoardCanvas, wxPanel) EVT_PAINT(BoardCanvas::OnPaint) EVT_MOUSE_EVENTS(BoardCanvas::MouseEvent) - EVT_CHAR_HOOK(BoardCanvas::OnKeyEvent) wxEND_EVENT_TABLE() + wxEND_EVENT_TABLE() diff --git a/src/game_tab/left_panel/board/BoardCanvas.hpp b/src/game_tab/left_panel/board/BoardCanvas.hpp index 9e375d1..1da45b2 100644 --- a/src/game_tab/left_panel/board/BoardCanvas.hpp +++ b/src/game_tab/left_panel/board/BoardCanvas.hpp @@ -60,14 +60,13 @@ class BoardCanvas : public wxPanel { // Drawing buffer (ANIMATIONS) wxBitmap *buffer; bool reuseBuffer; - wxTimer timer; - int frame,duration,fps; + int frame,duration,fps,duration_fast; std::string final_board; bool final_is_black_turn; std::map final_captures; - std::string src; - std::string dst; char piece_moved; + wxPoint src; + wxPoint transVect; public: BoardCanvas(wxFrame *parent); @@ -82,7 +81,7 @@ public: void Swap(); void SetupBoard(std::string board, bool is_black_turn, std::map captures); - void Animate(std::string board, bool is_black_turn, std::map captures, std::string src, std::string dst); + void Animate(std::string board, bool is_black_turn, std::map captures, std::string src, std::string dst,bool faster); void SetClockTime(short hours, short min, short sec, bool IsBlack); DECLARE_EVENT_TABLE() }; diff --git a/src/game_tab/right_panel/GameTabRightPanel.cpp b/src/game_tab/right_panel/GameTabRightPanel.cpp index 569f2c3..99ab4fd 100644 --- a/src/game_tab/right_panel/GameTabRightPanel.cpp +++ b/src/game_tab/right_panel/GameTabRightPanel.cpp @@ -29,9 +29,9 @@ GameTabRightPanel::GameTabRightPanel(wxFrame *parent, std::shared_ptr game wxID_ANY); this->Bind(SET_AS_MAINLINE_EVENT, &GameTabRightPanel::OnMoveSetAsMainline, this, wxID_ANY); - this->Bind(NEXT_MOVE_EVENT, &GameTabRightPanel::OnNextMove, this, wxID_ANY); + /*this->Bind(NEXT_MOVE_EVENT, &GameTabRightPanel::OnNextMove, this, wxID_ANY); this->Bind(PREVIOUS_MOVE_EVENT, &GameTabRightPanel::OnPreviousMove, this, - wxID_ANY); + wxID_ANY);*/ this->Bind(wxEVT_LIST_ITEM_SELECTED, &GameTabRightPanel::OnTagSelected, this, wxID_ANY); this->Bind(wxEVT_LIST_ITEM_DESELECTED, &GameTabRightPanel::OnTagDeselected, diff --git a/src/game_tab/right_panel/editor/EditorCanvas.cpp b/src/game_tab/right_panel/editor/EditorCanvas.cpp index 870dd26..07a2f56 100644 --- a/src/game_tab/right_panel/editor/EditorCanvas.cpp +++ b/src/game_tab/right_panel/editor/EditorCanvas.cpp @@ -173,7 +173,7 @@ void EditorCanvas::SetMoves(HalfMove *moves, HalfMove *current) { } void EditorCanvas::OnKeyEvent(wxKeyEvent &event) { - if (event.GetKeyCode() == WXK_LEFT) { + /*if (event.GetKeyCode() == WXK_LEFT) { wxCommandEvent previousEvent(PREVIOUS_MOVE_EVENT, GetId()); previousEvent.SetEventObject(this); ProcessEvent(previousEvent); @@ -181,7 +181,7 @@ void EditorCanvas::OnKeyEvent(wxKeyEvent &event) { wxCommandEvent nextEvent(NEXT_MOVE_EVENT, GetId()); nextEvent.SetEventObject(this); ProcessEvent(nextEvent); - } + }*/ } wxBEGIN_EVENT_TABLE(EditorCanvas, wxPanel) EVT_PAINT(EditorCanvas::OnPaint)