appwiki:unity

Unity Hub as tool to install old Visual Studio community edition

  • install old visual studio community edition become hard and hard, now you need microsoft account, join their free dev essential, dig through page of outdated link, found only allow you download latest community edition
  • while with unity hub, it auto find correct community edition that works with the unity version
    • Unity 2022 = visual studio community edition 2019
    • Unity 2018 = visual studio community edition 2017
    • Unity 5.3.1 = visual studio community edition 2015

Python for Unity

ref:

Instruction

  • as unity is project based, each project have its own list of copied package (like node.js project, unlike python which use shared modules)
  • so, to enable a feather, it is project based, so you need to enable that for each project manually
  • to enable python for unity for current project, go Window > Package Manager, change package source drop down on top left as “Unity Registery”, search python, if nothing showing up, means it is not official yet.
    • then, go top left + icon, click and choose “Add package from git url”, type name “com.unity.scripting.python”, then install button at bottom, now you get that Python for Unity feather for current project.
  • Tell check python app folder, go to (File > Project Setting) or (Building Setting > Player Setting button below - same as project setting); if you use version “Python for Unity” 4.0.0-pre.1 or above, you should have python auto installed for current project, (it is under ProjectPath\Library\PythonInstall\\lib); also you can see the button “Spawn shell in environ” to open the project's own python console (3.x 64bit)
  • once you open your project python console, to install PySide2 (which designed for py3.x, so we get latest Qt ui lib), type
    import pip
    pip.main(["install","PySide2"])
  • so we are done with basic python setup for the project, you can use same method to install other python modules
  • now, we go the unity built python command box, Window > General > Python Console, check python to query unity objects
    import UnityEngine as unity
    obj_list = unity.Object.FindObjectsOfType(unity.GameObject)
    for each_obj in obj_list:
        print(each_obj.name)

Python and Qt inside Unity

