组件
RigidBody
刚体,会给物体增加重力
spring joint:
弹簧关节,可以连接两个刚体,使得两个物体能够像弹簧一样运动。
Box Collision
增加碰撞检测
图片渲染器
Sprite精灵,游戏开发中指一张图片
Sprite Render 图片渲染器,是用来渲染图片的组件
1 2 3 4 5 6 7 8 9 10 11 void createMonster ( ) { float x = Random.Range(-2 , 2 ); float y = 5 ; GameObject monster = Instantiate(MonsterPrefab); monster.transform.position = new Vector3(x, y, 0 ); int index = Random.Range(0 , images.Length); SpriteRender render = monster.GetComponent<SpriteRender>(); render.sprite = this .images[index] }
所以图片对象其实是, Empty Gameobject+Sprite Render组件构成的
注:UI下的Image不是Sprite
API:
Translate()
根据 translation 的方向和距离移动变换。如果 relativeTo 被省略或设置为 Space.Self ,则会相对于变换的本地轴来应用该移动。(在场景视图中选择对象时显示的 X、Y 和 Z 轴。) 如果 relativeTo 为 Space.World ,则相对于世界坐标系应用该移动。
使用 Transform.Rotate 以各种方式旋转 GameObjects。通常以欧拉角而不是四元数提供旋转。
获得键鼠操作
获取鼠标:
1 2 3 4 Input.GetMouseButton(int ) int = 0 时,获取鼠标左键int = 1 时,获取鼠标右键int = 2 时,获取鼠标中键
获取方向键盘:
1 2 3 4 Input.Getkey(KeyCode.UPArrow) 上键 Input.GetKey(KeyCode.DownArrow) 下键 Input.GetKey(KeyCode.LeftArrow) 左键 Input.GetKey(KeyCode.RightArrow) 右键
获得对象上的组件
每个组件上都有transform组件,因此在scripts脚本中可以通过transform.position和transform.rotation来获得位置和朝向。
获得对象下的刚体组件: Rigidbody rd = bullet.GetComponent<Rigidbody>();
镜头跟随——移动摄像头位置
Camera.main.transform.position = Vector3.Lerp( Camera.main.transform.position, new Vector3(Mathf.Clamp(posX, 0, 15), Camera.main.transform.position.y, Camera.main.transform.position.y,), smooth* Time.deltaTime)
注:被挂载的脚本也算个组件,其名称为脚本名称,如下GetComponent<Bird>的Bird为挂载的Bird.cs脚本
1 collision.transform.GetComponent<Bird>().Hurt();
获得脚本挂载的对象以及上级对象
1 2 3 4 5 6 7 8 9 transform transform.parent transform.root transform.parent.getComponent<Text>()
有获得父对象就有获得子对象
1 2 3 for (int i = 0 ; i < transform.childCount; i ++ ){ transform.GetChild(i).GetComponent<Collider2D>().enabled = false ; }
脚本获得对象:
public Text TimeText将对象通过public暴露,然后手动拖拽指定
private Text TimeText指定为private,然后Aware()中TimeText = GameObject.Find("TimeText").GetComponent<Text>();
1.无法查找隐藏对象
隐藏对象包括查找路径的任何一个父节点隐藏(active=false)
▲:解决方案,可以先让它设置为active=true,然后在start中找到后设置为active=false,再在用的时候设置为true
2.如果查找不在最上层,建议合理使用路径查找,路径查找是把双刃剑
**优点1:**解决查找中可能出现的重名问题。
**优点2:**如果有完全的路径,减少查找范围,减少查找时间。
注:与GameObject.Find相似的api还有GameObject.FindWithTag
Transform.Find
1.可以查找隐藏对象
2.支持路径查找
3.查找隐藏对象的前提是transform所在的根节点必须可见,即active=true
实际开发中会将功能预制体放到一个可见的 GameObject目录下,将这个GameObject目录作为查找根节点,下面的所有对象(隐藏、非隐藏)都可以查找到。
SetActive和enable区别
相同点:
gameObject.SetActive函数控制的是上图复选框1是否勾选,不勾选,那么隐藏,勾选即显示;
enabled属性控制的是复选框2是否勾选,显隐规则同上;
不同点:
SetActive是针对元素对象gameObject的;enabled是针对gameObject下的某个component的
如果UI的复选框1默认不勾选, 无论复选框2是否勾选, 那么当UI显示时, MyImage.cs的所有函数都不会执行, 这就说明没有加载此脚本到内存;
如果UI的复选框1默认勾选,复选框2默认不勾选,那么当UI显示时, MyImage.cs的Awake函数会执行,其他函数均不会执行,这就说明此脚本会加载到内存中, 除了执行Awake函数外,其他函数均不执行.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public List<Bird> birds;birds[i].enabled = true ; birds[i].sp.enabled = true ; public GameObject win;public GameObject lose;lose.SetActive(true ); win.SetActive(true ); button.SetActive(false );
本地持久化保存与读取的类
PlayerPrefs: 工作原理非常简单,以键值对的形式将数据保存在文件中,然后程序可以根据这个名称取出上次保存的数值。
Playerprefs静态方法
SetFloat(),SetInt(),SetString()` 写入数据
GetFloat(),GetInt(),GetString() 读取数据
DeleteKey(),DeleteAll() 删除数据
HasKey("SS") 检查数据,是否有该键
Save()
创建和销毁prefab对象
1 2 3 4 5 6 7 8 9 10 11 12 13 GameObject bullet = Instantiate(myPrefab) GameObject go = Instantiate(score, transform.position + new Vector3(0 ,0.5 f,0 ), Quaternion.identity); bullet.transform.position = transform.position + new Vector3(0 , 1 f, 0 ) transform.Translate(0 , step, 0 , Space.Self) Vector3 sp = Camera.main.WorldToScreen(transform.position) if (sp.y > Screen.height){ Destroy(this .gameObject, 1.5 f) }
场景切换
Unity游戏开发中,单个Scene解决所有问题似乎不可能,那么多个Scene之间的切换是必然存在。如果仅仅是切换,似乎什么都好说,但是在场景比较大的时候不想让玩家等待加载或者说场景与场景之间想通过一些画面、动画表现出一些让玩家期待的东西,大家就要去认真考虑。这篇文章主要介绍两种增加切换中如何播放画面或者动画等等,提高玩家的浸入感,当然你也可以做成无缝的场景 。
1 2 3 4 5 6 7 8 9 10 11 12 // 异步切换场景 // AsyncOperation async = Application.LoadLevelAsync("MyBigLevel"); // 同步切换 // Application.LoadLevel("Middle"); using UnityEngine.SceneManagement; public void Retry() { Time.timeScale = 1; // 大多数情况下使用的方法 UnityEngine.SceneManagement.SceneManager.LoadScene(2); }
重玩 功能:可以通过切换到当前场景来实现,由于切换会把切之前的元素销毁掉,并重新建新的元素,因此相当于重玩
游戏暂停
设置Time.timeScale = 0 将会暂停所有和帧率有关的事情 。这些主要是指所有的物理事件和依赖时间的函数、刚体力和速度等,而且FixedUpdate会受到影响,会被暂停(不是Update),即timeScale =0 时将不会调用FixedUpdate函数了。
(1)Time.timeScale = 0可以暂停游戏,Time.timeScale = 1恢复正常,但这是作用于整个游戏的设置,不单单是当前场景,记得在需要的时候重置回Time.timeScale = 1。当然也可以使用Time.timeScale来做游戏的1倍、2倍整体加速。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 private void Awake ( ) { anim = GetComponent<Animator>(); } public void Retry ( ) { Time.timeScale = 1 ; UnityEngine.SceneManagement.SceneManager.LoadScene(2 ); } public void Pause ( ) { anim.SetBool("isPause" , true ); button.SetActive(false ); if (GameManager._instance.birds.Count > 0 ) { if (GameManager._instance.birds[0 ].isReleased == false ) { GameManager._instance.birds[0 ].canMove = false ; } } } public void Resume ( ) { Time.timeScale = 1 ; anim.SetBool("isPause" , false ); if (GameManager._instance.birds.Count > 0 ) { if (GameManager._instance.birds[0 ].isReleased == false ) { GameManager._instance.birds[0 ].canMove = true ; } } } public void Home ( ) { Time.timeScale = 1 ; UnityEngine.SceneManagement.SceneManager.LoadScene(1 ); }
碰撞检测
碰撞体的编辑
点εEdit collider进行编辑…,规定可碰撞的范围和形状
常见的形状:
方形 Box Collider2D
圆形 Circle collider2D
不规则边缘 Edge Collider2D
胶囊形状 Capsule Collider2D
带有collider组件的对象脚本,将组件的IsTrigger属性勾选上
1 2 3 4 5 6 7 8 9 10 11 12 13 14 void OnTriggerEnter (Collider collider ) { Debug.Log("开始接触" ); } void OnTriggerExit (Collider collider ) { Debug.Log("接触结束" ); } void OnTriggerStay (Collider collider ) { Debug.Log("接触持续中" ); }
带有collider组件的对象脚本,发生碰撞时会执行如下钩子函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 void OnCollisionEnter (Collision collision ) { Destroy(this .gameObject); } void OnCollisionExit (Collision collision ) { } void OnCollisionStay (Collision collision ) { }
两者的区别在于,将对象collider组件的isTriiger属性勾上后,游戏物体发生接触的时候就不会有碰撞的效果了,而是会直接穿过去,即碰撞逻辑需要我们自己手写了。
【值得注意的是,触发器回调的这三个方法的参数都是Collider类型,表示的就是被碰撞的游戏物体的触发器组件对象。】
1 2 3 4 5 6 7 var name = collision.collider.name;var tag = collision.collider.tag;collision.transform.GetComponent<Bird>().Hurt();
使用这种方式加载资源,首先需要下Asset目录下创建一个名为Resources的文件夹,这个命名是U3D规定的方式,然后把资源文件放进去,Cube放在Resource中的Prebs中,而Sphere放在Resources跟目录下
1 2 3 4 5 private string cubePath = "Prebs/MyCubePreb" ;private string spherePath = "MySpherePreb" ;Object cubePreb = Resources.Load(cubePath, typeof (GameObject)); Object spherePreb = Resources.Load(spherePath, typeof (GameObject));
DontDestroyOnLoad
新场景的负载会破坏所有当前场景对象。调用Object.DontDestroyOnLoad在级别加载期间保存对象。如果目标对象是组件或游戏对象,Unity还将保留Transform的所有子对象。对象.DontDestroyOnLoad不返回值。使用typeof操作符更改参数类型。
建议把需要DontDestroyOnLoad的游戏对象放到一个在游戏逻辑中不会返回的一个场景,比如说放到登录时加载的那个场景,,,或者代码使用一个静态变量做标志,(static bool isHave )是否DontDestroyOnLoad过,若DontDestroyOnLoad就将其赋值为True,
想要找到这个游戏物体的话,搜索Find 是可以找到的,或者使用标签的形式FindGameObjectWithTag
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 public class Main : MonoBehaviour { public GameObject ClientSystem; public void Start ( ) { DontDestroyOnLoad(ClientSystem); } void Update ( ) { } } using System.Collections;using System.Collections.Generic;using UnityEngine;using System.Net;using System.Net.Sockets;using System.Text;using UnityEngine.UI;using UnityEngine.SceneManagement;using System.Threading;public class Client : MonoBehaviour { public GameObject LoadCavas; public GameObject Ball; public GameObject StartBackGround; public GameObject InputText; float rota = 1.6 f; bool isBallRota = false ; string HeartBeatMsg = "Hello" ; string otherNameMsg = "otherName" ; string HookMsg = "Hook" ; string ExitMsg = "exit" ; string SendMsg = "" ; string RecvMsg = "" ; const string ip = "127.0.0.1" ; const int port = 9999 ; IPEndPoint ipe = new IPEndPoint(IPAddress.Parse(ip), port); Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); private void Start ( ) { LoadCavas.SetActive(false ); } private void Update ( ) { if (isBallRota && Ball != null ) { Ball.transform.Rotate(Vector3.forward, rota); } if (RecvMsg != "" ) { MsgHandle(); } SendMsg = GetSendMsg(); } void LoadAnimate (bool Start ) { if (Start) { LoadCavas.SetActive(true ); isBallRota = true ; StartBackGround.GetComponent<Image>().sprite = null ; StartBackGround.GetComponent<Image>().color = new Color(0 , 0 , 0 , 0 ); InputText.GetComponent<InputField>().text = "Matching...." ; } else { if (LoadCavas != null ) { LoadCavas.SetActive(false ); isBallRota = false ; StartBackGround.GetComponent<Image>().sprite = Resources.Load<Sprite>("Button" ); StartBackGround.GetComponent<Image>().color = Color.white; InputText.GetComponent<InputField>().text = PlayerPrefs.GetString("UserName" ); } } } public void Link ( ) { LoadAnimate(true ); socket.Connect(ipe); name = PlayerPrefs.GetString("UserName" ); byte [] nameB = Encoding.ASCII.GetBytes(name); socket.Send(nameB); string recvStr = "" ; byte [] recvBytes = new byte [1024 ]; int bytes; bytes = socket.Receive(recvBytes, recvBytes.Length, 0 ); recvStr += Encoding.ASCII.GetString(recvBytes, 0 , bytes); Debug.Log("client get message" + recvStr); Thread thread = new Thread(Communicate); thread.Start(); } private void OnApplicationQuit ( ) { if (socket.Connected) { socket.Send(Encoding.ASCII.GetBytes(ExitMsg)); byte [] recvBytes = new byte [1024 ]; int bytes; bytes = socket.Receive(recvBytes, recvBytes.Length, 0 ); socket.Close(); } } private void Communicate ( ) { while (socket.Connected) { byte [] heartBeatStrB = Encoding.ASCII.GetBytes(SendMsg); socket.Send(heartBeatStrB); string recvStr = "" ; byte [] recvBytes = new byte [1024 ]; int bytes; bytes = socket.Receive(recvBytes, recvBytes.Length, 0 ); recvStr += Encoding.ASCII.GetString(recvBytes, 0 , bytes); RecvMsg = recvStr; Thread.Sleep(100 ); } } void MsgHandle ( ) { string msg = RecvMsg; RecvMsg = "" ; if (msg.StartsWith(otherNameMsg)) { LoadAnimate(false ); Variable.OtherName = msg.Remove(otherNameMsg.Length); } else if (msg == HookMsg) { Variable.isOtherHookDown = true ; } else if (msg == "Left" ) { isBallRota = false ; Variable.isSelfLeft = true ; SceneManager.LoadScene("Game" ); } else if (msg == "Right" ) { isBallRota = false ; Variable.isSelfLeft = false ; SceneManager.LoadScene("Game" ); } else if (msg == "exit" ) { SceneManager.LoadScene("Main" ); LoadAnimate(false ); InputText.GetComponent<InputField>().text = "Mathing Failed QAQ" ; } } string GetSendMsg ( ) { string msg = HeartBeatMsg; if (Variable.isExit) { msg = ExitMsg; Variable.isExit = false ; } else { if (Variable.isSelfHookDown) { msg = HookMsg; Variable.isSelfHookDown = false ; } } return msg; } }
问题:
渲染顺序:
简单总结一下,
决定Sprite render的渲染关系的层级顺序是:
Camera: 不同的Camera的Depth
sorting layer: 相同Camera下的不同SortingLayer
sortingorder: 相同SortingLayer下的不同Z轴/Order in Layer
改变控件之间的层级关系
(1)同一canvas下:
改变控件transform的SiblingIndex,
transform.GetSiblingIndex();
transform.SetSiblingIndex(int index); //index值越大,越后渲染,层级越大,越显示在前面
(2)不同Canvas下:
设置Canvas下的Sort Order //Sort Order值越大,越后渲染,层级越大,越显示在前面
学习参考视频:
免费的素材网站