- Unity For Noob
- Module 1
- Module 2: A Shooting Game
- Module 3: AI Behaviour & Navigation
- Module 4: Raycasting, Animation timeline & Animator
- Module 5: Creating a Third Person Game
- Module 6: Saving & Loading using PlayerPrefs
- Module 7: Controller Input, Local Multiplayer & VFX
- Module 8: UI, XML, Localization, Scene Load & Build .exe
- Unity Scripting
- basic platform
- cube -> reset postion -> scale X & Z to 30
- light shadow
- property: ShadowType
- lights by default do not cast shadow, set
ShadowType
- script
void Start() ... void Update() ...
- rigidbody
- physics using Rigidbody & AddForce
m_rigidBody = GetComponent<Rigidbody>(); ... // press space to jump if (Input.GetKeyDown( KeyCode.Space )) { m_rigidBody.AddForce( new Vector3( 0,200,0 ) ); }
- collisions & trigger
- rigidbody will interact with collider
- collider property:
Is Trigger
- if this check-box is actived , you need manually implement the collision detecting.
- OnTriggerEnter & OnTriggerExit
private void OnTriggerEnter(Collider other) ... private void OnTriggerExit(Collider other) ...
- physics using Rigidbody & AddForce
- character controller: handle movement...
Time.deltaTime
= time between updatesUpdate()
has a variable Time.deltaTime- capturing input
Vector3 m_movement ; float m_horizontalInput ; ... m_characterController = GetComponent<CharacterController>(); ... // By default they are mapped to the arrow keys. // The value is in the range -1 to 1 m_horizontalInput = Input.GetAxis("Horizontal"); // input in axis
FixedUpdate()
has a fixed Time.deltaTime- physics update
m_movement.x = m_horizontalInput * m_movementSpeed * Time.deltaTime ; m_characterController.Move(m_movement);
- constant gravity VS. variable intensity gravity(Mario use it)
- Character controller property:
isGrounded
float m_jumpHeight = 0.5f; float m_gravity = 0.045f ; bool m_jump = false;
// Update() if (!jump && Input.GetButtonDown("Jump")) { m_jump = true; }
// FixedUpdate() // Apply Gravity if (!m_charactercontroller.isGrounded ) { /* // constant gravity m_movement.y -= m_gravity; /*/ // variable gravity if (m_movement.y > 0) { m_movement.y -= m_gravity; } else { // falling much faster m_movement.y -= m_gravity*1.5f; } //*/ } else { m_movement.y = 0 ; } // Set Jump Height to movement y if (m_jump) { m_movement.y = m_jumpHeight; m_jump = false; }
- Character controller property:
- playing a sound when collecting a coin
void OnTriggerEnter(Collider other) { if (other.tag == "Player") { if (m_audioSource && m_coinSound) { m_audioSource.PlayOneShot( m_coinSound ) ; } // if you play a sound, and destroy gameobject immediately // the sound won't be played. // GameObject.Destroy( gameObject ); // instead, you can disable the meshrender and collider m_meshRenderer.enable = false; // GetComponent<MeshRenderer>(); m_collider.enable = false; // GetComponent<Collider>(); } }
- another possible approcach is to make other object to play the sound.
- how to play a sound
- add AudioSource component
- an AudioClip
// We want to assign the sound from editor // Making it public is not the best way because other class // can access this variable which may not want to // Use `[SerializeField]` instead [SerializeField] AudioClip m_coinSound;
- sound listener
- To hear the sound, you need at least 1 sound listener, by default, it is attached to your main camera.
- x,z movement
// void Update() m_horizontalInput = Input.GetAxis( "Horizontal" ) ; // x-axis m_verticalInput = Input.GetAxis( "Vertical" ) ; // y-axis m_movementInput = new Vector3( m_horizontalInput, 0, m_verticalInput);
// void FixedUpdate() m_movement = m_movementInput * m_movementSpeed * Time.deltaTime; // we want to make this character facing the way he's moving if (m_movementInput != Vector3.zero) { transform.forward = m_movementInput.normalized; // or sometimes you also need an extra rotation transform.forward = Quaternion.Euler(0,-90,0) * m_movementInput.normalized; }
- character rotation to mouse positon
- Detect mouse position in Screen Space
- Convert player's World Postion to Screen Space
- Subtract player Screen Space position from Mouse Screen Space Position
- Use Mathematics to get Angle from Vector (Atan)
- Apply Angle Rotation to Player
- code
void RotateCharacterTowardsMouseCursor() { Vector3 mousePosInScreenSpace = Input.mousePosition; Vector3 playerPosInScreenSpace = Camera.main.WorldToScreenPoint( transform.position ); Vector3 directionInScreenSpace = mousePosInScreenSpace - playerPosInScreenSpace ; float angle = Mathf.Atan2( directionInScreenSpace.y, directionInScreenSpace.x ) * Mathf.Rad2Deg; // assuming the direction the player is facing right transform.rotation = Quaternion.AngleAxis( -angle, Vector3.up ); }
- spawning bullets
// Gun Logic [SerializeField] GameObject m_bulletPrefab; [SerializeField] Transform m_bulletSpawnPoint; ... Instantiate( m_bulletPrefab, m_BulletSpawnPoint.position, // spawn point's rotation * bullet's rotation m_BulletSpawnPoint.rotation * m_BulletPrefab.transform.rotation )
- bullet speed
- prefab of bullet
^ |
- set velocity, we want the bullets move along its y-axis (up)
m_rigidBody.velocity = transform.up * m_bulletSpeed
- prefab of bullet
- UI text
- creating a text will automatically create a Canvas , and an EventSystem.
- Canvas has Canvas Scaler component, we can set its UI Scale Mode to Scale With Screen Size, so that the text size will change with the screen size.
m_ammoText.text = "Ammo: " + m_ammoCount;
- apply texture to materials
- Create Material
- Click on Albedo
- select texture
- Setup Tiling X and Tiling Y if needed
- drag the material to object
- check with specific tag
private void OnTriggerEnter(Collier other) { if (other.tag == "<YourTarget>") { Destroy( other.gameobject ); Destroy( gameobject ); } }
- access the script attached to the children of a object
GunLogic gunlogic = other.GetComponentInChildren<GunLogic>(); if (gunLogic) { // if player have a weapon ... }
- Equip and UnEquip the gun
- add Rigidbody to gun
- when equipping the gun, uncheck its Use Gravity, when unequipping, activate Use Gravity.
- add Box Collider to gun
- you may use 2 box collider, one for normal collide, one for trigger( bigger ).
- use
GetComponents<T>
insteadGetComponent<T>
to retrieve the type T components all.GetComponent<T>
only return the 1st component T in inspector order.
- add a equipment point to the player
- add Rigidbody to gun
- Navmesh Baking
- in order for a AI to traverse a navigate map, we have to bake a nevmesh.
- in order to bake a nevmesh, simply select object, and mark them as static at top-right corner
- then we goto Window/AI/Navigation, select bake, then you will see a blue surface appear on top of your mesh, and this will be the walkable surface.
- If you want to use navmesh, then we have to setup a navmesh agent in the components of a character.
- Navmesh Max Slope
- you can also add navmesh to slopes, and in the settings , you can define the max angle of slope for navmesh agent to walk on
- NavMesh Agent Component
- Setup Steering Settings
- speed, angular speed, etc...
- Set Destination in code
m_navMeshAgent.SetDestination( m_destination.position) ;
- Stop/Resume NavMeshAgent
- e.g. policy according to distance
float distance = Vector3.Distance( m_destination.position, transform.position ) ; if (distance < 1.5f) { m_navMeshAgent.isStopped = true; m_navMeshAgent.velocity = Vector3.zero; } else { m_navMeshAgent.isStopped = false ; }
- Setup Steering Settings
- Use Gizmos to debug
// draw radius sphere private void OnDrawGizmos() { Gizmos.color = new Color(1,0,0,0.25f) ; Gizmos.DrawSphere(transform.position, m_aggroRadius); }
-
Raycast
- Raycast = Shoot Laster from point A to point B, check for any object hit.
- if it hits an object, that will be stored in a RayCastHit
- We're going to use a Raycast from Camera to Mouse Postion in World Space.
RaycastHit hit; ... Ray ray = Camera.main.ScreenPointToRay( Input.mousePosition ); if (Physics.Raycast(ray, out hit, 100.f) ) { // last param is distance ... }
- We can use mouse and Raycast to set player's destination
- Raycast = Shoot Laster from point A to point B, check for any object hit.
-
Camera Movement
- Move Camerea based on Mouse Position in Screen Space
float m_cameraMovementOffset = 0.15f; ... // Assuming this script is attached to the main camera void UpdateCameraPosition() { if (Input.mousePosition.x >= Screen.width) { // Move camera to the RIGHT transform.position = new Vector3(transform.position.x + m_cameraMovementOffset, transform.position.y, transform.position.z ); } else if (Input.mousePosition.x <= 0.0f) { // Move camera to the LEFT transform.position = new Vector3(transform.position.x - m_cameraMovementOffset, transform.position.y, transform.position.z ); } // PS. it's z-axis, not the vertical y-axis if (Input.mousePosition.y >= Screen.height) { // Move camera UP transform.position = new Vector3(transform.position.x , transform.position.y, transform.position.z + m_cameraMovementOffset); } else if (Input.mousePosition.y <= 0.0f) { // Move camera DOWN transform.position = new Vector3(transform.position.x , transform.position.y, transform.position.z - m_cameraMovementOffset); } }
- CenterCamera on Player when you press Spacebar
if (Input.GetKeyDown(keyCode.Space)) { transform.position = new Vector3( m_player.transform.position.x, transform.position.y, // keep camera y m_player.transform.position.z ); }
- Move Camerea based on Mouse Position in Screen Space
-
Import 3D Model
- how to import a 3D model
- Download .FBX file
- Import in Unity editor
- Example websites:
- https://www.turbosquid.com/Search/3D-Models/free
- https://free3d.com
- Unity Asset Store
- how to rotate the model around its some specify postion
- create an empty object as an anchor
- attach the model to that anchor, adjust its position
- rotate that anchor
- how to import a 3D model
-
Animation Timeline
- select the object you want to animate
- Window / Animation / Animation
- create
- Add Keyframes and change properties such as position, rotation, ...
- click the red record button
- repeat
- set the timeline
- adjust object's positon/rotaton/...
- stop record, and play it
-
After adding a animation, an Animator component will be added to the object.
- Controller: link to the animation controller
- Avatar
- Apply Root Motion
-
Animation Events
- Create A Code function to handle the event
- On animation timeline editor, Right-Click -> Add Animation Event
- In inspector, Link this AnimationEvent to Code Function we created
-
New Animator state, and Animator parameters
- add animator Parameter
IsAttacking
in Animator Editor - add new state Idle
- you can set the state as default state
- you can make a transition to other state
- at the transition arrow, you can set Condition, and use the animator parameter in the condition.
- if you want instantly go to the target state, disable has exit time
- set animator parameter
m_animator = GetComponent<Animator>(); ... m_animator.SetBool( "IsAttacking", true );
- add animator Parameter
-
Shoot Ray forward from Player position to another object
float MELEE_RANGE = 1.5f; ... void CheckAttackRange() { Debug.DrawRay( transform.position, transform.forward * MELEE_RANGE, Color.Red ); RaycastHit hit; Ray ray = new Ray( transform.position, transform.forward ) ; if (Physics.Raycast(ray, out hit, MELEE_RANGE)) { if( hit.collider.gameobject.tag == "Enemy") { if (m_swordLogic) { m_swordLogic.SetAttacking(true); } m_navMeshAgent.isStopped = true; } } }
- Third person camera
- Camera is behind player in the Z axis, a little bit high
- Camera can rotate around player on Y axis and X axis
- Camera looks at Player
- procedurally pricely control, or adjusting camera and attach it onto player
- Camera Movement (procedurally)
- Camera Position = Camera Target(Player) + Camera Offset( y-axis )
// CameraLogic float m_cameraTargetOffset = 1.0f; // high in y-axis float m_distanceZ = 5.0f; // behind the player, in z axis ... void Update() { m_cameraTarget = m_player.transform.positon; m_cameraTarget.y += m_cameraTargetOffset ; } // the reason why we assign new position to the cameras in the LateUpdate // is it gives better effect for camera's , this happens after the rendering is done. // you will see my we split it up when we add rotations private void LateUpdate() { Vector3 cameraOffset = new Vector3(0,0, -m_distanceZ); transform.position = m_cameraTarget + cameraOffset ; }
- Camera Rotation
- Use Mouse Axis to increase Rotation
- Clamp Rotation X
float m_rotateX; float m_rotateY; float m_rotateZ; const float MIN_X = -20.f; const float MAX_X = 20.f; ... if (Input.GetButton("Fire2")) { m_rotationY += Input.GetAxis("Mouse X"); // mouse move left/right m_rotationX -= Input.GetAxis("Mouse Y"); // mouse move up/down // ensure that camera does not go below the ground m_rotationX = Mathf.Clamp( m_rotationX, MIN_X, MIN_Y ); }
- Multiply Rotation with Camera Offset
- Add up with CameraTarget and assign to Camera Position
// all rotations in unity are done in `Quaternion` // angles -> rotation Quaternion cameraRotation = Quaternion.Euler(m_rotationX, m_rotationY, 0); Vector3 cameraOffset = new Vector3(0,0, -m_distanceZ); transform.position = m_cameraTarget + cameraRotation * cameraOffset;
- next, we will want to make the camera face the player because currently the camera only looks forwards, we want the camera to look at the player.
- Camera LookAt
- lucky for us, LookAt functin will face an object towards it's target.
transform.LookAt( m_cameraTarget );
- add this code
// all rotations in unity are done in `Quaternion` // angles -> rotation Quaternion cameraRotation = Quaternion.Euler(m_rotationX, m_rotationY, 0); Vector3 cameraOffset = new Vector3(0,0, -m_distanceZ); transform.position = m_cameraTarget + cameraRotation * cameraOffset; // new transform.LookAt( m_cameraTarget );
- lucky for us, LookAt functin will face an object towards it's target.
- Camera Zooming
- Mouse ScrollWheel Axis can be used to Zoom in / Zoom out
- Value should be clamped to prevent Camera going too far forward / back
// in Update() m_distanceZ -= Input.GetAxis("Mouse ScrollWheel"); m_distanceZ = Mathf.Clamp( m_distanceZ, MIN_Z, MAX_Z ) ; // 2.0f/8.0f
- DOWNLOADING MODEL & ANIMATION ASSETS USING MIXAMO
- mixamo.com
- register Adobe account
- Preview & Select Characters(model)
- download -> Format FBX for unity
- Preview & Select Animations
- download -> same format, 60fps, without skin( we've download the model in T-pose separately, not include skin in animation can reduce the file size )
- for "walking" animation, click "in place"
- ADVANCE: you can edit the clip, set frames , or create multiple clips, each clip could be state, and use them as different purpose.
- download -> same format, 60fps, without skin( we've download the model in T-pose separately, not include skin in animation can reduce the file size )
- Download & Import .FBX to Unity
- IMPORTING & SETTING UP THE CHARACTER
- in practice:
- put the model fbx for unity ( e.g. xx.fbx ) in
Models
folder - put the animation fbxs ( e.g. [email protected] ) in
Animation
folder
- put the model fbx for unity ( e.g. xx.fbx ) in
- you may not need to adjust the model , for animations, if you want it loop, in its Animation tab, check the
Loop Time
&Loop Pose
, and apply- it's also a good practice for animations to use the same Rig. In its Rig tab, change the Avatar definition from
Create From This Model
toCopy From Other Avart
and select the model fbx. - in case the texture doesn't show up, goto
Material
tab, changeLocation
field toUse External Materials(Legacy)
and hit apply & fix.
- it's also a good practice for animations to use the same Rig. In its Rig tab, change the Avatar definition from
- use the model and animations
- drag the model to the scene, this object is not playing any animtion yet because it does not has any animator controller yet .
- create a empty animator controller, and we can drag animation to animator layer
- select this animator controller in object's inspector
- in practice:
- 0509 ANIMATION BLENDTREE
- in order to implement our character's movement animation smoothly, and have the animations transition blend into each other nicely base on the state, we have to use an animation blendtree.
- first we only setup an animation blendtree with movement parameters, we will only setup 2 parameters , HorizontalMovementInput, and VerticalMovementInput. Based on these parameters, we have to setup a 2D Freeform Cartesian blendtree. Based on the X & Y value, we need to play different animation.
- in animation base layer, right click-> Create State-> From New Blend Tree, set the blendtree as default state.
- add 2 new parameters: HorizontalMovementInput & VerticalMovementInput
- select blendtree, go inside, set blendType to
2D Freeform Cartesian
, change the preset parameter to HorizontalMovementInput & VerticalMovementInput. then we can start add motion -> Add motion field
- how to set animator parameter
if (m_animator) { m_animator.SetFloat("HorizontalMovementInput", m_horizontalInput); m_animator.SetFloat("VerticalMovementInput", m_verticalInput); }
- SETTING UP ANIMATION EVENTS IN EDITOR
- setup animation events for footsteps ( 0=left, 1=right) , and play sound effect per step
- select a running animation, select its Animation tab, drag the model into inspector panel
- play the animation, each time when the foot hit the ground,
- click the button left to the timeline within
Events
field- add callback function
PlayFootstepSound
and give it a parameter value
[SerializeField] List<AudioClip> m_footstepSounds = new List<AudioClip>(); ... public void PlayFootstepSound( int footIndex ) { if (m_footstepSounds.Count > 0 && m_audioSource ) { int rand = Random.Range( 0, m_footstepSounds.Count -1 ); m_audioSource.PlayOneShot( m_footstepSounds[rand] ); } }
- add callback function
- ADVANCED - CONTEXTUAL SOUND THEORY & SCENE SETUP.mp4
- we will raycast from the positon of foots towards the groud, and we check what's the tag of the groud , and we will play sound based on this tag.
// you need first serialize left/right foot public void PlayFootstepSound( int footIndex ) { if (footIndex == 0) { RayCastTerrain( m_leftFoot.position) ; } else if (footIndex == 1) { RayCastTerrain( m_rightFoot.position) ; } }
- first we need to change the platform's(ground)
Layer
tag, add a new layerTerrain
,- and then add tags
Grass
,Stone
,Earth
, ... - create multiple terrains, and assign different tag to them
- and then add tags
void RayCastTerrain( Vector3 position) { LayerMask layerMask = LayerMask.GetMask("Terrain") ; Ray ray = new Ray(position, Vector3.down); RaycastHit hit; if (Physics.Raycast(ray, out hit, layerMask )) { string hitTag = hit.collider.gameObject.tag ; if (hitTag = "Grass" ) { PlayRandomSound( m_runGrassSounds) ; } else if (hitTag = "Stone" ) { ... } ... } }
- PLAYER TURNING TOWARDS CAMERA DIRECTION & OUTRO
// Camera_logic.cs public vector3 GetForwardVector() { Quaternion rotation = Quaternion.Euler(0, m_rotationY, 0) ; return rotation * Vector3.forward; } // player_logic.cs private void FixedUpdate() { ... // if player move... if (Mathf.Abs(m_horizontalInput)>0.1f || Mathf.Abs( m_verticalInput )>0.1f ) { transform.forward = m_cameraLogic.GetForwardVector(); } }
- animator parameter : trigger
m_enemyState = EnemyState.StandingUp; m_animator.SetTrigger("PlayerNearby");
- Behavior State Machine
- we want the zombie move after he standing up
- within the animator editor, select the
Zombie Stand up
state, clickAdd Behavior
to add a new scriptpublic class EnemyBehavior: StateMachineBehavior { ... override public void OnStateExit( Animator animator, AnimatorStateInfo stateinfo, int layerIndex ) { EnemyLogic enemyLogic = animator.GetComponent<EnemyLogic>(); if (enemyLogic) { enemyLogic.SetState( EnemyState.Attack ) ; } } }
- enemy weak point: use a collider trigger as a collision box
- Saving
PlayerPrefs.SetFloat( "PlayerPosX", transform.position.x ); ... PlayerPrefs.Save();
- Loading
float playerPosX = PlayerPrefs.GetFloat( "PlayerPosX" ) ;
- a modern thead-safe singleton in Unity
public abstract class Singleton<T> : MonoBehaviour where T : MonoBehaviour { private static readonly Lazy<T> LazyInstance = new Lazy<T>(CreateSingleton); public static T Instance => LazyInstance.Value; private static T CreateSingleton() { var ownerObject = new GameObject($"{typeof(T).Name} (singleton)"); var instance = ownerObject.AddComponent<T>(); DontDestroyOnLoad(ownerObject); return instance; } } //usage: public class GameManager : Singleton<GameManager> { … }
- requires enabling the .NET 4 scripting runtime to access the
Lazy<T>
type.- Open player settings in the Unity Inspector by selecting Edit > Project Settings > Player.
- Under the Configuration heading, click the Scripting Runtime Version drop-down and select .NET 4.x Equivalent. You will have to restart Unity for the change to take effect.
- The advantage of this is that
Lazy<T>
handles the thread-safety out of the box and we don’t have to do any locking ourselves. - Create an empty GameObject, attach our component to it and finally mark it so that it won’t be destroyed when new scene is loaded.
- requires enabling the .NET 4 scripting runtime to access the
-
Project Setting - Input
- Edit > Project Settings ... > Input
- You can duplicated the existing setting, and name your own, e.g. "Horizontal_P1", "Horizontal_P2"
-
INPUT
- GetButton(), GetButtonDown()
- GetAxis() smoothing on the values
- GetAxisRaw() no smoothing
-
SHOOTING A FIREBALL
- Instantiate Object based on hand transform
- run game, pause game, adjust animation to the frame you want to cast a fireball, add fireball spawn postion object to the right-hand, copy its transform data
- stop the game
- re-add fireball spawn postion, paste the transform data we copied
- Initialize Fireball movement at correct time using animation event
Instantiate( m_fireball, m_fireballSpawn.transform.position, transform.rotation );
- Attach Rigidbody and Trigger to detect collision
- Instantiate Object based on hand transform
-
particle Emitter
- right-click > Effects > Particle System
- get fireball effects...
- assetstore.unity.com
- search
explosion
, in Publisher search bar , inputunity
, and then you will find free asset pack
-
procedurally play a particle effect which doesn't PlayOnAwake
// fileball_logic.cs m_collider.enable = false; // disable the collider m_fireball.Stop(true); // stop the current particle effect m_explosion.Play(true); // play a new one, explosion effect should be not play on awake, and disable looping m_rigidBody.velocity = Vector3.zero; // stop fireball moving
-
EVENTS & DELEGATES
- so far, when we want to call a function in a class, we need to access to that object. Now we're going to learn event & delegates.
- we can
send events
with parameters from class X; Subscrib/Listen to events from class Y; Multiple classes can react to events without having access to each other.
// PlayerLogic.cs // Events public delegate void PlayerDeath(int playerNum) ; public static event PlayerDeath OnPlayerDeath ; // Increase the score for the opposing player public void Die() { ... if (OnPlayerDeath != null) { // wheneven the player dies, we will call this event OnPlayerDeath( GetPlayerNum() ); } }
// UIManager.cs void OnEnable() { // subscribe to the event PlayerLogic.OnPlayerDeath += OnUpdateScore; } void OnDisable() { PlayerLogic.OnPlayerDeath -= OnUpdateScore; } void OnUpdateScore(int playerNum) { ... }
-
MainMenu
- Create Scene
- Add BG
- create a plane
- add a material, or just drag a picture onto it as a texture
- Add Canvas
- automatially added when adding text
- Add Text, Button (component)
- button interact: set callback function in Button(Script)'s
On Click()
field - set button status
selected
:m_somebutton.Select();
- button interact: set callback function in Button(Script)'s
- Setup Anchor Alignment
- Setup Hover & Select Color
-
Localization file
- English.xml
<Localization> <Entry> <Key>Title</Key> <Value>UI MainMenu</Value> </Entry> <Entry> <Key>Start</Key> <Value>Start</Value> </Entry> </Localization>
- Chinese.xml
<Localization> <Entry> <Key>Title</Key> <Value>界面主菜单</Value> </Entry> <Entry> <Key>Start</Key> <Value>开始</Value> </Entry> </Localization>
- English.xml
-
Load Localization files from Resources Folder ```csharp public enum Languages { English, Chinese } ...
Dictionary<Languages, TextAsset> m_localizationFiles = new Dictionary<Languages, TextAsset>(); Dictionary<string, string> m_localizationData = new Dictionary<string, string>() ; void SetupLocalizationFiles() { foreach( Languages language in Languages.GetValues( typeof(Languages) )) { string textAsetPath = "Localization/" + language; TextAsset textAsset = (TextAsset) Resources.Load(textAssetPath); if (textAsset) { m_localizationFiles[textAsset.name] = textAsset; } else { Debug.LogError( "TextAsset not found:" + textAssetPath ); } } } ```
- Create Dictionary of Key and Value from XML
- Create Dictionary of Key and Value from XML
- Retrieve Localized string from Data
-
Make an object to exist in multiple scenes
void Start() { // this object won't be destroyed when loading a new scene DontDestoyOnLoad(gameobject); }
-
READING LOCALIZATION DATA
void SetupLocalizationData() { TextAsset textAsset; if(m_localizationFiles.ContainKey(m_currentLanguage)) { textAsset = m_localizationFiles[m_currentLanguage]; } else { Debug.LogError("Couldn't find localization file for: " + m_currentLanguage ); // default textAsset = m_localizationFiles[Languages.English]; } // +Using System.Xml XmlDocument xmlDocument = new XmlDocument(); xmlDocument.LoadXml( textAsset.text ) ; XmlNodeList entryList = xmlDocument.GetElementsByTagName("Entry"); foreach(XmlNode entry in entryList) { if (!m_localizationData.ContainKey( entry.FirstChild.InnerText )) { // add key-value m_localizationData.Add( entry.FirstChild.InnerText , entry.LastChild.InnerText ); } } }
public string GetLocalizedString(string key) { string localizedString = ""; if(!m_localizationData.TryGetValue(key, out localizedString)) { localizedString = "MISS LOC KEY: " + key; } return localizedString; }
-
Load Scene
using UnityEngine.SceneManagement; public void OnStartClicked() { SceneManager.LoadScene( "GameScene" ) ; } public void OnQuitClicked() { Application.Quit(); }
-
activate / de-activate an gameobject
obj.SetActive(true); ... obj.SetActive(false);
-
script architecture
- backeds: mono / IL2CPP
- you should use the .NET Standard 2.0 API Compatibility Level for all new projects
- for code size, cross-platform support, moves more errors to compile time, etc...
- Edit > Project Settings > Player > Other Settings, Api Compatibility Level
-
C# reflection overhead
- all C# reflection (System.Reflection) objects are cached, and do NOT be GC, this behavior will causes unnecessary and potentially GC overhead.
- avoid methods such as Assembly.GetTypes and Type.GetMethods() in your application, which create a lot of C# reflection objects at runtime.
- Instead, you should scan assemblies in the Editor for the required data and serialize and/or codegen it for use at runtime.
-
UnityEngine.Object special behavior
- UnityEngine.Object is a special type of C# object in Unity, because it is linked to a native C++ counterpart object, e.g.
Camera
- For this reason Unity does not currently support the use of the C# WeakReference class with UnityEngine.Objects
- Object.Destroy or Object.DestroyImmediate will destroy the native counter object, the GC will handle its C# managed object.
- If a destroyed UnityEngine.Object is accessed again, Unity recreates the native counterpart object for most types.
- Two exceptions to this recreation behavior are MonoBehaviour and ScriptableObject: Unity never reloads them once they have been destroyed.
- So you can compare a destroyed MonoBehaviour or ScriptableObject against
null
, and will gettrue
.
- UnityEngine.Object is a special type of C# object in Unity, because it is linked to a native C++ counterpart object, e.g.
-
Avoid using async and await
- Async tasks often allocate objects when invoked, which might cause performance issues if you overuse them.
- Additionally, Unity does not automatically stop async tasks that run on managed threads when you exit Play Mode.
- Unity overwrites the default SynchronizationContext with a custom UnitySynchronizationContext and runs all the tasks on the main thread in both Edit and Play modes.
-
Unity Editor uses a C# compiler
scripting runtim version C# compiler C# language version .NET 4.6 equivalent Roslyn C# 8.0 -
Script Serialization
- Serialization is the automatic process of transforming data structures or object states into a format that Unity can store and reconstruct later.
- saving/loading/Inspector windows/Prefabs ...
- To use field serialization:
- Is
public
, or has aSerializeField
attribute - Is not static, const, readonly
- Has a fieldtype that can be serialized.
- Custom non-abstract, non-generic classes non-static with the Serializable attribute
- Custom structs with the Serializable attribute
- Primitive data types (int, float, double, bool, string, etc.)
- Enum types
- Certain Unity built-in types: Vector2, Vector3, Vector4, Rect, Quaternion, Matrix4x4, Color, Color32, LayerMask, AnimationCurve, Gradient, RectOffset, GUIStyle
- Is
- Container field types that can be serialized
- An array of a simple field type that can be serialized
A List<T>
of a simple field type that can be serialized
- Serialization is the automatic process of transforming data structures or object states into a format that Unity can store and reconstruct later.