- Home /
Modifying the 2D Roguelike Player Script to Expect Other Objects
I used the Unity 2D Roguelike tutorials to make my player interact with the environment, and I'm at a point where I'd like to have the player take in other T's than just a Wall (see the call of "AttemptMove" below). I'm not very familiar with the use of T components. I'd like the player to also be able to expect to be hindered by an enemy, a door, or a chest. Sorry if this sounds extremely newbie-ish.
This is the player script from the tutorial itself:
using UnityEngine;
using System.Collections;
namespace Completed
{
//Player inherits from MovingObject, our base class for objects that can move, Enemy also inherits from this.
public class Player : MovingObject
{
public float restartLevelDelay = 1f; //Delay time in seconds to restart level.
public int pointsPerFood = 10; //Number of points to add to player food points when picking up a food object.
public int pointsPerSoda = 20; //Number of points to add to player food points when picking up a soda object.
public int wallDamage = 1; //How much damage a player does to a wall when chopping it.
private Animator animator; //Used to store a reference to the Player's animator component.
private int food; //Used to store player food points total during level.
//Start overrides the Start function of MovingObject
protected override void Start ()
{
//Get a component reference to the Player's animator component
animator = GetComponent<Animator>();
//Get the current food point total stored in GameManager.instance between levels.
food = GameManager.instance.playerFoodPoints;
//Call the Start function of the MovingObject base class.
base.Start ();
}
//This function is called when the behaviour becomes disabled or inactive.
private void OnDisable ()
{
//When Player object is disabled, store the current local food total in the GameManager so it can be re-loaded in next level.
GameManager.instance.playerFoodPoints = food;
}
private void Update ()
{
//If it's not the player's turn, exit the function.
if(!GameManager.instance.playersTurn) return;
int horizontal = 0; //Used to store the horizontal move direction.
int vertical = 0; //Used to store the vertical move direction.
//Get input from the input manager, round it to an integer and store in horizontal to set x axis move direction
horizontal = (int) (Input.GetAxisRaw ("Horizontal"));
//Get input from the input manager, round it to an integer and store in vertical to set y axis move direction
vertical = (int) (Input.GetAxisRaw ("Vertical"));
//Check if moving horizontally, if so set vertical to zero.
if(horizontal != 0)
{
vertical = 0;
}
//Check if we have a non-zero value for horizontal or vertical
if(horizontal != 0 || vertical != 0)
{
//Call AttemptMove passing in the generic parameter Wall, since that is what Player may interact with if they encounter one (by attacking it)
//Pass in horizontal and vertical as parameters to specify the direction to move Player in.
AttemptMove<Wall> (horizontal, vertical);
}
}
//AttemptMove overrides the AttemptMove function in the base class MovingObject
//AttemptMove takes a generic parameter T which for Player will be of the type Wall, it also takes integers for x and y direction to move in.
protected override void AttemptMove <T> (int xDir, int yDir)
{
//Every time player moves, subtract from food points total.
food--;
//Call the AttemptMove method of the base class, passing in the component T (in this case Wall) and x and y direction to move.
base.AttemptMove <T> (xDir, yDir);
//Hit allows us to reference the result of the Linecast done in Move.
RaycastHit2D hit;
//If Move returns true, meaning Player was able to move into an empty space.
if (Move (xDir, yDir, out hit))
{
//Call RandomizeSfx of SoundManager to play the move sound, passing in two audio clips to choose from.
}
//Since the player has moved and lost food points, check if the game has ended.
CheckIfGameOver ();
//Set the playersTurn boolean of GameManager to false now that players turn is over.
GameManager.instance.playersTurn = false;
}
//OnCantMove overrides the abstract function OnCantMove in MovingObject.
//It takes a generic parameter T which in the case of Player is a Wall which the player can attack and destroy.
protected override void OnCantMove <T> (T component)
{
//Set hitWall to equal the component passed in as a parameter.
Wall hitWall = component as Wall;
//Call the DamageWall function of the Wall we are hitting.
hitWall.DamageWall (wallDamage);
//Set the attack trigger of the player's animation controller in order to play the player's attack animation.
animator.SetTrigger ("playerChop");
}
//OnTriggerEnter2D is sent when another object enters a trigger collider attached to this object (2D physics only).
private void OnTriggerEnter2D (Collider2D other)
{
//Check if the tag of the trigger collided with is Exit.
if(other.tag == "Exit")
{
//Invoke the Restart function to start the next level with a delay of restartLevelDelay (default 1 second).
Invoke ("Restart", restartLevelDelay);
//Disable the player object since level is over.
enabled = false;
}
//Check if the tag of the trigger collided with is Food.
else if(other.tag == "Food")
{
//Add pointsPerFood to the players current food total.
food += pointsPerFood;
//Disable the food object the player collided with.
other.gameObject.SetActive (false);
}
//Check if the tag of the trigger collided with is Soda.
else if(other.tag == "Soda")
{
//Add pointsPerSoda to players food points total
food += pointsPerSoda;
//Disable the soda object the player collided with.
other.gameObject.SetActive (false);
}
}
//Restart reloads the scene when called.
private void Restart ()
{
//Load the last scene loaded, in this case Main, the only scene in the game.
Application.LoadLevel (Application.loadedLevel);
}
//LoseFood is called when an enemy attacks the player.
//It takes a parameter loss which specifies how many points to lose.
public void LoseFood (int loss)
{
//Set the trigger for the player animator to transition to the playerHit animation.
animator.SetTrigger ("playerHit");
//Subtract lost food points from the players total.
food -= loss;
//Check to see if game has ended.
CheckIfGameOver ();
}
//CheckIfGameOver checks if the player is out of food points and if so, ends the game.
private void CheckIfGameOver ()
{
//Check if food point total is less than or equal to zero.
if (food <= 0)
{
//Call the GameOver function of GameManager.
GameManager.instance.GameOver ();
}
}
}
}
This is the MovingObject script it depends on:
using UnityEngine;
using System.Collections;
namespace Completed
{
//The abstract keyword enables you to create classes and class members that are incomplete and must be implemented in a derived class.
public abstract class MovingObject : MonoBehaviour
{
public float moveTime = 0.1f; //Time it will take object to move, in seconds.
public LayerMask blockingLayer; //Layer on which collision will be checked.
private BoxCollider2D boxCollider; //The BoxCollider2D component attached to this object.
private Rigidbody2D rb2D; //The Rigidbody2D component attached to this object.
private float inverseMoveTime; //Used to make movement more efficient.
//Protected, virtual functions can be overridden by inheriting classes.
protected virtual void Start ()
{
//Get a component reference to this object's BoxCollider2D
boxCollider = GetComponent <BoxCollider2D> ();
//Get a component reference to this object's Rigidbody2D
rb2D = GetComponent <Rigidbody2D> ();
//By storing the reciprocal of the move time we can use it by multiplying instead of dividing, this is more efficient.
inverseMoveTime = 1f / moveTime;
}
//Move returns true if it is able to move and false if not.
//Move takes parameters for x direction, y direction and a RaycastHit2D to check collision.
protected bool Move (int xDir, int yDir, out RaycastHit2D hit)
{
//Store start position to move from, based on objects current transform position.
Vector2 start = transform.position;
// Calculate end position based on the direction parameters passed in when calling Move.
Vector2 end = start + new Vector2 (xDir, yDir);
//Disable the boxCollider so that linecast doesn't hit this object's own collider.
boxCollider.enabled = false;
//Cast a line from start point to end point checking collision on blockingLayer.
hit = Physics2D.Linecast (start, end, blockingLayer);
//Re-enable boxCollider after linecast
boxCollider.enabled = true;
//Check if anything was hit
if(hit.transform == null)
{
//If nothing was hit, start SmoothMovement co-routine passing in the Vector2 end as destination
StartCoroutine (SmoothMovement (end));
//Return true to say that Move was successful
return true;
}
//If something was hit, return false, Move was unsuccesful.
return false;
}
//Co-routine for moving units from one space to next, takes a parameter end to specify where to move to.
protected IEnumerator SmoothMovement (Vector3 end)
{
//Calculate the remaining distance to move based on the square magnitude of the difference between current position and end parameter.
//Square magnitude is used instead of magnitude because it's computationally cheaper.
float sqrRemainingDistance = (transform.position - end).sqrMagnitude;
//While that distance is greater than a very small amount (Epsilon, almost zero):
while(sqrRemainingDistance > float.Epsilon)
{
//Find a new position proportionally closer to the end, based on the moveTime
Vector3 newPostion = Vector3.MoveTowards(rb2D.position, end, inverseMoveTime * Time.deltaTime);
//Call MovePosition on attached Rigidbody2D and move it to the calculated position.
rb2D.MovePosition (newPostion);
//Recalculate the remaining distance after moving.
sqrRemainingDistance = (transform.position - end).sqrMagnitude;
//Return and loop until sqrRemainingDistance is close enough to zero to end the function
yield return null;
}
}
//The virtual keyword means AttemptMove can be overridden by inheriting classes using the override keyword.
//AttemptMove takes a generic parameter T to specify the type of component we expect our unit to interact with if blocked (Player for Enemies, Wall for Player).
protected virtual void AttemptMove <T> (int xDir, int yDir)
where T : Component
{
//Hit will store whatever our linecast hits when Move is called.
RaycastHit2D hit;
//Set canMove to true if Move was successful, false if failed.
bool canMove = Move (xDir, yDir, out hit);
//Check if nothing was hit by linecast
if(hit.transform == null)
//If nothing was hit, return and don't execute further code.
return;
//Get a component reference to the component of type T attached to the object that was hit
T hitComponent = hit.transform.GetComponent <T> ();
//If canMove is false and hitComponent is not equal to null, meaning MovingObject is blocked and has hit something it can interact with.
if(!canMove && hitComponent != null)
//Call the OnCantMove function and pass it hitComponent as a parameter.
OnCantMove (hitComponent);
}
//The abstract modifier indicates that the thing being modified has a missing or incomplete implementation.
//OnCantMove will be overriden by functions in the inheriting classes.
protected abstract void OnCantMove <T> (T component)
where T : Component;
}
}
Comment
Your answer
Follow this Question
Related Questions
2D Animation does not start 1 Answer
Player movement boudaries in 2D 1 Answer
Changing player's moving direction 0 Answers
How can I make a collision sensitive teleporter for a 2D game #c 1 Answer
2D Movement Problems 2 Answers