Jitai (字体): The font randomizer that fits

Have you enabled developer mode? FAQ | Tampermonkey

Thanks for the suggestion but neither of my browsers is a Chrome derivative so I don’t think this applies - the FAQ is aimed very specifically at Chrome and other browsers based on it.

It has started working!!! I have not done anything whatsoever, not reloaded anything, not adjusted anything - nothing. I did some reviews, normal font only, then some lessons. By the time I’d finished the lessons, some more reviews were available and when I started them, lo and behold, Jitai was running. All’s well that ends well, I suppose (reminds me of the old joke about the software engineer running the car with the brake failure down the mountain road one more time) - fingers crossed it keeps on working. Looks fantastic - many thanks to all who have contributed both to the script and to my dealings with it.

You sure you didn’t do something such as load the reviews in a new tab/window or refresh the reviews tab (and had neglected to try that in previous testing)?. This is in reference to the Turbo events issue, which you can do a search for if you’re curious (as I’m getting a bit tired of finding the appropriate link every time…).

Anyway, you’re welcome to try my modified version as well if you keep having issues, since I’ve updated it to function with complete awareness of the Turbo loading.
I also added a ctrl+shift “hold” trigger to more easily temporarily toggle the normal font. And there’s some other optimizations I’ve made over time as well.

Check it out
// ==UserScript==
// @name        Jitai
// @author      @marciska
// @namespace   marciska
// @description Displays your WaniKani reviews with randomized fonts (based on original by @obskyr with various fixes applied)
// @version     3.1.3
// @icon        https://raw.github.com/marciska/Jitai/master/imgs/jitai.ico
// @match       https://www.wanikani.com/*
// @match       https://preview.wanikani.com/*
// @license     MIT; http://opensource.org/licenses/MIT
// @run-at      document-end
// @grant       none
// ==/UserScript==

