差分
このページの2つのバージョン間の差分を表示します。
| 両方とも前のリビジョン前のリビジョン次のリビジョン | 前のリビジョン | ||
| youtube:cpp-intro-038 [2024/02/28 18:26] – 削除 - 外部編集 (不明な日付) 127.0.0.1 | youtube:cpp-intro-038 [2024/02/29 11:54] (現在) – [Tetris Take 6] freemikan | ||
|---|---|---|---|
| 行 1: | 行 1: | ||
| + | ====== Tetris Take 6 ====== | ||
| + | 作成日: 2023-07-23 (日) | ||
| + | |||
| + | [[https:// | ||
| + | |||
| + | {{: | ||
| + | |||
| + | ==== main.cpp ==== | ||
| + | |||
| + | <file cpp> | ||
| + | #include < | ||
| + | #include < | ||
| + | |||
| + | #include " | ||
| + | #include " | ||
| + | #include " | ||
| + | #include " | ||
| + | #include " | ||
| + | #include " | ||
| + | |||
| + | void process_commands(Player:: | ||
| + | Piece &piece, | ||
| + | BlockList const & | ||
| + | while (!command_queue.empty()) { | ||
| + | Player:: | ||
| + | switch (action) { | ||
| + | case Player:: | ||
| + | piece.move_left(); | ||
| + | if (piece.is_overlapping(dead_blocks)) { | ||
| + | piece.move_right(); | ||
| + | } | ||
| + | break; | ||
| + | case Player:: | ||
| + | piece.move_right(); | ||
| + | if (piece.is_overlapping(dead_blocks)) { | ||
| + | piece.move_left(); | ||
| + | } | ||
| + | break; | ||
| + | default: | ||
| + | break; | ||
| + | } | ||
| + | command_queue.pop(); | ||
| + | } | ||
| + | } | ||
| + | |||
| + | void process_piece_stuck(Piece &piece, BlockList & | ||
| + | if (piece.is_stuck()) { | ||
| + | piece.detach_blocks(dead_blocks); | ||
| + | piece.generate_next_shape(); | ||
| + | piece.reset_position(piece_spawn_position); | ||
| + | } | ||
| + | } | ||
| + | |||
| + | int main(int argc, char **argv) { | ||
| + | auto *WindowTitle = " | ||
| + | int const ScreenWidth = 600; // px | ||
| + | int const ScreenHeight = 550; // px | ||
| + | int const MsecsPerUpdate = 16; | ||
| + | | ||
| + | if (SDL_Init(SDL_INIT_VIDEO) < 0) { | ||
| + | std::cerr << " | ||
| + | std:: | ||
| + | } | ||
| + | | ||
| + | SDL_Window *window = SDL_CreateWindow( | ||
| + | WindowTitle, | ||
| + | SDL_WINDOWPOS_CENTERED, | ||
| + | SDL_WINDOWPOS_CENTERED, | ||
| + | ScreenWidth, | ||
| + | ScreenHeight, | ||
| + | 0); | ||
| + | if (window == nullptr) { | ||
| + | std::cerr << " | ||
| + | std:: | ||
| + | } | ||
| + | | ||
| + | SDL_Renderer *renderer = SDL_CreateRenderer(window, | ||
| + | if (renderer == nullptr) { | ||
| + | std::cerr << " | ||
| + | std:: | ||
| + | } | ||
| + | | ||
| + | // setup game | ||
| + | BoardInfo board { | ||
| + | 10, 20, | ||
| + | 24, 24, | ||
| + | SDL_Point{10, | ||
| + | }; | ||
| + | | ||
| + | APoint const piece_spawn_position{3, | ||
| + | | ||
| + | EventProcessor event_processor; | ||
| + | Fence fence = setup_fence(board, | ||
| + | CoordMediator coord_mediator{& | ||
| + | Piece test_piece{& | ||
| + | KeyboardState keyboard_state; | ||
| + | Player player{& | ||
| + | player.install_action(Player:: | ||
| + | player.install_action(Player:: | ||
| + | Player:: | ||
| + | | ||
| + | BlockList dead_blocks; | ||
| + | |||
| + | // game loop | ||
| + | while (!event_processor.should_quit_game()) { | ||
| + | Uint32 start = SDL_GetTicks(); | ||
| + | | ||
| + | // process events | ||
| + | event_processor.process(); | ||
| + | keyboard_state.update(); | ||
| + | | ||
| + | // update | ||
| + | player.update(command_queue); | ||
| + | process_commands(command_queue, | ||
| + | test_piece.update(dead_blocks); | ||
| + | process_piece_stuck(test_piece, | ||
| + | | ||
| + | // render | ||
| + | SDL_SetRenderDrawColor(renderer, | ||
| + | SDL_RenderClear(renderer); | ||
| + | |||
| + | draw_fence(renderer, | ||
| + | test_piece.draw(renderer); | ||
| + | draw_blocks(renderer, | ||
| + | | ||
| + | SDL_RenderPresent(renderer); | ||
| + | |||
| + | // synch frame rate | ||
| + | Uint32 next_start = start + MsecsPerUpdate; | ||
| + | Uint32 current = SDL_GetTicks(); | ||
| + | if (next_start >= current) { | ||
| + | SDL_Delay(next_start - current); | ||
| + | } | ||
| + | } | ||
| + | | ||
| + | SDL_DestroyRenderer(renderer); | ||
| + | SDL_DestroyWindow(window); | ||
| + | SDL_Quit(); | ||
| + | |||
| + | return 0; | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | ==== rectaux.h ==== | ||
| + | |||
| + | <file cpp> | ||
| + | #ifndef RECTAUX_H | ||
| + | #define RECTAUX_H | ||
| + | |||
| + | using APoint = SDL_Point; | ||
| + | using APointF = SDL_FPoint; | ||
| + | using PPoint = SDL_Point; | ||
| + | |||
| + | #endif | ||
| + | </ | ||
| + | |||
| + | ==== gfxaux.h ==== | ||
| + | |||
| + | <file cpp> | ||
| + | #ifndef GFXAUX_H | ||
| + | #define GFXAUX_H | ||
| + | |||
| + | #include < | ||
| + | |||
| + | SDL_Color const Gray{128, 128, 128, 255}; | ||
| + | SDL_Color const White{255, 255, 255, 255}; | ||
| + | |||
| + | void draw_rectangle(SDL_Renderer *renderer, SDL_Rect const &rect, SDL_Color const &fc, SDL_Color const &oc); | ||
| + | |||
| + | struct RenderStates { | ||
| + | float tx = 0; | ||
| + | float ty = 0; | ||
| + | }; | ||
| + | |||
| + | #endif | ||
| + | </ | ||
| + | |||
| + | ==== gfxaux.cpp ==== | ||
| + | |||
| + | <file cpp> | ||
| + | #include " | ||
| + | |||
| + | void draw_rectangle(SDL_Renderer *renderer, SDL_Rect const &rect, SDL_Color const &fc, SDL_Color const &oc) { | ||
| + | SDL_SetRenderDrawColor(renderer, | ||
| + | SDL_RenderFillRect(renderer, | ||
| + | SDL_SetRenderDrawColor(renderer, | ||
| + | SDL_RenderDrawRect(renderer, | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | ==== board.h ==== | ||
| + | |||
| + | <file cpp> | ||
| + | #ifndef BOARD_H | ||
| + | #define BOARD_H | ||
| + | |||
| + | #include " | ||
| + | #include " | ||
| + | #include < | ||
| + | #include < | ||
| + | |||
| + | struct BoardInfo { | ||
| + | int arena_width; | ||
| + | int arena_height; | ||
| + | int cell_width; | ||
| + | int cell_height; | ||
| + | PPoint position; | ||
| + | }; | ||
| + | |||
| + | class CoordMediator { | ||
| + | public: | ||
| + | CoordMediator(BoardInfo const *board); | ||
| + | PPoint arena_to_pixel(APoint const &arena) const; | ||
| + | PPoint arena_to_pixel(APointF const &arena) const; | ||
| + | APoint pixel_to_arena(PPoint const &pixel) const; | ||
| + | PPoint arena_origin_px() const; | ||
| + | int arena_top() const; | ||
| + | int arena_bottom() const; | ||
| + | int arena_left() const; | ||
| + | int arena_right() const; | ||
| + | |||
| + | BoardInfo const *board_info() const; | ||
| + | |||
| + | private: | ||
| + | BoardInfo const *board_; | ||
| + | }; | ||
| + | |||
| + | using Fence = std:: | ||
| + | |||
| + | Fence setup_fence(BoardInfo const &board, | ||
| + | SDL_Color const & | ||
| + | SDL_Color const & | ||
| + | |||
| + | void draw_fence(SDL_Renderer *renderer, Fence const & | ||
| + | |||
| + | #endif | ||
| + | </ | ||
| + | |||
| + | ==== board.cpp ==== | ||
| + | |||
| + | <file cpp> | ||
| + | #include " | ||
| + | |||
| + | #include " | ||
| + | #include " | ||
| + | |||
| + | CoordMediator:: | ||
| + | : board_{board} | ||
| + | {} | ||
| + | |||
| + | SDL_Point CoordMediator:: | ||
| + | int x = arena.x * board_-> | ||
| + | int y = arena.y * board_-> | ||
| + | return {x, y}; | ||
| + | } | ||
| + | |||
| + | SDL_Point CoordMediator:: | ||
| + | int x = static_cast< | ||
| + | int y = static_cast< | ||
| + | return {x, y}; | ||
| + | } | ||
| + | |||
| + | SDL_Point CoordMediator:: | ||
| + | int x = pixel.x / board_-> | ||
| + | int y = pixel.y / board_-> | ||
| + | return {x, y}; | ||
| + | } | ||
| + | |||
| + | SDL_Point CoordMediator:: | ||
| + | int x = board_-> | ||
| + | int y = board_-> | ||
| + | return {x, y}; | ||
| + | } | ||
| + | |||
| + | int CoordMediator:: | ||
| + | return 0; | ||
| + | } | ||
| + | |||
| + | int CoordMediator:: | ||
| + | return arena_top() + board_-> | ||
| + | } | ||
| + | |||
| + | int CoordMediator:: | ||
| + | return 0; | ||
| + | } | ||
| + | int CoordMediator:: | ||
| + | return arena_left() + board_-> | ||
| + | } | ||
| + | |||
| + | BoardInfo const *CoordMediator:: | ||
| + | return board_; | ||
| + | } | ||
| + | |||
| + | Fence setup_fence(BoardInfo const &board, | ||
| + | SDL_Color const & | ||
| + | SDL_Color const & | ||
| + | Fence fence; | ||
| + | |||
| + | // left wall | ||
| + | for (int i{}; i < board.arena_height; | ||
| + | SDL_Point pt{0, i * board.cell_height}; | ||
| + | fence.emplace_back(& | ||
| + | } | ||
| + | |||
| + | // right wall | ||
| + | for (int i{}; i < board.arena_height; | ||
| + | SDL_Point pt{(board.arena_width + 1) * board.cell_width, | ||
| + | fence.emplace_back(& | ||
| + | } | ||
| + | |||
| + | // floor | ||
| + | for (int i{}; i < board.arena_width + 2; ++i) { | ||
| + | SDL_Point pt{i * board.cell_width, | ||
| + | fence.emplace_back(& | ||
| + | } | ||
| + | |||
| + | return fence; | ||
| + | } | ||
| + | |||
| + | void draw_fence(SDL_Renderer *renderer, Fence const &fence) { | ||
| + | RenderStates states{}; | ||
| + | for (Cell const &cell : fence) { | ||
| + | cell.draw(renderer, | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | ==== cell.h ==== | ||
| + | |||
| + | <file cpp> | ||
| + | #ifndef CELL_H | ||
| + | #define CELL_H | ||
| + | |||
| + | #include < | ||
| + | |||
| + | struct BoardInfo; | ||
| + | struct RenderStates; | ||
| + | |||
| + | class Cell { | ||
| + | public: | ||
| + | Cell(BoardInfo const *board, | ||
| + | | ||
| + | | ||
| + | | ||
| + | | ||
| + | ~Cell() = default; | ||
| + | | ||
| + | SDL_Point global_position() const; | ||
| + | SDL_Point local_position() const; | ||
| + | | ||
| + | void draw(SDL_Renderer *renderer, RenderStates states) const; | ||
| + | |||
| + | private: | ||
| + | BoardInfo const *board_; | ||
| + | SDL_Point origin_; | ||
| + | SDL_Point position_; | ||
| + | SDL_Color fill_color_; | ||
| + | SDL_Color outline_color_; | ||
| + | }; | ||
| + | |||
| + | #endif | ||
| + | </ | ||
| + | |||
| + | ==== cell.cpp ==== | ||
| + | |||
| + | <file cpp> | ||
| + | #include " | ||
| + | |||
| + | #include " | ||
| + | #include " | ||
| + | |||
| + | Cell:: | ||
| + | | ||
| + | | ||
| + | | ||
| + | | ||
| + | : board_{board} | ||
| + | , origin_{origin} | ||
| + | , position_{position} | ||
| + | , fill_color_{fill_color} | ||
| + | , outline_color_{outline_color} { | ||
| + | } | ||
| + | |||
| + | SDL_Point Cell:: | ||
| + | return {origin_.x + position_.x, | ||
| + | } | ||
| + | |||
| + | SDL_Point Cell:: | ||
| + | return position_; | ||
| + | } | ||
| + | |||
| + | void Cell:: | ||
| + | auto [x, y] = global_position(); | ||
| + | SDL_Rect rect{x + static_cast< | ||
| + | y + static_cast< | ||
| + | board_-> | ||
| + | draw_rectangle(renderer, | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | ==== block.h ==== | ||
| + | |||
| + | <file cpp> | ||
| + | #ifndef BLOCK_H | ||
| + | #define BLOCK_H | ||
| + | |||
| + | #include " | ||
| + | #include " | ||
| + | #include < | ||
| + | #include < | ||
| + | |||
| + | struct RenderStates; | ||
| + | class CoordMediator; | ||
| + | class Piece; | ||
| + | |||
| + | class Block { | ||
| + | public: | ||
| + | Block(CoordMediator const *coord_mediator, | ||
| + | APoint const & | ||
| + | SDL_Color const &fc, | ||
| + | SDL_Color const &oc, | ||
| + | Piece const *parent = nullptr); | ||
| + | |||
| + | void draw(SDL_Renderer *renderer, RenderStates states) const; | ||
| + | APoint local_position() const; | ||
| + | APoint global_position() const; | ||
| + | void detach_from_parent(); | ||
| + | |||
| + | private: | ||
| + | Cell cell_; | ||
| + | CoordMediator const *coord_mediator_; | ||
| + | APoint position_; | ||
| + | Piece const *parent_; | ||
| + | }; | ||
| + | |||
| + | using BlockList = std:: | ||
| + | |||
| + | void draw_blocks(SDL_Renderer *renderer, BlockList const & | ||
| + | |||
| + | #endif | ||
| + | </ | ||
| + | |||
| + | ==== block.cpp ==== | ||
| + | |||
| + | <file cpp> | ||
| + | #include " | ||
| + | |||
| + | #include " | ||
| + | #include " | ||
| + | #include " | ||
| + | |||
| + | namespace { | ||
| + | Cell create_cell(CoordMediator const *mediator, | ||
| + | | ||
| + | | ||
| + | auto *board = mediator-> | ||
| + | auto origin = mediator-> | ||
| + | return Cell{board, origin, APoint{0, 0}, fc, oc}; | ||
| + | } | ||
| + | } // ns anon | ||
| + | |||
| + | Block:: | ||
| + | | ||
| + | | ||
| + | | ||
| + | Piece const *parent) | ||
| + | : cell_{create_cell(coord_mediator, | ||
| + | , coord_mediator_{coord_mediator} | ||
| + | , position_{position} | ||
| + | , parent_{parent} | ||
| + | {} | ||
| + | | ||
| + | void Block:: | ||
| + | SDL_FPoint tt; | ||
| + | tt.x = states.tx + position_.x; | ||
| + | tt.y = states.ty + position_.y; | ||
| + | auto [tx, ty] = coord_mediator_-> | ||
| + | cell_.draw(renderer, | ||
| + | } | ||
| + | |||
| + | APoint Block:: | ||
| + | return position_; | ||
| + | } | ||
| + | |||
| + | APoint Block:: | ||
| + | if (parent_ == nullptr) { | ||
| + | return position_; | ||
| + | } | ||
| + | auto [px, py] = parent_-> | ||
| + | return {px + position_.x, | ||
| + | } | ||
| + | |||
| + | void Block:: | ||
| + | position_ = global_position(); | ||
| + | parent_ = nullptr; | ||
| + | } | ||
| + | |||
| + | void draw_blocks(SDL_Renderer *renderer, BlockList const & | ||
| + | for (auto &block : blocks) { | ||
| + | block.draw(renderer, | ||
| + | } | ||
| + | }</ | ||
| + | |||
| + | ==== piece.h ==== | ||
| + | |||
| + | <file cpp> | ||
| + | #ifndef PIECE_H | ||
| + | #define PIECE_H | ||
| + | |||
| + | #include " | ||
| + | #include " | ||
| + | #include < | ||
| + | #include < | ||
| + | |||
| + | class CoordMediator; | ||
| + | |||
| + | class Piece { | ||
| + | public: | ||
| + | Piece(CoordMediator const *coord_mediator, | ||
| + | APoint const & | ||
| + | float falling_speed, | ||
| + | unsigned stuck_delay); | ||
| + | | ||
| + | void draw(SDL_Renderer *renderer) const; | ||
| + | APoint local_position() const; | ||
| + | APoint global_position() const; | ||
| + | void update(BlockList const & | ||
| + | |||
| + | bool move_left(); | ||
| + | bool move_right(); | ||
| + | |||
| + | bool is_stuck() const; | ||
| + | void detach_blocks(BlockList & | ||
| + | void reset_position(APoint const & | ||
| + | void generate_next_shape(); | ||
| + | bool is_overlapping(BlockList const & | ||
| + | |||
| + | private: | ||
| + | int top() const; | ||
| + | int bottom() const; | ||
| + | int left() const; | ||
| + | int right() const; | ||
| + | bool is_landing(BlockList const & | ||
| + | |||
| + | private: | ||
| + | BlockList blocks_; | ||
| + | unsigned stuck_count_; | ||
| + | CoordMediator const *coord_mediator_; | ||
| + | APoint position_; | ||
| + | float falling_speed_; | ||
| + | float fy_; | ||
| + | unsigned stuck_delay_; | ||
| + | }; | ||
| + | |||
| + | #endif | ||
| + | </ | ||
| + | |||
| + | ==== piece.cpp ==== | ||
| + | |||
| + | <file cpp> | ||
| + | #include " | ||
| + | |||
| + | #include " | ||
| + | #include " | ||
| + | #include " | ||
| + | #include < | ||
| + | #include < | ||
| + | #include < | ||
| + | |||
| + | namespace { | ||
| + | bool comp_block_x(Block const &a, Block const &b) { | ||
| + | return a.local_position().x < b.local_position().x; | ||
| + | } | ||
| + | |||
| + | bool comp_block_y(Block const &a, Block const &b) { | ||
| + | return a.local_position().y < b.local_position().y; | ||
| + | } | ||
| + | } // ns anon | ||
| + | |||
| + | Piece:: | ||
| + | | ||
| + | float falling_speed, | ||
| + | | ||
| + | : stuck_count_{0} | ||
| + | , coord_mediator_{coord_mediator} | ||
| + | , position_{} | ||
| + | , falling_speed_{falling_speed} | ||
| + | , fy_{} | ||
| + | , stuck_delay_{stuck_delay} { | ||
| + | reset_position(position); | ||
| + | generate_next_shape(); | ||
| + | } | ||
| + | |||
| + | void Piece:: | ||
| + | RenderStates states{float(position_.x), | ||
| + | draw_blocks(renderer, | ||
| + | } | ||
| + | |||
| + | APoint Piece:: | ||
| + | return position_; | ||
| + | } | ||
| + | |||
| + | APoint Piece:: | ||
| + | return position_; | ||
| + | } | ||
| + | |||
| + | void Piece:: | ||
| + | if (is_landing(dead_blocks)) { | ||
| + | fy_ = std:: | ||
| + | ++stuck_count_; | ||
| + | } else { | ||
| + | fy_ += falling_speed_; | ||
| + | position_.y = static_cast< | ||
| + | stuck_count_ = 0; | ||
| + | } | ||
| + | } | ||
| + | |||
| + | bool Piece:: | ||
| + | if (left() > coord_mediator_-> | ||
| + | position_.x -= 1; | ||
| + | return true; | ||
| + | } | ||
| + | return false; | ||
| + | } | ||
| + | |||
| + | bool Piece:: | ||
| + | if (right() < coord_mediator_-> | ||
| + | position_.x += 1; | ||
| + | return true; | ||
| + | } | ||
| + | return false; | ||
| + | } | ||
| + | |||
| + | bool Piece:: | ||
| + | return stuck_count_ >= stuck_delay_; | ||
| + | } | ||
| + | |||
| + | void Piece:: | ||
| + | for (auto &block : blocks_) { | ||
| + | block.detach_from_parent(); | ||
| + | } | ||
| + | std:: | ||
| + | std:: | ||
| + | blocks_.clear(); | ||
| + | } | ||
| + | |||
| + | void Piece:: | ||
| + | position_ = position; | ||
| + | fy_ = static_cast< | ||
| + | } | ||
| + | |||
| + | void Piece:: | ||
| + | SDL_Color fc = White; | ||
| + | SDL_Color oc = Gray; | ||
| + | blocks_.emplace_back(coord_mediator_, | ||
| + | blocks_.emplace_back(coord_mediator_, | ||
| + | blocks_.emplace_back(coord_mediator_, | ||
| + | blocks_.emplace_back(coord_mediator_, | ||
| + | } | ||
| + | |||
| + | int Piece:: | ||
| + | assert(!blocks_.empty()); | ||
| + | auto b = std:: | ||
| + | return b-> | ||
| + | } | ||
| + | |||
| + | int Piece:: | ||
| + | assert(!blocks_.empty()); | ||
| + | auto b = std:: | ||
| + | return b-> | ||
| + | } | ||
| + | |||
| + | int Piece:: | ||
| + | assert(!blocks_.empty()); | ||
| + | auto b = std:: | ||
| + | return b-> | ||
| + | } | ||
| + | |||
| + | int Piece:: | ||
| + | assert(!blocks_.empty()); | ||
| + | auto b = std:: | ||
| + | return b-> | ||
| + | } | ||
| + | |||
| + | bool Piece:: | ||
| + | if (bottom() >= coord_mediator_-> | ||
| + | return true; | ||
| + | } | ||
| + | | ||
| + | for (auto & | ||
| + | for (auto & | ||
| + | auto [mx, my] = myblock.global_position(); | ||
| + | auto [dx, dy] = dead_block.global_position(); | ||
| + | if (mx == dx && my + 1 == dy) { | ||
| + | return true; | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | | ||
| + | return false; | ||
| + | } | ||
| + | |||
| + | bool Piece:: | ||
| + | for (auto & | ||
| + | for (auto & | ||
| + | auto mx = myblock.global_position().x; | ||
| + | auto [dx, dy] = dead_block.global_position(); | ||
| + | if (mx != dx) continue; | ||
| + | | ||
| + | float my = myblock.local_position().y + fy_; | ||
| + | float diff = std:: | ||
| + | if (diff < 0.6) { | ||
| + | return true; | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | return false; | ||
| + | } | ||
| + | |||
| + | </ | ||
| + | |||
| + | ==== eventprocessor.h ==== | ||
| + | |||
| + | <file cpp> | ||
| + | #ifndef EVENTPROCESSOR_H | ||
| + | #define EVENTPROCESSOR_H | ||
| + | |||
| + | class EventProcessor { | ||
| + | public: | ||
| + | EventProcessor(); | ||
| + | | ||
| + | void process(); | ||
| + | bool should_quit_game() const; | ||
| + | | ||
| + | private: | ||
| + | bool should_quit_; | ||
| + | }; | ||
| + | |||
| + | #endif | ||
| + | </ | ||
| + | |||
| + | ==== eventprocessor.cpp ==== | ||
| + | |||
| + | <file cpp> | ||
| + | #include " | ||
| + | |||
| + | #include < | ||
| + | |||
| + | EventProcessor:: | ||
| + | : should_quit_{false} | ||
| + | {} | ||
| + | |||
| + | void EventProcessor:: | ||
| + | SDL_Event event; | ||
| + | while (SDL_PollEvent(& | ||
| + | switch (event.type) { | ||
| + | case SDL_QUIT: | ||
| + | should_quit_ = true; | ||
| + | break; | ||
| + | case SDL_KEYDOWN: | ||
| + | if (event.key.keysym.scancode == SDL_SCANCODE_ESCAPE) { | ||
| + | should_quit_ = true; | ||
| + | } | ||
| + | break; | ||
| + | default: | ||
| + | break; | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | |||
| + | bool EventProcessor:: | ||
| + | return should_quit_; | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | ==== keyboardstate.h ==== | ||
| + | |||
| + | <file cpp> | ||
| + | #ifndef KEYBOARDSTATE_H | ||
| + | #define KEYBOARDSTATE_H | ||
| + | |||
| + | #include < | ||
| + | #include < | ||
| + | |||
| + | class KeyboardState { | ||
| + | public: | ||
| + | KeyboardState(); | ||
| + | | ||
| + | void update(); | ||
| + | bool pressed(SDL_Scancode scancode) const; | ||
| + | bool just_pressed(SDL_Scancode scancode) const; | ||
| + | | ||
| + | private: | ||
| + | void init(); | ||
| + | |||
| + | private: | ||
| + | std:: | ||
| + | std:: | ||
| + | }; | ||
| + | |||
| + | #endif | ||
| + | </ | ||
| + | |||
| + | ==== keyboardstate.cpp ==== | ||
| + | |||
| + | <file cpp> | ||
| + | #include " | ||
| + | |||
| + | #include < | ||
| + | |||
| + | KeyboardState:: | ||
| + | init(); | ||
| + | } | ||
| + | |||
| + | void KeyboardState:: | ||
| + | int numkeys; | ||
| + | auto *state = SDL_GetKeyboardState(& | ||
| + | | ||
| + | if (old_state_.size() < cur_state_.size()) { | ||
| + | old_state_.resize(cur_state_.size(), | ||
| + | } | ||
| + | std:: | ||
| + | | ||
| + | if (cur_state_.size() < static_cast< | ||
| + | cur_state_.resize(numkeys, | ||
| + | } | ||
| + | std:: | ||
| + | } | ||
| + | |||
| + | bool KeyboardState:: | ||
| + | return cur_state_[scancode] == 1; | ||
| + | } | ||
| + | |||
| + | bool KeyboardState:: | ||
| + | return old_state_[scancode] == 0 && cur_state_[scancode] == 1; | ||
| + | } | ||
| + | |||
| + | void KeyboardState:: | ||
| + | cur_state_.clear(); | ||
| + | old_state_.clear(); | ||
| + | | ||
| + | int numkeys; | ||
| + | auto *state = SDL_GetKeyboardState(& | ||
| + | cur_state_.resize(numkeys, | ||
| + | old_state_.resize(numkeys, | ||
| + | | ||
| + | std:: | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | ==== player.h ==== | ||
| + | |||
| + | <file cpp> | ||
| + | #ifndef PLAYER_H | ||
| + | #define PLAYER_H | ||
| + | |||
| + | #include < | ||
| + | #include <map> | ||
| + | #include < | ||
| + | |||
| + | class Piece; | ||
| + | class KeyboardState; | ||
| + | |||
| + | class Player { | ||
| + | public: | ||
| + | enum class Action { | ||
| + | MoveLeft, | ||
| + | MoveRight, | ||
| + | RotateLeft, | ||
| + | RotateRight, | ||
| + | SoftDrop, | ||
| + | HardDrop, | ||
| + | }; | ||
| + | | ||
| + | using CommandQueue = std:: | ||
| + | |||
| + | Player(Piece *piece, KeyboardState const *keyboard); | ||
| + | | ||
| + | void update(CommandQueue & | ||
| + | void install_action(Action action, SDL_Scancode scancode); | ||
| + | | ||
| + | private: | ||
| + | bool just_pressed(Action action) const; | ||
| + | |||
| + | private: | ||
| + | std:: | ||
| + | Piece *active_piece_; | ||
| + | KeyboardState const *keyboard_; | ||
| + | }; | ||
| + | |||
| + | #endif | ||
| + | </ | ||
| + | |||
| + | ==== player.cpp ==== | ||
| + | |||
| + | <file cpp> | ||
| + | #include " | ||
| + | |||
| + | #include " | ||
| + | #include " | ||
| + | |||
| + | Player:: | ||
| + | : active_piece_{piece} | ||
| + | , keyboard_{keyboard} | ||
| + | { | ||
| + | } | ||
| + | | ||
| + | void Player:: | ||
| + | for (auto [action, scancode] : action_map_) { | ||
| + | if (keyboard_-> | ||
| + | command_queue.push(action); | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | |||
| + | void Player:: | ||
| + | action_map_.insert({action, | ||
| + | } | ||
| + | |||
| + | bool Player:: | ||
| + | if (auto p = action_map_.find(action); | ||
| + | p != action_map_.end()) { | ||
| + | return keyboard_-> | ||
| + | } | ||
| + | return false; | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | ==== Makefile ==== | ||
| + | |||
| + | <file makefile> | ||
| + | SRCS = \ | ||
| + | main.cpp \ | ||
| + | board.cpp \ | ||
| + | gfxaux.cpp \ | ||
| + | cell.cpp \ | ||
| + | block.cpp \ | ||
| + | piece.cpp \ | ||
| + | player.cpp \ | ||
| + | keyboardstate.cpp \ | ||
| + | eventprocessor.cpp | ||
| + | | ||
| + | OBJS = ${SRCS: | ||
| + | DEPS = ${SRCS: | ||
| + | EXECUTABLE = tetris.exe | ||
| + | CXXFLAGS = -std=c++20 -Wall | ||
| + | CPPFLAGS = -IC: | ||
| + | LDFLAGS = -LC: | ||
| + | LDLIBS = -lmingw32 -lSDL2main -lSDL2 | ||
| + | |||
| + | .PHONY: all | ||
| + | all: ${EXECUTABLE} | ||
| + | |||
| + | ${EXECUTABLE}: | ||
| + | ${CXX} -o $@ $^ ${LDFLAGS} ${LDLIBS} | ||
| + | |||
| + | %.o: %.cpp | ||
| + | ${CXX} -c ${CXXFLAGS} -o $@ $< -MMD -MP ${CPPFLAGS} | ||
| + | |||
| + | .PHONY: clean | ||
| + | clean: | ||
| + | ${RM} ${EXECUTABLE} ${OBJS} ${DEPS} | ||
| + | |||
| + | -include ${DEPS} | ||
| + | </ | ||
