Implement pieces move animations

This commit is contained in:
Loic Guegan 2022-12-29 10:08:22 +01:00
parent 7187e6d6ee
commit 3efabf1c33
10 changed files with 160 additions and 53 deletions

View file

@ -84,7 +84,7 @@ bool Game::Play(std::string move) {
std::string fen = GetFen(); std::string fen = GetFen();
arbiter.Setup(fen); arbiter.Setup(fen);
if (arbiter.Play(move)) { 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(); char capture = arbiter.GetCapture();
if (capture != ' ') { if (capture != ' ') {
wxLogDebug("%c", capture); wxLogDebug("%c", capture);

View file

@ -28,6 +28,22 @@ GameTab::GameTab(wxFrame *parent, std::shared_ptr<Game> game)
Bind(REFRESH_TAB_TITLE, &GameTab::OnRefreshTabTitle, this, wxID_ANY); Bind(REFRESH_TAB_TITLE, &GameTab::OnRefreshTabTitle, this, wxID_ANY);
Bind(GAME_CHANGE, &GameTab::OnGameChange, 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) { void GameTab::OnGameChange(wxCommandEvent &event) {

View file

@ -1,12 +1,14 @@
#include "HalfMove.hpp" #include "HalfMove.hpp"
HalfMove::HalfMove(std::string move) : capture(' ') { HalfMove::HalfMove(std::string move_absolute,std::string move_san) : capture(' ') {
this->move = move; this->move_absolute=move_absolute;
this->move = move_san;
fen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; fen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
} }
HalfMove::HalfMove(std::string move, std::string fen) : fen(fen), capture(' ') { HalfMove::HalfMove(std::string move_absolute, std::string move_san, std::string fen) : fen(fen), capture(' ') {
this->move = move; this->move_absolute=move_absolute;
this->move = move_san;
} }
HalfMove::~HalfMove() { HalfMove::~HalfMove() {

View file

@ -21,10 +21,12 @@ class HalfMove : public cgeditor::CGEHalfMove {
std::string fen; std::string fen;
char capture; char capture;
void BuildAndVerify(HalfMove *m, std::string fen); void BuildAndVerify(HalfMove *m, std::string fen);
std::string move_absolute;
public: 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(pgnp::HalfMove *m);
~HalfMove(); ~HalfMove();
@ -56,6 +58,7 @@ public:
void SetFen(std::string fen); void SetFen(std::string fen);
void SetCapture(char c); void SetCapture(char c);
bool IsABlackMove(); bool IsABlackMove();
std::string GetAbsoluteMove(){return move_absolute;};
/** /**
* @brief Build current move * @brief Build current move

View file

@ -17,17 +17,33 @@ GameTabLeftPanel::GameTabLeftPanel(wxFrame *parent, std::shared_ptr<Game> game)
fen_text_field->SetFont(wxFont(*wxNORMAL_FONT).Bold().Larger()); fen_text_field->SetFont(wxFont(*wxNORMAL_FONT).Bold().Larger());
Bind(PLAY_MOVE_EVENT, &GameTabLeftPanel::OnPlay, this, wxID_ANY); 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::OnSwap, this, SWAP_BTN);
Bind(wxEVT_BUTTON, &GameTabLeftPanel::OnZoomIn, this, ZOOM_IN_BTN); Bind(wxEVT_BUTTON, &GameTabLeftPanel::OnZoomIn, this, ZOOM_IN_BTN);
Bind(wxEVT_BUTTON, &GameTabLeftPanel::OnZoomOut, this, ZOOM_OUT_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) { void GameTabLeftPanel::PreviousMove(bool isKeyDown) {
game->Previous(); if(isKeyDown){
Notify(true); game->Previous();
NotifyEditor(); 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) { void GameTabLeftPanel::OnZoomIn(wxCommandEvent &event) {
@ -45,12 +61,7 @@ void GameTabLeftPanel::OnSwap(wxCommandEvent &event) {
board_canvas->Swap(); 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) { void GameTabLeftPanel::OnPlay(wxCommandEvent &event) {
wxLogDebug("Game tab received PLAY_MOVE_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::string fen = game->GetFen();
std::map<char, std::uint8_t> captures; std::map<char, std::uint8_t> captures;
HalfMove *m = game->GetCurrentMove(); HalfMove *m = game->GetCurrentMove();
@ -76,12 +88,37 @@ void GameTabLeftPanel::Notify(bool animate) {
} }
if(!animate){ if(!animate){
if(m){
last_absolute_move=m->GetAbsoluteMove();
}
board_canvas->SetupBoard(chessarbiter::FENParser::Parse(fen).board, board_canvas->SetupBoard(chessarbiter::FENParser::Parse(fen).board,
game->IsBlackToPlay(), captures); game->IsBlackToPlay(), captures);
} }
else{ else{
board_canvas->Animate(chessarbiter::FENParser::Parse(fen).board, if(backward && last_absolute_move.size()>0){
game->IsBlackToPlay(), captures,"a1","a2"); 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()); fen_text_field->SetValue(game->GetFen());

View file

@ -12,14 +12,16 @@ class GameTabLeftPanel : public TabGameLeftPanel {
std::shared_ptr<Game> game; std::shared_ptr<Game> game;
BoardCanvas *board_canvas; BoardCanvas *board_canvas;
void NotifyEditor(); void NotifyEditor();
std::string last_absolute_move;
bool repeat;
public: public:
GameTabLeftPanel(wxFrame *parent, std::shared_ptr<Game> game); GameTabLeftPanel(wxFrame *parent, std::shared_ptr<Game> game);
void Notify(bool animate=false); void Notify(bool animate=false,bool backward=false);
void OnPlay(wxCommandEvent &event); void OnPlay(wxCommandEvent &event);
void OnGotoMove(wxCommandEvent &event); void OnGotoMove(wxCommandEvent &event);
void OnPreviousMove(wxCommandEvent &event); void PreviousMove(bool isKeyDown);
void OnNextMove(wxCommandEvent &event); void NextMove(bool isKeyDown);
void OnZoomIn(wxCommandEvent &event); void OnZoomIn(wxCommandEvent &event);
void OnZoomOut(wxCommandEvent &event); void OnZoomOut(wxCommandEvent &event);
void OnSwap(wxCommandEvent &event); void OnSwap(wxCommandEvent &event);

View file

@ -4,7 +4,7 @@ wxDEFINE_EVENT(PLAY_MOVE_EVENT, wxCommandEvent);
BoardCanvas::BoardCanvas(wxFrame *parent) BoardCanvas::BoardCanvas(wxFrame *parent)
: wxPanel(parent), black_side(false), is_black_turn(true), frozen(false), : 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"; board = "rnbqkbnrpppppppp PPPPPPPPRNBQKBNR";
is_dragging = false; is_dragging = false;
valid_drag = false; valid_drag = false;
@ -15,9 +15,12 @@ BoardCanvas::BoardCanvas(wxFrame *parent)
ApplyPreferences(); ApplyPreferences();
// The following should be called when using an EVT_PAINT handler // The following should be called when using an EVT_PAINT handler
SetBackgroundStyle(wxBG_STYLE_PAINT); SetBackgroundStyle(wxBG_STYLE_PAINT);
timer.Bind(wxEVT_TIMER, [p=this](wxTimerEvent &e){p->Refresh();}); duration=200;
duration=500; duration_fast=100;
fps=30; fps=180;
Bind(wxEVT_KEY_DOWN, &BoardCanvas::OnKeyEvent, this);
Bind(wxEVT_KEY_UP, &BoardCanvas::OnKeyEvent, this);
} }
BoardCanvas::~BoardCanvas() { BoardCanvas::~BoardCanvas() {
@ -52,6 +55,8 @@ void BoardCanvas::OnPaint(wxPaintEvent &event) {
boardY = 0; boardY = 0;
// Setup buffer (later use for animations) // Setup buffer (later use for animations)
if(buffer!=nullptr)
free(buffer);
buffer=new wxBitmap(canvas_size.x,canvas_size.y,32); buffer=new wxBitmap(canvas_size.x,canvas_size.y,32);
wxMemoryDC memDC(*buffer); wxMemoryDC memDC(*buffer);
DrawBoard(memDC); DrawBoard(memDC);
@ -60,12 +65,15 @@ void BoardCanvas::OnPaint(wxPaintEvent &event) {
else { else {
// Otherwise reuse buffer and animate. TEST CODE FOR NOW: // Otherwise reuse buffer and animate. TEST CODE FOR NOW:
dc.DrawBitmap(*buffer, 0, 0, true); dc.DrawBitmap(*buffer, 0, 0, true);
// Draw piece double frames=duration/(1000/fps);
dc.DrawBitmap(*t->Get(piece_moved), 0+frame, 0+frame, false); 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 // end drawing
frame++; frame++;
if(frame*fps>=duration){ if(frame>=frames){
timer.Stop();
reuseBuffer=false; reuseBuffer=false;
SetupBoard(final_board, final_is_black_turn, final_captures); SetupBoard(final_board, final_is_black_turn, final_captures);
} }
@ -102,21 +110,56 @@ void BoardCanvas::SetupBoard(std::string board, bool is_black_turn,
Refresh(); Refresh();
} }
void BoardCanvas::Animate(std::string board, bool is_black_turn, std::map<char, std::uint8_t> captures, std::string src, std::string dst){ void BoardCanvas::Animate(std::string board, bool is_black_turn, std::map<char, std::uint8_t> captures, std::string src, std::string dst,bool faster){
return; // Shortcut this method for now
this->final_board=board; this->final_board=board;
this->final_is_black_turn=is_black_turn; this->final_is_black_turn=is_black_turn;
this->final_captures=captures; this->final_captures=captures;
this->src=src;
this->dst=dst;
std::uint8_t pfile = src[0]-'a'; std::uint8_t pfile = src[0]-'a';
std::uint8_t prank = src[1]-'1'; std::uint8_t prank = src[1]-'1';
this->piece_moved = this->board[(7 - pfile) + 8 * (7-prank)]; // Piece to move this->piece_moved = this->board[pfile + 8 * (7-prank)]; // Piece to move
// Animate piece here
// 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; reuseBuffer=true;
int duration_backup=duration;
duration=faster ? duration_fast : duration;
frame=0; 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) { void BoardCanvas::DrawBoard(wxDC &dc) {
@ -321,17 +364,22 @@ void BoardCanvas::Swap() {
} }
void BoardCanvas::OnKeyEvent(wxKeyEvent &event) { void BoardCanvas::OnKeyEvent(wxKeyEvent &event) {
event.ResumePropagation(1);
event.Skip();
return;
wxLogDebug("OnKeyEvent %d",event.GetUnicodeKey());
if (event.GetKeyCode() == WXK_LEFT) { if (event.GetKeyCode() == WXK_LEFT) {
wxCommandEvent previousEvent(PREVIOUS_MOVE_EVENT, GetId()); wxCommandEvent previousEvent(PREVIOUS_MOVE_EVENT, GetId());
previousEvent.SetEventObject(this); //previousEvent.SetEventObject(this);
ProcessEvent(previousEvent); ProcessEvent(previousEvent);
} else if (event.GetKeyCode() == WXK_RIGHT) { } else if (event.GetKeyCode() == WXK_RIGHT) {
wxCommandEvent nextEvent(NEXT_MOVE_EVENT, GetId()); wxCommandEvent nextEvent(NEXT_MOVE_EVENT, GetId());
nextEvent.SetEventObject(this); //nextEvent.SetEventObject(this);
ProcessEvent(nextEvent); ProcessEvent(nextEvent);
} }
} }
void BoardCanvas::SetClockTime(short hours, short min, short sec, void BoardCanvas::SetClockTime(short hours, short min, short sec,
bool IsBlack) { bool IsBlack) {
if (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) wxBEGIN_EVENT_TABLE(BoardCanvas, wxPanel) EVT_PAINT(BoardCanvas::OnPaint)
EVT_MOUSE_EVENTS(BoardCanvas::MouseEvent) EVT_MOUSE_EVENTS(BoardCanvas::MouseEvent)
EVT_CHAR_HOOK(BoardCanvas::OnKeyEvent) wxEND_EVENT_TABLE() wxEND_EVENT_TABLE()

View file

@ -60,14 +60,13 @@ class BoardCanvas : public wxPanel {
// Drawing buffer (ANIMATIONS) // Drawing buffer (ANIMATIONS)
wxBitmap *buffer; wxBitmap *buffer;
bool reuseBuffer; bool reuseBuffer;
wxTimer timer; int frame,duration,fps,duration_fast;
int frame,duration,fps;
std::string final_board; std::string final_board;
bool final_is_black_turn; bool final_is_black_turn;
std::map<char, std::uint8_t> final_captures; std::map<char, std::uint8_t> final_captures;
std::string src;
std::string dst;
char piece_moved; char piece_moved;
wxPoint src;
wxPoint transVect;
public: public:
BoardCanvas(wxFrame *parent); BoardCanvas(wxFrame *parent);
@ -82,7 +81,7 @@ public:
void Swap(); void Swap();
void SetupBoard(std::string board, bool is_black_turn, void SetupBoard(std::string board, bool is_black_turn,
std::map<char, std::uint8_t> captures); std::map<char, std::uint8_t> captures);
void Animate(std::string board, bool is_black_turn, std::map<char, std::uint8_t> captures, std::string src, std::string dst); void Animate(std::string board, bool is_black_turn, std::map<char, std::uint8_t> captures, std::string src, std::string dst,bool faster);
void SetClockTime(short hours, short min, short sec, bool IsBlack); void SetClockTime(short hours, short min, short sec, bool IsBlack);
DECLARE_EVENT_TABLE() DECLARE_EVENT_TABLE()
}; };

View file

@ -29,9 +29,9 @@ GameTabRightPanel::GameTabRightPanel(wxFrame *parent, std::shared_ptr<Game> game
wxID_ANY); wxID_ANY);
this->Bind(SET_AS_MAINLINE_EVENT, &GameTabRightPanel::OnMoveSetAsMainline, this->Bind(SET_AS_MAINLINE_EVENT, &GameTabRightPanel::OnMoveSetAsMainline,
this, wxID_ANY); 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, this->Bind(PREVIOUS_MOVE_EVENT, &GameTabRightPanel::OnPreviousMove, this,
wxID_ANY); wxID_ANY);*/
this->Bind(wxEVT_LIST_ITEM_SELECTED, &GameTabRightPanel::OnTagSelected, this, this->Bind(wxEVT_LIST_ITEM_SELECTED, &GameTabRightPanel::OnTagSelected, this,
wxID_ANY); wxID_ANY);
this->Bind(wxEVT_LIST_ITEM_DESELECTED, &GameTabRightPanel::OnTagDeselected, this->Bind(wxEVT_LIST_ITEM_DESELECTED, &GameTabRightPanel::OnTagDeselected,

View file

@ -173,7 +173,7 @@ void EditorCanvas::SetMoves(HalfMove *moves, HalfMove *current) {
} }
void EditorCanvas::OnKeyEvent(wxKeyEvent &event) { void EditorCanvas::OnKeyEvent(wxKeyEvent &event) {
if (event.GetKeyCode() == WXK_LEFT) { /*if (event.GetKeyCode() == WXK_LEFT) {
wxCommandEvent previousEvent(PREVIOUS_MOVE_EVENT, GetId()); wxCommandEvent previousEvent(PREVIOUS_MOVE_EVENT, GetId());
previousEvent.SetEventObject(this); previousEvent.SetEventObject(this);
ProcessEvent(previousEvent); ProcessEvent(previousEvent);
@ -181,7 +181,7 @@ void EditorCanvas::OnKeyEvent(wxKeyEvent &event) {
wxCommandEvent nextEvent(NEXT_MOVE_EVENT, GetId()); wxCommandEvent nextEvent(NEXT_MOVE_EVENT, GetId());
nextEvent.SetEventObject(this); nextEvent.SetEventObject(this);
ProcessEvent(nextEvent); ProcessEvent(nextEvent);
} }*/
} }
wxBEGIN_EVENT_TABLE(EditorCanvas, wxPanel) EVT_PAINT(EditorCanvas::OnPaint) wxBEGIN_EVENT_TABLE(EditorCanvas, wxPanel) EVT_PAINT(EditorCanvas::OnPaint)