(function(global) {
	'use strict';

    /* eslint no-multi-spaces: off */
    /* global wkof */

    //===================================================================
    // Variables
    //-------------------------------------------------------------------
    const script_id = "jitai";
    const script_name = "Jitai";
    const wkof_version_needed = '1.2.0';
    const listenerOptions = { passive: true };
    const pageRegex = /^\/subjects\/(?:review.*\/?|extra_study)$/;
    let item_element;
    let style_element;
    let setup_complete = false;

    // ----- Fonts -----
    const example_sentence = '質問:私立探偵 (P.I.) はどんな靴を履いていますか?<br>答え:・・・スニーカー。(笑)';
    let font_default;
    let font_randomized;

    // available fonts
    let font_pool = {
        // Default OSX fonts
        "Hiragino-Kaku-Gothic-Pro" : {full_font_name: "Hiragino Kaku Gothic Pro, ヒラギノ角ゴ Pro W3", display_name: "Hiragino Kaku Gothic Pro", url: 'local', recommended: false},
        "Hiragino-Maru-Gothic-Pro" : {full_font_name: "Hiragino Maru Gothic Pro, ヒラギノ丸ゴ Pro W3", display_name: "Hiragino Maru Gothic Pro", url: 'local', recommended: false},
        "Hiragino-Mincho-Pro" : {full_font_name: "Hiragino Mincho Pro, ヒラギノ明朝 Pro W3", display_name: "Hiragino Mincho Pro", url: 'local', recommended: false},
        // Default Windows fonts
        "Meiryo" : {full_font_name: "Meiryo, メイリオ", display_name: "Meiryo", url: 'local', recommended: false},
        "MS-PGothic" : {full_font_name: "MS PGothic, MS Pゴシック, MS Gothic, MS ゴック", display_name: "MS Gothic", url: 'local', recommended: false},
        "MS-PMincho" : {full_font_name: "MS PMincho, MS P明朝, MS Mincho, MS 明朝", display_name: "MS Mincho", url: 'local', recommended: false},
        "Yu-Gothic" : {full_font_name: "Yu Gothic, YuGothic", display_name: "Yu Gothic", url: 'local', recommended: false},
        "Yu-Mincho" : {full_font_name: "Yu Mincho, YuMincho", display_name: "Yu Mincho", url: 'local', recommended: false},
        // GoogleFonts
        "Zen-Kurenaido" : {full_font_name: "Zen Kurenaido", display_name: "Zen Kurenaido", url: 'https://fonts.googleapis.com/css?family=Zen+Kurenaido&subset=japanese', recommended: false},
        "Kaisei-Opti" : {full_font_name: "Kaisei Opti", display_name: "Kaisei Opti", url: 'https://fonts.googleapis.com/css?family=Kaisei+Opti&subset=japanese', recommended: false},
        "Reggae-One" : {full_font_name: "Reggae One", display_name: "Reggae One", url: 'https://fonts.googleapis.com/css?family=Reggae+One&subset=japanese', recommended: false},
        "New-Tegomin" : {full_font_name: "New Tegomin", display_name: "New Tegomin", url: 'https://fonts.googleapis.com/css?family=New+Tegomin&subset=japanese', recommended: false},
        "Yuji-Boku" : {full_font_name: "Yuji Boku", display_name: "Yuji Boku", url: 'https://fonts.googleapis.com/css?family=Yuji+Boku&subset=japanese', recommended: false},
        "Yuji-Mai" : {full_font_name: "Yuji Mai", display_name: "Yuji Mai", url: 'https://fonts.googleapis.com/css?family=Yuji+Mai&subset=japanese', recommended: false},
        "Yuji-Syuku" : {full_font_name: "Yuji Syuku", display_name: "Yuji Syuku", url: 'https://fonts.googleapis.com/css?family=Yuji+Syuku&subset=japanese', recommended: false},
        "DotGothic16" : {full_font_name: "DotGothic16", display_name: "DotGothic16", url: 'https://fonts.googleapis.com/css?family=DotGothic16&subset=japanese', recommended: true},
        "Hachi-Maru-Pop" : {full_font_name: "Hachi Maru Pop", display_name: "Hachi Maru Pop", url: 'https://fonts.googleapis.com/css?family=Hachi+Maru+Pop&subset=japanese', recommended: true},
        "Yomogi" : {full_font_name: "Yomogi", display_name: "Yomogi", url: 'https://fonts.googleapis.com/css?family=Yomogi&subset=japanese', recommended: false},
        "Potta-One" : {full_font_name: "Potta One", display_name: "Potta One", url: 'https://fonts.googleapis.com/css?family=Potta+One&subset=japanese', recommended: false},
        "Dela-Gothic-One" : {full_font_name: "Dela Gothic One", display_name: "Dela Gothic One", url: 'https://fonts.googleapis.com/css?family=Dela+Gothic+One&subset=japanese', recommended: true},
        "RocknRoll-One" : {full_font_name: "RocknRoll One", display_name: "RocknRoll One", url: 'https://fonts.googleapis.com/css?family=RocknRoll+One&subset=japanese', recommended: false},
        "Stick" : {full_font_name: "Stick", display_name: "Stick", url: 'https://fonts.googleapis.com/css?family=Stick&subset=japanese', recommended: true},
        "Yusei-Magic" : {full_font_name: "Yusei Magic", display_name: "Yusei Magic", url: 'https://fonts.googleapis.com/css?family=Yusei+Magic&subset=japanese', recommended: false},
        "Kaisei-Decol" : {full_font_name: "Kaisei Decol", display_name: "Kaisei Decol", url: 'https://fonts.googleapis.com/css?family=Kaisei+Decol&subset=japanese', recommended: false},
        "Kaisei-Tokumin" : {full_font_name: "Kaisei Tokumin", display_name: "Kaisei Tokumin", url: 'https://fonts.googleapis.com/css?family=Kaisei+Tokumin&subset=japanese', recommended: false},
        // Other popular fonts
        "ArmedBanana" : {full_font_name: "ArmedBanana", display_name: "Armed Banana", url: 'https://marciska.github.io/Jitai/ArmedBanana.css', recommended: true},
        "ArmedLemon" : {full_font_name: "ArmedLemon", display_name: "Armed Lemon", url: 'local', recommended: false},
        "AoyagiReisyosimo-AoyagiKouzan" : {full_font_name: "aoyagireisyosimo2, AoyagiKouzanFont2OTF", display_name: "Aoyagi Kouzan", url: 'local', recommended: false},
        "Aquafont" : {full_font_name: "aquafont", display_name: "Aquafont", url: 'local', recommended: false},
        "Shin-Maru-Go-Pro" : {full_font_name: "A-OTF Shin Maru Go Pro", display_name: "Shin Maru Go Pro", url: 'local', recommended: false},
        "Chifont" : {full_font_name: "'chifont+', chifont", display_name: "Chifont", url: 'local', recommended: false},
        "Cinecaption" : {full_font_name: "cinecaption", display_name: "Cinecaption", url: 'local', recommended: false},
        "Darts" : {full_font_name: "darts font", display_name: "Darts", url: 'https://marciska.github.io/Jitai/Darts.css', recommended: false},
        "EPSON-行書体M" : {full_font_name: "EPSON 行書体M", display_name: "EPSON 行書体M", url: 'local', recommended: false},
        "EPSON-正楷書体M" : {full_font_name: "EPSON 正楷書体M", display_name: "EPSON 正楷書体M", url: 'local', recommended: false},
        "EPSON-教科書体M" : {full_font_name: "EPSON 教科書体M", display_name: "EPSON 教科書体M", url: 'local', recommended: false},
        "EPSON-太明朝体B" : {full_font_name: "EPSON 太明朝体B", display_name: "EPSON 太明朝体B", url: 'local', recommended: false},
        "EPSON-太行書体B" : {full_font_name: "EPSON 太行書体B", display_name: "EPSON 太行書体B", url: 'local', recommended: false},
        "EPSON-丸ゴシック体M" : {full_font_name: "EPSON 丸ゴシック体M", display_name: "EPSON 丸ゴシック体M", url: 'local', recommended: false},
        "FC-Flower" : {full_font_name: "FC-Flower", display_name: "FC-Flower", url: 'https://marciska.github.io/Jitai/FCFlower.css', recommended: false},
        "HakusyuKaisyoExtraBold_kk" : {full_font_name: "HakusyuKaisyoExtraBold_kk", display_name: "Hakusyu Kaisyo (Extra Bold)", url: 'local', recommended: false},
        "Hosofuwafont" : {full_font_name: "Hosofuwafont", display_name: "Hoso Fuwa", url: 'https://marciska.github.io/Jitai/HosoFuwa.css', recommended: false},
        "Nagayama-Kai" : {full_font_name: "nagayama_kai", display_name: "Nagayama Kai", url: 'https://marciska.github.io/Jitai/NagayamaKai.css', recommended: false},
        "Pop-Rum-Cute" : {full_font_name: "PopRumCute", display_name: "Pop Rum Cute", url: 'https://marciska.github.io/Jitai/PopRumCute.css', recommended: false},
        "San-Chou-Me" : {full_font_name: "santyoume-font", display_name: "San Chou Me", url: 'https://marciska.github.io/Jitai/SanChouMe.css', recommended: false},
    };

    // fonts that are selected by user to be shown
    let font_pool_selected = [];

    // bool indicating if hovering effect is flipped
    let hover_flipped = false;

    // bool indicating if a modifier key is being held down
    let modifier_held = false;

    //===================================================================
    // Settings related stuff
    //-------------------------------------------------------------------
    function installSettingsMenu() {
        wkof.Menu.insert_script_link({
            name:      script_id,
            submenu:   'Settings',
            title:     script_name,
            on_click:  settingsOpen
        });
    }

    function settingsPrepare(dialog) {
        dialog.dialog({width:720});
    }
    async function settingsSave(settings) {
        await wkof.Settings.save(script_id);
        settingsApply(settings);
        settingsClose(settings);
    }
    async function settingsLoad() {
        const settings = await wkof.Settings.load(script_id);
        settingsApply(settings);
    }
    function settingsClose(settings) {
        // Remove all urls to fonts we don't use
        for (const [fontkey, value] of Object.entries(font_pool)) {
            if (!(fontkey in settings)) { continue; }
            if (!settings[fontkey]) { // check if font is disabled
                // if it is a webfont, uninstall webfont
                if (value.url !== 'local') {
                    uninstallWebfont(value.full_font_name, value.url);
                }
            }
        }
    }
    function settingsApply(settings) {
        // clear cache of selected fonts
        font_pool_selected = [];
        // now refill the pool of selected fonts
        for (const [fontkey, value] of Object.entries(font_pool)) {
            if (!(fontkey in settings)) { continue; }
            if (settings[fontkey]) { // check if font is enabled
                // if it is a webfont, install webfont
                if (value.url !== 'local') {
                    installWebfont(value.full_font_name, value.url);
                    // recheck if font is installed on machine
                    // if (!isFontInstalled(value.full_font_name)) { continue; }
                } else { // check if local font is installed on machine
                    if (!isFontInstalled(value.full_font_name)) { continue; }
                }
                // put fonts in selected fonts
                let frequency = settings[fontkey+'_frequency'];
                if (frequency === undefined) { frequency = 1; } // if script started first time, the value might be undefined
                frequency = Math.ceil(frequency);
                for (let i = 0; i < frequency; i++) {
                    font_pool_selected.push(value.full_font_name);
                }
            }
        }

        // randomly shuffle font pool
        shuffleArray(font_pool_selected);

        if (setup_complete && pageRegex.test(document.location.pathname)) {
            updateRandomFont();
            setflippedFontState();
        }
    }

    function settingsOpen() {
        // install webfonts, and remove non-accesible local fonts for selection
        for (const [fontkey, value] of Object.entries(font_pool)) {
            if (value.url !== 'local') { // install webfonts
                installWebfont(value.full_font_name, value.url);
            } else if (!isFontInstalled(value.full_font_name)) { // remove local fonts that are not installed for selection
                delete font_pool[fontkey];
            }
        }

        // order fonts alphabetically
        const fontkeys = Object.keys(font_pool).sort((a, b) =>
            font_pool[a].display_name.localeCompare(font_pool[b].display_name, undefined, {sensitivity: 'base'})
        );

        // prepare selection option for every font
        const font_selector = Object.fromEntries(fontkeys.map(fontkey => ['BOX_'+fontkey, {
            type: 'group',
            label: `<span class="font_label${font_pool[fontkey].recommended ? ' font_recommended' : ''}">${font_pool[fontkey].display_name}</span>`,
            content: {
                sampletext: {
                    type: 'html',
                    html: `<p class="font_example" style="font-family:'${font_pool[fontkey].full_font_name}'">${example_sentence}</p>`
                },
                [fontkey]: {
                    type: 'checkbox',
                    label: 'Use font in '+script_name,
                    default: false,
                },
                [fontkey+'_frequency']: {
                    type: 'number',
                    label: 'Frequency',
                    hover_tip: 'The higher the value, the more often you see this font during review. It is affected by how many fonts you have enabled.',
                    default: 1,
                    min: 1,
                    step: 1,
                }
            }
        }]));

        // prepare configuration dialog
        let dialog = new wkof.Settings({
            script_id:  script_id,
            title:      script_name+' Settings',
            pre_open:   settingsPrepare,
            on_save:    settingsSave,
            on_close:   settingsClose,
            content: {
                currentfont: {
                    type: 'group',
                    label: `<span class="font_label">Current Font: ${font_randomized}</span>`,
                    content: {
                        sampletext: {
                            type: 'html',
                            html: `<p class="font_example" style="font-family:'${font_randomized}'">${example_sentence}</p>`
                        }
                    }
                },
                legend: {
                    type: 'html',
                    html: `<div class="font_legend"><span class="font_recommended">: Recommended Font</span></div>`
                },
                divider: {
                    type: 'section',
                    label: `Filter Fonts (${fontkeys.length} available)`
                },
                ...font_selector
            }
        });
        dialog.open();
    }

    //===================================================================
    // Main Script Functionality
    //-------------------------------------------------------------------

    function getDefaultFont(item) {
        return getComputedStyle(item).fontFamily;
    }

    function isFontInstalled(font_name) {
        // Approach from kirupa.com/html5/detect_whether_font_is_installed.htm - thanks!
        // Will return false for the browser's default monospace font, sadly.
        const canvas = document.createElement('canvas');
        const context = canvas.getContext('2d');
        const text = "wim-—l~ツ亻".repeat(100); // Characters with widths that often vary between fonts.

        context.font = "72px monospace";
        const defaultWidth = context.measureText(text).width;

        // Microsoft Edge raises an error when a context's font is set to a string
        // containing certain special characters... so that needs to be handled.
        try {
            context.font = "72px " + font_name + ", monospace";
        } catch (e) {
            return false;
        }
        const testWidth = context.measureText(text).width;

        return testWidth !== defaultWidth;
    }

    function isCanvasBlank(canvas) {
        return !canvas.getContext('2d', { willReadFrequently: true })
            .getImageData(0, 0, canvas.width, canvas.height).data
            .some(channel => channel !== 0);
    }
    function canRepresentGlyphs(fontName, glyphs) {
        const canvas = document.createElement('canvas');
        canvas.width = 50;
        canvas.height = 50;
        const context = canvas.getContext("2d", { willReadFrequently: true });
        context.textBaseline = 'top';

        context.font = "24px " + fontName;

        let result = true;
        for (let i = 0; i < glyphs.length; i++) {
            context.fillText(glyphs[i], 0, 0);
            if (isCanvasBlank(canvas)) {
                result = false;
                break;
            }
            context.clearRect(0, 0, canvas.width, canvas.height);
        }

        return result;
    }

    function installWebfont(font_name, url) {
        // If webfont already installed on local machine, don't need to reinstall
        if (isFontInstalled(font_name)) { return; }

        // install webfont
        const link = document.querySelector(`link[href="${url}"]`);
        if (!link) {
            const newlink = document.createElement("link");
            newlink.href = url;
            newlink.rel = "stylesheet";
            document.head.append(newlink);
        }
    }
    function uninstallWebfont(font_name, url) {
        const link = document.querySelector(`link[href="${url}"]`);
        if (!link) {
            link.remove();
        }
    }
    function addPreconnectLinks() {
        // add preconnect links to GoogleFonts servers
        let googleApiLink = document.querySelector(`link[href="https://fonts.googleapis.com"]`);
        if (!googleApiLink) {
            googleApiLink = document.createElement("link");
            googleApiLink.rel = "preconnect";
            googleApiLink.href = "https://fonts.googleapis.com";
            document.head.append(googleApiLink);
        }
        let gstaticLink = document.querySelector(`link[href="https://fonts.gstatic.com"]`);
        if (!gstaticLink) {
            gstaticLink = document.createElement("link");
            gstaticLink.rel = "preconnect";
            gstaticLink.href = "https://fonts.gstatic.com";
            gstaticLink.crossOrigin = true;
            document.head.append(gstaticLink);
        }
    }

    function shuffleArray(array) {
        for (let i = array.length - 1; i > 0; i--) {
            const j = Math.floor(Math.random() * (i + 1));
            [array[i], array[j]] = [array[j], array[i]];
        }
    }

    function updateRandomFont() {
        // choose new random font
        const glyphs = item_element.innerText;
        if (font_pool_selected.length === 0) {
            console.log(script_name+': empty font pool!');
            font_randomized = font_default;
        } else {
            do {
                font_randomized = font_pool_selected[Math.floor(Math.random() * font_pool_selected.length)];
            } while (!canRepresentGlyphs(font_randomized, glyphs));
        }
        style_element.innerHTML = style_element.innerHTML.replace(/(--font-family-japanese:).*;([\s\S]*?--font-family-japanese-hover:).*;/,`$1 ${font_randomized};$2 ${font_default};`);
    }

    function setflippedFontState() {
        const flipped = hover_flipped ? !modifier_held : modifier_held;
        item_element.classList.toggle('flipped', flipped);
    }

    function insertStyle() {
        // insert CSS
        if ((style_element = document.getElementById(`${script_id}-style`)) != null) return;
        style_element = document.createElement('style');
        style_element.setAttribute('id', `${script_id}-style`);
        style_element.innerHTML = `
.font_label {
    font-size: 1.2em;
    display: flex;
    align-items: center;
}
.font_legend {
    text-align: center;
    margin: 15px !important;
}
.font_example {
    margin: 5px 10px 10px 10px !important;
    font-size: 1.6em;
    line-height: 1.1em;
}
.font_recommended::before {
    content: '⭐️';
    font-size: 1.4em;
}
.character-header__characters {
    --font-family-japanese: ;
    --font-family-japanese-hover: ;
    font-family: var(--font-family-japanese);
}
.character-header__characters:hover { font-family: var(--font-family-japanese-hover); }
.character-header__characters.flipped { font-family: var(--font-family-japanese-hover); }
.character-header__characters.flipped:hover { font-family: var(--font-family-japanese); }
`;
        document.head.appendChild(style_element);
    }

    function cacheDefaultElementStyles() {
        if (font_default !== undefined && font_randomized !== undefined) return;
        item_element = document.getElementsByClassName("character-header__characters")[0];
        if (!item_element) return;
        font_default = getDefaultFont(item_element);
        font_randomized = font_default;
    }

    function onKeyDown(event) {
        if (event.repeat) return;
        switch (event.key) {
            // on holding ctrl and shift, swap the shown font
            case 'Control':
            case 'Shift':
                if (!event.ctrlKey || !event.shiftKey) return;
                modifier_held = true;
                setflippedFontState();
                break;
            // on alt+j, update to a new random font
            case 'j':
                if (!event.altKey) return;
                updateRandomFont();
                setflippedFontState();
                break;
        }
    }
    function onKeyUp(event) {
        if (event.repeat) return;
        switch (event.key) {
            case 'Control':
            case 'Shift':
                modifier_held = false;
                setflippedFontState();
                break;
        }
    }
    function onLostFocus() {
        modifier_held = false;
        setflippedFontState();
    }
    function onDidAnswerQuestion() {
        hover_flipped = true;
        setflippedFontState();
    }
    function onDidUnanswerQuestion() {
        hover_flipped = false;
        updateRandomFont();
        setflippedFontState();
    }
    function onWillShowNextQuestion() {
        hover_flipped = false;
        if (setup_complete) {
            updateRandomFont();
            setflippedFontState();
        }
    }

    function registerJitaiEvents() {
        // on answer submission, invert hovering event
        //  - normal  : default font
        //  - hovering: randomized font
        document.body.addEventListener("didAnswerQuestion", onDidAnswerQuestion, listenerOptions);

        // on advancing to next item question, randomize font again
        document.body.addEventListener("willShowNextQuestion", onWillShowNextQuestion, listenerOptions);

        // on reverting an answer by DoubleCheckScript, reroll random font and fix inverting of hovering
        document.body.addEventListener("didUnanswerQuestion", onDidUnanswerQuestion, listenerOptions);

        // add event to show regular font
        // add event to reroll randomized font
        document.body.addEventListener("keydown", onKeyDown, listenerOptions);
        document.body.addEventListener("keyup", onKeyUp, listenerOptions);
        // when page loses focus, revert any temporary modifications
        document.body.addEventListener("blur", onLostFocus, listenerOptions);
    }

    //===================================================================
    // Script Startup
    //-------------------------------------------------------------------
    function startup() {
        // initialization of the Wanikani Open Framework
        if (!wkof) {
            if (confirm(script_name+' requires Wanikani Open Framework.\nDo you want to be forwarded to the installation instructions?'))
                global.location.href = 'https://community.wanikani.com/t/instructions-installing-wanikani-open-framework/28549';
            return;
        }
        if (wkof.version.compare_to(wkof_version_needed) === 'older') {
            if (confirm(script_name+' requires Wanikani Open Framework version '+wkof_version_needed+'.\nDo you want to be forwarded to the update page?'))
                global.location.href = 'https://greasyfork.org/en/scripts/38582-wanikani-open-framework';
            return;
        }
        const wkof_modules = 'Settings';
        wkof.include(wkof_modules);
        return wkof.ready(wkof_modules)
            .then(addPreconnectLinks)
            .then(settingsLoad)
            .then(() => {setup_complete = true;});
    }

    function onReviewsPage() {
        const wkof_modules = 'Menu';
        wkof.include(wkof_modules);
        return wkof.ready(wkof_modules)
            .then(cacheDefaultElementStyles)
            .then(insertStyle)
            .then(registerJitaiEvents)
            .then(installSettingsMenu)
            .then(updateRandomFont)
            .then(setflippedFontState);
    }

    startup().then(() => {
        wkof.on_pageload(pageRegex, onReviewsPage);
    });

})(window);

