# Official 2D Roguelike: Q&A

Discussion in 'Community Learning & Teaching' started by Matthew-Schell, Feb 10, 2015.

Not open for further replies.
1. ### Sartoris

Joined:
Dec 8, 2013
Posts:
21
Hi everyone,
I'm trying to extend the tutorial using the dungeon generation script available here: https://unity3d.com/learn/tutorials/topics/scripting/basic-2d-dungeon-generation

However, Unity slows down a lot when a large number of walls is instantiated. I was wondering how one would go about spawning only those walls that are bordering the rooms and corridors, while leaving empty space in other locations. Or if that's difficult, how would you cut down on the number of walls being spawned in all that empty space "out there", i.e. in areas of the map that do not contain rooms or corridors?

I'd be grateful for any ideas

2. ### robert-sherwood

Joined:
Jan 7, 2018
Posts:
14
I have a question on the following code in lesson 6:

protected IEnumerator SmoothMovement(Vector3 end)
{
float sqrRemainingDistance = (transform.position - end).sqrMagnitude;

while (sqrRemainingDistance > float.Epsilon)
{
Vector3 newPosition = Vector3.MoveTowards(rb2d.position, end, inverseMoveTime * Time.deltaTime);
rb2d.MovePosition(newPosition);
sqrRemainingDistance = (transform.position - end).sqrMagnitude;
yield return null;
}
}

Why do we switch between transform.position and rb2d.position in our calculations? Are these positions different? Is there a specific reason why we use them both in the same function for very similar looking calculations?

Thanks!

3. ### FlamingVorpalCow

Joined:
Oct 29, 2014
Posts:
6
Hi, i'm trying to extend on this project, but i'm encountering an issue, from what i saw in youtube comments is common...

I created a Main Menu screen, from where i'm trying to launch the game, if I click play and go into this scene, it will start on Day 2, and after completing the map, the next day will be Day 4 and as soon as i touch and try to move, I die and have -1 food and the game stays there.

This doesn't happen if i normally launch from finished tutorial scene, only if i try to go into it from somewhere else...

Any Ideas on how to work around this?

Tried debugging but i don't see why this is happening...

4. ### marivs

Joined:
Dec 2, 2017
Posts:
2
Hello! I am following the tutorials to this game but after finishing the 12th part my game has 2 problems. One is that the level text does not show the correct number (it shows day 2, day 4, day 6 and so on) and if I set the value of level to 0 initially I get day 1, day 3, day 5 and so on. I have not yet realised where it jumps by 1. Another problem is that I lose the food after the 1st level. It displays 0 and after I move the game over message appears. Another issue is that sometimes there are tiles over another tiles (food on the same tile as wall and sometimes even 2 walls in the same place).I am using Unity 2017.3.

5. ### robert-sherwood

Joined:
Jan 7, 2018
Posts:
14
Check out the top comment on the video explaining UI and Levels. It tells you what you need to do.

marivs likes this.
6. ### robert-sherwood

Joined:
Jan 7, 2018
Posts:
14
Check out the top comment on the video explaining UI and Levels. It tells you what you need to do.

7. ### marivs

Joined:
Dec 2, 2017
Posts:
2
Thanks man! I was really struggling.You are the best!

Joined:
Jan 15, 2018
Posts:
4
Hi everyone! So I am really new to Unity and I started having problems with my game only after I tried adding sound. The game will load up just fine, but the second I attempt to move my player it immediately goes to the game over screen and the program ends. I am really confused because my game worked just fine before I attempted to add sound. If anyone has ideas as to what happened, I would appreciate the help!

Joined:
Jan 10, 2018
Posts:
1
Has anybody else been having difficulty with the Player only moving one spot, then getting stuck there? I just finished video 11 and tried my level out and he won't move more than one space before freezing.

10. ### ArSplln

Joined:
Jan 17, 2018
Posts:
7
Hi Guys !

I'm at the end of video 11.
My player is moving correctly, i can chop walls. But i can't pick up food or soda, enemies don't attack but move, and I can walk throught them. I can walk on the "Exit" Tiles, but it's not ending the current level. There is no error message in Unity's console.

Enemies are tagged "Enemy" and on the "Blocking Layer". Food and soda are tagged "Food" and "Soda"

Someone have any idea of where the problem is ?

Here is the code :

Code (CSharp):
1. //player.cs
2.
3. using System.Collections;
4. using System.Collections.Generic;
5. using UnityEngine;
6. using UnityEngine.SceneManagement;
7.
8. public class Player : MovingObjects
9. {
10.
11.     public int wallDamage = 1;
12.     public int pointsPerFood = 10;
13.     public int pointsPerSoda = 20;
14.     public float restartLevelDelay = 1f;
15.
16.     private Animator animator;
17.     private int food;
18.
19.
20.     protected override void Start()
21.     {
22.         animator = GetComponent<Animator>();
23.
24.         food = GameManager.instance.playerFoodPoints;
25.
26.         base.Start();
27.     }
28.
29.     private void OnDisable()
30.     {
31.         GameManager.instance.playerFoodPoints = food;
32.     }
33.
34.     void Update()
35.     {
36.         if (!GameManager.instance.playersTurn) return;
37.
38.         int horizontal = 0;
39.         int vertical = 0;
40.
41.         horizontal = (int)Input.GetAxisRaw("Horizontal");
42.         vertical = (int)Input.GetAxisRaw("Vertical");
43.
44.         if (horizontal != 0)
45.             vertical = 0;
46.
47.         if (horizontal != 0 || vertical != 0)
48.             AttemptMove<Wall>(horizontal, vertical);
49.     }
50.
51.     protected override void AttemptMove<T>(int xDir, int yDir)
52.     {
53.         food--;
54.
55.         base.AttemptMove<T>(xDir, yDir);
56.
57.         RaycastHit2D hit;
58.
59.         CheckIfGameOver();
60.
61.         GameManager.instance.playersTurn = false;
62.     }
63.
64.     private void OnTriggerEnter2D(Collider other)
65.     {
66.         if (other.tag == "Exit")
67.         {
68.             Invoke("Restart", restartLevelDelay);
69.             enabled = false;
70.         }
71.         else if (other.tag == "Food")
72.         {
73.             food += pointsPerFood;
74.             other.gameObject.SetActive(false);
75.         }
76.         else if (other.tag == "Soda")
77.         {
78.             food += pointsPerSoda;
79.             other.gameObject.SetActive(false);
80.         }
81.
82.     }
83.
84.     protected override void OnCantMove <T> (T component)
85.     {
86.         Wall hitWall = component as Wall;
87.         hitWall.DamageWall(wallDamage);
88.         animator.SetTrigger("playerChop");
89.     }
90.
91.     private void Restart()
92.     {
94.     }
95.
96.     public void LoseFood(int loss)
97.     {
98.         animator.SetTrigger("playerHit");
99.         food -= loss;
100.         CheckIfGameOver();
101.     }
102.
103.     private void CheckIfGameOver()
104.     {
105.         if (food <= 0)
106.             GameManager.instance.GameOver();
107.     }
108. }
109.
Code (CSharp):
1. //enemy.cs
2.
3. using System.Collections;
4. using System.Collections.Generic;
5. using UnityEngine;
6.
7. public class Enemy : MovingObjects {
8.
9.     public int playerDamage;
10.
11.     private Animator animator;
12.     private Transform target;
13.     private bool skipMove;
14.
15.
16.
17.     protected override void Start ()
18.     {
20.         animator = GetComponent<Animator>();
21.         target = GameObject.FindGameObjectWithTag("Player").transform;
22.         base.Start();
23.     }
24.
25.     protected override void AttemptMove<T>(int xDir, int yDir)
26.     {
27.         if (skipMove)
28.         {
29.             skipMove = false;
30.             return;
31.         }
32.
33.         base.AttemptMove <T> (xDir, yDir);
34.
35.         skipMove = true;
36.     }
37.
38.     public void MoveEnemy ()
39.     {
40.         int xDir = 0;
41.         int yDir = 0;
42.
43.         if (Mathf.Abs(target.position.x - transform.position.x) < float.Epsilon)
44.             yDir = target.position.y > transform.position.y ? 1 : -1;
45.         else
46.             xDir = target.position.x > transform.position.x ? 1 : -11;
47.
48.         AttemptMove<Player>(xDir, yDir);
49.     }
50.
51.     protected override void OnCantMove <T> (T component)
52.     {
53.         Player hitPlayer = component as Player;
54.
55.         animator.SetTrigger("enemyAttack");
56.
57.         hitPlayer.LoseFood(playerDamage);
58.     }
59. }
60.
Code (CSharp):
1. //gameManager.cs
2.
3. using System.Collections;
4. using System.Collections.Generic;
5. using UnityEngine;
6.
7. public class GameManager : MonoBehaviour
8. {
9.     public float turnDelay = .1f;
10.     public static GameManager instance = null;
11.     public BoardManager boardScript;
12.     public int playerFoodPoints = 100;
13.     [HideInInspector] public bool playersTurn = true;
14.
15.     private int level = 3;
16.     private List<Enemy> enemies;
17.     private bool enemiesMoving;
18.
19.
20.     void Awake ()
21.     {
22.         if (instance == null)
23.             instance = this;
24.         else if (instance != this)
25.             Destroy(gameObject);
26.
28.         enemies = new List<Enemy>();
29.         boardScript = GetComponent<BoardManager>();
30.         InitGame();
31.     }
32.
33.     void InitGame ()
34.     {
35.         enemies.Clear();
36.         boardScript.SetupScene(level);
37.     }
38.
39.     public void GameOver()
40.     {
41.         enabled = false;
42.     }
43.
44.     void Update ()
45.     {
46.         if (playersTurn || enemiesMoving)
47.             return;
48.
49.         StartCoroutine(MoveEnemies());
50.     }
51.
53.     {
55.     }
56.
57.     IEnumerator MoveEnemies()
58.     {
59.         enemiesMoving = true;
60.         yield return new WaitForSeconds(turnDelay);
61.         if (enemies.Count == 0)
62.         {
63.             yield return new WaitForSeconds(turnDelay);
64.         }
65.
66.         for (int i = 0; i < enemies.Count; i++)
67.         {
68.             enemies[i].MoveEnemy();
69.             yield return new WaitForSeconds(enemies[i].moveTime);
70.         }
71.
72.         playersTurn = true;
73.         enemiesMoving = false;
74.     }
75. }

11. ### gwnguy

Joined:
Apr 25, 2017
Posts:
144
Are you getting a console error message regarding line 64 of player.cs?
on line 64 of player.cs, please try
private void OnTriggerEnter2D(Collider2D other)
private void OnTriggerEnter2D(Collider other)

and see if it makes any difference

BTW, inspector on the player:

Last edited: Jan 17, 2018
12. ### ArSplln

Joined:
Jan 17, 2018
Posts:
7

Writting private void OnTriggerEnter2D(Collider2D other) , is not making any changes.
Here's my items in the Inspector.

13. ### gwnguy

Joined:
Apr 25, 2017
Posts:
144
Darn, I really thought that was the problem.

1) Exit your Unity session and restart it. Some people have found this clears up their problems,
and it is an easy thing to do. It will force any changes to be committed or dropped.

2) Confirm there are no compile/build errors in your scripts, or runtime errors upon execution.
Check that the console log is not reporting ANY errors.

3) Confirm you have attached your working script (not the script from the "_Completed_Game" directory) to your game objects
in your inspector and the code you are modifying is in your working directory (not the "_Completed_Game" directory)
Personally, I think the "Completed Game" directory is a double edged sword,
since scripts from it may be confused with scripts from the working hierarchy.
*** Move or Delete the "_Completed_Game" directory to make sure there is no accidental confusion. ***

(void Start() , NOT void start ())

