{"id":1735,"date":"2023-05-03T23:47:21","date_gmt":"2023-05-03T23:47:21","guid":{"rendered":"https:\/\/writingagame.com\/?p=1735"},"modified":"2026-04-08T16:58:33","modified_gmt":"2026-04-08T16:58:33","slug":"chapter-2-hello-android-2","status":"publish","type":"post","link":"https:\/\/writingagame.com\/index.php\/2023\/05\/03\/chapter-2-hello-android-2\/","title":{"rendered":"Chapter 2. Hello Android"},"content":{"rendered":"\n<p><strong>Tags:<\/strong> <em>Android Studio, NDK, GameActivity, C++, OpenGL ES 3.2, EGL, Native Programming<\/em><\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p>Default Android languages are <em>Java <\/em>and <em>Kotlin<\/em>. In order to use <em>C++<\/em> we&#8217;ll need so called <em>NativeActivity<\/em>.<\/p>\n\n\n\n<p>Start <strong>Android Studio<\/strong>.<\/p>\n\n\n\n<p>Pick <strong>New Project<\/strong>.<\/p>\n\n\n\n<p>Scroll down, select <strong>Game Activity (C++)<\/strong> which is a direct descendant of <em>NativeActivity<\/em> and shares a similar architecture. It&#8217;s a Jetpack library designed to assist Android games in processing app cycle commands, input events, and text input in the application&#8217;s C\/C++ code.<\/p>\n\n\n\n<p><em>GameActivity<\/em> is a relatively new option, available from Android 11. If you don&#8217;t have 11 (or higher) yet, emulator theoretically should help, although this is not always the case, particularly with ES 3.2. I personally prefer to work with real devices. Or you can just ignore the Android parts until better times when you have a more modern device.<\/p>\n\n\n\n<p><strong>Next<\/strong>.<\/p>\n\n\n\n<p>Project name: Let&#8217;s call it <strong>pa<\/strong>. &#8220;p&#8221; &#8211; for platform, &#8220;a&#8221; &#8211; for android.<\/p>\n\n\n\n<p>Package name: replace &#8220;example&#8221; by your own name, in my case &#8211; <strong>com.writingagame.pa<\/strong>.<\/p>\n\n\n\n<p>Save location: replace it by <strong>C:\\CPP\\a999hello\\pa<\/strong><\/p>\n\n\n\n<p>Language: pick <strong>Java<\/strong>.<\/p>\n\n\n\n<p>Minimum SDK: Select <strong>API 30: Android 11.0 (R)<\/strong>. For our <em>Game Activity<\/em> &#8211; quite enough.<\/p>\n\n\n\n<p><img decoding=\"async\" src=\"https:\/\/writingagame.com\/img\/b04\/c02\/01.jpg\"><\/p>\n\n\n\n<p><\/p>\n\n\n\n<p><strong>Next<\/strong>. <\/p>\n\n\n\n<p>Toolchain: <strong>Replace<\/strong> <em>Default<\/em> by <strong>C++14<\/strong> (for compatibility with upcoming Windows project). <\/p>\n\n\n\n<p><strong>Finish<\/strong>.<\/p>\n\n\n\n<p>Wait a bit while it loads. When ready, we can try to run it (green triangular arrow in the top menu) on a connected real device (with <strong>USB debugging enabled<\/strong>) or on an emulator. <\/p>\n\n\n\n<p><\/p>\n\n\n\n<p>For this we will need to enable (if not enabled yet) \u201c<strong>USB debugging<\/strong>\u201d in <em>Settings -&gt; Developer options<\/em> on the device.<\/p>\n\n\n\n<p>If \u201cDeveloper Options\u201d are not in the list, to enable them go to <em>Settings -&gt; About phone -&gt; Software information<\/em> and tap on <em>Build number<\/em> seven times. After that Developer options supposed to be available, so you can go back to <em>Settings -&gt; Developer options<\/em> and <strong>enable USB debugging<\/strong>.<\/p>\n\n\n\n<p>Turn on, unlock your phone and plug it in to your PC with USB cable. Device should ask \u201cAllow USB debugging?\u201d \u2013 allow. After that your device should appear as an available option in Android Studio&#8217;s top menu.<\/p>\n\n\n\n<p>Now, we turn the key!<\/p>\n\n\n\n<p>Run the project (green triangular arrow in the top menu). Expected result:<\/p>\n\n\n\n<p><img decoding=\"async\" src=\"https:\/\/writingagame.com\/img\/b04\/c02\/02.jpg\"><\/p>\n\n\n\n<p><\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p>However, this example seems to me somewhat overloaded and <em>misleading<\/em>. Also, it uses OpenGL ES 3.0 when we target 3.2. <\/p>\n\n\n\n<p>As You can see, there is a bunch of files in <em>cpp <\/em>folder. Of course, eventually we&#8217;ll need all these helper classes, but not now, not in this form and not in this place.<\/p>\n\n\n\n<p>So, let&#8217;s clean it up a bit. Close running app (red square in the top menu).<\/p>\n\n\n\n<p>Open<strong> app\/cpp\/main.cpp<\/strong> and replace code by<\/p>\n\n\n\n<p><\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; highlight: [5,64,65]; title: ; notranslate\" title=\"\">\n#include &lt;jni.h&gt;\n#include &lt;game-activity\/GameActivity.cpp&gt;\n#include &lt;game-text-input\/gametextinput.cpp&gt;\n#include &lt;EGL\/egl.h&gt;\n#include &lt;GLES3\/gl32.h&gt;\n#include &lt;game-activity\/native_app_glue\/android_native_app_glue.c&gt;\nstruct android_app* pAndroidApp = NULL;\nEGLDisplay androidDisplay = EGL_NO_DISPLAY;\nEGLSurface androidSurface = EGL_NO_SURFACE;\nEGLContext androidContext = EGL_NO_CONTEXT;\nbool bExitApp = false;\nint screenSize&#91;2] = {0,0};\nvoid android_init_display() {\n    \/\/ Choose your render attributes\n    constexpr EGLint attribs&#91;] = {\n            EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT,\n            EGL_SURFACE_TYPE, EGL_WINDOW_BIT,\n            EGL_BLUE_SIZE, 8,\n            EGL_GREEN_SIZE, 8,\n            EGL_RED_SIZE, 8,\n            EGL_DEPTH_SIZE, 24,\n            EGL_NONE\n    };\n    \/\/ The default display is probably what you want on Android\n    auto display = eglGetDisplay(EGL_DEFAULT_DISPLAY);\n    eglInitialize(display, nullptr, nullptr);\n    \/\/ figure out how many configs there are\n    EGLint numConfigs;\n    eglChooseConfig(display, attribs, nullptr, 0, &amp;numConfigs);\n    \/\/ get the list of configurations\n    std::unique_ptr&lt;EGLConfig&#91;]&gt; supportedConfigs(new EGLConfig&#91;numConfigs]);\n    eglChooseConfig(display, attribs, supportedConfigs.get(), numConfigs, &amp;numConfigs);\n    \/\/ Find a config we like.\n    \/\/ Could likely just grab the first if we don&#039;t care about anything else in the config.\n    \/\/ Otherwise hook in your own heuristic\n    auto config = *std::find_if(\n            supportedConfigs.get(),\n            supportedConfigs.get() + numConfigs,\n            &#91;&amp;display](const EGLConfig &amp;config) {\n                EGLint red, green, blue, depth;\n                if (eglGetConfigAttrib(display, config, EGL_RED_SIZE, &amp;red)\n                    &amp;&amp; eglGetConfigAttrib(display, config, EGL_GREEN_SIZE, &amp;green)\n                    &amp;&amp; eglGetConfigAttrib(display, config, EGL_BLUE_SIZE, &amp;blue)\n                    &amp;&amp; eglGetConfigAttrib(display, config, EGL_DEPTH_SIZE, &amp;depth)) {\n                    \/\/aout &lt;&lt; &quot;Found config with &quot; &lt;&lt; red &lt;&lt; &quot;, &quot; &lt;&lt; green &lt;&lt; &quot;, &quot; &lt;&lt; blue &lt;&lt; &quot;, &quot;\n                    \/\/     &lt;&lt; depth &lt;&lt; std::endl;\n                    return red == 8 &amp;&amp; green == 8 &amp;&amp; blue == 8 &amp;&amp; depth == 24;\n                }\n                return false;\n            });\n    \/\/ create the proper window surface\n    EGLint format;\n    eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &amp;format);\n    EGLSurface surface = eglCreateWindowSurface(display, config, pAndroidApp-&gt;window, nullptr);\n    \/\/ Create a GLES 3 context\n    EGLint contextAttribs&#91;] = {\n            EGL_CONTEXT_MAJOR_VERSION, 3,\n            EGL_CONTEXT_MINOR_VERSION, 2,\n            EGL_NONE};\n    EGLContext context = eglCreateContext(display, config, nullptr, contextAttribs);\n    \/\/ get some window metrics\n    auto madeCurrent = eglMakeCurrent(display, surface, surface, context);\n    if(!madeCurrent) {\n        ;\n    }\n    androidDisplay = display;\n    androidSurface = surface;\n    androidContext = context;\n}\nvoid android_term_display() {\n    if (androidDisplay != EGL_NO_DISPLAY) {\n        eglMakeCurrent(androidDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);\n        if (androidContext != EGL_NO_CONTEXT) {\n            eglDestroyContext(androidDisplay, androidContext);\n            androidContext = EGL_NO_CONTEXT;\n        }\n        if (androidSurface != EGL_NO_SURFACE) {\n            eglDestroySurface(androidDisplay, androidSurface);\n            androidSurface = EGL_NO_SURFACE;\n        }\n        eglTerminate(androidDisplay);\n        androidDisplay = EGL_NO_DISPLAY;\n    }\n}\nvoid updateRenderArea() {\n    EGLint width,height;\n    eglQuerySurface(androidDisplay, androidSurface, EGL_WIDTH, &amp;width);\n    eglQuerySurface(androidDisplay, androidSurface, EGL_HEIGHT, &amp;height);\n    if (width != screenSize&#91;0] || height != screenSize&#91;1]) {\n        screenSize&#91;0] = width;\n        screenSize&#91;1] = height;\n        glViewport(0, 0, width, height);\n    }\n}\nvoid handle_cmd(android_app *pApp, int32_t cmd) {\n    switch (cmd) {\n        case APP_CMD_INIT_WINDOW:\n            android_init_display();\n            updateRenderArea();\n            break;\n        case APP_CMD_TERM_WINDOW:\n            android_term_display();\n            break;\n        default:\n            break;\n    }\n}\nvoid handleInput() {\n    \/\/ Process all pending events before running game logic.\n    int events;\n    android_poll_source *pSource;\n    if (ALooper_pollAll(0, nullptr, &amp;events, (void **) &amp;pSource) &gt;= 0)\n        if (pSource)\n            pSource-&gt;process(pAndroidApp, pSource);\n    \/\/if no display - wait for it\n    while (androidDisplay == EGL_NO_DISPLAY)\n        if (ALooper_pollAll(0, nullptr, &amp;events, (void **) &amp;pSource) &gt;= 0)\n            if (pSource)\n                pSource-&gt;process(pAndroidApp, pSource);\n    \/\/ handle all queued inputs\n    for (auto i = 0; i &lt; pAndroidApp-&gt;motionEventsCount; i++) {\n        \/\/ cache the current event\n        auto &amp;motionEvent = pAndroidApp-&gt;motionEvents&#91;i];\n        \/\/ cache the current action\n        auto action = motionEvent.action;\n        \/\/ Find the pointer index, mask and bitshift to turn it into a readable value\n        auto pointerIndex = (action &amp; AMOTION_EVENT_ACTION_POINTER_INDEX_MASK)\n                &gt;&gt; AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;\n        \/\/aout &lt;&lt; &quot;Pointer &quot; &lt;&lt; pointerIndex &lt;&lt; &quot;:&quot;;\n        \/\/ get the x and y position of this event\n        auto &amp;pointer = motionEvent.pointers&#91;pointerIndex];\n        auto x = GameActivityPointerAxes_getX(&amp;pointer);\n        auto y = GameActivityPointerAxes_getY(&amp;pointer);\n        \/\/aout &lt;&lt; &quot;(&quot; &lt;&lt; x &lt;&lt; &quot;, &quot; &lt;&lt; y &lt;&lt; &quot;) &quot;;\n        \/\/ Only consider touchscreen events, like touches\n        auto actionMasked = action &amp; AINPUT_SOURCE_TOUCHSCREEN;\n        \/\/ determine the kind of event it is\n        switch (actionMasked) {\n            case AMOTION_EVENT_ACTION_DOWN:\n            case AMOTION_EVENT_ACTION_POINTER_DOWN:\n                \/\/aout &lt;&lt; &quot;Pointer Down&quot;;\n                break;\n            case AMOTION_EVENT_ACTION_UP:\n            case AMOTION_EVENT_ACTION_POINTER_UP:\n                \/\/aout &lt;&lt; &quot;Pointer Up&quot;;\n                break;\n            default:\n                ;\/\/aout &lt;&lt; &quot;Pointer Move&quot;;\n        }\n    }\n    android_app_clear_motion_events(pAndroidApp);\n    \/\/ handle key inputs\n    for (auto i = 0; i &lt; pAndroidApp-&gt;keyUpEventsCount; i++) {\n        \/\/ cache the current event\n        auto &amp;keyEvent = pAndroidApp-&gt;keyUpEvents&#91;i];\n        if (keyEvent.keyCode == AKEYCODE_BACK) {\n            \/\/ actions on back key\n            bExitApp = true;\n        }\n    }\n    android_app_clear_key_up_events(pAndroidApp);\n}\n\/*!\n * This the main entry point for a native activity\n *\/\nfloat g=0;\nvoid android_main(struct android_app *pApp) {\n    pAndroidApp = pApp;\n    \/\/ register an event handler for Android events\n    pApp-&gt;onAppCmd = handle_cmd;\n    while (!bExitApp){\n        \/\/ Process game input\n        handleInput();\n        \/\/ Render a frame\n        g+=(2.0\/256.0);\n        if(g&gt;1)\n            g=0;\n        glClearColor(0.0, g, 0.0, 1.0);\n        glClear(GL_COLOR_BUFFER_BIT);\n        \/\/ Present the rendered image. This is an implicit glFlush.\n        eglSwapBuffers(androidDisplay, androidSurface);\n    }\n    android_term_display();\n    std::terminate();\n}\n\n<\/pre><\/div>\n\n\n<p><\/p>\n\n\n\n<p><strong>Run (green arrow)<\/strong>. If you see a flashing green screen, the heart of your future engine is beating:<\/p>\n\n\n\n<p><\/p>\n\n\n\n<p><img decoding=\"async\" src=\"https:\/\/writingagame.com\/img\/b04\/c02\/03.jpg\"><\/p>\n\n\n\n<p><\/p>\n\n\n\n<p>Stop (red square in the top menu).<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p>Now is safe to remove by-passed classes\/files from project&#8217;s <strong>cpp<\/strong> folder (left side pane, right click on the file -&gt; Delete):<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>AndroidOut.cpp<\/li>\n\n\n\n<li>AndroidOut.h<\/li>\n\n\n\n<li>Model.h<\/li>\n\n\n\n<li>Renderer.cpp<\/li>\n\n\n\n<li>Renderer.h<\/li>\n\n\n\n<li>Shader.cpp<\/li>\n\n\n\n<li>Shader.h<\/li>\n\n\n\n<li>TextureAsset.cpp<\/li>\n\n\n\n<li>TextureAsset.h<\/li>\n\n\n\n<li>Utility.cpp<\/li>\n\n\n\n<li>Utility.h<\/li>\n<\/ul>\n\n\n\n<p>Leave only<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>includes<\/li>\n\n\n\n<li>CMakeLists.txt<\/li>\n\n\n\n<li>main.cpp<\/li>\n<\/ul>\n\n\n\n<p>Deleting files from Android Studio&#8217;s project will delete them from hard drive as well, so don&#8217;t need to delete them in Windows File Explorer. <\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p>Now &#8211; remove these files from <em>CMakeLists.<\/em><\/p>\n\n\n\n<p>Open <em>cpp\/CMakeLists.txt <\/em>and replace by:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; highlight: [24]; title: ; notranslate\" title=\"\">\n# For more information about using CMake with Android Studio, read the\n# documentation: https:\/\/d.android.com\/studio\/projects\/add-native-code.html\n# Sets the minimum version of CMake required to build the native library.\ncmake_minimum_required(VERSION 3.22.1)\n# Declares and names the project.\nproject(&quot;pa&quot;)\n# Creates and names a library, sets it as either STATIC\n# or SHARED, and provides the relative paths to its source code.\n# You can define multiple libraries, and CMake builds them for you.\n# Gradle automatically packages shared libraries with your APK.\nadd_library( # Sets the name of the library.\n        pa\n        # Sets the library as a shared library.\n        SHARED\n        # Provides a relative path to your source file(s).\n        main.cpp)\n# Searches for a specified prebuilt library and stores the path as a\n# variable. Because CMake includes system libraries in the search path by\n# default, you only need to specify the name of the public NDK library\n# you want to add. CMake verifies that the library exists before\n# completing its build.\nfind_library( # Sets the name of the path variable.\n        log-lib\n        # Specifies the name of the NDK library that\n        # you want CMake to locate.\n        log)\n# Searches for a package provided by the game activity dependency\nfind_package(game-activity REQUIRED CONFIG)\n# Specifies libraries CMake should link to your target library. You\n# can link multiple libraries, such as libraries you define in this\n# build script, prebuilt third-party libraries, or system libraries.\ntarget_link_libraries( # Specifies the target library.\n        pa\n        android\n        # The game activity\n        game-activity::game-activity\n        # EGL, required for configuring the display context\n        EGL\n        # GL ES 3, used for the sample renderer\n        GLESv3\n        # for AImageDecoder, to load images from resources\n        jnigraphics\n        # Links the target library to the log library\n        # included in the NDK.\n        ${log-lib})\n<\/pre><\/div>\n\n\n<p><\/p>\n\n\n\n<p>Also, ok to delete <em>android_robot.png<\/em> from <em>assets<\/em>.<\/p>\n\n\n\n<p>Run (green arrow) again, make sure it still works.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p>Final touch: As was defined by the project settings, the app name on the device is &#8220;pa&#8221;. Let&#8217;s change it to something more meaningful. <\/p>\n\n\n\n<p>Stop app (red square).<\/p>\n\n\n\n<p>Open <em>app\/manifests\/AndroidManifest.xml<\/em><\/p>\n\n\n\n<p>and change <em>android:label<\/em> property to let&#8217;s say &#8220;Hello Android&#8221;:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: xml; highlight: [10,16]; title: ; notranslate\" title=\"\">\n&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;\n&lt;manifest xmlns:android=&quot;http:\/\/schemas.android.com\/apk\/res\/android&quot;\n    xmlns:tools=&quot;http:\/\/schemas.android.com\/tools&quot;&gt;\n    &lt;application\n        android:allowBackup=&quot;true&quot;\n        android:dataExtractionRules=&quot;@xml\/data_extraction_rules&quot;\n        android:fullBackupContent=&quot;@xml\/backup_rules&quot;\n        android:icon=&quot;@mipmap\/ic_launcher&quot;\n        android:label=&quot;Hello Android&quot;\n        android:supportsRtl=&quot;true&quot;\n        android:theme=&quot;@style\/Theme.Pa&quot;\n        tools:targetApi=&quot;31&quot;&gt;\n        &lt;activity\n            android:name=&quot;.MainActivity&quot;\n            android:screenOrientation=&quot;portrait&quot;\n            android:exported=&quot;true&quot;&gt;\n            &lt;intent-filter&gt;\n                &lt;action android:name=&quot;android.intent.action.MAIN&quot; \/&gt;\n                &lt;category android:name=&quot;android.intent.category.LAUNCHER&quot; \/&gt;\n            &lt;\/intent-filter&gt;\n            &lt;meta-data\n                android:name=&quot;android.app.lib_name&quot;\n                android:value=&quot;pa&quot; \/&gt;\n        &lt;\/activity&gt;\n    &lt;\/application&gt;\n&lt;\/manifest&gt;\n\n<\/pre><\/div>\n\n\n<p><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>I also added property <em>android:screenOrientation=&#8221;portrait&#8221;<\/em>, since <em>GameActivity <\/em>handles screen-rotate not very gracefully (it restarts <em>main.cpp<\/em> completely), besides I planned to do so anyway (for the sake of future game layout).<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p><em>Optional <\/em>(just in case you ever need): <\/p>\n\n\n\n<p>Another thing I changed here is an <em>applicationId<\/em> (to <strong>com.writingagame.pa_hello<\/strong>).. Changing it in <em>build.gradle<\/em> or in <em>AndroidManifest<\/em> or by <em>refactoring <\/em>a package folder name  didn&#8217;t really work. <\/p>\n\n\n\n<p>Correct procedure is: In the Project explorer (left pane) right-click on <strong>app <\/strong><em>-&gt; Open Module Settings -&gt; Modules -&gt; Default Config<\/em>. Change <em>Application ID<\/em>. Then &#8211; <strong>OK<\/strong><\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p>Run (green arrow) again, perfect.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p>Just in case: When You open Android Studio next time, it will pre-load this <em>pa<\/em> project automatically.<\/p>\n\n\n\n<p>To disable pre-load go to <em>top menu -&gt; File -&gt; Settings -&gt; Appearance &amp; Behavior -&gt; System Settings<\/em>, <\/p>\n\n\n\n<p><strong>uncheck <\/strong><em>Reopen Projects on Startup<\/em>.<\/p>\n\n\n\n<p>Apply, Ok.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p>For now, let&#8217;s put Android aside and move on to the PC.<\/p>\n\n\n\n<p>Stop app (red square) and close Android Studio.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n","protected":false},"excerpt":{"rendered":"<p class=\"mb-2\">Tags: Android Studio, NDK, GameActivity, C++, OpenGL ES 3.2, EGL, Native Programming Default Android languages are Java and Kotlin. In order to use C++ we&#8217;ll need so called NativeActivity. Start Android Studio. Pick New Project. Scroll down, select Game Activity (C++) which is a direct descendant of NativeActivity and shares a similar architecture. It&#8217;s a [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":210,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4],"tags":[],"class_list":["post-1735","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-cross-platform-3d"],"_links":{"self":[{"href":"https:\/\/writingagame.com\/index.php\/wp-json\/wp\/v2\/posts\/1735","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/writingagame.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/writingagame.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/writingagame.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/writingagame.com\/index.php\/wp-json\/wp\/v2\/comments?post=1735"}],"version-history":[{"count":81,"href":"https:\/\/writingagame.com\/index.php\/wp-json\/wp\/v2\/posts\/1735\/revisions"}],"predecessor-version":[{"id":4277,"href":"https:\/\/writingagame.com\/index.php\/wp-json\/wp\/v2\/posts\/1735\/revisions\/4277"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/writingagame.com\/index.php\/wp-json\/wp\/v2\/media\/210"}],"wp:attachment":[{"href":"https:\/\/writingagame.com\/index.php\/wp-json\/wp\/v2\/media?parent=1735"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/writingagame.com\/index.php\/wp-json\/wp\/v2\/categories?post=1735"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/writingagame.com\/index.php\/wp-json\/wp\/v2\/tags?post=1735"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}