Edit: Made a couple small improvements to better handle edge case scenarios.

Edit 2: Updated the script to utilize wkof version 1.2.0, and to be a bit more efficient about how it adds the event listeners (i.e., adding them to the body element, so they’re automatically removed when the page navigates). (To be fully reliable, you should probably wait till the upcoming patch update of the open framework is released).

2 Likes

Inserio: I know how unlikely it sounds, and I’m sure I would not believe it myself, but I truly did not do anything. Certainly no refreshes or reloads (other than that switching from reviews to lessons to reviews again did reload the tabs, but I had already tried that lots of times). Anyway, thanks for the note about the Turbo issue which I will look up, and thanks also for your modified script which I will indeed try if problems persist.

1 Like

god i love when ppl who realllyy know how to code/know MUTIPLE coding launagues just post scripts so i can play random anime window games on my Chromebook when i cant use my monitor LMAO

… anyway, in terms of, creating a video game. what coding language is best, i tried learning c++ and phyton a couple years ago so i remember some stuff but not everything anymore :sweat:

The best one tbh is the one that motivates you the most to actually use it.

Though, check out https://www.develop.games/ if you’re really lost and in need of some solid advice.

2 Likes

??
thanks man, but ive been learning jp for 2 1/2-3 years ww
i understand what I’m doing, thanks though, ill check it out in my free time heh!