5) Check that spelling on the tags on the game object in the inspector with the tags in the C# script.
Same spelling, capitalization and punctuation. (tag name "pick up", PickUp", "pickUp", "Pick Up" are all different)

6) a simple sanity check debug statement to the player.cs script
Code (csharp):
1.
2. void Start ()
3. {
4.    Debug.Log("Player.Start: we have entered the start method");
5.    // ... other code ...
6. }
7.
and confirm it prints to the console.

Last edited: Jan 19, 2018
14. ### ArSplln

Joined:
Jan 17, 2018
Posts:
7

No error compile in the console, before or after running the game.
No spelling error in my methods neither. I'm using my own sccripts, not ones from the Completed_Game file.

I have nothing called Pick Up or pickUp. Did I miss something ?

If I change Collider2D to Collider, it gets an error on the console, so you were right for that.

Debug.Log("Player.Start: we have entered the start method"); is working, it prints to the console.

SeNd HaLp !

Thank you guys !

15. ### gwnguy

Joined:
Apr 25, 2017
Posts:
144
Sorry, the "6 items to check" is a fairly generic checklist tailored to Roll-a-ball.
In this case, the tags to check for spelling would "Player", "Food" and "Soda"

I copied and pasted your player code into my project’s Player.cs
I got this message:

I corrected the code to be:
private void OnTriggerEnter2D(Collider2D other)
And the game worked. I picked up food/soda, changed levels, the enemies attack and I can’t walk through them. BUT, the enemies DON’T follow me

I copied/pasted the enemy.cs code into my enemy.cs
I saw what you report. The enemy doesn’t move nor attack (but I don’t walk through them).

So, my animator.SetTrigger("enemyAttack"); from my working code had a capital E:
animator.SetTrigger("EnemyAttack");
That fixed the problem, the enemy attacked, but, doesn’t move.
I modified line 46
From
xDir = target.position.x > transform.position.x ? 1 : -11;
To

xDir = target.position.x > transform.position.x ? 1 : -1;
(-1, not -11) and everything worked, the enemies follow me

I haven’t tried the gameManager.cs.

Last edited: Jan 20, 2018
16. ### unity_P_E6_wAQcwWw2g

Joined:
Dec 15, 2017
Posts:
2
Hi, i have a problem since i completed lesson Adding UI & level transitions, error code is here:

NullReferenceException: Object reference not set to an instance of an object
UnityEditor.Graphs.Edge.WakeUp () (at C:/buildslave/unity/build/Editor/Graphs/UnityEditor.Graphs/Edge.cs:114)
UnityEditor.Graphs.Graph.DoWakeUpEdges (System.Collections.Generic.List`1 inEdges, System.Collections.Generic.List`1 ok, System.Collections.Generic.List`1 error, Boolean inEdgesUsedToBeValid) (at C:/buildslave/unity/build/Editor/Graphs/UnityEditor.Graphs/Graph.cs:387)
UnityEditor.Graphs.Graph.WakeUpEdges (Boolean clearSlotEdges) (at C:/buildslave/unity/build/Editor/Graphs/UnityEditor.Graphs/Graph.cs:286)
UnityEditor.Graphs.Graph.WakeUp (Boolean force) (at C:/buildslave/unity/build/Editor/Graphs/UnityEditor.Graphs/Graph.cs:272)
UnityEditor.Graphs.Graph.WakeUp () (at C:/buildslave/unity/build/Editor/Graphs/UnityEditor.Graphs/Graph.cs:250)
UnityEditor.Graphs.Graph.OnEnable () (at C:/buildslave/unity/build/Editor/Graphs/UnityEditor.Graphs/Graph.cs:245)

NullReferenceException: Object reference not set to an instance of an object
GameManager.InitGame () (at Assets/Scripts/GameManager.cs:56)
GameManager.Awake () (at Assets/Scripts/GameManager.cs:32)
UnityEngine.Object:Instantiate(GameObject)

NullReferenceException: Object reference not set to an instance of an object
UnityEditor.Graphs.Edge.WakeUp () (at C:/buildslave/unity/build/Editor/Graphs/UnityEditor.Graphs/Edge.cs:114)
UnityEditor.Graphs.Graph.DoWakeUpEdges (System.Collections.Generic.List`1 inEdges, System.Collections.Generic.List`1 ok, System.Collections.Generic.List`1 error, Boolean inEdgesUsedToBeValid) (at C:/buildslave/unity/build/Editor/Graphs/UnityEditor.Graphs/Graph.cs:387)
UnityEditor.Graphs.Graph.WakeUpEdges (Boolean clearSlotEdges) (at C:/buildslave/unity/build/Editor/Graphs/UnityEditor.Graphs/Graph.cs:286)
UnityEditor.Graphs.Graph.WakeUp (Boolean force) (at C:/buildslave/unity/build/Editor/Graphs/UnityEditor.Graphs/Graph.cs:272)
UnityEditor.Graphs.Graph.WakeUp () (at C:/buildslave/unity/build/Editor/Graphs/UnityEditor.Graphs/Graph.cs:250)
UnityEditor.Graphs.Graph.OnEnable () (at C:/buildslave/unity/build/Editor/Graphs/UnityEditor.Graphs/Graph.cs:245)

