Rpg combat system tutorial for Unity with simple state machine – part 3

Share

Rpg combat system tutorial for Unity with simple state machine – part 3

At this point of this rpg combat system tutorial for Unity our scene has 2  important gameobjects, the player and the enemy, with their Collision Box components named CombatBox belonging to a user created Physic Layer that is now behaving as any other default layer. We must change the Combat Layer physic behaviour to make this Layer independent from the others, this way every gameobject belonging to the Combat Layer will calculate collisions only with gameobjects belonging to the Combat Layer, ignoring every other Layer. We need this behaviour as the rpg combat system is a gameplay logic system, not a physic system, so we need to isolate its collision detection to avoid unwanted behaviours. In order to achieve this we need to use the Unity Layer Collision Matrix.

  • Open the Physics Preference Panel by clicking on Edit->Project Settings->Physics.
  • Select which layers on the Layer Collision Matrix will interact with the other layers by checking them. In our case, we want our Combat Layer to interact only with itself, check the matrix accordingly as you can see in the following image : 01-unity-rpg-combat-tutorial-matrix-collision-layer

As you can see, this Unity Layer Collision Matrix configuration makes our Combat Layer ignoring collisions with every other Layer including the default one. Now we need to modify our scripts, and introduce the actual combat system code in Unity.

Getting the player character attack and damage the enemy

The first script we are going to modify is the PlayerController script, double click on it the project panel to edit it with MonoDevelop or your preferred code editor. On line 22 check for the variables declaration section, and change the boolean variable isAttacking from private to public, so we can check its status from other scripts. The modified PlayerController section should look like the following :

private var isRunning : boolean ;
private var isWalking : boolean ;
private var isStrafing : boolean ;
private var isJumping : boolean ;
// CHANGE isAttacking to PUBLIC to access it from
// CombatBox script
public var isAttacking : boolean ; 
private var isIdle : boolean ;
/* Create boolean status variables to identify animation
status, e.g. what am i doing right now?               */

Again inside the PlayerController script, near the end at line 150, modifiy the slash() function call when left clicking with :

/* Play attack animation calling slash function 
and make sure it isn calling more than once */
if(Input.GetButtonDown("Fire1") && !isAttacking){
slash() ;
}

Now after these lines modify the slash function to add the combat system state machine basic feature :

//Modify the attack Slash function to change the isAttacking
//state variable accordingly to the timing of the
//attack animation
function slash(){
isAttacking = true ;
animation.CrossFade("attack");
yield WaitForSeconds (animation["attack"].length);
isAttacking = false ;
}

If you press the Play button in the Unity editor at this point of this rpg combat system tutorial the player character should move and play the attack animation, but it still can’t damage the enemy ! In order to achieve this needed combat system feature we need to add a new script to the CombatBox gameobject child of our player character, this new javascript script will be named CombatSystem and will manage collisions and states. Create a new javascript named CombatSystem in the Scripts folder, this script will manage the rpg combat system and state machine for our SpartanKing player character and will be attached as a component to his CombatBox child gameobject, the following code is commented to explain how it works :

/* CombatSystem.js - Unity rpg combat system tutorial - www.Gameobject.net */
/* Author : Piffa of www.Gameobject.net */
#pragma strict
/* Declaring and inizializing private variables to define collisions states */
public var isTouching : boolean = false; // player is touching an enemy collider box
public var wasTouching : boolean = false;// player was touching an enemy collider box
public var isEngaged : boolean = false;  // player and enemy are engaged in combat
public var isHitting : boolean = false;  // player is landing an hit and dealing dmg
public var isHitted : boolean = false ; //  enemy is landing an hit to player
public var playerControllerScript : PlayerController ; //handle to access parent player gameobject
private var playerIsAttacking : boolean ; //read from Playercontroller.js if player is attacking
private var enemyIsAttacking : boolean ; //read from enemyAi.js if enemy is attacking
private var collidingEnemy : GameObject ; // reference to enemy gameobject we are colliding with

public var playerHealthPoints : int = 20 ; // player health pool
private var playerDamage : int = 2 ; // amount of damage dealt by player with 1 hit

function Start () {
playerControllerScript =  transform.parent.gameObject.GetComponent(PlayerController);
//Get the PlayerController script in our parent gameobject and store in a variable
}

function Update () {
/* player is engaged in combat if for the first time is touching the enemy's collider */
    if(!wasTouching && isTouching){
	isEngaged = true;	
	};	 
/* player is not engaged anymore if was touching the enemy and now it's not anymore */
	if(wasTouching && !isTouching){
	isEngaged = false;	
	};
/* Store in a variable if the player is attacking */
	playerIsAttacking = playerControllerScript.isAttacking;
/* Store in a variable if the enemy is attacking */
	if(collidingEnemy){
		enemyIsAttacking = collidingEnemy.GetComponent(enemyAi).isAttacking;
	};
Debug.Log(enemyIsAttacking);
/* if player is attacking while engaged and in touch with the enemy apply damage 
to the enemy by calling its takedamage function via sendmessage */
	if(playerIsAttacking && isEngaged && isTouching){
		if(isHitting==false){
			isHitting=true;
			collidingEnemy.SendMessage("TakeDamage",playerDamage);
		}else if(!transform.parent.gameObject.animation.IsPlaying("attack")){
			isHitting=false;
		}
	}
/* if player is attacking while engaged and in touch with the enemy apply damage 
to the enemy by calling its takedamage function via sendmessage */
	if(enemyIsAttacking && isEngaged && isTouching){
		if(isHitted==false){
			isHitted=true;
			TakeDamage(collidingEnemy.gameObject.GetComponent(enemyAi).enemyDamage);
		}
	}else if(!enemyIsAttacking){
			isHitted=false; //FIXARE PERCHE NON TORNA FALSE
		}
}
/* The above state machine logic it's needed to avoid to call the takedamage function every frame
that verify the if statement thus applying more than needed. This way the function is called just
once for every hit landed as it should. */
/* Check for collision with enemy and flag states appropriately */
function OnTriggerEnter(col : Collider) {
	if(col.gameObject.name =="demon"){
	wasTouching = isTouching;
	isTouching = true;
	collidingEnemy = col.gameObject;
 	};
 }	
/* Check for collision with enemy and flag states appropriately */
function OnTriggerExit(col : Collider) {
	Debug.Log("Colliding with : " + col.gameObject.name);
	if(col.gameObject.name =="demon"){
 	wasTouching = isTouching;
	isTouching = false;
 	};
}
//Function to call when hit by enemy
function TakeDamage(dmg : int){
	playerHealthPoints -= dmg ;
	Debug.Log("Player suffered " + dmg  +" damages");
}

This script should make visible the player health points and his states.

11-Unity-rpg-hack-slash-combat-system-tutorial

As this scripts is calling some functions defined on the enemy gameobject via the SendMessage Unity before hit play we should edit also the enemyAi.js to implement these function and allow the enemy to strike back against our player character.

PREVIOUS : Unity rpg combat system tutorial with simple state machine – Part 2

NEXT : Unity rpg combat system tutorial with simple state machine – Part 4

1 thought on “Rpg combat system tutorial for Unity with simple state machine – part 3

Leave a Comment