ooooohhhh im so slow, sorry LMAOAO
I THOUGHT THAT LINK WAS FOR LIKE JP STUFF NOT CODING
i sorry www
yeah but, that wasn’t the question…
whats better for what and why, that’s really what i was looking for

Maybe I misunderstood :person_shrugging:

The thing is, a lot of coding languages can get the job done just as well as many others.
What I meant is that finding one that vibes with you such that learning and practicing the language doesn’t get in the way of you using it…finding that one is probably more important in the grand scheme of things than picking one based on what is “best”.

2 Likes

for like a steam, windows, xbox, pc, storymode with cgdct type stuff with like some spoken, pokemon type fight scenes.
a game like octopath travelerrrr

… id need to find va´s anyway cause i cant do all of them, especially no guy roles LMAO

1 Like

Outside specific niches, this is usually the wrong question to ask.

It’s almost always the wrong question to ask when starting out. Learning any language is going to make you familiar with programming concepts, and those transfer right to different languages.

If you want to know which language to learn for making a game, decide what kind of game you want to make, and then look at what engine you’d use for that (for the love of all that is holy don’t try to make your own engine), and then see what programming language you’d need for that. If you’re using Unreal for instance, you’ll probably end up using some C++ when their blueprint system doesn’t do what you need anymore (which may be never, it’s quite powerful really). But if you use Unity, you’re probably gonna end up with C# instead. If you go with RPG Maker, you’re gonna end up with Ruby. Godot uses GDScript, C# and C++. And who knows what else is out there.

