minor updates

add fruit class

update simple sprites

include variant type
This commit is contained in:
2026-07-14 13:45:07 +01:00
parent 237f906127
commit ec7060668a
18 changed files with 551 additions and 38 deletions
BIN
View File
Binary file not shown.
+9
View File
@@ -0,0 +1,9 @@
HTML Template Files
===================
The files in this directory are html templates used by emscripten when
generating html output. These are only used when the output of the compiler
has the `.html` suffix (or when `-oformat=html` is specified).
By default the `shell.html` file in this directory is used but an alternative
can be specified using the `--shell-file` flag.
+96
View File
@@ -0,0 +1,96 @@
body {
font-family: arial;
margin: 0;
padding: none;
}
.emscripten { padding-right: 0; margin-left: auto; margin-right: auto; display: block; }
div.emscripten { text-align: center; }
div.emscripten_border { border: 1px solid black; }
/* the canvas *must not* have any border or padding, or mouse coords will be wrong */
canvas.emscripten { border: 0px none; background-color: black; }
#emscripten_logo {
display: inline-block;
margin: 0;
padding: 6px;
width: 265px;
}
.spinner {
height: 30px;
width: 30px;
margin: 0;
margin-top: 20px;
margin-left: 20px;
display: inline-block;
vertical-align: top;
-webkit-animation: rotation .8s linear infinite;
-moz-animation: rotation .8s linear infinite;
-o-animation: rotation .8s linear infinite;
animation: rotation 0.8s linear infinite;
border-left: 5px solid rgb(235, 235, 235);
border-right: 5px solid rgb(235, 235, 235);
border-bottom: 5px solid rgb(235, 235, 235);
border-top: 5px solid rgb(120, 120, 120);
border-radius: 100%;
background-color: rgb(189, 215, 46);
}
@-webkit-keyframes rotation {
from {-webkit-transform: rotate(0deg);}
to {-webkit-transform: rotate(360deg);}
}
@-moz-keyframes rotation {
from {-moz-transform: rotate(0deg);}
to {-moz-transform: rotate(360deg);}
}
@-o-keyframes rotation {
from {-o-transform: rotate(0deg);}
to {-o-transform: rotate(360deg);}
}
@keyframes rotation {
from {transform: rotate(0deg);}
to {transform: rotate(360deg);}
}
#status {
display: inline-block;
vertical-align: top;
margin-top: 30px;
margin-left: 20px;
font-weight: bold;
color: rgb(120, 120, 120);
}
#progress {
height: 20px;
width: 300px;
}
#controls {
display: inline-block;
float: right;
vertical-align: top;
margin-top: 30px;
margin-right: 20px;
}
#output {
width: 100%;
height: 200px;
margin: 0 auto;
margin-top: 10px;
border-left: 0px;
border-right: 0px;
padding-left: 0px;
padding-right: 0px;
display: block;
background-color: black;
color: white;
font-family: 'Lucida Console', Monaco, monospace;
outline: none;
}
+115
View File
@@ -0,0 +1,115 @@
<!doctype html>
<html lang="en-us">
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Emscripten-Generated Code</title>
<style>{{{ SHELL_CSS }}}</style>
</head>
<body>
<a href="http://emscripten.org">{{{ SHELL_LOGO }}}</a>
<div class="spinner" id='spinner'></div>
<div class="emscripten" id="status">Downloading...</div>
<span id='controls'>
<span><input type="checkbox" id="resize">Resize canvas</span>
<span><input type="checkbox" id="pointerLock" checked>Lock/hide mouse pointer &nbsp;&nbsp;&nbsp;</span>
<span><input type="button" value="Fullscreen" onclick="Module.requestFullscreen(document.getElementById('pointerLock').checked,
document.getElementById('resize').checked)">
</span>
</span>
<div class="emscripten">
<progress value="0" max="100" id="progress" hidden=1></progress>
</div>
<div class="emscripten_border">
<canvas class="emscripten" id="canvas" oncontextmenu="event.preventDefault()" tabindex=-1></canvas>
</div>
<textarea id="output" rows="8"></textarea>
<script type='text/javascript'>
var statusElement = document.getElementById('status');
var progressElement = document.getElementById('progress');
var spinnerElement = document.getElementById('spinner');
var canvasElement = document.getElementById('canvas');
var outputElement = document.getElementById('output');
if (outputElement) outputElement.value = ''; // clear browser cache
// As a default initial behavior, pop up an alert when webgl context is lost. To make your
// application robust, you may want to override this behavior before shipping!
// See http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.15.2
canvasElement.addEventListener('webglcontextlost', (e) => {
alert('WebGL context lost. You will need to reload the page.');
e.preventDefault();
}, false);
var setStatus = (text) => {
if (!setStatus.last) setStatus.last = { time: Date.now(), text: '' };
if (text === setStatus.last.text) return;
var m = text.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/);
var now = Date.now();
// if this is a progress update, skip it if too soon
if (m && now - setStatus.last.time < 30) return;
setStatus.last.time = now;
setStatus.last.text = text;
if (m) {
text = m[1];
progressElement.value = parseInt(m[2])*100;
progressElement.max = parseInt(m[4])*100;
progressElement.hidden = false;
spinnerElement.hidden = false;
} else {
progressElement.value = null;
progressElement.max = null;
progressElement.hidden = true;
if (!text) spinnerElement.style.display = 'none';
}
statusElement.innerHTML = text;
};
#if MODULARIZE && !EXPORT_ES6
var moduleArgs = {
#else
var Module = {
#endif
print(...args) {
console.log(...args);
// These replacements are necessary if you render to raw HTML
//text = text.replace(/&/g, "&amp;");
//text = text.replace(/</g, "&lt;");
//text = text.replace(/>/g, "&gt;");
//text = text.replace('\n', '<br>', 'g');
if (outputElement) {
var text = args.join(' ');
outputElement.value += text + "\n";
outputElement.scrollTop = outputElement.scrollHeight; // focus on bottom
}
},
canvas: canvasElement,
setStatus: setStatus,
totalDependencies: 0,
monitorRunDependencies(left) {
this.totalDependencies = Math.max(this.totalDependencies, left);
setStatus(left ? 'Preparing... (' + (this.totalDependencies-left) + '/' + this.totalDependencies + ')' : 'All downloads complete.');
}
};
setStatus('Downloading...');
window.onerror = window.onunhandledrejection = () => {
// TODO: do not warn on ok events like simulating an infinite loop or exitStatus
setStatus('Exception thrown, see JavaScript console');
spinnerElement.style.display = 'none';
setStatus = (text) => {
if (text) console.error('[post-exception status] ' + text);
};
};
</script>
{{{ SCRIPT }}}
#if MODULARIZE && !EXPORT_ES6
<script type='text/javascript'>
{{{ EXPORT_NAME }}}(moduleArgs);
</script>
#endif
</body>
</html>
+51
View File
@@ -0,0 +1,51 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>Flappy Bird Bullet Hell</title>
<style>
/* Dark theme, removes scrollbars, and blocks standard browser drag behaviors */
html, body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
background-color: #111111;
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
touch-action: none;
}
/* Dynamically fits the container window while preserving your Raylib aspect ratio */
canvas {
border: none;
outline: none;
background-color: #000000;
display: block;
image-rendering: pixelated; /* Keeps 2D pixel-art crisp if it stretches */
image-rendering: crisp-edges;
max-width: 100%;
max-height: 100%;
object-fit: contain;
}
</style>
</head>
<body>
<canvas id="canvas" oncontextmenu="event.preventDefault()" tabindex="-1"></canvas>
<script type='text/javascript'>
var Module = {
canvas: (function() {
var canvas = document.getElementById('canvas');
return canvas;
})()
};
</script>
{{{ SCRIPT }}}
</body>
</html>
+152
View File
@@ -0,0 +1,152 @@
<!doctype html>
<html lang="en-us">
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Emscripten-Generated Code</title>
<style>
.emscripten { padding-right: 0; margin-left: auto; margin-right: auto; display: block; }
textarea.emscripten { font-family: monospace; width: 80%; }
div.emscripten { text-align: center; }
div.emscripten_border { border: 1px solid black; }
/* the canvas *must not* have any border or padding, or mouse coords will be wrong */
canvas.emscripten { border: 0px none; background-color: black; }
.spinner {
height: 50px;
width: 50px;
margin: 0px auto;
-webkit-animation: rotation .8s linear infinite;
-moz-animation: rotation .8s linear infinite;
-o-animation: rotation .8s linear infinite;
animation: rotation 0.8s linear infinite;
border-left: 10px solid rgb(0,150,240);
border-right: 10px solid rgb(0,150,240);
border-bottom: 10px solid rgb(0,150,240);
border-top: 10px solid rgb(100,0,200);
border-radius: 100%;
background-color: rgb(200,100,250);
}
@-webkit-keyframes rotation {
from {-webkit-transform: rotate(0deg);}
to {-webkit-transform: rotate(360deg);}
}
@-moz-keyframes rotation {
from {-moz-transform: rotate(0deg);}
to {-moz-transform: rotate(360deg);}
}
@-o-keyframes rotation {
from {-o-transform: rotate(0deg);}
to {-o-transform: rotate(360deg);}
}
@keyframes rotation {
from {transform: rotate(0deg);}
to {transform: rotate(360deg);}
}
</style>
</head>
<body>
<hr/>
<figure style="overflow:visible;" id="spinner"><div class="spinner"></div><center style="margin-top:0.5em"><strong>emscripten</strong></center></figure>
<div class="emscripten" id="status">Downloading...</div>
<div class="emscripten">
<progress value="0" max="100" id="progress" hidden=1></progress>
</div>
<div class="emscripten_border">
<canvas class="emscripten" id="canvas" oncontextmenu="event.preventDefault()" tabindex=-1></canvas>
</div>
<hr/>
<div class="emscripten">
<input type="checkbox" id="resize">Resize canvas
<input type="checkbox" id="pointerLock" checked>Lock/hide mouse pointer
&nbsp;&nbsp;&nbsp;
<input type="button" value="Fullscreen" onclick="Module.requestFullscreen(document.getElementById('pointerLock').checked,
document.getElementById('resize').checked)">
</div>
<hr/>
<textarea class="emscripten" id="output" rows="8"></textarea>
<hr>
<script type='text/javascript'>
var statusElement = document.getElementById('status');
var progressElement = document.getElementById('progress');
var spinnerElement = document.getElementById('spinner');
var canvasElement = document.getElementById('canvas');
var outputElement = document.getElementById('output');
if (outputElement) outputElement.value = ''; // clear browser cache
// As a default initial behavior, pop up an alert when webgl context is lost. To make your
// application robust, you may want to override this behavior before shipping!
// See http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.15.2
canvasElement.addEventListener("webglcontextlost", (e) => {
alert('WebGL context lost. You will need to reload the page.');
e.preventDefault();
}, false);
function setStatus(text) {
if (!setStatus.last) setStatus.last = { time: Date.now(), text: '' };
if (text === setStatus.last.text) return;
var m = text.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/);
var now = Date.now();
if (m && now - setStatus.last.time < 30) return; // if this is a progress update, skip it if too soon
setStatus.last.time = now;
setStatus.last.text = text;
if (m) {
text = m[1];
progressElement.value = parseInt(m[2])*100;
progressElement.max = parseInt(m[4])*100;
progressElement.hidden = false;
spinnerElement.hidden = false;
} else {
progressElement.value = null;
progressElement.max = null;
progressElement.hidden = true;
if (!text) spinnerElement.hidden = true;
}
statusElement.innerHTML = text;
}
#if MODULARIZE && !EXPORT_ES6
var moduleArgs = {
#else
var Module = {
#endif
print(...args) {
// These replacements are necessary if you render to raw HTML
//text = text.replace(/&/g, "&amp;");
//text = text.replace(/</g, "&lt;");
//text = text.replace(/>/g, "&gt;");
//text = text.replace('\n', '<br>', 'g');
console.log(...args);
if (outputElement) {
var text = args.join(' ');
outputElement.value += text + "\n";
outputElement.scrollTop = outputElement.scrollHeight; // focus on bottom
}
},
canvas: canvasElement,
setStatus: setStatus,
totalDependencies: 0,
monitorRunDependencies(left) {
this.totalDependencies = Math.max(this.totalDependencies, left);
setStatus(left ? 'Preparing... (' + (this.totalDependencies-left) + '/' + this.totalDependencies + ')' : 'All downloads complete.');
}
};
setStatus('Downloading...');
window.onerror = window.onunhandledrejection = () => {
setStatus('Exception thrown, see JavaScript console');
spinnerElement.style.display = 'none';
setStatus = (text) => {
if (text) console.error('[post-exception status] ' + text);
};
};
</script>
{{{ SCRIPT }}}
#if MODULARIZE && !EXPORT_ES6
<script type='text/javascript'>
{{{ EXPORT_NAME }}}(moduleArgs);
</script>
#endif
</body>
</html>
+26
View File
@@ -0,0 +1,26 @@
<!doctype html><html lang="en-us"><head><meta charset="utf-8"></head>
<body>
<canvas style='display:block; margin:auto;'></canvas>
<script>
#if !MODULARIZE
var {{{ EXPORT_NAME }}} = {};
#endif
#if WASM == 2
var supportsWasm = window.WebAssembly && location.search.indexOf('_rwasm=0') < 0;
#endif
// Depending on the build flags that one uses, different files need to be downloaded
// to load the compiled page. The right set of files will be expanded to be downloaded
// via the directive below.
{{{ DOWNLOAD_JS_AND_WASM_FILES }}}
#if SINGLE_FILE
// If we are doing a SINGLE_FILE=1 build, inlined JS runtime code follows here:
{{{ JS_CONTENTS_IN_SINGLE_FILE_BUILD }}}
#endif
</script>
</body>
</html>
+13 -6
View File
@@ -17,7 +17,8 @@ LDFLAGS = -L./external/raylib/lib \
WEB_LDFLAGS = -L./external/raylib-web/lib \
-L./external/asset_packer/bin \
-sUSE_GLFW=3 \
--preload-file $(ASSETS)
--preload-file $(ASSETS) \
--shell-file "./html/shell_custom.html"
LIBS = -lraylib -lwinmm -lgdi32 -lbox2d -lassetpacker
@@ -60,6 +61,7 @@ IMGUI_BACKEND = ./external/rlImGui/rlImGui.cpp
SRC_FILES = ./src/asset.cpp \
./src/movement.cpp \
./src/player.cpp \
./src/fruit.cpp \
./src/render.cpp \
./src/scenes/scene.cpp \
./src/scenes/game_play_scene.cpp \
@@ -75,7 +77,7 @@ OBJS_FILES = $(patsubst ./src/%.cpp,./bin/%.o,$(SRC_FILES))
DEP_FILES = $(patsubst ./bin/%.o,./bin/%.d,$(OBJS_FILES))
all: build-game run-desktop
all: build-desktop run-desktop
./bin/%.o: ./external/imgui/%.cpp
@mkdir -p bin
@@ -110,7 +112,7 @@ build-gui-backend: $(IMGUI_BACKEND_OBJS)
build-core: $(OBJS_FILES)
build-game: $(OBJS_FILES) $(IMGUI_OBJS) $(IMGUI_BACKEND_OBJS)
build-desktop: $(OBJS_FILES) $(IMGUI_OBJS) $(IMGUI_BACKEND_OBJS)
@mkdir -p bin
$(CXX) $(CXXFLAGS) $(INCLUDES) $(ENTRY_POINT) $(OBJS_FILES) $(IMGUI_OBJS) $(IMGUI_BACKEND_OBJS) $(LDFLAGS) $(LIBS) -o ./bin/main.exe
cp ./assets ./bin -rf
@@ -128,11 +130,11 @@ build-debug:
web:
@mkdir -p web
$(WEB_CXX) $(CXXFLAGS) $(WEB_INCLUDES) $(ENTRY_POINT) $(SRC_FILES) $(IMGUI_SRCS) $(IMGUI_BACKEND) $(WEB_LDFLAGS) $(WEB_LIBS) -o ./web/web.html
$(WEB_CXX) $(CXXFLAGS) $(WEB_INCLUDES) $(ENTRY_POINT) $(SRC_FILES) $(IMGUI_SRCS) $(IMGUI_BACKEND) $(WEB_LDFLAGS) $(WEB_LIBS) -o ./web/index.html
web-native:
@mkdir -p web
em++ $(CXXFLAGS) $(WEB_INCLUDES) $(ENTRY_POINT) $(SRC_FILES) $(IMGUI_SRCS) $(IMGUI_BACKEND) $(WEB_LDFLAGS) $(WEB_LIBS) -o ./web/web.html
em++ $(CXXFLAGS) $(WEB_INCLUDES) $(ENTRY_POINT) $(SRC_FILES) $(IMGUI_SRCS) $(IMGUI_BACKEND) $(WEB_LDFLAGS) $(WEB_LIBS) -o ./web/index.html
clean-desktop:
@rm -r -f ./bin
@@ -155,7 +157,7 @@ open-browser:
start-server:
$(WSL) python -m http.server 3000 -d ./web
run-web:
$(BROWSER) http://localhost:3000/web.html
$(BROWSER) http://localhost:3000/index.html
$(WSL) python -m http.server 3000 -d ./web
# $(WSL) node ./web/web.js
@@ -165,3 +167,8 @@ run-debug:
run-test: build-test
./bin/test.exe
pack-assets:
./external/asset_packer/bin/assetpacker.exe assets/
View File
+30
View File
@@ -0,0 +1,30 @@
// src\fruit.cpp
//
#include<raylib.h>
#include<entt/entt.hpp>
#include<components.hpp>
entt::entity fruit;
void create_fruit(entt::registry& registry,Texture* texture){
const auto entity = registry.create();
registry.emplace<Position>(entity,100.0f,0.0f);
registry.emplace<Fruit>(entity);
registry.emplace<Sprite>(entity,texture);
}
void update_fruit_system(entt::registry& registry,float dt){
auto view = registry.view<const Fruit, const Position, Velocity>();
for (entt::entity entity : view){
const Position& pos = view.get<Position>(entity);
Velocity& vel = view.get<Velocity>(entity);
}
}
-8
View File
@@ -1,8 +0,0 @@
[Window][Debug##Default]
Pos=60,60
Size=400,400
[Window][VBoxContainer]
Pos=540,275
Size=200,250
+1 -1
View File
@@ -41,7 +41,6 @@ class AssetLoader{
std::unordered_map<std::string,Asset> assets;
virtual void load_all_assets(std::string data_path) = 0;
virtual void unload_all_assets() = 0;
AssetReader get_reader();
public:
AssetLoader(std::string asset_data);
@@ -49,6 +48,7 @@ class AssetLoader{
virtual void load_asset(std::string path) = 0;
virtual void unload_asset(std::string path) = 0;
virtual Asset get_asset(std::string path) = 0;;
AssetReader get_reader();
};
+1 -1
View File
@@ -3,7 +3,7 @@
#include <asset/asset.hpp>
#include <raylib.h>
class RaylibAssetLoader : AssetLoader{
class RaylibAssetLoader : public AssetLoader{
protected:
void load_all_assets(std::string asset_list) override {};
void unload_all_assets() override;
+2
View File
@@ -13,6 +13,8 @@ struct Velocity{
struct Player{};
struct Fruit{};
struct Sprite{
Texture *texture;
};
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#include<raylib.h>
#include<entt/entt.hpp>
void create_fruit(entt::registry& registry,Texture* texture);
void update_fruit_system(entt::registry& registry,float dt);
+1 -1
View File
@@ -79,7 +79,7 @@ int main(){
SetTargetFPS(60);
// SetTraceLogLevel(TraceLogLevel::LOG_NONE);
InitWindow(screen_width, screen_height, "Bullet bird");
InitWindow(screen_width, screen_height, "Snake");
SetWindowState(FLAG_WINDOW_RESIZABLE);
+34 -16
View File
@@ -21,30 +21,48 @@ void update_player_system(entt::registry& registry,float dt){
auto view = registry.view<const Player, const Position, Velocity>();
// Constants for fine-tuning game feel
const float GRAVITY = 900.0f; // Constant downward pull (pixels/sec^2)
const float JUMP_FORCE = -350.0f; // Negative force to burst UPWARD
// const float GRAVITY = 900.0f; // Constant downward pull (pixels/sec^2)
// const float JUMP_FORCE = -350.0f; // Negative force to burst UPWARD
for (entt::entity entity : view){
const Position& pos = view.get<Position>(entity);
Velocity& vel = view.get<Velocity>(entity);
const float SPEED = 350;
if (pos.x < -50 && vel.dx < 0){
vel.dx *= -1;
} else if (pos.x > (GetScreenWidth()-250) && vel.dx > 0){
vel.dx *= -1;
if (IsKeyPressed(KEY_W)){
if (vel.dy > 0)
continue;
vel.dx = 0;
vel.dy = -SPEED;
} else if (IsKeyPressed(KEY_S)){
if (vel.dy < 0)
continue;
vel.dx = 0;
vel.dy = SPEED;
} else if (IsKeyPressed(KEY_A)){
if (vel.dx > 0)
continue;
vel.dx = -SPEED;
vel.dy = 0;
} else if (IsKeyPressed(KEY_D)){
if (vel.dx < 0)
continue;
vel.dx = SPEED;
vel.dy = 0;
}
if (IsKeyPressed(KEY_SPACE)){
vel.dy = JUMP_FORCE;
}
vel.dy += GRAVITY * dt;
// if (pos.x < -50 && vel.dx < 0){
// vel.dx *= -1;
// } else if (pos.x > (GetScreenWidth()-250) && vel.dx > 0){
// vel.dx *= -1;
// }
// if (IsKeyPressed(KEY_SPACE)){
// vel.dy = JUMP_FORCE;
// }
// vel.dy += GRAVITY * dt;
}
view.each([dt,GRAVITY,JUMP_FORCE](entt::entity entity,const Position &position, auto &velocity){
if (position.x > 100 && position.x < 120){
velocity.dx += 20;
}
});
}
+11 -5
View File
@@ -1,23 +1,29 @@
#include <imgui.h>
#include <player.hpp>
#include <movement.hpp>
#include <scene.hpp>
#include <render.hpp>
#include <asset/raylib_asset.hpp>
#include <fruit.hpp>
#include <asset/raylib_asset.hpp>
#include <imgui.h>
#include <iostream>
#include <format>
GamePlayScene::GamePlayScene() {
registry = entt::registry();
asset_loader.load_asset("assets/bird.png");
create_player(registry,asset_loader.get_texture("assets/bird.png"));
asset_loader.load_asset("assets/snake_head.png");
asset_loader.load_asset("assets/fruit.png");
create_player(registry,asset_loader.get_texture("assets/snake_head.png"));
create_fruit(registry,asset_loader.get_texture("assets/fruit.png"));
}
void GamePlayScene::update(float dt) {
update_movement_system(registry, dt);
update_player_system(registry, dt);
update_fruit_system(registry, dt);
}
@@ -49,5 +55,5 @@ void GamePlayScene::ui() {
GamePlayScene::~GamePlayScene(){
asset_loader.unload_asset("assets/bird.png");
asset_loader.unload_asset("assets/snake_head.png");
}