NullReferenceException: Object reference not set to an instance of an object
UnityEditor.Graphs.Edge.WakeUp () (at C:/buildslave/unity/build/Editor/Graphs/UnityEditor.Graphs/Edge.cs:114)
UnityEditor.Graphs.Graph.DoWakeUpEdges (System.Collections.Generic.List`1 inEdges, System.Collections.Generic.List`1 ok, System.Collections.Generic.List`1 error, Boolean inEdgesUsedToBeValid) (at C:/buildslave/unity/build/Editor/Graphs/UnityEditor.Graphs/Graph.cs:387)
UnityEditor.Graphs.Graph.WakeUpEdges (Boolean clearSlotEdges) (at C:/buildslave/unity/build/Editor/Graphs/UnityEditor.Graphs/Graph.cs:286)
UnityEditor.Graphs.Graph.WakeUp (Boolean force) (at C:/buildslave/unity/build/Editor/Graphs/UnityEditor.Graphs/Graph.cs:272)
UnityEditor.Graphs.Graph.WakeUp () (at C:/buildslave/unity/build/Editor/Graphs/UnityEditor.Graphs/Graph.cs:250)
UnityEditor.Graphs.Graph.OnEnable () (at C:/buildslave/unity/build/Editor/Graphs/UnityEditor.Graphs/Graph.cs:245)

NullReferenceException: Object reference not set to an instance of an object
GameManager.InitGame () (at Assets/Scripts/GameManager.cs:56)
GameManager.Awake () (at Assets/Scripts/GameManager.cs:32)
UnityEngine.Object:Instantiate(GameObject)

Game Manager Script:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;

public class GameManager : MonoBehaviour {

public BoardManager boardScript;
public static GameManager instance = null;
public int playerFoodPoints = 100;
[HideInInspector] public bool playersTurn = true;
public float turnDelay = .1f;
public float levelStartDelay = 2f;

private int level = 1;
private List<Enemy> enemies;
private bool enemiesMoving;
private Text levelText;
private GameObject levelImage;
private bool doingSetup;

void Awake ()
{
if (instance == null)
instance = this;
else if (instance != this)
Destroy (gameObject);
enemies = new List<Enemy> ();
boardScript = GetComponent<BoardManager> ();
InitGame ();

}

{
level++;
InitGame ();
}

void OnEnable()
{
}

void OnDisable()
{
}

void InitGame()
{
doingSetup = true;
levelImage = GameObject.Find ("LevelImage");
levelText = GameObject.Find ("LevelText").GetComponent<Text> ();
levelText.text = "Day " + level;
levelImage.SetActive (true);
Invoke ("HideLevelImage", levelStartDelay);

enemies.Clear ();
boardScript.SetupScene (level);
}

void HideLevelImage()
{
levelImage.SetActive (false);
doingSetup = false;
}

void Update ()
{
if (playersTurn || enemiesMoving || doingSetup)
return;
StartCoroutine (MoveEnemies());
}

{
}

public void GameOver()
{
levelText.text = "After " + level + "days, you starved.";
levelImage.SetActive (true);
enabled = false;
}

IEnumerator MoveEnemies()
{
enemiesMoving = true;
yield return new WaitForSeconds (turnDelay);
if (enemies.Count == 0)
{
yield return new WaitForSeconds (turnDelay);
}
for (int i = 0; i < enemies.Count; i++)
{
enemies.MoveEnemy ();
yield return new WaitForSeconds (enemies.moveTime);
}
playersTurn = true;
enemiesMoving = false;
}
}

Player script:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;

public class Player : MovingObjects {

public int wallDamage=1;
public int pointsPerFood=10;
public int pointsPerSoda=20;
public float restartLevelDelay = 1f;
public Text foodText;

private Animator animator;
private int food;

// Use this for initialization
protected override void Start ()
{
animator = GetComponent <Animator> ();
food = GameManager.instance.playerFoodPoints;
foodText.text = "Food: " + food;
base.Start ();
}

// Update is called once per frame
void Update ()
{
if (!GameManager.instance.playersTurn)
return;
int horizontal = 0;
int vertical = 0;

horizontal = (int)Input.GetAxisRaw ("Horizontal");
vertical = (int)Input.GetAxisRaw ("Vertical");

if (horizontal != 0)
vertical = 0;

if (horizontal != 0 || vertical != 0)
AttemptMove<Wall> (horizontal, vertical);
}

private void OnDisable ()
{
GameManager.instance.playerFoodPoints = food;
}

protected override void AttemptMove <T> (int xDir, int yDir)
{
food--;
foodText.text = "Food: " + food;
base.AttemptMove <T> (xDir, yDir);
RaycastHit2D hit;
CheckIfGameOver();
}

private void CheckIfGameOver()
{
if (food <= 0)
GameManager.instance.GameOver();

}

private void OnTriggerEnter2D (Collider2D other)
{
if (other.tag == "Exit")
{
Invoke ("Restart", restartLevelDelay);
enabled = false;
}
else if (other.tag == "Food")
{
food += pointsPerFood;
foodText.text = "+" + pointsPerFood + " Food: " + food;
other.gameObject.SetActive (false);
}
else if (other.tag == "Soda")
{
food += pointsPerSoda;
foodText.text = "+" + pointsPerSoda + " Food: " + food;
other.gameObject.SetActive (false);
}
}

protected override void OnCantMove<T> (T component)
{
Wall hitWall = component as Wall;
hitWall.DamageWall (wallDamage);
animator.SetTrigger ("playerChop");
}

private void Restart ()
{
}

public void LoseFood (int loss)
{
animator.SetTrigger ("plaherHit");
food -= loss;
foodText.text = "-" + loss + " Food: " + food;
CheckIfGameOver ();
}

}

Before that, the game was perfect, i can move player, pick up food and soda

17. ### archteck

Joined:
Jan 29, 2018
Posts:
2
Hello,
I'm making this post because maybe it could help someone that is in trouble because if you do all the steps in tutorial even with the new suggestions it will not be working, you still have to "tune" the scripts, i keep it very simple, similar with the result expected from the tutorial.

Code (CSharp):
1. using System.Collections;
2. using System.Collections.Generic;       //Allows us to use Lists.
3. using UnityEngine;
4. using UnityEngine.SceneManagement;
5. using UnityEngine.UI;
6.
7. public class GameManager : MonoBehaviour
8. {
9.     public float levelStartDelay = 2f;                      //Time to wait before starting level, in seconds.
10.     public float turnDelay = 0.1f;                          //Delay between each Player turn.
11.     public int playerFoodPoints = 100;                      //Starting value for Player food points.
12.     public static GameManager instance;              //Static instance of GameManager which allows it to be accessed by any other script.
13.     [HideInInspector] public bool playersTurn = true;       //Boolean to check if it's players turn, hidden in inspector but public.
14.
15.     private Text levelText;                                 //Text to display current level number.
16.     private GameObject levelImage;                          //Image to block out level as levels are being set up, background for levelText.
17.     private BoardManager boardScript;                       //Store a reference to our BoardManager which will set up the level.
18.     private int level;                                  //Current level number, expressed in game as "Day 0".
19.     private List<Enemy> enemies;                          //List of all Enemy units, used to issue them move commands.
20.     private bool enemiesMoving;                             //Boolean to check if enemies are moving.
21.     private bool doingSetup;                         //Boolean to check if we're setting up board, prevent Player from moving during setup.
22.
23.     //Awake is always called before any Start functions
24.     private void Awake()
25.     {
26.         //Check if instance already exists
27.         if (instance == null)
28.
29.             //if not, set instance to this
30.             instance = this;
31.
32.         //If instance already exists and it's not this:
33.         else if (instance != this)
34.
35.             //Then destroy this. This enforces our singleton pattern, meaning there can only ever be one instance of a GameManager.
36.             Destroy(gameObject);
37.
40.
41.         //Assign enemies to a new List of Enemy objects.
42.         enemies = new List<Enemy>();
43.
44.         //Get a component reference to the attached BoardManager script
45.         boardScript = GetComponent<BoardManager>();
46.     }
47.
48.     //This is called each time a scene is loaded.
50.     {
51.         //Add one to our level number.
52.         level++;
53.         //Call InitGame to initialize our level.
54.         InitGame();
55.     }
56.
57.     private void OnEnable()
58.     {
59.         //Tell our ‘OnLevelFinishedLoading’ function to start listening for a scene change event as soon as this script is enabled.
61.     }
62.
63.     private void OnDisable()
64.     {
65.         //Tell our ‘OnLevelFinishedLoading’ function to stop listening for a scene change event as soon as this script is disabled.
66.         //Remember to always have an unsubscription for every delegate you subscribe to!
68.     }
69.
70.     //Initializes the game for each level.
71.     private void InitGame()
72.     {
73.         //While doingSetup is true the player can't move, prevent player from moving while title card is up.
74.         doingSetup = true;
75.
76.         //Get a reference to our image LevelImage by finding it by name.
77.         levelImage = GameObject.Find("LevelImage");
78.
79.         //Get a reference to our text LevelText's text component by finding it by name and calling GetComponent.
80.         levelText = GameObject.Find("LevelText").GetComponent<Text>();
81.
82.         //Set the text of levelText to the string "Day" and append the current level number.
83.         levelText.text = string.Format("Day {0}", level);
84.
85.         //Set levelImage to active blocking player's view of the game board during setup.
86.         levelImage.SetActive(true);
87.
88.         //Call the HideLevelImage function with a delay in seconds of levelStartDelay.
89.         Invoke("HideLevelImage", levelStartDelay);
90.
91.         //Clear any Enemy objects in our List to prepare for next level.
92.         enemies.Clear();
93.
94.         //Call the SetupScene function of the BoardManager script, pass it current level number.
95.         boardScript.SetupScene(level);
96.     }
97.
98.     //Hides black image used between levels
99.     private void HideLevelImage()
100.     {
101.         //Disable the levelImage gameObject.
102.         levelImage.SetActive(false);
103.
104.         //Set doingSetup to false allowing player to move again.
105.         doingSetup = false;
106.     }
107.
108.     //Update is called every frame.
109.     private void Update()
110.     {
111.         //Check that playersTurn or enemiesMoving or doingSetup are not currently true.
112.         if (playersTurn || enemiesMoving || doingSetup)
113.
114.             //If any of these are true, return and do not start MoveEnemies.
115.             return;
116.
117.         //Start moving enemies.
118.         StartCoroutine(MoveEnemies());
119.     }
120.
121.     //Call this to add the passed in Enemy to the List of Enemy objects.
123.     {
124.         //Add Enemy to List enemies.
126.     }
127.
128.     //GameOver is called when the player reaches 0 food points
129.     public void GameOver()
130.     {
131.         //Set levelText to display number of levels passed and game over message
132.         levelText.text = string.Format("After {0} days,{1}you starved.", level, System.Environment.NewLine);
133.
134.         //Enable black background image gameObject.
135.         levelImage.SetActive(true);
136.
137.         //Disable this GameManager.
138.         enabled = false;
139.     }
140.
141.     //Coroutine to move enemies in sequence.
142.     private IEnumerator MoveEnemies()
143.     {
144.         //While enemiesMoving is true player is unable to move.
145.         enemiesMoving = true;
146.
147.         //Wait for turnDelay seconds, defaults to .1 (100 ms).
148.         yield return new WaitForSeconds(turnDelay);
149.
150.         //If there are no enemies spawned (IE in first level):
151.         if (enemies.Count == 0)
152.         {
153.             //Wait for turnDelay seconds between moves, replaces delay caused by enemies moving when there are none.
154.             yield return new WaitForSeconds(turnDelay);
155.         }
156.
157.         //Loop through List of Enemy objects.
158.         for (int i = 0; i < enemies.Count; i++)
159.         {
160.             //Call the MoveEnemy function of Enemy at index i in the enemies List.
161.             enemies[i].MoveEnemy();
162.
163.             //Wait for Enemy's moveTime before moving next Enemy,
164.             yield return new WaitForSeconds(enemies[i].moveTime);
165.         }
166.         //Once Enemies are done moving, set playersTurn to true so player can move.
167.         playersTurn = true;
168.
169.         //Enemies are done moving, set enemiesMoving to false.
170.         enemiesMoving = false;
171.     }
172. }
Because off the changes in the SceneManager, they suggest we use OnLevelFinishedLoading for initialize the game, well that's a good sugesttion but we have to do more changes in the code. We must remove InitGame from the Awake because if we dont when we start the game we will have 2 boards loaded(1 from Awake and from OnLevelFinishedLoading). We only want InitGame on OnLevelFinishedLoading. We must also change the private int level to start in zero or better remove any initialization because int default value is zero.

Other recommendations i can do (for user's and teachers), use string.Format to construct strings is better and easy, exemple :

Code (CSharp):
1. foodText.text = string.Format("Food: {0}", food);
And when is collectibles please don't put the object "inactive", Destroy it!

Code (CSharp):
1. //Add pointsPerSoda to players food points total
2.                 food += pointsPerSoda;
3.                 foodText.text = string.Format("+{0} Food: {1}", pointsPerSoda, food);
4.
5.                 //Call RandomizeSfx of SoundManager to play the drink sound, passing in two audio clips to choose from.
6.                 SoundManager.instance.RandomizeSfx(drinkSound1, drinkSound1);
7.
8.                 //Destroy the soda object the player collided with.
9.                 Destroy(other.gameObject);
The objective in this tutorials should be teaching good ways too, if we start from the beginning with bad roots the tree will fall.

I hope that nobody gets angry with anything i said.

Best regards

Last edited: Feb 1, 2018
oferzivony and Emilylyn1 like this.
18. ### BashiDono

Joined:
Oct 23, 2017
Posts:
3
Hi! So I have a slight problem. I've just finished video 11 of 14 for the Rogue@D, but for some reason my player doesn't register the Inner Walls as blocking layers, he and the enemies both walk right over them like they are the floor. I can't notice anything wrong in the programming, and neither can my friend, but obviously something is wrong. Please help?

Wall code:
Code (CSharp):
1. using System.Collections;
2. using UnityEngine;
3.
4. public class Wall : MonoBehaviour {
5.
6.     public Sprite dmgSprite;
7.     public int hp = 4;
8.
9.     private SpriteRenderer spriteRenderer;
10.
11.     // Use this for initialization
12.     void Awake ()
13.     {
14.         spriteRenderer = GetComponent<SpriteRenderer>();
15.     }
16.
17.     public void DamageWall (int loss)
18.     {
19.         spriteRenderer.sprite = dmgSprite;
20.         hp -= loss;
21.         if (hp <= 0)
22.             gameObject.SetActive(false);
23.     }
24. }
25.

Player code:
Code (CSharp):
1. using System.Collections;
2. using UnityEngine;
3. using UnityEngine.SceneManagement;
4.
5. public class Player : MovingObject
6. {
7.     public int wallDamage = 1;
8.     public int pointsPerFood = 10;
9.     public int pointsPerSoda = 20;
10.     public float restartLevelDelay = 1f;
11.
12.     private Animator animator;
13.     private int food;
14.
15.     // Use this for initialization
16.     protected override void Start()
17.     {
18.         animator = GetComponent<Animator>();
19.
20.         food = GameManager.instance.playerFoodPoints;
21.
22.         base.Start();
23.     }
24.
25.     private void OnDisable()
26.     {
27.         GameManager.instance.playerFoodPoints = food;
28.     }
29.
30.     // Update is called once per frame
31.     private void Update()
32.     {
33.         if (!GameManager.instance.playersTurn) return;
34.
35.         int horizontal = 0;
36.         int vertical = 0;
37.
38.         horizontal = (int)(Input.GetAxisRaw("Horizontal"));
39.         vertical = (int)(Input.GetAxisRaw("Vertical"));
40.
41.         if(horizontal != 0)
42.         {
43.             vertical = 0;
44.         }
45.
46.         if (horizontal != 0 || vertical != 0)
47.             AttemptMove<Wall>(horizontal, vertical);
48.     }
49.
50.     protected override void AttemptMove <T> (int xDir, int yDir)
51.     {
52.         food--;
53.
54.         base.AttemptMove<T>(xDir, yDir);
55.
56.         RaycastHit2D hit;
57.
58.         if (Move (xDir, yDir, out hit))
59.         {
60.
61.         }
62.
63.         CheckIfGameOver();
64.
65.         GameManager.instance.playersTurn = false;
66.     }
67.
68.    protected override void OnCantMove <T> (T component)
69.     {
70.         Wall hitwall = component as Wall;
71.         hitwall.DamageWall(wallDamage);
72.         animator.SetTrigger("playerChop");
73.     }
74.
75.     private void OnTriggerEnter2D(Collider2D other)
76.     {
77.         if (other.tag == "Exit")
78.         {
79.             Invoke("Restart", restartLevelDelay);
80.             enabled = false;
81.         }
82.         else if (other.tag == "Food")
83.         {
84.             food += pointsPerFood;
85.             other.gameObject.SetActive(false);
86.         }
87.         else if (other.tag == "Soda")
88.         {
89.             food += pointsPerSoda;
90.             other.gameObject.SetActive(false);
91.         }
92.     }
93.     private void Restart()
94.     {
96.     }
97.
98.     public void LoseFood (int loss)
99.     {
100.         animator.SetTrigger("playerHit");
101.         food -= loss;
102.         CheckIfGameOver();
103.     }
104.
105.     private void CheckIfGameOver()
106.     {
107.         if (food <= 0)
108.         {
109.             GameManager.instance.GameOver();
110.         }
111.     }
112. }

Enemy Code:
Code (CSharp):
1. using System.Collections;
2. using UnityEngine;
3.
4. public class Enemy : MovingObject
5. {
6.
7.     public int playerDamage;
8.
9.     private Animator animator;
10.     private Transform target;
11.     private bool skipMove;
12.
13.     protected override void Start ()
14.     {
16.         animator = GetComponent<Animator>();
17.         target = GameObject.FindGameObjectWithTag("Player").transform;
18.         base.Start();
19.     }
20.
21.     protected override void AttemptMove<T>(int xDir, int yDir)
22.     {
23.         if (skipMove)
24.         {
25.             skipMove = false;
26.             return;
27.         }
28.
29.         base.AttemptMove<T>(xDir, yDir);
30.
31.         skipMove = true;
32.     }
33.
34.     public void MoveEnemy()
35.     {
36.         int xDir = 0;
37.         int yDir = 0;
38.
39.         if (Mathf.Abs(target.position.x - transform.position.x) < float.Epsilon)
40.             yDir = target.position.y > transform.position.y ? 1 : -1;
41.         else
42.             xDir = target.position.x > transform.position.x ? 1 : -1;
43.
44.         AttemptMove<Player>(xDir, yDir);
45.     }
46.
47.     protected override void OnCantMove<T>(T component)
48.     {
49.         Player hitPlayer = component as Player;
50.
51.         animator.SetTrigger("enemyAttack");
52.
53.         hitPlayer.LoseFood(playerDamage);
54.     }
55. }

19. ### archteck

Joined:
Jan 29, 2018
Posts:
2

Hi there, my guess off a plausible cause for this is :
• Check if your Wall prefab's are untagged and the Layer selected is the same as the Player and the Enemy's, BlockingLayer.
You may found more problems later, so i recommend you to read my post above(https://forum.unity.com/threads/2d-roguelike-q-a.297180/page-22#post-3375746), because you will have probably 2 boards at the same time.

P.S. Please use Destroy(gameObject) instead off turning the gameObject.SetActive(false), because that Wall will never be used again, it will be instantiated a new one.

Best regards

20. ### BashiDono

Joined:
Oct 23, 2017
Posts:
3
Thank you for the script help, but now there seems to be a problem. The play screen is all black except for the player, you can see them, but they aren't dancing, and I checked my walls, all are Untagged BlockingLayers, and it loads the error:
NullReferenceException: Object reference not set to an instance of an object
GameManager.InitGame () (at Assets/Mine/Scripts/GameManager.cs:56)

21. ### hailtonothing

Joined:
Sep 17, 2014
Posts:
2
Hi all,

I'm on step 5. Copied out the code exactly and it refuses to recognise boardmanager, and consequently does not recognise setupscene "(boardmanager does not contain a definition for setupscene") in the init function. I can't work out why and I'm tearing my hair out.

The GameManager gameobject is created, both scripts are added to it. I've followed the video step by step as far as I can tell.

Maybe worth noting that In the script inspector, neither of the scripts have visible variables - i.e. there's no way for me to drag prefabs onto the arrays etc. I assume these issues are related but...
Also the gamemanager script icon always changes to a gear instead of the script icon. No idea why. I've deleted and recreated it 10 times and can't get it to not do this. it doesn't show this way in the completed game script.

Some screens below... appreciate any help!

Edit: Unity version is 2017.3

File size:
24.9 KB
Views:
1,242
22. ### hailtonothing

Joined:
Sep 17, 2014
Posts:
2
Never mind, I am a buffoon, and accidentally had all of BoardManager functions and variables INDSIDE the Count class. Sigh.

On the plus side, I have saved money on haircuts.

23. ### Varishaa

Joined:
Feb 19, 2018
Posts:
16
Hi,
i got all things working but have a question just for my understanding.
I wonder how it is done that the floor tiles are not overlapping. They are 32x32 in size and are aligned like:
(0,0,0) first tile, (1,0,0) second tile, (2,0,0) 3rd tile, etc.

Why is this sufficient, why is it not necessary to put the second tile at (32,0,0)?

Regards Marcus

24. ### Billedo

Joined:
Nov 24, 2016
Posts:
1
If you click on the "Scavengers_SpriteSheet" in the "Sprites" folder, you open the "Import Settings" window (see attached image), and there is specified "32 pixels per unit".

This may explain why it is sufficient to put the second tile at (2, 0, 0).

#### Attached Files:

• ###### Capture.PNG
File size:
27.1 KB
Views:
927
pittmakesgames likes this.
25. ### Beta4

Joined:
Nov 6, 2014
Posts:
34
So is there a solution to this tutorial on making a restart function work properly by using a keyboard press like an R for example when your character loses or is still still up in the air. Im currently using Unity 2017.3 0f3 Personal Edition. Any tips to getting it work properly and not restarting at level 2 and then losing at level 4 when you take your first step would be greatly appreciated.

Joined:
Nov 6, 2014
Posts:
34
bump

27. ### guilhermecorintho

Joined:
Sep 24, 2017
Posts:
11

What if I dont want to use 32x32 tiles? I've spent hours trying to find a solution. For example: When I try with 16x6 tiles, the board creator generates empty spaces between every tile. I can't find any reference to change it to be able to do it correctly with tiles that are not 32x32. Thank you

28. ### Aminpk

Joined:
Feb 24, 2018
Posts:
4
it is a very detailed tutorial in an easy way, but the script creation is a little complicated.
can i scale up the sprite sheet, for example it is 256x224 . i want to scale it 1000x500
if i scale where can i change coding to make it fit.

29. ### calebjackson

Joined:
Mar 1, 2018
Posts:
1
//This line:

protected override void AttemptMove<T>(int xDir, int yDir)

//Turns up this error:

`Player.AttemptMove<T>(int, int)' cannot override inherited member 'MovingObject.AttemptMove<T>(int,int)' because it is not marked virtual, abstract, or override

IT IS MARKED OVERRIDE! ...Isn't it? I replicated the error by copying the code straight from Unity's website. :/

30. ### Aminpk

Joined:
Feb 24, 2018
Posts:
4
I changed the following part from 8 to 25, while the output in the game is just 10 cells.

public int columns = 25; //Number of columns in our game board.
public int rows = 25; //Number of rows in our game board.

plz help anybody

in video no. 4 I learnt the following

" These will delineate the dimensions of our game board

· 02:38 - 02:40

and will allow us to make our game board

· 02:40 - 02:42

larger or smaller by changing them

· 02:42 - 02:43

if we want to.

· 02:43 - 02:46

In this case we're going to initialise them each to 8

· 02:46 - 02:49

meaning we're going to have an 8 by 8 game board.

· 02:50 - 02:53 "

why i am not getting a 25 by 25 gameboard ?

Last edited: Mar 7, 2018
31. ### Aminpk

Joined:
Feb 24, 2018
Posts:
4

..........................................................

Thanks everybody.
I got it myself from GameManager's inspector window as below

File size:
106.9 KB
Views:
951
32. ### omalleym1115

Joined:
Aug 7, 2012
Posts:
2
Hello all,

I just finished the tutorial, but I am having some problems. Followed all of the steps in the thread and in the PDF, but my scenes won't transition. I am using the onEnable and it hits it, but now the OnLevelFinishedLoading and I don't know why. Can anyone see what I am doing wrong?

Code (CSharp):
1. using System.Collections;
2. using System.Collections.Generic;
3. using UnityEngine;
4. using UnityEngine.UI;
5. using UnityEngine.SceneManagement;
6.
7. public class GameManager : MonoBehaviour {
8.
9.     public float levelStartDelay = 2f;                      //Time to wait before starting level, in seconds.
10.     public float turnDelay = 0.1f;                          //Delay between each Player turn.
11.     public int playerFoodPoints = 100;                      //Starting value for Player food points.
12.     public static GameManager instance = null;              //Static instance of GameManager which allows it to be accessed by any other script.
13.     [HideInInspector] public bool playersTurn = true;       //Boolean to check if it's players turn, hidden in inspector but public.
14.
15.
16.     private Text levelText;                                 //Text to display current level number.
17.     private GameObject levelImage;                          //Image to block out level as levels are being set up, background for levelText.
18.     private BoardManager boardScript;                       //Store a reference to our BoardManager which will set up the level.
19.     private int level = 1;                                  //Current level number, expressed in game as "Day 1".
20.     private List<Enemy> enemies;                            //List of all Enemy units, used to issue them move commands.
21.     private bool enemiesMoving;                             //Boolean to check if enemies are moving.
22.     private bool doingSetup = true;                         //Boolean to check if we're setting up board, prevent Player from moving during setup.
23.
24.
25.
26.     void OnEnable()
27.     {
28.         //Tell our ‘OnLevelFinishedLoading’ function to start listening for a scene change event as soon as this script is enabled.
29.         Debug.Log("Calling onEnable!");
31.     }
32.
33.     //This is called each time a scene is loaded.
35.     {
37.         Debug.Log(mode);
39.         //Add one to our level number.
40.         level++;
41.         //Call InitGame to initialize our level.
42.         Debug.Log("Calling init game!");
43.         InitGame();
44.     }
45.     void OnDisable()
46.     {
47.         Debug.Log("Calling onDisable!");
48.         //Tell our ‘OnLevelFinishedLoading’ function to stop listening for a scene change event as soon as this script is disabled.
49.         //Remember to always have an unsubscription for every delegate you subscribe to!
51.     }
52.     // Use this for initialization
53.     //Awake is always called before any Start functions
54.     void Awake()
55.     {
56.         //Check if instance already exists
57.         if (instance == null)
58.
59.             //if not, set instance to this
60.             instance = this;
61.
62.         //If instance already exists and it's not this:
63.         else if (instance != this)
64.
65.             //Then destroy this. This enforces our singleton pattern, meaning there can only ever be one instance of a GameManager.
66.             Destroy(gameObject);
67.
70.
71.         //Assign enemies to a new List of Enemy objects.
72.         enemies = new List<Enemy>();
73.
74.         //Get a component reference to the attached BoardManager script
75.         boardScript = GetComponent<BoardManager>();
76.
77.         //Call the InitGame function to initialize the first level
78.       //  InitGame();
79.     }
80.     void InitGame()
81.     {
82.         doingSetup = true;
83.
84.         levelImage = GameObject.Find("LevelImage");
85.         levelText = GameObject.Find("LevelText").GetComponent<Text>();
86.         levelText.text = "Day " + level;
87.         levelImage.SetActive(true);
88.
89.         Debug.Log("starting invode to hide level image! ");
90.         //Call the HideLevelImage function with a delay in seconds of levelStartDelay.
91.         Invoke("HideLevelImage", levelStartDelay);
92.
93.         enemies.Clear(); //clears enemies out from previous levels.
94.
95.         boardScript.SetupScene(level);
96.     }
97.
98.     private void HideLevelImage()
99.     {
100.         levelImage.SetActive(false);
101.         doingSetup = false;
102.     }
103.
104.     public void GameOver()
105.     {
106.         levelText.text = "After " + level + " days, you starved";
107.         levelImage.SetActive(true);
108.         enabled = false;
109.     }
110.
111.     // Update is called once per frame
112.     void Update()
113.     {
114.         if (playersTurn || enemiesMoving || doingSetup)
115.             return;
116.
117.         StartCoroutine(MoveEnemies());
118.     }
119.
121.     {
123.     }
124.
125.     IEnumerator MoveEnemies()
126.     {
127.         enemiesMoving = true;
128.         yield return new WaitForSeconds(turnDelay);
129.         if (enemies.Count ==0)
130.         {
131.             yield return new WaitForSeconds(turnDelay);
132.         }
133.
134.         for (int i = 0; i < enemies.Count; i++)
135.         {
136.             enemies[i].MoveEnemy();
137.             yield return new WaitForSeconds(enemies[i].moveTime);
138.         }
139.
140.         playersTurn = true;
141.         enemiesMoving = false;
142.     }
143.
144.
145.
146. }
147.
148.
149.

33. ### omalleym1115

Joined:
Aug 7, 2012
Posts:
2
Meant to say it won't hit OnLevelFinishedLoading right after hitting onEnable. Won't let me hit edit to fix the text.

34. ### guilhermecorintho

Joined:
Sep 24, 2017
Posts:
11
What if I want to build larger corridors using the Basic 2D Dungeon Generation?

35. ### Merlin143

Joined:
Apr 19, 2018
Posts:
1
where r the instructions???
I click on page to get to them but they not there
I require answer in less than hour... school project due tomorrow...

36. ### gamemaster0007

Joined:
Apr 30, 2018
Posts:
3
Hey all, I finished the player.cs script, but when I was done there was the following error
Assets/scripts/Player.cs(23,26): error CS0115: `Player.attemptMove<T>(int, int)' is marked as an override but no suitable method found to override
and then the same thing but for Player.onCantMove<T>(int,int)
I don't understand what the problem is. here is the code
Code (CSharp):
1. using System.Collections;
2. using System.Collections.Generic;
3. using UnityEngine;
4. using Completed;
5. using UnityEngine.SceneManagement;
6. public class Player : MovingObject
7. {
8.     public int WallDamage = 1;
9.     public int pointsPerFood = 10;
10.     public int pointsPerSoda = 20;
11.     public float restartLevelDelay = 1f;
12.     private Animator animator;
13.     private int food = 100;
14.     // Use this for initialization
15.     protected override void Start () {
16.         animator = GetComponent<Animator> ();
17.         food = GameManager.instance.playerFoodPoints;
18.         base.Start;
19.     }
20.     public void onDisable(){
21.         GameManager.instance.playerFoodPoints = food;
22.     }
23.     protected override void attemptMove<T>(int xdir,int ydir){
24.         food -= 1;
25.         base.attemptMove<T> (xdir, ydir);
26.         Raycast2D hit;
27.         checkIfGameOver ();
28.         GameManager.instance.playersTurn = false;
29.     }
30.     // Update is called once per frame
31.     void Update () {
32.         if (!GameManager.instance.payersTurn) {
33.             return;
34.         }
35.             int horizantal = 0;
36.             int vertical = 0;
37.         horizantal = (int)Input.GetAxisRaw ("Horizantal");
38.         vertical = (int)Input.GetAxisRaw ("Vertical");
39.         if (horizantal != 0) {
40.             vertical = 0;
41.         }
42.         if (horizantal != 0 || vertical != 0) {
43.             attemptMove <WallDamage> (horizantal,vertical);
44.         }
45.     }
46.     public override void onCantMove<T>(T component){
47.         Wall hitWall = component as Wall;
48.         hitWall.damageWall (WallDamage);
49.         animator.SetTrigger ("playerChop");
50.     }
51.     private void onTriggerEnter2D (Collider2D other){
52.         if (other.Tag == "Exit") {
53.             Invoke ("restart", restartLevelDelay);
54.             enabled = false;
55.         } else if (other.tag == "Food") {
56.             food += pointsPerFood;
57.             other.gameObject.SetActive(false);
58.         }else if (other.tag == "Soda") {
59.             food += pointsPerSoda;
60.             other.gameObject.SetActive(false);
61.         }
62.     }
63.     private void restart(){
65.     }
66.     public void loseFood(int loss){
67.         animator.SetTrigger ("playerHit");
68.         food -= loss;
69.         checkIfGameOver ();
70.     }
71.     private void checkIfGameOver(){
72.         if (food <= 0) {
73.             GameManager.instance.GameOver ();
74.         }
75.     }
76. }
77.
and moving object
Code (CSharp):
1. using UnityEngine;
2. using System.Collections;
3. using Completed;
4. //The abstract keyword enables you to create classes and class members that are incomplete and must be implemented in a derived class.
5. public abstract class MovingObject : MonoBehaviour
6. {
7.     public float moveTime = 0.1f;           //Time it will take object to move, in seconds.
8.     public LayerMask blockingLayer;         //Layer on which collision will be checked.
9.
10.
11.     private BoxCollider2D boxCollider;      //The BoxCollider2D component attached to this object.
12.     private Rigidbody2D rb2D;               //The Rigidbody2D component attached to this object.
13.     private float inverseMoveTime;          //Used to make movement more efficient.
14.
15.
16.     //Protected, virtual functions can be overridden by inheriting classes.
17.     protected virtual void Start ()
18.     {
19.         //Get a component reference to this object's BoxCollider2D
20.         boxCollider = GetComponent <BoxCollider2D> ();
21.
22.         //Get a component reference to this object's Rigidbody2D
23.         rb2D = GetComponent <Rigidbody2D> ();
24.
25.         //By storing the reciprocal of the move time we can use it by multiplying instead of dividing, this is more efficient.
26.         inverseMoveTime = 1f / moveTime;
27.     }
28.
29.
30.     //Move returns true if it is able to move and false if not.
31.     //Move takes parameters for x direction, y direction and a RaycastHit2D to check collision.
32.     protected bool Move (int xDir, int yDir, out RaycastHit2D hit)
33.     {
34.         //Store start position to move from, based on objects current transform position.
35.         Vector2 start = transform.position;
36.
37.         // Calculate end position based on the direction parameters passed in when calling Move.
38.         Vector2 end = start + new Vector2 (xDir, yDir);
39.
40.         //Disable the boxCollider so that linecast doesn't hit this object's own collider.
41.         boxCollider.enabled = false;
42.
43.         //Cast a line from start point to end point checking collision on blockingLayer.
44.         hit = Physics2D.Linecast (start, end, blockingLayer);
45.
46.         //Re-enable boxCollider after linecast
47.         boxCollider.enabled = true;
48.
49.         //Check if anything was hit
50.         if(hit.transform == null)
51.         {
52.             //If nothing was hit, start SmoothMovement co-routine passing in the Vector2 end as destination
53.             StartCoroutine (SmoothMovement (end));
54.
55.             //Return true to say that Move was successful
56.             return true;
57.         }
58.
59.         //If something was hit, return false, Move was unsuccesful.
60.         return false;
61.     }
62.
63.
64.     //Co-routine for moving units from one space to next, takes a parameter end to specify where to move to.
65.     protected IEnumerator SmoothMovement (Vector3 end)
66.     {
67.         //Calculate the remaining distance to move based on the square magnitude of the difference between current position and end parameter.
68.         //Square magnitude is used instead of magnitude because it's computationally cheaper.
69.         float sqrRemainingDistance = (transform.position - end).sqrMagnitude;
70.
71.         //While that distance is greater than a very small amount (Epsilon, almost zero):
72.         while(sqrRemainingDistance > float.Epsilon)
73.         {
74.             //Find a new position proportionally closer to the end, based on the moveTime
75.             Vector3 newPostion = Vector3.MoveTowards(rb2D.position, end, inverseMoveTime * Time.deltaTime);
76.
77.             //Call MovePosition on attached Rigidbody2D and move it to the calculated position.
78.             rb2D.MovePosition (newPostion);
79.
80.             //Recalculate the remaining distance after moving.
81.             sqrRemainingDistance = (transform.position - end).sqrMagnitude;
82.
83.             //Return and loop until sqrRemainingDistance is close enough to zero to end the function
84.             yield return null;
85.         }
86.     }
87.
88.
89.     //The virtual keyword means AttemptMove can be overridden by inheriting classes using the override keyword.
90.     //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).
91.     protected virtual void AttemptMove <T> (int xDir, int yDir)
92.         where T : Component
93.     {
94.         //Hit will store whatever our linecast hits when Move is called.
95.         RaycastHit2D hit;
96.
97.         //Set canMove to true if Move was successful, false if failed.
98.         bool canMove = Move (xDir, yDir, out hit);
99.
100.         //Check if nothing was hit by linecast
101.         if(hit.transform == null)
102.             //If nothing was hit, return and don't execute further code.
103.             return;
104.
105.         //Get a component reference to the component of type T attached to the object that was hit
106.         T hitComponent = hit.transform.GetComponent <T> ();
107.
108.         //If canMove is false and hitComponent is not equal to null, meaning MovingObject is blocked and has hit something it can interact with.
109.         if(!canMove && hitComponent != null)
110.
111.             //Call the OnCantMove function and pass it hitComponent as a parameter.
112.             OnCantMove (hitComponent);
113.     }
114.
115.
116.     //The abstract modifier indicates that the thing being modified has a missing or incomplete implementation.
117.     //OnCantMove will be overriden by functions in the inheriting classes.
118.     protected abstract void OnCantMove <T> (T component)
119.         where T : Component;
120. }
121.
122.

37. ### gwnguy

Joined:
Apr 25, 2017
Posts:
144
line 23 of 'public class Player : MovingObject' references class named "attemptMove" (lower case a in attempt)
protected override void attemptMove<T>(int xdir,int ydir){

line 91 of 'public abstract class MovingObject : MonoBehaviour' defines class named "AttemptMove" (upper case A in Attempt)
protected virtual void AttemptMove <T> (int xDir, int yDir)
where T : Component

Try changing line 23 of 'public class Player : MovingObject' to
protected override void AttemptMove<T>(int xdir,int ydir){

and see if that helps

Same thing with onCantMove, upper vs lower case

38. ### Smokeythebud

Joined:
Aug 9, 2017
Posts:
34
Hello, I am on part 4 of the tutorial and just wondering about the for loop why
Code (CSharp):
1. For (int x = 1; x &lt; columns -1; x++;)
Code (CSharp):
1. For (int x = 1; x < columns -1; x++;)

39. ### Epic-Jargon

Joined:
Mar 23, 2013
Posts:
1
Code (CSharp):
1. public class SoundManager : MonoBehaviour {
2.
3.     public AudioSource efxSource;                   //Drag a reference to the audio source which will play the sound effects.
4.     public AudioSource musicSource;                 //Drag a reference to the audio source which will play the music.
5.     public static SoundManager instance = null;     //Allows other scripts to call functions from SoundManager.
6.
7.     public float lowPitchRange = .95f;              //The lowest a sound effect will be randomly pitched.
8.     public float highPitchRange = 1.05f;            //The highest a sound effect will be randomly pitched.
9.
10.
11.     void Awake()
12.     {
13.         //Check if there is already an instance of SoundManager
14.         if (instance == null)
15.             //if not, set it to this.
16.             instance = this;
18.         else if (instance != this)
19.             //Destroy this, this enforces our singleton pattern so there can only be one instance of SoundManager.
20.             Destroy(gameObject);
21.
24.     }
25.
26.
27.     //Used to play single sound clips.
28.     public void PlaySingle(AudioClip clip)
29.     {
30.         //Set the clip of our efxSource audio source to the clip passed in as a parameter.
31.         efxSource.clip = clip;
32.
33.         //Play the clip.
34.         efxSource.Play();
35.     }
36.
37.
38.     //RandomizeSfx chooses randomly between various audio clips and slightly changes their pitch.
39.     public void RandomizeSfx(params AudioClip[] clips)
40.     {
41.         //Generate a random number between 0 and the length of our array of clips passed in.
42.         int randomIndex = Random.Range(0, clips.Length);
43.
44.         //Choose a random pitch to play back our clip at between our high and low pitch ranges.
45.         float randomPitch = Random.Range(lowPitchRange, highPitchRange);
46.
47.         //Set the pitch of the audio source to the randomly chosen pitch.
48.         efxSource.pitch = randomPitch;
49.
50.         //Set the clip to the clip at our randomly chosen index.
51.         efxSource.clip = clips[randomIndex];
52.
53.         //Play the clip.
54.         efxSource.Play();
55.     }
im on part 13 : Audio and Sound Manager but the sound manager seems to break the game any time a sound would play ie code such as
``SoundManager.instance.RandomizeSfx(drinkSound1, drinkSound2);``

would run i get an error like:
``````NullReferenceException: Object reference not set to an instance of an object
Player.OnTriggerEnter2D (UnityEngine.Collider2D other) (at Assets/Scripts/Player.cs:214)``````

im not sure whats happening as the sound manager is definatly instanciated, its in the hierachy and playing background music. after i got this error and couldnt figure it out i started just replacing my code with theres to make sure i hadnt copied it wrong but i still get the same errors

edit: added sound manager script in, its there script but figured if i didnt someone would ask me to include it. figuring that id screwed up (if i have let me know )

Last edited: May 4, 2018
40. ### Emilylyn1

Joined:
May 5, 2018
Posts:
1
Thanks for this. I was about to post here asking for help when I came across this post. It resolved the bugs I was seeing.

41. ### D-E-L-B

Joined:
Aug 27, 2013
Posts:
21
Does anyone know why when the game first launches the character speed is faster than when you go to the next scene/next day?

Code (CSharp):
1. using UnityEngine;
2. using System.Collections;
3.
4. namespace RogueKnight
5. {
6.     public abstract class MovingElement : MonoBehaviour
7.     {
8.         // BoxCollider2D, Rigidbody2D and float variable (which is used to make the movement more refined)
9.         private BoxCollider2D boxCollider2D;
10.         private float oppositeMoveTime;
11.         private new Rigidbody2D rigidbody2D;
12.
13.         // This is the time it will take for an element to move
14.         public float movingElementTime;
15.         // Layer for collision detection
17.
18.
19.         //Protected, virtual functions can be overridden by inheriting classes.
20.         protected virtual void Start ()
21.         {
22.             // Reference to "THIS" element's "BoxCollider2D" component
23.             boxCollider2D = GetComponent <BoxCollider2D> ();
24.
25.             // Reference to "THIS" element's "Rigidbody2D" component
26.             rigidbody2D = GetComponent <Rigidbody2D> ();
27.
28.             // Stores the reciprocal of movingElementTime we can use it by doing multiplication
29.             oppositeMoveTime = 1f / movingElementTime;
30.         }
31.
32.
33.         // This function returns a "true" if it is available to move, otherwise it's false
34.         // Takes an x and y for direction and a RaycastHit2D to check for a collision
35.         protected bool MoveElement(int x, int y, out RaycastHit2D raycastHit)
36.         {
37.             // Use this variable to assign a start position to move from
38.             Vector2 startPos = transform.position;
39.
40.             // This is used to calculate the end position based on direction params passed into this function
41.             Vector2 endPos = startPos + new Vector2 (x, y);
42.
43.             // This disables the boxCollider2D so that the linecast doesn't hit the element's collider
44.             boxCollider2D.enabled = false;
45.
46.             // Casts a line from a start point to an end point whilst checking collision on blockingLayerMask
47.             raycastHit = Physics2D.Linecast (startPos, endPos, blockingLayerMask);
48.
49.             // Enable the boxCollider2D again
50.             boxCollider2D.enabled = true;
51.
52.             // Check if something had been hit
53.             if(raycastHit.transform == null)
54.             {
55.                 // If there was nothing to hit then start the "SmoothMovement" coroutine whilst passing the "Vector2" end as the destination
56.                 StartCoroutine (SmoothElementMovement(endPos));
57.
58.                 // If there was a "Move" then return true
59.                 return true;
60.             }
61.
62.             // If there was a collision, the "Move" was not successful
63.             return false;
64.         }
65.
66.
67.         // This is a coroutine for moving entities from one space to the next space
68.         protected IEnumerator SmoothElementMovement(Vector3 end)
69.         {
70.             // This variable is used to calculate the leftover distance to move along by using the square magnitude of the difference between the current pos and end param
71.             // Square magnitude is used instead of magnitude because it's computationally cheaper.
72.             float sqredRemainingDistance = (transform.position - end).sqrMagnitude;
73.
74.             // While sqredRemainingDistance is greater than a miniscule amount:
75.             while(sqredRemainingDistance > float.Epsilon)
76.             {
77.                 // Find a new position that is closer to the end based on the oppositeMoveTime
78.                 Vector3 newPos = Vector3.MoveTowards(rigidbody2D.position, end, oppositeMoveTime * Time.deltaTime);
79.
80.                 // Call the MovePosition on the attached "Rigidbody2D" and move it to the calculated pos
81.                 rigidbody2D.MovePosition (newPos);
82.
83.                 // Calculate the leftover distance again after an element
84.                 sqredRemainingDistance = (transform.position - end).sqrMagnitude;
85.
86.                 // Return and keep repeating until sqredRemainingDistance is approximately zero to end this function
87.                 yield return null;
88.             }
89.         }
90.
91.
92.         // The virtual keyword makes sure that "TryMove" can be overridden by inheriting classes using "override"
93.         // "TryMove" takes a generic parameter to specify the type of the component we expect our entity to interact with if the entity is blocked
94.         protected virtual void TryMove <T> (int x, int y)
95.             where T : Component
96.         {
97.             // Hit will hold what the linecast hits when "MoveElement" is invoked
98.             RaycastHit2D hit;
99.
100.             // canMove is set to true if "MoveElement" was successful
101.             bool moveAllowed = MoveElement(x, y, out hit);
102.
103.             // This checks to see if nothing was hit by the linecast
104.             if (hit.transform == null)
105.             {
106.                 // If nothing was hit at all then get out of this function
107.                 return;
108.             }
109.
110.             // Gets a "component" reference to the type T (a generic) that is attached to the element that was hit
111.             T hitComponent = hit.transform.GetComponent<T>();
112.
113.             // If canMove is false and also hitComponent is not equal to null
114.             if (!moveAllowed && hitComponent != null)
115.             {
116.                 // Call the IfCantMove function and pass hitComponent as the param
117.                 IfCantMove(hitComponent);
118.             }
119.         }
120.
121.         // This function in an inherited class
122.         protected abstract void IfCantMove <T> (T component)
123.             where T : Component;
124.     }
125. }
126.
Code (CSharp):
1. using UnityEngine;
2. using System.Collections;
3. using UnityEngine.UI;    //Allows us to use UI.
4. using UnityEngine.SceneManagement;
5.
6. namespace RogueKnight
7. {
8.     // "Player" inherits from MovingElement
9.     public class Knight : MovingElement
10.     {
11.         // Adds more energy when the player picks up a cake
12.         public int pointsPerCake = 15;
13.
14.         //Delay time to restart level
15.         public float levelRestartDelay = 1f;
16.
17.         // The amount of damage a player does to a wall when stabbing it
18.         public int damagedWall = 1;
19.
20.         // Adds more energy when the player picks up wine
21.         public int pointsPerWine = 25;
22.
23.         // This is used to store the player food points total during the level
24.         private int energy;
25.
26.         // Text to display how much energy you have
27.         public Text energyText;
28.
29.         // References to "AudioClips" to play sounds for a variety of different reasons
30.         public AudioClip moveSound1;
31.         public AudioClip moveSound2;
32.         public AudioClip eatSound1;
33.         public AudioClip eatSound2;
34.         public AudioClip drinkSound1;
35.         public AudioClip drinkSound2;
36.         public AudioClip gameOverSound;
37.
38.         // Reference to access the player animations
39.         private Animator animator;
40.
41.
42.         // This will override the Start function in "MovingObject"
43.         protected override void Start ()
44.         {
45.             // Gets a reference to the Player's "animator" component
46.             animator = GetComponent<Animator>();
47.
48.             // Gets the current energy point total stored in GameManager.instance and is shared between levels
49.             energy = GameManager.instance.playerFoodPoints;
50.
51.             // Sets the energyText to reflect the current energy total
52.             energyText.text = "Energy: " + energy;
53.
54.             // Calls the "Start" function of the "MovingObject" base class
55.             base.Start ();
56.         }
57.
58.
59.         // This function is called when "THIS" game object the script is attached to is disabled
60.         private void OnDisable ()
61.         {
62.             // When the "Player" object is disabled, it stores the current local energy total in the "GameManager" so it can be shared to the next level
63.             GameManager.instance.playerFoodPoints = energy;
64.         }
65.
66.
67.         private void Update ()
68.         {
69.             // If it isn't the player's turn then don't run this function anymore
70.             if (!GameManager.instance.knightsTurn)
71.             {
72.                 return;
73.             }
74.
75.             // These variables are used to store the horizontal and vertical direction
76.             int vertical = 0;
77.             int horizontal = 0;
78.
79.             // Assigns "horizontal" and "vertical" to the axes in Unity
80.             vertical = (int)(Input.GetAxisRaw("Vertical"));
81.             horizontal = (int) (Input.GetAxisRaw ("Horizontal"));
82.
83.             // If moving horizontally, you aren't moving vertically
84.             if(horizontal != 0)
85.             {
86.                 vertical = 0;
87.             }
88.
89.             //Check if we have a non-zero value for horizontal or vertical
90.             if(horizontal != 0 || vertical != 0)
91.             {
92.                 // Invoke "TryMove" whilst passing in the param "BreakableWall," since that is what the Player might encounter
93.                 // Pass in the "horizontal" and "vertical" as params to tell the direction to move the Player in
94.                 TryMove<BreakableWall> (horizontal, vertical);
95.             }
96.         }
97.
98.         // Takes a generic "T" which will be of type "Wall," it also takes the integers for the x and y direction to move in
99.         protected override void TryMove <T> (int x, int y)
100.         {
102.             energy--;
103.
104.             // Update the food text display to reflect the current amount of energy
105.             energyText.text = "Energy: " + energy;
106.
107.             // Call the "TryMove" function in the base class by passing in the generic "T" (the wall) and the x and y to move in
108.             base.TryMove<T> (x, y);
109.
110.             // RaycastHit2D allows a reference to the result of the Linecast done in "MoveObject"
111.             RaycastHit2D raycastHit;
112.
113.             // If "MoveObject" does return true then the player can move into an empty space
114.             if (MoveElement(x, y, out raycastHit))
115.             {
116.                 // This will play a random sound, either moveSound1/2
117.                 SoundManager.instance.RandomSoundEffect(moveSound1, moveSound2);
118.             }
119.
120.             // Check to see if the player has no cake points left
121.             IsGameOver ();
122.
123.             // The players turn is now over, so they cannot move when the enemies are moving
124.             GameManager.instance.knightsTurn = false;
125.         }
126.
127.         // This function takes a generic "T" which in this scenario is the "Wall"
128.         protected override void IfCantMove <T> (T component)
129.         {
130.             // Set hitWall to equal the component passed in as the param
131.             BreakableWall wallHit = component as BreakableWall;
132.
133.             // Call the HitWall function of the Wall we are now hitting
134.             wallHit.HitWall (damagedWall);
135.
136.             // Set it so the player is now stabbing
137.             animator.SetTrigger ("playerStab");
138.         }
139.
140.         private void OnTriggerEnter2D (Collider2D other)
141.         {
142.             // Check if the player collided with a game object with the tag "NextDungeon"
143.             if(other.tag == "NextDungeon")
144.             {
145.                 // Call the "Restart" function to start the next level with a delay of restartLevelDelay
146.                 Invoke ("Restart", levelRestartDelay);
147.
148.                 // Disable the player object since the level is now over
149.                 enabled = false;
150.             }
151.
152.             // If the player collected some cake
153.             else if(other.tag == "Cake")
154.             {
156.                 energy += pointsPerCake;
157.
158.                 // Update the energyText to show the current amount of energy that the player possesses
159.                 energyText.text = "+" + pointsPerCake + " Energy: " + energy;
160.
161.                 // Invoke the RandomSoundEffect function pass in two eat sounds to choose from
162.                 SoundManager.instance.RandomSoundEffect(eatSound1, eatSound2);
163.
164.                 // Don't show the cake the player collided with
165.                 other.gameObject.SetActive (false);
166.             }
167.
168.             // Same as above but for the wine
169.             else if(other.tag == "Wine")
170.             {
171.                 energy += pointsPerWine;
172.
173.                 energyText.text = "+" + pointsPerWine + " Energy: " + energy;
174.
175.                 SoundManager.instance.RandomSoundEffect(drinkSound1, drinkSound2);
176.
177.                 other.gameObject.SetActive (false);
178.             }
179.         }
180.
181.
182.         // This reloads the scene
183.         private void Restart ()
184.         {
185.             //Load the last scene loaded and load it in "Single" mode to replace the existing one
187.         }
188.
189.
190.         // This is called when any enemy attacks the player
191.         public void LoseEnergy (int energyLoss)
192.         {
193.             // Set it so the player has been hurt
194.             animator.SetTrigger ("playerHurt");
195.
196.             //Subtract energyLoss from the total amount of energy
197.             energy -= energyLoss;
198.
199.             // Update the food display with the most up-to-date energy total
200.             energyText.text = "-" + energyLoss + " Energy: " + energy;
201.
202.             // Check to see if game has been ended
203.             IsGameOver ();
204.         }
205.
206.
207.         // If there is no more energy left then play a sound and end the game
208.         private void IsGameOver()
209.         {
210.             if (energy <= 0)
211.             {
212.                 SoundManager.instance.musicSource.Stop();
213.                 GameManager.instance.GameOver ();
214.             }
215.         }
216.     }
217. }
218.
I have tried to rebuild it myself so variables and comments are different.

Thanks.

42. ### Czar-Man

Joined:
May 10, 2013
Posts:
20
When starting the completed project on my phone, it doesn't detect touch input on my phone outside of the menus.

43. ### AlfredvanRaal

Joined:
Feb 27, 2017
Posts:
5
Thanks, this helped me to solve two of my bugs in gameplay:
- The game started on Day 2 and then next level was Day 4.
- On day four scene began with food points zero. After one move, sprite "starved".

Still have some bugs left:
- i can move through enemy.
- And in player script: "The variable "hit" is declared but never used"

44. ### AlfredvanRaal

Joined:
Feb 27, 2017
Posts:
5
______________

Thanks! I searched two hours myself for this and couldnt find it!
This is exactly what i need to stop my sprite from moving through enemies!

45. ### AlfredvanRaal

Joined:
Feb 27, 2017
Posts:
5
Thought i almost were there. But now i finished the Audio and Soundmanager and for some reason the game states at starting te Level after just 1 move "you starved".
No idea why? What does Soundmanager and Audio have to do with that? Maybe some-one knows?

46. ### Negtiv

Joined:
Mar 27, 2018
Posts:
1
i have a problem with the player. i can only move once after that it doesn't move anymore

Player.cs script.
Code (CSharp):
1. using System.Collections;
2. using System.Collections.Generic;
3. using UnityEngine;
4. using UnityEngine.SceneManagement;
5.
6. public class Player : MovingObject
7. {
8.
9.     public int wallDamage = 1;
10.     public int pointsPerFood = 10;
11.     public int pointsPerSoda = 20;
12.     public float restartLevelDelay = 1f;
13.
14.     private Animator animator;
15.     private int food;
16.
17.
18.     protected override void Start()
19.     {
20.         animator = GetComponent<Animator>();
21.
22.         food = GameManager.instance.playerFoodPoints;
23.
24.         base.Start();
25.     }
26.
27.     private void OnDisable()
28.     {
29.         GameManager.instance.playerFoodPoints = food;
30.     }
31.
32.     void Update()
33.     {
34.         if (!GameManager.instance.playersTurn) return;
35.
36.         int horizontal = 0;
37.         int vertical = 0;
38.
39.         horizontal = (int)Input.GetAxisRaw("Horizontal");
40.         vertical = (int)Input.GetAxisRaw("Vertical");
41.
42.         if (horizontal != 0)
43.             vertical = 0;
44.
45.         if (horizontal != 0 || vertical != 0)
46.             AttemptMove<Wall>(horizontal, vertical);
47.     }
48.
49.     protected override void AttemptMove<T>(int xDir, int yDir)
50.     {
51.         food--;
52.
53.         base.AttemptMove<T>(xDir, yDir);
54.
55.         RaycastHit2D hit;
56.
57.         CheckIfGameOver();
58.
59.         GameManager.instance.playersTurn = false;
60.     }
61.
62.     private void OnTriggerEnter2D(Collider2D other)
63.     {
64.         if (other.tag == "Exit")
65.         {
66.             Invoke("Restart", restartLevelDelay);
67.             enabled = false;
68.         }
69.         else if (other.tag == "Food")
70.         {
71.             food += pointsPerFood;
72.             other.gameObject.SetActive(false);
73.         }
74.         else if (other.tag == "Soda")
75.         {
76.             food += pointsPerSoda;
77.             other.gameObject.SetActive(false);
78.         }
79.
80.     }
81.
82.     protected override void OnCantMove<T>(T component)
83.     {
84.         Wall hitWall = component as Wall;
85.         hitWall.DamageWall(wallDamage);
86.         animator.SetTrigger("playerChop");
87.     }
88.
89.     private void Restart()
90.     {
92.     }
93.
94.     public void LoseFood(int loss)
95.     {
96.         animator.SetTrigger("playerHit");
97.         food -= loss;
98.         CheckIfGameOver();
99.     }
100.
101.     private void CheckIfGameOver()
102.     {
103.         if (food <= 0)
104.             GameManager.instance.GameOver();
105.     }
106. }
GameManager.cs

Code (CSharp):
1. using System.Collections;
2. using System.Collections.Generic;
3. using UnityEngine;
4.
5. public class GameManager : MonoBehaviour
6. {
7.     public float turnDelay = .1f;
8.     public static GameManager instance = null;
9.     public BoardManager boardScript;
10.     public int playerFoodPoints = 100;
11.     [HideInInspector] public bool playersTurn = true;
12.
13.     private int level = 3;
14.     private List<Enemy> enemies;
15.     private bool enemiesMoving;
16.
17.
18.     void Awake()
19.     {
20.         if (instance == null)
21.             instance = this;
22.         else if (instance != this)
23.             Destroy(gameObject);
24.
26.         enemies = new List<Enemy>();
27.         boardScript = GetComponent<BoardManager>();
28.         InitGame();
29.     }
30.
31.     void InitGame()
32.     {
33.         enemies.Clear();
34.         boardScript.SetupScene(level);
35.     }
36.
37.     public void GameOver()
38.     {
39.         enabled = false;
40.     }
41.
42.     void Update()
43.     {
44.         if (playersTurn || enemiesMoving)
45.             return;
46.
47.         StartCoroutine(MoveEnemies());
48.     }
49.
51.     {
53.     }
54.
55.     IEnumerator MoveEnemies()
56.     {
57.         enemiesMoving = true;
58.         yield return new WaitForSeconds(turnDelay);
59.         if (enemies.Count == 0)
60.         {
61.             yield return new WaitForSeconds(turnDelay);
62.         }
63.
64.         for (int i = 0; i < enemies.Count; i++)
65.         {
66.             enemies[i].MoveEnemy();
67.             yield return new WaitForSeconds(enemies[i].moveTime);
68.         }
69.
70.         playersTurn = true;
71.         enemiesMoving = false;
72.     }
73. }

paulatkins88 likes this.
47. ### Smokeythebud

Joined:
Aug 9, 2017
Posts:
34
hello, I worked my way through all of step 11, when I play tested everything works fine except the enemies will not attack, I compared the compete code to mine and cant see the difference. Although when I use the completed code it works fine so I must be missing something and here is the code

mine not working:
Code (CSharp):
1.
2. using System.Collections;
3. using System.Collections.Generic;
4. using UnityEngine;
5.
6. public class Enemy : MovingObject
7. {
8.     public int playerDamage;
9.
10.     private Animator animator;
11.     private Transform target;
12.     private bool skipMove;
13.
14.     protected override void Start()
15.     {
17.         animator = GetComponent<Animator>();
18.         target = GameObject.FindGameObjectWithTag("Player").transform;
19.         base.Start();
20.     }
21.
22.     protected override void AttemptMove<T>(int xDir, int yDir)
23.     {
24.         if (skipMove)
25.         {
26.             skipMove = false;
27.             return;
28.         }
29.
30.         base.AttemptMove<T>(xDir, yDir);
31.         skipMove = true;
32.     }
33.     public void MoveEnemy()
34.     {
35.         int xDir = 0;
36.         int yDir = 0;
37.         if (Mathf.Abs(target.position.x - transform.position.x) > float.Epsilon)
38.             yDir = target.position.y > transform.position.y ? 1 : -1;
39.         else
40.             xDir = target.position.x > transform.position.x ? 1 : -1;
41.
42.         AttemptMove<Player>(xDir, yDir);
43.     }
44.
45.     protected override void OnCantMove<T>(T component)
46.     {
47.         Player hitPlayer = component as Player;
48.         hitPlayer.LoseFood(playerDamage);
49.         animator.SetTrigger("enemyAttack");
50.     }
51. }
52.
and the completed code from the kit:
Code (CSharp):
1. using System.Collections;
2. using System.Collections.Generic;
3. using UnityEngine;
4.
5. public class Enemy : MovingObject
6. {
7.     public int playerDamage;
8.
9.     private Animator animator;
10.     private Transform target;
11.     private bool skipMove;
12.
13.     protected override void Start()
14.     {
16.         animator = GetComponent<Animator>();
17.         target = GameObject.FindGameObjectWithTag("Player").transform;
18.         base.Start();
19.     }
20.
21.     protected override void AttemptMove<T>(int xDir, int yDir)
22.     {
23.         if (skipMove)
24.         {
25.             skipMove = false;
26.             return;
27.         }
28.
29.         base.AttemptMove<T>(xDir, yDir);
30.         skipMove = true;
31.     }
32.     public void MoveEnemy()
33.     {
34.         int xDir = 0;
35.         int yDir = 0;
36.         if (Mathf.Abs(target.position.x - transform.position.x) < float.Epsilon)
37.             yDir = target.position.y > transform.position.y ? 1 : -1;
38.         else
39.             xDir = target.position.x > transform.position.x ? 1 : -1;
40.
41.         AttemptMove<Player>(xDir, yDir);
42.     }
43.
44.     protected override void OnCantMove<T>(T component)
45.     {
46.         Player hitPlayer = component as Player;
47.         hitPlayer.LoseFood(playerDamage);
48.         animator.SetTrigger("enemyAttack");
49.     }
50. }
51.
52.
it has to be something in there, but I went though it 3 times and they look identical

48. ### gamemaster0007

Joined:
Apr 30, 2018
Posts:
3
I have a problem where the first two levels are fine. however, then it skips to day four with 0 food and then it says I survived 5 days, why is that?
the player script
Code (CSharp):
1. using UnityEngine;
2. using UnityEngine.UI;
3. using System.Collections;
4. using UnityEngine.SceneManagement;      //Allows us to use SceneManager
5. using Completed;
6. //Player inherits from MovingObject, our base class for objects that can move, Enemy also inherits from this.
7. public class Player : MovingObject
8. {
9.     public float restartLevelDelay = 1f;        //Delay time in seconds to restart level.
10.     public int PointsPerGold = 10;
11.     public int PointsPerJewel = 20;
12.     public int pointsPerFood = 10;              //Number of points to add to player food points when picking up a food object.
13.     public int pointsPerSoda = 20;              //Number of points to add to player food points when picking up a soda object.
14.     public int wallDamage = 1;                  //How much damage a player does to a wall when chopping it.
15.     public Text foodText;
16.     public Text GoldText;
17.     private Animator animator;                  //Used to store a reference to the Player's animator component.
18.     private int food;                           //Used to store player food points total during level.
19.     private int Cash;
20.     public AudioClip MoveSound1;
21.     public AudioClip MoveSound2;
22.     public AudioClip EatSound1;
23.     public AudioClip EatSound2;
24.     public AudioClip DrinkSound1;
25.     public AudioClip DrinkSound2;
26.     public AudioClip GameOverSound;
27.     //Start overrides the Start function of MovingObject
28.     protected override void Start ()
29.     {
30.         //Get a component reference to the Player's animator component
31.         animator = GetComponent<Animator>();
32.
33.         //Get the current food point total stored in GameManager.instance between levels.
34.         Cash = GameManager.instance.PlayerCashPoints;
35.         food = GameManager.instance.playerFoodPoints;
36.         foodText.text = "Food: " + food;
37.         GoldText.text = "Cash: \$" + Cash;
38.         //Call the Start function of the MovingObject base class.
39.         base.Start ();
40.     }
41.
42.
43.     //This function is called when the behaviour becomes disabled or inactive.
44.     private void OnDisable ()
45.     {
46.         //When Player object is disabled, store the current local food total in the GameManager so it can be re-loaded in next level.
47.         GameManager.instance.playerFoodPoints = food;
48.         GameManager.instance.PlayerCashPoints = Cash;
49.     }
50.
51.
52.     private void Update ()
53.     {
54.         //If it's not the player's turn, exit the function.
55.         if(!GameManager.instance.playersTurn) return;
56.
57.         int horizontal = 0;     //Used to store the horizontal move direction.
58.         int vertical = 0;       //Used to store the vertical move direction.
59.
60.
61.         //Get input from the input manager, round it to an integer and store in horizontal to set x axis move direction
62.         horizontal = (int) (Input.GetAxis ("Horizontal"));
63.
64.         //Get input from the input manager, round it to an integer and store in vertical to set y axis move direction
65.         vertical = (int) (Input.GetAxis ("Vertical"));
66.
67.         //Check if moving horizontally, if so set vertical to zero.
68.         if(horizontal != 0)
69.         {
70.             vertical = 0;
71.         }
72.
73.         //Check if we have a non-zero value for horizontal or vertical
74.         if(horizontal != 0 || vertical != 0)
75.         {
76.             //Call AttemptMove passing in the generic parameter Wall, since that is what Player may interact with if they encounter one (by attacking it)
77.             //Pass in horizontal and vertical as parameters to specify the direction to move Player in.
78.             AttemptMove<Wall> (horizontal, vertical);
79.         }
80.     }
81.
82.     //AttemptMove overrides the AttemptMove function in the base class MovingObject
83.     //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.
84.     protected override void AttemptMove<T>  (int xDir, int yDir)
85.     {
86.         //Every time player moves, subtract from food points total.
87.         food--;
88.         foodText.text = "Food: " + food;
89.         GoldText.text = "Cash: \$" + Cash;
90.         //Call the AttemptMove method of the base class, passing in the component T (in this case Wall) and x and y direction to move.
91.         base.AttemptMove<Wall>  (xDir, yDir);
92.
93.         //Hit allows us to reference the result of the Linecast done in Move.
94.         RaycastHit2D hit;
95.
96.         //If Move returns true, meaning Player was able to move into an empty space.
97.         if (Move (xDir, yDir, out hit))
98.         {
99.             SoundManager.instance.RandomizeSfx (MoveSound1, MoveSound2);
100.         }
101.
102.         //Since the player has moved and lost food points, check if the game has ended.
103.         CheckIfGameOver ();
104.
105.         //Set the playersTurn boolean of GameManager to false now that players turn is over.
106.         GameManager.instance.playersTurn = false;
107.     }
108.
109.
110.     //OnCantMove overrides the abstract function OnCantMove in MovingObject.
111.     //It takes a generic parameter T which in the case of Player is a Wall which the player can attack and destroy.
112.     protected override void OnCantMove<T>  (T component)
113.     {
114.         //Set hitWall to equal the component passed in as a parameter.
115.         Wall hitWall = component as Wall;
116.
117.         //Call the DamageWall function of the Wall we are hitting.
118.         hitWall.DamageWall (wallDamage);
119.
120.         //Set the attack trigger of the player's animation controller in order to play the player's attack animation.
121.         animator.SetTrigger ("playerChop");
122.     }
123.
124.
125.     //OnTriggerEnter2D is sent when another object enters a trigger collider attached to this object (2D physics only).
126.     private void OnTriggerEnter2D (Collider2D other)
127.     {
128.         //Check if the tag of the trigger collided with is Exit.
129.         if (other.tag == "Exit") {
130.             //Invoke the Restart function to start the next level with a delay of restartLevelDelay (default 1 second).
131.             Invoke ("Restart", restartLevelDelay);
132.
133.             //Disable the player object since level is over.
134.             enabled = false;
135.         }
136.
137.         //Check if the tag of the trigger collided with is Food.
138.         else if (other.tag == "Food") {
139.             //Add pointsPerFood to the players current food total.
140.             food += pointsPerFood;
141.             foodText.text = "+ " + pointsPerFood + " Food: " + food;
142.             SoundManager.instance.RandomizeSfx (EatSound1, EatSound2);
143.             //Disable the food object the player collided with.
144.             other.gameObject.SetActive (false);
145.         }
146.
147.         //Check if the tag of the trigger collided with is Soda.
148.         else if (other.tag == "Soda") {
149.             //Add pointsPerSoda to players food points total
150.             food += pointsPerSoda;
151.             foodText.text = "+ " + pointsPerSoda + " Food: " + food;
152.             SoundManager.instance.RandomizeSfx (DrinkSound1, DrinkSound2);
153.             //Disable the soda object the player collided with.
154.             other.gameObject.SetActive (false);
155.         } else if (other.tag == "gold") {
156.             Cash += PointsPerGold;
157.             GoldText.text = "+" + PointsPerGold + "Cash: \$" + Cash;
158.             other.gameObject.SetActive (false);
159.         } else if (other.tag == "Jewel") {
160.             Cash += PointsPerJewel;
161.             GoldText.text = "+" + PointsPerJewel + "Cash: \$" + Cash;
162.             other.gameObject.SetActive (false);
163.         }
164.     }
165.
166.
167.     //Restart reloads the scene when called.
168.     private void Restart ()
169.     {
170.         //Load the last scene loaded, in this case Main, the only scene in the game.
172.     }
173.
174.
175.     //LoseFood is called when an enemy attacks the player.
176.     //It takes a parameter loss which specifies how many points to lose.
177.     public void LoseFood (int loss)
178.     {
179.         //Set the trigger for the player animator to transition to the playerHit animation.
180.         animator.SetTrigger ("playerHit");
181.
182.         //Subtract lost food points from the players total.
183.         food -= loss;
184.         foodText.text = "- " + loss + " Food: " + food;
185.         //Check to see if game has ended.
186.         CheckIfGameOver ();
187.     }
188.
189.
190.     //CheckIfGameOver checks if the player is out of food points and if so, ends the game.
191.     private void CheckIfGameOver ()
192.     {
193.         //Check if food point total is less than or equal to zero.
194.         if (food <= 0)
195.         {
196.             SoundManager.instance.RandomizeSfx (GameOverSound);
197.             SoundManager.instance.musicSource.Stop ();
198.             //Call the GameOver function of GameManager.
199.
200.             GameManager.instance.GameOver ();
201.         }
202.     }
203. }
204.
the game manager
Code (CSharp):
1. using UnityEngine;
2. using System.Collections;
3. using Completed;
4. using UnityEngine.UI;
5. using System.Collections.Generic;       //Allows us to use Lists.
6. using UnityEngine.SceneManagement;
7. public class GameManager : MonoBehaviour
8. {
9.     public float levelStartDelay = 2f;                      //Time to wait before starting level, in seconds.
10.     public float turnDelay = 0.1f;                          //Delay between each Player turn.
11.     public int playerFoodPoints = 100;                      //Starting value for Player food points.
12.     public int PlayerCashPoints = 0;
13.     public static GameManager instance = null;              //Static instance of GameManager which allows it to be accessed by any other script.
14.     [HideInInspector] public bool playersTurn = true;       //Boolean to check if it's players turn, hidden in inspector but public.
15.
16.     private Text LevelText;
17.     private GameObject LevelImage;
18.     private BoardManager boardScript;                       //Store a reference to our BoardManager which will set up the level.
19.     private int level = 1;                                  //Current level number, expressed in game as "Day 1".
20.     private List<Enemy> enemies;                           //List of all Enemy units, used to issue them move commands.
21.     private bool enemiesMoving;                             //Boolean to check if enemies are moving.
22.     private bool DoingSetup;
23.
24.
25.     //Awake is always called before any Start functions
26.     void Awake()
27.     {
28.         //Check if instance already exists
29.         if (instance == null)
30.
31.             //if not, set instance to this
32.             instance = this;
33.
34.         //If instance already exists and it's not this:
35.         else if (instance != this)
36.
37.             //Then destroy this. This enforces our singleton pattern, meaning there can only ever be one instance of a GameManager.
38.             Destroy(gameObject);
39.
42.
43.         //Assign enemies to a new List of Enemy objects.
44.         enemies = new List<Enemy>();
45.
46.         //Get a component reference to the attached BoardManager script
47.         boardScript = GetComponent<BoardManager>();
48.
49.         //Call the InitGame function to initialize the first level
50.         InitGame();
51.     }
52.
54.     {
55.         //Add one to our level number.
56.         level++;
57.         //Call InitGame to initialize our level.
58.         InitGame();
59.     }
60.     //Initializes the game for each level.
61.     void InitGame()
62.     {
63.         DoingSetup = true;
64.         LevelImage = GameObject.Find ("LevelImage");
65.         LevelText = GameObject.Find ("LevelText").GetComponent<Text>();
66.         LevelText.text = "Day " + level;
67.         LevelImage.SetActive (true);
68.         Invoke ("HideLevelImage",levelStartDelay);
69.         //Clear any Enemy objects in our List to prepare for next level.
70.         enemies.Clear();
71.
72.         //Call the SetupScene function of the BoardManager script, pass it current level number.
73.         boardScript.SetupScene(level);
74.
75.     }
76.
77.     private void HideLevelImage(){
78.         LevelImage.SetActive (false);
79.         DoingSetup = false;
80.     }
81.     //Update is called every frame.
82.     void Update()
83.     {
84.         //Check that playersTurn or enemiesMoving or doingSetup are not currently true.
85.         if(playersTurn || enemiesMoving || DoingSetup)
86.
87.             //If any of these are true, return and do not start MoveEnemies.
88.             return;
89.
90.         //Start moving enemies.
91.         StartCoroutine (MoveEnemies ());
92.     }
93.
94.     //Call this to add the passed in Enemy to the List of Enemy objects.
96.     {
97.         //Add Enemy to List enemies.
99.     }
100.
101.
102.     //GameOver is called when the player reaches 0 food points
103.     public void GameOver()
104.     {
105.         //Set levelText to display number of levels passed and game over message
106.         LevelText.text = "After " + level + " days, you starved.";
107.         LevelImage.SetActive(true);
108.
109.         //Disable this GameManager.
110.         enabled = false;
111.     }
112.
113.
114.     //Coroutine to move enemies in sequence.
115.     IEnumerator MoveEnemies()
116.     {
117.         //While enemiesMoving is true player is unable to move.
118.         enemiesMoving = true;
119.
120.         //Wait for turnDelay seconds, defaults to .1 (100 ms).
121.         yield return new WaitForSeconds(turnDelay);
122.
123.         //If there are no enemies spawned (IE in first level):
124.         if (enemies.Count == 0)
125.         {
126.             //Wait for turnDelay seconds between moves, replaces delay caused by enemies moving when there are none.
127.             yield return new WaitForSeconds(turnDelay);
128.         }
129.
130.         //Loop through List of Enemy objects.
131.         for (int i = 0; i < enemies.Count; i++)
132.         {
133.             //Call the MoveEnemy function of Enemy at index i in the enemies List.
134.             enemies[i].MoveEnemy ();
135.
136.             //Wait for Enemy's moveTime before moving next Enemy,
137.             yield return new WaitForSeconds(enemies[i].moveTime);
138.         }
139.         //Once Enemies are done moving, set playersTurn to true so player can move.
140.         playersTurn = true;
141.
142.         //Enemies are done moving, set enemiesMoving to false.
143.         enemiesMoving = false;
144.     }
145. }

unity_gNf04-k13wyxCA likes this.
49. ### chipuchipu

Joined:
Jun 20, 2017
Posts:
1
Hey! Great tutorials! I learned a lot!

I managed to merge both the 2D-Roguelike tutorial with the Basic 2D Dungeon Generation but I have a question regarding the Objective Tile.

Currently, the first level works flawlessly. However, once the player enters level 2, the hierarchy seems to spawn two boardHolders and the map becomes impossible to navigate as the player spawns in walls.

For testing purposes, I manually deleted one of the boardHolders and got my player moving again. Interestingly, if the player enters level 3, the player game object is destroyed somehow and exceptions are thrown about the enemies unable to locate the player.

I was just wondering if anyone could help me with this weird issue.

Joined:
Feb 27, 2017
Posts:
5