Which language you need to learn is probably the last question to ask yourself, because it follows naturally from all the other bits you should really be worrying about first when making a game.

3 Likes

whats python for then?

ive done research and done courses in both c++ and python about 3-5 years ago, I’m just trying to recall my memories about it ww
in term of unity versus unreal engine, id mostly use whichever seem more interesting to me, but id most like use unreal since i feel it would be easier for to do so :woozy_face:
and especially for what i want to use it for cause the pixel storymode/pokemon type videogame and a 12 eposide anime series in around 15 months :kissing_smiling_eyes:
id just need to get into contact with uh va’s so that i can create the storyboard/character degsin in order to get started www

Anything you want it to be for, basically. Most of the time, what programming language you use is pretty inconsequential. There are more niche-focused languages, like PHP and JS for the web (though JS has expanded beyond that, in a way), and if you’re doing embedded development you’ll probably end up with C and assembly, possibly Rust, but beyond purpose-specific languages and very specific niches the language is unlikely to be a limiting factor in what you want to do. Anything you can do in Python, you can do in Go, or Ruby, or Rust, or Java, or C#, or…

That’s directly counter to what most game devs would say :sweat_smile: Unreal is a massively complex beast. Unity is too, but it’s generally more beginner-friendly and last I compared the two has a lot more resources available in terms of learning material, video tutorials, practice projects, etc.