Once above basic setup for python is done for unity, I would like to get Qt working and interaction with unity.
(Note, don't launch python Qt code from Python Console inside Unity, it will show Qt window, but it also block Unity UI interaction, run it from c sharp script)

After a bit research, mainly from video of Indie-Pixel's Python for Unity 3D 2020!!: (https://www.youtube.com/watch?v=3UOlN8FcNbE)

I got it further into make the PySide2 UI interaction working. the main thing is print(). you need to

import UnityEngine as unity
unity.Debug.Log("use this way of print in unity when run from python script")

Steps:

  1. create a empty PyTool.cs for attaching to a GameObject
  2. create a PyToolEditor.cs for making buttons to launch those python script file, and PyToolEditor.cs is actual interface (so called UI file) of PyTool.cs
  3. so depends on how many python scripts to launch, you make how many button inside PyToolEditor.cs
  4. I created 2 .py examples, one without UI, one with Qt UI
  5. I put 2 python script inside Assets's “PythonScript” folder
  6. PythonScript/UnityPythonRun.py is a simple test python script to query info, (but this time, it is from script, code a bit different from PythonConsole inside Window>General>Python Console)
  7. PythonScript/TestQ.py is a simple Qt UI python tool with a Qt button to do some query unity info.

PyTool.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public class PyTool : MonoBehaviour
{
 
}
PyToolEditor.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using UnityEditor.Scripting.Python;
 
[CustomEditor(typeof(PyTool))]
public class PyToolEditor : Editor
{
    PyTool targetTool;
    private void OnEnable()
    {
        targetTool = (PyTool)target;
    }
    public override void OnInspectorGUI()
    {
        if (GUILayout.Button("PyTool", GUILayout.Height(35))) {
            Debug.Log("Hello PyTool");
            string cur_path = Application.dataPath;
            Debug.Log(cur_path);
            string py_file = cur_path + "/PythonScript/UnityPythonRun.py";
            Debug.Log(py_file);
            PythonRunner.RunFile(py_file);
        }
        if (GUILayout.Button("TestQ", GUILayout.Height(35)))
        {
            Debug.Log("Hello TestQ");
            string cur_path = Application.dataPath;
            string py_file = cur_path + "/PythonScript/TestQ.py";
            Debug.Log(py_file);
            PythonRunner.RunFile(py_file);
        }
    }
}
UnityPythonRun.py
import UnityEngine as unity
def u_print(info):
    unity.Debug.Log(info)
 
obj_list = unity.Object.FindObjectsOfType(unity.GameObject)
for each_obj in obj_list:
    print(each_obj.name)
    u_print(each_obj.name)
print('python here')
u_print('python here')
TestQ.py
import UnityEngine as unity
from PySide2 import QtWidgets
class TestUI(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        QtWidgets.QMainWindow.__init__(self, parent)
        # win
        self.setWindowTitle("TestQ")
        # ui part
        main_widget = QtWidgets.QWidget()
        self.setCentralWidget(main_widget)
        main_layout = QtWidgets.QVBoxLayout()
        main_widget.setLayout(main_layout)
 
        test_btn = QtWidgets.QPushButton("List Object")
        main_layout.addWidget(test_btn)
        # connection
        test_btn.clicked.connect(self.list_items)
    # function
    def list_items(self):
        print('hello in python, not shown in unity. - from TestQ')
        self.u_print('hello in unity. - from TestQ')
        obj_list = unity.Object.FindObjectsOfType(unity.GameObject)
        for each_obj in obj_list:
            self.u_print(each_obj.name)
    def u_print(self, info):
        unity.Debug.Log(info)
# fix QApplication singleton issue for run python inside some app
if not QtWidgets.QApplication.instance():
    app = QtWidgets.QApplication([])
else:
    app = QtWidgets.QApplication.instance()
main_ui = TestUI()
main_ui.show()

About Unity

  • a great game dev engine with free option available (previous known as Unity3D)

Structure of Unity

  • Project Template: (tut)
    • pre-configured project setting for common type.
      • 2d: includes: texture import, sprite packer, scene view, lighting and ortho camera
      • 3d: using standard rendering pipeline
      • HD render Pipeline: for highend graphics, support dx11+
      • universal render pipeline: for wide range of publish platform support.
  • update order
    • Fixed update: more frequenty, before physics, fixed timer
    • update: normal update
    • LateUpdate: once per frame, after update

Shortcut

viewport navigation alt-based Mouse navigate, RMB-hold WASD key navigate
focus on selected f
snap obj to view pos ctrl+shift+f (good for cam to cam match)
max view shift+space (like max)

Customize view

asset panel slide <left to list, >right to grid view

C# Script's attribute process timeline

  1. C# script's public attribute default value is the factory default value
  2. C# script's start() attribute change is game start initial setting
  3. once C# script in unity are dragged into Game Object, it is under Unity Game maker's control in Inspector panel, if game maker make a new value there, it is baked into game project for that inspector object (however, if start() has value overwrite, then the game will always be initialized with start() value, game maker still can make change to test it but start() will replace on start)

C# common code for Unity

  • attributes
    Debug.Log("Current Index is " + index);
    public bool CamSwitch = true;
    [Header("Distance in meters")]
    [Tooltip("Tell the truth")]
    public float Distance;
    public float Radius;
    public int Counter;
    public Color NewColor;
    public string NewName;
    if (NewName!=null && NewName!=""){}
    if (char.IsLetter(NewName[0])) {} // first char is letter
    public enum Meal {
        Breakfast,
        Lunch,
        Dinner,
        Supper
    }
    int choice = (int)(MyMeal);
    public Vector3 MyVector;
    public Transform BulletEmiter;
    public Object BulletPrefab;
    public Light MyLight;
  • gameObject common attribute
    gameObject.name = "NewName";
    transform.position = new Vector3(1,2,3);
     
    Camera myCam = GetComponent<Camera>();
    myCam.fieldOfView = CameraFov;
    myCam.enabled = CamSwitch;
     
    Light cur_light = GetComponent<Light>();
    cur_light.color = MyColor;
  • change color (Render can be MeshRenderer or SpriteRenderer)
    gameObject.GetComponent<Renderer>().material.color = Color.red;
    // for safe, better check GetComponent return not null
    if (compo != null){}
  • destroy object
    Destroy(gameObject, life); // delete object after life seconds
  • common update functions
    // opt check require for rigid body
    [RequireComponent(typeof(Rigidbody))]
    void Start(){
        // set initial speed after born
        Rigidbody rb = GetComponent<Rigidbody>();
        rb.velocity = transform.forward * BulletSpeed;
    }
    void FixedUpdate(){
        // before physics
        float vertical = Input.GetAxis("Vertical");
        float horizontal = Input.GetAxis("Horizontal");
        //this is inside FixedUpdate, so we use Time.fixedDeltaTime
        float deltaTime = Time.fixedDeltaTime;
        Quaternion rotationDelta = Quaternion.Euler(0, TurnRate * horizontal * deltaTime, 0);
        Quaternion newRotation = rb.rotation * rotationDelta;
        rb.rotation = newRotation;
     
        Vector3 forward = rb.rotation * Vector3.forward;
        Vector3 moveDelta = forward * Speed * vertical * deltaTime;
        Vector3 newPos = rb.position + moveDelta;
        rb.position = newPos;
    }
    void Update()
    {
        float dist = Time.deltaTime * speed;
        transform.Translate(0, 0, dist);
     
        //if (Input.GetKeyDown(KeyCode.Space))
        if (Input.GetButtonDown("Fire1") && BulletPrefab!=null)
        {
             Instantiate(BulletPrefab, BulletEmiter.position, BulletEmiter.rotation);
        }
        //transform.Rotate(0, Time.deltaTime * speed, 0);
        float turn_amount_h = Time.deltaTime * RotateSpeed * Input.GetAxis("GunHorizontal");
        float turn_amount_v = Time.deltaTime * RotateSpeed * Input.GetAxis("GunVertical");
        transform.Rotate(turn_amount_v, turn_amount_h, 0);
     
        float turn_amount = Time.deltaTime * RotateSpeed * Input.GetAxis("Horizontal");
        float forward_dist = Time.deltaTime * speed * Input.GetAxis("Vertical");
        //transform.Translate(x_dist, 0, y_dist);
        transform.Rotate(0, turn_amount, 0);
        transform.Translate(0, 0, forward_dist);
     
        bool jump_start = Input.GetKeyDown(KeyCode.Space);
        bool jump_end = Input.GetKeyUp(KeyCode.Space);
        bool flying = Input.GetKey(KeyCode.Space);
     
        if (Input.GetKeyDown(KeyCode.W)) { print("Wave~~~~~"); }
        if (Input.GetKeyDown(KeyCode.H) ) {
            if ((Input.GetKey(KeyCode.RightControl) || Input.GetKey(KeyCode.LeftControl)))
            { print("Help NOW!!!"); }
            else { print("Need a help."); }
        }
    }
  • input event
    Input.GetAxis() 
    // "Horizontal" and "Vertical": mapped to joystick, A, W, S, D and the arrow keys
    // "Mouse X" and "Mouse Y" : mapped to the mouse delta.
    // "Fire1", "Fire2" "Fire3" are mapped to Ctrl, Alt, Cmd keys and three mouse or joystick buttons
    // for any kind of movement behaviour use
    Input.GetButton() for action-like events only
    //all the Input calls in the Update Loop.
    KeyCode : all of the key press, mouse and joystick options.
    Input.touches : mobile
    Input.acceleration & Input.deviceOrientation: device orientation in three-dimensional space
     
    -property
    Input.anyKey: any key been holding
    Input.anyKeyDown: the first frame the user hits any key or mouse button
    Input.mousePosition: current mouse position in pixel coordinates
    -method()
    Input.GetAxis(): value of the virtual axis identified by axisName
    Input.GetButton(): button been hold down?
    Input.GetButtonDown(): the frame the user pressed down the virtual button?
    Input.GetButtonUp(): first frame the user releases the virtual button?
    Input.GetMouseButton(): the given mouse button is held down?
    Input.GetMouseButtonDown(): the frame the user pressed the given mouse button?
    Input. GetMouseButtonUp(): the frame the user releases the given mouse button?
    Input.GetTouch(): return touch info
     
    Input.GetJoystickNames(): Retrieves a list of input device names
    Input.GetKey(KeyCode.Space): user holds down the key? (space key example)
    Input.GetKeyDown(): the frame the user starts pressing down the key?
    Input.GetKeyUp(): the frame the user releases the key?
     
     
     
    if (Input.GetMouseButtonDown(0)){} 
    // 0 - left button; 1 - right button; 2 - middle button
  • other event function
    private void OnCollisionEnter(Collision collision)
    {
        if (collision.gameObject.CompareTag("Wall")) {
            print("I hit a wall");
        }
        //Destroy(collision.gameObject); // destroy target when it hit
        Destroy(gameObject);//destroy the bullet when it hit something
    }
  • scene manager
    //require to access SceneManager
    using UnityEngine.SceneManagement;
    SceneManager.LoadScene(SceneName);

Common package

Pipeline Connection

  • Maya to fbx to Unity:
    • copy texture set to unity first, so unity knows where is texture
    • then send fbx, or copy fbx to unity
    • then unity will correctly show texture with fbx
    • Note, for unit, maya 1 unit is 1cm, Unity 1 unit is 1meter, so either you build in maya in real scale to avoid scale issue
      • for one-off case, in unity asset browser, select fbx item, and in right inspector panel, set sale factor to 100 (most case)
      • once for all solution, in Maya, set preference unit to meter, then you have to deal with clipping plane of default cameras
      • another once for all solution, in fbx export, set Adv options > unit > uncheck auto, file units converted to meter
      • best solution, model real world size in Maya, that is cm, so build a cube that is 100 unit as reference. (grid = 120,50)
  • Unity to FBX:
    • install package: FBX Exporter
    • setup FBX export integration (need to redo per project base for the config to work)
      1. in Project Setting, FBX Export tab, choose 3D application to integration (or set manually with browse)
      2. click Install Unity Integration, choose project folder as “install the integration”
      3. wait for 3d app to open in background, and close project setting
      4. (Run Component update for update old unity fbx plugin)
    • export outline object as linked fbx prefab
      1. right click menu on object > Convert to FBX linked prefab
    • in 3DS max,
      1. file > import > Import from Unity, select that fbx file
      2. make your model edit
      3. file > export > export to Unity (support animation), model only with no animation
      4. now, unity window got updated.
      5. for LOD (setup in unity)
        1. ObjectNameLOD0 is the object
        2. ObjectNameLOD1, OBjectNameLOD2 is the lod auto linked object name
        3. put all LOD object in a empty group called “ObjectName_LOD_Group”, unity will auto detect that
        4. if not detect, add LOD Group component in Unity object, drag each LOD component to LOD component slot.
      6. for LOD (setup in 3ds max)
        1. in the group node, go utilites tab, More..>Level of Detail
        2. now you have Level of Detail property in 3ds max group noe, then click “Create New set” to loadd its children LODs
        3. set LOD0 as 50%-100%, LOD1 as 25%-50%, LOD2 as 0-25%;
  • appwiki/unity.txt
  • Last modified: 2023/06/18 17:12
  • by ying