Back to Projects

Bachelor Graduation Project

Previous Pause Next

Time: 4 Months
Date: Spring 2014
Role: General Programmer
Tools: C#, Unity
Platforms: PC
Description:

For the graduation project of my bachelor degree a data driven game prototype was built. This graduation project was done in collaboration with another student at the same university who developed the game design as his graduation project. The project's concept is a role-playing game similar to Golden Sun and Final Fantasy VI but with a unique combat system. The game contains two major game-states: the dungeon state and a battle state. Within the dungeon state the player is able to walk around freely and in order to progress further has to solve puzzles. The player will occasionally encounter monsters while moving through a dungeon level which triggers the combat game-state. In this game-state the player's goal is to defeat all enemies. The combat system contains a mix between turn-based and real-time elements. Each unit can perform actions, such as attack move, defend move and cast spell. Actions on units are performed in parallel and real-time. During the selection of a new action by the player time frozen until a complete action is selected, giving the player ample time to think about the following action(s) they will perform.

My primary goal for the prototype was to make everything as data driven as possible to allow the designer to experiment to his heart's content with the available systems. A constraint for this data driven system was to make it as robust as possible and give as much feedback about the data at 'compile time' to avoid errors showing up mid game. The secondary goal was to build everything as flexible as possible, to account for the ever evolving game design being developed by the game design student.

The most impactful system for data driving the prototype was the static data system, which allowed for easy definition and handling of data files. This static data system was used in almost every part of the prototype and contained definitions for the AI, battle abilities, enemies, user interface layouts and much more. The system works by taking two JSON input files, the schema file which defines the fields, data types and restrictions on values for fields, and the data file which defines the data according to the format defined by the associated schema. The schema file is then taken by the system and is converted to a C# structure which contains all the fields with the correct data types and a deserialisation routine. The data file is then checked for errors and converted to a binary data file if no errors are reported. The systems is fully integrated with the Unity editor and will automatically rebuild data to make the system have as little impact as possible to the workflow.

A visual programming system was developed to aid in the construction for dungeon levels. This system allows for easy definition of gameplay logic by the level editor without the need for specialised scripts. The system works with linking operation nodes together to specify the desired logic.


This is a basic schema file used to define a status effect. This one of the most basic schema file used in the prototype.

Features such as inheritance and nesting data types are not shown here but are supported by the system. In addition to this the system can create two types of data structures, hard and soft data structures. In a hard data structure values are accessed by using the C# properties created for each field, while in a soft data structure values are accessed through the use of an index much like how values are accessed when using a dictionary (C#) or map (C++).
{
	"StatusEffectType": 
	{
		"type": "EStatusEffectType",
		"dataSource": "Filename"
	},
	"EffectIconPath":
	{
		"type": "string"
	},
	"EffectAmount":
	{
		"type": "float",
		"validator": "NumericRange",
		"validatorArgs": "-1.0, 1.0",
		"required": false
	},
	"TickTimeSeconds":
	{
		"type": "float",
		"validator": "NumericRange",
		"validatorArgs": "0.01, 1e25",
		"required": false
	},
	"DurationInSeconds":
	{
		"type": "float",
		"validator": "NumericRange",
		"validatorArgs": "0.01, 1e25",
		"required": false
	}
}


A data file to be used with the status effect schema file. This data defines the 'Burn' status effect, which inflicts 0.02% damage every 1 second in battle time. The effect times out after 10 seconds.
{
	"EffectAmount": 0.02,
	"TickTimeSeconds": 1.0,
	"EffectIconPath": "UserInterface/Images/StatusEffects/Burn",
	"DurationInSeconds": 10.0
}


The C# output class generated for the status effect schema.
//-------------------DO NOT MODIFY-------------------//
//This file was generated by the static data compiler//
//                                                   //

using UnityEngine;
using System.Collections.Generic;
using FairyTale.Core;
using FairyTale.Utility;

namespace FairyTale.StaticData
{
	[StaticDataInfo("StatusEffectData/", 635379105708561162)]
	public class StatusEffectData : StaticDataStructure
	{
		public FairyTale.Statistics.EStatusEffectType StatusEffectType 
		{
			get;
			private set;
		}
		
		public string EffectIconPath 
		{
			get;
			private set;
		}
		
		public float EffectAmount 
		{
			get;
			private set;
		}
		
		public float TickTimeSeconds 
		{
			get;
			private set;
		}
		
		public float DurationInSeconds 
		{
			get;
			private set;
		}
		
		public override void Deserialize(StaticDataSerializer a_Serializer)
		{
			object StatusEffectType_Local = StatusEffectType;
			a_Serializer.Serialize(typeof(FairyTale.Statistics.EStatusEffectType), ref StatusEffectType_Local);
			StatusEffectType = (FairyTale.Statistics.EStatusEffectType)StatusEffectType_Local;
			
			object EffectIconPath_Local = EffectIconPath;
			a_Serializer.Serialize(typeof(string), ref EffectIconPath_Local);
			EffectIconPath = (string)EffectIconPath_Local;
			
			bool EffectAmount_ValueIsPresent = false;
			a_Serializer.Serialize(ref EffectAmount_ValueIsPresent);
			if (EffectAmount_ValueIsPresent)
			{
				object EffectAmount_Local = EffectAmount;
				a_Serializer.Serialize(typeof(float), ref EffectAmount_Local);
				EffectAmount = (float)EffectAmount_Local;
				
			}
			bool TickTimeSeconds_ValueIsPresent = false;
			a_Serializer.Serialize(ref TickTimeSeconds_ValueIsPresent);
			if (TickTimeSeconds_ValueIsPresent)
			{
				object TickTimeSeconds_Local = TickTimeSeconds;
				a_Serializer.Serialize(typeof(float), ref TickTimeSeconds_Local);
				TickTimeSeconds = (float)TickTimeSeconds_Local;
				
			}
			bool DurationInSeconds_ValueIsPresent = false;
			a_Serializer.Serialize(ref DurationInSeconds_ValueIsPresent);
			if (DurationInSeconds_ValueIsPresent)
			{
				object DurationInSeconds_Local = DurationInSeconds;
				a_Serializer.Serialize(typeof(float), ref DurationInSeconds_Local);
				DurationInSeconds = (float)DurationInSeconds_Local;
				
			}
		}
		
	}
}


Back to Projects