That seems very ambitious, but don’t let that stop you

It also seems like maybe not the kind of thing I’d pick Unreal for, especially at a short timescale like that. I’d look at engines more tailored towards a specific purpose - maybe RPG Maker could do what you want to, while giving you an easier time in building the foundations? There may be other engines fit for the type of game you have in mind. Have a look around, and ask on some gamedev-focused communities, they might know.

That sounds to me like you’re focusing on the wrong things at the start, tbh. Thinking about story and art style is great, but start from a concept, plan it out, and work from there. If you start from the details and finishing touches, you’re gonna end up with a lot of wasted effort that doesn’t end up going anywhere.

3 Likes

no i have the storyline, the start and finish and the ppl i need to va, the animation style and i already taught myself how to key-frame, in between and other artist animation stuff, and i already taught myself how to sing n stuff. like i have all the artistic parts now. genuinely, i need the people to voice there roles. but, i later decided i would base there character designs, based off of the range of there voice, singing and non-singing.
i just need the gmail to reach all of em www

and yes, it would be more based on rng-style but the storyline would be the main focus, with in-between fighting scenes, that i would animate myself :kissing_smiling_eyes:
I’m just happy cause my school got a grant for 2 drawing tablets and since I’m in AP studio art, i got first access :woozy_face:

Will the game be in Japanese?

