1. Universal Render Pipeline
  2. Διαγράφω example assets από hierarchy και Assets.
  3. Camera > Reset   και πάω την camera λίγο πίσω (z=-5).
  4. Hierarchy> Create empty> Environment
  5. Φτιάχνω ένα “παιδί” του 3D Object>Cube με scale(2,2,2).
  6. Περιστρέφουμε το φως ώστε Rotation x=153
  7. Grid> Edit Grid and snap settings> 2. Κατόπιν φτιάχνω 8-9 αντίγραφα (Ctrl+d). Με Ctrl τα μετακινώ.
  8. Hierarchy> Create empty> Hero         (reset)
  9. Φτιάχνω ένα “παιδί” του 3D Object>Capsule . Κάνω reset
  10. Τοποθετώ το Hero πάνω στον πρώτο κύβο και την κάψουλα το ίδιο.
  11. Επιλέγω Hero και Add Component> Character Controller (αντί για Rigidbody). Center Y=1.
  12. Main Camera διαγράφω Simple Camera Controller
  13. Δημιουργώ ένα Layer (Ground) και ορίζω στο Environment ως layer το Ground.
  14. Στον φάκελο Assets δημιουργώ φάκελο Hero και μέσα σε αυτόν δημιουργώ ένα αρχείο C# με όνομα HeroCharacterController το οποίο και βάζω στο Hero.
  15. using UnityEngine;
    
    public class HeroCharacterController : MonoBehaviour
    {
        private float βαρύτητα = -50f;
        private CharacterController cc;
        private Vector3 ταχύτητα;
        void Start()
        {
             cc = GetComponent();   }
        void Update()
        {
            ταχύτητα.y = ταχύτητα.y + βαρύτητα*Time.deltaTime;
            cc.Move(ταχύτητα * Time.deltaTime);
        }
    }
    
  16. using UnityEngine;
    
    public class HeroCharacterController : MonoBehaviour
    {
        [SerializeField] LayerMask μονοπάτι;
        private float βαρύτητα = -50f;
        private CharacterController cc;
        private Vector3 ταχύτητα;
        private bool ακούμπησε;
        void Start()
        {
            cc = GetComponent();
        }
    
        // Update is called once per frame
        void Update()
        {
            ακούμπησε = Physics.CheckSphere(transform.position, 0.1f, μονοπάτι, QueryTriggerInteraction.Ignore);
            if (ακούμπησε && ταχύτητα.y<0)
            {
                ταχύτητα.y = 0;
            }
            else
            {
                ταχύτητα.y = ταχύτητα.y + βαρύτητα * Time.deltaTime;
            }
            
            cc.Move(ταχύτητα * Time.deltaTime);
        }
    }
    
  17. using UnityEngine;
    
    public class HeroCharacterController : MonoBehaviour
    {
        [SerializeField] LayerMask μονοπάτι;
        [SerializeField] private float συντελεστήςΤαχύτητας = 8f;
        private float βαρύτητα = -50f;
        private CharacterController cc;
        private Vector3 ταχύτητα;
        private bool ακούμπησε;
        private float οριζόντιαΚατεύθυνση;
        void Start()
        {
            cc = GetComponent();
        }
    
        // Update is called once per frame
        void Update()
        {
            οριζόντιαΚατεύθυνση = 1;
            transform.forward= new Vector3(οριζόντιαΚατεύθυνση, 0 , Mathf.Abs(οριζόντιαΚατεύθυνση)-1);
            ακούμπησε = Physics.CheckSphere(transform.position, 0.1f, μονοπάτι, QueryTriggerInteraction.Ignore);
            if (ακούμπησε && ταχύτητα.y<0)
            {
                ταχύτητα.y = 0;
            }
            else
            {
                ταχύτητα.y = ταχύτητα.y + βαρύτητα * Time.deltaTime;
            }
            cc.Move(new Vector3(οριζόντιαΚατεύθυνση * συντελεστήςΤαχύτητας, 0, 0) * Time.deltaTime);
            cc.Move(ταχύτητα * Time.deltaTime);
        }
    }
    
  18. using UnityEngine;
    
    public class HeroCharacterController : MonoBehaviour
    {
        [SerializeField] LayerMask μονοπάτι;
        [SerializeField] private float συντελεστήςΤαχύτητας = 8f;
        [SerializeField] private float άλμα = 2f;
        private float βαρύτητα = -50f;
        private CharacterController cc;
        private Vector3 ταχύτητα;
        private bool ακούμπησε;
        private float οριζόντιαΚατεύθυνση;
    
        void Start()
        {
            cc = GetComponent();
        }
    
        // Update is called once per frame
        void Update()
        {
            οριζόντιαΚατεύθυνση = 1;
            transform.forward= new Vector3(οριζόντιαΚατεύθυνση, 0 , Mathf.Abs(οριζόντιαΚατεύθυνση)-1);
            ακούμπησε = Physics.CheckSphere(transform.position, 0.1f, μονοπάτι, QueryTriggerInteraction.Ignore);
            if (ακούμπησε && ταχύτητα.y<0)
            {
                ταχύτητα.y = 0;
            }
            else
            {
                ταχύτητα.y = ταχύτητα.y + βαρύτητα * Time.deltaTime;
            }
            cc.Move(new Vector3(οριζόντιαΚατεύθυνση * συντελεστήςΤαχύτητας, 0, 0) * Time.deltaTime);
            if (ακούμπησε && Input.GetButtonDown("Jump"))
            {
                ταχύτητα.y = ταχύτητα.y + Mathf.Sqrt(άλμα* -2 *βαρύτητα);
            }
            
            // κατακόρυφη κίνηση
            cc.Move(ταχύτητα * Time.deltaTime);
        }
    }
    
    

    Αν πάμε στο Edit>Project Settings>Input Manager>Jump βλέπουμε το όνομα και το κουμπί.

  19. Δημιουργούμε ένα νέο C# αρχείο (όνομα CameraFollow) μέσα στον φάκελο scripts τον οποίο και εκχωρούμε στο main camera. Στο τέλος πρέπει να σύρω τον hero στο πεδίο στόχος.
    using UnityEngine;
    
    public class CameraFollow : MonoBehaviour
    {
        [SerializeField] private Transform στόχος = null;
        private Vector3 απόσταση;
            void Start()
        {
            απόσταση = transform.position - στόχος.position;
        }
    
        void LateUpdate()
        {
            transform.position = Vector3.Lerp(transform.position, new Vector3(στόχος.position.x, 0, στόχος.position.z) + απόσταση, Time.deltaTime * 3);
        }
    }
    
    
  20. Στον φάκελο Assets δημιουργούμε 2 φακέλους : Meshes, Textures. Στον πρώτο σέρνουμε το Environment objects το οποίο και μετά το σέρνουμε στο Environment στο Hierarchy. Στο Textures σερνουμε Imphenzia Palette -256Gradient.
  21. Στον φάκελο Materials που υπάρχει ήδη, δημιουργούμε νέο material “Common”. Σέρνουμε το Imphenzia Palette -256Gradient στο base map του Common. 
  22. Στο μενού window>Rendering>Lighting. Το  σέρνω δίπλα από τον Inspector. Environment και απο-επιλέγω fog. 
  23. Hierarchy >Environment> Environment. Δεξί κλικ και prefab>unpack completely.
  24.  Σέρνω BlockDirt πάνω μαζί με τα cubes. Σβήνω παλιούς κύβους.  Κρύβω (ξετσεκάρω στον inspector) BlockGrass μέχρι Tree.
  25. Σέρνω BlockGrass στο BlockDirt για να γίνει παιδί του.
  26. Δημιουργούμε φάκελο στο Assets με όνομα prefab.
  27. Στο BlockDirt  > add component> Box collider (center 0,-1,0) (size 2,2,2).
  28. Tο blockDirt το σέρνω στο prefab φάκελο του assets.
  29. Δημιουργούμε 8-9 αντίγραφα του BlockDirt. Μπορούμε να τα περιστρέψουμε κατά 90 (Υ). Βάζουμε και κανένα BlockRock στο οποίο και θα προσθέσουμε boccollider με τις ίδιες ρυθμίσεις. 
  30. Στον φάκελο Hero βάζω το αρχείο HeroMesh. To σέρνω στο Hero . Δεξί κλικ >prefab> unpack.
  31. Σέρνω Hero και HeroAmature στο Hero. Διαγράφω capsule.
  32. Ο ήρωας δεν κοιτάζει μπροστά!
  33. Assets>Hero>HeroMesh και Inspector>Model>Bake Axis Convertion και το τσεκάρουμε.
  34. Hierarchy>Hero(πατέρα) >inspector add component >animator
  35. Assetts >create>animator controller> ονομάζω HeroAnimatorController
  36. Το σέρνω στο Hero (πατέρα) ή  εναλλακτικά στο Hero>Inspector>Animator>Controller
  37. Διπλό κλικ στον HeroAnimatorController   
  38. Επιλέγω HEROMESH > inspector> τσεκάρω loop time για πρώτα 8 (μέχρι herotpose εκτός heroHit) και πατάω apply.video 13:47
  39.  

