Sonntag, 2. August 2020

Unity Architecture best practice

Add a persistent manager class to the hierarchy. This class should be added to all scenes. Add this class to a scene common prefab. This class will be initiated only once in the complete lifecycle of the application. To achive this persistent behavior add the command "DontDestroyOnLoad(gameObject)" to the Awake() method of the script. The persistent manager is just an empty game object renamed to e.g. PersistentManager with such assigned characteristic script:

using UnityEngine;
using UnityEngine.SceneManagement;

public class PersistentManagerCode : MonoBehaviour
{
    public int Level;
    public delegate void GameEvent();

    public static PersistentManagerCode Instance { get; private set; }
    public static event GameEvent OnResetGame; // listeners can subscribe to this event

    private void Awake()
    {
        if (Instance == null)
        {
            Instance = this; // what creates the first instance? The gameobject which is called persistentGameManager - that is all - discover the static vars on top
            DontDestroyOnLoad(gameObject); // to be persistent, okay - is available in all scenes
            DoResetGame();
        }
        else
            Destroy(gameObject);
    }

    public void DoResetGame()
    {
        if (OnResetGame != null)
            OnResetGame(); // if there are any subscribers, then call the OnResetGame GameEvent delegate function of the subscriber
        this.Level = 0;
    }

    ...
}

to be continued...




Sonntag, 10. Mai 2020

TAG - FindGameObjectWithTag(..)

Assigning a GameObject to a script variable is often done using drag and drop in the inspector.
Another way to load a GameObject instance into a variable is to apply ...
private transform playerTransform
...
playerTransform = GameObject.FindGameObjectWithTag("MyPlayer").transform;

Sonntag, 26. April 2020

Unity - animation prefab creation

The challenge is to take over the transform position of the parent object. Approach:

Creation:

- create an empty gameObject in the hierarchy
- drop a resource like a sprite png onto the empty gameObject in the hierachy
- the gameObject covering the sprite png is now a child object of the empty one
- if not available create a subfolder in the Assets folder named prefabs
- create a subfolder in the new folder prefabs called mySampleAnimationObject
- drop the empty gameobject of the hierarchy in the prefab subfolder just created
- add a animator component to the child of the empty gameobject in the prefab
- you should be asked to create the animation
- record the animation
- create a new c# script and drop it onto the child of the empty gameobject. The gameobject including the animator now!
- add a public function like : public void AnimFinished() {  Destroy(gameObject); }
- add a event to the end of the animation / onto the last key frame add the event.
- assign the AnimFinished() method to the event.
- save

Usage:

- open the main actor script
- add a public GameObject variable to host the created prefab including the upper mentioned stuff
       public GameObject MySampleAnimPrefab;
- drop this prefab within the inspector on this new public member of the script component
- now is the prefab in general linked to the script of the main actor;
- the main actor script can now fire and forget as many prefab animation objects as desired
- this will be done using for example:
        Instantiate(MySampleAnimPrefab, collision.gameObject.transform.position, Quaternion.identity); // the second arg is any Vector3 type position

Montag, 13. April 2020

Unity - how to collect item

The item to collect is a prefab.
This prefab got a collider
The collider of the prefab is checked as 'isTrigger'!
The gameobject of this collectable item is tagged as 'Coin'.

The prefab of the player actor checks for collisions in its script:

private void OnTriggerEnter2D(Collider collision)
{
       if (collision.gameObject.CompareTag("Coin"))
       {
                Destroy(collision.gameObject);
                MyPersistentManager.Instance.DoCollectCoinAction(); // todo

        }
}

Sonntag, 12. April 2020

Unity - modify Parameters of animator transactions via scripting

There are animations.
Each animation got an animator.
Drop as many other animation animator to the current animator optionaly.
Connect each animator element through new transactions. (right click on the element).
Add a parameter to the transaction of type float, bool etc.
Add a condition to the transaction to be true or false.
The condition is the result of a comparison of such Parameters.

How to feed the Parameters from code in order to let the animator decide which condition is true and finally to change the desired animation?

