diff --git a/.github/workflows/ros-tests.yml b/.github/workflows/ros-tests.yml index 209c6563..0e75ac45 100644 --- a/.github/workflows/ros-tests.yml +++ b/.github/workflows/ros-tests.yml @@ -18,10 +18,10 @@ jobs: - name: Checkout repository uses: actions/checkout@v3 - - name: Update ROS signing key (per ROS migration guide) - run: | - sudo rm /usr/share/keyrings/ros2-latest-archive-keyring.gpg - sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros2-latest-archive-keyring.gpg + # - name: Update ROS signing key (per ROS migration guide) + # run: | + # sudo rm /usr/share/keyrings/ros2-latest-archive-keyring.gpg + # sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros2-latest-archive-keyring.gpg - name: Configure environment and install Conan release run: | @@ -44,10 +44,10 @@ jobs: - name: Checkout repository uses: actions/checkout@v3 - - name: Update ROS signing key (per ROS migration guide) - run: | - sudo rm /usr/share/keyrings/ros2-latest-archive-keyring.gpg - sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros2-latest-archive-keyring.gpg + # - name: Update ROS signing key (per ROS migration guide) + # run: | + # sudo rm /usr/share/keyrings/ros2-latest-archive-keyring.gpg + # sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros2-latest-archive-keyring.gpg - name: Configure environment and install Conan from develop2 branch run: | diff --git a/examples/cross_build/emscripten/bindings/CMakeLists.txt b/examples/cross_build/emscripten/bindings/CMakeLists.txt new file mode 100644 index 00000000..0be17f0f --- /dev/null +++ b/examples/cross_build/emscripten/bindings/CMakeLists.txt @@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 3.15) +project(wasm_example CXX) + +find_package(Eigen3 REQUIRED) +find_package(ZLIB REQUIRED) +find_package(fmt REQUIRED) +add_executable(wasm_example main.cpp) + +target_link_libraries(${PROJECT_NAME} PRIVATE Eigen3::Eigen ZLIB::ZLIB fmt::fmt) + +# Set the executable suffix to .html in order to generate a html page by +# Emscripten (there is no way of setting this from a user toolchain or +# conanfile as it is later overridden by the Emscripten toolchain) +set(CMAKE_EXECUTABLE_SUFFIX ".html") diff --git a/examples/cross_build/emscripten/bindings/README.md b/examples/cross_build/emscripten/bindings/README.md new file mode 100644 index 00000000..1bc3febd --- /dev/null +++ b/examples/cross_build/emscripten/bindings/README.md @@ -0,0 +1,39 @@ +# WASM project with bindings and conan dependency + +## Build and run + +To compile the project: + +```sh +$ conan build . -pr:h ../profiles/wasm32 --build=missing +``` + +To open a WASM webpage locally, most of the browsers will complain due to security reasons as WASM must be loaded asynchronous + +The easiest way of opening the generated webpage (should be in `build/release-wasm/wasm_example.html`) is by running a local server. +This can be done via `emrun` command: + +`emrun` is packaged with `emskd` recipe so it should be available by activating build environment: + +**POSIX** +```sh +$ source build/release-wasm/generators/conanbuild.sh +``` + +**Windows** +```sh +$ build\release-wasm\generators\conanbuild.bat +``` + +By this time, `emrun`, `node`, and other JS/WASM tools should be available in the path: + +```sh +$ emrun --browser build/release-wasm/wasm_example.html +``` + +Or using python `http.server` module: + +```sh +$ python -m http.server 8080 +``` +Then, navigating to your build folder and open `wasm_example.html` diff --git a/examples/cross_build/emscripten/bindings/ci_test_example.py b/examples/cross_build/emscripten/bindings/ci_test_example.py new file mode 100644 index 00000000..bc30cbcd --- /dev/null +++ b/examples/cross_build/emscripten/bindings/ci_test_example.py @@ -0,0 +1,8 @@ +import os +from test.examples_tools import run + +run("conan build . --build=missing --profile:host ../profiles/wasm32") + +assert os.path.exists(os.path.join("build", "release-wasm", "wasm_example.html")) +assert os.path.exists(os.path.join("build", "release-wasm", "wasm_example.js")) +assert os.path.exists(os.path.join("build", "release-wasm", "wasm_example.wasm")) diff --git a/examples/cross_build/emscripten/bindings/conanfile.py b/examples/cross_build/emscripten/bindings/conanfile.py new file mode 100644 index 00000000..2075dca7 --- /dev/null +++ b/examples/cross_build/emscripten/bindings/conanfile.py @@ -0,0 +1,43 @@ +from conan import ConanFile +from conan.tools.cmake import CMake, CMakeDeps, CMakeToolchain, cmake_layout +from conan.errors import ConanInvalidConfiguration + + +class WasmExampleRecipe(ConanFile): + name = "wasm-example" + version = "1.0" + package_type = "application" + settings = "os", "compiler", "build_type", "arch" + + def layout(self): + cmake_layout(self) + + def requirements(self): + self.requires("eigen/3.4.0") + self.requires("zlib/1.3.1") + self.requires("fmt/11.1.4") + + def validate(self): + if self.settings.os != "Emscripten": + raise ConanInvalidConfiguration("This example is only supported on Emscripten.") + + def generate(self): + deps = CMakeDeps(self) + deps.generate() + tc = CMakeToolchain(self) + + # HEAPxx values need to be exported explicitly since Emscripten 4.0.7 + # https://github.com/emscripten-core/emscripten/blob/main/ChangeLog.md#407---041525 + tc.extra_exelinkflags.append( + "-sEXPORTED_FUNCTIONS=['_malloc','_free'] \ + -sEXPORTED_RUNTIME_METHODS=['ccall','cwrap','getValue','setValue','HEAPF32'] \ + -sALLOW_MEMORY_GROWTH=1 \ + -sNO_EXIT_RUNTIME=1 \ + --shell-file ${CMAKE_SOURCE_DIR}/shell.html" + ) + tc.generate() + + def build(self): + cmake = CMake(self) + cmake.configure() + cmake.build() diff --git a/examples/cross_build/emscripten/bindings/main.cpp b/examples/cross_build/emscripten/bindings/main.cpp new file mode 100644 index 00000000..ea643d82 --- /dev/null +++ b/examples/cross_build/emscripten/bindings/main.cpp @@ -0,0 +1,59 @@ +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +#define EXTERN extern "C" +#else +#define EXTERN +#endif + +EXTERN EMSCRIPTEN_KEEPALIVE uint32_t fib(uint32_t n) { + std::cout << "Calculating Fibonacci for n = " << n << std::endl; + if (n <= 1) + return n; + uint32_t a = 0, b = 1, c; + for (uint32_t i = 2; i <= n; ++i) { + c = a + b; + a = b; + b = c; + } + return c; +} + +EXTERN EMSCRIPTEN_KEEPALIVE void printMessage(const char *message) { + std::cout << "Message from C: " << message << std::endl; + std::string script = + "alert('Message from C++: " + std::string(message) + "')"; + std::cout << "Executing script: " << script << std::endl; + emscripten_run_script(script.c_str()); +} + +EXTERN EMSCRIPTEN_KEEPALIVE void addOne(int32_t *input, int32_t *output) { + *output = *input + 1; +} + +EXTERN EMSCRIPTEN_KEEPALIVE float sumArray(const float *data, int32_t size) { + fmt::print("Data input: "); + for (int i = 0; i < size; ++i) { + fmt::print("{} ", data[i]); + } + std::cout << std::endl; + Eigen::Map vec(data, size); + return vec.sum(); +} + +EXTERN EMSCRIPTEN_KEEPALIVE void getZlibVersion() { + fmt::print("Zlib version being used: {}\n", zlibVersion()); +} + +int main() { + std::cout << "Hello World!" << std::endl; + auto data = new float[5]{1.0f, 2.0f, 3.0f, 4.0f, 5.0f}; + std::cout << sumArray(data, 5) << std::endl; + fmt::print(zlibVersion()); +} diff --git a/examples/cross_build/emscripten/bindings/shell.html b/examples/cross_build/emscripten/bindings/shell.html new file mode 100644 index 00000000..40b53c26 --- /dev/null +++ b/examples/cross_build/emscripten/bindings/shell.html @@ -0,0 +1,166 @@ + + + +

