{"id":19839839,"url":"https://github.com/themarlboroman/dfw-jumpstart","last_synced_at":"2025-02-28T19:17:46.371Z","repository":{"id":100037217,"uuid":"97352056","full_name":"TheMarlboroMan/dfw-jumpstart","owner":"TheMarlboroMan","description":"Jumpstart repo for dfw + libdansdl2","archived":false,"fork":false,"pushed_at":"2024-03-06T11:39:53.000Z","size":9111,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-01-11T11:26:35.515Z","etag":null,"topics":["app-development","cpp","examples","game-development","jumpstart"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"unlicense","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/TheMarlboroMan.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-07-16T01:24:10.000Z","updated_at":"2022-09-12T17:49:42.000Z","dependencies_parsed_at":"2024-11-12T12:38:52.717Z","dependency_job_id":null,"html_url":"https://github.com/TheMarlboroMan/dfw-jumpstart","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TheMarlboroMan%2Fdfw-jumpstart","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TheMarlboroMan%2Fdfw-jumpstart/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TheMarlboroMan%2Fdfw-jumpstart/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TheMarlboroMan%2Fdfw-jumpstart/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/TheMarlboroMan","download_url":"https://codeload.github.com/TheMarlboroMan/dfw-jumpstart/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241209587,"owners_count":19927737,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["app-development","cpp","examples","game-development","jumpstart"],"created_at":"2024-11-12T12:24:33.517Z","updated_at":"2025-02-28T19:17:46.345Z","avatar_url":"https://github.com/TheMarlboroMan.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"#TODO:\n\t- Add a cartesian camera example, with polys.\n\t- Comment the cartesian vs screen system.\n\n\t- Center character animation.\n\t- Check camera spatiable objects.\n\t- Skip camera test (set in camera...)\n\n# Dependencies\n\n- A compiler supporting the C++17 standard\n- libdansdl2 (https://github.com/themarlboroman/libdansdl2)\n\t- lm (https://github.com/themarlboroman/log)\n\t- tools (https://github.com/themarlboroman/tools)\n\t\t- rapidjson (https://github.com/tencent/rapidjson)\n- dfw (https://github.com/themarlboroman/dfw)\n\nOn linux I just use gcc (currently gcc 9.3.0) and cmake to build / install all deps, then cmake to build this project.\n\n# Building on windows.\n\nIn the past I have built this project and its dependencies with ming32 (or mingw64-64. As of today, I find it much easier to use msys2 and pretend I am doing things the linux way. See \"building with msys2\" for a complete guide.\n\nIf you are using mingw32, bear in mind that the zlib1.dll bundled with the mingw development packages at https://www.libsdl.org/projects/SDL_image/ is out of date with regards to libpng. Basically this means that you can build the project, but it will not run. Download a more up to date version, such as the one at https://sourceforge.net/projects/uqm-mods/ and you will be ready to go. If I remember correctly, this does not apply to mingw64-w64.\n\n# Basic framework.\n\nThe idea here is to have a very basic application so things can run with little copy and paste. It will also include a few features I tend to forget about.\n\nThere are a few controllers here:\n\n- a very simple console with four commands or so (first try to implement one)\n\t- Demonstrates a very simple controller with text input.\n- a little game-like thing in axonometric 2d perspective (think snes rpg).\n\t- Demonstrates screen-coordinates camera, implementation of application classes, use of animations and frames, screen representations, input, SAT collision, signal broadcasting...\n- a game thing companion that displays texts along the previous controller.\n\t- Demonstrates multi-controller drawing and signal broadcasting.\n- another very simple racing game.\n\t- Demonstrates cartesian coordinates, use of polygons and collisions.\n- a very simple frames-per-second test, not accesible unless tampering with the command line.\n- a very simple step test, (not accesible unless command line is used), created to see how the timestep keeps up in different computers.\n\nThe rest of this text file includes:\n\t- a getting started guide.\n\t- notion on coordinate systems.\n\t- \"howto\" for many things.\n\t- files and directory structure.\n\t- interesting examples in classes.\n\n## Getting started:\n\n- Install dependencies and compile them (libdansdl2, dfw, tools, log).\n- Alter makefile_linux/win to set the right paths.\n\nNow for the ugly part.\n\n- chmod +x scripts/setup_fresh.sh\n- scripts/setup_fresh.sh\n- code your own stuff\n- make -f makefile_[linux-win] all\n\n## On coordinate systems.\n\nCopied and pasted from the \"camera.h\" header file of libdansdl2:\n\n\"A particularly interesting feature is the coordinate system: in \"screen\",\nthe focus box will be assumed to be a rectangle with its origin in the top-left\ncorner its width extending right and its height extending down (just as the screen\ncoordinate system used on the library). In \"cartesian\", the origin is the\nbottom-left corner and the \"height\" parameter \"ascends\". The choice of coordinate\nsystem makes the camera act as a mediator between screen and application space.\nWhatever the system chosen, all representations MUST be in \"screen\" form.\nThe camera will not perform the transformations itself... Even if this\ntransformation could be implemented into the framework, I'd rather not do it\nand keep it simple. All the client code needs to do is invert the y axis\nwhen creating a representation for a logic object.\".\n\nWhich translates to:\n\n1 - Code all your logic assuming whatever coordinate system you want. The\n\tgeometry classes in libdansdl2 assume a cartesian space, but you may be\n\tused to the trope of 0.0 being the top-left.\n2 - When it comes to draw your representations, first set the camera to\n\tthe space you are using in the state. If you assume 0.0 is the top of the\n\tscreen and 0.0 is above 0.10, use \"screen\". Else use \"cartesian\".\n3 - Create your representations in 'screen' space, always. This might be\n\tconfusing if you are using the \"cartesian\" system: the trick is to invert\n\tthan Y axis. Seriously, just go to \"test_poly.cpp\" and look at the\n\tdraw_polygon method. It takes in a ldt::polygon_2d in cartesian space and\n\tturns it into a ldv::polygon_representation by copying all its points and\n\tinverting the y axis.\n\n## A few definitions:\n\n- Controller:\n\tTODOz\n- State driver:\n\tTODO\n- Signal broadcasting.\n\n## Howto\n\nHere are a few things that need to be done in a regular basis.\n\n### Add new controllers:\n\nYou can do the automatic or manual methods...\n\nThe automatic method, under Linux:\n\n- from the root of the project:\n\n\t- cd scripts\n\t- ./create_controller.sh controllername.\n\nThe manual method:\n\n- Create your files in class/controllers. Use the _template.* files for your convenience.\n- Add the controller to _makefile_app. state_driver should depend on it. This is a good moment to try and see if it compiles.\n- Register the controller in state_driver.*. Perhaps in prepare_state() too.\n- Add the state to states.h, so it can be used from other controllers to request state changes.\n- Test the controller by changing the default state in state_driver's constructor.\n\nIt is a bit clunky, but does not take more than 5 minutes...\n\n### Run code once, at application startup.\n\nThe one thing that best matches this is to use your main state_driver under the\nclass/dfwimpl directory. Modify the constructor and have it call your own hooks.\n\n### Register and draw TTF fonts.\n\nUse the tools::ttf_manager class. You can have a manager for each of your\ninstances or use the same for all of them (for example, making it property of\nthe state driver).\n\nThe ttf_manager can be constructed with no arguments. Once constructed, use\n\nbool ttf_manager::insert(const std::string\u0026 _alias, int _size, const std::string\u0026 _path);\n\nto insert fonts, which will return true on success (false if the font was already\nregistered!).\n\nWhen it comes to draw the font you will need both a registered ttf and a\ndrawable object:\n\n//Get the font.\nconst auto\u0026 font=ttf_manager.get(\"myfontalias\", 14);\n//Create the drawable... More parameters are available!!!!.\nldv::ttf_representation txt(font, ldv::rgba8(255,255,255,255), \"My text\");\ntxt.draw(screen);\n\nAlternatively, you may want to consider the layout classes if you want to skip\ncreating instances all the time.\n\n### Do signal broadcasting between controllers and with the state_driver\n\nThere are a few classes in the dfw header \"signal_broadcasting.h\" which allow\nto setup a signal system. The concepts are:\n\n- a broadcast_signal: the message to be sent. It is virtual since each message\n\tmay have its own data.\n- a receiver: the entity that receives a message. It may later choose to discard\n\tor interpret it. Since the receiver is designed to work by composition,\n\tyou will need to design and instance it, probably using a lambda to\n\tmake your controller's data accesible to it.\n- sender: the entity that sends.\n- a dispatcher: registers all receivers and senders alike. Through it, the\n\tsender may send a signal that will be dispatched to all receivers.\n\nThe easiest thing to do here is:\n\n- Create you own signal header (usually in the controllers directory). In there:\n\t- Extend the receiver with your own (so, for example, injects automatically).\n\t- Setup your signals, extended from the original.\n- When you need to setup a sender:\n\t- In your state driver, inject the dispatcher (get_signal_dispatcher())\n\tinto your controller.\n\t- In your controller, create a sender object (dfw::signal_broadcaster)\n\tand use the dispatcher to construct it.\n\t- Send a message through the sender object.\n- When you need to setup a receiver:\n\t- In your state driver, inject the dispatcher (get_signal_dispatcher())\n\tinto your controller.\n\t- In your controller, use your own receiver object with the dispatcher.\n\t- In your controller, set up a lambda function into the receiver so it\n\tforwards to your own controller logic.\n\t- In your controller, setup your controller logic for receiving a\n\tsignal (it will usually involve checking the message type (get_type())\n\tand later doing static casting.\n\nThere's an example of this between the test_2d and test_2d_text controllers.\n\nThe fun part is that mostly everything should be able to latch to this system.\nFor example, the state driver partakes in it so the screen size or audio\nproperties can be updated. On this particular example the lambda is spiffed up\nso the kernel can be captured.\n\n### Use a menu\n\nMenus are time consuming if done by hand, but they are also an important part\nof a relatively polished application.\n\nThe tools::options_menu class allows for creating dynamic menus of a single depth\non the fly. The particulars of the class are documented but the most important\nthing to know is how to parse one from a dnot file. There's an example in the\nmenu controller that basically goes:\n\n\t//Create a translation map.\n\tstd::map\u003cstd::string, int\u003e\ttranslation_map;\n\t//Parse the menu and fill the translation map.\n\ttools::mount_from_dnot(tools::dnot_parse_file(\"data/app_data/menus.dat\")[\"main\"], menu, \u0026translation_map);\n\t//Create a translation vector.\n\tstd::vector\u003ctools::options_menu\u003cstd::string\u003e::translation_struct \u003e trad;\n\t//Use the translation map to fill the translation vector.\n\tfor(const auto\u0026 p: translation_map) trad.push_back({p.first, menu_localization.get(p.second)});\n\t//Translate with the translation vector.\n\tmenu.translate(trad);\n\nThat's enough to mount and translate a menu. Now, representations are a\ncompletely different matter (all that tools::options_menu contains is the data).\nThe end user is responsible to convert the data to representations. However,\nthis project has a \"menu_representation\" class that is very useful to both\ncreate the representations and add events (change selection, navigate...).\nExamples are in the code of the controller. It is easier to copy and paste\nthan to try and explain. The two things to understand here:\n\n\t- in tools::options_menu terms, get_name is the name of a property and\n\tget_title is its value.\n\t- the \"menu_representation\" class is just a framework that must be filled\n\twith six functions (for example, with lambdas)\n\n\t\t - register name/value representations void(const T\u0026, std::vector\u003cldv::representation*\u003e\u0026)\n\t\t\tT is the key type, in case some keys need special treatment.\n\t\t\tThe vector must be filled with as many representations\n\t\t\tas needed.\n\t\t - draw name/value (const T\u0026, size_t, const std::vector\u003cldv::representation*\u003e\u0026, const std::string\u0026, bool)\n\t\t\tT is the key again, and size_t is the index we are currently\n\t\t\tdrawing (two ways of referencing the same thing). The\n\t\t\tvector contains the representations created previously\n\t\t\tin the order of insertion (they need to be static_cast,\n\t\t\tof course). The bool indicates if this is the current\n\t\t\tselection.\n\t\t - step name/value (const T\u0026, size_t, float, const std::vector\u003cldv::representation*\u003e\u0026, const std::string\u0026, bool)\n\t\t\tSimilar to the previous one, to be called if the menu\n\t\t\thas \"time\" effects. Float is the delta value. This is\n\t\t\tonly needed if we are going to use the \"step\" function\n\t\t\tof the menu.\n\t- representations are refreshed only when:\n\t\t - it is forced through a menu_representation.refresh() call.\n\t\t - whenever next, previous or browse are invoked.\n\t\t - if the step functions are used.\n\nThere are alternatives, like the tools::grid_list and tools::vertical list\nbut these need to be manually rolled... In any case, complex menu systems\n(like one with submenus, specific actions taking place upon selection of an\noption and so on) are sinonyms with grunt work... The menu controller is a good\nexample of things that can be done.\n\n### Redefine keys.\n\nThis is bound to exist on every game... The particulars are complex and the\nclasses involved many, so just check the \"menu\" controller. There's an entire\nworld of stuff there.\n\n### Draw a controller different than the active one:\n\nThere may be cases when you need to keep drawing a controller while executing one\n(for example, a help screen about a certain controller). While this can be done\nsetting up different states in controller and branching logic, input control\nand drawing, there's an alternative. When you need it, add this function in\nthe controller:\n\nvirtual void\t\t\trequest_draw(dfw::controller_view_manager\u0026 cvm)\n{\n\t//Do this to add another controller by index.\n\tcvm.add(1);\n\n\t//Do this to add your controller.\n\tcvm.add_ptr(this);\n}\n\nAnd that's all. Controllers will be drawn in the declared order (in the example,\nfirst the one with the index 1 and then \"this\"). No logic of the controllers will be\nexecuted at all, so the view will be frozen.\n\nAn example can be seen in the test_2d_text controller which requests text_2d to\nbe drawn and then overlays new elements.\n\n### Use the tools::view_composer to create static views.\n\nStatic views can be composed with dnot files to avoid the need of hardcoded\nvalues and recompilation. These are most useful for menus, presentation screens,\nfixed graphics... There is an example in the test_2d_text controller, using the\nfile in \"data/app_data/layouts.dat\". Another is in the menu controller, which\ndoes things like use external representations and manipulate representations\nin the layout from the code.\n\nThe documentation of the class is fairly complete in any case.\n\n### Add new inputs:\n\n- Add it to the enum in class/input.h.\n- In case you need a keyboard input:\n\t- Locate the sdl key mapping in SDL_keycode.h (locate SDK_scancode.h first, usually in /usr/include/SDL2).\n\t- Or use the showkey -a command (second result).\n\t- Remember, this is a scancode, not the ASCII value!.\n- Add this mapping to data/config/config.dnot in the \"input\" sequence.\n\t- The first parameter is \"type\" (0 is keyboard).\n\t- The second is device number and\n\t- The third is the code.\n\n- Map it in class/dfwimpl/state_driver.cpp's prepare_input().\n- Use it in your code by referencing as indicated in class/input.h:\n\t- if(input.is_input_[down|pressed|up](input_app::your_input_here)) {...}\n\n### Change application states (controllers).\n\n- From the controller code use set_state(state), as in set_state(state_main). Use a state defined in states.h.\n- From the state_driver use states.set(v), where v is a state defined in states.h. \"states\" is a protected property of the state_driver_interface.\n\nThere are examples of both: you can see it done in main.cpp/state_driver.cpp and in the controllers.\n\n### Use \"loop\" and application step.\n\nThis is the main application flow:\n\n- Do common step (virtual function for your own state driver).\n- Do common input (virtual function for your own state driver).\n- Begin consuming loop time:\n\t- Loop the input (now there are events... this is actually sdl_input::loop).\n\t- Loop the state. (this is the main loop of your state).\n\t- Process message queue.\n\t- Evaluate possible state change. Break out of this loop if needed.\n\t-\n- If change state\n\t- Confirm state change.\n- If not change state\n\t- Do fps loop init.\n\t- Draw state.\n\t- Update screen.\n\t- Do fps loop end.\n\nThus:\n\n- Controller loop happens X times, as much as needed to fill N seconds of logic before drawing.\n\t- This time is measured by a ldt::fps_counter, property of the kernel.\n\t- The value \"delta_step\" on the kernel represents the 0.01 seconds of logic.\n\t- Once N time units of logic are run, the screen is refreshed.\n\t- The loop repeats with N being the time the screen took to refresh or a maximum value set in state_driver::get_max_timestep().\n\n### Use and inject resource managers.\n\nIn order to obtain any resource (music, sound, texture...) first these must be\nloaded within the framework. The files in \"/data/resources\" contain all these\nstatically loaded resources. Of course, resources can be loaded dinamically too\nbut it just doesn't pay for small applications with small memory prints.\n\nEach resource goes into a different resource manager, property of the kernel,\nthus accesible from the state_driver:\n\nldv::resource_manager\u0026\tkernel.get_video_resource_manager() provides access to\ntextures kernel.get_video_resource_manager().get_texture(index) and\nsurfaces kernel.get_video_resource_manager().get_surface(index).\n\nlda::resource_manager\u0026\tkernel.get_audio_resource_manager() does the same for\naural resources with \"get_sound\" and \"get_music\".\n\nThese managers are emptied once the application stops. In order to access any\ntexture from any class or controller (which happens all the time) either the\nkernel must be accessible to these (bad idea) or the managers must be injected\ninto them (better idea). The de facto way of doing this is to set a reference\nto them into your controller and pass them when the controllers are being\nconstructed in the state_driver. Controllers, in turn, can pass these references\nto classes or methods.\n\nAnother option is the one used in this code: a single \"shared_resources\" object\nthat has references to all managers and shared stuff, injected into the\ncontrollers later. Shoddy, but quick.\n\n### Play audio\n\nThe simplest way to play sound and music is to obtain the dfw::audio object\nfrom the kernel (kernel.get_audio()) and use its play_music and play_sound\nmethods, that map directly to the libdansdl2 audio_controller (check the\ndocumentation). The play_sound method should be enclosed in a try-catch\nblock, as it will throw when no channels are available (for example, if you\nplay many sounds simultaneously... you can always request more channels anyway).\nSince the kernel is not accessible from the controllers you will want to inject\nthis dfw::audio object into them... This would be the \"fire and forget\" method,\nas no control over the played sounds is given.\n\nTo achieve control over the sounds (for example, to change their volume, panning\nor stop them on the fly), a channel must be acquired from the\nlda::audio_controller class (accesible through the () method of the dfw::audio,\nis in kernel.get_audio()()).\n\nTo acquire a channel use lda::audio_controller::get_channel(size_t) or\nlda::audio_controller::get_free_channel() (which may throw). This will\nreturn an lda::audio_channel object that must instantly be set to play something\nor monitored (lest the same channel is returned again with the next call).\nThis object is \"linked\" to a real_audio_channel so every action upon it will\nreflect on the real channel. When the lda::audio_channel objects have long\nlifetimes this can be problematic, as different objects may point to the same\nreal channel.\n\nA call to \"unlink\" will unlink the lda::audio_channel from\nits real_audio_channel (any operation upon the channel will crash, as it is\nequivalent to deferencing an invalid pointer). After \"unlink\" is called, the\nchannel should not be used except for reassigning it through\n\nmy_channel=lda::audio_controller::get_free_channel().\n\nChannels will be reported as free if they are not playing. Any monitored channel\nwill not be reported as free. Once a channel is monitored, it won't be returned\nto the channel pool until it is unmonitored. Monitored, thus, basically means\n\"Please, do not return this channel to the pool when it is done playing, I want\nto keep using it\".\n\nTo control the lifetime of channels, it is possible to attach a callback\n(derived from lda::audio_callback_interface) to the channel, whose method\noperator() will be executed when the sound stops playing. This makes sense\nsince you can:\n\n- Acquire a channel.\n- Monitor it (now the channel is yours).\n- Set the callback.\n- Play sounds.\n- Once the sound is done, execute the callback.\n- In the callback, clear the channel callback, unmonitor it and free it so it\ncan be acquired again.\n\nNotice that the callback function will be called only if a lda::audio_callback_interface\nis attached. It will be executed either when the sound ends \"naturally\" or\nwhen \"stop\" is called on the channel.\n\nAlso, notice these methods on the channel:\n\n- free (private function).\n\tunmonitors the channel, removes panning effects, removes callback_listener...\n- set_monitoring(false)\n\tonly unmonitors the channel. The rest is the same.\n- stop:\n\thalts the sound and frees the channel (if unmonitored).\n- do_callback (the callback execution, happens when stop is called or the sound ends)\n\texecutes the callback listener. After that, if the channel is unmonitored,\n\tfrees it.\n\nUnderstanding these dynamics is key to properly manage sound channels. The\nclass \"object_audio_player\" implements these audio ideas, including callbacks\nand monitoring. If improperly done, crashes are sure to happen.\n\nWhen lda::audio_channel instances exist, this guide can help set them up. It is\ndone from the point of view of an application object that owns a\nlda::audio_channel\n\n- Always remember:\n\t- If you set a callback, clear when you are done with the channel.\n\t- If you monitor a channel, unmonitor when you are done with the channel.\n\t- If you \"stop\" a channel, you callback executes.\n\n- About unlinking channels:\n\t- Unlink does not affect the real channel.\n\t- Unlink signals the developer (that is, YOU) that the channel should not be used.\n\t- If in doubt, use the audio_channel_safe class. It will throw if you mess up.\n\t- With long lived and unmonitored channels, your unlinked channel may\n\tend up linked to a channel used by another entity. That's actually ok\n\tas the relationship is not exclusive. It may, however, do unexpected\n\tthings like your sounds not playing because other entity is using the\n\treal channel.\n\nNow, a few rules about sound channels for things you might like to do in your\nclasses:\n\n- When I am constructed:\n\t- My channel is linked\n\t\t=\u003e Do so at constructor.\n\t- My channel is unlinked\n\t\t=\u003e Leave things as they are.\n\n- When I am destroyed\n\t- My channel is playing and...\n\t\t- ... I want it to keep playing.\n\t\t\t=\u003e If there's any call to \"channel.unlink\" in your code,\n\t\t\tcheck if the channel is linked first!\n\t\t\t=\u003e Unmonitor if monitored: on the contrary it will be\n\t\t\tlost forever (not really, but well).\n\t\t\t=\u003e Remove the callback if any: specially if it lies in\n\t\t\tthe scope of your class.\n\t\t\t=\u003e You don't need to unlink it: it will do so upon destruction.\n\t\t- ... I want it to stop:\n\t\t\t=\u003e Unmonitor if monitored. This never has side effects.\n\t\t\t=\u003e If you don't want your callback to execute, remove it.\n\t\t\tIf your callback unlinks the channel you certainly want to do this,\n\t\t\tas it will do so and then crash when the destructor continues.\n\t\t\t=\u003e Call \"stop\"... Stop will call free.\n\n- When my audio channel ends playing ...\n\t- ... I have a callback ...\n\t\t- ... and I want to reuse the very same channel.\n\t\t\t=\u003e Make sure you are monitoring the channel before playing it.\n\t\t\t=\u003e Do not unlink your channel in your callback. Do so in your destructor.\n\t\t\t=\u003e Do not unmonitor it.\n\t\t- ... I want to free the channel.\n\t\t\t=\u003e If monitored, unmonitor it. \"Free\" will be called next.\n\t\t\t=\u003e Remove the callback listener: else it may execute out of your scope or worse, crash.\n\t\t\t=\u003e Unlink the channel.\n\t- ... I don't have a callback and I want to do something.\n\t\t=\u003e Bad choice. Why do you have a lda::audio_channel member? You can use fire-and-forget techniques.\n\n### Implement text input.\n\nGetting the text input to work properly seems tricky but it is actually easy. Two important things:\n\n- It is wise not to use the dli::sdl_input (implemented by dfw::input) buffer, but to build your own so you can keep control. A simple std::string will do.\n- Control characters (backspace, enter...) do not translate to input, and must be controlled individually as keydown presses.\n\nThe \"console\" controller includes a full working example, which goes like this:\n\n- On awakening: start text input (input().start_text_input();) and clear previous contents (this last part just in case).\n- On loop:\n\t- Check text events if(input().is_event_text()) and add input().get_text_input() to the buffer.\n\t- Check non text events that are text related (backspace, enter) as regular key down events. Act upon the buffer.\n- On sleeping: stop the text input (input().stop_text_input();) so it does not affect other controllers.\n\n### Implement localization.\n\nA very basic example of localization is shown in the test_2d controller. The class localization extends tools::base_localization. The rules are simple:\n\n- Languages are represented by integers.\n- Strings are stored in files named #file#.#id_language#.dat. Their format is fixed and simple.\n\n## Files and directory structure.\n\nThe most relevant files are.\n\n- /_makefile_app: This is the real makefile for the application. Things that need tinkering are:\n\t- App dependencies: a list of all your controllers and classes, to make sure they are compiled before building the executable file.\n\t- External dependencies: Tools need to be added if they are to be used.\n\t- Implementation of DFW: state_driver will depend on all your controllers.\n\t- Controllers: You controllers go here.\n\t- Class: Your application classes go here.\n- makefile_[common-linux-win]: These need no modification except for doing path configuration.\n- /class/input.h: Definition of application inputs in a simple enum. Shorthand commodities. Modifiy each time a new input is added.\n- /class/app: Your application classes go here.\n- /class/controllers: All your controllers go here.\n\t- states.h : A simple enum with all states (controllers) for your application, shorthand commodities.\n\t- _template.*: Example files, for copy+paste convenience :).\n- /class/dfwimpl: Implementation of dfw.\n\t- app_config.h: modify get_file_path() if you wish to change your config file location.\n\t- kernel_config.cpp: get_input_pairs() should be modified if you wish to add new inputs.\n\t- state_driver.h: all your controllers should be included here. If there are specific properties for the main driver they go here too.\n\t- state_driver.cpp: controllers must be registered in register_controllers().\n- /data/config/config.dnot: main configuration file. New inputs must be registered here.\n- /data/resources: all static resource files (music, sounds, textures...). # denotes a comment. Examples are included. Surfaces are not likely to be used, textures are the new thing instead.\n\nThe organisation of the class and controller files is non prescriptive. As far as you are concerned you can have all your controllers, application classes and implementation of the framework on the root directory and everything will be allright.\n\n## Interesting examples in the code:\n\n- The use of the animation sheet class is found in class/app/player.cpp\n- The use of the sprite sheet class is found in class/app/tile_decoration.cpp\n\n## Building with msys2\n\nThis example assumes a virtual machine running windows 10 (surprisingly, it can be installed for free).\n\n- go to msys2.org, download and run the installer (mind the path where you install, read what the website says!). Be patient, it might take a while.\n- run msys now (it says to in the installer, just do it)\n- by now you should be running a console that is familiar to linux users. On this console, run \"pacman -Syu\" (no quotes, of course) to update the package database and base packages. Confirm what necessary (including closing the terminal).\n- now, from the menu, run \"Msys2 MSYS\" to access that console again. run \"pacman -Su\" (again, no quotes) to update the thing. This time the console will not close.\n- you will need to install some more stuff with pacman so run \"pacman -S --needed\" on the following packages (it might ask you some questions, I went with the defaults):\n\t- base-devel (build stuff)\n\t- mingw-w64-x86_64-toolchain (build stuff)\n\t- mingw-w64-x86_64-cmake (cmake, to build the projects, please do not use the cmake package!!!!)\n\t- mingw64/mingw-w64-x86_64-SDL2 (SDL2 dependencies)\n\t- mingw64/mingw-w64-x86_64-SDL2_image (SDL2 dependencies)\n\t- mingw64/mingw-w64-x86_64-SDL2_mixer (SDL2 dependencies)\n\t- mingw64/mingw-w64-x86_64-SDL2_ttf (SDL2 dependencies)\n\t- mingw-w64-x86_64-rapidjson (rapidjson, there's a package here, no need to build and install it... besides, it would need tweaking).\n\t- git (so we can pull the repositories)\n\t- msys2-runtime-devel (provides sys/time.h, for the tools repository)\n\t- mingw-w64-x86_64-mesa\n\t- mingw64/mingw-w64-x86_64-glew\n\t- mingw64/mingw-w64-x86_64-freeglut\n\tA command to install everything would be \n\t\t\n\t\tpacman -S --needed base-devel mingw-w64-x86_64-toolchain mingw-w64-x86_64-cmake mingw64/mingw-w64-x86_64-SDL2 mingw64/mingw-w64-x86_64-SDL2_image mingw64/mingw-w64-x86_64-SDL2_mixer mingw64/mingw-w64-x86_64-SDL2_ttf mingw-w64-x86_64-rapidjson git mingw-w64-x86_64-mesa msys2-runtime-devel mingw64/mingw-w64-x86_64-freeglut mingw64/mingw-w64-x86_64-glew\n\n\tThis might take a long while.\n- if need be, you can search for packages with pacman -Ss #search#\n- ok, now, bear in mind that this terminal is running mounted on wherever you installed MSYS2 (this is important when you want to find whatever it is that you build!!!).\n- in MSYS2, go to a suitable directory and download the dependencies (and this, if need be).\n\t- git clone https://github.com/themarlboroman/log\n\t- git clone https://github.com/themarlboroman/tools\n\t- git clone https://github.com/themarlboroman/libdansdl2\n\t- git clone https://github.com/themarlboroman/libdansdl2-tools\n\t- git clone https://github.com/themarlboroman/dfw\n\t- git clone https://github.com/themarlboroman/dfw-jumpstart\n- Ok, that's for setting up repositories and dependencies. Close the terminal and from the menu, open \"MSYS2 Ming64 64-bit\" but run is as an administrator!!\n- cd into the place where you downloaded your stuff\n- Now, in the same order you got them:\n\t- cd into the directory (cd log, for example)\n\t- mkdir build\n\t- cd build\n\t- cmake .. -G \"MSYS Makefiles\"\n\t- make install (just make for dfw-jumpstart!!!)\n- for dfw-jumpstart, you may need to edit the CmakeLists.txt file and uncomment the \"include_directories\" for the libs.\n\n### troubleshooting\n\n- make sure you set your windows git configuration right so the NL becomes CR NL. The localization files will suffer greatly and the thing won't boot if you don't!.\n- do not try to build rapidjson downloading it from source. Save you the trouble, use the pacman package.\n- it is very important that you install the cmake version listed. \"tools\" will not build if not.\n- it is very important that you run cmake .. -G \"MSYS Makefiles\" so it uses the MSYS Makefiles generator.\n- you MUST run the mingw64-bit as an administrator to install stuff.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthemarlboroman%2Fdfw-jumpstart","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthemarlboroman%2Fdfw-jumpstart","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthemarlboroman%2Fdfw-jumpstart/lists"}