If you’re looking to create your own games, you should definitely take into consideration making your own RPG. RPGs present you too with the opportunity to expand your coding and game design skills – as they incorporate lots of different game mechanics. However, no RPG really works without a UI, so this can’t be neglected as you develop your ideas.
In this Godot tutorial, we’re focusing on the implementation of UI elements for your RPG game. Here, we’ll be setting up a health bar together and we’ll also style a text label in order to display the amount of gold we have in the game.
Let’s dive in!
In order to be able to follow along with this tutorial, you’ll need to download the assets found here. You can also download a copy of the source code files for the project done in the tutorial by clicking here.
Let’s start by creating the UI for our RPG game.
Let’s go up to Scene > New Scene:
We’re going to create a root node of ‘User Interface‘:
We’ll rename the node to “UI” and save the scene as “UI.tscn“:
Let’s right-click on our UI node and add a child node of type TextureProgress:
A TextureProgress is a bar, which can fill up a certain amount based on a given value:
In the Inspector, we’ll drag in the image for our UI texture (UI > UI_WhiteSquare.png). into Textures > Under and Textures > Progress:
We’ll also set Tint > Under to dark grey, Tint > Progress to red, and Range > Value to 50 so we can see that the progress is 50% of the way.
Enable ‘Nine Patch Stretch‘ so that the bar can be stretched to fit the size of the border:
We’ll click and drag the anchor point down to the bottom left, so the health bar is snapped to the bottom left corner of the screen regardless of the screen size:
Let’s create a new label node as a child of UI, and rename it to “GoldText”:
Then position it above the health bar and extend it out sideways:
We’ll give it a sample text (“Gold: 500”) and align the text to the center:
Let’s right-click on our font file (Font > Ubuntu-Regular.ttf), and click on “New Resource…“:
… and create DynamicFont and save it as “Ubuntu-Regular.tres”:
We’ll set the font size to 30 in the Inspector, and drag our font file (Ubuntu-Regular.ttf) into Font Data:
Now, let’s select the GoldText node, and assign a custom font here by dragging in the dynamic font (Ubuntu-Regular.tres) into Custom Fonts:
Make sure that the anchor point of this node is also positioned at the bottom left corner of the screen:
Now that our UI is all set up, let’s go back to our main scene and create a new node of type CanvasLayer:
Let’s drag in our UI (UI.tscn) as a child of the CanvasLayer:
Our UI is now rendered on our screen:
In the UI Scene, let’s go to our UI node and create a new script:
First of all, we’re going to be creating variables to get access to our UI nodes (get_node):
extends Control onready var healthBar = get_node("HealthBar") onready var goldText = get_node("GoldText")
Now we need to create a function for each of these variables, which will update their values.
Let’s update our health bar first. Note that the value of the health bar is a number between zero and 100. (0% ~ 100%):
extends Control onready var healthBar = get_node("HealthBar") onready var goldText = get_node("GoldText") # called when we take damage func update_health_bar (curHp, maxHp): healthBar.value = (100 / maxHp) * curHp
We divided 100 by our maxHP to get the percentage value and then multiplied it by curHP to get our current health.
Let’s update our gold text now. Make sure to include the text and the colon (“Gold :“) before the number:
extends Control onready var healthBar = get_node("HealthBar") onready var goldText = get_node("GoldText") # called when we take damage func update_health_bar (curHp, maxHp): healthBar.value = (100 / maxHp) * curHp # called when our gold changes func update_gold_text (gold): goldText.text = "Gold: " + str(gold)
Next, we need to connect this up to the actual Player node. Let’s open up our Player script:
… and create a variable to get access to our UI node:
onready var ui = get_node("/root/MainScene/CanvasLayer/UI")
We need to initialize the UI inside of the ready() function for us to see the current values instead of the default values:
func _ready (): # initialize UI ui.update_health_bar(curHp, maxHp) ui.update_gold_text(gold)
Make sure that the UI node is placed at the top of the hierarchy as it’ll return an error otherwise. This is needed because Godot initializes and updates all their nodes from top to bottom:
The UI node must be placed above the Player node in order for the player script to access it.
Finally, we have to update our UI values as we play. For that, let’s get our UI node to update the HP bar inside take_damage function:
func take_damage (damageToTake): curHp -= damageToTake ui.update_health_bar(curHp, maxHp) if curHp <= 0: die()
We also need to update the gold text inside give_gold function:
func give_gold (amount): gold += amount ui.update_gold_text(gold)
Now if we press play, you can see that our health starts to go down if we get attacked, and our gold goes up each time we collect a coin as expected:
Well done on completing this tutorial!
Here, you’ve learned how to implement the UI for your RPG game in Godot, something you can surely expand upon and also use in other game projects as well. This UI is also useable for any kind of subgenre you want to pursue as well – whether you want to go for an action RPG like we did or something more traditional and turn-based.
However, there’s a lot more to expand upon here even in the realm of UIs! Perhaps you want to create a second bar for mana, add a frame to your health bar, or even change the health bar’s shape entirely. There are endless possibilities even in this realm – in addition to learning tons of other core mechanics!
We hope that you have fun as you continue developing your games and wish you the best of luck in your future projects!
Want to learn more about RPGs in Godot? Try our complete Develop a 3D Action RPG with Godot 3 course.
]]>
RPGs are one of the oldest and best-known game genres, taking in the likes of Final Fantasy, Pokemon, The Elder Scrolls and a variety of other famous series. The appeal of creating your own RPG is the ability to craft your own fantastic world and tell an epic story – but to do that, you first need to know how to actually code an RPG.
We’ve compiled some of the best RPG tutorials available to help you start your journey into creating your very own RPG. Whether you’re a novice coder or an experienced game-maker looking to expand your skill set, these tutorials cover all the skills you’ll need to craft your first RPG.
The games industry is also an absolutely huge sector and offers a wealth of opportunities for up-and-coming coders – especially when it comes to RPGs. The global RPG market is expected to be worth nearly $22.5 billion by 2023, clearly showing just how lucrative a career in RPG development can be!
RPGs also happen to be one of the most successful genres for solo and indie game developers – you don’t have to be part of a large studio to create a fantastic and successful RPG. The huge success of indie titles like Undertale, CrossCode and Disco Elysium clearly shows the potential for indie RPG developers to make a huge splash with their games.
Aside from the career benefits of learning to make your own RPG, they also present a great opportunity to expand your coding and game design skillsets. RPGs incorporate many different game mechanics and systems like world design, NPC programming, battle mechanics, quest systems, inventories and more, all of which help you learn valuable transferable skills to use in other game design projects.
More than anything, creating your own RPG is fun! Designing and creating an RPG is a great way to let your imagination run wild, creating fantastic worlds and epic stories to share with others.
The following list includes some of the best tutorials currently available for learning the core mechanics of RPG games and how to code them.
Most of these are designed for beginners – while some of them might require existing knowledge of certain tools or programming languages, they do a great job of introducing you to the basics of RPG design and providing a step-by-step guide to building your own RPG.
The 2D RPG Academy and 3D RPG Academy are a set of comprehensive RPG design curriculums from Zenva Academy. The aim of the curriculums is to learn to create your own 2D and 3D RPGs (respectively) from scratch in Unity, teaching you to implement battles, NPCs, dungeons, and more. The RPG Academies cover everything you need to know about building RPGs in Unity, taking you from an RPG novice to a master of the craft by the end of their individual course sets.
These curriculums are a great option for those who are just starting out with game design, since it covers the basics of game development in general in its first module before moving onto RPG-specific systems in subsequent sections. The curriculums are structured to walk you through world design, core gameplay systems and other essential features for building your own RPG.
Key Topics Covered:
Duration: 25 hours of video tutorials across a wide range of topics
Created by Unity tutor Epitome, the “Learn Unity Engine and C# by creating a real top down RPG” tutorial is an extensive guide to creating a top-down RPG in Unity from start to finish.
It’s a comprehensive guide that takes you through absolutely everything you need to know about creating your own top-down RPG, from the initial installation and setup of Unity to implementing core RPG systems. Later sections also focus on improving the player experience by tightening up UI and other features, as well as adding new content to extend the length of the RPG.
This tutorial is slightly older than others on this list, having first been published on Udemy in 2017 before being made available for free on Epitome’s YouTube channel in 2021. However, it still coves a lot of useful subjects that budding RPG makers will find extremely useful when working on their own projects.
Key Topics Covered:
Duration: A 7 hour, 45 minute-long video broken up into segments focusing on specific areas of RPG development.
If you’re someone who’s already gained some experience in coding and game design and you’re looking for a shorter tutorial to learn some of the core mechanics of RPGs, then the Construct A Micro-Farming RPG tutorial may be the guide for you.
This slightly shorter tutorial allows you to follow in the footsteps of classic series like Harvest Moon and recent hits such as Stardew Valley by creating your own farming-based RPG. The tutorial delves into various systems and mechanics prevalent in the popular farming RPG subgenre such as crop growing, scriptable objects, tilemaps and resource management.
By the end of the tutorial, you’ll have created your own micro-farming RPG project, which can make for a great addition to your portfolio. The skills you learn in this farming RPG tutorial can also help you to diversify and expand your future projects with new mechanics and systems.
All you’ll need to successfully complete this course is a basic understanding of Unity and C#, making it an ideal course for game design beginners and pros alike.
Key Topics Covered:
Duration: About 1 hour, 20 minutes of video tutorials covering farming RPG systems
Another option for those who are looking for a shorter tutorial is the “How to make an RPG in Unity” series from Brackeys and Sebastian Lague. This tutorial series breaks down RPG development into a series of videos exploring core subjects, making it easier to get to grips with the basic skills needed to successfully create an RPG in Unity.
The series is split into gameplay tutorials delivered by Brackeys and Sebastian Lague’s tutorials on integrating graphical elements into your RPG, with further RPG graphics tutorials also available on Lague’s own channel.
The gameplay tutorials cover subjects like player and camera movement, interactables and items, inventory systems, equipment, character stats and more. Meanwhile, the graphics tutorials teach you how to model, rig and animate simple characters in Blender, then integrate these and other graphical elements into your RPG.
Key Topics Covered:
Duration: Just over 3 hours of video tutorials organised in a dedicated YouTube playlist, plus additional graphics tutorials on Sebastian Lague’s channel.
Turn-based combat is a staple feature of the RPG genre, made famous by classics like the early Final Fantasy games, Pokemon, and more. To this day, it remains one of the most popular battle systems used in RPGs, so it makes sense to learn how to implement turn-based battle mechanics if you want to build your own RPG.
That’s where the Create A 3D Turn-Based RPG tutorial comes in. This tutorial helps you to master turn-based battle systems by building your own 3D RPG from scratch, teaching you to program different kinds of attacks and actions, customize enemy encounters and more.
The project is fairly open-ended, giving the scope to create a larger 3D RPG project off the back of this course or simply to use it to create a smaller project and add new skills to your arsenal. All you need to get started is some existing knowledge of C# and Unity; the course will walk you through everything else.
Key Topics Covered:
Duration: Roughly 5 hours, 20 minutes of video tutorials guiding you through the project from start to finish.
Most of the tutorials in this list are for the hugely popular Unity game engine; however, if you prefer to use other engines, then the Godot Action RPG tutorial series from HeartBeast may appeal more to you.
Godot 3.2 is an open-source game engine specifically designed for use by indie game developers, so solo game designers may find it more appealing than Unity. The Godot Action RPG tutorial series teaches you how to use Godot to create a top-down action RPG in Godot, covering various topics like movement, hitboxes and hurtboxes, stats systems and more.
The series is intended for beginners but expects you to have some level of coding knowledge before starting. If you’re a complete coding beginner you may need to learn some programming basics before taking on these tutorials, but otherwise, they’re very beginner-friendly with helpful explanations for each new concept or mechanic that’s introduced.
Key Topics Covered:
Duration: Roughly 8 hours, 40 minutes of video tutorials.
If you’ve already gained some game design experience on other projects and want to dip your toes into RPG development to learn some new skills, then you might like the Build A Micro-RPG course. This tutorial takes you through the entire process of building a top-down micro-RPG in Unity, giving you the skills you need to take on bigger RPG projects or add RPG mechanics to other games.
This course is a great option for beginners and experienced coders alike. It introduces you to a range of essential skills for developing fully-fledged RPGs while keeping the initial project short, simple and exciting. All you need is a basic understanding of C# and Unity; the course will teach you the rest.
Once you’ve completed the Build A Micro-RPG tutorial, you’ll have mastered a variety of transferable skills that will help you expand and diversify your next project with new mechanics. Whether that means another RPG of larger scope or a completely different genre that you want to add RPG mechanics to is up to you.
Key Topics Covered:
Duration: Just over 2 hours of video tutorials.
Learning to build your own RPG is a fun and rewarding process that helps you to master a variety of new game mechanics and systems at the same time as practicing and developing existing coding skills. By following the tutorials above, you’ll learn many of the foundational skills you’ll need to craft your own epic RPG – and hopefully, you’ll have fun at the same time!
Don’t forget though – RPGs are a broad genre, so don’t forget to continue to explore around. There are lots of learning resources out there – and the more you learn, the better your games will be!
]]>
If you’re interested in creating your own roguelike games, we’ve put together this handy list of online tutorials that provide a perfect starting point for you to learn the essential game mechanics and programming skills you’ll need to succeed.
Roguelikes are a slightly more niche genre than others, but they still have plenty to offer to both players and developers. Recent hits like Hades and Darkest Dungeon prove that this genre can be used for extremely inventive gameplay and innovative storytelling, which makes roguelikes a very interesting project for game developers in-training to work on.
Roguelikes are often characterized by a randomized, procedurally generated game world that changes with every new playthrough. Learning procedural generation can be a valuable skill for game developers; as well as roguelikes, it can be applied to survival games, simulations, sandboxes and more.
In addition to procedural generation, roguelikes give developers a chance to work with a variety of other game mechanics as well. Action-oriented roguelikes can help you to practice character movement, enemy scripting and combat mechanics, while roguelikes with RPG elements can help you to learn more about turn-based combat, stat systems, and game balance.
Basically, roguelikes are a highly adaptable genre that you can use to practice new mechanics and experiment with systems from different genres, all while creating a fun and highly replayable gaming experience. As such, they make for great projects for beginners and more experienced game developers alike.
If you’re ready to get stuck into your first roguelike project, then the following list of tutorials will give you plenty of potential starting points. From dungeon-crawlers to survival games, these tutorials cover a variety of niches within the roguelike genre and will help you to build a range of essential skills to make a successful roguelike of your own.
Roguelikes are naturally slightly more complex than other genres, but we’ve made sure to include several tutorials in this list that are perfect for beginners as well as more experienced developers.
If you’re looking for somewhere to start your roguelike development journey, then there are few better options than this comprehensive tutorial from Zenva Academy. The Complete 2D Roguelike Course is a detailed introduction to the genre, exploring the fundamentals of roguelike development and guiding you through the process of making your first roguelike.
The tutorial walks you through a step-by-step guide to creating your own 2D dungeon-crawling roguelike in Unity. You’ll learn to procedurally generate dungeons, create enemies, code items and powerups, and more, all while learning a variety of skills that can help you to flesh out future projects with all sorts of new features.
All you need to get started is a basic understanding of Unity and C#, making this tutorial a fantastic option for beginners. Even if you already have plenty of experience with the Unity platform, The Complete 2D Roguelike Course is still definitely worth taking – you’ll learn a range of useful skills for other projects and master a new genre at the same time as expanding your portfolio with a new game.
Key Topics Covered:
Duration: Just over 3 hours of video tutorials explaining the entire process of building a 2D dungeon-crawler roguelike.
Another option for learning to make your own 2D roguelike in Unity comes from Unity itself in the form of the Unity 2D Roguelike Project tutorial series by Unity itself. These tutorials guide you through the process of creating a simple 2D roguelike where the player character must search for food and survive a wasteland for as long as possible.
The game project is turn-based and tile-based, with each tutorial teaching you to add new elements like level randomisation, enemies, audio and more. By the end of the series you’ll have mastered a variety of fundamental game mechanics for the roguelike genre and be ready to implement these systems into your own games to create your own roguelike.
Key Topics Covered:
Duration: A series of shorter tutorial videos lasting roughly 2 hours.
Another option for roguelike development from Zenva is the Build a Roguelike Game with C++ and SFML tutorial. This tutorial works along similar lines to the 2D Roguelike Course, except it uses the Simple and Fast Multimedia Library rather than Unity, so if you’re not keen on using Unity then this is an ideal alternative.
The tutorial guides you through the process of using SFML’s multimedia capabilities to build a 2D action-oriented roguelike where players must dodge moving enemies to reach the exit in each level. By the end you’ll have created your own version of the project, learning various skills such as user interaction, adding graphics, building dungeons and more along the way.
You’ll need some existing familiarity with the C++ language and SFML to get started with this project, but with a simple step-by-step structure, it’s easy to follow and perfect for beginners. It also makes for a fun project for more experienced developers who want to learn to use new tools, since the course acts as a great introduction to both C++ and SFML.
Key Topics Covered:
Duration: 2 hours of video tutorials explaining how to make a 2D roguelike in SFML.
The How To Create A 2D Roguelike Game tutorial series from Dapper Dino provides another great entry point for game developers who are curious about dipping their toes into the roguelike genre. Like many of the other tutorials on this list, the tutorial focuses on using Unity to implement the various mechanics and systems that are commonly found in roguelike games.
The tutorial takes inspiration from the roguelike game Wizard of Legend, using it as a reference point for which systems to include in your own roguelike. Following the tutorial, you’ll steadily build up your own simplified roguelike in the style of Wizard of Legend, mastering core mechanics like combat, loot drops, class systems, map generation and more.
You’ll need some existing knowledge of Unity and C# to ensure you understand each part of the tutorial, but it does a good job of providing step-by-step instructions at each stage, so it’s fairly accessible even if you’re more of a beginner than a pro.
Key Topics Covered:
Duration: Just under 4 hours of video tutorials, each exploring different mechanics in the roguelike genre.
If you’ve already worked on a few different game projects and want to try your hand at a roguelike next, but don’t want to have to sit through a tutorial covering game design fundamentals you’ve already learned, then this may be the tutorial for you.
The 2D Procedural Maps for Beginners tutorial is a shorter tutorial series that provides step-by-step instructions for creating procedurally generated 2D game maps in Unity. You’ll learn the expandable programming logic needed to create these maps, plus key systems like noise maps, biome calculations, player controllers, and more.
Once you’ve used this tutorial to master procedural generation, you’ll be all set to implement these systems into your projects, allowing you to build roguelikes from the ground up or add randomized worlds into other projects to give them a roguelike twist.
Key Topics Covered:
Duration: 1 hour, 30 minutes of video tutorials explaining each step in creating procedurally generated maps in Unity.
If you’re unfamiliar with Unity or C# and would like to learn to make a simple roguelike without using these, then another option is to follow the Roguelike Python Tutorial from Ombarus. This series guides you through the process of creating a very basic roguelike using standard keyboard symbols to represent the map and characters, giving it an appearance similar to the original Rogue game that the genre is named for.
The tutorial follows the structure of the text-based 2020 Roguelike Tutorial, meaning you can refer to a written guide if you get stuck at any point. Through the course of the tutorial series, you’ll learn to create and control a player character, generate randomized maps, implement a turn system and more, allowing you to get to grips with many of the core mechanics of the roguelike genre.
While this tutorial leads to a much more simplistic finished project than some of the other options on this list, it’s still a fun way to learn more about the systems used in roguelike games, and it provides a fun entry point for coders more familiar with Python than Unity.
Key Topics Covered:
Duration: Roughly 2 hours, 45 minutes of video tutorials covering each stage of creating a simple Python-based roguelike.
While the majority of the roguelike genre is made up of 2D games, there are no rules against using a 3D environment for your roguelike, especially when blending your roguelike with other genres. Recent hits like Returnal have shown that blending the random elements of the roguelike genre with 3D environments and fast-paced action can be a winning combination.
So, if you want to try your hand at creating your own 3D roguelike, the best place to start is to understand how procedural generation works in a 3D environment in order to craft random levels for your players. The Complete Procedural Terrain Generation Course allows you to do just that, teaching you the fundamentals of using procedural terrain generation to create randomized maps complete with varied biomes.
The tutorial will help you learn to algorithmically generate a terrain’s shape with C# in Unity, and also provides a guide to performance optimization to ensure a smooth playing experience in your randomised world. You’ll need a basic understanding of Unity and C# to get started, but otherwise, this step-by-step guide is extremely accessible and beginner-friendly.
Key Topics Covered:
Duration: Roughly 3 hours, 40 minutes of video tutorials explaining the process of procedural terrain generation using Unity.
Roguelikes provide aspiring game developers with an exciting and unique challenge, tasking them with creating fun and replayable gameplay within randomized levels and worlds. As such, they’re a perfect next step for those who have completed a few different projects and want to tackle a more complex genre next.
Using the tutorials above, you should be able to master many of the core mechanics found in roguelike games. Whether you choose to create your own roguelike or implement roguelike mechanics within your other projects to give them a new and unique twist is up to you!
]]>
Kaboom.js is a relative newcomer to the game development scene, but it is already making a big splash – especially among indie creators. This JavaScript-based library was made specifically with games in mind, in the same vein of frameworks such as Phaser or Pixi.js. However, there are many aspects that have made it stand above its competitors and worth your time checking out.
In this article, we’re going to introduce you to Kaboom.js and show you how you can get started learning this new tool.
Let’s get started!
As mentioned, Kaboom.js is a JavaScript library in the vein of HTML5 games, which use the browser to render games made with it. The library was made with the idea of simplicity, but comes with many important game development features – such as layering, collision detection, input handling, and more. Regardless, though, the goal is to allow novice developers to create game scenes as quickly as possible.
Game objects in Kaboom.js are conveniently rendered with a component-based system. This means they’re extremely easy to manipulate and apply behaviors too. The behaviors can include everything from simple game mechanics like health to basic movement-based animations – so each game object essentially suits object oriented programming principles.
Beyond this, it is worth noting Kaboom.js is also an available template for Replit – a popular, in-browser coding environment. This means its well-suited for developers who aren’t interested in setting up local development environments or just want to play around with the library.
Why should you use Kaboom.js? Below, we’re going to explore what makes Kaboom.js stand out and how, as a developer, you can benefit from the advantages Kaboom.js offers.
Unfortunately, not everyone has a computer that can run game engines such as Unity or Unreal Engine – which both are pretty process intensive programs. However, Kaboom.js does not require a great computer and can be used even with low-end laptops. In fact, due to its incorporation with Replit, as mentioned above, you really only need a browser if you’re intent on using it – not even a code editor is required.
Likewise, because of how lightweight it is, games made with Kaboom.js run very smoothly as well. This means you don’t have to get hung up too much on optimization. Instead, your games will likely run quickly right out of the box, leaving you more time to focus on what matters.
Many frameworks and libraries out there require several layers of dependencies to be able to run properly. If you forget the dependency, or more tragically, if the dependency has a breaking change, your game isn’t going to work.
Kaboom.js doesn’t require any sort of dependencies. While you certainly can add packages such as webpack, natively you just need to import the Kaboom.js library itself. Once that is added, you won’t have the extra worry of maintaining the dependencies. This helps Kaboom.js achieve its goal of making it quick to setup and allowing you to dive straight into the fun stuff of actually making the game.
Since Kaboom.js is specifically for creating browser-based games, this means that there are no platform limitations. If the device can connect to the internet, your players can play it from anywhere.
For you as a developer, this means two things. First, this helps you avoid messy setups of needing multiple game versions just to have it available for multiple platforms. Second, this opens you up for a wider audience since you can tap into different sorts of players. All in all, though, this is a boon that makes it much easier for you to get the game into multiple different hands.
If you’ve ever tried to read documentation for frameworks or libraries before, you’ll know they can get rather complicated. If you don’t believe us, just go check out Unity’s documentation. While certainly this is fine for experienced developers, beginner developers are going to find this a nightmare.
Kaboom.js was designed to eliminate this problem and be as simple as possible. Not only does this help with keeping it lightweight, but makes the documentation much, much easier to understand when you’re just a beginner. This, in turn, just makes development itself easier.
Additionally, it comes with the extra benefit that this documentation can more easily be maintained as the library is updated. This means you won’t be reading old information that was relevant five versions ago.
As you might have seen from the previous points, beginners were at the heart of the design choices for Kaboom.js. Everything about the library is made to benefit beginners who have never made a game before. While there is a need for JavaScript experience before diving in, this is a much lower point of entry compared to heftier engines.
Thus, you’re only going to need a few tutorials to get the gist of how Kaboom.js works – and soon be able to develop your own projects with it.
Over the years, many HTML5 frameworks have come and gone. For others that remain, the development can often be slow and new versions, bug fixes, and features are few and far between. While many of these frameworks still technically work, the lack of developer support can eventually become a hindrance.
Kaboom.js, on the other hand, is very actively and caringly developed. Though we don’t expect them to break their principles of simplicity, improvements to the library come frequently. Thus, you can be assured the library will be supported for whatever time it needs for you to build your project.
Being based on HTML5 (i.e. for the browser), projects made with Kaboom.js do have limitations – as our browsers and internet are only so powerful no matter which JS library we’re talking about. So, you shouldn’t expect to make any kind of AAA game with the library. Kaboom.js was also specifically made for 2D development, so 3D graphics are kind of off the menu as well.
Past these two minor limitations, though, Kaboom.js does not put any further obstacles in your way. You can build small games in just about every genre, and only your imagination is there to hold you back. This includes everything from platformers to adventure games to old-style retro games like Breakout, Super Mario Bros., Space Invaders, and so forth.
If you want to explore the full scope of what people have already made, you can check out Kaboom.js made games below:
Ready to create games with Kaboom.js? Check out the links of tutorials below which cover Kaboom.js, JavaScript for HTML5 development, and the game development process.
In order to follow this tutorial, you are expected to be familiar with the following concepts:
Before starting reading the tutorial, create a new Unity project and import all sprites available through the source code. You will need to slice the spritesheets accordingly.
You can download the tutorial source code files here.
The sprites used in this tutorial are from the Superpowers Asset Packs by Pixel-boy. All sprites are available under the Creative Commons Zero (CC0) license. So, you can use them in your games, even commercial ones.
Before beginning, it is assumed that you have the basics of Unity under your belt. If not, you can feel free to check out some of our other Unity tutorials first. Alternatively, you can also jump into more comprehensive online courses designed to take you from zero to industry-ready developer.
For teachers, you may also want to check out Zenva Schools. This platform made specifically for classroom usage offers several Unity-based courses (alongside other in-demand digital technologies topics). There are also plenty of useful features for classrooms including classroom management tools, reporting, course plans, and more.
First of all, we are going to create a Canvas to show the background image in the Title Scene. You can do that by creating a new Canvas called BackgroundCanvas, and setting its render mode as Screen Space – Camera. In order to do so, we need to specify the camera of the canvas, which will be our main camera. Also, the UI Scale Mode (in Canvas Scaler) will be set to follow the screen size, with a reference resolution of 1280×960.
After doing that, we create a new Image object as a child of this canvas. The source image will be the background image, and we can set its native size in order to properly show it.
Now, we need another Canvas to show the HUD elements. In the Title Scene, those elements will be a title text, and a play button.
Let’s start by creating another Canvas following the same process as the BackgroundCanvas. However, in order to show this canvas over the background one, we need to properly set its sorting layer. We are going to create another layer called HUD, and put the HUDCanvas on this layer.
Finally, we need to create the two HUD objects. The first one is a Text, while the second one is a Button. For the Text object we only need to set its message. On the other hand, for the Button object we need to set its OnClick callback.
The PlayButton object will have the following ChangeScene script for the OnClick callback. This script will simply define a method to start a new scene given its name. Then, we set the OnClick callback of the play button to call the loadNextScene method with “Town” as parameter.
public class ChangeScene : MonoBehaviour { public void loadNextScene (string sceneName) { SceneManager.LoadScene(sceneName); } }
In our game, we want to keep the player units data saved even when changing scenes. In order to do so, we are going to create a PlayerParty persistent object, which won’t be destroyed when changing scenes. You will also need to properly set its position so that it will be created in the correct position in Battle Scene.
In order to keep the PlayerParty alive when changing scenes, we are going to use the following script, called StartBattle. This script keeps the object from being destroyed when changing scenes in the Start method. It also adds a callback when a new scene is loaded and set the object as inactive, so that it won’t be shown in the title screen.
The configured callback (OnSceneLoaded) checks if the current loaded scene is the Title scene. If so, the PlayerParty object must be destroyed, to avoid duplicates. Otherwise, it should be set as active if the current scene is the Battle scene.
public class StartBattle : MonoBehaviour { // Use this for initialization void Start () { DontDestroyOnLoad(this.gameObject); SceneManager.sceneLoaded += OnSceneLoaded; this.gameObject.SetActive (false); } private void OnSceneLoaded (Scene scene, LoadSceneMode mode) { if (scene.name == "Title") { SceneManager.sceneLoaded -= OnSceneLoaded; Destroy(this.gameObject); } else { this.gameObject.SetActive(scene.name == "Battle"); } } }
Also, the PlayerParty will have two children to represent the player units. So, first let’s create a PlayerUnit prefab with only a few things for now. Later on this tutorial, we are going to add the rest of its behavior.
For now, the PlayerUnit will have only the Sprite Renderer and a script called UnitStats, which will store the stats of each unit, such as: health, mana, attack, magic attack, defense and speed.
public class UnitStats : MonoBehaviour { public float health; public float mana; public float attack; public float magic; public float defense; public float speed; }
The figure below shows the example of one player unit, called MageUnit. In this example the UnitStats script has other attributes, which will be used later (such as Animator and Damage Text Prefab), but you can ignore those attributes for now.
By now, you can already try running your game with the Title scene. You can create an empty Town scene only to test the play button.
We will start by creating the Town Scene. So, create a new Scene in your project and call it Town.
The Town scene will have a tilemap for us to walk around. To begin, let’s download the 2D Tilemap Editor package in the Package Manager (Window > Package Manager).
Then open the Tile Palette window (Window > 2D > Tile Palette). Here, create a new palette and save it in a new folder called Tilemap.
Now we need to go to our tilemap image. Set the “Sprite Mode” to Multiple, the “Pixels Per Unit” to 64, then click “Sprite Editor”.
Here, we want to slice the image up into 20 columns and 12 rows.
Then we can drag our tileset into the window to create the tiles.
In the Town scene, right click and create a 2D Object > Tilemap.
Now, we can select a tile and draw it on the tilemap. Let’s create a grass background.
In order to layer tiles, we can create a new tilemap for anything above the grass.
Set the grid child object’s sorting layer to 1.
Create a scene, something like this.
Now that we added our town map to Unity, we are going to create a Player prefab. The player will be able to move around the town, and must collide with the collidable Tiles.
So, let’s start by creating a GameObject called Player, adding the correct sprite renderer, a Box Collider 2D and a Rigidbody 2D as shown below. Observe that we need to set the Gravity Scale attribute of the Rigidbody2D as 0, so that the Player won’t be affected by the gravity.
We also need to create the Player animations. The Player will have four walking animations and four idle animations, one for each direction. So, first, we create all the animations naming them IdleLeft, IdleRight, IdleUp, IldeDown, WalkingLeft, WalkingRight, WalkingUp and WalkingDown.
The next thing we need is the player animator. So, we create a new animator called PlayerAnimator and add all the created animations to it. Once we add the animator to the Player object in the inspector, we can create its animations using the Unity animation window and the player spritesheet. The figure below shows the example of the WalkingUp animation.
Now we need to configure the animation transitions in the player animator. The animator will have two paremeters: DirectionX and DirectionY, which describes the current direction of the player movement. For example, if the player is moving to the left, DirectionX is equal to -1, while DirectionY is equal to 0. Those parameters will be correctly set later in a movement script.
Each idle animation will have a transition to each walking animation. The direction parameters should have the values according to the animation direction. For example, the Idle Left animation will change to Walking Left if DrectionX is equal to -1. Also, each walking animation will have a transition for its correspondent idle animation. Finally, if the player changes its walking direction without stopping, we need to update the animation. So, we need to add transitions between the walking animations as well.
In the end, the player animator should look like the figure below. The next figures show examples of transitions between animations (IdleLeft -> WalkingLeft and WalkingLeft -> IdleLeft).
Now let’s create the PlayerMovement script. All movement happens in the FixedUpdate method. We use the horizontal and vertical axis inputs to check if the player should move horizontally or vertically. The player can move to a given direction if it is not already moving to the opposite direction. For example, it can move to the left if it is not already moving to the right. We do that for both vertical and horizontal directions. When moving to a given direction, we need to set the animator parameters. In the end, we apply the velocity to the Player Rigidbody2D.
public class PlayerMovement : MonoBehaviour { [SerializeField] private float speed; [SerializeField] private Animator animator; void FixedUpdate () { float moveHorizontal = Input.GetAxis("Horizontal"); float moveVertical = Input.GetAxis("Vertical"); Vector2 currentVelocity = gameObject.GetComponent<Rigidbody2D>().velocity; float newVelocityX = 0f; if(moveHorizontal < 0 && currentVelocity.x <= 0) { newVelocityX = -speed; animator.SetInteger("DirectionX", -1); } else if(moveHorizontal > 0 && currentVelocity.x >= 0) { newVelocityX = speed; animator.SetInteger("DirectionX", 1); } else { animator.SetInteger("DirectionX", 0); } float newVelocityY = 0f; if(moveVertical < 0 && currentVelocity.y <= 0) { newVelocityY = -speed; animator.SetInteger("DirectionY", -1); } else if(moveVertical > 0 && currentVelocity.y >= 0) { newVelocityY = speed; animator.SetInteger("DirectionY", 1); } else { animator.SetInteger("DirectionY", 0); } gameObject.GetComponent<Rigidbody2D>().velocity = new Vector2(newVelocityX, newVelocityY); } }
In the end, the Player prefab should look like the figure below.
For now, you can start your game and try moving the player around the map. Remember to check if the tile collisions are properly working.
The player can start battles by interacting with enemy spawners. The enemy spawner will be an immovable object that, when touched by the player will switch to another scene called Battle Scene.
Also, the enemy spawner will be responsible for creating the enemy units objects in the Battle Scene. This will be done by creating an EnemyEncounter prefab, with enemy units as children. Like the player units from the Title Scene, for now we are only going to add the UnitStats script and the Sprite Renderer to enemy units. The figure below shows an example of an enemy unit. You can create the EnemyEncounter by creating a new prefab and adding the desired enemy units as children to it. You will also need to properly set its position so that it will be created in the correct position in Battle Scene.
So, let’s create a prefab called EnemySpawner. This prefab will have a collision box and a Rigidbody2D, in order to check for collisions with the Player prefab.
Also, it will have a script called SpawnEnemy as below. This script implements the OnCollisionEnter2D method to check for collisions with the Player prefab. We do so by checking if the other object tag is “Player” (remember to properly set the Player prefab tag). If there is a collision, it is going to start the Battle Scene and set a spawning attribute to true.
In order to create the enemy units in the Battle Scene, the script needs as an attribute the enemy encounter prefab, and the enemy spawner must not be destroyed when changing scenes (done in the Start method). When loading a scene (in the OnSceneLoaded method), if the scene being loaded is the Battle Scene, the enemy spawner will destroy itself and will instantiate the enemy encounter if the spawning attribute is true. This way, we can make sure that only one spawner will instantiate the enemy encounter, but all of them will be destroyed.
public class SpawnEnemy : MonoBehaviour { [SerializeField] private GameObject enemyEncounterPrefab; private bool spawning = false; void Start () { DontDestroyOnLoad(this.gameObject); SceneManager.sceneLoaded += OnSceneLoaded; } private void OnSceneLoaded (Scene scene, LoadSceneMode mode) { if(scene.name == "Battle") { if(this.spawning) { Instantiate(enemyEncounterPrefab); } SceneManager.sceneLoaded -= OnSceneLoaded; Destroy(this.gameObject); } } void OnTriggerEnter2D (Collider2D other) { if(other.gameObject.tag == "Player") { this.spawning = true; SceneManager.LoadScene("Battle"); } } }
By now, you can try running your game and interacting with the enemy spawner. Try creating an empty Battle Scene to allow changing the scenes.
Let’s start by creating the canvases that we are going to use for Battle Scene. Similarly to Title Scene, we are going to use one for the background and another for the HUD elements.
The background canvas will be the same as for the Title Scene, so I’m not going to show its creation. The HUD canvas, on the other hand, will need a lot of elements to allow proper player interaction.
First ,we will add an actions menu, which will show the possible actions for the player. This will be an empty object used as parent of all action menu items. Each action menu item, by its turn, will be a button, added as child of the ActionsMenu.
We are going to add three possible actions: attacking with physical attack (PhysicalAttackAction), attacking with magical attack (MagicAttackAction) and running from the battle (RunAction). Each action will have its OnClick event but, for now, we are not going to add it. The figure below shows only the PhysicalAttackAction, since the other ones will only change the source image for now. The source images for those menu items are from the icons Sprite, which was sliced in many icons.
The second menu we are going to add to the HUD canvas is the EnemyUnitsMenu. This menu will be used to show the enemy units, so that the player can choose one to attack. Similarly to the ActionsMenu, it will be an empty object used to group its menu items. However, the enemy menu items will be created by the enemy units, when the Battle scene starts.
In order to make the enemy unit to create its menu item, we need to create the menu item prefab. This prefab will be called TargetEnemy and will be a button. The OnClick callback of this button will be used to select this enemy as the target.
We need to add two scripts to the EnemyUnit prefab to handle its menu item: KillEnemy and CreateEnemyMenuItem.
The KillEnemy script is simple. It will have as an attribute the menu item of this unit, and when the unit is destroyed (OnDestroy method), the menu item must be destroyed too.
public class KillEnemy : MonoBehaviour { public GameObject menuItem; void OnDestroy () { Destroy(this.menuItem); } }
Now let’s create the CreateEnemyMenuItem script. This script will be responsible for creating its menu item and setting its OnClick callback. All this is done in the Awake method. First, the menu item position is calculated based on the number of existing items. Then, it is instantiated as children of EnemyUnitsMenu, and the script sets its localPosition and localScale. In the end, it sets the OnClick callback to be the selectEnemyTarget method, and sets the menu item as the one for this unit in KillEnemy.
The selectEnemyTarget method should make the player to attack this unit. However, we don’t have the code to do that now. So, for now we are going to leave this method empty.
public class CreateEnemyMenuItems : MonoBehaviour { [SerializeField] private GameObject targetEnemyUnitPrefab; [SerializeField] private Sprite menuItemSprite; [SerializeField] private Vector2 initialPosition, itemDimensions; [SerializeField] private KillEnemy killEnemyScript; // Use this for initialization void Awake () { GameObject enemyUnitsMenu = GameObject.Find("EnemyUnitsMenu"); GameObject[] existingItems = GameObject.FindGameObjectsWithTag("TargetEnemyUnit"); Vector2 nextPosition = new Vector2(this.initialPosition.x + (existingItems.Length * this.itemDimensions.x), this.initialPosition.y); GameObject targetEnemyUnit = Instantiate(this.targetEnemyUnitPrefab, enemyUnitsMenu.transform) as GameObject; targetEnemyUnit.name = "Target" + this.gameObject.name; targetEnemyUnit.transform.localPosition = nextPosition; targetEnemyUnit.transform.localScale = new Vector2(0.7f, 0.7f); targetEnemyUnit.GetComponent<Button>().onClick.AddListener(() => selectEnemyTarget()); targetEnemyUnit.GetComponent<Image>().sprite = this.menuItemSprite; killEnemyScript.menuItem = targetEnemyUnit; } public void selectEnemyTarget () { } }
The final HUD elements we are going to add are those to show the player unit information, such as health and mana. So, we are going to start by creating an empty GameObject called PlayerUnitInformation, to hold all those HUD elements.
Then, we are going to add an image as child of this object called PlayerUnitFace. This element will simply show the face of the current unit. For now, let’s select any unit face.
The next elements will be the health bar and its text. The health bar will be an image showing the health bar sprite, while the text will show the HP message. Finally we do the same for the mana bar, only changing the sprite and the text message. The figures below show only the health bar, since the mana bar is very similar.
By now, your Battle Scene should look like this. This figure corresponds to the Scene viewer, and not the running game, since there is a lot of content we still need to add to properly run the Battle Scene. The righthand figure shows the objects hierarchy in the scene.
The next thing we are going to do is creating the units animations. Each unit will have four animatios: Idle, PhysicalAttack, MagicalAttack and Hit. So, let’s start by creating an animator for one of the player units (for example, the MageUnit), and adding it to its correspondent prefab.
Now, if we select this prefab and open the Animator view, we can configure its animations state machine as below. We are going to create a state for each animation, with Idle being the default one, and all other ones having transitions to Idle when they end.
Now, we need to create the four animations to add them to their correspondent states. The figure below shows the MagicalAttack animation for MageUnit. You can create all animations following the same process with the animation view, so I’m not going to show them all. Also, you have to do the same for all units (including the enemy ones).
We still need to define when to play those animations. However, we are going to do so when adding more functionalities for the units. For now, the units will only play the Idle animation, as it is the default one.
If you play the game now, it should show the units with the Idle animation. However, remeber you have to play Title Scene and go until the Battle Scene, in order to see the units.
The next thing we are going to add to our game is a turn-based battle system. So, let’s start by creating an empty game object with a script called TurnSystem.
The TurnSystem script will keep a List with the UnitStats script of all units (player and enemy ones). Then, in each turn, it can pop the first element of the list, make the unit act and add it again to the list. Also, it needs to keep the list sorted according to the units acting turns.
This script is shown below. The UnitStats list is created in the Start method. This is done by iterating through all game objects with the tags “PlayerUnit” or “EnemyUnit” (remember to properly tag your objects). For each unit, the script gets its UnitStats script, calculate its next acting turn and add it to the list. After adding all units the list is sorted. Finally, the menus are disabled, since they will be used only on the player turns, and the first turn begins (by calling nextTurn).
The nextTurn method, by its turn, will start by removing the first UnitStats from the list and checking if it is not dead. If the unit is alive, it will calculate its next acting turn in order to add it again to the list. Finally, it will make it act. We still don’t have the acting methods of the units, so we are only going to print a message in the console for now. On the other hand, if the unit is dead, we are simply going to call nextTurn without adding it to the list again.
public class TurnSystem : MonoBehaviour { private List<UnitStats> unitsStats; [SerializeField] private GameObject actionsMenu, enemyUnitsMenu; void Start () { unitsStats = new List<UnitStats>(); GameObject[] playerUnits = GameObject.FindGameObjectsWithTag("PlayerUnit"); foreach(GameObject playerUnit in playerUnits) { UnitStats currentUnitStats = playerUnit.GetComponent<UnitStats>(); currentUnitStats.calculateNextActTurn(0); unitsStats.Add (currentUnitStats); } GameObject[] enemyUnits = GameObject.FindGameObjectsWithTag("EnemyUnit"); foreach(GameObject enemyUnit in enemyUnits) { UnitStats currentUnitStats = enemyUnit.GetComponent<UnitStats>(); currentUnitStats.calculateNextActTurn(0); unitsStats.Add(currentUnitStats); } unitsStats.Sort(); this.actionsMenu.SetActive(false); this.enemyUnitsMenu.SetActive(false); this.nextTurn(); } public void nextTurn () { UnitStats currentUnitStats = unitsStats [0]; unitsStats.Remove(currentUnitStats); if(!currentUnitStats.isDead()) { GameObject currentUnit = currentUnitStats.gameObject; currentUnitStats.calculateNextActTurn(currentUnitStats.nextActTurn); unitsStats.Add(currentUnitStats); unitsStats.Sort(); if(currentUnit.tag == "PlayerUnit") { Debug.Log("Player unit acting"); } else { Debug.Log("Enemy unit acting"); } } else { this.nextTurn(); } } }
Before moving on, we need to implement the UnitStats methods we used in TurnSystem, so let’s go back to the UnitStats script.
First, the calculateNextActTurn method is responsible for calculating the next acting turn based on the current one. This is done based on the unit speed, as shown below. Also, we need to make UnitStats to extend the IComparable interface, and implement the CompareTo method, so that we can properly sort the UnitStats list. The CompareTo method will simply compare the acting turn of the two scripts. Finally, we need to implement the isDead getter, which will simply return the dead attribute value. By default, this attribute is false, because the unit is alive at the beginning of the game.
public class UnitStats : MonoBehaviour, IComparable { public float health; public float mana; public float attack; public float magic; public float defense; public float speed; public int nextActTurn; private bool dead = false; public void calculateNextActTurn (int currentTurn) { this.nextActTurn = currentTurn + (int)Math.Ceiling(100.0f / this.speed); } public int CompareTo (object otherStats) { return nextActTurn.CompareTo(((UnitStats)otherStats).nextActTurn); } public bool isDead () { return this.dead; } }
For now, you can try playing the game again, to see if the turn message is being properly printed in the console.
Now that we have our turn-based battle system we are going to allow units to attack each other. First, we are going to create Attack prefabs, which will be used by the units. Then, we are going to add the action scripts of both player and enemy units, so that they can properly attack. When receiving damage, units will show a Text prefab with the damage value.
The Attack prefab will be an inivisible prefab with an script called AttackTarget. This script will describe the attack properties such as attack and defense multipliers and mana cost. Also, the attack will have an owner, which is the unit currently attacking.
First, the script checks if the owner has enough mana to execute the attack. If so, it picks random attack and defense multipliers based on the minimum and maximum values. So, the damage is calculated based on those multipliers and the attack and defense of the units. Observe that, if the attak is a magical attack (this.magicAttack is true), then it will use the magic stat of the unit, otherwise it uses the attack stat.
In the end, the script plays the attack animation, inflicts the damage to the target unit and reduces the mana of the owner accordingly.
public class AttackTarget : MonoBehaviour { public GameObject owner; [SerializeField] private string attackAnimation; [SerializeField] private bool magicAttack; [SerializeField] private float manaCost; [SerializeField] private float minAttackMultiplier; [SerializeField] private float maxAttackMultiplier; [SerializeField] private float minDefenseMultiplier; [SerializeField] private float maxDefenseMultiplier; public void hit (GameObject target) { UnitStats ownerStats = this.owner.GetComponent<UnitStats>(); UnitStats targetStats = target.GetComponent<UnitStats>(); if(ownerStats.mana >= this.manaCost) { float attackMultiplier = (Random.value * (this.maxAttackMultiplier - this.minAttackMultiplier)) + this.minAttackMultiplier; float damage = (this.magicAttack) ? attackMultiplier * ownerStats.magic : attackMultiplier * ownerStats.attack; float defenseMultiplier = (Random.value * (this.maxDefenseMultiplier - this.minDefenseMultiplier)) + this.minDefenseMultiplier; damage = Mathf.Max(0, damage - (defenseMultiplier * targetStats.defense)); this.owner.GetComponent<Animator>().Play(this.attackAnimation); targetStats.receiveDamage(damage); ownerStats.mana -= this.manaCost; } } }
We are going to create two attack prefabs: PhysicalAttack and MagicalAttack, each one with its own multipliers.
Now we need to implement the reiceveDamage method, used in the AttackTarget script. This will be a method from UnitStats that, besides reducing the unit health, it will also show the damage using a Text over the unit’s head.
This method is shown below. First, it will simply reduce the unit health and play the Hit animation. Then, it will create the damage text (using this.damageTextPrefab). Observe that the damage text must be a child of the HUDCanvas, since it is an UI element, and we need to properly set its localPosition and localScale. In the end, if the unit health is less than zero, the script set the unit as dead, change its tag and destroy it.
public void receiveDamage (float damage) { this.health -= damage; animator.Play("Hit"); GameObject HUDCanvas = GameObject.Find("HUDCanvas"); GameObject damageText = Instantiate(this.damageTextPrefab, HUDCanvas.transform) as GameObject; damageText.GetComponent<Text>().text = "" + damage; damageText.transform.localPosition = this.damageTextPosition; damageText.transform.localScale = new Vector2(1.0f, 1.0f); if(this.health <= 0) { this.dead = true; this.gameObject.tag = "DeadUnit"; Destroy(this.gameObject); } }
Now we can already implement the act method of the units. An enemy unit will always attack a random enemy with the same attack. This attack will be an attribute in EnemyUnitAction. In the Awake method we are going to create a copy of it for the unit and properly set its owner. We need to create a copy since we want each unit to have its own attack object instance.
Then, the act method will pick a random target and attack it. The findRandomTarget method, by its turn, will start by listing all possible targets given their tags (for example, “PlayerUnit”). If there is at least one possible target in this list, it will generate a random index to pick the target.
public class EnemyUnitAction : MonoBehaviour { [SerializeField] private GameObject attack; [SerializeField] private string targetsTag; void Awake () { this.attack = Instantiate(this.attack); this.attack.GetComponent<AttackTarget>().owner = this.gameObject; } GameObject findRandomTarget () { GameObject[] possibleTargets = GameObject.FindGameObjectsWithTag(targetsTag); if(possibleTargets.Length > 0) { int targetIndex = Random.Range(0, possibleTargets.Length); GameObject target = possibleTargets [targetIndex]; return target; } return null; } public void act () { GameObject target = findRandomTarget(); this.attack.GetComponent<AttackTarget>().hit(target); } }
Player units, by their turn, will have two different attacks: physical and magical. So, in the Awake method we need to properly instantiate and set the owner of these two attacks. Also, we are going to set the current attack as the physical one by default.
Then, the act method will receive as parameter the target unit and will simply attack it.
public class PlayerUnitAction : MonoBehaviour { [SerializeField] private GameObject physicalAttack; [SerializeField] private GameObject magicalAttack; private GameObject currentAttack; void Awake () { this.physicalAttack = Instantiate(this.physicalAttack, this.transform) as GameObject; this.magicalAttack = Instantiate(this.magicalAttack, this.transform) as GameObject; this.physicalAttack.GetComponent<AttackTarget>().owner = this.gameObject; this.magicalAttack.GetComponent<AttackTarget>().owner = this.gameObject; this.currentAttack = this.physicalAttack; } public void act (GameObject target) { this.currentAttack.GetComponent<AttackTarget>().hit(target); } }
Now, we can already call the enemy unit act method in the TurnSystem script. We still can’t do the same for player units, since we still need to properly select the current unit and its attack. This is the next thing we are going to do.
public void nextTurn () { UnitStats currentUnitStats = unitsStats [0]; unitsStats.Remove(currentUnitStats); if(!currentUnitStats.isDead()) { GameObject currentUnit = currentUnitStats.gameObject; currentUnitStats.calculateNextActTurn(currentUnitStats.nextActTurn); unitsStats.Add(currentUnitStats); unitsStats.Sort(); if(currentUnit.tag == "PlayerUnit") { Debug.Log("Player unit acting"); } else { currentUnit.GetComponent<EnemyUnitAction>().act(); } } else { this.nextTurn(); } }
We need to properly select the current player unit each turn. This will be done by adding the following script (SelectUnit) to the PlayerParty object. This script will need references to the battle menus, so when the Battle scene is loaded it is going to set them.
Then, we need to implement three methods: selectCurrentUnit, selectAttack and attackEnemyTarget. The first one will set a unit as the current one, enable the actions menu, so that the player can choose an action, and update the HUD to show the current unit face, health and mana (this last method will be implemented later).
The selectAttack method, by its turn, will call selectAttack for the current unit, and will change the current menu, by disabling the actions menu and enabling the enemies menu. The selectAttack method also needs to be implemented in the PlayerUnitAction script. This way, now that the player has selected an attack, it can select the target.
Finally, the attackEnemyTarget will disable both menus and call the act method for the current unit, with the selected enemy as the target.
public class SelectUnit : MonoBehaviour { private GameObject currentUnit; private GameObject actionsMenu, enemyUnitsMenu; void Awake () { SceneManager.sceneLoaded += OnSceneLoaded; } private void OnSceneLoaded (Scene scene, LoadSceneMode mode) { if(scene.name == "Battle") { this.actionsMenu = GameObject.Find("ActionsMenu"); this.enemyUnitsMenu = GameObject.Find("EnemyUnitsMenu"); } } public void selectCurrentUnit (GameObject unit) { this.currentUnit = unit; this.actionsMenu.SetActive(true); } public void selectAttack (bool physical) { this.currentUnit.GetComponent<PlayerUnitAction>().selectAttack(physical); this.actionsMenu.SetActive(false); this.enemyUnitsMenu.SetActive(true); } public void attackEnemyTarget (GameObject target) { this.actionsMenu.SetActive(false); this.enemyUnitsMenu.SetActive(false); this.currentUnit.GetComponent<PlayerUnitAction>().act(target); } }
public class PlayerUnitAction : MonoBehaviour { [SerializeField] private GameObject physicalAttack; [SerializeField] private GameObject magicalAttack; private GameObject currentAttack; public void selectAttack (bool physical) { this.currentAttack = (physical) ? this.physicalAttack : this.magicalAttack; } }
Now, we need to properly call all those three methods. The first one (selectCurrentUnit) will be called in TurnSystem, when it is a player unit turn.
public void nextTurn () { UnitStats currentUnitStats = unitsStats [0]; unitsStats.Remove(currentUnitStats); if(!currentUnitStats.isDead ()) { GameObject currentUnit = currentUnitStats.gameObject; currentUnitStats.calculateNextActTurn(currentUnitStats.nextActTurn); unitsStats.Add(currentUnitStats); unitsStats.Sort(); if(currentUnit.tag == "PlayerUnit") { this.playerParty.GetComponent<SelectUnit>().selectCurrentUnit(currentUnit.gameObject); } else { currentUnit.GetComponent<EnemyUnitAction>().act(); } } else { this.nextTurn(); } }
The second one (selectAttack), will be called by the PhysicalAttackAction and MagicalAttackAction buttons in the HUDCanvas. Since the PlayerParty object is not from the same scene as these buttons, we can’t add the OnClick callbacks in the inspector. So, we are going to do that using the following script (added to those buttons objects), which will add the callback in the Start method. The callback will simply call the selectAttack method from SelectUnit. This same script should be added to both buttons, only changing the “physical” attribute.
public class AddButtonCallback : MonoBehaviour { [SerializeField] private bool physical; // Use this for initialization void Start () { this.gameObject.GetComponent<Button>().onClick.AddListener(() => addCallback()); } private void addCallback () { GameObject playerParty = GameObject.Find("PlayerParty"); playerParty.GetComponent<SelectUnit>().selectAttack(this.physical); } }
The third method (attackEnemyTarget) will be called from the enemy unit menu item. When creating the CreateEnemyMenuItems script, we left the selectEnemyTarget (which is the button callback) empty. Now, we are going to implement it. This method is going to find the PlayerParty object and call its attackEnemyTarget method.
public void selectEnemyTarget () { GameObject partyData = GameObject.Find("PlayerParty"); partyData.GetComponent<SelectUnit>().attackEnemyTarget(this.gameObject); }
Finally, now we need to update the HUD to show the current unit face, health and mana.
We are going to use the following script to show the unit health and mana. This script will start its initial localScale in the Start method. Then, in the Update method it will update the localScale according to the current stat value of the unit. Also, it will have a method do change the current unit being showed and an abstract method to retrieve the current stat value.
public abstract class ShowUnitStat : MonoBehaviour { [SerializeField] protected GameObject unit; [SerializeField] private float maxValue; private Vector2 initialScale; void Start () { this.initialScale = this.gameObject.transform.localScale; } void Update () { if(this.unit) { float newValue = this.newStatValue(); float newScale = (this.initialScale.x * newValue) / this.maxValue; this.gameObject.transform.localScale = new Vector2(newScale, this.initialScale.y); } } public void changeUnit (GameObject newUnit) { this.unit = newUnit; } abstract protected float newStatValue(); }
Instead of directly using this script, we are going to create two other ones that specialize it, implementing the abstract method: ShowUnitHealth and ShowUnitMana. The only method in those two scripts will be newStatValue, which will return the correct unit stats (health or mana).
public class ShowUnitHealth : ShowUnitStat { override protected float newStatValue () { return unit.GetComponent<UnitStats>().health; } }
public class ShowUnitMana : ShowUnitStat { override protected float newStatValue () { return unit.GetComponent<UnitStats>().mana; } }
Now we can add those two scripts to the health and mana bar objects. Another thing to do is to change their Pivot in the X coordinate to be zero, so that it will change the scale only on the right side of the bar.
Finally, we need to call the changeUnit method in those scripts when the current unit changes. This will start in the selectCurrentUnit method of SelectUnit. After setting the actionsMenu as active, it will call a method called updateHUD for the current unit.
public void selectCurrentUnit (GameObject unit) { this.currentUnit = unit; this.actionsMenu.SetActive(true); this.currentUnit.GetComponent<PlayerUnitAction>().updateHUD(); }
The updateHUD method, by its turn, will start by setting the sprite of the PlayerUnitFace object to be the current unit face (saved as an attribute of PlayerUnitAction). Then, it will set itself as the current unit in both ShowUnitHealth and ShowUnitMana.
[SerializeField] private Sprite faceSprite; public void updateHUD () { GameObject playerUnitFace = GameObject.Find("PlayerUnitFace") as GameObject; playerUnitFace.GetComponent<Image>().sprite = this.faceSprite; GameObject playerUnitHealthBar = GameObject.Find("PlayerUnitHealthBar") as GameObject; playerUnitHealthBar.GetComponent<ShowUnitHealth>().changeUnit(this.gameObject); GameObject playerUnitManaBar = GameObject.Find("PlayerUnitManaBar") as GameObject; playerUnitManaBar.GetComponent<ShowUnitMana>().changeUnit(this.gameObject); }
By now, you can try playing the game to see if you can select different actions, and if the stats are being correctly updated. The only action from the menu that we still have to implement is the Run action.
We still have to add ways of finishing the battle. There are three ways of doing that:
If the player wins the battle, it will receive a reward from the enemy encounter. In order to do that we are going to use the following script, which will be added to the enemy encounter object. In the Start method, it will set the enemy encounter in the TurnSystem object. Then, the collectReward method (which will be called from TurnSystem), will equally divide the encounter experience among all living player units.
public class CollectReward : MonoBehaviour { [SerializeField] private float experience; public void Start () { GameObject turnSystem = GameObject.Find("TurnSystem"); turnSystem.GetComponent<TurnSystem>().enemyEncounter = this.gameObject; } public void collectReward () { GameObject[] livingPlayerUnits = GameObject.FindGameObjectsWithTag("PlayerUnit"); float experiencePerUnit = this.experience / (float)livingPlayerUnits.Length; foreach(GameObject playerUnit in livingPlayerUnits) { playerUnit.GetComponent<UnitStats>().receiveExperience(experiencePerUnit); } Destroy(this.gameObject); } }
Now we need to implement the receiveExperience method used in collectReward. This will be a method from UnitStats used only to save the received experience. This can be used later to implement a level system in the game, but we are not going to do that in this tutorial.
public void receiveExperience (float experience) { this.currentExperience += experience; }
Finally, let’s call the collectReward method in TurnSystem. We are going to change the nextTurn method to check if there are still living enemy units, by finding the objects with the “EnemyUnit” tag. Remember that when a unit dies, we change its tag to “DeadUnit”, so that it won’t be found by this method. If there are no remaining enemy units, it calls the collectReward method for the enemy encounter, and go backs to the Town scene.
On the other hand, if there are no remaining player units, that means the player has lost the battle, so the game goes back to the Title scene.
public void nextTurn () { GameObject[] remainingEnemyUnits = GameObject.FindGameObjectsWithTag("EnemyUnit"); if(remainingEnemyUnits.Length == 0) { this.enemyEncounter.GetComponent<CollectReward>().collectReward(); SceneManager.LoadScene("Town"); } GameObject[] remainingPlayerUnits = GameObject.FindGameObjectsWithTag("PlayerUnit"); if(remainingPlayerUnits.Length == 0) { SceneManager.LoadScene("Title"); } UnitStats currentUnitStats = unitsStats [0]; unitsStats.Remove(currentUnitStats); if(!currentUnitStats.isDead ()) { GameObject currentUnit = currentUnitStats.gameObject; currentUnitStats.calculateNextActTurn(currentUnitStats.nextActTurn); unitsStats.Add(currentUnitStats); unitsStats.Sort(); if(currentUnit.tag == "PlayerUnit") { this.playerParty.GetComponent<SelectUnit>().selectCurrentUnit(currentUnit.gameObject); } else { currentUnit.GetComponent<EnemyUnitAction>().act(); } } else { this.nextTurn(); } }
The last way of finishing a battle is by running from it. This can be done by selecting the run action in the actions menu. So, we need to attach the script below to the run button, and add its OnClick callback.
The RunFromBattle script will have a tryRunning method. This method will generate a random number between 0 and 1, and compare it with a runningChance attribute. If the generated random number is less than the running chance, the player successfully avoids the battle, and goes back to the Town scene. Otherwise, the next turn starts.
public class RunFromBattle : MonoBehaviour { [SerializeField] private float runnningChance; public void tryRunning () { float randomNumber = Random.value; if(randomNumber < this.runnningChance) { SceneManager.LoadScene("Town"); } else { GameObject.Find("TurnSystem").GetComponent<TurnSystem>().nextTurn(); } } }
Finally, by now you should have everything working. Try playing battles until the end to check if everything is working. Now you can also try adding different enemy encounters and tuning some game parameters, such as units stats and attack multipliers.
Also, try adding things that could not have been covered in the tutorial, such as more intelligent enemies and a level system. You might also consider mastering techniques for mobile development and optimizing your game for use on a smartphone!
Of course, you may wish to explore other genres as well to expand your skills further. So don’t be afraid to seek out other online courses or even find Unity courses to integrate into the classroom.
The sky is the limit here, so don’t be afraid to experiment!
You can access the full course here: Build an Action RPG in Unreal Engine
In the Blueprints folder, create a new blueprint of type Character called Player.
Double-click to open it up inside of the blueprint editor. You’ll see that we have a number of default components. These help us out with creating our player. To begin, select the Mesh component.
Next we’ll create the damage collider. Create a new Box Collision component as a child of the mesh component. Rename it to DamageCollider.
Now we need to create our camera which will follow the player. This will be done by using a spring arm. This is a component which connects the camera to the player and also allows for collision detection. Create a new Spring Arm component.
As a child of the SpringArm component, create a new Camera component.
We can then click compile, save then return to the level editor. In order for the game to know what our player is, we’ll need to create a new blueprint of type GameMode Base called MyGameMode. Open it up and inside, set the Default Pawn Class to Player. Save, compile, then return to the level editor.
To assign this game mode, go to the Details panel and click on the World Settings tab. Here, set the GameMode Override to MyGameMode.
Now when you press play, you should see the player spawn in, with us looking through the camera.
In this lesson, we’re going to set it up so that we can rotate the camera around the player with our mouse. Inside of the Player blueprint, let’s go over to the Event Graph. First, we want to create a new event node called Mouse X. This gets triggered when we move our mouse and it outputs the horizontal axis value.
Plug this into an AddControllerYawInput node like below.
Compile, save and press play. You’ll see that we can move the mouse horizontally to rotate the player.
Now let’s rotate the camera up and down. We can’t do it like before since this will rotate the player sideways. Instead, we want to set it up like this:
One problem with this, is that the camera can rotate all the way around the player. To fix this, we need to clamp the vertical rotation. To begin, we want to set the spring arm rotation after we add to it.
To clamp, let’s start by getting the target rotation of the spring arm and breaking it.
Connect this to a Clamp Angle node.
Then we can plug that result into the Y of a make rotator node, and plug that into the set rotation node.
Now you should see the camera rotation is clamped vertically.
Welcome back everyone. In this lesson we are going to begin to create our Player Blueprint. This is gonna be featuring our Player model, it’s gonna to be featuring the colliders, the camera, everything we need in order to have our Player set up and ready to go.
So inside of our blueprints folder here, I’m gonna right click and create a new blueprint class. This is gonna be of type character, which basically gives us some preset things such as a Capsule Collider, a Mesh, a forward Direction, and also includes a Character movement Component which allows us to easily set up the player movement, the camera rotating, jumping, ground detection, and many other movement related things.
So it will create a character right here and we are gonna call this one player. We can then double click to open this up inside of the Blueprint Editor. I’m gonna dock it to the main window here. And as you can see here, we have a few things set up already for us. We have a capsule component here, which is collider, we have an arrow component, this is this blue arrow right here. And this just defines the forward direction. We have a mesh, which is going to be our Player mesh which we can then animate later on. And the character movement component, which as you can see here in the details panel includes a lot of different options we can tweak in order to find sharing our movement, okay?
So the first thing we’re gonna be doing is setting up a mesh. So I’m gonna select our mesh here, and I’m gonna select the skeletal mesh, and I’m gonna change this to our Player model right here. So there we go. We’ve got our Player model now in here, but it doesn’t really match the bounds or the direction of this capsule, we need to rotate it and move it down a bit. So I’m gonna set the Z location to be negative 90, so it is down in the correct position.
And now we just need to rotate it negative 90 on the Z as well, so it is facing in the correct forward direction it’s facing in its positive X direction luxor, okay? So we’ve got our player mesh there. Now what we need is the damage collider and the damage collider is basically going to be a collider that’s gonna check to see if there are any enemies inside of it when we are attacking.
So as a child of mesh, we can just select mesh here to arrow component and I’m gonna type box collision. And I’ll just call this one our damage collider luxor. And in terms of positioning it, we can just lift it up here, move it forward a bit and have it about there. So over here where we have the box extents, I’m gonna change this to be 25. And let’s also position it to the left a bit since that’s where the weapon is. Make sure it’s around in line like so and there we go.
Okay, so along with this, we also want to make sure that it is not a solid collider it needs to be a trigger that objects can move through. So down the details panel, let’s make sure generate overlap events is enabled. And let’s change the collision presets from overlap all dynamic to trigger okay, there we go.
Now what we can do is set up the camera. And the way we’re gonna do the camera is by attaching it to something known as a spring arm. And a spring arm is a very handy component which basically sets its child object or its child component a fixed direction and distance away from it. So what we can do then, is just rotate the spring arm and the camera is gonna rotate around our player.
So I’m gonna go add component, I’m gonna look for a spring arm. Make sure it is a child of the capsule component and not the mesh. So with this spring arm now, all we need to do is go to the details panel and change a few things.
First of all, we are on the point of orbit not to be around the waist of our player but probably up oops, probably up over near the players head right here. So to drag that up to the head there, we’ll set the wire rotation to be 20. Oh, negative 20 I mean, so we do have a bit of a downwards facing camera when we start off. And apart from this, we can leave everything as it is.
So what we’re gonna do now is as a child of spring arm, we’re gonna add component, we’re gonna look for camera, there we go. So now we got our camera in here, it is going to be right at the end of the spring arm as you can see here.
Now for our camera, what we want to do is move it over just a little bit to the right because since this is gonna be a third person controller, we probably don’t want the camera directly behind the play as that might sort of obstruct the view a bit. So normally what other games do, is they move the camera over to one of the sides a bit. So I’m gonna move this camera to be 60 on the Y location so it’s a bit to the right of the player.
And that is pretty much it. So if you select the spring arm on here, and we press E to get the rotation gizmo, you’ll see that if I rotate it, the camera follows along as well. Okay, so now what we can do is click save, click compile, we can go back to our main level here.
And we can set it up so that when we press play, our Player spawns in because right now if we press play, the play is not there, we just have controlled the default camera anchor, which just allows us to fly around similar to the main viewport.
So what we need to do is create a game mode so that it knows the default things such as the default player to spawn at the started level. So let’s right click here in the blueprint class, we’re gonna create a new blueprint. This one is gonna be a game mode base. And a game mode base basically defines some of the defaults, again such as the player, the default UI, the default multiplayer aspects, okay?
So we’ll go game mode base, I’m gonna call this one my game of mode. We can double click to open this up, this is also a blueprint. So we don’t actually have to add anything here if you do want some sort of global logic for your level, then you can probably add it to the event graph here, but we’re just gonna leave it as it is. And over in the details panel, we want to make sure that we have the default pawn class here. We want to change that our Player right here, okay? That’s all we need to do.
So we can click compile, we can click save, we can close out of the game mode here, I’m also gonna close out our project settings. And in our main level here, let’s go over to the world settings tab, which is inside of the details panel here, and world settings basically, global settings for the current level. So we can change things such as air game mode override. Right now it’s on none which means there’s no game mode active. So let’s select that and choose my game mode.
So now when we press play, the game is gonna look at the game mode and go okay, what is the default player class to spawn? It’s gonna see it is our Player blueprint. So it is gonna spawn the player blueprint in on this player start position right here. So we can press play and there we go.
This is our player blueprint setup, it’s spawning in. Now all we need to do is implement the functionality to actually look around with the mouse rotate the camera and move around with the keyboard. So we are gonna be beginning on setting up the camera orbiting in the next lesson. So I’ll see you all then.
Welcome back everyone. In this lesson, we are gonna be setting up the camera orbiting system, for app play blueprint here. In the previous lesson, we set up the play blueprint, so it has all the components set up ready to go. We also have this spring arm component, which if we go to the rotation tool, we can rotate it around, which has the camera then orbiting around the player. Okay?
So in this lesson, we’re gonna make it so that whenever we move our mouse, the camera rotates around a player.
So, let’s go to the event graph here, and what we’re gonna do is start off by deleting these three nodes that are here by default, so that we have a blank canvas here, and to begin, we’re gonna set up the horizontal movement. So, when we move the mouse left or right, the camera is gonna rotate left to right around the player.
And to do this, I’m gonna right click, and I’m gonna go mouse X, and you’ll see that we can get mouse events. We want to get the mouse X input event. And what this means, is pretty much whenever the mouse moves, this gets triggered and it sends over an axis value of the current map of the current horizontal mouse movement. Okay? So, this value ranges from negative one to one, negative one, meaning the mouse is moving left, one, meaning the mouse is moving right, and zero, meaning the mouse isn’t moving at all.
But with this, we aren’t actually gonna be rotating the spring arm or the camera, instead we’re going to be moving the entire player, left or right. We’re gonna be rotating them around, because, when we move around, we want the player to always be facing in the forward direction that the camera is.
So, we’re gonna drag off from here, and, we are gonna go to the add controller, your input node right here. And what this does is, this adds a certain value to our control is your input, which is the vertical rotation. So if we plug access value into the value input, that is all we need to do. And this is a node here, that is set up for us by this character movement component. This involves all of the movement, variables and properties that we can modify.
So if we save this compile press play, you should see that when we moved the mouse left and right, the character rotates to face the forward direction. So no matter where we look the player’s always rotating with us as we move the mouse. Okay?
So, we got the left and right movement, but, what about the up and down? Well, that’s a bit different because with this, we are gonna be rotating the spring arm. We don’t want to rotate the entire player, because that will look a bit weird so, we can be irritating the camera up and down. Okay? So, what we’re gonna do for that, is just like getting mouse X.
We now need to get mouse Y, so, mouse events, mouse Y here. And this trick is when we moved the mouse up and down, axis value again ranging from, a one being moving the mouse up, and negative one being moving the mouse down. And, we want to plug this into an add local rotation. Add local rotation. On the spring arm component here. Okay? There we go, creates that.
So, what we’re doing here is, we are gonna add a rotation to the spring arm, and that rotation is going to be along the Y axis. And the Y axis, if we go to the viewpoint here, select the spring on. The Y axis, is this green one here. So we can move this up and down, so the camera goes like so.
Okay. So, in event graph, to do that, what we’re gonna do is right click, and create a new node called make rotator. This basically creates a rotation for us, which we can then plug into, this add local rotation node like so, and X value, we are gonna be plugging into the Y axis here, on the micro data node. So, basically it was sending over a delta rotation, which is gonna be added on to the existing spring arm rotation.
So, if we press play, you’ll see that we can move left and right, but we can also move up and down. Like so. But something you may notice, is that if we keep moving down, our camera just keeps going around, and we can actually go 360 degrees, which isn’t probably what you want.
And something else you may also notice, is that when the camera goes down, and it hits the ground, it sort of moves towards the player. This is really cool, as the spring arm already has included with it, basically detecting if there’s anything between, the start of the spring arm and the camera. So if there’s anything between it, it pretty much snaps the camera, so, it is always looking at the plan. Nothing can ever get in the way between the camera, and the player. Which is really good. Okay?
But we still need to fix the issue of actually being able to rotate all the way around like this, as it may be very disorientating, to use as the player. So, what we need to do for this is clamp the rotation. And clamping the rotation basically means, we get the Y axis here, the Y rotation, and, we want to make sure that it never goes below a certain number, and never goes above a certain number.
So to do this, I’m gonna plug this add local rotation node, into a set relative rotation for the spring arm. So after we add the rotation to this spring arm, we then want to set it, so that is, make sure it’s in between two certain values. So over here, what we’re then gonna do, is create the get targets rotation node on the spring arm. And this year basically just returns to us the rotation of the spring arm currently.
So, we wanna get that. And then we want to plug this rotation here into a break rotator node. And what this does, is pretty much the exact opposite of make rotator. Instead of creating a rotation, it gets a rotation and breaks it up into each of the individual axis, which we can use. Now, since a vertical rotations are only dependent on the Y axis, we’re gonna get this, and we are gonna plug this into a clamp angle node here.
Okay. And this clamp angle node, has a few things. First of all, it has the actual angle that we are inputting into it, which is our Y axis. And then we have a min angle degrees, and a max angle degrees. And these are basically the min and max values that these angle can possibly be. So, the minimum rotation, we’re gonna have it at about 45 degrees, and the max rotation, we are gonna have it at 20. Okay. You might wanna tweak these numbers a bit, as we continue on, as you may want it a bit higher, you may want it a bit lower. Okay. So we got this value here.
Now we need to make it into a rotator again, so we can plug it into this set relative rotation node, so we’re gonna put this into make rotator, we actually wanna delete this connection, and make sure it was connected to the Y axis here. And, then we can just plug that into the new rotation input, on the set relative rotation node. And there we go. So this is pretty much it, in order to set it up so that it clamps the rotation. So it can’t get too high, or too low, let’s now press play and see if it works.
So here we go, we can rotate left and right. And if I moved down, you’ll see that it stops, when I reach here. I can no longer move the camera down anymore. I can move it up and it stops here. So I can’t move it up anymore either. So there we go. We’ve got our camera rotation set up. We can rotate left or right, we can rotate up and down, and, that is all good.
So, in the next lesson, we are going to be looking at actually setting up app play movement. So that we can actually start moving around, jumping, and then later on, setting up the abilities to actually attack.
Okay. Now before we go real quick, I’m gonna probably put these in some comments, so that they’re a bit neater. So, I’m gonna get these here, select these two nodes, right click, go create comment from selection, and I’m gonna call this one our camera, rotate, (keyboard clicking) camera rotates, horizontal. (keyboard clicking) Okay. We can then readjust these, like so, shrink this down a bit. It’s really good practice to actually, comment your nodes as later on, once you have many, many new nodes in your project. It may be quite hard to understand what you shouldn’t do, especially if they’re all messy, like this right here. So I’m just gonna tidy them up a bit.
We can then select them again, and, I’m gonna right click, create comment from selection, and this one is going to be called, our camera rotate vertical. All right. We can always select these again, readjust them to a position where we want them, and, there we go.
Okay. So yeah, overall, make sure you do comment your nodes, we’ll be doing this again in the future, for a movement for our jumping. Just so you can see what each of the different segments here are, at a quick glance. Okay. So, thank you for watching. And, I’ll see you all in the next lesson.
Interested in continuing? Check out the full Build an Action RPG in Unreal Engine course, which is part of our Unreal Game Development Mini-Degree.
]]>In this Unity tutorial, we’re going to cover how to set up the most essential component of farming-based games: planting crops. This tutorial will be based around a grid-based, top-down 2D project, but many of these concepts can be retrofitted as needed for 3D as well.
While some basic Unity experience is required, including knowledge of how to set up top-down 2D player characters, these explanations will be very beginner-friendly!
If you’re ready to start learning farming mechanics, let’s dive into Unity!
While you’ll need to set up your own Unity project, we have included a full copy of the code files used in this tutorial, as well as sprite assets used.
To start our tutorial off, we’re first going to set up our crop data and our game manager that will control the entire aspect of how crops are planted. We will be setting up our data based around a top-down, 2D game with grid-based, interactable tiles.
To start, let’s create a new C# script called “CropData”. This script is going to contain all the information specific to the crop we grow.
This script isn’t going to be attached to any GameObject. Instead, we want to create an asset for each of the different types of crop. To do this, simply replace MonoBehaviour with ScriptableObject and use the CreateAssetMenu property to create a menu item for it.
using System.Collections; using System.Collections.Generic; using UnityEngine; [CreateAssetMenu(fileName = "Crop Data", menuName = "New Crop Data")] public class CropData : ScriptableObject { }
This will allow us to right-click on the Project window to create a new Crop Data asset.
We’re going to create one for “Wheat”.
Crops will display different sprites depending on the number of days it has grown for. Let’s declare the following variables inside the script to track these sprites, as well as a few other details related to the crop (such as sell price):
using System.Collections; using System.Collections.Generic; using UnityEngine; [CreateAssetMenu(fileName = "Crop Data", menuName = "New Crop Data")] public class CropData : ScriptableObject { public int daysToGrow; public Sprite[] growProgressSprites; public Sprite readyToHarvestSprite; public int purchasePrice; public int sellPrice; }
Now we can save the script and go back to the Editor to start filling out the crop data for Wheat. You can fill whatever data you want, but we used:
To manage the crops, we will create a new C# script called “GameManager“. We need to know primarily what the current day is, how much money we have, and what crop we want to plant.
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Events; using TMPro; public class GameManager : MonoBehaviour { public int curDay; public int money; public CropData selectedCropToPlant; }
We can set up a Singleton for this script so that we can easily access this script from other scripts as well.
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Events; using TMPro; public class GameManager : MonoBehaviour { public int curDay; public int money; public CropData selectedCropToPlant; // Singleton public static GameManager instance; void Awake () { // Initialize the singleton. if(instance != null && instance != this) { Destroy(gameObject); } else { instance = this; } } }
Now we want to implement the following functionalities:
Let’s create some dummy functions first:
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Events; using TMPro; public class GameManager : MonoBehaviour { public int curDay; public int money; public int cropInventory; public CropData selectedCropToPlant; public event UnityAction onNewDay; // Singleton public static GameManager instance; void Awake () { // Initialize the singleton. if(instance != null && instance != this) { Destroy(gameObject); } else { instance = this; } } // Called when a crop has been planted. // Listening to the Crop.onPlantCrop event. public void OnPlantCrop (CropData cop) { } // Called when a crop has been harvested. // Listening to the Crop.onCropHarvest event. public void OnHarvestCrop (CropData crop) { } // Called when we want to purchase a crop. public void PurchaseCrop (CropData crop) { } // Do we have enough crops to plant? public bool CanPlantCrop () { } // Called when the buy crop button is pressed. public void OnBuyCropButton (CropData crop) { } }
Next, let’s set up the ability to actually grow our crops.
Let’s create a new C# script called “Crop“. This is going to spawn in the crops once we put the seeds down.
First of all, we need to declare the variables and make sure to set up UnityEngine.Events so we can automatically trigger events when we plant or harvest a crop:
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Events; public class Crop : MonoBehaviour { private CropData curCrop; private int plantDay; private int daysSinceLastWatered; public SpriteRenderer sr; public static event UnityAction<CropData> onPlantCrop; public static event UnityAction<CropData> onHarvestCrop; }
We can then keep track of the number of days that the crop has been planted (i.e. Crop Progress), by subtracting plantDay from GameManager.curday.
Let’s return a boolean to check if the crop is old enough for harvesting.
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Events; public class Crop : MonoBehaviour { private CropData curCrop; private int plantDay; private int daysSinceLastWatered; public SpriteRenderer sr; public static event UnityAction<CropData> onPlantCrop; public static event UnityAction<CropData> onHarvestCrop; // Returns the number of days that the crop has been planted for. int CropProgress () { return GameManager.instance.curDay - plantDay; } // Can we currently harvest the crop? public bool CanHarvest () { return CropProgress() >= curCrop.daysToGrow; } }
Now, when we harvest the crop, we’re going to invoke the onHarvestCrop event and destroy the crop gameObject. At the same time, we’ll also add our water function, which simply changes daysSinceLastWatered to 0.
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Events; public class Crop : MonoBehaviour { private CropData curCrop; private int plantDay; private int daysSinceLastWatered; public SpriteRenderer sr; public static event UnityAction<CropData> onPlantCrop; public static event UnityAction<CropData> onHarvestCrop; // Called when the crop has been watered. public void Water () { daysSinceLastWatered = 0; } // Called when we want to harvest the crop. public void Harvest () { if(CanHarvest()) { onHarvestCrop?.Invoke(curCrop); Destroy(gameObject); } } // Returns the number of days that the crop has been planted for. int CropProgress () { return GameManager.instance.curDay - plantDay; } // Can we currently harvest the crop? public bool CanHarvest () { return CropProgress() >= curCrop.daysToGrow; } }
Note that the question mark next to the unity event (onHarvestCrop?.Invoke) allows us to invoke it only if we have functions listening to the event. This effectively prevents errors from popping up.
When the crop has progressed, we can check if the crop is ready to be harvested. If it is, then we should change the sprite of the crop.
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Events; public class Crop : MonoBehaviour { private CropData curCrop; private int plantDay; private int daysSinceLastWatered; public SpriteRenderer sr; public static event UnityAction<CropData> onPlantCrop; public static event UnityAction<CropData> onHarvestCrop; // Called when the crop has progressed. void UpdateCropSprite () { int cropProg = CropProgress(); if(cropProg < curCrop.daysToGrow) { sr.sprite = curCrop.growProgressSprites[cropProg]; } else { sr.sprite = curCrop.readyToHarvestSprite; } } // Called when the crop has been watered. public void Water () { daysSinceLastWatered = 0; } // Called when we want to harvest the crop. public void Harvest () { if(CanHarvest()) { onHarvestCrop?.Invoke(curCrop); Destroy(gameObject); } } // Returns the number of days that the crop has been planted for. int CropProgress () { return GameManager.instance.curDay - plantDay; } // Can we currently harvest the crop? public bool CanHarvest () { return CropProgress() >= curCrop.daysToGrow; } }
When a new day takes over, we will increment the daySinceLastWatered, and destroy the crop if it has been more than 3 days.
If it hasn’t, then we can call UpdateCropSprite.
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Events; public class Crop : MonoBehaviour { private CropData curCrop; private int plantDay; private int daysSinceLastWatered; public SpriteRenderer sr; public static event UnityAction<CropData> onPlantCrop; public static event UnityAction<CropData> onHarvestCrop; // Called when a new day ticks over. public void NewDayCheck () { daysSinceLastWatered++; if(daysSinceLastWatered > 3) { Destroy(gameObject); } UpdateCropSprite(); } // Called when the crop has progressed. void UpdateCropSprite () { int cropProg = CropProgress(); if(cropProg < curCrop.daysToGrow) { sr.sprite = curCrop.growProgressSprites[cropProg]; } else { sr.sprite = curCrop.readyToHarvestSprite; } } // Called when the crop has been watered. public void Water () { daysSinceLastWatered = 0; } // Called when we want to harvest the crop. public void Harvest () { if(CanHarvest()) { onHarvestCrop?.Invoke(curCrop); Destroy(gameObject); } } // Returns the number of days that the crop has been planted for. int CropProgress () { return GameManager.instance.curDay - plantDay; } // Can we currently harvest the crop? public bool CanHarvest () { return CropProgress() >= curCrop.daysToGrow; } }
Finally, we will set up the initial values for the variables when we first plant the crop (i.e. curCrop, plantDay, daysSinceLastWatered).
Once they’re set, we can update the crop sprite and invoke the onPlantCrop event.
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Events; public class Crop : MonoBehaviour { private CropData curCrop; private int plantDay; private int daysSinceLastWatered; public SpriteRenderer sr; public static event UnityAction<CropData> onPlantCrop; public static event UnityAction<CropData> onHarvestCrop; // Called when the crop has been planted for the first time. public void Plant (CropData crop) { curCrop = crop; plantDay = GameManager.instance.curDay; daysSinceLastWatered = 1; UpdateCropSprite(); onPlantCrop?.Invoke(crop); } // Called when a new day ticks over. public void NewDayCheck () { daysSinceLastWatered++; if(daysSinceLastWatered > 3) { Destroy(gameObject); } UpdateCropSprite(); } // Called when the crop has progressed. void UpdateCropSprite () { int cropProg = CropProgress(); if(cropProg < curCrop.daysToGrow) { sr.sprite = curCrop.growProgressSprites[cropProg]; } else { sr.sprite = curCrop.readyToHarvestSprite; } } // Called when the crop has been watered. public void Water () { daysSinceLastWatered = 0; } // Called when we want to harvest the crop. public void Harvest () { if(CanHarvest()) { onHarvestCrop?.Invoke(curCrop); Destroy(gameObject); } } // Returns the number of days that the crop has been planted for. int CropProgress () { return GameManager.instance.curDay - plantDay; } // Can we currently harvest the crop? public bool CanHarvest () { return CropProgress() >= curCrop.daysToGrow; } }
We now need to register listeners for the events we made. Right now the events are invoked, but there are no functions listening to them.
Let’s open up the GameManager script, and subscribe to the events inside OnEnable.
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Events; using TMPro; public class GameManager : MonoBehaviour { public int curDay; public int money; public int cropInventory; public CropData selectedCropToPlant; public event UnityAction onNewDay; // Singleton public static GameManager instance; void OnEnable () { Crop.onPlantCrop += OnPlantCrop; Crop.onHarvestCrop += OnHarvestCrop; } void OnDisable () { Crop.onPlantCrop -= OnPlantCrop; Crop.onHarvestCrop -= OnHarvestCrop; } ... }
Here we can define what to execute when onPlantCrop and onHarvestCrop are invoked.
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Events; using TMPro; public class GameManager : MonoBehaviour { public int curDay; public int money; public int cropInventory; public CropData selectedCropToPlant; public event UnityAction onNewDay; // Singleton public static GameManager instance; void OnEnable () { Crop.onPlantCrop += OnPlantCrop; Crop.onHarvestCrop += OnHarvestCrop; } void OnDisable () { Crop.onPlantCrop -= OnPlantCrop; Crop.onHarvestCrop -= OnHarvestCrop; } // Called when a crop has been planted. // Listening to the Crop.onPlantCrop event. public void OnPlantCrop (CropData cop) { cropInventory--; } // Called when a crop has been harvested. // Listening to the Crop.onCropHarvest event. public void OnHarvestCrop (CropData crop) { money += crop.sellPrice; } }
Last but not least, we need to be able to plant crops, so we will set up an Interact function that can be called by the player whenever they interact with the tiles we can plant on.
On any script that is controlling your field tiles, add in the Interact function (which will ultimately be invoked by your player character in whatever manner you choose):
// Called when the player interacts with the tile. public void Interact () { if(!tilled) { Till(); } else if(!HasCrop() && GameManager.instance.CanPlantCrop()) { PlantNewCrop(GameManager.instance.selectedCropToPlant); } else if(HasCrop() && curCrop.CanHarvest()) { curCrop.Harvest(); } else { Water(); } }
After this, we’ll create a new function called PlantNewCrop, which takes a parameter of CropData (which is the crop we want to plant).
Planting should only occur if the tile is tilled.
// Called when we interact with a tilled tile and we have crops to plant. void PlantNewCrop (CropData crop) { if(!tilled) return; curCrop = Instantiate(cropPrefab, transform).GetComponent<Crop>(); curCrop.Plant(crop); GameManager.instance.onNewDay += OnNewDay; }
Note that we’re subscribing to the GameManager’s OnNewDay event here rather than OnEnable, because not every tile needs to listen to the event. We want to subscribe to it once we have a crop planted.
Next, we’ll create a function for Till and Water, where we will switch the tile’s sprite.
// Called when we interact with a grass tile. void Till () { tilled = true; sr.sprite = tilledSprite; } // Called when we interact with a crop tile. void Water () { sr.sprite = wateredTilledSprite; if(HasCrop()) { curCrop.Water(); } }
If we no longer have any crop on this tile, then we just want to reset the tile and unsubscribe from the OnNewDay event.
If we do have a crop planted and a new day occurred, then we need to trigger the crop’s NewDayCheck:
// Called every time a new day occurs. // Only called if the tile contains a crop. void OnNewDay () { if(curCrop == null) { tilled = false; sr.sprite = grassSprite; GameManager.instance.onNewDay -= OnNewDay; } else if(curCrop != null) { sr.sprite = tilledSprite; curCrop.NewDayCheck(); } }
Let’s save this script and go to the Editor to test it out.
Make sure that the Crop script has the sprite renderer assigned:
Save the Crop GameObject as a prefab and assign sprites to your tile object. We’ve included an example of our FieldTile script so you can see how we did it, but your results will vary.
Finally, we can set up the GameManager with the public variables filled in.
We can now till the ground, water the ground, and plant crops!
And that covers the basics of setting up farming mechanics for farming sim games! Congratulations for making it through!
As mentioned, farming is very popular, and it can add variety to tried and true genres. However, keep in mind our way is not the only way. Depending on your own game project, you may need to adapt these systems. However, we’ve designed these systems to be expandable and fit in with most top-down, 2D games.
From here, though, you can try out tons of other features using the same fundamentals. Perhaps you want to have livestock, which you can also set up as Scriptable Objects and utilize the Unity events system to also have an invokable Interact function. Or, maybe you want to create a farming sim on an alien world where the crops can be poisonous! The choice is yours!
Regardless, we hope you’ve expanded your horizons with farming, and we wish you the best of luck with your future projects!
Want to learn more about farming sims? Try our complete Construct a Micro-Farming RPG course.
]]>Start creating third-person action RPGs and more by first learning how to set up player movement with orbital camera! You can also extend the material covered here by checking out the full course below!
Action RPG Development for Beginners
Created by Daniel Buckley, this mini-course will show you step-by-step how to set up a third-person character controller using simple primitive objects in Unity. You’ll learn key skills including how to prepare the player in Unity, how to add standard movements like walking and jumping, and how to implement features for the camera to orbit around the player. Regardless of what sort of game you want to build, these player scripts will provide a strong foundation as you expand your skills and build your dream game projects!
]]>Authored by Unreal Engine expert Daniel Buckley, this eBook explores all there is to know about getting started with Unreal Engine development. You’ll learn not only how to work with the foundations of the engine itself, but also develop several projects from scratch including an FPS game, an action RPG, a road-crossing game, a platfromer, and a puzzle-like game! In addition, you’ll do all this while using the Blueprints Visual Scripting system – meaning no prior coding knowledge is required or needed to create your game projects!
Whether you’ve always wanted to learn Unreal Engine or want to start putting knowledge into practice, this eBook will help you gain the in-demand skills necessary to start building your dream game projects!
]]>In over 230 pages of awesomeness, the book starts from zero and covers all the way to building simple 2D and 3D games with Unity. Some of the topics covered in the course are:
We hope this free ebook helps you gain valuable, real-world skills and we very much look forward to play your games!
This book is provided at no cost in PDF format.
What type of game will you be building in Unity? Share it in the comments!
]]>