Εκτός από το unity 2020.2.2f1 θα πρέπει να εγκαταστήσουμε 1) Microsoft Visual Studio Community 2019 και 2) Android Build Support (android sdk και OpenJDK).

  1. Δημιουργούμε ένα νέο project
  2. File>Build Settings>Android 
  3. Hierarchy>3D Object> Plane
  4. Edit>Project Settings> XR Plugin management>install Plugin management>Oculus και στα 2 tabs.
  5. Window>Package Manager 
    1. Layout (πάνω δεξιά)
    2.  Μετακινήσεις: α) ροδέλα β)μεσαίο κουμπί γ)alt αριστερό κουμπί
    3. Επιλέγω αντικείμενο και shift-F εστιάζει σε αυτό.
    4. Hierarchy > Create >cube και στο Transform (x=15, y=1, z=100). Όνομα Ground. Υπάρχει το reset.
    5. Hierarchy > Create >cube και στο Transform (x=0, y=1, z=0). Όνομα Player.
    6. project>create material> όνομα PlayerMat (κόκκινο χρώμα)
    7. Στον Player > Add Component>Physics>Rigibody
    8. Φτιάνω ένα δεύτερο Player και παίζουμε για να δείξουμε την  βαρύτητα. Διαγράφω τον δεύτερο Player και ξετσεκάρω gravity.
    9. Διαλέγω Main Camera> Clear Flags>Solid color
    10. Shift+Space Bar για μεγιστοποίηση Game παραθύρου.
    11. File>Save As > Level01
    12. Επιλέγουμε Player> Add Component>New Script>PlayerMovement.
      using UnityEngine;
      public class PlayerMovement : MonoBehaviour
      {
      public Rigidbody rb;
      void FixedUpdate()
      {
      // Debug.Log("Hello World!");
      // rb.useGravity = false;
      rb.AddForce(0, 0, 2000 * Time.deltaTime); 
      }
      }

      Στον inspector εμφανίζεται μια μεταβλητή rb σε αυτή θα πρέπει να σύρουμε το RigiBody.

    13. Ας προσθέσουμε κώδικα ώστε να κινείται αριστερά ή δεξιά.
      using UnityEngine;
      public class PlayerMovement : MonoBehaviour
      {
          public Rigidbody rb;
          public float forwardForce=2000f;
          public float sidewaysForce = 500f;
          void FixedUpdate()
          {
              // Debug.Log("Hello World!");
              // rb.useGravity = false;
              rb.AddForce(0, 0, forwardForce * Time.deltaTime); 
              if (Input.GetKey("a"))
              {
                  rb.AddForce(-sidewaysForce * Time.deltaTime, 0, 0);
              }
              if (Input.GetKey("d"))
              {
                  rb.AddForce(sidewaysForce * Time.deltaTime, 0,0);
              }
          }
      }
      
    14. Ας γράψουμε κώδικα ώστε η κάμερα να ακολουθεί τον κύβο. Επιλέγουμε MAin Camera>Add component>New Script> FollowPlayer
      using UnityEngine;
      
      public class FollowPlayer : MonoBehaviour
      {
          public Transform player;
          public Vector3 offset;
          // Update is called once per frame
          void Update()
          {
              transform.position = player.position + offset; 
          }
      }
    15. Ας γράψουμε κώδικα για τις συγκρούσεις με άλλα αντικείμενα. Δημιουργούμε νέο κύβο που ονομάζουμε Obstacle, me x=2, tag Obstacle, προσθέτουμε RigidBody, ορίζουμε την μάζα να είναι  2 και του βάζουμε material σκούρο (ObstacleMat). Παρατηρούμε ότι έχει boxcollider.
    16. Επιλέγουμε Player>Add component>New Script> PlayerCollision
      using UnityEngine;
      
      public class PlayerCollision : MonoBehaviour
      {
         
          void OnCollisionEnter()
          {
              // Debug.Log("Κάποιον χτύπησα! "); 
          }
      
      }
      using UnityEngine;
      
      public class PlayerCollision : MonoBehaviour
      {
          public PlayerMovement movement;
          void OnCollisionEnter(Collision collisionInfo)
          {
             if (collisionInfo.collider.tag == "Obstacle")     //για μόνο ένα αντικείμενο: name
             {
                  // Debug.Log("Κάποιον χτύπησα! "); 
                  movement.enabled = false;
              }
          }
      
      }

      Δεν πρέπει να ξεχάσουμε στον Inspector να προσθέσουμε στη μεταβλητή movement το script PlayerMovement από τον inspector.
      ——————–   video 6 ———-

    17. Για να μην περιστρέφεται ο player θα ρυθμίσουμε την ολισθηρότητα μεταξύ player και  ground. Στο project>Create> Physics Material >Slippery> Dynamic friction=0 static friction=0. Σέρνουμε το slippery στο Ground. (video 3)
    18. Επειδή θα χρειαστούμε πολλά εμπόδια (Obstacles) σέρνουμε το Obstacle στα Assets και από εκεί στο scene όσες φορές θέλουμε. Ότι αλλαγές κάνουμε στο Obstacle στα Assets (δημιουργούμε prefab) θα επηρεάσει όλα τα εμπόδια που τοποθετήσαμε. Ενώ οι αλλαγές στα Obstacle που βρίσκονται στο Hierarchy επηρεάζουν μόνο το αντίστοιχο.
    19. Ας μεγαλώσουμε το Ground (scale z=10000 και position=4980). Πατώντας τους άξονες στο scene αλλάζουμε οπτική γωνία.
    20. Επιλέγουμε Ground και στον inspector φτιάχνουμε ένα layer και ας το ονομάσουμε Enviroment. Επιλέγουμε το Ground και στον inspector>Layer>Enviroment. Στην συνέχεια από το μενού Layers πάνω πάνω, δίπλα από το enviroment τσεκάρω το λουκέτο. Τώρα μπορούμε να επιλέξουμε πολλά εμπόδια και να τα μετακινήσουμε όλα μαζί (πράσινο τετράγωνο).
    21. Από το μενού Edit>Grid and snap settings> (2,2,2).
    22. Εύκολα τοποθετούμε πολλά εμπόδια στο Ground με Ctrl+d. Με πατημένο το Ctrl μπορούμε να μετακινήσουμε τα αντικείμενα κατά 2 μονάδες.
    23. Στο script PlayerMovement προσθέτουμε μια παράμετρο στην AddForce και γίνεται:
      rb.AddForce(-sidewaysForce * Time.deltaTime, 0, 0, ForceMode.VelocityChange);

      Επίσης στον Player μπορούμε να ορίσουμε drag=1 και να παίξουμε με ForwardForce και SideWardForce.

    24. Για να θολώσουμε το βάθος: Window>rendering>Lightning settings. Το παράθυρο το βάζουμε μαζί με inspector. Τσεκάρουμε το fog και θέτουμε Density=0.02. Αλλάζουμε και χρώμα αν θέλουμε.
      ——————–   video 7 ———-
    25.  Δημιουργούμε φάκελο στο ASSETS με το όνομα Scripts. Δημιουργούμε φάκελο στο ASSETS με το όνομα Materials.  Τοποθετούμε τα αντίστοιχα αρχεία μέσα σε αυτούς τους φακέλους.
    26. Επειδή μπορεί κάποιες φορές να μην προλάβει ο υπολογιστής να πιάσει μια σύγκρουση, μπορούμε να κάνουμε το εξής: επιλέγουμε Obstacle στα Assets και στο Rigidbody >Collision detection > Continues. Το ίσιο και για τον player.
    27. Μενού:  Edit>Project Settings>Time >Fixed timestamp να γίνει 0.01.
    28. Για το σκορ. Hierarchy > δεξί κλικ>UI>text  . Δεν το βλέπουμε γιαυτό και θα πάμε να επιλέξουμε 2D και διπλό κλικ στο text. To πάμε όπου θέλουμε και   μετά inspector>font size 38 και  horizontal Overflow >overflow. Για να μην είναι σταθερό το κείμενο,  πάμε hierarchy>camvas>inspector>UIScaleMode>scalewithscreensize  και Match>height.
    29. Fonts.   κατεβάζω από ιντερνέτ robot google fonts. Αποσυμπιέζω και ρίχνω στο ASSETS. Δημιουργώ φάκελο Fonts και τα πετάω μέσα. Διαλέγω font και χρώμα.
    30. Ας βάλουμε κώδικα στο Text. Δημιουργούμε AddComponent>new script>Score
      using UnityEngine;
      using UnityEngine.UI;
      public class Score : MonoBehaviour
      {
      public Transform player;
      public Text scoreText;
      // Update is called once per frame
      void Update()
      {
      //Debug.Log(player.position.z);
      scoreText.text = player.position.z.ToString("0");
      }
      }

      —————————   video 8   ——————————–

    31. Hierarchy>Create empty . Το ονομάζουμε GameManager   και inspector> Transforms>reset. Του προσθέτουμε νέο script με όνομα GameManager.
      using UnityEngine;
      
      public class GameManager : MonoBehaviour
      {
          public void EndGame()
          {
              Debug.Log("Game over!");
          }
      }
      
    32. Επιλέγουμε player και ανοίγουμε PlayerCollision
      using UnityEngine;
      
      public class PlayerCollision : MonoBehaviour
      {
          public PlayerMovement movement;
          void OnCollisionEnter(Collision collisionInfo)
          {
              if (collisionInfo.collider.tag == "Obstacle")     //για μόνο ένα αντικείμενο: name
              {
                  // Debug.Log("Κάποιον χτύπησα! "); 
                  movement.enabled = false;
                  FindObjectOfType<GameManager>().EndGame(); //νέα γραμμή
              }
          }
      
      }
    33. Επιλέγουμε player και ανοίγουμε PlayerMovement και προσθέτουμε μια ακόμη if.
               if (rb.position.y < -1f)
              {
                  FindObjectOfType().EndGame();
              }
    34. Αν το τρέξουμε θα εμφανιστεί πολλές φορές το GAME OVER! στην κονσόλα. Οποτε πάμε στο GAMEMANAGER και φροντίζουμε να εκτελεστεί το σψριπτάκι μια φορά μόνο.
        using UnityEngine;
      
      public class GameManager : MonoBehaviour
      {
          bool gameHasEnded = false;
          public void EndGame()
          {
              if (gameHasEnded == false) { 
                  gameHasEnded = true;
              Debug.Log("Game over!");
              }
          }
      }
    35. Όταν τελειώνει το παιχνίδι θέλουμε να ξεκινάει από την αρχή, οπότε στο ίδιο script γράφουμε:
       using UnityEngine;
      using UnityEngine.SceneManagement;
      
      public class GameManager : MonoBehaviour
      {
          bool gameHasEnded = false;
          public void EndGame()
          {
              if (gameHasEnded == false) { 
                  gameHasEnded = true;
                  Debug.Log("Game over!");
                  Restart();
              }
          }
      
          void Restart()
          {
              SceneManager.LoadScene(SceneManager.GetActiveScene().name);   //"Level01"
          }
      
      }
    36. Με το που τελειώνει θέλουμε να καθυστερεί λιγάκι:
       using UnityEngine;
      using UnityEngine.SceneManagement;
      
      public class GameManager : MonoBehaviour
      {
          bool gameHasEnded = false;
          public float restartDelay = 1f;
          public void EndGame()
          {
              if (gameHasEnded == false) { 
                  gameHasEnded = true;
                  Debug.Log("Game over!");
                  Invoke("Restart", restartDelay);
              }
          }
      
          void Restart()
          {
              SceneManager.LoadScene(SceneManager.GetActiveScene().name);   //"Level01"
          }
      
      }
    37. File>Build Settings>add open scenes
      ——————————- video 9 ————————————
    38.  Hierarchy>3d object>cube το ονομάζουμε END και να το βλέπουμε 3D.  Στο Transform πατάμε RESET και SCALE: (15.5.5) και POSITION Y =3. Το πάμε μετά τα εμπόδια. Δεν θέλουμε να φαίνεται όποτε ξετσεκάρουμε το MESH RENDERER. Στο BOX COLLIDER τσεκάρουμε is trigger.
    39. Ο τερματισμός είναι αόρατος. Ας του βάλουμε μία ταμπέλα. Πάμε στον Inspector και στον κύβο επιλέγουμε μία ετικέτα. Πάμε στο GameManager script και προσθέτουμε μια συνάρτηση για την περίπτωση που φτάσουμε στο τέλος.
          public void CompleteLevel()
          {
              Debug.Log("Έφτασες στο τέλος!");
          }
    40. Στο END  προσθέτουμε ένα script (ας το πούμε EndTrigger) και σέρνουμε το GameManager στο ENDTRIGGER (script) στον INSPECTOR.
      using UnityEngine;
      
      public class EndTrigger : MonoBehaviour
      {
          public GameManager gameManager;
          void OnTriggerEnter(Collider other)
          {
              gameManager.CompleteLevel();
          }
      
      }
    41. Δεξί κλικ στο Canvas>UI>panel  (σαν εικόνα που πιάνει όλη την οθόνη).  2D και  διπλό κλικ στο Panel για να πάμε σε αυτό. Στον Inspector >Source Image>none   και color λευκό (alpha τερμα δεξιά) και το όνομα LevelComplete. Hierarchy Δεξί κλικ LevelComplete>UI>text . Το μεγαλώνω , κεντραρω, μεγεθος γραμμάτων 100 και κείμενο LEVEL.
    42. Κάνω duplicate το LEVEL στο Hierarchy και ο ονομάζω Complete. Αλλάζω κείμενο σε Complete, μέγεθος 50,  και το κατεβάζω λίγο πιο κάτω από το LEVEL.  Το ΤΕΧΤ που ειχαμε το μετονομάζουμε σε Score.
    43. Στην αρχή το LevelComplete πρέπει να είναι αόρατο. Γιαυτό και το ξετσεκάρουμε στον inspector.
      xxxxxxxxxxxxxxxxxxxxxxxx
    44. Στο script GameManager προσθέτουμε μια ιδιότητα και συμπληρώνουμε την μέθοδο CompleteLevel
       using UnityEngine;
      using UnityEngine.SceneManagement;
      
      public class GameManager : MonoBehaviour
      {
          bool gameHasEnded = false;
          public float restartDelay = 1f;
          public GameObject completeLevelUI;
      
          public void CompleteLevel()
          {
              //Debug.Log("Έφτασες στο τέλος!");
              completeLevelUI.SetActive(true);
          }
      
          public void EndGame()
          {
              if (gameHasEnded == false) { 
                  gameHasEnded = true;
                  Debug.Log("Game over!");
                  Invoke("Restart", restartDelay);
              }
          }
      
          void Restart()
          {
              SceneManager.LoadScene(SceneManager.GetActiveScene().name);   //"Level01"
          }
      
      }
    45. xxxxxxxxxxxxxxxxxxxxxxxxxx
      xxxxxxxxxxxxxxxxxxxxxxxx
    46. xxxxxxxxxxxxxxxxxxxxxx
      xxxxxxxxxxxxxxxxxxxxxxxx
    47. xxxxxxxxxxxxxxxxxxx
      xxxxxxxxxxxxxxxxxxxxxxxx
    48. xxxxxxxxxxxxxxxxxxxx
      xxxxxxxxxxxxxxxxxxxxxxxx
    49. xxxxxxxxxxxxxxxxxxxxxxx
      xxxxxxxxxxxxxxxxxxxxxxxx
    50. xxxxxxxxxxxxxxxxxxxxx
      xxxxxxxxxxxxxxxxxxxxxxxx