Make a public class member of type animator:

public Animator animator;

Feed this public Animator variable in the UI of  Unity by draging and dropping the animator of the same gameObject over there. Now is the specific gameObject animator linked to the gameObject public script variable. 

Set the Parameter of the Animator:

animator.SetFloat("MyTransactionFloatParameter", 0.01f); // for instance
animator.SetBool("MyTransactionBoolPara",true);

Unity - quickest way to add colliders to all tiles within the tile map.

Select the TileMap in the hierarchy and add a "TileMap Collider 2D" property.

Unity - 2d movement and jumping and character facing to left, right direction (Variant 20200412)

There is a Player GameObject with a rigid body property and a 2d collider property.
In the hierarchy is a another gameobject as a child of Player GameObject. This chlid got only a 2d collider property.
The collider of the parent player game object checks the normal bounderies.
The collider of the child player object is only used to detect if the parent game object is grounded or not. It is not grounded, when it currently jumps.

(What is the trick to let the character face to the right (default) and then to the left by the movement to the left of course: Whenever the character changes the movement to the left, flip the value of gameObject.transform.localScale.x from +1 to -1 and the other way around by a multiplying with just * -1;)
Snippets (1 script of the parent and another one for this child object)

Important is only to allow jumping when the character is currently not jumping. That is because the collider detecting if the character is grounded or not is a few pixel large and it takes a couple of frames to leave this zone. If you are ignoring to check the flag if it is currently jumping you will get a inconstince result with various jump heights!

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerCode : MonoBehaviour
{
    public float PlayerSpeed = 5f;
    public float PlayerJumpForce = 0.6f;
    public bool isGrounded = false;

    private bool IsJumping = false;
    private bool isIdle = true;
    private bool isMoving = false;
    private float lockRotation = 0f;
    private bool isFacingRight = false;

    // Update is called once per frame
    void Update()
    {
        move();
        jump();
        gameObject.transform.rotation = Quaternion.Euler(lockRotation, lockRotation, lockRotation);
         
    }

    private void move()
    {
        // @see https://docs.unity3d.com/ScriptReference/Input.GetAxis.html
        // @see https://www.youtube.com/watch?v=L6Q6VHueWnU
        float horizontalMovementRate = Input.GetAxis("Horizontal");
        if (horizontalMovementRate != 0)
        {
            isMoving = true;
            Vector3 movement = new Vector3(horizontalMovementRate, 0f, 0f);
            transform.position += movement * Time.deltaTime * PlayerSpeed;
            if (horizontalMovementRate>0 & !isFacingRight ||       horizontalMovementRate<0 & isFacingRight)
            {
                isFacingRight = !isFacingRight;
                Vector3 newScale = transform.localScale;
                newScale.x = newScale.x * -1;
                transform.localScale = newScale;

            }
        }
        else
            isMoving = false;
    }


    void jump()
    {
        if (Input.GetButton("Jump") & isGrounded & !IsJumping)
        {
            gameObject.GetComponent<Rigidbody2D>().AddForce(new Vector2(0f, PlayerJumpForce), ForceMode2D.Impulse);
            IsJumping = true;
        }
    }
}

child object script:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GroundedCode : MonoBehaviour
{
    GameObject Player;

    // Start is called before the first frame update
    void Start()
    {
        Player = gameObject.transform.parent.gameObject;
    }

    // Update is called once per frame
    void Update()
    {
        
    }

    private void OnCollisionEnter2D(Collision2D collision)
    {
        if (collision.collider.tag == "Ground")
        {
            Player.GetComponent<PlayerCode>().isGrounded = true;
Player.GetComponent<PlayerCode>().IsJumping = false;
        }
    }

    private void OnCollisionExit2D(Collision2D collision)
    {
        if (collision.collider.tag == "Ground")
        {
            Player.GetComponent<PlayerCode>().isGrounded = false;
        }
    }

}





Unity Architecture best practice

Add a persistent manager class to the hierarchy. This class should be added to all scenes. Add this class to a scene common prefab. This cla...