Closed NingW101 closed 9 months ago
After adding Snake project(upload with zip or pull with Github repo link), click Step
to enter the build steps configuration page, then APPLY
recommended builder steps then click Build
.
During the Building process, webinizer will give following recipes.
Recipe for checking project dependent packages
SDL
is used as dependency and this lib has been ported by emscripten toolchain. Webinizer will help to remove find_package(SDL)
in CMakeLists.txt
file and add -s USE_SDL=2
into compiler & linker flags after clicking APPLY RECIPE
`Recipe for errors not handled by Webinizer
This error is not handled by Webinizer, please try to resolve it manually:
CMake Error at CMakeLists.txt:17 (string):
string sub-command STRIP requires two arguments.
STRIP
operation which is used to remove the spaces at the beginning and the end of the string.
string(STRIP ${SDL2_LIBRARIES} SDL2_LIBRARIES)
SDL_LIBRARIES
is defined by default after the execution for find_package(SDL)
, it is not necessary anymore since we have removed the find_package
and use -s USE_SDL=2
instead, so we just remove this statement as well. BTW, we will make enhancement to detect unless library related statements in CMakeLists.txt
file then remove them as well automatically.
Recipe for main loop issue
In the source code of Snake Game as following shows, while
loop is used to continually check for user input. We should refactor these code with emscripten_set_main_loop
api to avoid hanging during the execution in the broswer.
void Game::Run(Controller const &controller, Renderer &renderer,
std::size_t target_frame_duration) {
....
while (running) {
frame_start = SDL_GetTicks();
// Input, Update, Render - the main game loop.
controller.HandleInput(running, snake);
Update();
renderer.Render(snake, food);
frame_end = SDL_GetTicks();
....
}
}
// src/main.cpp
int main() {
....
Renderer renderer(kScreenWidth, kScreenHeight, kGridWidth, kGridHeight);
Controller controller;
Game game(kGridWidth, kGridHeight);
game.Run(controller, renderer, kMsPerFrame);
....
return 0;
}
After
void Game::Run(Controller const &controller, Renderer &renderer,
std::size_t target_frame_duration) {
....
//while (running) {
frame_start = SDL_GetTicks();
// Input, Update, Render - the main game loop.
controller.HandleInput(running, snake);
Update();
renderer.Render(snake, food);
frame_end = SDL_GetTicks();
....
//}
}
//src/main.c
constexpr std::size_t kFramesPerSecond{60};
constexpr std::size_t kMsPerFrame{1000 / kFramesPerSecond};
constexpr std::size_t kScreenWidth{640};
constexpr std::size_t kScreenHeight{640};
constexpr std::size_t kGridWidth{32};
constexpr std::size_t kGridHeight{32};
Renderer renderer(kScreenWidth, kScreenHeight, kGridWidth, kGridHeight);
Controller controller;
Game *game;
void mainLoopWrapper(){
game->Run(controller, renderer, kMsPerFrame);
}
int main() {
Game gameIns(kGridWidth, kGridHeight);
game = &gameIns;
emscripten_set_main_loop(mainLoopWrapper, 0, 1);
return 0;
}
We define a wrapper function mainLoopWrapper()
and used as the callback in emscripten_set_main_loop
api. We also should remove the while(running)
in game.cpp
because emscripten_set_main_loop
will create the main loop for the program and it will continue running asynchronously in the browser, and the callback will be called on each iteration of the loop. We should be aware of that the parameters in main
function should be moved out of the function as global parameters and the classes should be instantized properly because they are used in the Game.run
callback functions.
In the original code of Snake game, the score will be displayed when user quit or end up the game, which could not be normally realized in the browser and we can make some modification to solve that.
We can insert the score prompt logic when Snake died as followings,
//game.cpp
void Game::Update() {
if (!snake.alive){
EM_ASM({
var message = 'Game Over...\nYour Score: ' + $0 + '\nSize: '+ $1;
alert(message);
}, score, snake.size);
emscripten_cancel_main_loop();
return;
}
snake.Update();
int new_x = static_cast<int>(snake.head_x);
int new_y = static_cast<int>(snake.head_y);
// Check if there's food over here
if (food.x == new_x && food.y == new_y) {
score++;
PlaceFood();
// Grow snake and increase speed.
snake.GrowBody();
snake.speed += 0.02;
}
}
close as the demo has been added
The Snake Game is classic game using SDL2, we are trying to successfully build and add it into the demos pool. Moreover, we can enhance the current advisors' implementation during porting. This issue will track the problems encountered and the corresponding solutions during the migration
The source code link of Snake Game: https://github.com/udacity/CppND-Capstone-Snake-Game