{ "version": "https://jsonfeed.org/version/1", "title": "This is Vini!", "home_page_url": "https://thisisvini.com", "feed_url": "https://thisisvini.com/feed.json", "description": "Vinicius Gerevini's personal website. Ideas, projects and other things.", "icon": "https://thisisvini.com/images/thisisvini.png", "author": { "name": "Vinicius Gerevini", "url": "https://thisisvini.com/about" }, "items": [ { "id": "https://thisisvini.com/game-dev-projects", "content_html": "
Hello there. As you may or may not know, I have a Youtube channel where I share stuff about my solo game dev journey. In the two years since I started the channel, I released a few plugins and created a few samples, which are all open source and available on my Github.
\nAs I’m really bad at self-promotion, I don’t think these projects reached too many people. That’s why I decided to compile this list to make them more discoverable.
\n\n\nNote: Some of these samples were created in older Godot versions and may not be working anymore. Let me know if you find something broken and I might find some time to fix it.
\n
\n\n
\nhttps://github.com/viniciusgerevini/godot-aseprite-wizard
\nThis is by far my most popular and useful project. Aseprite Wizard helps to import animations from Aseprite to Godot. It supports AnimatedSprite, AnimationPlayer and SpriteFrames. The video above is slightly outdated when it comes to configuration, but the usage is pretty much the same.
\n \n
\nhttps://github.com/viniciusgerevini/godot-clyde-dialogue
\nClyde is a language I created to allow me to write my game dialogues more simply. Stop the JSON file madness!!!
\nIt’s supposed to be closer to normal writing and it supports branching dialogues, translations and interfacing with the game through variables and events.
\nThis plugin adds an importer so you can load .clyde files directly, and also exposes the interpreter as ClydeDialogue.
In the video above, I quickly go through the main features. I’ve got feedback that the video was a little bit rushed, so I’m intending to create a better tutorial which covers even the interface part.
\n\n\n
\nhttps://github.com/viniciusgerevini/godot-behavior-tree-example
\nThis is pretty close to the Behaviour Tree implementation I use in my own game. My idea here was to foster Godot’s node tree structure to implement my behaviour tree, without needing extra visual tools.
\nThis boilerplate is in fact a plugin that adds some custom nodes that can be used to build your tree. There are also some examples included showing the trees in action.
\nThe video above is a simple introduction to behaviour trees and how I implemented them in Godot.
\n\n\n
\nhttps://github.com/viniciusgerevini/godot-goap
\nFollowing the AI subject, this was an experiment I made building my own Goal Oriented Action Planner. I wanted to validate this AI pattern because I felt most of the examples I found online were incomplete or not realistic.
\nAs opposed to the behaviour tree sample, this is not battle-tested, so you probably should not use it as is.\nThe video above works as an intro to GOAP, but keep in mind my experience with it is limited to experiments only.
\n\n\n
\nhttps://github.com/viniciusgerevini/godot-color-replacement-example
\nThis example includes 3 different ways to change colours in an image. Using shaders, the modulate property and changing the pixel colour directly. This came from a real use case where I needed to change the skin colours of characters in my game. The shader method is what I use currently.
\nThe video goes through the three methods and the pros and cons of each one.
\n\n\n
\nhttps://github.com/viniciusgerevini/godot-boids
\n\n\nBoids is an artificial life program, developed by Craig Reynolds in 1986, which simulates the flocking behaviour of birds.
\n
This is a simple and naive Boids implementation. It does not include collision avoidance and other fancy features, but I know it works as this is exactly the code I use in my game.
\nThe video talk about the 3 simple rules that make it work and how I implemented them.
\n\n\n
\nhttps://github.com/viniciusgerevini/godot-navigation-2d-example
\nThis example is kind of outdated now that Godot 3.5 includes the new and improved Navigation Server. However, if you are curious about how I implemented pathfinding for a 2D Platformer and the challenges I faced, you might want to take a look at the video above.
\nI hope you found something useful on this list. Please, if you have any interesting topic or subject, let me know in the comments. I’m always looking for new ideas.
\nIf you like this kind of stuff, you might want to consider subscribing to my Youtube channel. The channel is mostly focused on my projects, but I try to balance it with tutorials and other things.
\nFor latest updates, messages or comments, I'm on Twitter as @vini_gerevini.
\nYou can also support me by wishlisting my game, Far Star, on Steam.
", "url": "https://thisisvini.com/game-dev-projects", "title": "Godot useful samples and plugins", "summary": "Hello there. As you may or may not know, I have a [Youtube channel](https://www.youtube.com/c/ThisIsVini) where I share stuff abou…", "image": "https://thisisvini.com/images/content/godot_projects_banner.png", "date_modified": "2022-08-25T07:20:45.000Z" }, { "id": "https://thisisvini.com/2021-restrospective", "content_html": "I’ve created this blog to exercise my ideas, share my thoughts and showcase what I’m up to. However, I’ve just realised I didn’t post a single thing in 2021. So I decided to write this one, to reflect on all the things I’ve done in the past year.
\nEvery time I have the chance, I like to give back to the community. Most of the software I use in the day-to-day is open source. As I don't have enough time or energy to contribute to these open source projects, every time I have the chance I squeeze in an update to the projects I maintain, release some useful sample or tutorial that might help others.
\n
My year started with a pull request fixing and improving Aseprite Wizard, a plugin I created for Godot to help import animations from Aseprite to the game engine.
\nThis plugin started as a bash script, which I realised could be useful for other people. So I translated it to Godot’s GDScript and released it as an editor plugin. Since then, I received positive feedback from the community, many suggestions and bug fixing contributions.
\nThis week I'm going to release a new version that supports a more powerful animation node, a feature that was asked a few times in the past.
\n
Also related to open source projects, back in January, I released the first version of Clyde, a scripting language I developed for creating game dialogues.
\nBesides the language specs, I created a parser and an interpreter in Javascript, also a CLI tool for executing Clyde files and an online editor for people to test the language. By the end of the month, I also released a plugin for Godot, with a native parser, interpreter and importer in GDScript, so I could use the language in my own game.
\nI also played around developing syntax support for VSCode and Vim.\nThis was a fun project, as I was able to play around with language lexers, parsers and interpreters. Something I haven’t looked into since my times at Uni.
\n
In 2021, I also resumed my project of creating my own mechanical keyboard. This was a split keyboard using some Gatheron Brown switches, a Bluefruit Feather and a Pro Micro. Instead of creating a PCB, I wired the switches manually.
\nAfter soldering everything and spending some weeks playing around with C and Arduino board deployment issues, I managed to make it work.
\nIt did work, but it was far from being good. Some bugs with layers and at the end, that Frankenstein felt a little bit too weird to type. Mission accomplished anyway.
\n
My main side project and hobby is Game Development. I’m currently working on my first game called Far Star. In 2020, I started a Youtube channel to log my progress, and I kept doing it in 2021.
\nIn 2021, The Far Star repository received 571 commits across 195 days. This doesn’t say much, as the size of my commits vary depending on what I’m working on, and also when working on animations, sounds and other assets I usually don’t commit often.
\nWhen it comes to Youtube, I released 14 videos in 2021. Mostly dev logs, but I also released a few tutorials with subjects such as Behaviour Trees, Goal Oriented Action Planning, Boids and color replacement. In my github you can find a bunch of sample projects I used for my videos.
\nWhen it comes to subscribers, my channel reached 600 subscribers after one year alive. That’s not much for youtube standards, but I’m happy with the result anyway, as I never actively promoted my videos anywhere, besides eventually posting on my Twitter account, which only contains a handful of people.
\nWell, I don’t talk about work in my personal stuff, and also I don’t think I can share externally details on what I work on during the day. It's not top-secret, it's just that I don't want to spend cognitive energy thinking on what may be under NDA.
\nI’m a Software Developer, so what I can say is that I worked on a bunch of projects related to scaling and performance. I had the chance to lead a few projects, code a little bit and be paged overnight for some wrong metric we setup. he he.
\n
Well, that's not really the focus of these posts, but thanks for asking. My plants grew like crazy, I spent most of the time in lockdowns because of the current thingy happening with the world and I completed a few series and games (mostly indies, but I did complete everything on Metroid Prime and Jedi Fallen Order).
\nYep, I think this pretty much sums up the highlights of the year. Hopefully, I get the motivation to share things more often here this year. If I don't, see you in the 2022 review :D.
", "url": "https://thisisvini.com/2021-restrospective", "title": "What I've been up to in the past year", "summary": "I’ve created this blog to exercise my ideas, share my thoughts and showcase what I’m up to. However, I’ve just realised I didn’t p…", "image": "https://thisisvini.com/images/content/here_comes_the_sun_melbourne.jpeg", "date_modified": "2022-01-19T13:46:40.000Z" }, { "id": "https://thisisvini.com/behavior-trees", "content_html": "I recently posted a video with an introduction to Behavior Trees. My first draft was almost 40 minutes long, so I decided to cut it and simplify a few parts.
\nI managed to make it 10 minutes long, but to be honest I'm not satisfied with how it turned. Here in this post, I'll try to summarise the topics covered in the video and hopefully, they will be clearer and easier to understand.
\nBehavior Tree is a common pattern used on game AIs. It makes it possible to create complex behaviors by using smaller, independent tasks.
\nTo understand how Behavior Trees work, we need to talk about the different elements that compose a Behavior Tree.
\nFor my examples, I'll be using GDScript, the default language in Godot. It was inspired by Python, but even if you are not familiar with it, having a basic programming knowledge is enough to understand the examples.
\nWith exception of the root node, nodes in a behavior tree have a common contract. A node may return one of the following states: running if its execution has not finished in the current call, success if it has achieved its goal, or failure otherwise.
In my implementation, nodes also have a tick method. This method is called by the node's parent and receives two arguments: an actor, which is the element which the behavior is applied to, and a blackboard, which is a type of dictionary where data may be stored for later use.
Behavior Tree's implementation and naming may change wildly, but I will stick with a simple implementation, to make things easier to understand.
\n# task node pseudo implementation\nfunc tick(actor, blackboard):\n if actor.attack(blackboard.get(\"target\")):\n return SUCCESS\n return FAILURE\n\nThe root node is responsible for controlling the execution of the behavior tree. It has only one child and calls it with a certain frequency.
\nHere is an example of implementation for a root node:
\n# root node example\nonready var blackboard = Blackboard.new()\nonready var child = self.get_child(0)\nonready var actor = get_parent()\n\nfunc _process(delta):\n blackboard.set(\"delta\", delta)\n child.tick(actor, blackboard)\n\nLet me clarify a few details from the example above.
\nIf you are used to game development, you already know about something called the game loop. Most game engines expose a function which is called on each loop, so you can implement your game logic in it. Godot calls it _process, (there is also a _physics_process method, but this is not important for this subject). As my root node uses the _process function, it guarantees that the tree will be executed once every loop.
This function receives an argument, called delta. This is the time elapsed since the last time this function was executed. This is useful for calculations in general. For convenience, I'm adding the delta value to the blackboard, so other nodes can use it if needed.
This implementation also assumes that the Behavior Tree is a child of the actor, as you can see on line 4.
\nFinally, on line 8, it calls its child, passing the actor and the blackboard.
\n
Control flow nodes are the branches of the tree. They are used to control which tasks should be executed. The two most common implementations are sequences and selectors.
\nA sequence executes its children one by one, in order. In case one of its children returns failure or running, it stops the execution and returns the same status. If all of its children succeed, it returns success.
# sequence composite example\nfunc tick(actor, blackboard):\n for c in get_children():\n var response = c.tick(actor, blackboard)\n if response != SUCCESS:\n return response\n\n return SUCCESS\n\nA selector also executes its children one by one, in order. However, it stops when one of its children returns success or running. In case a child returns failure, it tries the next one. Only when all children return failure, it will fail.
# selector composite example\nfunc tick(actor, blackboard):\n for c in get_children():\n var response = c.tick(actor, blackboard)\n if response != FAILURE:\n return response\n\n return FAILURE\n\n
Execution nodes are where your custom logic is implemented. They are also called leaf nodes because they are located on the edges of the tree. In other words, leaf nodes do not have child nodes.
\nYour leaf node could implement an action, such as \"attack\", \"move\" and \"jump\", or a condition, such as \"has low health\", \"is enemy\" or \"is in attack range\".
\n# leaf node example: go to position\nfunc tick(actor, blackboard):\n var target_position = blackboard.get(\"target_position\")\n var delta = blackboard.get(\"delta\")\n actor.move_towards_position(target_position, delta)\n\n if actor.position.distance_to(target_position) > 0:\n return RUNNING\n return SUCCESS\n\nThe example above implements an action to move the actor to a predefined position. The movement implementation is abstracted in the method move_towards_position. Assume this method does some kind of linear interpolation.
Because this kind of action is usually not finished in only one tick, we check if the actor has reached the target and, in case it has not, we return running, otherwise we return success.

The decorator pattern is a common design pattern in object-oriented programming. It allows behavior to be added to objects dynamically, without changing their class. Decorator nodes do the same thing for nodes. They may add extra checks before executing a node or changing its output after execution. Let me show you examples for these two cases.
\nExample 1: changing the output
\nThe inverter decorator inverts the output of its child. This way success becomes failure, and failure becomes success. This makes nodes more reusable.
Imagine a situation where you have an enemy with an attack cooldown period. It attacks the player if not in cooldown, and when in cooldown, it runs away. Instead of implementing two different scripts for the leaf nodes, \"is in cooldown\" and \"is not in cooldown\", you can use the same script, only adding an inverter decorator to the condition when needed.
\n# Inverter decorator\nfunc tick(action, blackboard):\n var child = self.get_child(0)\n var response = child.tick(action, blackboard)\n\n if response == SUCCESS:\n return FAILURE\n if response == FAILURE:\n return SUCCESS\n\n return RUNNING\n\nExample 2: adding input behavior
\nA limiter decorator adds a limit on how many times a node can be called. It keeps track of how many times the node was executed, and in case the predefined limit is reached, it stops passing calls to the child node, and returns failure on every subsequent call.
Let's say you are implementing a fighting game. In this game there is a character with an over-powered attack, that should be executed at most once in a fight. For that, you could add a limiter decorator to the branch that deals with the over-powered attack logic. This way, even if the tree calls this branch multiple times, it will only succeed once.
\nonready var cache_key = 'limiter_%s' % self.get_instance_id()\nexport (float) var max_count = 0\n\nfunc tick(actor, blackboard):\n var current_count = blackboard.get(cache_key)\n\n if current_count == null:\n current_count = 0\n\n if current_count <= max_count:\n blackboard.set(cache_key, current_count + 1)\n return self.get_child(0).tick(actor, blackboard)\n else:\n return FAILED\n\nIn my example, for convenience, I'm saving the execution count in the blackboard. Another thing to note, is that max_count is defined through a parameter, 0 is its default value.
Most game engines have some built-in or community developed Behavior Tree implementation. Godot has at least one community developed one, that I know, with a drag-and-drop interface. I haven't used it myself, so I can't really recommend it.
\nGodot's architecture was built around the concept of a tree of \"nodes\", which makes it easy to create Behavior Trees without requiring any extra plugin.
\nHere is an example of tree using Godot nodes (these node icons are custom images I added to the project).
\n
And here is an example showing how easy is to change node's parameters through Godot's editor.
\n
I created this example to show how to implement a Behavior Tree in Godot. In my example, there is a behavior tree for an actor that follows the mouse, when close enough, and in case the mouse is too far, it goes to the closest \"home\". As a third branch, the actor changes its colour when reaches home for the first time.
\nIn case you are interested in the step-by-step implementation, check the video in the beginning of this blog post.
\nIf you decide to implement a behavior tree yourself, there are a few tips you may want to follow:
\nKeeping state inside nodes makes them less reusable. Always keep in mind other nodes may want to use the info stored. Also, you can't guarantee a node will be called on every tick, so internal data could get stale.
\nIf you need to save or share data, save it in the blackboard.
\nIn this post, most of my examples have blackboard keys embbeded in the script, like the \"go to position\" action:
\nfunc tick(actor, blackboard):\n var target_position = blackboard.get(\"target_position\")\n var delta = blackboard.get(\"delta\")\n actor.move_towards_position(target_position, delta)\n if actor.position.distance_to(target_position) > 0:\n return RUNNING\n return SUCCESS\n\nThis is not a good idea, as it means you will only be able to use this node with data from \"target_position\", and, in some situations, you may have issues where different nodes overwrite the same blackboard key.
\nInstead, you should export your key as a parameter, like this:
\nexport (String) var target_key\n\nfunc tick(actor, blackboard):\n var target_position = blackboard.get(target_key)\n var delta = blackboard.get(\"delta\")\n actor.move_towards_position(target_position, delta)\n if actor.position.distance_to(target_position) > 0:\n return RUNNING\n return SUCCESS\n\nFor instance, even though your action may say \"go to position\", it doesn't need to know how your actor moves around.
\nYour actor may choose to implement movement like this:
\nposition += position.direction_to(target_position) * delta\n\nOr maybe this way:
\nself.position = Vector2(\n lerp(self.position.x, target_position.x, delta),\n lerp(self.position.y, target_position.y, delta)\n)\n\nEither way, this should be abstracted into a method, and the action node should use the high-level method, instead of re-implementing it itself. This makes your \"go to position\" node more reusable, as it can be used by different actors, only requiring them to expose an implementation for the high-level method.
\nAs an improvement, you could use types in your implementation. This way, you make sure your actor implements the methods required by your action.
\nfunc tick(actor: Character, blackboard: Blackboard):\n # ...\n\nUsing an example from the video, you could implement an action \"follow mouse cursor\". This action would:
\nThis node is very specific, making it less reusable and harder to change. What if I want to add a maximum time for how long the NPC should follow the mouse? Or what if I decide to implement some kind of cooldown period?
\nAny change would require making this node even more complex. To prevent that, you should have small nodes with clear responsibilities.
\nEach bullet point in that list could be a node by itself. In fact, this is the end result in my example.
\nEven though smaller, generic nodes are more reusable, it's harder to start like this.
\nIt's easier to start with a very specific implementation, and then generalise it when more scenarios appear, and you can see the similarities.
\nThis is actually a piece of advice also applied to general programming, known as Rule of three.
\nAs I mentioned before, you should abstract details about your actor from your behavior tree. There are many things you don't need to have explicit in your behavior tree.
\nAs an example, if when moving to position you want to play a \"walking animation\", this should be made internally in your actor, and not like a node \"play animation\".
In my example, I ended with a tree, like this:
\n
But maybe this tree could be better represented like this:
\n
Both trees achieve the same result, but they do it in different ways. Feel free to move nodes around if it makes your tree clearer.
\nIt's good to give your nodes descriptive names, even though you may be using generic scripts. This makes it easier to understand what your behavior tree is doing.
\nA tree path:
\nsequence > load mouse position > is target nearby > go to position
is harder to understand than:
\nsequence: follow mouse > load mouse position > is mouse cursor nearby > go to the current mouse position.
I hope this text may have helped you understand a little bit more about behavior trees. Even if you decide to use an off-the-shelf solution, it's important to understand how it works under the hood.
\nAs I said before, this is a very naive and simple implementation. There are probably more robust and performant implementations out there.
\nHaving said that, this is how I implement behavior trees in my game, and it has worked well so far.
\nYou can download and fork my example from github.
\nIf you like game devlogs, you may consider subscribing to my Youtube channel.
\nThanks for reading. Comments, suggestions and feedback are always welcome.
\nSee you! Stay safe!
", "url": "https://thisisvini.com/behavior-trees", "title": "Behavior Trees and how to implement them in Godot", "summary": "I recently posted a [video](https://www.youtube.com/watch?v=1rNLTHbFFaA) with an introduction to Behavior Trees. My first draft wa…", "image": "https://thisisvini.com/images/content/bt_example_1.png", "date_modified": "2020-10-16T14:41:07.000Z" }, { "id": "https://thisisvini.com/far-star-first-devlog", "content_html": "I've been working on a game in my spare time and I decided to start publishing devlogs to track the progress.
\nIt's called Far Star. This is the first one. I hope you enjoy.
\n\nIf you like it and want to keep getting updates, consider subscribing to the channel.
\nThanks.
", "url": "https://thisisvini.com/far-star-first-devlog", "title": "I've been working on a game, this is the first devlog", "summary": "I've been working on a game in my spare time and I decided to start publishing devlogs to track the progress.\n\nIt's called Far Sta…", "image": "https://thisisvini.com/images/content/farstar_devlog_thumb.png", "date_modified": "2020-08-17T05:02:31.000Z" }, { "id": "https://thisisvini.com/responsive-mosaic-with-dnd-reactjs", "content_html": "TL/DR
\nAs a way to brush up my skills, I decided to experiment creating a simple \"Google Keep\"-like note-taking app in ReactJS. However, while building it, I couldn't find a grid library with the features I wanted and that would work well with ReactJS.
\nWhat I was looking for was:
\nI decided to create this example for anyone who is looking for something similar. Feel free to fork it and change it as you see fit.
\nFor the grid part, I found this pure CSS/JS implementation that works just fine. It uses CSS Grid for the responsive layout and JavaScript for adjusting how many rows an item should occupy.
\nTo see my ReactJS adapted grid implementation using hooks, check the grid folder in the source code.
\nFor the Drag and Drop implementation, I use a library called react-dnd. It provides the foundation and utilities for implementing Drag and Drop. I adapted their sortable example to be used in a grid.
\nYou can see my implementation in the dnd folder.
\nTo keep it simple, I handle items' order as a list, instead of a matrix, as people usually do for grids. This makes sorting easier and allows the grid to be responsive, as items can change rows without having to be sorted again.
\nYou can check the source code for tests and comments that explain a little bit more about the implementation.
\nThere are a few improvements that can be made in my example.
\nFirst of all, when dragging an element in a touch-enabled device there is no preview (ghost) element. This is due to the Touch Drag and Drop API not supporting it. There are ways to work around it by using the react-dnd-preview plugin, which allows you to define a custom ghost item while dragging.
\nAnother possible improvement is to use react-dnd-test-backend in unit tests for testing drag and drop behaviour. As I decided to not eject from create-react-app, to keep this example as simple as possible, I was not able to wire the test backend to my application. In a real project, I suggest using this test backend, as it provides many utilities and simplifies testing those components.
\nIf you want to learn more about CSS grids, I recommend this awesome guide by CSS Tricks.
\nCheck the source code for more information.
\nFeel free to reach to me if you have any questions. See you!
", "url": "https://thisisvini.com/responsive-mosaic-with-dnd-reactjs", "title": "Responsive mosaic/grid in ReactJS with Drag and Drop support", "summary": "----\n\n__TL/DR__\n- This is an example of how to implement a responsive vertical mosaic/masonry grid with drag-and-drop support in R…", "image": "https://thisisvini.com/images/content/dndgrid.png", "date_modified": "2020-05-21T13:31:11.000Z" }, { "id": "https://thisisvini.com/vim-pairing-and-good-practices", "content_html": "VIM is extremely powerful and configurable, allowing developers to set it up in a way that matches their preferences and workflow. However, this also brings a big issue: using another person's configuration can be rather frustrating.
\nThis problem is even more accentuated on a pair programming rotation with many teammates and many different setups. Some teams try to mitigate this issue by coming up with standards and default mappings, however, even though this can be an effective approach, it also sacrifices flexibility and it may undermine organic configuration improvements.
\nIn my setup, I follow a few rules that help me keep my configuration flexible and approachable. They may be handy and give you some ideas on what can be improved to make your (and your team's) life easier.
\nBefore creating a new mapping or installing a new plugin, search for how to do the same task using VIM's default commands. You'll be surprised by how many things can be easily accomplished using what comes out-of-the-box.
\nBy doing this you may get two benefits: You'll be able to work when using a vanilla VIM configuration and you will avoid bloating VIM with unnecessary mappings and plugins.
\nMy rule of thumb is: I only use a mapping or plugin if the command is too complex to remember. Everything else I prefer to keep as it comes.
\n\nEvery time you are pairing on someone's else machine, which does not have the same mappings you have, you can rely on the default commands to accomplish the job. Well, as long as they are still available.
\nI've seen many people overriding default mappings with their preferences and even plugins suggesting mappings that conflict with VIM defaults. For someone trying to use your editor, this can become annoying and frustrating very quickly.
\nAs a good teammate, you should keep the defaults working just fine so other people have a fallback in case they are not used to your mappings. Replacing a few silly mappings is fine, but changing some important navigation command is mean.
\n\nA good way to keep your configuration simple and avoid collisions is to set mappings only for commands you use frequently or require quick access.
\nWhen setting up a new mapping think on how often you are going to use it. There is a chance you will never need it or when the time comes you'd have forgotten what it does.
\n\nEvery person has a preferred workflow. I don't like to use tabs and I use mainly ctrlP to navigate between buffers. A friend of mine, on the other hand, uses mainly tabs. He uses them like an accordion, expanding and collapsing as needed.
\nWhen starting a pairing session we usually define how we are going to work. If both can use their preferred approach without messing with the others, fine. If not, choose the simplest one. It's also worth to try other people's workflow, as you may end improving yours or even enjoying the new way.
\nMaybe this one is not related to pairing, but it does help to share knowledge and avoid forgetting \"why the heck is this configuration here\". I usually add comments explaining what a plugin does or why I use a given configuration.
\nYour future self will thank you when reading your documented configuration.
\nIn summary:
\nKeeping your configuration sane and accessible helps your pairing mates to have the same joy and speed you have when using your setup. Also, make sure you share your good practices, so people can learn from them.
\n\n\nPhoto by Kevia Tan on Unsplash\n
", "url": "https://thisisvini.com/vim-pairing-and-good-practices", "title": "The problem with VIM and pair programming (and how to fix it)", "summary": "VIM is extremely powerful and configurable, allowing developers to set it up in a way that matches their preferences and workflow.…", "image": "https://thisisvini.com/images/content/kevia-tan-yxcaYNdHZro-unsplash.jpg", "date_modified": "2020-02-25T07:49:19.000Z" }, { "id": "https://thisisvini.com/ts-input-validation", "content_html": "Even though I've been working with JS in all ends for the past 4 years, it was only last year that I had the chance (and will) to use TypeScript.
\nIt's nice to work with TS as it makes debugging, inspecting and making sense of the codebase easier. However, it may give us a false sense of safety by making us forget that the type system only works until transpilation.
\nConsider the following example:
\ntype Response = {\n category: {\n description: string\n }\n};\n\n// [ ... ]\n\nconst request = await fetch(...);\nconst response = (await request.json()) as Response;\n\n// [ ... ]\n\n// category is undefined\nreturn response.category.description\n\nIn this example, we are assuming our request will return a type Response, which contains a mandatory property category with description. As this property is mandatory, accessing response.category.description should be safe, as category should never be undefined. However, we can't guarantee this will be always true as it's coming from an external source.
Other typed languages, like Rust and Java, force us to handle those scenarios upfront, either by defining exception paths, default values or by explicitly assuming the risk of parsing issues. TypeScript chooses to trust the developer instead. Because of that, if we don't add proper input validation a bad input could go down many levels before it \"explodes\", making spotting issues very hard.
\nI've seen at least two issues caused by bad input validation since I started working with TS. Since then, I've been treating TypeScript types as a hint for developers during development, and not as a full-blown type system.
\nAll things considered, working with TypeScript has been very satisfying, and I recommend giving it a chance. :)
", "url": "https://thisisvini.com/ts-input-validation", "title": "TypeScript won't protect you from bad input", "summary": "Even though I've been working with JS in all ends for the past 4 years, it was only last year that I had the chance (and will) to …", "date_modified": "2019-09-03T10:20:52.000Z" }, { "id": "https://thisisvini.com/rigid-body-vs-kinematic-body", "content_html": "Kinematic and Rigid bodies are two common nodes for dealing with physics and collisions. The fundamental difference between those two types is that RigidBody is influenced by physics, while KinematicBody is not. This impacts how you move and handle collisions with each one of them.
\nThe only way to move a Rigid body is by applying forces to it, such as impulse, torque, gravity and friction. The physics engine is responsible to calculate the resulting movement and its new position. This makes the movement more realistic, but also harder to control.
\nAs an example, if a body is moving to left, in order to make it go right you need to apply an impulse in the reverse direction. However, before moving to the new direction the body will first deaccelerate to a stop. This may look realistic, but for a user-controlled object, it could be frustrating as controls won't feel as responsive as they should.
\nKinematic bodies, on the other hand, are not affected by physics. This gives you freedom two do whatever you want, with the downside of having to implement everything manually. For most games, this is not an issue at all, as probably they would require a simplified and sometimes crooked version of physics.
\nRigid bodies are always detecting collisions and notifying it through signals. For instance, every time a body touches a RigidBody it emits a body_entered signal, and a body_exited when the contact stops. This makes the implementation easier as you know those signals will be triggered on every contact.
In contrast, Kinematic bodies do not notify collisions unless a movement is made. When moving it, you need to check if a collision happened either by moving it using move_and_collide or calling get_slide_collision afterwards. Because of that, you will have to design your code in a way it can handle scenarios where your node is being hit while stopped.
Rigid bodies are usually used in 3D games or games that require realistic physics simulation. Kinematic bodies are mostly used in 2D games and user-controlled bodies. Your necessities will define which one fits best your game.
\nLeave a comment if you have something to add or suggest. Thanks.
\nDocumentation: Kinematic Body: 2D, 3D | Rigid Body: 2D, 3D
\n\nPost image from Godot Docs\n
", "url": "https://thisisvini.com/rigid-body-vs-kinematic-body", "title": "Godot: RigidBody vs KinematicBody", "summary": "Kinematic and Rigid bodies are two common nodes for dealing with physics and collisions. The fundamental difference between those …", "image": "https://thisisvini.com/images/content/kbscene.png", "date_modified": "2019-06-05T10:01:20.000Z" }, { "id": "https://thisisvini.com/convert-video-to-gif-tool", "content_html": "Some time ago I created a helper to convert videos to gif using ffmpeg and gifsicle. This script provides a set of options through a simple interface and, in case you don't have those libraries installed, it does everything inside a docker container.
\nIf you are not interested in hearing about this tool, here is a simple command that does the job:
\n ffmpeg -i [input video] -f gif - | gifsicle --optimize=3 > [output gif]\n\nHowever, if like me, you don't like to install random libraries and their dependencies in your main system, or maybe you just find this command too hard to remember, keep reading and I will tell you a bit more about it.
\nThose are all the options available in video2gif:
\nvideo2gif [OPTIONS] [input file]\n\n-s, --size size. e.g 600x400. Default: same as video size\n-o, --output output file name. Default: [input].gif.\n-i, --input input file.\n-ts, --start-time time position from video to start gif. Seconds or HH:mm:ss. Default: start of the video\n-te, --end-time time position from video to stop gif. Seconds or HH:mm:ss. Default: end of the video\n-d, --gif-frame-duration delay/duration of each Gif frame in hundredths of a second. Default: 3.\n-fr, --video-frame-rate video frame rate. Less frames generate smaller gifs. Used by ffmpeg. Default: 10.\n-h, --help print help message.\n-v, --version print version.\n\nEverything you do with video2gif you can do with ffmpeg and gifsicle. Because of that, I will show you how to use both commands, so you don't need to download my tool if you don't want to.
\nvideo2gif
\nvideo2gif -s 600x400 input.mpeg\n\nffmpeg + gifsicle
\n ffmpeg -i input.mpeg -s 600x400 -f gif - | gifsicle --optimize=3 > output.gif\n\nvideo2gif
\nvideo2gif -ts 1:05 input.mpeg\n\nffmpeg + gifsicle
\n ffmpeg -ss 1:05 -i input.mpeg -f gif - | gifsicle --optimize=3 > output.gif\n\nNote: -ss needs to come before -i, otherwise it won't work.
video2gif
\nvideo2gif -te 3:00 input.mpeg\n\nffmpeg + gifsicle
\n ffmpeg -to 3:00 -i input.mpeg -f gif - | gifsicle --optimize=3 > output.gif\n\nNote: as in the previous example, argument position matters.
\nDuration is defined by hundredths of a second, so 3 is equivalent to 0.03 second.
\nvideo2gif
\nvideo2gif -d 3 input.mpeg\n\nffmpeg + gifsicle
\n ffmpeg -i input.mpeg -f gif - | gifsicle --optimize=3 --delay 3 > output.gif\n\nFewer frames generate smaller gifs, with the expense of making it choppier.
\nvideo2gif
\nvideo2gif -fr 10 input.mpeg\n\nffmpeg + gifsicle
\n ffmpeg -i input.mpeg -r 10 -f gif - | gifsicle --optimize=3 > output.gif\n\nvideo2gif
\nvideo2gif -ts 10 -te 1:10 -s 400x200 -d 3 -fr 10 input.mpeg\n\nffmpeg + gifsicle
\n ffmpeg -ss 10 -to 1:10 -i input.mpeg -s 400x200 -r 10 -f gif - | gifsicle --optimize=3 -d 3 > output.gif\n\nAs you can see, it is not that hard to use ffmpeg+gifsicle, however, if you do this conversion frequently you may consider using my script.
\nI hope it was useful. To know more about how it works, you can check the repository.
", "url": "https://thisisvini.com/convert-video-to-gif-tool", "title": "Converting videos to gif with a dockerised helper", "summary": "Some time ago I created a helper to convert videos to gif using [ffmpeg](https://ffmpeg.org) and [gifsicle](https://www.lcdf.org/g…", "date_modified": "2019-05-03T14:06:12.000Z" }, { "id": "https://thisisvini.com/coc-intelisense-nvim-vim", "content_html": "Some months ago I wrote about using ALE for navigating through code with help of language servers. That works really well and I was totally satisfied, however I decided to give Neovim a try and in the process I found a plugin that took the game to another level: Coc (Conquer of Completion).
\nCoc is a completion framework and a language server client. It's fast, responsive and provide full LSP support. I've been using it for a little more than a month and I'm really impressed by its performance.
\nThe only downside I can see so far is the number of dependencies required. Coc requires Node and Yarn to work, besides those I had to install a Neovim python dependency and if you are using Vim it requires a vim-node-rpc plugin as well. In general I don't like plugins with too many external dependencies, however I feel this time it worths the hassle.
Here are some examples of Coc in action:
\nCompletion\n
\nThe completion prompt has been really fast and responsive so far.
Go to definition\n
Show references\n
\nPreview pane on references list is very useful.
Rename\n
\nI was able to rename variables/methods (and their references) effectively, even in JavaScript.
Show buffer's issues in a list\n
Check the project repository for a more detailed explanation about the tool as well as some configuration examples.
", "url": "https://thisisvini.com/coc-intelisense-nvim-vim", "title": "Coc - Intelisense for Vim and Neovim", "summary": "Some months ago I wrote about using [ALE for navigating through code with help of language servers](https://thisisvini.com/vim-bet…", "image": "https://thisisvini.com/images/content/coc_logo.jpg", "date_modified": "2019-04-13T03:53:00.000Z" }, { "id": "https://thisisvini.com/you-should-learn-more-than-one-programming-language", "content_html": "These days I was thinking how some people can be so fanatic about programming languages. They choose one language to learn and all of sudden all other languages become garbage. Probably you've heard people saying things such as \"C# > Java\", \"You can't make a real project in JavaScript\" and \"Goodbye X hello Y\". That's just sad.
\nThere is nothing wrong enjoying to work with one specific programming language and specializing in it. The problem is when you start looking down to other technologies and close yourself to alternatives.
\nEvery language has a strong and a weak suit. If you rely on one single language you will be bounded by its weaknesses. \"If all you have is a hammer, everything looks like a nail.\"
\nLearning a new language may bring challenges, but it also brings a new ecosystem with different patterns, structures and techniques. A good example to think about is how different languages deal with same situations such as asynchronicity, IO, mutability, concurrence, parallelism and persistence.
\nBut can you be a good programmer not focusing in one single language? Sure. However in the end all boils down to one word: balance.
\nUnderstanding deeply how one specific language works is as important as knowing how to code in different languages. First you focus, then you expand.
\nWith time every interface bureaucrat recognise the benefits of dynamic languages and every duck typing hippie admits the power of strongly typed languages.
\nThanks for reading. I know this subject can have many branches, but for now I'm just leaving this simple thought. Comments and suggestions are welcome.
\n\nPhoto by Rock'n Roll Monkey on Unsplash\n
", "url": "https://thisisvini.com/you-should-learn-more-than-one-programming-language", "title": "Be a polyglot", "summary": "These days I was thinking how some people can be so fanatic about programming languages. They choose one language to learn and all…", "image": "https://thisisvini.com/images/content/rock-n-roll-monkey-681546-unsplash.jpg", "date_modified": "2018-10-31T07:00:00.000Z" }, { "id": "https://thisisvini.com/vim-better-go-to-definition-completion", "content_html": "Probably you've already heard about ALE, an asyncronous lint engine that analyse buffers in background showing the results without requiring saving files to disk.
\nJust recently I found out that ALE allows you to use features like \"Go to definition\", \"Find usages\" and completion suggestions using the power of LSP (Language Server Protocol) linters.
\nI've been using it for Rust and JavaScript and it has been working better than CTAGs ever worked for me.
\nHere are some examples:
\nAutocompletion:
\n
Go to definition:
\n\nAfter installing ALE the only required configuration for enabling autocompletion hijacking is let g:ale_completion_enabled = 1.
For Rust you need to install RLS (Rust Language Server). The catch here is that ALE uses Rust Nightly by default. You can choose between installing RLS in the nightly toolchain or change ALE configuration to use stable instead.
\nFor JavaScript I installed typescript globally (npm install -g typescript). It comes with tsserver included, which is the default engine in ALE for JS.
There may be other plugins powered by LSP's that provide same features, but as ALE was something that I was already using and it was so easy to configure I didn't want to spend time looking for alternatives.
\nTake a look on my VIM dotfile for configuration details.
\nSuggestions and questions are welcome! :D
", "url": "https://thisisvini.com/vim-better-go-to-definition-completion", "title": "VIM: Better \"Go to definition\" and completion using ALE", "summary": "Probably you've already heard about ALE, an asyncronous lint engine that analyse buffers in background showing the results without…", "image": "https://thisisvini.com/images/content/autocompletion.jpg", "date_modified": "2018-10-04T12:04:00.000Z" }, { "id": "https://thisisvini.com/the-bohemian-rhapsody-stream", "content_html": "This is a quick one.\nWhile playing around with Server-Sent Events I've created this page to consume my Bohemian Rhapsody stream.
\nBefore building this page I had created a stream that sends Queen's Bohemian Rhapsody lyrics as messages.
\nThose messages are sent obeying the time they would appear in the song. What this means is if you press play when the first verse is received you are able to listen to the song synced with the lyrics.
\nhttp://playground.thisisvini.com/bohemian-rhapsody-event-stream
\n
I intend to publish both server and client's source code as soon I have time for this.
\nThe server was made using NodeJS (I need to improve it a little bit).
\nThe client uses the native implementation of EventSource to consume the stream. This is how it looks like:
const eventSource = new EventSource(STREAM_URL)\n\neventSource.onmessage = (message) => {\n container.innerHTML = message.data\n}\n\neventSource.onerror = (error) => {\n container.innerHTML = 'Sorry! Something went wrong with the stream'\n}\n\neventSource.addEventListener('started', (e) => {\n status.innerHTML = 'Playing...'\n})\n\neventSource.addEventListener('stopped', (e) => {\n status.innerHTML = 'Stopped'\n})\n\nThis is a minimum implementation for testing the stream.
\nevent field defined.started. Every time a started is received the label in the bottom right corner of the page is changed to \"Playing…\". The same principle applies for the next listener.This is how the raw stream looks like:\n
To avoid complications I decided to set some limits to this stream:
\nI don't think usage will ever get close to those numbers.
\nServer-Sent Events is supported by most of the browsers.
In case you need support for other/older browsers you can use this polyfill.
\nIf you want to dig a little bit more in Server-Sent Events maybe this post could be of your interest.
Server-Sent events is a standard that describes a way for servers to send updates to clients through a persistent HTTP stream.
\nSSE doesn't rely on any new technology. Instead, it uses features already existent on HTTP.
\nThis is an example of how an event-stream response looks like:
HTTP/1.1 200 OK\nContent-Type: text/event-stream\nCache-Control: no-cache\nConnection: keep-alive\nTransfer-Encoding: chunked\n\n: Welcome! this is a comment.\n\nid: 1535462271572\ndata: my first message!\n\nid: 1535462271572\ndata: another message\nevent: my_event\n\n[...]\n\nIn a event stream all information is sent as plain text in UTF-8. Each event is separated by an empty line and each line defines a field. The standard describes how clients should react to those messages and how to parse them. This is made through an interface named EventSource, which looks like this:
// JavaScript\nconst eventSource = new EventSource('http://stream/sub');\n\neventSource.onmessage = function(e) {\n console.log(e.data);\n};\n\neventSource.addEventListener('my_event', function(e) {\n console.log(e.data);\n});\n\nAfter the first handshake no messages are sent from client to server, only the other way around. This means event streams provide one-way communication where the server talks and all clients listen.
\nEvery client receive the same data in the same order. When a new client connects, it receives all the messages sent in the stream before it's connection.
\nIn case of failure, the standard describes a way for clients to retrieve the stream starting from the last event received. The standard doesn't cover questions like message expiration or limits. It's up to the server to handle those questions.
\nThose are just some highlights. The standard contains a lot more about how to handle reconnections, redirects, failures and messages.
\nAs I said before, SSE relies on features already existent in the HTTP implementation. Let's check how they are used.
\nIn order to easily explain how SSE works let's remember, in a simplified way, how a normal HTTP request flow works.
\nLet's say you want to access the resource https://thisisvini.com/about/. This is what happens between your client/browser and the server:
As the server is listening to incoming connections when the client contacts it a TCP socket is opened between them. After the connection is established the following conversation happens:
\nClient:
\nGET /about/ HTTP/1.1\nHost: thisisvini.com\nAccept: */*\n\n\n\n\"Yo! Could you give me the resource
\n/about. By the way, I know how to communicate using HTTP/1.1, and to be honest, I don't really care about the type of the content you'll send me.\"
Server:
\nHTTP/1.1 200 OK\nContent-Type: text/html; charset=utf-8\nContent-Length: 7915\n\n<html>...\n\n\n\n\"Sure thing! I can do that. I'll send you HTML. Here it is […] See you later\"
\n
The content is streamed to the client in chunks of data. After all the content is transmitted the socket is closed by the server and this polite conversation is over.
\nAs we can see, TCP and HTTP already allow us to send chuncked data and keep a stream open between server and client. The missing part was defining a standardised way for servers and clients to deal with communication through a long-lived stream.
\nThe only real difference between the previous flow and an event stream is that instead of closing the connection, the server would keep it open sending new chunks of data when necessary.
\nObviously there are more details and bureaucracy in this conversation. You can check the living standard for more details.
\n
If you want to see a real stream working you can curl my Bohemian Rhapsody stream with the following command:\ncurl -i -k -H \"Accept: text/event-stream\" http://playground.thisisvini.com/bohemian-rhapsody-event-stream
This stream sends messages in sync with Bohemian Rhapsody song. Just press play when the [START] message is received and sing together. :P
I recently implemented a SSE-Client in Rust for a side project.\nThis library is fully functional, following the living standard and even implementing an exponential back off for reconnections.\nSource code\nDocumentation
\nSSE is really good for sending live data and broadcasting updates for many clients.
\nThings to keep in mind:
\nThat's all for now. Thanks for reading.
\nIf you have something to add, questions or suggestions don't exitate to contact me.
", "url": "https://thisisvini.com/server-sent-events", "title": "Server-Sent Events", "summary": "Server-Sent events is a standard that describes a way for servers to send updates to clients through a persistent HTTP stream.\n\nSS…", "image": "https://thisisvini.com/images/content/event_stream.jpg", "date_modified": "2018-08-30T08:53:00.000Z" }, { "id": "https://thisisvini.com/pros-and-cons-godot-game-engine-and-others", "content_html": "For the last 2 years I've been working on a 2D game and Godot was the engine I chose for this job.
\nAlthough I've never released a game before, I did experiment with other engines and technologies along the years. That's why I decided to summarise here my point of view of Godot's pros and cons and why I chose it over other options available.
\nUnfortunately I can just talk about the 2D part of Godot, as I haven't really tried the 3D engine. You can read more about Godot features here.
\nEverything in Godot is a node, and the proper way of communicating between nodes is through signals. In the editor you can easily embed scenes or extract a group of nodes to a new scene. This modularized orientation allows a good level of decoupling and makes creating, changing and supporting your game easier.
\nFar from having the best code editor ever, Godot editor does a decent job. Auto-completion is complete and useful, it is easy to find errors and switch between the graphic part and its related scripts.
\nGDScript, Godot's official language, is simple and easy. It's based on Python, a language known as being beginner friendly. Its dynamic nature makes it a good fit for scripting and spiking.
\nAnother good thing is that you are not limited to GDScript. Godot has support for other languages such as C# and C++, besides the community-provided support for a few others.
\nGodot editor runs in all major platforms and exports games for mobile, web and desktop. Tested and approved!
\nIt is amazing how many answers you can get with a simple search. Godot community is very active and growing everyday. You can dig the forum or look into the documentation which is usually easy to search and mostly complete.
\nAll Godot files are plain text. This makes solving version control conflicts an easy task. It is relatively easy to understand what changed only looking at the diff.
\nBesides all the benefits we get from an open source tool, Godot counts with a small team of skilled people working full time on the project. Every release come with many bug fixes and cool features and it is always transparent for users and contributors.
\nSometimes when reviewing my changes before commiting I see differences in files that I didn't change. This is due Godot storing information about my workspace (such as nodes expanded or collapsed) in the scene's file.
\nIt is useful to come back to the editor and find nodes in the way you left them, however I can see the hassle it would be for developers working in teams where every time you commit something you need to review which files are changes and which ones are just noise.
\nGUI development always sucks. It's boring and annoying. Godot does a decent job here, but it's not pleasant. If you don't structure your elements carefully they will expand and shrink in the craziest ways.
\nAs most of the game engines, there is no easy way of testing your scripts. Given testing is one of the best tools for designing complex scripts, it would be nice to have a way of keeping them inside the IDE workflow.
\nI see why writing tests are not the focus for game engines given the fact that many things are done using the interface and scripts are usually procedural, short and simple. I agree in those cases testing would be contra-productive.
\nMy workaround when developing complex scripts is to make them in a different language outside my project and then translating them to GDScript. I'm talking about pure logic scripts. I don't think you should test things that interact with Godot's API.
\nIn the post \"How to make your dream game, publish it and not die in the process\", Juan Linietsky, Godot's lead developer, explains Godot's philosophy and his point of view of game development. Tests are not one of the topics of this post, however it makes clear why their absence shouldn't be a problem.
\nWhether you are an experienced developer or a beginner I do recommend trying Godot. Keep in mind an engine always come with a learning curve and some constraints, however Godot still enjoyable.
\nDisclaimer: Even though these engines didn't fit my scenario, this doesn't mean they are bad or worse than Godot.
\n
Cocos2D-x is an open-source, multi-platform framework.
\nThe main problem was that I got the worst of two worlds: I didn't have all the benefits of a full engine workflow and at the same time I was not as free to work in my way. I was constantly looking into the documentation and trying to understand why things didn't work.
\nTo make things worse, some times the only help I could find was all written in Chinese.
\nTo be fair, I used Cocos2D-X more than 5 years ago, and things probably improved since then. Another thing to point is actually their focus seems to be mobile games.
\n
Unity has a big community and the many resources available make it easy to get in. Having said that, developing 2D games in Unity always felt hacky to me.
\n2D in Unity is actually 3D with a fixed camera. I lost the count how many times I had to solve issues that where just things in wrong perspective.
\nAmong things that made me look for something else are: Unity is heavy, consumes a lot of resources, you need to download updates very often and there is no Linux editor. (Update: On mid 2019, a Linux version was release for Unity editor)

Defold looks really good. To be honest I didn't develop on it, because when I started for the first time they asked me to login and save my files in their servers. The Defold catch is that it was made to work using King's servers.
\nThere is a way to use your own version control, but after reading I realised it wouldn't be that simple.
\nThere are other possible downsides such as the small community and being a proprietary engine maintained by a small team(Update: On May 2020, Defold was transferred to Defold Foundation and all source code was made available). But I confess what scarred me most was the lack of freedom in the way you develop and organise your application.

Gamemaker Studio is a heavy weight of game engines with a impressive showcase. The reason I didn't follow up with it is simple and for many people probably silly. Their support for Linux is limited to Ubuntu only.
\nI've been using Linux as my main operational system for more than 10 years and making a game that runs in Linux distros, Windows and OS-X is always one of my goals.
\nI know people will say that moneywise Linux is not a good target, but for me it is more a matter of principle than anything else.
\n
MonoGame is an open-source implementation of Microsoft XNA 4 Framework. You can make games for many different platforms, including PS4 and XBox One. Many successful indie games were made using MonoGame such as Fez, Bastion, Tower Fall and StarDew Valley.
\nWhat turned me off was all the requirements, confusing documentation and the heavy dependency on other softwares such as Visual Studio and Xamarin.
\n
LÖVE is a cute, multi-platform, and open source framework for 2D games. Not much to say. It was just too limited for what I was looking for.
\nThe best engine for you will depend on your goals. First define what you want to achieve and then think how an engine would be able to help you.
\nFor me Godot is serving well. I still want to make a game from scratch eventually, however it requires a big amount of work and my goal now is just to release my first game.
\nI appreciate feedback. If you have something to add, a different point of view, comments or suggestions leave them bellow. Thanks!
", "url": "https://thisisvini.com/pros-and-cons-godot-game-engine-and-others", "title": "My experience with Godot and other engines", "summary": "For the last 2 years I've been working on a 2D game and Godot was the engine I chose for this job.\n\nAlthough I've never released a…", "image": "https://thisisvini.com/images/content/godoteditor.jpg", "date_modified": "2018-07-10T11:06:00.000Z" }, { "id": "https://thisisvini.com/sometimes-it-is-ok-to-not-write-tests", "content_html": "We spent a long time trying to convince people that automated tests are important. It's good to see that nowadays things changed and our discussions are more about the quality of our tests than if we are doing them or not.
\nHowever, with experience, we realize that not everything requires tests and in some cases, it's better to not have them at all. Madness? No, this is being pragmatic.
\nIf you are reading this probably you already know about tests, but in case you are just starting and trying to see the value of it I'll list some benefits I see on writing tests:
\nWith those benefits why would you prefer to not write tests? Well, it's all about trade-offs.
\nSpiking is a good way to get familiarized with a new technology or finding answers for assumptions. Here, you'll want to go dirty, hack and slash, copy and paste and make it work. Starting by testing will only delay your discovery process. You would be fighting test frameworks and losing focus from what you want to validate.
\nIt is a good strategy to isolate your code from third-party code. Having extra layers on top of libraries may feel overkill, but they pay off making changes, updates and even replacing dependencies easier.
\nWriting unit tests for this layer requires many stubs, mocks or fake implementations, which are difficult to maintain and to keep in sync. There is still the option of integration or E2E tests, however, they are costly and in some cases flaky.
\nYou should never test something that you don't understand. A test based on assumptions may cause more harm than good.
\nTesting legacy code is painful, difficult and not efficient. The best strategy is isolating the code and forgetting about it.
\nI know! It annoys you knowing there is that dark place in your codebase. Every new developer will try to refactor it failing miserably or creating more legacy code and bugs in the process. Some people will even suggest rewriting the whole application, but they will fail as well. Just keep in mind that if it doesn't affect your performance and your ability to change, it is better to let it be.
\nCreating tests for legacy code only creates a false feeling of safety. People will think it's ok to refactor it because it is covered by tests. Instead of putting a lipstick in the legacy pig, it is better to write a big sign saying \"Abandon hope all ye who enter here\".
\nOk, You caught me. All the items before are all about this one. In summary, it is ok to not write tests when writing tests doesn't make sense.
\nIt is not about coverage, but the quality of the tests you have. By quality, I mean tests should be easy to maintain and easy to change. They should be consistent and ensure that your code is working properly. If you can't ensure the quality of your tests, maybe you should try a different approach.
\nHaving said that, tests are an important tool. In most cases not being able to write them properly could mean that something else is wrong, which may require your attention.
\nThe decision of not writing tests shouldn't be a stigma as long it is conscious and supported by good arguments.
\nThat's all for now. If you have something to add, a different point of view, comments or suggestions, leave them below. Thanks!
\n\n Photo credit: Internet Archive Book Images on Visualhunt\n
", "url": "https://thisisvini.com/sometimes-it-is-ok-to-not-write-tests", "title": "It's ok to not write tests sometimes", "summary": "We spent a long time trying to convince people that automated tests are important. It's good to see that nowadays things changed a…", "image": "https://thisisvini.com/images/content/woman_fixing_machine.jpg", "date_modified": "2018-06-06T11:17:00.000Z" }, { "id": "https://thisisvini.com/lets-get-started", "content_html": "Hi, I'm Vini and this is my first blog post.
\nThe main reason I had never thought about blogging before was the amount of content out there. Good or bad, there are so many tutorials, opinions and reviews on the Internet. I didn't feel I should add to this blob of data only for the sake of getting attention.
\nNowadays you can solve most of your problems searching for the solution on the Internet. However sometimes you face a situation where you have a simple issue and all the solutions you find are not valid for you, probably because of the many variables involved.
\nAfter reflecting about this I realized that maybe all the things I didn't write about could had helped someone with the same constraints I had.
\nThis is why I'm starting now. I'm not saying this will change people's lives, but if at least one person find something interesting in this dark corner of the Internet, it is enough for me.
\n\nPhoto credit: RAETHIER on Visual hunt / CC BY-NC-SA\n
", "url": "https://thisisvini.com/lets-get-started", "title": "Let's get started", "summary": "Hi, I'm Vini and this is my first blog post.\n\nThe main reason I had never thought about blogging before was the amount of content …", "image": "https://thisisvini.com/images/content/visual_hunt_start_here.jpg", "date_modified": "2018-04-20T23:02:00.000Z" } ] }