Scorchball was my MSc dissertation project. I created a fictional sport, where 2 teams of 3 players each, play a game where you have to score goals throwing a ball through rings at the end of the pitch.
For the moment is single player, the other 3 dragon riders are AI controlled, using FSM to chase the ball, dribble, look for best pass positions and attack other players throwing fireballs (hence the name Scorchball).
It was developed in Unity in around 3 months. Want to know how it was done? Keep reading.
Since the dissertation topic was about camera preferences, because it had to follow a scientific methodology and Game Dev per se wasn’t it, I chose the analysis of the camera type as the hypothesis (with Cinemachine it was something easy to change, although it probably needs more tuning) and the side product was the game (which was the fun part).
In Scorchball, you take the role of a dragon rider in a ball-scoring game with two teams, each with three players. The objective is to pass the Ball through one of the rings in the opponent’s court. The team with the most goals is the winner.
It has a medieval, low-poly vibe, with dragons, knights, fireballs, castles, and a small village (eventually you’ll see some villagers doing their things and running from you).
Some of the actions that can be done in Scorchball are:
The dragon model is Unka the Dragon. The asset has three different dragon styles with colour variants. It comes wholly rigged with varying animations for walking, running, flying, swimming, attacking, taking damage, and evading, among many others.
The asset comes with a demo animator and scene with basic functionality, but the animator has many unnecessary things (for example, the swimming or running animations), and the controller depends on many proprietary systems, so for the sake of simplicity, new components were developed and optimized for Scorchball.
There are 5 main objects in Scorchball that compose the gameplay:
It is based on the Simple Soccer ball class (from Matt Bucklands “Programming AI by Example”). Each Game has only one Ball, which can be accessed through the Game Manager instance.
Some of the main differences between the Ball from Scorchball and the one from Simple Soccer are that the physics for the scorch are all managed by Unity (with its rigid body and collider components). The other one is that the Ball in Scorchball gets attached to the rider that touches it into an anchor point in the mouth of the Dragon Rider, so it has methods to attach to a parent (and disable the colliders) and a method to drop it, for example, when hit.
If you are good enough, you can impede a goal by throwing a fireball to the Ball and divert its trajectory.
As Buckland states, we don’t need to develop an accurate simulation of a sport and tactics, we only need to produce “agents capable of providing an entertaining challenge for the player”. At the moment they still need improvement, but because of the time constraint (I had 3 months to have a playable demo, write the dissertation document, do the testing, analyze the data and work part-time) I deemed this good enough for now.
The first step was coding a simple Character Controller, that allowed the player to fly, capture and throw the ball. Nothing complex (yet).
Next was developing the AI, starting with the Steering Behaviours. First I tried to use the native AI system that comes with Unity, but it relies on NavMesh and is mostly thought for grounded gameplay. Since all the action in my Game happens in the sky, and the terrains are big enough that Navmesh could become computationally expensive, I developed my own Seek, Arrive, Flee and Interpose behaviours, based on the ones we programmed for Python in the Game AI module in the first term of my Master.
Once the behaviours were done, a RiderState class was created. All states for the AI DragonRiders inherit from this class. Also, a DragonClass was designed to contain and manage each Rider’s FSM and their basic components (like animations, SteeringBehaviours, currentState, speed and functions like ThrowBall or ThrowFireball).
Just like any other state in an FSM, it has a name, access to some components, and an Enter, Update, and Extit function. The current stage is processed in the DragonRider (later DragonRiderAI) update function.
using UnityEngine;
public class RiderState: State { public enum RIDER_STATE { WAIT, RECEIVE_BALL, THROW_BALL, DRIBBLE, CHASE_BALL, INTERCEPT, ATTACK, TAKE_DAMAGE, RETURN_TO_HOME, SUPPORT_ATTACKER, GAME_OVER, NOT_INITIALIZED }; public RIDER_STATE name; protected DragonRiderAI rider; protected Animator anim; protected SteeringBehaviours steeringBehaviours; protected Ball ball; protected new RiderState nextState; public RiderState(DragonRiderAI _rider, Animator _anim, SteeringBehaviours _steeringBehaviours) { rider = _rider; anim = _anim; steeringBehaviours = _steeringBehaviours; ball = GameManager.instance. Ball; } public new RiderState Process() { //Debug.Log("Process Rider State: " + stage); if (stage == EVENT.ENTER) Enter(); if (stage == EVENT.UPDATE) Update(); if (stage == EVENT.EXIT) { Exit(); return nextState; } //If we are not changing the State, return the same one return this; } }
The first states developed were: ChaseBall, Dribble and ThrowBall.
With these states, we had an agent that could get the Ball, move towards a position, and once close enough, call the Ball’s throw method to throw it to a given direction with a given force.
Since we didn’t want our riders to act like small kids, running after the Ball without any kind of strategy, we needed to create a new class to manage all the riders from the same team, and be aware of the opponents, to track the important positions, such as the rider closer to the Ball, the support rider (the rider closer to the one with the Ball), and also to track the State of the Game, a Team class was created.
One of the most interesting methods in this class is the CalculateBestSupportSpot. This method creates an array of points around the Ball and evaluates their potential for a good pass considering:
The best support spot (BSS) is only calculated every second if the Team’s currentState is Attacking since we don’t want to waste resources.
That’s why we have a class that inherits from State and manages the possible states a team can be in: Prepare for Kickoff, Attacking, Defending and Game Over.
Prepare for Kickoff -> is called at the beginning of the Game and after every goal. It transports the human player to a position in the centre of the screen and locks the movement. It also instructs the AI to return to their starting home positions.
Defending -> In this State, the Team class sends the closest player to chase the Ball, and the remaining two riders go to their defending home positions, closer to their own goals, to try to intercept the attacker when it gets closer to the goals. We transition to this State after the kickoff and when opponents touch the Ball.
Attacking -> is called when a team member becomes the Ball’s parent. Once this happens, we send all non-key players to their new home positions closer to the opponent’s goal. We assign the role of SupportAttacker to the rider closest to the one with the Ball (if the closest is the human player, we assign the other rider as a supporter, no matter if it’s far away).
The Dragon Riders (again)
Once we had a team that managed the simple strategy, it was time to add the rest of the states. The next ones are Support Attacker, Receive Ball, Intercept, Attack, and Take Damage.
Most states are self-explanatory, but here is a bit on how the “support attacker” works.
Once a player gets promoted to support the attacker, the arrive steering behaviour is used to get to the BSS. When it gets close enough it will request the Team class a pass.
The team will evaluate if a pass can be made or not. If the rider controlling the Ball is in a position to score, it won’t pass. If the controlling rider is the human player, it won’t send the pass (sending a pass with human control is a manual process where you must select the teammate you are sending the pass first). If opponents can interfere with the pass, it won’t pass. However, if the pass is a good idea, the controlling player will be asked to send the pass to the BSS. The team will assign the requesting player the position of Receive Ball (and also make it the controlling player). The player waiting for the pass will then make sure that it’s in the right position (or move to it), and once it receives it, it will change to dribble.
Meanwhile, the sender will now become the supporter player, trying to find the best position to request the Ball back.
Each team has three ring goals at the end of their zones. These objects have a trigger collider that, when it detects the Ball, sends a signal to the Game Manager, letting it know that a goal was scored so it can update the GUI, send teams back to Kickoff positions, and restart the Game.
It doesn’t matter who throws the Ball; if you send it through your own ring, you will give your opponents a goal!
Unlike its Simple Soccer counterpart, the field here doesn’t have a class. It consists of 6 rectangular prisms with collision boxes, which don’t render any mesh and don’t allow the Dragons or the Ball to get out.
The project has a lot of room for improvement, specially the control and the AI. A multiplayer mode would be also nice and obviously some cosmetic enhancements, different classes with different attributes, power-ups, a mini-map… the list keeps growing. Stay tunned in the following months to see some.
If you want more information, have questions or would like to help don’t hesitate to contact me through the Contact Form.