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 - avoid physical rotation of a rigid body - object should remain parallel to the horizont

Prefer to set the checkmarks in the RigidBody Settings at.
Constraints
-> Freeze Rotation Z

Otherwise by code simply reset any calculated rotation to 0 in each frame within the Update function:

float lockRotation = 0f;
gameObject.transform.rotation = Quaternion.Euler(lockRotation, lockRotation, lockRotation);

TIP:


            
Constraints
-> Freeze Position X 
will prevent the character from sliding down physically from a slant platform.

Donnerstag, 9. April 2020

Unity - concept of loading and saving (InputField) data

First experience about how to structure saving and loading of data - e.g. of an InputField

- create class of UserData with the properties to save and load
- create class of SaveSystem which covers LoadData() and SaveData() functions
- create empty GameObject and call it UserGameObject
- create class User and assign it per drag and drop to the UserGameObject
- add public InputField property to the User class to be linked with the InputField control.
- add the callback function of the On End Edit InpuField event to the User class.
- There you are going to put the new value of the InputField to the UserData object and call the SaveData() function to save the UserData object.

Snippets (different conversation)

using System.IO;
using UnityEngine;
using System.Runtime.Serialization.Formatters.Binary;
using System;

public static class SaveSystem
{

    public static void SavePlayer(Player player)
    {
        BinaryFormatter formatter = new BinaryFormatter();
        string path = Application.persistentDataPath + "/player.bin";
        try
        {
            if (File.Exists(path))
                File.Delete(path);
            FileStream fileStream = new FileStream(path, FileMode.Create);
            formatter.Serialize(fileStream, player.UserData);
            fileStream.Close();
            fileStream.Dispose();
            Debug.Log("Okay, saved settings");
        }
        catch (Exception ex)
        {
            Debug.LogError("Unable to save settings! " + ex.Message);
        }
    }

    public static PlayerData LoadPlayer()
    {
        PlayerData player = new PlayerData();
        string path = Application.persistentDataPath + "/player.bin";
        try
        {
            FileStream fileStream = new FileStream(path, FileMode.OpenOrCreate);
            BinaryFormatter formatter = new BinaryFormatter();
            player = (PlayerData)formatter.Deserialize(fileStream);
            fileStream.Close();
            fileStream.Dispose();
            Debug.Log("Okay, Loaded settings.");
        }
        catch (Exception ex)
        {
            Debug.LogError("Unable to load settings! " + ex.Message);
        }
        return player;
    }


}


Data class

[System.Serializable]
public class PlayerData 
{
    public string Partnertoken;
    public string TempPartnerToken;

    public PlayerData()
    {
        this.Partnertoken = "";
        this.TempPartnerToken = "";
    }

}

GameObject class

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

public class Player : MonoBehaviour
{
    public PlayerData UserData;
    public InputField PartnertokenInputField;

    // Start is called before the first frame update
    void Start()
    {
        UserData = new PlayerData();
        UserData = SaveSystem.LoadPlayer();
        if (UserData == null)
            Debug.LogError("Code 202004100825 - UserData object is still null");
        else
            this.PartnertokenInputField.text = UserData.Partnertoken;
    }

    #region InputField Partnertoken

    public void OnPartnertokenChanged(string newToken)
    {
        UserData.TempPartnerToken = newToken;
    }

    public void OnPartnertokenEndEdit(string newToken)
    {
        UserData.TempPartnerToken = newToken;
        this.ShiftTempPartnertoken();
        SaveSystem.SavePlayer(this);
    }

    public void ShiftTempPartnertoken()
    {
        UserData.Partnertoken = UserData.TempPartnerToken;
    }

    #endregion
}