Conan C++ Emscripten Example

+
+
Downloading...
+
+ +
+ +
+ + + +
+ + +
+ + + + +

+
+ + + +

+
+ + + + +

+ + + {{{ SCRIPT }}} + + diff --git a/examples/cross_build/emscripten/profiles/asmjs b/examples/cross_build/emscripten/profiles/asmjs new file mode 100644 index 00000000..b587992f --- /dev/null +++ b/examples/cross_build/emscripten/profiles/asmjs @@ -0,0 +1,8 @@ +include(./emsdk-base) + +[settings] +arch=asm.js + +[conf] +tools.build:exelinkflags+=['-sMAXIMUM_MEMORY=2GB', '-sINITIAL_MEMORY=64MB'] +tools.build:sharedlinkflags+=['-sMAXIMUM_MEMORY=2GB', '-sINITIAL_MEMORY=64MB'] diff --git a/examples/cross_build/emscripten/profiles/emsdk-base b/examples/cross_build/emscripten/profiles/emsdk-base new file mode 100644 index 00000000..0982d9f5 --- /dev/null +++ b/examples/cross_build/emscripten/profiles/emsdk-base @@ -0,0 +1,28 @@ +# Note: this profile uses emsdk package from Conan Center Index +# To use a local emsdk installation use -pr:h -pr:h emsdk-native + +[settings] +build_type=Release +compiler=emcc +compiler.cppstd=17 +compiler.libcxx=libc++ +compiler.version=4.0.10 +os=Emscripten + +[tool_requires] +emsdk/4.0.10 +ninja/[*] + +[conf] +tools.build:exelinkflags+=['-sALLOW_MEMORY_GROWTH=1'] +tools.build:sharedlinkflags+=['-sALLOW_MEMORY_GROWTH=1'] + +# Set Ninja as default generator as it is faster and will sove issues on Windows +tools.cmake.cmaketoolchain:generator=Ninja + +# Verbosity to see emcc invocations +tools.build:verbosity=verbose +tools.compilation:verbosity=verbose + +# Distinguish between architectures +tools.cmake.cmake_layout:build_folder_vars=['settings.build_type', 'settings.arch'] diff --git a/examples/cross_build/emscripten/profiles/emsdk-native b/examples/cross_build/emscripten/profiles/emsdk-native new file mode 100644 index 00000000..9470c5bb --- /dev/null +++ b/examples/cross_build/emscripten/profiles/emsdk-native @@ -0,0 +1,15 @@ +include(./emsdk-base) + +[platform_tool_requires] +emsdk/4.0.10 + +[conf] +tools.build:compiler_executables={'c':'emcc', 'cpp':'em++'} + +[buildenv] +CC=emcc +CXX=em++ +AR=emar +NM=emnm +RANLIB=emranlib +STRIP=emstrip diff --git a/examples/cross_build/emscripten/profiles/wasm32 b/examples/cross_build/emscripten/profiles/wasm32 new file mode 100644 index 00000000..d3349757 --- /dev/null +++ b/examples/cross_build/emscripten/profiles/wasm32 @@ -0,0 +1,10 @@ +include(./emsdk-base) + +[settings] +arch=wasm + +[conf] +tools.build:exelinkflags+=['-sMAXIMUM_MEMORY=4GB', '-sINITIAL_MEMORY=64MB'] +tools.build:sharedlinkflags+=['-sMAXIMUM_MEMORY=4GB', '-sINITIAL_MEMORY=64MB'] + +#tools.build:exelinkflags+=['-sMIN_NODE_VERSION=230000'] diff --git a/examples/cross_build/emscripten/profiles/wasm64 b/examples/cross_build/emscripten/profiles/wasm64 new file mode 100644 index 00000000..8923a65c --- /dev/null +++ b/examples/cross_build/emscripten/profiles/wasm64 @@ -0,0 +1,13 @@ +include(./emsdk-base) + +[settings] +arch=wasm64 + +[conf] +# In this early stage of wasm64, ALLOW_MEMORY_GROWTH is not having effect. Also it may not be the most efficient solution. +# wasm64 for now needs to declare INITIAL_MEMORY as the maximum memory +tools.build:exelinkflags+=['-sMAXIMUM_MEMORY=16GB', '-sINITIAL_MEMORY=16GB', '-sASSERTIONS'] +tools.build:sharedlinkflags+=['-sMAXIMUM_MEMORY=16GB', '-sINITIAL_MEMORY=16GB', '-sASSERTIONS'] + +#tools.build:exelinkflags+=['-sMIN_NODE_VERSION=221600'] +#tools.build:sharedlinkflags+=['-sMIN_NODE_VERSION=221600'] diff --git a/examples/cross_build/emscripten/wasm64/CMakeLists.txt b/examples/cross_build/emscripten/wasm64/CMakeLists.txt new file mode 100644 index 00000000..f6a48836 --- /dev/null +++ b/examples/cross_build/emscripten/wasm64/CMakeLists.txt @@ -0,0 +1,4 @@ +cmake_minimum_required(VERSION 3.15) +project(wasm-alloc CXX) + +add_executable(wasm-alloc main.cpp) diff --git a/examples/cross_build/emscripten/wasm64/README.md b/examples/cross_build/emscripten/wasm64/README.md new file mode 100644 index 00000000..d7f3ee70 --- /dev/null +++ b/examples/cross_build/emscripten/wasm64/README.md @@ -0,0 +1,70 @@ +# WASM 32 bit vs 64 bit project comparison + + +This basic example aims to show the differences between cross compiling a project with `arch=wasm` (which stands for `wasm32` bits) +and `arch=wasm64` the newly introduced architecture which supports WebAssembly 64-bits. + + +To compile the project in 32 bits follow this instructions: + +```sh +$ conan build . -pr ../profiles/wasm32 +$ node build/release-wasm/wasm-alloc.js + +Current allocated: 1 GiB +Current allocated: 2 GiB +Current allocated: 3 GiB +Failed after allocating 4064 MiB ~ 3 GiB +``` + +As we can see, in 32 bits mode, we can allocate up to 4 GB of dynamic memory. This is a limitation of `wasm32` architecture. +I we take a look at the `wasm32` profile, we can see `sMAXIMUM_MEMORY=4GB`. Trying to increase this number will result in a compilation error similar to this one: + +``` +wasm-ld: error: maximum memory too large, cannot be greater than 4294967296 +``` + +This is the main reason for `wasm64` to exist. Mind than `wasm64` is still under development: +To compile this project in `64 bit` mode, run the following commands: + +```sh +$ conan build . -pr ../profiles/wasm64 +$ node build/release-wasm64/wasm-alloc.js + +Current allocated: 1 GiB +Current allocated: 2 GiB +Current allocated: 3 GiB +Current allocated: 4 GiB +Current allocated: 5 GiB +Current allocated: 6 GiB +Current allocated: 7 GiB +Current allocated: 8 GiB +Current allocated: 9 GiB +Current allocated: 10 GiB +Current allocated: 11 GiB +Current allocated: 12 GiB +Current allocated: 13 GiB +Current allocated: 14 GiB +Current allocated: 15 GiB +Failed after allocating 16352 MiB ~ 15 GiB +``` + +The difference is notable, the 4 GB limitation does not exist more in a `64 bit` architecture. +The dynamic memory limit could be easily increased by modifying the profile. + + +## Binary inspection + +Another way of determining if a WASM lib is 32 or 64 bit, `wasm-objdump` command can be used (must be downloaded first): + +```sh +$ wasm-objdump -d build/release-wasm/wasm-alloc.wasm | grep '\.load' +``` +This should output `i32.load`. + +But the following should show plenty of `i64.load`, indicating `64-bit` operations: + +```sh +$ wasm-objdump -d build/release-wasm64/wasm-alloc.wasm | grep '\.load' +``` + diff --git a/examples/cross_build/emscripten/wasm64/ci_test_example.py b/examples/cross_build/emscripten/wasm64/ci_test_example.py new file mode 100644 index 00000000..33fb2549 --- /dev/null +++ b/examples/cross_build/emscripten/wasm64/ci_test_example.py @@ -0,0 +1,20 @@ +import platform +from test.examples_tools import chdir, run + +run("conan build . --build=missing --profile:host ../profiles/wasm32") +# wasm64 not yet added to conan-io: https://github.com/conan-io/conan/pull/18400 +# run("conan build . --build=missing --profile:host ../profiles/wasm64") + +if platform.system() == "Windows": + with chdir("build"): + run("generators\\conanbuild.bat && node --version && node release-wasm\\wasm-alloc.js") + # Needs at least Node.js 24.0.0 + # run("generators\\conanbuild.bat && node --version && node release-wasm64\\wasm-alloc.js") +else: + with chdir("build/release-wasm"): + run(". generators/conanbuild.sh && node --version && node wasm-alloc.js") + + # Needs at least Node.js 24.0.0 + # with chdir("build/release-wasm64"): + # run(". generators/conanbuild.sh && node --version && node wasm-alloc.js") + diff --git a/examples/cross_build/emscripten/wasm64/conanfile.py b/examples/cross_build/emscripten/wasm64/conanfile.py new file mode 100644 index 00000000..a7453498 --- /dev/null +++ b/examples/cross_build/emscripten/wasm64/conanfile.py @@ -0,0 +1,23 @@ +from conan import ConanFile +from conan.tools.cmake import CMakeToolchain, CMake, cmake_layout, CMakeDeps + + +class WasmAllocatorRecipe(ConanFile): + name = "wasm-alloc" + version = "1.0" + package_type = "application" + settings = "os", "compiler", "build_type", "arch" + + def layout(self): + cmake_layout(self) + + def generate(self): + deps = CMakeDeps(self) + deps.generate() + tc = CMakeToolchain(self) + tc.generate() + + def build(self): + cmake = CMake(self) + cmake.configure() + cmake.build() diff --git a/examples/cross_build/emscripten/wasm64/main.cpp b/examples/cross_build/emscripten/wasm64/main.cpp new file mode 100644 index 00000000..f7b30c16 --- /dev/null +++ b/examples/cross_build/emscripten/wasm64/main.cpp @@ -0,0 +1,37 @@ +#include +#include +#include +#include + +int main() { + const size_t GIGABYTE = 1024 * 1024 * 1024; + const size_t CHUNK_SIZE = 32 * 1024 * 1024; // 32 MiB + std::vector allocations; + size_t total_allocated = 0; + + while (true) { + char *block = static_cast(malloc(CHUNK_SIZE)); + if (!block) { + std::cerr << "Failed after allocating " << total_allocated / (1024 * 1024) + << " MiB ~ " << total_allocated / GIGABYTE << " GiB" + << std::endl; + break; + } + + // Touch memory to ensure it's actually committed (not lazy-allocated) + std::memset(block, 0xAB, CHUNK_SIZE); + allocations.push_back(block); + total_allocated += CHUNK_SIZE; + if (total_allocated % GIGABYTE == 0) { + std::cout << "Current allocated: " << total_allocated / GIGABYTE << " GiB" + << std::endl; + } + } + + // Free the memory afterward + for (char *ptr : allocations) { + free(ptr); + } + + return 0; +} diff --git a/examples/libraries/raylib/introduction/CMakeLists.txt b/examples/libraries/raylib/introduction/CMakeLists.txt index 665b9004..9e0b0b5c 100644 --- a/examples/libraries/raylib/introduction/CMakeLists.txt +++ b/examples/libraries/raylib/introduction/CMakeLists.txt @@ -1,8 +1,14 @@ cmake_minimum_required(VERSION 3.23) project(runner_game) -set(CMAKE_CXX_STANDARD 17) find_package(raylib) add_executable(runner_game main.cpp) target_link_libraries(runner_game raylib) + +if(EMSCRIPTEN) + # Set the executable suffix to .html in order to generate a html page by + # Emscripten (there is no way of setting this from a user toolchain or + # conanfile as it is later overridden by the Emscripten toolchain) + set(CMAKE_EXECUTABLE_SUFFIX ".html") +endif() diff --git a/examples/libraries/raylib/introduction/README.md b/examples/libraries/raylib/introduction/README.md index d823e07f..0951150c 100644 --- a/examples/libraries/raylib/introduction/README.md +++ b/examples/libraries/raylib/introduction/README.md @@ -1,3 +1,18 @@ # Example using raylib to create a game -Example for using [raylib](https://www.raylib.com/) to create a simple runner game. \ No newline at end of file +Example for using [raylib](https://www.raylib.com/) to create a simple runner game. + + +## Emscripten - WebAssembly - WASM - compilation + +- `CMakeLists.txt` will generate a `html` target which can bee opened by a web browser +- `conanfile.py` `generate()` will link against raylib correctly using WASM + - `-sUSE_GLFW=3 -sASYNCIFY`: see [raylib web manual](https://github.com/raysan5/raylib/wiki/Working-for-Web-(HTML5)#23-using-cmake) for a deeper explanation + - `--shell-file`: generate the html code from the template + +![image](https://github.com/user-attachments/assets/941b5922-edb6-400f-9d38-ba805fc8a3ab) + + +### Build and run + +See instructions in [bindings example](https://github.com/conan-io/examples2/tree/main/examples/cross_build/wasm/bindings) diff --git a/examples/libraries/raylib/introduction/ci_test_example.py b/examples/libraries/raylib/introduction/ci_test_example.py index 34504fff..1da845b6 100644 --- a/examples/libraries/raylib/introduction/ci_test_example.py +++ b/examples/libraries/raylib/introduction/ci_test_example.py @@ -1,5 +1,6 @@ import platform from test.examples_tools import run +import os print("raylib example") @@ -11,3 +12,11 @@ else: run("cmake --preset conan-release") run("cmake --build --preset conan-release") + + +# WASM tests +run("conan build . --build=missing --profile:host ../../../cross_build/emscripten/profiles/wasm32") + +assert os.path.exists(os.path.join("build", "release-wasm", "runner_game.html")) +assert os.path.exists(os.path.join("build", "release-wasm", "runner_game.js")) +assert os.path.exists(os.path.join("build", "release-wasm", "runner_game.wasm")) diff --git a/examples/libraries/raylib/introduction/conanfile.py b/examples/libraries/raylib/introduction/conanfile.py index 2b565b0e..12512a69 100644 --- a/examples/libraries/raylib/introduction/conanfile.py +++ b/examples/libraries/raylib/introduction/conanfile.py @@ -1,13 +1,30 @@ from conan import ConanFile -from conan.tools.cmake import cmake_layout, CMakeToolchain +from conan.tools.cmake import CMake, CMakeToolchain, cmake_layout +from conan.tools.build import check_min_cppstd class ConanApplication(ConanFile): package_type = "application" settings = "os", "compiler", "build_type", "arch" - generators = "CMakeDeps", "CMakeToolchain" + generators = "CMakeDeps" def layout(self): cmake_layout(self) def requirements(self): self.requires("raylib/5.5") + + def validate(self): + check_min_cppstd(self, "17") + + def generate(self): + tc = CMakeToolchain(self) + if self.settings.os == "Emscripten": + tc.extra_exelinkflags.append( + "-sUSE_GLFW=3 -sASYNCIFY --shell-file=${CMAKE_SOURCE_DIR}/shell.html" + ) + tc.generate() + + def build(self): + cmake = CMake(self) + cmake.configure() + cmake.build() diff --git a/examples/libraries/raylib/introduction/main.cpp b/examples/libraries/raylib/introduction/main.cpp index 2cd56404..4bbe3167 100644 --- a/examples/libraries/raylib/introduction/main.cpp +++ b/examples/libraries/raylib/introduction/main.cpp @@ -1,5 +1,5 @@ -#include "raylib.h" #include +#include "raylib.h" int main() { // --- Initialization --- @@ -39,7 +39,7 @@ int main() { if (!gameOver) { // Jump logic - if (IsKeyPressed(KEY_SPACE) && player.y + player.height >= groundY) { + if ((IsKeyPressed(KEY_SPACE) || IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) && player.y + player.height >= groundY) { vy = jumpImpulse; } vy += gravity * dt; @@ -74,7 +74,7 @@ int main() { } } else { - if (IsKeyPressed(KEY_R)) { + if (IsKeyPressed(KEY_R) || IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) { // reset everything player.y = screenH - 80; vy = 0; @@ -97,7 +97,7 @@ int main() { DrawText(TextFormat("Score: %d", score), 10, 10, 20, BLACK); if (gameOver) { - DrawText("GAME OVER! Press R to restart", 200, screenH/2 - 20, 20, MAROON); + DrawText("GAME OVER! Press R or left click to restart", 180, screenH/2 - 20, 20, MAROON); } EndDrawing(); diff --git a/examples/libraries/raylib/introduction/shell.html b/examples/libraries/raylib/introduction/shell.html new file mode 100644 index 00000000..f5818562 --- /dev/null +++ b/examples/libraries/raylib/introduction/shell.html @@ -0,0 +1,267 @@ + + + + + + + Conan WASM game example + + + + + + + + +
+ +
+ + + + + + + + {{{ SCRIPT }}} + +