A story about my journey through a new challenge- Game Off 2024. We gonna tell everything that we made to achieve our final product. Everyday updating with the news. Stay tunned! π ## Day 01 - 02 Those firsts days were reserved for planning the game. We had already thought about which game style would like, even before the game had started. So we take those days to refine the idea and change little things to fit the theme - SECRETS. The game will be a manager clicker, where player would need to create recipes given by NPCs, very basic, but we need to think that we only have one month and have to do everything from graphic assets to song and music assets. This will be hard, but we are surly will be fun. ## Day 03 - 04 After deciding which style the game will have, I finally started creating the project on Unreal and setting the camera. As I said before, the game will be like a clicker, so I had two options to make- First would create the full game as widgets in Unreal which would limit me a little bit in what I want to do, well, the second option would create the game in a top-down like perspective and make the character invisible, so would looks like those manager game, like Cities Skyline and other city building. ## Day 05 This day was more easy, I started to make some main assets, but quickly realize that somethings will not be possible or too difficult for me to handle in this short time, so I changed some plans and started smaller. The basic idea is create a overcooked like game, but super simple and with a twist, instead of a restaurant making food, you will make potions with essences. You will have 5 essences types- Fire, Water, Earth, Magic and Disorder. Each potion will be made by merging two essences, and the combination will be discovered by player base on hints that the clients will say. They will come at random times and them make a dialog that contains the hint (This will be where we will fit the "SECRETS" jam's theme). ## Day 06 I started making the main mechanic of the game- Combining the essences to make a potion. So as this is my first game on Unreal, I was a little lost, so I stated very small- adding the text to ui and trying to change this base on click of some static mesh (This is a Unreal term, if you want me to make an article with the simplest terms of Unreal, mail me π). <Img src="/article-images/personal/game-off-2024-journey/day06.gif" name="Change text on ui by clicking a button" alt="Change text on ui by clicking a button" /> ## Day 07 - 08 In the day 07 I could not do so much because I was busy with work, so I did a lot in day 08. First, I needed a occlusion mask because the whole game will be running inside a building like a tavern, so I need the player to see through walls. I follow a very good tutorial about it and increase the details with a comment I saw here: [video link](https://www.youtube.com/watch?v=O6W7pLF2GwU) and the comment is from **@JAMbosco**. I just did not like how it glows so much that interferes into the objects color, but I will try to solve this later, most because I wanted to add the potion crafting system. Day 8 I also change the UI a lot, because I thought with myself and realize that the game will be almost click, so this mechanic has to be simple and fun to play, so I did some drawings and got the UI changed a lot and probably will change tomorrow too. But besides that, I also implement the potion selection logic before into this new UI. <Img src="/article-images/personal/game-off-2024-journey/day08.webp" name="New UI elements" alt="New UI elements" /> ## Day 09 -12 I need to confess that I procrastinated a lot during this time, specially because I was tired from work and just wanted to watch One Piece theories videos, but even so, I also did a really great addition and a very necessary clean up First about the addition was the NPC dialog box that will have a random request for the player to craft. When doing this, I realize that my code was garbage, I did not plan good enough and was everything kinda messy. For that reason, I made a full clean up in everything I have done. From the essence selector to crafting station, and even the logic of the items. Now they are classified as S_Item, a structure that has name, sprite and everything that an item need and not just an Enum of what essences are needed. I made the items using something I never used: a Data Table, and is very easy and helpful, because I feel that if I need to add some different type of item, will not crash everything. ## Day 12 - 17 I gotta admit I am a little late in what I've been planing. I a blink of eyes, we are already in half of the month, and I should have all the game ready and just do some optimizations, but I haven't and now, I need to compensate this in the last days. A project planning is difficult and you need to have your goals not just organized, but also realistic. I did not did this. But that is ok, because I already know how to compensate, and will be using free assets from the community. When I started the project, I wanted to do everything, from coding to music, modeling and assets 2D. That would be ok if the scale of my game wasn't that big and if I already knew how to do those things, but I am a starter in game development and learning all this is hard. But that's why I entered in this challenge. I will make this game no matter what and you can be sure that soon enough you will be playing Mystic Cauldron. I started the day trying to build the project for the first time. Yes, I haven't build until now, I discovered this is a very bad practice because everything you do in Unreal, can cause a build error, which the log does not help too much. But fortunately the only error I got was: ``` LogCook: Error: GameDefaultMap contains a redirected reference '/Game/MainLevel'. The intended asset will fail to load in a packaged build. Select the intended asset again in Project Settings to fix this issue. ``` And this error is saying exactly what I need to do: Just reselect the level in the project settings. This was caused because they have the wrong reference, which I think was because I changed the location of the level, but I am not sure. What is important is that I made the my first build π₯³ I also cleaned a specific widget from crafting station, because it has a LOT of logic inside, from the crafting to balcony, with a lot of event binding so I added some comments to remember this later. At end, I added the remaining recipes in the data table for be ready to use in the NPC dialog logic I will do tomorrow. ## Day 18 I finally made the NPC request system, although need to add something else, like the sprites and the complete quest system, I am more than happy to what I could do today. ## Day 19
In every programming language you will find some primitives values. It's not different in JavaScript, we have 7- number, string, boolean, bigint, null, undefined and symbol. Every one having a different case usage. In this article I gonna show you every Javascript primitive values and its usage case, but first what is a primitive value? ## Definition In Javascript, a data type is considered primitive value if immutable and is not an object and has no methods or properties. You can never change a primitive value in Javascript, for example: ```js let a = 'hello' let b = a console.log(b) // "hello" b.substring(2) // "llo" console.log(b) // "hello" console.log(a) // "hello" let c = [3] let d = c c.push(1) console.log(c) // Array [ 3, 1 ] console.log(d) // Array [ 3, 1 ] ``` In the example above, we can never change the primitive value string *hello* no matter how many methods we do in *b*, but is not the case of array *c* and *d*, when we push item to *d*, automatically pushes to *c* too, so is mutable. Primitives values are super important to know, independently of the language used, every one has a critical importance and usage case. For this reason, we're gonna explain each one that exists in JavaScript. ## type number In JavaScript, **Number** is a primitive data type used to represent numerical values. It stores 64-bit floating-point numbers following the IEEE 754 standard, which supports double-precision. This makes it versatile for handling integers as well as decimals, and it's the primary type for representing numbers in JavaScript. Number accepts almost all operators. An interesting fact is that JavaScript handles somethings that would be consider error in other programming languages, we will see more in the next topics. An important aspect of numbers in JavaScript is that when you perform an operation that is not possible, such attempting invalid operations between types (like multiplying strings), the result will be *NaN*, which stands for 'Not a Number' and indicates an error in the computation. ```js const n = 1 const n2 = 2 console.log(n + n2) // 3 console.log(n - n2) // -1 console.log(n * n2) // 2 console.log(n / n2) // 0.5 console.log(n % n2) // 1 console.log(n === n2) // false console.log('text' * 'text') // NaN ``` ## type string A **String** is a sequence of characters used to represent text in JavaScript. It allows for the storage and manipulation of textual data, making it essential for handling words, sentences, and other character-based information. Strings have a wide range of use cases due to their versatility in representing and manipulating text, making them one of the most frequently used data types in JavaScript. JavaScript has a particularly behavior- If you use the sum operator with some string there will be no error as would happen in other programming languages, instead JavaScript handle this by transforming the result in a string by concatenating the values. ```js const s = '1' const s2 = 'text' console.log(1 + s) // 11 console.log(false + s2) // falsetext ``` ## bigint **BigInt** is used to store integers beyond JavaScript's safe integer limit (Number.MAX_SAFE_INTEGER). While powerful for handling very large numbers, it is rarely needed in typical small-medium applications. ```js const maxSafeNumber = Number.MAX_SAFE_INTEGER // 9007199254740991 const beyondMaxN1 = maxSafeNumber + 1 // 9007199254740992 const beyondMaxN2 = maxSafeNumber + 2 // 9007199254740992 const bigN = 9007199254740991n const beyondMaxBigN = 9007199254740991n + 2n // 9007199254740993n ``` ## boolean A **Boolean** is a logical data type that can only hold one of two values: *true* or *false*. It is commonly used in conditions and decision-making statements. Those values can be represented in other forms like binary, 1 represents true and 0 represents false. This means that, in Javascript you can make operations with those values. ```js const isDeveloper = true const isLastType = false console.log(true + 2) // 3 console.log(true - 2) // -1 ``` ## undefined **undefined** is a default value automatically assigned to variables that have been declared but not yet given a value, or to function parameters that have no corresponding arguments. ```js let value const value2 = undefined ``` ## symbol A **Symbol** is a unique and immutable primitive value that can be used as a key for properties in an object, ensuring property names do not conflict. ```js const sym1 = Symbol() const sym2 = Symbol('desc') const sym3 = Symbol('desc') console.log(sym2 === sym3) // false ``` ## null A **null** value represents a reference that intentionally points to a nonexistent or invalid object or address, indicating the absence of any value. In operations, you can treat null as 0 since many calculations can be performed by substituting null with 0 without affecting the outcome. ```js const value = null console.log(2 + null) // 2 console.log(2 * null) // 2 ``` ## Conclusion In this article we saw all the primitive data types that exists in JavaScript. This article was short, but I hope you like it. Have a nice day. πβ¨β¨β¨π
Tailwind CSS is a popular modern CSS framework known for its utility-first approach. Unlike component-based frameworks like Bootstrap, Tailwind provides a collection of low-level utility classes that you combine to achieve your desired styles. This approach offers greater flexibility and granular control over your styles. It also boasts excellent build optimizations, including generating only the classes used in your project. While Tailwind offers built-in customization options, this article explores various methods for creating custom classes, with a focus on maintaining a positive developer experience. ## Tailwind CSS: Beyond the Basics - Crafting Custom Classes Tailwind CSS has a lot of classes, but there is some time you find yourself the need to use a class that are not in the default package, so what you do? There are a lot of ways to create custom classes, described in [docs](https://tailwindcss.com/docs/adding-custom-styles). I am gonna go though all of them, but be aware my favorite is the last one- By creating a plugin, so if you want, just skip to here ## Arbitrary values-properties-variants Tailwind is built for really hard customization, and thatβs so true, you can literaly create any class just combining some prefix or sufix and the compiler will generate for you the classes for build. So, Why we avoid this approach when dealing to customization? The answer is because this goes agains the reusability concept of Tailwind, and if you use more than one that that arbitrary value, I recommend to create a custom utility for it. <Img src="https://github.com/fescherer/fennec-tales-blog/assets/62115215/76b33e7a-bd3f-4940-aabd-e3232034fc7b" name="Arbitrary property Tailwind example" alt="code example of tailwind arbitrary using a color of background" /> <Img src="https://github.com/fescherer/fennec-tales-blog/assets/62115215/9994a2a8-a1df-4be8-813a-759d7ea15e64" name="Build file " alt="Build file showing the arbitrary property became other class" /> ## Customizing the theme If you want to change the properties already build in, like the font-size or color, itβs perfect and really easy to do just customizing the Tailwind theme file. <Img src="https://github.com/fescherer/fennec-tales-blog/assets/62115215/3fcdaf65-9624-4966-8ccd-49aa74daf8c1" name="tailwind.config.ts" alt="Creating a class my-blue in Tailwind file" /> <Img src="https://github.com/fescherer/fennec-tales-blog/assets/62115215/61559e23-be18-43ea-a0a5-04dc0fbca834" name="Code example" alt="Using the custom classes created in a div" /> <Img src="https://github.com/fescherer/fennec-tales-blog/assets/62115215/810e5dfc-70f1-4505-88df-bc1a25c8653d" name="Build file- This may looks weird, but is the same blue I configured, but just with some Tailwind optimization." alt="Build file showing that Tailwind handle better using theme" /> The problem is when you need to create a custom component class, like you repeat that same classes and that became redundant and hard to read. <Img src="https://github.com/fescherer/fennec-tales-blog/assets/62115215/5920d701-17f9-476e-8df4-c5c3024c4594" name="Example of reduntant classes" alt="File showing a lot of container with same classes" /> Isnβt better having just one class like `hello-world-container`? Yes. ## @layer and @apply directive There is a way to add custom classes using some Tailwind directives in CSS files. Like this: <Img src="https://github.com/fescherer/fennec-tales-blog/assets/62115215/53c6e419-42b5-472e-a321-1af95569e58b" name="Example of reduntant classes" alt="File showing a lot of container with same classes" /> You can literaly put any CSS valid property just like the way you would do in normal CSS, but noticed I put the classes inside some directives, why? Tailwind is composed by layers that can be overwritten, eg. You have a base style for your button but you want to change itβs color just in one component, to make that you will need to overwrite it, in normal CSS you will probably use !important, and trust me in my experience if you had use !important, probabily there is something wrong with your code. So use layers with wisdom. I recommend using: - [@layer base](https://tailwindcss.com/docs/adding-custom-styles#adding-base-styles) for tag styles, like I used in the example for tag `code`. - [@layer components](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes) for classes that has more than one style like the `hello-world-container` which has **display: flex;flex-direction:column;justify-content:center;background-color:purple;padding:1rem**. Most customization you will need is gonna be made here. - [@layer utilities](https://tailwindcss.com/docs/adding-custom-styles#adding-custom-utilities) is the highest priority, so use carefully. I recommend using just for one single property like **transparent:background-color:transparent**. This will mostly be used for classes that do not exist in default Tailwind. Well, so we learned about the @layer directive, but what is that @apply in the example? This is a way to use your Tailwind token in the styling file. For build does not matter if you use the **hello-world-container** or **hello-world-container-2**, both produces the same result, so @apply helps your code readbility. Using directives may seen the best way to create custom classes in Tailwind, but as I said in the beginning of the article, this is not my favorite way. I prefer much more creating a custom plugin. ## Why I do not like Tailwind directives like @layer and @apply Fist I need to say why I love the experience of working with Taiwlind insted other CSS framework similar like Bootstrap. Tailwind has been investing not just in optimizations and fast files, but in development experience too. Tailwind has a very nice VSCode plugin called [Tailwind Intellisense](https://tailwindcss.com/docs/editor-setup#intelli-sense-for-vs-code) I personally do not recommend you use Tailwind without this plugin. One thing it changes is when you hover a class, it shows what is that CSS equivalent, is literally required for me and I do not see me not using it. <Img src="https://github.com/fescherer/fennec-tales-blog/assets/62115215/9b5e1959-3c80-4589-867c-47b0fdf40e48" name="Tailwind VSCode Intellisense showcase" alt="Print showing when hovering the class has a popup showing CSS equivalent" /> You know, when you create custom classes, you need Tailwind Intellisense to recognize it, but I never found a way to make recognize directives like @layer. When creating custom classes like that, you do not have the hover preview :( This is unacceptable, specially because custom classes do not exist in Tailwind Docs, is the most important classes to have a register and a easy way to check the CSS equivalent. So going though Github Issues and making a lot of testing. I probably have the best solution to create custom classes in Tailwind. ## Create custom plugins As you can see there are a lot of ways to customize you styles in Tailwind, but there is superior way to customize and still get the Tailwind Intellisense working for us, itβs called πͺ½ Plugins πͺ½. What is and how we implement? <Blockquote cite="https://en.wikipedia.org/wiki/Plug-in_(computing)"> is a software component that adds a specific feature to an existing computer program </Blockquote> So in [Tailwind](https://tailwindcss.com/docs/plugins) is not diffent, plugins are a way to modify the base of Tailwind, you can do a lot of it, but in this article I just gonna show how you can use to create custom classes styles. Every Tailwind project has a **tailwind.config.js** or **tailwind.config.ts** which has all of Tailwindβs configuration, including the **theme** from earlier, but what we want is the **plugins** <Img src="https://github.com/fescherer/fennec-tales-blog/assets/62115215/27b51eae-d5a0-4c14-81cf-19c97aa76398" name="tailwind.config.ts" alt="tailwind.config.ts file" /> You will need to use a function from Tailwind called `plugin`. I highly recommend using Typescript, for its typing features, which helps a lot, but of course you can use Vanila Javascript too. <Img src="https://github.com/fescherer/fennec-tales-blog/assets/62115215/e42b46ba-2710-4575-81b4-6ad8e864e30f" name="Plugin function" alt="Plugin function" /> In itβs parameters you put what you want to create. For this article, we want a component, so we use the addComponent param. <Img src="https://github.com/fescherer/fennec-tales-blog/assets/62115215/bee18037-70d5-435f-810f-fa6876a62629" name="Plugin function add components" alt="Plugin function add components" /> Now just add the class name and the styles like CSS or (CSS Style Declation)[https://www.w3.org/TR/cssom/#the-cssstyledeclaration-interface] (backgroundColor insted background-color). <Img src="https://github.com/fescherer/fennec-tales-blog/assets/62115215/5a6db547-3f58-4eca-98ce-c5564885fbd6" name="Custom class made by plugin example" alt="Custom class made by plugin example" /> And there you go, now you have the hovering effec from Tailwind Intellisense :) <Img src="https://github.com/fescherer/fennec-tales-blog/assets/62115215/688da377-c59e-4cd2-ba24-b02499d4da25" name="Class in action" alt="Print showing when hovering the custom class made by plugin, showing the equivalent CSS" /> ## More dev experience Here's an additional tip to elevate your Tailwind development experience: consider using the [eslint-plugin-tailwindcss package](https://www.npmjs.com/package/eslint-plugin-tailwindcss). This ESLint plugin offers various features to help you write cleaner and more maintainable Tailwind code. ## Summary This article explored various methods for creating custom classes in Tailwind CSS, emphasizing the importance of a positive development experience. By leveraging Tailwind plugins, you can create custom styles while maintaining IntelliSense support and overall code clarity. This approach empowers you to extend Tailwind's capabilities while keeping your development workflow efficient and enjoyable. Thanks for reading see you in the next article, have a nice day π
VSCode is one of the most popular IDE's for coding everything from Java to Javascript. VSCode is super customizable and for that the community created a lot of custom extensions for VSCode. That's what we will cover in this article. ## Extensions.json This is a file you can add to `.vscode` folder and configure a set of extensions that are recommended by VSCode if the user don't have them. ## 1. VSCode Eslint <Img src="https://github.com/fescherer/blog/assets/62115215/be8bd7f5-b4f1-40c7-9974-290068604da3" name="VSCode Eslint" alt="Print showing VSCode Eslint extension" /> [As the doc says](https://eslint.org/docs/latest/use/getting-started): <Blockquote cite="https://eslint.org/docs/latest/use/getting-started"> Eslint is a tool for identifying and reporting on patterns found in ECMAScript/JavaScript code </Blockquote> This means that its goal is to be a tool to enforce certain patterns that we can consider "Good practices" or just avoid errors and bad syntaxes. This is possible by using a big set of rules that are completely customizable, and with just one line you can turn on or off the rules that you prefer and of course there are a lot of custom rules made by the community that in addition to add more validation to Javascript, also makes an integration to other stuff you may have like [React](https://www.npmjs.com/package/eslint-plugin-React), [TailwindCSS](https://www.npmjs.com/package/eslint-plugin-tailwindcss), [Typescript](https://typescript-eslint.io) and many more. So Eslint is very good but why do I need the extension? Well, you can run `npx eslint .` every time to check if Eslint got any error or warning, but if you want something more fast and easy to do, and trust me, you do, [installing the extension](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) allows Eslint to run parallel while coding, ensuring that the error will be shown as you code. **Link for download**: [VSCode Eslint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) ## 2. Git Lens <Img src="https://github.com/fescherer/blog/assets/62115215/5a918dda-768d-40ae-b1aa-7baf01632f11" name="Git Lens" alt="Print showing Git Lens extension" /> Have you ever code with a partner? A friend, co-worker or relative and found a line with a very serious bug and needed went to Github to found how made that line? Well, with Git Lens solves your problem by adding a little hint of who made the last update to that line and in how much time ago. I can tell you must be thinking. How this is so useful? For me, it can be handy to have, you may forgot the extension installed but at some time I can assure you will use. Not only for to know the person how did change the line, but also to know when was the last time that line was updated. I know Git Lens offers other uses, but for my necessities this is good enough. You can read more about it [here](https://www.gitkraken.com/gitlens). **Link for download**: [Git Lens](https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens) ## 3. Editor Config <Img src="https://github.com/fescherer/blog/assets/62115215/433781d2-df06-44ee-a87c-0399dd640e7e" name="Editor Config" alt="Print showing Editor Config extension" /> [Editor config](https://editorconfig.org) is a set of configurations that can maintain consistent coding across multiple IDEs, the configurations are basically about indentations, file formats, charset, unix style new lines, etc. This extensions enables to overwrite vscode configurations with the ones set on `.editorconfig` file. I always like to config this file instead my vscode because this way I know the code will be more consistent across multiple IDEs. **Link for download**: [Editor Config](https://marketplace.visualstudio.com/items?itemName=EditorConfig.EditorConfig) ## 4. Print showing Code Snapshot extension <Img src="https://github.com/fescherer/blog/assets/62115215/9fe14a53-fdf7-4849-b417-7ad969150b72" name="Code Snapshot" alt="Print showing Code Snapshot extension" /> I confess that Code Snapshot is not a super must have extension, but it is really nice and works really well. It can take screenshots from your code in MacOS style for you to post anywhere you want, like in a social media. **Link for download**: [Code Snapshot](https://marketplace.visualstudio.com/items?itemName=robertz.code-snapshot) ## 5. VSCode Icons <Img src="https://github.com/fescherer/blog/assets/62115215/f310c814-9f83-4752-a1a6-d21c8e9be2e2" name="VSCode Icons" alt="Print showing VSCode Icons extension" /> This one has a lot of my opinion, but I really think you should have an extension to modify your icons. It help so much when finding that specific file, I always go for the icon first instead of the file extension or the name of it. **Link for download**: [VSCode Icons](https://marketplace.visualstudio.com/items?itemName=vscode-icons-team.vscode-icons) ## 6. Import Cost <Img src="https://github.com/fescherer/blog/assets/62115215/80b88259-d63e-4233-b75d-32881818b14b" name="Import Cost" alt="Print showing Import Cost extension" /> One of the most important thing we as developer need to understand is how our site can be found by search engines like Google or Bing, and one thing I see a lot is a very large [First Contentful Paint(FCP)](https://web.dev/fcp). This is basically the time when the first render of page ends and this is [crucial for user experience](https://web.dev/fcp/#what-is-a-good-fcp-score), a bad time may result in user leaving your page. One of the strategies to decrease FCP time is the only first load the necessary and after you can load the small pieces like an api call. And this can be achieved by removing all the big bundles inside your project. The [Import Cost extension](https://marketplace.visualstudio.com/items?itemName=wix.vscode-import-cost) helps you to do that by showing the bundle size of that lib and then you can or remove it or search for a lighter lib. **Link for download**: [Import Cost](https://marketplace.visualstudio.com/items?itemName=wix.vscode-import-cost) ## 7. Markdown All in One and MDX <Img src="https://github.com/fescherer/blog/assets/62115215/53dd13aa-8bef-4898-9604-45bf2d875d79" name="MDX extension" alt="Print showing MDX extension" /> <Img src="https://github.com/fescherer/blog/assets/62115215/61d48f6d-a3bb-419a-bc6b-f0952582bd7d" name="A markdown file" alt="Print showing markdown file" /> In this topic I will put two extensions because I know not everyone is a React developer and uses [MDX](https://marketplace.visualstudio.com/items?itemName=unifiedjs.vscode-mdx) files as I use, so it is more reasonable for these people use [Markdown All in One](https://marketplace.visualstudio.com/items?itemName=yzhang.markdown-all-in-one) that gives all the essential stuff to have a great time writing your files. For React developers I suggest using [MDX](https://mdxjs.com), a superset of Markdown allowing to use React component inside Markdown. It is super handful and as I see, MDX is becoming a popular way to write Markdown in [NextJS](https://nextjs.org/docs/app/building-your-application/configuring/mdx). You can even use [Content layer](https://contentlayer.dev/docs/sources/files/mdx-d747e46d) to type your Markdown, if that's not fantastic I don't know what is. **Link for download**: [Markdown All in One](https://marketplace.visualstudio.com/items?itemName=yzhang.markdown-all-in-one) **Link for download**: [MDX](https://marketplace.visualstudio.com/items?itemName=unifiedjs.vscode-mdx) ## 8. Code Spell Checker <Img src="https://github.com/fescherer/blog/assets/62115215/fd817aa8-e864-4a55-98a9-8844c359cd9e" name="Code Spell Checker" alt="Print showing Code Spell Checker extension" /> Again, if you write files Markdown or anything that will probably be useful to have a grammar corrector and this extension is just like that. **Link for download**: [Code Spell Checker](https://marketplace.visualstudio.com/items?itemName=streetsidesoftware.code-spell-checker) ## 9. Auto Rename Tag <Img src="https://github.com/fescherer/blog/assets/62115215/10594378-40dd-419a-8ff6-9c38c2ec95e0" name="Auto Rename Tag" alt="Print showing Auto Rename Tag extension" /> One of the most useful extension I use is Auto Rename Tag, this as the name says, when change the tag name, the matching one will be changed too. **Link for download**: [Auto Rename Tag](https://marketplace.visualstudio.com/items?itemName=formulahendry.auto-rename-tag) ## 10. Themes <Img src="https://github.com/fescherer/blog/assets/62115215/effc88d1-7a25-40a3-b2b4-2c5f02c1d93f" name="Dracula Theme" alt="Print showing Dracula Theme" /> In number 10 I will put a more vast collection of extensions used to change the VSCode appearance, of course I am talking about [themes](https://vscodethemes.com/). There are a lot and I mean A LOT of available themes that can change the most different aspects, but most likely the colors, you can find a more lighter, darker, greener, yellowish and many more themes. If you want a recommendation, I would certain suggest to use [Dracula Theme](https://marketplace.visualstudio.com/items?itemName=dracula-theme.theme-dracula), it is one of most famous themes out there with more than 5 million installs. The colors are great even for dark environments or bright environments. **Link for download**: [Dracula Theme](https://marketplace.visualstudio.com/items?itemName=dracula-theme.theme-dracula) ## 11. Extra ones Well, for last I would suggest you to install the extension useful for what you are using to code, like if you are using [TailwindCSS](https://tailwindcss.com), please use the [TailwindCSS extension](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss), and this can be applied to everything in your project, [Styled Components](https://marketplace.visualstudio.com/items?itemName=styled-components.vscode-styled-components) or even other language than Javascript like [Python](https://www.python.org) VSCode has the extension [Python for VSCode](https://marketplace.visualstudio.com/items?itemName=ms-python.python). So have a look what you have installed and search if has an extension that could help you. π ## Conclusion Conclusion reading this article you were introduced to a set of VSCode extensions that can help improve your coding time and quality. All of that are free and can be downloaded from [Microsoft VSCode MarketPlace](https://marketplace.visualstudio.com). Thank you for reading and see you in the next article. Have a nice day. πβ¨β¨β¨π
Did you know what is morse code? It is a method of communication using short and long signals that are common represented as dots and dashes. Developed to transmit human language to math language and as only translate letters it became and international standard. ## Short Answer To create a morse code encrypter and decrypter you have two methods. One can use the [ASCII table](https://www.lookuptables.com/text/ascii-table) as described in this [answer of Stackoverflow](https://stackoverflow.com/a/26059284). But my preferred way is making a dictionary mapping all valid characters, which would be letters A from Z, number 0 to 9 and a blank space to separate words, anything else would be `undefined` and you can handle as you want. 1. Create a dictionary containing all valid characters: ```typescript const dictionary = { 'a': '.-', 'b': '-...', 'c': '-.-.', 'd': '-..', 'e': '.', 'f': '..-.', 'g': '--.', 'h': '....', 'i': '..', 'j': '.---', 'k': '-.-', 'l': '.-..', 'm': '--', 'n': '-.', 'o': '---', 'p': '.--.', 'q': '--.-', 'r': '.-.', 's': '...', 't': '-', 'u': '..-', 'v': '...-', 'w': '.--', 'x': '-..-', 'y': '-.--', 'z': '--..', '1': '.----', '2': '..---', '3': '...--', '4': '....-', '5': '.....', '6': '-....', '7': '--...', '8': '---..', '9': '----.', '0': '-----', ' ': '/' } ``` 2. Add function to transform letters into morse code: ```typescript function transformToMorse(rawText: string) { return rawText.split('').map(letter => dictionary[letter.toLowerCase()]).join(' ') } ``` --- ## Long Answer ### What is morse code? Morse code was created by [Samuel Morse](https://en.wikipedia.org/wiki/Samuel_Morse) and [Alfred Vail](https://en.wikipedia.org/wiki/Alfred_Vail). It is a method to encode human words characters into just short and long sequences that can be anything from lights, sounds, smoke, anything you can make a short or long signal can be used to represent any word or number from *a-z* and *0-9*. It became very popular in telegraph - grandfather of communication that used morse code to transmit messages from short and long bip. They even had a profession to people hearing and translating the messages- [telegraphist](https://en.wikipedia.org/wiki/Telegraphist). Morse code works by using a [sequence of short or long signal](https://en.wikipedia.org/wiki/Morse_code) to represent one letter/number. For example, the letter **t** can be represent by a long signal **-**, but the letter **g** is two long signal and one short signal **--.**. With this you can make a very good system of communication to transform human language into natural language. It's also very popular on pop culture as almost every riddle movie has a part using morse to encrypt or decrypt some kind of message. ### Alphabet |Character | Morse Equivalent| |----|----| | 0 | ----- | | 1 | .---- | | 2 | ..--- | | 3 | ...-- | | 4 | ....- | | 5 | ..... | | 6 | -.... | | 7 | --... | | 8 | ---.. | | 9 | ----. | | a | .- | | b | -... | | c | -.-. | | d | -.. | | e | . | | f | ..-. | | g | --. | | h | .... | | i | .. | | j | .--- | | k | -.- | | l | .-.. | | m | -- | | n | -. | | o | --- | | p | .--. | | q | --.- | | r | .-. | | s | ... | | t | - | | u | ..- | | v | ...- | | w | .-- | | x | -..- | | y | -.-- | | z | --.. | ### Google learn morse If you stay curious to learn more about morse code, I highly recommend you try [Google's morse learn](https://morse.withgoogle.com/learn). They use images to help us remember the letters. <Img src="https://github.com/fescherer/blog/assets/62115215/65e73bb9-c4a8-4316-9391-a001b0d77d2a" source="https://kpronline.com/blog/learn-morse-code-with-the-morse-typing-trainer" name="Code cues used in the Morse Typing Trainer" alt="List of all codes cues used in morse typing trainer" /> ### Javascript/Typescript functions To create a morse code encrypter and decrypter you have two methods. - Using ASCII table You can use the [ASCII table](https://www.lookuptables.com/text/ascii-table) as described in this [answer of Stackoverflow](https://stackoverflow.com/a/26059284). By knowing that ASCII table characters *a - z* goes from *97* to *122* and numbers *0 - 9* goes from *48* to *57* you can do something like this. 1. First create 2 arrays containing the characters and one constant for the space. ```typescript // Characters in order A - Z and 0 - 9 const charactersAZ = ['.-', '-...','-.-.','-..','.','..-.','--.','....','..','.---','-.-','.-..','--','-.','---','.--.','--.-','.-.','...','-','..-','...-','.--','-..-','-.--','--..'] const numbers09 = ['-----', '.----', '..---', '...--', '....-', '.....', '-....', '--...', '---..', '----.'] const characterSpace = '/' ``` 2. Now you need to remove all weird characters that does not have an index on our arrays ```typescript function cleanRawText(rawText: string) { return rawText.toLowerCase().replace(/[^a-z0-9\s]/g, ""); } ``` 3. Finally make a function to return the encrypted character. ```typescript const isNumberRegex = /^\d+$/ function transformToMorse(rawText: string) { const formatText = cleanRawText(rawText); return formatText.split('').map(letter => { if(letter === " ") return characterSpace; asciiCode = letter.charCodeAt(letter); if(isNumberRegex.test(letter)) return numbers09[asciiCode - 48]; else return charactersAZ[asciiCode - 97]; }).join(' '); } ``` 4. Test the functions ```typescript transformToMorse('Hello world') // result .... . .-.. .-.. --- / .-- --- .-. .-.. -.. ``` -Using dictionary My preferred way is making a dictionary mapping all valid characters, which would be letters A from Z, number 0 to 9 and a blank space to separate words, anything else would be `undefined` and you can handle as you want. 1. Create a dictionary containing all valid characters: ```typescript const dictionary = { 'a': '.-', 'b': '-...', 'c': '-.-.', 'd': '-..', 'e': '.', 'f': '..-.', 'g': '--.', 'h': '....', 'i': '..', 'j': '.---', 'k': '-.-', 'l': '.-..', 'm': '--', 'n': '-.', 'o': '---', 'p': '.--.', 'q': '--.-', 'r': '.-.', 's': '...', 't': '-', 'u': '..-', 'v': '...-', 'w': '.--', 'x': '-..-', 'y': '-.--', 'z': '--..', '0': '-----', '1': '.----', '2': '..---', '3': '...--', '4': '....-', '5': '.....', '6': '-....', '7': '--...', '8': '---..', '9': '----.', ' ': '/' } ``` 2. Add function to transform letters into morse code: ```typescript function transformToMorse(rawText: string) { return rawText.split('').map(letter => dictionary[letter.toLowerCase()]).join(' ') } ``` 3. Test it ```typescript transformToMorse('Hello world') // result .... . .-.. .-.. --- / .-- --- .-. .-.. -.. ``` Which one of them should I choose? Well, I think is up to you, personally I will not go to performance side because both has `map` method and the others doesn't make that much difference. I would choose based on readability as the first one the arrays seems a bit confusing and object making a dictionary seems much more readable for me. Okay, but this is only an encrypter, where is the decrypter? For this, just invert the logic, make a dictionary containing morse code as keys, letters with values and the backslash for white space. ### Bonus: UI - NextJS - Tailwind This article only shows how to convert, but not how to create an application with these logics. I'am gonna leave this for you to create a design. I personally recommend to create a [NextJS](https://nextjs.org/docs/getting-started/installation) project with [TailwindCSS](https://tailwindcss.com/docs/installation) for styling and [Vercel](https://vercel.com) as free host. [You can checkout the source code to get inspired](https://github.com/fescherer/morse-secret-scripter) <Img src="https://github.com/fescherer/blog/assets/62115215/e3c04fd3-5140-484a-8063-541abe46f66f" source="https://morse-secret-scripter.felipescherer.com" name="Morse secret scripter home page" alt="Home page of morse secret scripter containing input and output text areas for encrypt and decrypt" /> ### Conclusion In this article you learnt the history of morse code and how to create simple functions to translate/encrypt/decrypt morse code using Javascript/Typescript. Feel free to leave any suggestions, corrections and tips. Thank you for reading and see you in the next article. Have a nice day. πβ¨β¨β¨π
In my opinion one of the most difficult part in frontend is to make the layout look similar in every browser and that's because we have CSS Resets, but scrollbars can be one of the points we forget but can makes a huge difference to users. ## Introduction At my first year as developer, I always will remember a gaffe committed when introducing some new features I had developed to the CEO's company. Well, I use Mozilla Developer Edition to code, but at that time I forgot users uses Chrome in majority. <Img src="https://github.com/fescherer/blog/assets/62115215/8be10ac9-bb0f-47d5-93b7-bed3144ad9fe" source="https://gs.statcounter.com" name="Browser Market Share Worldwide Dec 2022 - Jan 2024" alt="Statcounter graph showing Chrome are used by 70% of the users" /> So the CEO. Yes, the scrollbar's feature was horrible in Chrome because the scrollbars are not aesthetic as Firefox. In the end, I did not got any in any trouble, the CEO only just pointed out and I did the fix in the next patch. I learnt to never forget scrollbars again, it cannot be neglected. ## Problems The main issue with scrollbars it is different styles in each browser which makes really hard to us track how the final layout is gonna be delivered to the user. And is not only the visuals, some browsers make the scrollbars take a size of the container, which can lead to layout looking weird. We gonna follow the name convention in [this fantastic article by Zach Leatherman - Two Browsers Walked Into a Scrollbar](https://www.filamentgroup.com/lab/scrollbars/): <Blockquote cite="https://www.filamentgroup.com/lab/scrollbars"> - Obtrusive scrollbars: scrollbars that take up screen real estate. These do not overlay on top of the content, but appear next to it. - Unobtrusive scrollbars: scrollbars that sit on top of the content. These don't subtract screen real estate away from the container they belong to. </Blockquote> I personally would prefer that every browser had unobtrusive scrollbars most because we as developer would not need to worry about taking some space in our layout. But I know that's not possible and probably would ruin a lot of layouts out there. Don't get me wrong, I am not saying some browser is better than other, I really think that every browser has some kind of problem when talking about scrollbars. Let's point out: ### Mozilla Firefox Firefox has unobtrusive and really aesthetic scrollbar which is great but I don't like how only "grows" the scrollbar on hover, not the container, but the scrollbar. I think if it has to grow, that should grow on hover the content to be easy accessed and not the scrollbar. Like if I already have reached the scrollbar, it does not need to grow, right? The grow is to make the bar more accessible... <Img src="https://github.com/fescherer/blog/assets/62115215/ad1747c2-699f-492f-9f07-be5b8f2f97aa" name="Mozilla Firefox default scrollbar" alt="Gif showing that Firefox only grows the scrollbar if hovered IN the scrollbar" /> ### Chromium Browsers I love chromium browsers specially Chrome and Opera GX, I use them both in different situations, like coding I like Chrome and Mozilla Firefox Developer, but for daily routines I prefer Opera GX. But most of the scrollbars are not that great. First, they are obtrusive which for sole already brings some problems. Second, it is so wider even if user is not hovering the content, which is really ugly... <Img src="https://github.com/fescherer/blog/assets/62115215/d0c6360e-0546-4999-a296-8c11c2f62947" name="Chrome default scrollbar" alt="Gif showing the default behavior scrollbar in Mozilla Firefox" /> ### Edge One scrollbar I really like is from Edge Browser, yes, the successor of Internet Explorer. I think Edge has a lot of similar stuff with Chrome and the scrollbar is no different. Edge still has a wide scrollbar, but the borders are rounded which gives like a Firefox feeling. I really like, but I think could be better if were a little thinner. <Img src="https://github.com/fescherer/blog/assets/62115215/64badb1a-3fb8-4174-8c73-ba4222560296" name="Edge default scrollbar" alt="Gif showing the default behavior scrollbar in Edge" /> ## Mobile In mobile we have a little different problem. In my opinion mobile, the scrollbar can have the same problems above depending on which mobile browser you are, but with additional that scrollbars are only shown if you are actually scrolling, how I am supposed to know if that container has scroll if does not have a scrollbar? ## Objectives In this article I gonna show you how you can make your scrollbars aesthetic as can be in many different browsers, only using CSS. I am already warning you that is not possible to make like a reset to them, like we have done to **h1**, **p** and so on. CSS has its limitations and to achieve the fully goal probably will you need some Javascript code or maybe create all the logic with it. The method I am gonna show you is relative simple and can works on most browsers like Mozilla Firefox, Google Chrome, Brave, Opera GX, Edge and Safari, there will be an image with properties browser support in the end of the article. I think that's enough of talk, let's code. ## Solution First, please, do not ever style the main scrollbar. This is not standard, this do not need to follow your layout, it's good to look native to users do get lost. I think is good to style your scrollbars inside something you created, because this is "part" of your layout, different from the main scrollbar that is "part" of browser's layout. Did get it? Well, for the solution, I searched a lot and got really frustrated when finding out the perfect solution do not exist, but we can came to a really nice result. Most of the sites uses some kind of Javascript or very difficult CSS approaches, so I filtered out the really necessary styles and merge them in a class **styled-scrollbars** you can use in the overflowing container. Most of these styles were gotten from [Reddit's aside menu](https://www.reddit.com). ## CSS code There is no error, we gonna use two properties for Firefox (**scrollbar-color** and **scrollbar-width**) and some webkit for Chromium's browsers. ``` css /* Firefox Only */ /* Approaches mobile first, because here, the scrollbar-color will be set and always will be that color, but on desktop, that we have hover effect, we can give a transparent color, but how this is mobile first, we give the color here, and treat the desktop in other style */ .styled-scrollbars { scrollbar-color: var(--some-really-nice-color-here); scrollbar-width: thin; } /* Chrome and others Browsers */ .styled-scrollbars::-webkit-scrollbar { width: 4px; height: 4px } /* Chrome and others Browsers */ .styled-scrollbars::-webkit-scrollbar-track { background: transparent; } /* With device has hover (Desktop or TV, etc) give a little effect on hover */ @media (hover: hover) { .styled-scrollbars { scrollbar-color: var(--some-really-nice-color-here-not-hover) transparent ; scrollbar-width: thin; border-radius: 8px; transition: cubic-bezier(0.165, 0.84, 0.44, 1) 500ms all ; } .styled-scrollbars:hover { scrollbar-color: var(--some-really-nice-color-here) transparent } .styled-scrollbars:hover::-webkit-scrollbar-thumb { background: var(--some-really-nice-color-here); border-radius: 8px; -ms-overflow-style: none; scrollbar-width: thin; } } ``` To find the browser's support, please look at: - [webkit-scrollbar](https://developer.mozilla.org/en-US/docs/Web/CSS/::-webkit-scrollbar) - [scrollbar-color](https://developer.mozilla.org/en-US/docs/Web/CSS/scrollbar-color) - [scrollbar-width](https://developer.mozilla.org/en-US/docs/Web/CSS/scrollbar-width) ## Final results <Img src="https://github.com/fescherer/blog/assets/62115215/4af5a2b1-b06a-444d-a135-3b5f85472089" name="Mozilla Firefox" alt="Gif showing the result of aesthetic scrollbar in Firefox" /> <Img src="https://github.com/fescherer/blog/assets/62115215/24d9ca86-69a8-4335-9dea-b68f0278701d" name="Chrome" alt="Gif showing the result of aesthetic scrollbar in Chrome" /> <Img src="https://github.com/fescherer/blog/assets/62115215/9e114c2b-06ca-459b-aa95-aecc4b02e57d" name="Edge" alt="Gif showing the result of aesthetic scrollbar in Edge" /> ## Additional tips One very interesting stuff I found by searching content for this article is a solution for components that has a special css for hovering effects, is to use **media query hover** to only enable this on hover support devices. You can checkout the [full video by the master of CSS Kevin Powell](https://www.youtube.com/watch?v=uuluAyw9AI0). I highly recommend. ## Conclusion In this article we saw a very clean way, consistent and CSS only to style your scrollbars to be aesthetic as possible between many different browsers like Mozilla Firefox, Google Chrome, Brave, Opera GX, Edge and Safari. Of course this approach is not 100% effective in all platforms, but works on most of them. Have a nice day. πβ¨β¨β¨π
In this article I will talk about some strategies you can use to create themes on Tailwind, a way to write CSS in utility classes, like if you want to say `display: flex`, you can just give a *classname* of `flex`. Of course this is a very simple example, but you can do so much more and everything is customizable. [Tailwind CSS](https://tailwindcss.com) as the docs says: <Blockquote cite="https://eslint.org/docs/latest/use/getting-started"> A utility-first CSS framework packed with classes like `flex`, `pt-4`, `text-center` and `rotate-90` that can be composed to build any design, directly in your markup. </Blockquote> ### What is Tailwind CSS To understand Tailwind CSS is good to know how it was created. Well, the framework was created by [Adam Wathan](https://twitter.com/adamwathan) because he wanted to use a [Bootstrap](https://getbootstrap.com) kinda syntax with [Less processor](https://lesscss.org), as you may know Bootstrap has build in classes for `buttons`, `forms`, etc. Tailwind in other hand has just utility classes, like `flex`, `backgrounds`, `paddings`, `margins`, etc. That's why in the first versions of Tailwind, it has some of the classes like Bootstrap, but from time on Adam switched to more generic classes. He also needed to change the processor to PostCSS, but you can find the whole history at [OfferZen Origins's channel](https://www.youtube.com/watch?v=1x7HlvSfW6s). For me, frameworks that's uses utility classes are the best ones, it can be strange at first but the productivity grows so much using this strategy that I cannot stop using. ### Popularity For popularity Tailwind is one of the favorite CSS Framework out there. You can see this in [State of CSS Survey](https://2023.stateofcss.com/en-US/css-frameworks): <Img src="https://github.com/fescherer/blog/assets/62115215/23055cb4-026e-4f25-b287-e3556251faf3" name="State of CSS" source="https://2023.stateofcss.com/en-US/css-frameworks" alt="Survey from State of CSS showing the is one of most used technologies for styling" /> They have a lot of interesting data. I suggest you to take look and see for yourself. ### How to use As said before, Tailwind uses utility classes, so you will add them into your html as so: ```html {1-2} <div className="m-4 flex items-center justify-center"> <span>Item 1</span> <span>Item 2</span> </div> ``` When I started coding, the company used [Angular](https://angular.io) with kinda of utility classes. We a had a big style sheet containing all the most used classes like `padding` or `margin` and them, if we would need some more specific style, we create a class just for it. I grow up and for my experience, don't do that, try to stay just with utility classes, it way more easy to maintain. You can find the list of utility classes available [in the docs](https://tailwindcss.com/docs/utility-first). ## What is theme Theme is a set of characteristics that build an idea. I can be applied to an environment to give the feeling of that characteristics. So you can made a party using a Halloween theme, so everyone and the party probably will have characteristics from Halloween. In design is the same thing, we can made certain [rules that build in a theme](https://www.uxpin.com/studio/blog/design-system-theming), like font sizes, spaces, images and colors. This can be used for accessibility advantages or just to made a design some unique like a special version for christmas. It became very popular for all designs to have at least a version called `dark` with more darker colors and a `light` with, of course, lighter colors. So it is very important to know how you can handle this. ## Strategies for theming in Tailwind In this tutorial, I will show only themes that change colors, that's because I think changing the font-size or spaces can be tricky and not so great in the final product. Tailwind already has a [theme build in support](https://tailwindcss.com/docs/dark-mode) but I think is not ideal mostly if you want to add more than just dark and light theme. So that's why I gonna show you some strategies you can use to add more themes for Tailwind CSS. ### Using external libraries There a lot of libs created by the community to solve the problem of multiple themes in Tailwind. Some examples would be: - [Tailwind CSS Theme Variants](https://github.com/JNavith/tailwindcss-theme-variants); - [Tailwind Themer](https://github.com/RyanClementsHax/tailwindcss-themer); - [TW Colors](https://github.com/L-Blondy/tw-colors); - Many more... All have a very similar concept and way to solve this by enable to create themes on `Tailwind.config.ts`, as an example of configuring three themes dark, light and forest using [TW Colors](https://github.com/L-Blondy/tw-colors#add-more-themes): ```typescript const { createThemes } = require('tw-colors'); module.exports = { content: ['./src/**/*.{html,js,jsx,md,mdx,svelte,ts,tsx,vue}'], plugins: [ createThemes({ light: { 'primary': 'steelblue', 'secondary': 'darkblue', 'brand': '#F3F3F3', }, dark: { 'primary': 'turquoise', 'secondary': 'tomato', 'brand': '#4A4A4A', }, forest: { 'primary': '#2A9D8F', 'secondary': '#E9C46A', 'brand': '#264653', }, }) ], }; ``` It is a great improve on my point of view and you can certainly use it, but for me I would like to make my own code in my own way. ### CSS Variables TW Colors uses CSS variables to make the themes, so I thought I could use too as I already made a theme logic using them when working with Angular 2 years ago. Then I started thinking what are my needs and how can I solve them. The first thing was that I want as many themes as possible- So I would need to provide to every theme into different files as I wanted a lot of them, staying at the same file would not be very scalable. Second thing I wanted is to control the theme based on a html attribute `data-theme` as: ```html <html data-theme="dark"></html> ``` With this in mind, I thought on a simple solution: Use CSS variables, and change the value of them using Javascript. To start this I overwrite [Tailwind default colors](https://tailwindcss.com/docs/customizing-colors) with my colors in `Tailwind.config.ts` using CSS variables syntax as: ```typescript import type { Config } from 'tailwindcss' import defaultTheme from 'tailwindcss/defaultTheme' const config: Config = { content: [ './src/**/**/*.{js,ts,jsx,tsx,mdx}', ], theme: { colors: { 'primary': 'var(--primary)', 'secondary': 'var(--secondary)', 'text': 'var(--text)', 'title': 'var(--title)', 'foreground': 'var(--foreground)', 'background': 'var(--background)', 'background-card': 'var(--background-card)', 'text-on-primary': 'var(--text-on-primary)', 'text-hover': 'var(--text-hover)', 'primary-hover': 'var(--primary-hover)', 'code-header': 'var(--code-header)', 'transparent': 'transparent', 'current': 'currentColor', }, } } export default config ``` A little observation about two colors `transparent` and `current` that are been used as constants and not variables values, because I do not want to change them when the theme is changed. Continuing after colors configuration, I need to declare these variables and default values for them into some place so I went to `global.css` where [Tailwind is init](https://tailwindcss.com/docs/installation/using-postcss) and then declare the variables into a root [pseudo-class](https://developer.mozilla.org/en-US/docs/Web/CSS/:root): ```css @tailwind base; @tailwind components; @tailwind utilities; :root { --primary: #51C28A; --secondary: #74DFAA; --text: #DDDDDD; --title: #EAEAEA; --foreground: #282929; --background: #131313; --background-card: #ffffff0a; --text-on-primary: #FFFFFF; --text-hover: #888888; --primary-hover: #409c6e; --code-header: #21222C; } ``` If you want, you can add a accessible [medias prefers color scheme](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme). That almost it, now you just need to add in some css file the values for each theme like so: ```css :root[data-theme='dark'] { --primary: #51C28A; --secondary: #74DFAA; --text: #DDDDDD; --title: #EAEAEA; --foreground: #282929; --background: #131313; --background-card: #ffffff0a; --text-on-primary: #FFFFFF; --text-hover: #888888; --primary-hover: #409c6e; --code-header: #21222C; } ``` To make a better organization I like to make one file for each theme and then export all in one file. <Img src="https://github.com/fescherer/blog/assets/62115215/672b8aae-169a-4028-ace2-0f5482005252" name="Folder structure" alt="Folder structure" /> ```css /* themes/variants/dark.css */ :root[data-theme='dark'] { --primary: #51C28A; --secondary: #74DFAA; --text: #DDDDDD; --title: #EAEAEA; --foreground: #282929; --background: #131313; --background-card: #131313; --text-on-primary: #FFFFFF; --text-hover: #888888; --primary-hover: #409c6e; --code-header: #21222C; } ``` ```css /* themes/variants/light.css */ :root[data-theme='light'] { --primary: #359967; --secondary: #4d9470; --text: #090a0a; --title: #191b1a; --foreground: #dfdfdf; --background: #ffffff; --background-card: #dfdfdf; --text-on-primary: #FFFFFF; --text-hover: #888888; --primary-hover: #409c6e; --code-header: #21222C; } ``` ```css /* themes/index.css */ @import './variants/dark.css'; @import './variants/light.css'; ``` This way I can import the `index.css` file with all the imports already set up. The last part, we need to make the logic to change themes. To do this first you need to map all your themes into an array. ```typescript /* themes/themes.theme.ts */ export const themes = [ 'dark', 'light', ] ``` Lastly just change the value of the `html` attribute `data-theme` with Javascript and voilΓ . ```javascript document.getElementsByTagName('html')[0].setAttribute('data-theme', 'light') ``` ## SSR Themes preference If you analyze my blog you would probably noticed that theme preference is storage somewhere and then loaded before even the page. How that's possible? The key word is [`cookies`](https://web.dev/understanding-cookies). These type of data is storage and can be access by the server, not only the browser like [`local storage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage), using this, we can load the theme in server and then bring to browser the storage one as default. There are many ways to manage cookies and I think that every framework has its own way to do that. In this tutorial I will teach you with [NextJS 13 using app router](https://nextjs.org) because it is what I use and have experience. First we need to install 1 dependency called [cookies-next](https://www.npmjs.com/package/cookies-next) because with the [new API of app router, we can only use cookies at server components](https://nextjs.org/docs/app/api-reference/functions/cookies) but this solves just one part of the equation the load can be at server components, but every time we use interaction, the component need to be client, so how am I supposed to save a cookie of user theme choice? That's why we use `cookies-next`, to manipulate cookies at client components. ```bash npm install cookies-next ``` Let's start in the root `layout.tsx`. ```jsx export default function RootLayout({ children, }: { children: React.ReactNode }) { const cookieTheme = cookies().get('data-theme') const theme = themes.includes(cookieTheme?.value ?? '') ? cookieTheme?.value : 'dark' return ( <html lang="en" data-theme={theme}> <body> <main> {children} </main> </body> </html> ) } ``` Using the code above you can load the cookie `data-theme` and then pass to `html`. Now to change this just use the code Javascript that I said to you earlier. ```jsx // Here you can storage these values together with the themes like I showed you earlier. const themes = [ 'dark', 'light', ] function handleTheme(theme: string) { document.getElementsByTagName('html')[0].setAttribute('data-theme', theme) } export function ThemeSelector() { return ( <div> { themes.map((theme) => ( <button key={theme} onClick={() => handleTheme(theme)}> {theme} </button> )) } </div> ) } ``` ## User preferences - prefers-color-scheme, prefers-reduced-motion, etc One last thing you can do to make your code even better is to add a default theme based on user preference, it is very common to add a option on theme selector saying `default` or `system`, this is a theme based on user's system theme. So that's really import to make our interface looks like the user's on first time accessing our site. To do that is very simple, just add a [CSS query prefers-color-scheme](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme). ```css /* themes/variants/system.css */ @media (prefers-color-scheme: dark) { :root { --primary: #51C28A; --secondary: #74DFAA; --text: #DDDDDD; --title: #EAEAEA; --foreground: #282929; --background: #131313; --background-card: #282929; --text-on-primary: #FFFFFF; --text-hover: #888888; --primary-hover: #409c6e; --code-header: #21222C; } } @media (prefers-color-scheme: light) { :root { --primary: #359967; --secondary: #4d9470; --text: #090a0a; --title: #191b1a; --foreground: #dfdfdf; --background: #ffffff; --background-card: #dfdfdf; --text-on-primary: #FFFFFF; --text-hover: #888888; --primary-hover: #409c6e; --code-header: #f3e9de; } } ``` ## Conclusion In this article I told you a little about the history behind Tailwind CSS creation, with the explain about what are themes and how people use them with Tailwind. For last how I would implement themes by myself using Tailwind and NextJS SSR feature to load theme in the server rendering time. Thank you for your time. Have a nice day. πβ¨β¨β¨π
This blog post is my journey into Game Off 2023 by Github and Lee Reilly, started on November 01 and ended December 01. It's my very first big game project, I am so exited! I will update this page every day, telling what I did and the expectations to the next day. ## Day 01 I was so excited to participate. I remember only making one game in Javascript very simple, three or four years ago, I think using [P5](https://p5js.org), It was a runner game, like the [Google's Chrome Dinosaur Game](https://en.wikipedia.org/wiki/Dinosaur_Game), very nice, but I think something more complex. This day was the great theme reveal, which was *Scale*. It was very nice, very embracing. The first moment I thought in making something like [Cuphead](https://store.steampowered.com/app/268910/Cuphead), a boss battle rush themed as [pirate](https://en.wikipedia.org/wiki/Golden_Age_of_Piracy) or other theme related to sea that I can fit [scales](https://en.wikipedia.org/wiki/Scale_(anatomy)). <Img src="https://github.com/fescherer/blog/assets/62115215/dedf7b77-3049-44d9-a724-47bcbea0eddc" name="Game's off 2023 theme" alt="Print showing Game's Off Site Scale theme description" /> Thinking more about, a boss battle rush would be very difficult for my first game because I need to create a lot of mechanics, and in one month maybe it's not possible. So I decided to go to [scale, an increasing stuff](https://en.wikipedia.org/wiki/Scale_(ratio)). I thought in games like [Vampire Survivors](https://store.steampowered.com/app/1794680/?snr=1_5_9__205), a bullet hell game. Maybe I cannot do a lot of enemies variations, but I think I will go if this. ## Day 02 This day I was searching for a game engine. As I only code one game, I do not have an expertise of how game engine is better for my choices, so I take a time to study some of the engines recommended in the Game Off 2023 page. I started in [Unity](https://unity.com), It's a very good engine to design first games because it's simple and intuitive. I also look at [Unreal](https://www.unrealengine.com/en-US), with a very good lighting system and incredible stuff to handle 3D games it's probably the best engine out there for someone trying a next gen game, but for me that is my first game, and will probably be a 2D game, Unreal would be kill a mosquito with a bazooka, and for this, Unreal will not be my choice. The last engine I tried was [Godot](https://godotengine.org), a very good engine open source that you can create 2D and 3D games, but I think the greater part is for 2D games. Godot has a scripting language called [GDScript](https://godotengine.org/features/#script) with a very simple syntax inspired mostly by [Python](https://www.python.org), a programming language that I used a lot on my side projects. In the end I chose Godot, mostly because GDScript is so similar to Python that I had the better experience of the three ones. Godot is also intuitive, and I think my challenge in the next days will learn everything about Godot and create the game. I had a little extra time in this day, and started to learn about Godot using the Godot's fundamental's playlist in the [Game Dev Artisan channel](https://www.youtube.com/watch?v=6YNKi3hcS5U&list=PLrnWJKR7bTuw7L0g70GzCM-cUr9XmtnAp). ## Day 03 This was a not good day. I mostly study how to use Godot, I learned to create collision and pickup items, but I tried to refactor my files and I delete my Player with all the animations and logic already finished π₯²π₯²π₯². I stopped and I hope tomorrow will be a better day. Well at late night I thought better and decided to start a new project, this time, with a better preparation, and clarification of the ideas. I started searching in the assets I downloaded at [Ichio.io](https://itch.io/game-assets). I found a very good collection with free license [Ninja Adventure](https://pixel-boy.itch.io/ninja-adventure-asset-pack), it has a full set of incredible pixel art with event dialog faces that I can use in the buy menu. The next step was think of what type of game I wanted and maybe, a tower defense it's really nice and I can reuse some code to other enemies. Other point is the map, I just need to create one for this first time. To get started I search in the assets pack and decided the towers. Of course I can have a lot of towers, but the game is scope is to be small and fishable until December, so I cut and decided to create only 6 towers, each one being unique and having a upgraded version. Name|Damage|Sprite normal|Sprite special|Weapon :---:|:---:|:---:|:---:|:---: Knight|area, close|![Faceset](https://github.com/fescherer/blog/assets/62115215/7929c487-bd2a-43a9-b56f-57b4d0c5ba7a)|![Faceset](https://github.com/fescherer/blog/assets/62115215/3f10977b-951d-4cf8-b21e-7e522974f259)|![Sprite](https://github.com/fescherer/blog/assets/62115215/c56c5402-06c2-49ef-b135-2429776cb67b) Mage|single, far|![Faceset](https://github.com/fescherer/blog/assets/62115215/9bf88738-5ba0-4980-bd87-b784440dba08)|![Faceset](https://github.com/fescherer/blog/assets/62115215/5527d227-bc4d-498e-b4b7-a720f4488446)|![WandSprite](https://github.com/fescherer/blog/assets/62115215/eeeebf18-ab52-41cd-9b8a-cf617117ebae) Support|None (gain coins and life)|![Faceset](https://github.com/fescherer/blog/assets/62115215/0e806c05-b600-4ed5-ab53-ad97b478ae76)|![Faceset](https://github.com/fescherer/blog/assets/62115215/a9d87a88-ba1f-4a4e-a34b-61c0da86ec8d)|![Sprite](https://github.com/fescherer/blog/assets/62115215/776ec828-5957-4ee0-a730-d70fc6e773ab) Ice man|area, far (Slow the enemies)|![Faceset](https://github.com/fescherer/blog/assets/62115215/b808799b-bb5e-4896-844f-5a2d8ab7f0af)|![Faceset](https://github.com/fescherer/blog/assets/62115215/4726bb8c-a508-4446-bd24-f9fb6b3248de)| Ice ball Defense|None (Stops the enemies with barrier)|![Faceset](https://github.com/fescherer/blog/assets/62115215/cc70df41-39a1-4d46-a8d0-c576e2396979)|![Faceset](https://github.com/fescherer/blog/assets/62115215/3856589f-75b4-4b9c-bb36-bf19f70d4556)|Stone Rock barrier Summon|single, close|![Faceset](https://github.com/fescherer/blog/assets/62115215/401ff9cf-754e-43bb-a808-8a712c25dc7f)|![Faceset](https://github.com/fescherer/blog/assets/62115215/297d92c6-2104-4135-a555-24533c0776da)|![FacesetWhite](https://github.com/fescherer/blog/assets/62115215/2b243899-e2c5-4eab-b42a-21b72d3d0a92)![FacesetBlack](https://github.com/fescherer/blog/assets/62115215/ba89c060-f664-4ed3-aad1-647f8810faf0) And when thinking of enemies, I would like to show the first boss early because, the objective is to create a simple game, so I decided to separate into 20 levels and each level will have a new enemy. Maybe I will change this in the future and just have 4 levels with a longer duration each one with a boss in the end. I don't know, and we will see what I end doing. For now, the level are these: Level|Slot|Slot|Slot|Slot|Slot|Boss :---:|:---:|:---:|:---:|:---:|:---:|:---: 1|![Slime3Faceset](https://github.com/fescherer/blog/assets/62115215/18a450db-6c3a-4b2c-958b-e0a8429ca9f1)|![ButterflyBlueFaceset](https://github.com/fescherer/blog/assets/62115215/fbc4006c-ea58-45cd-9565-258d8e5b4584)|x|x|x|x 2|![Slime3Faceset](https://github.com/fescherer/blog/assets/62115215/18a450db-6c3a-4b2c-958b-e0a8429ca9f1)|![ButterflyBlueFaceset](https://github.com/fescherer/blog/assets/62115215/fbc4006c-ea58-45cd-9565-258d8e5b4584)|![Larva2Faceset](https://github.com/fescherer/blog/assets/62115215/2bb14fee-d9ef-4385-b845-4026d44b72ea)|x|x|x 3|![Slime3Faceset](https://github.com/fescherer/blog/assets/62115215/18a450db-6c3a-4b2c-958b-e0a8429ca9f1)|![ButterflyBlueFaceset](https://github.com/fescherer/blog/assets/62115215/fbc4006c-ea58-45cd-9565-258d8e5b4584)|![Larva2Faceset](https://github.com/fescherer/blog/assets/62115215/2bb14fee-d9ef-4385-b845-4026d44b72ea)|![RacoonFaceset](https://github.com/fescherer/blog/assets/62115215/d4200329-8d64-4b8c-be85-6ae0eb744eb8)|x|x 4|![Slime3Faceset](https://github.com/fescherer/blog/assets/62115215/18a450db-6c3a-4b2c-958b-e0a8429ca9f1)|![ButterflyBlueFaceset](https://github.com/fescherer/blog/assets/62115215/fbc4006c-ea58-45cd-9565-258d8e5b4584)|![Larva2Faceset](https://github.com/fescherer/blog/assets/62115215/2bb14fee-d9ef-4385-b845-4026d44b72ea)|![RacoonFaceset](https://github.com/fescherer/blog/assets/62115215/d4200329-8d64-4b8c-be85-6ae0eb744eb8)|![GoldRacoonFaceset](https://github.com/fescherer/blog/assets/62115215/5ac3f14f-c635-44b3-93fe-4cc4b1f22842)|x 5|![Slime3Faceset](https://github.com/fescherer/blog/assets/62115215/18a450db-6c3a-4b2c-958b-e0a8429ca9f1)|![ButterflyBlueFaceset](https://github.com/fescherer/blog/assets/62115215/fbc4006c-ea58-45cd-9565-258d8e5b4584)|![Larva2Faceset](https://github.com/fescherer/blog/assets/62115215/2bb14fee-d9ef-4385-b845-4026d44b72ea)|![RacoonFaceset](https://github.com/fescherer/blog/assets/62115215/d4200329-8d64-4b8c-be85-6ae0eb744eb8)|![GoldRacoonFaceset](https://github.com/fescherer/blog/assets/62115215/5ac3f14f-c635-44b3-93fe-4cc4b1f22842)|![Faceset](https://github.com/fescherer/blog/assets/62115215/e86f0017-7625-4173-ab3a-8bcc073abcf8) 6|![Slime4Faceset](https://github.com/fescherer/blog/assets/62115215/3c58a0fb-2d73-4557-b7ea-d9ff27665e60)|![ButterflyFaceset](https://github.com/fescherer/blog/assets/62115215/6f185898-cbf4-448a-9c2e-47c5a74d31fd)|x|x|x|x 7|![Slime4Faceset](https://github.com/fescherer/blog/assets/62115215/3c58a0fb-2d73-4557-b7ea-d9ff27665e60)|![ButterflyFaceset](https://github.com/fescherer/blog/assets/62115215/6f185898-cbf4-448a-9c2e-47c5a74d31fd)|![LarvaFaceset](https://github.com/fescherer/blog/assets/62115215/44ee204c-6368-4782-8adb-0c69be77a503)|x|x|x 8|![Slime4Faceset](https://github.com/fescherer/blog/assets/62115215/3c58a0fb-2d73-4557-b7ea-d9ff27665e60)|![ButterflyFaceset](https://github.com/fescherer/blog/assets/62115215/6f185898-cbf4-448a-9c2e-47c5a74d31fd)|![LarvaFaceset](https://github.com/fescherer/blog/assets/62115215/44ee204c-6368-4782-8adb-0c69be77a503)|![Bamboo](https://github.com/fescherer/blog/assets/62115215/5a287dbb-34b8-4d89-9394-0bb9a55cacb4)|x|x|x 9|![Slime4Faceset](https://github.com/fescherer/blog/assets/62115215/3c58a0fb-2d73-4557-b7ea-d9ff27665e60)|![ButterflyFaceset](https://github.com/fescherer/blog/assets/62115215/6f185898-cbf4-448a-9c2e-47c5a74d31fd)|![LarvaFaceset](https://github.com/fescherer/blog/assets/62115215/44ee204c-6368-4782-8adb-0c69be77a503)|![Bamboo](https://github.com/fescherer/blog/assets/62115215/5a287dbb-34b8-4d89-9394-0bb9a55cacb4)|![Cyclope2Faceset](https://github.com/fescherer/blog/assets/62115215/01cf9767-7bf4-42f8-a147-a57d9f55a8fb)|x 10|![Slime4Faceset](https://github.com/fescherer/blog/assets/62115215/3c58a0fb-2d73-4557-b7ea-d9ff27665e60)|![ButterflyFaceset](https://github.com/fescherer/blog/assets/62115215/6f185898-cbf4-448a-9c2e-47c5a74d31fd)|![LarvaFaceset](https://github.com/fescherer/blog/assets/62115215/44ee204c-6368-4782-8adb-0c69be77a503)|![Bamboo](https://github.com/fescherer/blog/assets/62115215/5a287dbb-34b8-4d89-9394-0bb9a55cacb4)|![Cyclope2Faceset](https://github.com/fescherer/blog/assets/62115215/01cf9767-7bf4-42f8-a147-a57d9f55a8fb)|![Faceset](https://github.com/fescherer/blog/assets/62115215/587fad6d-b31c-420e-97c6-d12409337bdc) 11|![BeastFaceset](https://github.com/fescherer/blog/assets/62115215/ce28ab57-5228-4d41-bd13-96d049444864)|![SkullFaceFaceset](https://github.com/fescherer/blog/assets/62115215/507aa1c4-da31-41f5-884e-03393b9dda92)|x|x|x|x 12|![BeastFaceset](https://github.com/fescherer/blog/assets/62115215/ce28ab57-5228-4d41-bd13-96d049444864)|![SkullFaceFaceset](https://github.com/fescherer/blog/assets/62115215/507aa1c4-da31-41f5-884e-03393b9dda92)|![EyeFaceset](https://github.com/fescherer/blog/assets/62115215/20ebf748-f8ec-4a33-9448-824ecc6ddc62)|x|x|x 13|![BeastFaceset](https://github.com/fescherer/blog/assets/62115215/ce28ab57-5228-4d41-bd13-96d049444864)|![SkullFaceFaceset](https://github.com/fescherer/blog/assets/62115215/507aa1c4-da31-41f5-884e-03393b9dda92)|![EyeFaceset](https://github.com/fescherer/blog/assets/62115215/20ebf748-f8ec-4a33-9448-824ecc6ddc62)|![MolluscFaceset](https://github.com/fescherer/blog/assets/62115215/b585b478-825b-4930-9727-b92fade0ff91)|x|x 14|![BeastFaceset](https://github.com/fescherer/blog/assets/62115215/ce28ab57-5228-4d41-bd13-96d049444864)|![SkullFaceFaceset](https://github.com/fescherer/blog/assets/62115215/507aa1c4-da31-41f5-884e-03393b9dda92)|![EyeFaceset](https://github.com/fescherer/blog/assets/62115215/20ebf748-f8ec-4a33-9448-824ecc6ddc62)|![MolluscFaceset](https://github.com/fescherer/blog/assets/62115215/b585b478-825b-4930-9727-b92fade0ff91)|![FlamFaceset](https://github.com/fescherer/blog/assets/62115215/e0ee8e76-7b02-440f-8794-73f1c504412f)|x 15|![BeastFaceset](https://github.com/fescherer/blog/assets/62115215/ce28ab57-5228-4d41-bd13-96d049444864)|![SkullFaceFaceset](https://github.com/fescherer/blog/assets/62115215/507aa1c4-da31-41f5-884e-03393b9dda92)|![EyeFaceset](https://github.com/fescherer/blog/assets/62115215/20ebf748-f8ec-4a33-9448-824ecc6ddc62)|![MolluscFaceset](https://github.com/fescherer/blog/assets/62115215/b585b478-825b-4930-9727-b92fade0ff91)|![FlamFaceset](https://github.com/fescherer/blog/assets/62115215/e0ee8e76-7b02-440f-8794-73f1c504412f)|![Faceset](https://github.com/fescherer/blog/assets/62115215/15cd77a1-7f76-43d1-9de7-39da5959671d) 16|![Beast2Faceset](https://github.com/fescherer/blog/assets/62115215/4db6a7ff-a079-4e19-a28b-c4a2643b7545)|![SkullFaceset](https://github.com/fescherer/blog/assets/62115215/1fbefe0a-01fd-4b26-be03-320faf2d3b95)|x|x|x|x 17|![Beast2Faceset](https://github.com/fescherer/blog/assets/62115215/4db6a7ff-a079-4e19-a28b-c4a2643b7545)|![SkullFaceset](https://github.com/fescherer/blog/assets/62115215/1fbefe0a-01fd-4b26-be03-320faf2d3b95)|![Eye2Faceset](https://github.com/fescherer/blog/assets/62115215/b11829e4-2825-4847-b6ed-a81d0bbcd695)|x|x|x 18|![Beast2Faceset](https://github.com/fescherer/blog/assets/62115215/4db6a7ff-a079-4e19-a28b-c4a2643b7545)|![SkullFaceset](https://github.com/fescherer/blog/assets/62115215/1fbefe0a-01fd-4b26-be03-320faf2d3b95)|![Eye2Faceset](https://github.com/fescherer/blog/assets/62115215/b11829e4-2825-4847-b6ed-a81d0bbcd695)|![Mollusc2Faceset](https://github.com/fescherer/blog/assets/62115215/cfa5bd94-1ab2-41a0-ac80-e06a1d6f1347)|x|x 19|![Beast2Faceset](https://github.com/fescherer/blog/assets/62115215/4db6a7ff-a079-4e19-a28b-c4a2643b7545)|![SkullFaceset](https://github.com/fescherer/blog/assets/62115215/1fbefe0a-01fd-4b26-be03-320faf2d3b95)|![Eye2Faceset](https://github.com/fescherer/blog/assets/62115215/b11829e4-2825-4847-b6ed-a81d0bbcd695)|![Mollusc2Faceset](https://github.com/fescherer/blog/assets/62115215/cfa5bd94-1ab2-41a0-ac80-e06a1d6f1347)|![Flam2Faceset](https://github.com/fescherer/blog/assets/62115215/a80dcc97-752b-4c39-b8f3-047e3e84bcc4)|x 20|![Beast2Faceset](https://github.com/fescherer/blog/assets/62115215/4db6a7ff-a079-4e19-a28b-c4a2643b7545)|![SkullFaceset](https://github.com/fescherer/blog/assets/62115215/1fbefe0a-01fd-4b26-be03-320faf2d3b95)|![Eye2Faceset](https://github.com/fescherer/blog/assets/62115215/b11829e4-2825-4847-b6ed-a81d0bbcd695)|![Mollusc2Faceset](https://github.com/fescherer/blog/assets/62115215/cfa5bd94-1ab2-41a0-ac80-e06a1d6f1347)|![Flam2Faceset](https://github.com/fescherer/blog/assets/62115215/a80dcc97-752b-4c39-b8f3-047e3e84bcc4)|![Faceset](https://github.com/fescherer/blog/assets/62115215/c6f3a1df-4374-459e-a999-051268cb7eb8) The main idea is that each tower can be good/bad to a enemy type. For example the Knight tower is only able to damage near enemies but can deal area damage, so It's good to enemies that come in groups(Mostly land), on the other hand the Mage tower has single shot and far distance, which is good to fast and single enemies(Mostly air). ## Day 04 This day I was little busy playing tennis with my brother and my girlfriend. Was the first time we played, it's nice, but tiring. Anyways, in the spare time I worked at creating the project on Godot (I know it still has the songs and tiles, but this I choose later). This time I looked a lot in the [Godot Documentation](https://docs.godotengine.org/en/stable/index.html) therefore I don't have much to say, I mostly just studied. ## Day 05 Today is Sunday, so I have all the time to play around with the game building. I started to follow a Youtube series [from Game Development Center](https://www.youtube.com/watch?v=wFdpCGbrVXI&list=PLZ-54sd-DMAJltIzTtZ6ZhC-9hkqYXyp6), it's a bit out of date, but we can get a very good idea to how tower defense games are. The first classes is to structure the project and make the first configurations on Godot. He said that we can reuse this to future projects. First, the folder structure. It is separated in 6 folders, the first one being the `Assets`, that is branched into `Environment`, `Icons` and `UI`, everything auto explaining. Inside the `Environment` we have `Props`(Files with one asset per file) and `Tilesets`(Files with two or more assets in). The `Licenses` is where you store license of fonts, assets and everything else. The `Resources` is objects that will be reused on project. The Scenes is where will be store all the objects(Classes) created. Lastly we have Singletons, objects that are independent from the rest of project. <Img src="https://github.com/fescherer/blog/assets/62115215/90c25a0f-701a-46fc-a34f-7c9bfff6ec7a" name="Folder structure" alt="Folder structure" /> Other thing I did is the first map, I finally learned about [tilesets](https://docs.godotengine.org/pt-br/4.x/classes/class_tileset.html). Following the documentation and the [video of Game Development Center](https://www.youtube.com/watch?v=SIQKpBCmkvk) I finally make the first map using some scalable techniques like make terrains. Terrains for now is just used to create roads, that using assets Godot can automatically find the right asset to that road, this way I don't need to find the right asset to that corner on the road. I followed [this tutorial by Maker Tech](https://www.youtube.com/watch?v=N6aVQ2ylMrU). The map has still really poor flora and I want to trees like terrains, to be more efficient in creating forests (If you pay attention, there are some tiles with bugged trees). In general was a very productive game. If I hope I can keep up this rhythm. <Img src="https://github.com/fescherer/blog/assets/62115215/ab6f4c7a-500f-4dc3-9e87-d8a05bb2f66c" name="Fist scenario" alt="Fist scenario with a lot of trees and a really strange path" /> ## Day 06 This day I was busy with some stuff and could not make any progress π₯²π₯²π₯² ## Day 07 I know I should give more attention to the map, but everything seems kind weird. The scale doesn't seems right. That's because I need to confirm that the scale is right to create the map based on. So I decided to create the first tower of the game, thinking mostly in adapt the scale (Spoiler *I did not have the time to play with the scale*). Anyways, I started with `node2D` and then put the sprite inside, I don't know if this is the best practice, but for me, giving a wrapper of `node2D` makes me feel more organized. The sprite for now will be the idle: <Img src="https://github.com/fescherer/blog/assets/62115215/3a8c84b0-8d1d-4ccc-995f-cce346e14ada" name="Idle Knight animation" alt="Idle Knight animation spritesheet" /> One thing is that it has right, left, bottom and top sides, which gives me the necessity to turn the sprite as the enemy changes it's direction. I started trying the sprite to follow my mouse, after many tries, I came with a simple code, but I happy that I can understand everything from this code. π ```python extends Node2D var direction = Vector2(0, 0) func _physics_process(delta): turn() func turn(): var target_position = get_global_mouse_position() var character_position = global_position direction = (target_position - character_position).normalized() if(direction.x > abs(direction.y)): get_node("Knight").set_frame(3) elif direction.x < -abs(direction.y): get_node("Knight").set_frame(2) elif direction.y > abs(direction.x): get_node("Knight").set_frame(0) else: get_node("Knight").set_frame(1) ``` <Img src="https://github.com/fescherer/blog/assets/62115215/ca045152-c4bb-4d7a-947f-a29860f2eee7" name="Mouse following" alt="Gif showing character knight follows the mouse direction" /> ## Day 08, 09, 10 and 11 I am gonna group all these days together because I really did not have a lot of time to progress in the game and the little time I had was trying to make the map looks a little bit better. It was really hard, not because the sprites, they are awesome, it's because of me, I did not have and experience grouping those tiles together in a pleasant way to the eye. I don't know, I was looking for a diverse map, having the flower, ice, rocky and forest parts, but I don't know, something looks out of place. <Img src="https://github.com/fescherer/blog/assets/62115215/8add9292-f00c-4027-9fc4-bcd2073e91c2" name="Map details" alt="Add more details(maybe too much) and removed most of trees" /> ## Day 12 This day I created another tower and rescale the ***knight*** already created to fit better in the map. It was very nice, I learned about scaling and import pixel art in Godot 4. I also put a base in tower to better highlight on map. The code part was very easy, following the tutorial [Game Development Center](https://www.youtube.com/watch?v=IHGEiQhR1PQ&list=PLZ-54sd-DMAJltIzTtZ6ZhC-9hkqYXyp6&index=3) I just imported the logic from a generic class. <Img src="https://github.com/fescherer/blog/assets/62115215/fcb3e20c-5cc4-46e5-875c-5aac6817c6bd" name="Tower Knight and Mage" alt="Picture showing the two towers created, one the knight and other the mage" /> ## Day 13 It was a very short day. The only thing I could do was the main menu. I was following the video of Game Development Center, but in Godot 4, some methods have changed, main thing is to **connect** now is deprecated and we need to use something like `[Node].[Signal].connect([method])` instead of `.connect([signal], [object], [method]` <Img src="https://github.com/fescherer/blog/assets/62115215/94a8289c-ca05-4aa0-ad54-636d5b24d053" name="Menu" alt="Picture showing the menu created, three blue buttons" /> ## Day 14 This is one more busy day, I just make some initial HUD, following the same tutorial of [Game Development Center](https://www.youtube.com/watch?v=X5YsM-dLbCA&list=PLZ-54sd-DMAJltIzTtZ6ZhC-9hkqYXyp6&index=4). It was kinda simple because this hud doesn't do anything else, it's just two images. I used the assets of [Ninja's Adventure](https://pixel-boy.itch.io/ninja-adventure-asset-pack) again, they are just wonderful, I just need to make them smaller, but this is problem for other day. <Img src="https://github.com/fescherer/blog/assets/62115215/4540f130-9de2-4318-8ab1-5fa8aff532b2" name="HUD" alt="Showing the HUD below the map, having only the image of the two towers" /> ## Day 15, 16 and 17 These days I found a very big problem π. At day 15 I was intended to do the fifth part of the [Game Development Center Godot's tutorial](https://www.youtube.com/watch?v=9W4zVD-kOwU&list=PLZ-54sd-DMAJltIzTtZ6ZhC-9hkqYXyp6&index=6), but there were a lot of different things I did in the project that I was getting the consequences. Like the resolution of 1280x720 is too big for a 16x16 assets of [Ninja's Adventure](https://pixel-boy.itch.io/ninja-adventure-asset-pack), so I tried to add scale and messed with everything else, now, 4 tiles were supposed to be just 1, and the collision system was not working and I knew I needed to make a work around that I would not like. So after trying many strategies, I just stopped π. <Img src="https://github.com/fescherer/blog/assets/62115215/0105d092-1eda-4a39-b605-3e2333f4a00f" name="Building tower" alt="User clicks in the HUD and get the tower to build it" /> ## Day 18 I was so frustrated because of bugs that I decided not think about it today. ## Day 19, 20 and 21 Even if I was frustrated, I have not given up, and would make the game from zero if necessary. And so I did, not everything, but I can guarantee was a lot. One of the many problems was the scale, resolution and sprite sizes. The problem I had was a pixelart game with assets of 16x16 that was running on a resolution of 1280x720 which are 1280/16 * 720/16 = **3,600** tiles to fill the entire screen. Think about it, every sprite is 16x16, *even the towers* which in the 3600 tiles, max 10 would be towers, of course this will cause a strange sensation, ***it's not in the right scale***. To solve this, I scale some arts, like the towers and bases, this way, a tower had 1 tile size, but the path was 3 tiles size. You can imagine the problem when making collisions. To solve this, I changed the ***Viewport width and height*** to something way smaller (**320x192**) and put the ***Window Width and height Override*** to 3 times of the **Viewport size**. This way, the game will have a resolution of 320x192 and will open in a window of 960x576, making all the scale for me. The game now doesn't needed to scale some arts, everything from enemies, path and towers will be 16x16 and just one tile. I did a small change on the [Ninja's Adventure' assets](https://pixel-boy.itch.io/ninja-adventure-asset-pack) because some tiles were way to big for me, and then I made them smaller (Logs and a type of grass). <Img src="https://github.com/fescherer/blog/assets/62115215/e71883a2-56cf-43b0-addc-49873b9fd0bf" name="Fixing viewport" alt="Picture showing the viewport 320 x 192 and the override 960 x 572" /> <Img src="https://github.com/fescherer/blog/assets/62115215/8804a239-4187-4f20-8a73-b5ec86cfd2f7" name="Map remade" alt="The new map with only one clear path at center and forest borders, the HUD now is in the right, very clean layout" /> Now using a resolution of 320x192 gives us 320/16 * 192/16 = **240** tiles only. It may look way to little, but is not. I feel I can make better maps with this size. Of course this brought me a lot of more problems π₯². All the assets in the scenes were with wrong sizes and so I went in every scene fixing the bugs (towers, map, ui, menu, everything π₯². If you are starting as me, just make sure the scale is right π ). In this process I redid the menu, ui and the map. Here is the final result: <Img src="https://github.com/fescherer/blog/assets/62115215/08ce33c1-c752-4c92-9527-f4440ceb74ff" name="New menu" alt="Remake of menu, now is dark with shadows on buttons" /> *Note*: I leaned how to animate some sprites in the map π€©π€©π€©. # Day 22 Now after all that remake I finally can move on to other features. In this day I finish the selection and collision/building tower system. There were some changes to make from tutorial because Godot 4 has changed some names, but essentially is the same of the [Game Development Center's tutorial](https://www.youtube.com/watch?v=-K1c4OfC2SU&list=PLZ-54sd-DMAJltIzTtZ6ZhC-9hkqYXyp6&index=5). <Img src="https://github.com/fescherer/blog/assets/62115215/2d81e57a-a589-47a2-aad0-32be40018016" name="Collision" alt="Gif showing when tower is pickup you cannot put in other tower, or blocked grid" /> I also did a very simple thing that I was supposed to do in the first time starting this project π . A [git repo](https://github.com/fescherer/game-off-2023). ## Day 23 Now I can see a game under construction. This day I make a range indicator to tower following [Game Development Center's tutorial #5](https://www.youtube.com/watch?v=9W4zVD-kOwU&list=PLZ-54sd-DMAJltIzTtZ6ZhC-9hkqYXyp6&index=6). This range can be configured in a separate file, this way the towers can have different sizes of ranges (Of course this file also can be used to configured damage, rate of fire, cost, etc.). <Img src="https://github.com/fescherer/blog/assets/62115215/d1230900-33e8-4ede-9d3a-628b9cd60b84" name="Range indicator" alt="A gif showing the range indicator, green circle area in the character" /> ## Day 24 It has finally come, the day I built the enemies. A enemy in a tower defense game should not be a problem, because it's IA is just- "Follow the path", and so I just need to configure the path and make the enemy follow it. Can easy be done using [Godot's Path2D](https://docs.godotengine.org/en/stable/classes/class_path2d.html). My problem was not configure this path, but configure enemy's animation, well the [Game Development Center's tutorial #6](https://www.youtube.com/watch?v=JBQgmy3Oiw4&list=PLZ-54sd-DMAJltIzTtZ6ZhC-9hkqYXyp6&index=7) uses a top down view asset, as [Ninja's Adventure' assets](https://pixel-boy.itch.io/ninja-adventure-asset-pack) uses side view assets, so just rotating the sprite will not work in my case. I did by calculating how the position is different from the last frame and give the right animation. Solving this made my day πππ. So Happy π. ```GDScript extends PathFollow2D @onready var _animated_sprite = get_node("CharacterBody2D").get_node('AnimatedSprite2D') var speed = 0.5 var last_position = Vector2(0, 0) func _physics_process(delta): move(delta) func move(delta): set_progress(get_progress() + speed + delta) var current_position = position if(last_position != current_position): if(abs(last_position.x) < abs(current_position.x)): # Going Right _animated_sprite.play('east') if(abs(last_position.x) > abs(current_position.x)): # Going Left _animated_sprite.play('west') if(abs(last_position.y) < abs(current_position.y)): # Going down _animated_sprite.play('south') if(abs(last_position.y) > abs(current_position.y)): # Going up _animated_sprite.play('north') last_position = current_position ``` <Img src="https://github.com/fescherer/blog/assets/62115215/36af2dec-a5e7-44f3-a92d-e881252e0c74" name="Enemy" alt="First enemy following the path. It is an angry bamboo enemy" /> ## Day 25 As I solve a lot of problems in the last days, I lost my track of progressing the game creation, so this day I finally had the sense of progression. I did a lot of things. First of all, I make a start/pause/fast forward system. Very simple, just followed [Game Development Center's tutorial #7](https://www.youtube.com/watch?v=T1auCC-PT4k&list=PLZ-54sd-DMAJltIzTtZ6ZhC-9hkqYXyp6&index=8) and make some simple pixelart 16x16 sprites, because [Ninja's Adventure](https://pixel-boy.itch.io/ninja-adventure-asset-pack) didn't have sprites for arrow, double arrow and pause. Second, the tower need to look at enemy and not my mouse π I followed the most part of the [Game Development Center's tutorial #8](https://www.youtube.com/watch?v=ugNRDsl33OI&list=PLZ-54sd-DMAJltIzTtZ6ZhC-9hkqYXyp6&index=9), but seeing the comments, we could improve the tower tracking. In the video, Stefan select the enemy with most progress in the path (Which is very good, because more progress in the path, means more likely to complete the path), but this gets a problem when using more than one path. Think with me, if we have two paths and enemies in both that crossed the same amount of pixels, the tower will get confused with enemy is the right to pick. The best choice will be get the enemy with the largest progression and in the shortest path. For example if we have two paths, one having 450px size and other having 200px size, there are enemies in both paths and the enemies have crossed the same amount of pixels, like 30. The progress of the first path will be 450 - 30 = 420, and the second will be 200 - 30 = 170, if we get the largest number, the enemy on path1 will be chosen, but can agree that is more likely to enemy on path two finish the path, because only need to cross more 170 instead of 420 in the first path. So that's why we need to get the minimum value. After this though I made the damage system. It was very simple, the only thing I did different from the tutorial was that there, Stefan put a heath bar above the enemy. In my case, I changed the enemy color based on heath percentage like: heath > 70% = Normal Color; heath > 50% = Yellowish; heath > 20% = Orangish; else = Reddish. The last thing I did was a shooting mechanic. In the tutorial, the towers shoot the same projectile, but in my game, it needs to be different so I abstracted the code and make a muzzle and impact animation for each tower, so thats in the future I can add a projectile to certain towers, like the Mage one, and for the Knight it does not need this. I will think more about this tomorrow. The result: <Img src="https://github.com/fescherer/blog/assets/62115215/2b8b60d8-c0c1-4d93-817a-9c8a1835c1b7" name="Powers" alt="Knight powers, lounging his spear" /> ## Day 26 This was a very simple day I just make the system of health and coin, there is no much to say. I just make some signals that are emit when enemy is killed or its complete the map's path. <Img src="https://github.com/fescherer/blog/assets/62115215/f11b65d3-b434-434a-a861-0b161788ab7f" name="Coin - Health" alt="Gif showing the HUD with coins and health and every enemy defeated, add one coin, if the enemy escapes, you loose one life" /> ## Day 27, 28, 29 and 30 As the voting day is coming, I decided to focus in finish the main mechanics instead of adding new stuff, so I decided to make the last updated in this article grouping all the stuff made in the last 4 days.πΆβπ«οΈ Well, first of all, as usual I went through the game and noticed 4 things to do: The game need waves counter, more enemies, about page (Where the credits of me and Ninja's Adventure will be), and for last, there are some bugs to take care. The first thing I did was about the waves. This time I would finally make the balance between towers and enemies (I can say to that I though would be easy, but not, this is a very crucial part that can make your game look good or terrible). Was very hard to do that, but I made. I add a simple wave counter, and made the waves into an `GameData` dictionary so I could easily modify. I add two more screens, one for `Game over` and other for `Game Win`. And of course, more enemies. They are just an extension of the main enemy's script, with some values and sprites changed (In the future, I want to make this less manual, to add even more enemies, creating a scene in usual way is no good). In the meantime I noticed that the game was very short, so I add a `Infinity mode` that would spawn random enemies (In the end, this was supposed to be a very challenging mode, but proved to be not so difficult, in the future I will change that). The about page was very easy to make. It's just a simple new scene. The more complicated part was to create a `Game over` and `Game win` because these two scenes need to be over `game's scene` and not in root. But it was ok, and simple to do.π In the bug's part, there were a lot linked to the wave system. I don't know, but making all that logic in just one script just made it very confusing. This is one more thing to change in the future, a better `game's script`, I don't know, maybe just separate the logic of spawning enemies, controlling the waves, building towers and enemies path in a separate script would make so much better. It had a bug in the fast forward too, but I could not had time to fix this, so it will be a future issue π . You can play the game at [Itch.io](https://drafonf.itch.io/enchanted-towers-realm-defenders). Leave a message to me. I will be very happy to talk to you π. <Img src="https://github.com/fescherer/blog/assets/62115215/2f68d076-42f7-459e-b723-6ac36d7ceb98" name="Final results" alt="Gif showing everything made, with new enemies- bugs" /> <Img src="https://github.com/fescherer/blog/assets/62115215/d9dc2485-144c-4e6f-9b9d-08e23b67b1af" name="Game over screen" alt="Game over screen, reddish, with two buttons to leave or play again" /> ## Final thoughts Well, what I learned with all this? This was my first experience making a game in a professional way (In some pro engine), so because of this there was many days that I was unmotivated, even if I came from web programming, coding in game, it's harder (Maybe that's because I did not knew the language) and that made me a little frustrated, but as I was going, never giving up, I saw a light in end of the tunnel. Some stuff that was hard to understand like what is a scene, became very easy and I could make all the changes that my imagination could have. I really like the experience. Well, there was many thing I could not had time to develop. If you see the first days of this article, you will understand that, I planned a lot of things, the scope was too big for a first project that needed to be made in just a month, but that's ok, I plan to add these things later, I will finish the game as was proposed back there. For last, thanks everyone who read this article. It was a very difficult month, but I could say that I very happy and proud about the results. I see you in the next article. Have a nice day. πβ¨β¨β¨π. dt