diff --git a/include/app/app.h b/include/app/app.h index e9947d3..d1bacd4 100644 --- a/include/app/app.h +++ b/include/app/app.h @@ -10,6 +10,10 @@ enum Type { IMAGE, TEXT, RECT }; +enum ClickMode { PASS, + BLOCK, + IGNORE }; + namespace app { struct Element { Type type; @@ -19,7 +23,7 @@ struct Element { Vec2 scale = {1, 1}; float alpha = 1.0f; float z = 0; - bool click_through = false; + ClickMode click_mode = ClickMode::BLOCK; Ruby::Block on_click; Element(Type type) : type(type) {} @@ -191,11 +195,12 @@ struct Scene { Vec2 size = e->size(); Rect rect = Rect::from(size); if (rect.contains(world_to_local(position, e))) { - result.push_back(*it); - if (e->click_through) - continue; - else + if (e->click_mode == ClickMode::PASS) { + result.push_back(*it); + } else if (e->click_mode == ClickMode::BLOCK) { + result.push_back(*it); break; + } } } diff --git a/include/binding/definitions.h b/include/binding/definitions.h index b0e9fa9..56115fd 100644 --- a/include/binding/definitions.h +++ b/include/binding/definitions.h @@ -320,7 +320,7 @@ static mrb_value ElementText_init(mrb_state *mrb, mrb_value self) { float x = 0, y = 0, z = 0, rotation = 0; float scale_x = 1, scale_y = 1, alpha = 1; float pivot_x = 0.5, pivot_y = 0.5; - bool click_through = false; + mrb_sym click_mode_sym = sym_block; if (!mrb_nil_p(kwargs)) { mrb_value pos_hash = HASH_GET(kwargs, position); if (!mrb_nil_p(pos_hash)) { @@ -343,7 +343,10 @@ static mrb_value ElementText_init(mrb_state *mrb, mrb_value self) { HASH_FLOAT(kwargs, z, z); HASH_FLOAT(kwargs, rotation, rotation); HASH_FLOAT(kwargs, alpha, alpha); - HASH_BOOL(kwargs, click_through, click_through); + mrb_value v = HASH_GET(kwargs, click_mode); + if (!mrb_nil_p(v)) { + click_mode_sym = mrb_symbol(v); + } } uint64_t id = get_id_Font(mrb, font_val); @@ -362,7 +365,15 @@ static mrb_value ElementText_init(mrb_state *mrb, mrb_value self) { element->pivot = {pivot_x, pivot_y}; element->alpha = alpha; element->z = z; - element->click_through = click_through; + + std::string click_mode = mrb_sym2name(mrb, click_mode_sym); + + if (click_mode == "pass") + element->click_mode = ClickMode::PASS; + else if (click_mode == "block") + element->click_mode = ClickMode::BLOCK; + else if (click_mode == "ignore") + element->click_mode = ClickMode::IGNORE; s->id = app::element_pool.acquire(element); app::element_pool.use(s->id); @@ -620,19 +631,41 @@ static mrb_value element_text_z_set(mrb_state *mrb, mrb_value self) { return mrb_nil_value(); } -static mrb_value element_text_click_through_get(mrb_state *mrb, mrb_value self) { +static mrb_value element_text_click_mode_get(mrb_state *mrb, mrb_value self) { uint64_t id = get_id_ElementText(mrb, self); app::EText *element = (app::EText *)app::element_pool[id]; - return mrb_bool_value(element->click_through); + + std::string click_mode_str; + switch (element->click_mode) { + case ClickMode::PASS: + click_mode_str = "pass"; + break; + case ClickMode::BLOCK: + click_mode_str = "block"; + break; + case ClickMode::IGNORE: + click_mode_str = "ignore"; + break; + } + + return mrb_symbol_value(mrb_intern_cstr(mrb, click_mode_str.c_str())); } -static mrb_value element_text_click_through_set(mrb_state *mrb, mrb_value self) { - mrb_bool click_through; - mrb_get_args(mrb, "b", &click_through); +static mrb_value element_text_click_mode_set(mrb_state *mrb, mrb_value self) { + mrb_sym click_mode_sym; + mrb_get_args(mrb, "n", &click_mode_sym); uint64_t id = get_id_ElementText(mrb, self); app::EText *element = (app::EText *)app::element_pool[id]; - element->click_through = click_through; + + std::string click_mode_str = mrb_sym2name(mrb, click_mode_sym); + + if (click_mode_str == "pass") + element->click_mode = ClickMode::PASS; + else if (click_mode_str == "block") + element->click_mode = ClickMode::BLOCK; + else if (click_mode_str == "ignore") + element->click_mode = ClickMode::IGNORE; return mrb_nil_value(); } @@ -676,7 +709,7 @@ static mrb_value ElementRect_init(mrb_state *mrb, mrb_value self) { float x = 0, y = 0, z = 0, rotation = 0; float scale_x = 1, scale_y = 1, alpha = 1, pivot_x = 0.5, pivot_y = 0.5; - bool click_through = false; + mrb_sym click_mode_sym = sym_block; if (!mrb_nil_p(kwargs)) { mrb_value pos_hash = HASH_GET(kwargs, position); if (!mrb_nil_p(pos_hash)) { @@ -699,7 +732,10 @@ static mrb_value ElementRect_init(mrb_state *mrb, mrb_value self) { HASH_FLOAT(kwargs, z, z); HASH_FLOAT(kwargs, rotation, rotation); HASH_FLOAT(kwargs, alpha, alpha); - HASH_BOOL(kwargs, click_through, click_through); + mrb_value v = HASH_GET(kwargs, click_mode); + if (!mrb_nil_p(v)) { + click_mode_sym = mrb_symbol(v); + } } Vec2 shape = {(float)w, (float)h}; @@ -720,7 +756,15 @@ static mrb_value ElementRect_init(mrb_state *mrb, mrb_value self) { element->pivot = {pivot_x, pivot_y}; element->alpha = alpha; element->z = z; - element->click_through = click_through; + + std::string click_mode = mrb_sym2name(mrb, click_mode_sym); + + if (click_mode == "pass") + element->click_mode = ClickMode::PASS; + else if (click_mode == "block") + element->click_mode = ClickMode::BLOCK; + else if (click_mode == "ignore") + element->click_mode = ClickMode::IGNORE; s->id = app::element_pool.acquire(element); app::element_pool.use(s->id); @@ -946,19 +990,41 @@ static mrb_value element_rect_z_set(mrb_state *mrb, mrb_value self) { return mrb_nil_value(); } -static mrb_value element_rect_click_through_get(mrb_state *mrb, mrb_value self) { +static mrb_value element_rect_click_mode_get(mrb_state *mrb, mrb_value self) { uint64_t id = get_id_ElementRect(mrb, self); app::ERect *element = (app::ERect *)app::element_pool[id]; - return mrb_bool_value(element->click_through); + + std::string click_mode_str; + switch (element->click_mode) { + case ClickMode::PASS: + click_mode_str = "pass"; + break; + case ClickMode::BLOCK: + click_mode_str = "block"; + break; + case ClickMode::IGNORE: + click_mode_str = "ignore"; + break; + } + + return mrb_symbol_value(mrb_intern_cstr(mrb, click_mode_str.c_str())); } -static mrb_value element_rect_click_through_set(mrb_state *mrb, mrb_value self) { - mrb_bool click_through; - mrb_get_args(mrb, "b", &click_through); +static mrb_value element_rect_click_mode_set(mrb_state *mrb, mrb_value self) { + mrb_sym click_mode_sym; + mrb_get_args(mrb, "n", &click_mode_sym); uint64_t id = get_id_ElementRect(mrb, self); app::ERect *element = (app::ERect *)app::element_pool[id]; - element->click_through = click_through; + + std::string click_mode = mrb_sym2name(mrb, click_mode_sym); + + if (click_mode == "pass") + element->click_mode = ClickMode::PASS; + else if (click_mode == "block") + element->click_mode = ClickMode::BLOCK; + else if (click_mode == "ignore") + element->click_mode = ClickMode::IGNORE; return mrb_nil_value(); } @@ -1001,7 +1067,7 @@ static mrb_value ElementImage_init(mrb_state *mrb, mrb_value self) { float x = 0, y = 0, z = 0, rotation = 0; float scale_x = 1, scale_y = 1, alpha = 1, pivot_x = 0.5, pivot_y = 0.5; - bool click_through = false; + mrb_sym click_mode_sym = sym_block; if (!mrb_nil_p(kwargs)) { mrb_value pos_hash = HASH_GET(kwargs, position); if (!mrb_nil_p(pos_hash)) { @@ -1024,7 +1090,10 @@ static mrb_value ElementImage_init(mrb_state *mrb, mrb_value self) { HASH_FLOAT(kwargs, z, z); HASH_FLOAT(kwargs, rotation, rotation); HASH_FLOAT(kwargs, alpha, alpha); - HASH_BOOL(kwargs, click_through, click_through); + mrb_value v = HASH_GET(kwargs, click_mode); + if (!mrb_nil_p(v)) { + click_mode_sym = mrb_symbol(v); + } } uint64_t id = get_id_Animation(mrb, animation_val); @@ -1036,7 +1105,15 @@ static mrb_value ElementImage_init(mrb_state *mrb, mrb_value self) { element->pivot = {pivot_x, pivot_y}; element->alpha = alpha; element->z = z; - element->click_through = click_through; + + std::string click_mode = mrb_sym2name(mrb, click_mode_sym); + + if (click_mode == "pass") + element->click_mode = ClickMode::PASS; + else if (click_mode == "block") + element->click_mode = ClickMode::BLOCK; + else if (click_mode == "ignore") + element->click_mode = ClickMode::IGNORE; s->id = app::element_pool.acquire(element); app::element_pool.use(s->id); @@ -1245,19 +1322,41 @@ static mrb_value element_image_z_set(mrb_state *mrb, mrb_value self) { return mrb_nil_value(); } -static mrb_value element_image_click_through_get(mrb_state *mrb, mrb_value self) { +static mrb_value element_image_click_mode_get(mrb_state *mrb, mrb_value self) { uint64_t id = get_id_ElementImage(mrb, self); app::EImage *element = (app::EImage *)app::element_pool[id]; - return mrb_bool_value(element->click_through); + + std::string click_mode_str; + switch (element->click_mode) { + case ClickMode::PASS: + click_mode_str = "pass"; + break; + case ClickMode::BLOCK: + click_mode_str = "block"; + break; + case ClickMode::IGNORE: + click_mode_str = "ignore"; + break; + } + + return mrb_symbol_value(mrb_intern_cstr(mrb, click_mode_str.c_str())); } -static mrb_value element_image_click_through_set(mrb_state *mrb, mrb_value self) { - mrb_bool click_through; - mrb_get_args(mrb, "b", &click_through); +static mrb_value element_image_click_mode_set(mrb_state *mrb, mrb_value self) { + mrb_sym click_mode_sym; + mrb_get_args(mrb, "n", &click_mode_sym); uint64_t id = get_id_ElementImage(mrb, self); app::EImage *element = (app::EImage *)app::element_pool[id]; - element->click_through = click_through; + + std::string click_mode = mrb_sym2name(mrb, click_mode_sym); + + if (click_mode == "pass") + element->click_mode = ClickMode::PASS; + else if (click_mode == "block") + element->click_mode = ClickMode::BLOCK; + else if (click_mode == "ignore") + element->click_mode = ClickMode::IGNORE; return mrb_nil_value(); } @@ -1697,8 +1796,8 @@ inline void setup() { ADD_METHOD(ElementText, "alpha", element_text_alpha_get, MRB_ARGS_NONE()); ADD_METHOD(ElementText, "z=", element_text_z_set, MRB_ARGS_REQ(1)); ADD_METHOD(ElementText, "z", element_text_z_get, MRB_ARGS_NONE()); - ADD_METHOD(ElementText, "click_through=", element_text_click_through_set, MRB_ARGS_REQ(1)); - ADD_METHOD(ElementText, "click_through", element_text_click_through_get, MRB_ARGS_NONE()); + ADD_METHOD(ElementText, "click_mode=", element_text_click_mode_set, MRB_ARGS_REQ(1)); + ADD_METHOD(ElementText, "click_mode", element_text_click_mode_get, MRB_ARGS_NONE()); ADD_METHOD(ElementText, "width", element_text_width, MRB_ARGS_NONE()); ADD_METHOD(ElementText, "height", element_text_height, MRB_ARGS_NONE()); @@ -1727,8 +1826,8 @@ inline void setup() { ADD_METHOD(ElementRect, "alpha", element_rect_alpha_get, MRB_ARGS_NONE()); ADD_METHOD(ElementRect, "z=", element_rect_z_set, MRB_ARGS_REQ(1)); ADD_METHOD(ElementRect, "z", element_rect_z_get, MRB_ARGS_NONE()); - ADD_METHOD(ElementRect, "click_through=", element_rect_click_through_set, MRB_ARGS_REQ(1)); - ADD_METHOD(ElementRect, "click_through", element_rect_click_through_get, MRB_ARGS_NONE()); + ADD_METHOD(ElementRect, "click_mode=", element_rect_click_mode_set, MRB_ARGS_REQ(1)); + ADD_METHOD(ElementRect, "click_mode", element_rect_click_mode_get, MRB_ARGS_NONE()); DEF_CLASS(ElementImage, MRB_ARGS_REQ(1) | MRB_ARGS_OPT(1)); ADD_METHOD(ElementImage, "on_click", element_image_on_click, MRB_ARGS_BLOCK()); @@ -1751,8 +1850,8 @@ inline void setup() { ADD_METHOD(ElementImage, "alpha", element_image_alpha_get, MRB_ARGS_NONE()); ADD_METHOD(ElementImage, "z=", element_image_z_set, MRB_ARGS_REQ(1)); ADD_METHOD(ElementImage, "z", element_image_z_get, MRB_ARGS_NONE()); - ADD_METHOD(ElementImage, "click_through=", element_image_click_through_set, MRB_ARGS_REQ(1)); - ADD_METHOD(ElementImage, "click_through", element_image_click_through_get, MRB_ARGS_NONE()); + ADD_METHOD(ElementImage, "click_mode=", element_image_click_mode_set, MRB_ARGS_REQ(1)); + ADD_METHOD(ElementImage, "click_mode", element_image_click_mode_get, MRB_ARGS_NONE()); DEF_CLASS(Scene, MRB_ARGS_NONE()); ADD_METHOD(Scene, "==", scene_equal, MRB_ARGS_REQ(1)); diff --git a/include/binding/ruby.h b/include/binding/ruby.h index b6a5c9d..18e2f49 100644 --- a/include/binding/ruby.h +++ b/include/binding/ruby.h @@ -107,7 +107,10 @@ DEF_SYM(pivot) DEF_SYM(scale) DEF_SYM(alpha) DEF_SYM(fps) -DEF_SYM(click_through) +DEF_SYM(click_mode) +DEF_SYM(pass) +DEF_SYM(block) +DEF_SYM(ignore) DEF_SYM(call) DEF_SYM(x) DEF_SYM(y) diff --git a/samples/button.rb b/samples/button.rb index 213aa2e..2570367 100644 --- a/samples/button.rb +++ b/samples/button.rb @@ -1,4 +1,4 @@ -App.run 720, 480, "Button Test", fps: 24 +App.run 720, 480, "Button Test", fps: 60 main_scene = Scene.new @@ -12,11 +12,11 @@ bg_animation = Animation.new [bg_image1, bg_image2], fps: 5 App.language = :en App.localize(:en, :btn_text, "Click %{name}!") -text = ElementText.new :btn_text, default_font, 0xFFFF00, {name: "Me"}, position: {x: 50, y: 50}, z: 1, click_through: true, alpha: 1 +text = ElementText.new :btn_text, default_font, 0xFFFF00, {name: "Me"}, position: {x: 50, y: 50}, z: 1, click_mode: :pass, alpha: 1 rect = ElementRect.new 100, 25, 0xFF0000, position: {x: 50, y: 50}, z: 0, rotation: 60, scale: { x: 1, y: 1 }, alpha: 0.4 -image_element = ElementImage.new bg_animation, position: {x: 0, y: 0}, z: -1, click_through: true, alpha: 1 +image_element = ElementImage.new bg_animation, position: {x: 0, y: 0}, z: -1, click_mode: :ignore, alpha: 1 pp text, rect, image_element @@ -31,7 +31,7 @@ rect.on_click do text.params! end -App.map_key :mouse_left, :press +App.map_key :mouse_left, :action_name start = Time.now c_ = 0.0 @@ -49,9 +49,9 @@ main_scene.on_update do |delta_time| pp App.text_input if App.text_input != "" - next unless App.pressed?(:press) + next unless App.pressed?(:action_name) - pp "#{App.pressed?(:press)}, #{App.down?(:press)}, #{App.released?(:press)}" + pp "#{App.pressed?(:action_name)}, #{App.down?(:action_name)}, #{App.released?(:action_name)}" for elem in el if elem == image_element diff --git a/samples/main.rb b/samples/main.rb index 1af63b5..8b5287f 100644 --- a/samples/main.rb +++ b/samples/main.rb @@ -69,15 +69,6 @@ world.spawn do |e| # Name is optional # c.tile[x: 0, y: 0] = tiles[0] # set tile at position x,y in tilemap to a specific tile from the sheet (if no auto_tile is used) c.tile[x: 0, y: 0] = true # true/false otherwise end - e.add_component :render do |c| - c.z_index = 0 - c.renderer = proc do - draw_rect 0, 0, 50, 50, {r: 255, g: 0, b: 0, a: 255} # example of custom rendering code for this entity (0,0 is the position of the entity) - draw_text "Hello World", default_font, 0, 0, {r: 255, g: 255, b: 255, a: 255} - end - # offset from the position of the entity, useful for things like speech bubbles or health bars that should be above the entity, etc - c.offset = {x: 0, y: 0} - end e.add_component :health do |c| c.health = 100 end diff --git a/writeup/analysis.md b/writeup/analysis.md index bb3320e..1462576 100644 --- a/writeup/analysis.md +++ b/writeup/analysis.md @@ -222,15 +222,40 @@ Summarization of survey results and its limitations ## What more is needed? -Other features that a game engine should have/has (need to go in depth) +Other features that a game engine should have + +core: + +- app, defined with fixed heght/width and aspect ratio is maintained with letterboxing, and fps + +- scenes +- elements in scenes (in screen coordinates) + - ElementText + - ElementRect + - ElementImage + - ElementScript - uses function to return a pizel buffer each frame, can use frame caching (for stuff liek minimaps) + - should not reallocate every frame, can use dirty flag based system, uses a single string \ + from ruby and maybe keep string alive (add to gc exception) on c++ side too and use the same buffer to send to sdl. \ + so 2 copies 1 from ruby which can eb directly edited and then 1 a sdl texture object + - ElementWorld - (It is a view through a camera) + +- The world has to have an ecs system buitl in it + - components are type definitions (and thier instances are stored in cache efficient arrays) + - systems use queries to select components (with filtering) and can perform bulk operations on them + - entities - an id and position which can relate to compoenent instances + +- The world can render everything in view based on camera, and everything inside here is in world coordinates + +- shaders, layers on top of teh world, (per world, not possible per entity) + +extra: - physics (forces, mass, movement and collisions) - Localization - keymappings (and settings) -- shaders - tilesets - sprite system (tiled, animated, static etc.) -- 9 segment ui +- 9 segment ui system - audio playing - network interface, with a lib to work with rack backends (and generic socket api) - persistence store