Omniscience
At a Glance
  • Time Frame:
  • May 3, 2014 - May 24, 2014

  • Roles:
  • Lead Game Designer - Engineering Lead

  • Technology:
  • Blender - Unity - Javascript

Summary

Omniscience is a 3D side scrolling multiplayer action RPG. In this prototype, the player has access to nine unique spells and one weapon. Using the spells and weapon at his or her disposal, the player must fight alone or with friends through a small horde of enemies to defeat the boss at the end of the level. Within the span of a few weeks, our group of five completed this working prototype as our high school senior capstone project. We then presented Omniscience to a large group of people and a panel of judges.

Details

The original idea behind omniscience stemmed from my desire for an RPG in which a player creates his or her own story / narrative; not by meticulous means on the player’s behalf, but rather the player’s interaction with the world and the characters. That is to say that the player forges their own adventure by playing the game while the world reacts to the player’s choices. I wanted something more than the standard procedure of milling through scripted dialogue to receive scripted quests. That was my original vision of the complete game, but of course the prototype for the project could not be as grand. We decided to create a small set of spells and a weapon to use in an enclosed environment.

Combat

The finished prototype that we presented allowed for up to four players to battle a couple different monsters in a small three section level. The combat wasn’t fully realized lacking the weapon types, mechanics, and versatility that I initially envisioned. It is also not nearly as fluid as I had hoped, but it was a good start. Programming the combat system was a difficult process because it was my first attempt at developing such a robust combat system. I was trying to make the perfect advanced system that I planned for the finished product. I had fully intended to continue working on the project after the deadline, and I let myself lose sight of the initial project goal. I lost a lot of time developing features that we couldn’t make use of in the prototype, but I learned the importance of narrowing my focus to meet time constraints. The Attack () function is an example of how the uses weapons.

Attack () Function Example

// attempts to attack in the direction the character is facing
function Attack()
{
	PlaySound(attackSounds[attackNum - 1]);

	// initiate attack if character is an npc
	if(npc)
	{
		startCoolDown = Time.time;
		coolDown = w.attackSpeed;
		coolDownTime = w.attackSpeed;
		attacking = true;
	}
	// otherwise apply weapon or spell costs to player character
	else
	{
		source.Activate(w.attackSpeed);

		if(w.energyCost > 0 || w.healthCost > 0)
		{
			w.Cast(p.p);
		}
	}

	// halt character movement
	p.canMove = false;

	// wait for peak of attack
	yield WaitForSeconds(w.attackSpeed / 2);

	// apply damage for characters hit by attack
	if(w.damage > 0)
	{
		var hits : RaycastHit[];
		var charCtrl : CharacterController = GetComponent(CharacterController);
		var p1 : Vector3 = -p.character.forward * charCtrl.radius + transform.position + 
		charCtrl.center + Vector3.up * (-charCtrl.height*0.5);
		var p2 : Vector3 = p1 + Vector3.up * charCtrl.height;

		// get all characters hit by attack
		hits = Physics.CapsuleCastAll (p1, p2, charCtrl.radius, p.character.forward, w.swingRadius + charCtrl.radius);

		for (var i = 0;i < hits.Length;i++)
		{
			var hit : RaycastHit = hits[i];	
			var kb;

			// get direction of attack for knockback
			if(p.faceDirection == "forward")
			{
				kb = w.knockBack;
			}
			else
			{
				kb = -w.knockBack;
			}

			// if the hit character is a player and the attacker is an npc
			if(hit.collider.GetComponent("Player") && npc)
			{
				// inflict damage to character
				hit.collider.GetComponent("Player").ApplyDamage(w.damage, w.damageType, w.damagePen, p.p);

				// apply knockback
				if(w.knockBack)
				{
					hit.collider.GetComponent("Player").KnockBack(kb, w.knockBackStun);
				}
			}

			// if the hit character is a npc
			if(hit.collider.GetComponent("NPC"))
			{
				// inflict damage to character
				hit.collider.GetComponent("NPC").ApplyDamage(w.damage, w.damageType, w.damagePen, p.p);		
				PlaySound(hitSounds[Random.Range(0,w.soundsNum)]);

				// apply knockback
				if(w.knockBack)
				{
					hit.collider.GetComponent("NPC").KnockBack(kb, w.knockBackStun);
				}
			}

			// apply hit effects to character
			if(hit.collider.CompareTag("Player"))
			{
				Instantiate(blood, hit.point + Vector3.up, Quaternion.identity);
				PlaySound(hitSounds[Random.Range(0,w.soundsNum)]);
			}
		}
	}

	// if attack launches projectile
	if(w.projectile)
	{
		pr = Network.Instantiate(w.projectile, transform.position + p.character.forward, transform.rotation, 0);

		if(!npc)
		{
			t = Camera.main.ScreenToWorldPoint(Vector3(Input.mousePosition.x, Input.mousePosition.y, 10));
			t.z = 0.0;
			pr.transform.LookAt(t);
		}
		else
		{
			pr.transform.LookAt(p.target);
		}

		pr.GetComponent("Projectile").damage = w.projectileDamage;
		pr.GetComponent("Projectile").speed = w.projectileSpeed;
		pr.GetComponent("Projectile").damageType = w.damageType;
		pr.GetComponent("Projectile").damagePen = w.damagePen;
	}

	// wait for rest of attack
	yield WaitForSeconds(w.attackSpeed / 2);

	// iteratre through attack sequence
	if(attackNum < w.attacks)
	{
		attackNum += 1;
	}
	else
	{
		attackNum = 1;
		swinging = false;
	}

	p.canMove = true;

	// reset attack sequence if done attacking
	if(!attacking)
	{
		swinging = false;
		attackNum = 1;
	}
}