I am using the quoted function below to render a ‘tech tree’ in a game I am making. It works but is pretty performance heavy when the tech tree grows. In my case it is taking up to 3 seconds to render, and it is called whenever the graph state changes which in my game can be every 10 seconds or so if the user is on that screen that shows it.
I am wondering if it would be beneficial to use a web worker, I have spent a few hours trying to set one up but I could use some tips on which libraries to use for this, and how to set it up using CDNs and ES6, as my project is a web app, not nodeJS.
If this would not be the solution needed to speed up these renders, any better libraries i can use? I already cache the render for when the state doesnt change and for example the user just switches to that screen, but if they are on that screen and it needs to change, I need to show that update.
Here is the function and what I pass in:
export async function drawTechTree(techData, svgElement, renew) {
const cachedTree = getRenderedTechTree();
const container = document.querySelector(svgElement);
container.innerHTML = '';
const bgColor = getComputedStyle(container).getPropertyValue('--bg-color').trim();
const textColor = getComputedStyle(container).getPropertyValue('--text-color').trim();
const researchedBgColor = getComputedStyle(container).getPropertyValue('--text-color').trim();
const researchedTextColor = getComputedStyle(container).getPropertyValue('--ready-text').trim();
const researchedTechs = getTechUnlockedArray();
const svgWidth = container.clientWidth || container.parentNode.clientWidth;
const svgHeight = container.clientHeight || container.parentNode.clientHeight;
if (cachedTree && !renew) {
container.innerHTML = '';
container.appendChild(cachedTree.cloneNode(true));
setupTooltip(svgElement);
return;
}
let graphDef = `digraph TechTree {
graph [bgcolor="${bgColor}", size="${svgWidth / 72},${svgHeight / 72}!", size="10,7!", rankdir="TB"];
node [
color="${textColor}"
style="filled,rounded",
shape="box",
fontname="Arial",
fontsize=24,
penwidth=4
fixedsize=true,
width=4.5,
height=1.3
];
edge [
color="${textColor}",
penwidth=2,
arrowsize=1.2,
fontname="Arial",
fontsize=10
];
`;
let title = `<b>???</b><br/>???`;
for (const [key, value] of Object.entries(techData)) {
const isResearched = researchedTechs.includes(key);
const nodeBgColor = isResearched ? researchedBgColor : bgColor;
const nodeTextColor = isResearched ? researchedTextColor : textColor;
const capitalisedTechName = capitaliseString(key);
const separatedCapitalisedTechNames = capitalisedTechName.replace(/([a-z])([A-Z])/g, '$1 $2');
const price = value.price;
if (getUpcomingTechArray().includes(key) && !getRevealedTechArray().includes(key)) {
title = `<b>???</b><br/>Price: ${price}`;
} else {
title = `<b>${separatedCapitalisedTechNames}</b><br/>Price: ${price}`;
}
graphDef += `${key} [label=<${title}> shape="box" style="rounded,filled" fontcolor="${nodeTextColor}" fillcolor="${nodeBgColor}" fontname="Arial"];n`;
}
for (const [key, value] of Object.entries(techData)) {
const appearsAt = value.appearsAt || [];
if (appearsAt.length > 1) {
for (let i = 1; i < appearsAt.length; i++) {
const prereq = appearsAt[i];
if (prereq) {
graphDef += `${prereq} -> ${key};n`;
}
}
}
}
graphDef += "}";
const graphviz = d3.select(svgElement)
.graphviz()
.zoom(false)
.scale(0.8)
.fit(false);
graphviz.renderDot(graphDef);
setTimeout(() => {
setupTooltip(svgElement);
setRenderedTechTree(container);
}, 50);
}
I pass in:
export async function getTechTreeData(renew) {
let techData = getResourceDataObject('techs');
const unlockedTechs = getTechUnlockedArray();
const upcomingTechs = getUpcomingTechArray();
techData = Object.fromEntries(
Object.entries(techData).filter(([key]) =>
unlockedTechs.includes(key) || upcomingTechs.includes(key)
)
);
await drawTechTree(techData, '#techTreeSvg', renew);
}
I have tried using a timer and consoling out the time taken for each part of the function, and the bottleneck is where it renders it, taking up to 3 seconds sometimes, so maybe I need a better solution, or help setting up a web worker, which is mentioned in the graphViz documentation but I cannot seem to get working in the CDN ES6 environment.
So open ended really, either help with this, or ideas for better libraries for my needs, which is to visualise my tech tree data which looks like below:
techs: {
knowledgeSharing: { appearsAt: [0, null, null], prereqs: [null], price: 150, idForRenderPosition: 10 },
fusionTheory: { appearsAt: [500, "knowledgeSharing", ""], prereqs: ['Knowledge Sharing'], price: 750, idForRenderPosition: 20 },
hydrogenFusion: { appearsAt: [1000, "fusionTheory", ""], prereqs: ['Fusion Theory'], price: 1150, idForRenderPosition: 30 },
heliumFusion: { appearsAt: [2000, "hydrogenFusion", ""], prereqs: ['Hydrogen Fusion'], price: 2300, idForRenderPosition: 40 },
carbonFusion: { appearsAt: [4100, "nobleGasCollection", ""], prereqs: ['Noble Gas Collection'], price: 4300, idForRenderPosition: 50 },
basicPowerGeneration: { appearsAt: [3000, "heliumFusion", ""], prereqs: ['Helium Fusion'], price: 4200, idForRenderPosition: 51 },
sodiumIonPowerStorage: { appearsAt: [5000, "basicPowerGeneration", ""], prereqs: ['Basic Power Generation'], price: 7000, idForRenderPosition: 52 }
}