1 Like

Sorry to be boring, but back to Jitai for a moment: after my experience yesterday (ie that it just started to work, for no apparent reason), by today it had stopped again. This is on a laptop, so all that had happened was that it was suspended overnight - no actual change to anything, and I pick up where I left off. Out of interest, I did a few reviews with Jitai not running, then did a block of lessons, then returned to reviews, and this time Jitai was running. Not sure why it stopped, but it seems that doing some lessons and then going back to reviews causes it to start. I only mention this in case anyone finds it useful in some way - since it now seems possible to get it to work, I will stick with it as is. It is fantastic to see the range of fonts and be challenged that way.

Inserio: I will try your script when I have a few minutes; I’ll post a brief follow-up about how I get on.

I would love if someone with experience could list several ~hard~ fonts that are very different from each other and could plausibly be seen from time to time in native media / everyday life.

For context, I’m already hand-writing almost the entire set of WK kanji in Ringotan (1785 kanji set to review frequently with only 55 at Akachan and 370 at Gakusei), and I’m using this 8-bit esque font among others on WaniKani currently because reading Japanese on an old SNES game is a plausible use case I thought of for adding some difficulty. (Pictured: 酸 as in 酸素 (さんそ), oxygen)

I like the fact that these kinds of reviews force me to go back to the stroke order in my head to figure out what it is.