00.VS Code编辑器
VS Code安装插件
- C#
- Unity Code Snippets
- Debugger for Unity
- Unity Tools
VS Code没有代码提示
这里底下给出的回复是保存项目,然后关掉编译器和Unity编辑器,接着删掉项目文件夹下的.csproj和.sln这两个文件(如果你用的IDE是VS的话那么还有一个.vs的隐藏文件夹),然后重开项目,系统就又会重新生成匹配的.csproj和.sln文件。
以后在使用新的Unity版本或者引用新的工具库时,出现编译器不提示的情况就可以照上面的方法解决,不过安全起见,在删之前最好还是备份下.csproj和.sln这两个文件。
- 工程下的**.csproj和.sln文件中记录着当前项目的脚本文件、程序集引用以及一些平台宏、版本信息
- 删除工程中的**.csproj和.sln文件并不会对项目造成影响(除非你在这些文件中加入了你需要的更改)
- 可以通过重启项目来重新生成丢失的**.csproj和.sln文件,或者也可以通过以下方式刷新这些文件:①编辑器中"Editor->Preferences...->External Tools->Regenerate project files"刷新.csproj和.sln文件
01.Input.GetKey
调整立方体的颜色,方法是调整该对象关联的默认材质的color值
我们引用了这个脚本关联的游戏对象,接着引用渲染器 Mesh Renderer ,然后引用关联至这个渲染器的材质,最后引用这种材质的颜色,我将它设为名为Red的快捷方式,它是color类的组成部分
void Update()
{
if (Input.GetKeyDown(KeyCode.R)) {
GetComponent<Renderer>().material.color = Color.red;
}
if (Input.GetKeyDown(KeyCode.G)) {
GetComponent<Renderer>().material.color = Color.green;
}
if (Input.GetKeyDown(KeyCode.B)) {
GetComponent<Renderer>().material.color = Color.blue;
}
}
02.变量与函数
前半部分是声明,后半部分是初始化
在脚本中,我们会用到 start 和 Updata 函数,当这个脚本绑定的对象进入场景时就会调用 Start 函数
我们可以用 Debug.log 获取游戏中某一参数的值
int myInt = 5;
void Start()
{
int myInt = 55;
Debug.Log(myInt * 2);
}
我们可以指定函数的返回值,括号内写函数的传入值,实际上是创建了一个临时变量。
创建一个函数,将传入的值乘2,然后作为返回值返回
调用函数将 myInt 传入,然后再赋给myInt
int myInt = 5;
void Start(){
myInt = MultiplyByTwo(myInt);
Debug.Log(myInt);
}
int MultiplyByTwo(int number) {
int result;
result = number * 2;
return result;
}
03.约定和语法
缩进和注释
using UnityEngine;
using System.Collections;
public class BasicSyntax : MonoBehaviour
{
void Start ()
{
Debug.Log(transform.position.x);
if(transform.position.y <= 5f)
{
Debug.Log ("I'm about to hit the ground!");
}
}
}
04.IF语句
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class IfSatements : MonoBehaviour {
// Start is called before the first frame update
float coffeeTemperature = 85.0f;
float hotLimitTemperature = 70.0f;
float coldLimitTemperature = 40.0f;
// Update is called once per frame
void Update() {
if (Input.GetKeyDown(KeyCode.Space)) {
TemperatureTest();
}
coffeeTemperature -= Time.deltaTime * 5f;
}
void TemperatureTest() {
if (coffeeTemperature > hotLimitTemperature) {
print("Coffee is too hot.");
} else if (coffeeTemperature < coldLimitTemperature) {
print("Coffee is too cold");
} else {
print("Coffee is just right.");
}
}
}
05.循环
while循环
条件为真时执行循环,条件为假时跳出循环
int cupInTheSink = 4;
void Start()
{
while (cupInTheSink>0){
Debug.Log("I have washed a cup!");
cupInTheSink--;
}
}
do-while循环:
无论是否复合条件都先执行一次do中内容,然后再在while中进行判断
bool shouldContine=false;
void Start(){
do{
print("hello world");
}
while (shouldContine == true);
}
for循环:
int numEnemies = 3;
void Start(){
for(int i=0;i<numEnemies;i++){
Debug.Log("Creating enemy number: " + i);
}
}
Foreach循环
string[] strings = new string[3];
strings[0] = "First string";
strings[1] = "Second string";
strings[2] = "Third string";
foreach(string item in strings)
{
print (item);
}
06.作用域和访问修饰符 public、private
alpha、bata、gama在A06_ScopAndAccessModifiers作用域内,而pens、crayons、answer在Example作用域内
公开访问修饰符public可以在游戏引擎Inspector窗口中显示并可以修改值,如果在Start和Awake函数中设置该值,它们会覆盖Inspector中设置的值(输出29),在游戏运行中在Inspector中修改的话则会是Inspector的值(游戏中修改的值不会保存)。游戏中的Inspector中修改 -> Start和Awake函数 -> Inspector中修改 -> 公开访问修饰符public
- 私有访问修饰符private只能在类内使用,在C#中未指定访问修饰符的任意变量默认为private私有访问修饰符
- 公开访问修饰符public可以跨脚本调用
public class ScopeAndAccessModifiers : MonoBehaviour
{
public int alpha = 5;
private int beta = 0;
private int gamma = 5;
private AnotherClass myOtherClass;
void Start ()
{
alpha = 29;
myOtherClass = new AnotherClass();
myOtherClass.FruitMachine(alpha, myOtherClass.apples);
}
void Example (int pens, int crayons)
{
int answer;
answer = pens * crayons * alpha;
Debug.Log(answer);
}
void Update ()
{
Debug.Log("Alpha is set to: " + alpha);
}
}
public class AnotherClass
{
public int apples;
public int bananas;
private int stapler;
private int sellotape;
public void FruitMachine (int a, int b)
{
int answer;
answer = a + b;
Debug.Log("Fruit total: " + answer);
}
private void OfficeSort (int a, int b)
{
int answer;
answer = a + b;
Debug.Log("Office Supplies total: " + answer);
}
}
07.Awake和Start函数
Awake和Start是在加载脚本时自动调用的两个函数,执行顺序Awake() →OnEnable() → start()
- 首先调用Awake,即使还未启用脚本组件
- Start在Awake之后首次更新前调用,前提是已经启用了脚本组件
Awake和Start在一个对象绑定脚本的生命周期内只能调用一次,不能通过禁用和重新启用来重复执行
void Awake()
{
Debug.Log("Awake called.");
}
void Start()
{
Debug.Log("Start called.");
}
08.Update和FixdUpdate
- Update每帧执行一次,电脑运行速度不同调用时间亦会不同,不是按照固定时间调用的
- FixdUpdate调用间隔时间相同(默认0.02s)
void FixedUpdate()
{
Debug.Log("FixedUpdate time: " + Time.deltaTime);
}
void Update()
{
Debug.Log("Update time: " + Time.deltaTime);
}
09.矢量数学(线性代数)
向量可以使用勾股定理计算
向量加法可直接相加

3D比2D多个了Z轴,在Unity中使用左手坐标系,举起左手食指向上指向Y轴,大拇指向右指向X轴,中指向前指向Z轴

Vector A(x,y,z) ; Vector A(x,y,z) (Ax*Bx)+(Ay*By)+(Az*Bz)=Dot Product 点乘
如果点乘等于0则相互垂直
10.启用和禁用组件 enabled
使用GetComponent<>()将这个变量设置为对象关联Light组件
如果抬起空格键,则将myLight的enable设置为非当前值
private Light myLight;
void Start()
{
myLight = GetComponent<Light>();
}
void Update()
{
if (Input.GetKeyUp(KeyCode.Space))
{
myLight.enabled = !myLight.enabled;
}
}
11.激活游戏对象 SetActive
禁用父对象同时也会禁用场景中的子对象,但它仍在其层次结构中保持活跃状态
gameObject.SetActive(false);
要确认某个对象在场景或在层次结构中是否为活跃状态,可以使用Active Self和Active in Hierarchy状态查询
如果禁用父对象,子对象被激活但是它所处的层次结构未被激活
要激活子对象就必须激活父对象
public GameObject myObject;
void Start()
{
Debug.Log("Active Self: " + myObject.activeSelf);
Debug.Log("Active in Hierarchy: " + myObject.activeInHierarchy);
}
12.Translate和Rotate
transform.Translate(new Vector3(0, 0, 1));
乘以Time.deltaTime按照每秒移动多少米的移动速度(方向*速度*时间帧)
public float mvoeSpeed=10f;
void Update()
{
transform.Translate(Vector3.forward*mvoeSpeed*Time.deltaTime);
}
可以使用按键来控制前后
public float mvoeSpeed=10f;
void Update()
{
if(Input.GetKey(KeyCode.UpArrow))
transform.Translate(Vector3.forward*mvoeSpeed*Time.deltaTime);
if(Input.GetKey(KeyCode.DownArrow))
transform.Translate(-Vector3.forward*mvoeSpeed*Time.deltaTime);
}
transform.Rotate与transform.Translate十分相似,Vector3.up围绕哪个轴旋转
public float turnSpeed = 50f;
void Update()
{
if (Input.GetKey(KeyCode.LeftArrow))
transform.Rotate(Vector3.up * -turnSpeed * Time.deltaTime);
if (Input.GetKey(KeyCode.RightArrow))
transform.Rotate(Vector3.up * turnSpeed * Time.deltaTime);
}
(如果物体运动涉及物理应考虑使用Physics函数而非Transform和Rotate)
13.Look At 指向
LookAt可让对象指向时世界另一个Transform,
只要将脚本绑定到摄像机上,同时绑定目标对象,就可以让摄像机对准目标对象
public Transform target;
void Update()
{
transform.LookAt(target);
}
可以再Scene视图顶部切换世界坐标Global和局部坐标Local
14.线性差值
Lerp 函数可以在两个值之间进行线性插值。线性插值会在两个给定值之间找到某个百分比的值。
例如,我们可以在数字 3 和 5 之间按 50% 进行线性插值以得到数字 4。这是因为 4 是 3 和 5 之间距离的 50%。
// 在此示例中,result = 4
float result = Mathf.Lerp (3f, 5f, 0.5f);
起始值3,结束值5,插值为 0.5,表示 50%。如果为 0,则函数将返回“from”值;如果为 1,则函数将返回“to”值。
Color.Lerp(两种给定颜色的某种混合),颜色由代表红色、蓝色、绿色和 Alpha 的 4 个 float 参数表示。
Vector3.Lerp(占两个给定矢量之间的百分比)
Vector3 from = new Vector3 (1f, 2f, 3f);
Vector3 to = new Vector3 (5f, 6f, 7f);
// 此处 result = (4, 5, 6)
Vector3 result = Vector3.Lerp (from, to, 0.75f);
在某些情况下,可使用 Lerp 函数使值随时间平滑。
void Update ()
{
light.intensity = Mathf.Lerp(light.intensity, 8f, 0.5f);
}
如果光的强度从 0 开始,则在第一次更新后,其值将设置为 4。下一帧会将其设置为 6,然后设置为 7,再然后设置为 7.5,依此类推。因此,经过几帧后,光强度将趋向于 8,但随着接近目标,其变化速率将减慢。请注意,这是在若干个帧的过程中发生的。如果我们不希望与帧率有关,则可以使用以下代码,这意味着强度变化将按每秒而不是每帧发生。
void Update ()
{
light.intensity = Mathf.Lerp(light.intensity, 8f, 0.5f * Time.deltaTime);
}
请注意,在对值进行平滑时,通常情况下最好使用 SmoothDamp 函数。仅当您确定想要的效果时,才应使用 Lerp 进行平滑。
15.Destroy 销毁
运行时移除游戏对象或从游戏对象移除组件,也可通过延迟达到相同目的
Destroy(gameObject);
按下空格时,游戏对象就会被销毁
void Update()
{
if(Input.GetKey(KeyCode.Space)){
Destroy(gameObject);
}
}
你可能将这个脚本用于不同用途,因此不应该销毁对象,否则脚本也会同时销毁,所以应该引用另一对象
public GameObject other;
void Update()
{
if(Input.GetKey(KeyCode.Space)){
Destroy(other);
}
}
也可以用Destroy移除组件
Destroy(GetComponent<MeshRenderer>());
可以在后面增加延迟
Destroy(gameObject,3);
16.GetButton和GetKey
GetButton和GetKey二者区别是GetKey会使用KeyCode明确置顶按键名称,
建议使用GetButton指定你自己的控制,输入管理器Input Manager允许指定输入名称,然后给它指定一个键或按钮,菜单栏 -> Project Settings -> Input Manager(旧版输入系统)
- GetButtonDown(1帧后恢复FALSE)
- GetButton(直至抬起)
- GetButtonUp(抬起后触发)
Input.GetButtonDown("")
17.GetAxis
GetAxis返回浮点型小数,这个值介于-1到1之间
- Gravity是归零速度,数值越大归零越快
- Sensitivity是反应速度,数值越大速度越快
- Dead盲区,用于调整误差
获取横轴或者纵轴的值,只需要在代码中添加
Input.GetAxis("Horizontal");
Input.GetAxis("Vertical");
也可使用Input.GetAxis("Raw")来返回整数值
Input.GetAxis("Raw");
18.OnMouseDown 鼠标点击
OnMouseDown可检测碰撞体或GUI文本元素的点击
void OnMouseDown()
{
Debug.Log("Clicked on the Cube!");
}
给被点击的对象添加一个作用力AddForce,
private Rigidbody rb;
private void Awake() {
rb = GetComponent<Rigidbody>();
}
void OnMouseDown()
{
rb.AddForce(transform.forward * 500f);
rb.useGravity = true;
}
19.GetComponent 获取组件
GetComponent 比较占用性能,建议在Awake和Start中调用一次
public class A19_AnotherScript : MonoBehaviour
{
public int playerScore = 9001;
}
public class A19_YetAnotherScript : MonoBehaviour
{
public int numberOfPlayerDeaths = 3;
}
GetComponent会返回调用它的游戏对象中任意指定类型组件的引用
public class A19_UsingOtherComponents : MonoBehaviour
{
public GameObject otherGameObject;
private A19_AnotherScript anotherScript;
private A19_YetAnotherScript yetAnotherScript;
void Awake()
{
anotherScript = GetComponent<A19_AnotherScript>();
yetAnotherScript = otherGameObject.GetComponent<A19_YetAnotherScript>();
}
void Start()
{
Debug.Log("The player's score is "+anotherScript.playerScore);
Debug.Log("The player has died " + yetAnotherScript.numberOfPlayerDeaths + "times");
}
}
GetComponent常用来访问其他脚本,它也可用来访问API未公开的其他组件,
public class A19_UsingOtherComponents : MonoBehaviour
{
private BoxCollider boxCol;
void Awake()
{
boxCol = GetComponent<BoxCollider>();
}
void Start()
{
boxCol.size = new Vector3(3, 3, 3);
}
}
20.DeltaTime
指两次更新或者固定更新函数的间隔时长,作用是让其他增量计算的值变得平滑
*Time.deltaTime即使帧率变化,速度也会保持恒定
public float speed = 8f;
public float countdown = 3.0f;
void Update()
{
countdown -= Time.deltaTime;
if (countdown <= 0.0f)
GetComponent<Light>().enabled = true;
if (Input.GetKey(KeyCode.RightArrow))
transform.position += new Vector3(speed * Time.deltaTime, 0.0f, 0.0f);
}
21.数据类型
两种主要的数据类型:值类型和引用类型
值类型存储某种值;所有引用类型变量都包含存储位置和存储地址
- 如果值类型改变则只会影响特定变量
- 如果引用类型改变,所以包含特定存储地址的变量都会受到影响
Value值类型:
- int
- float
- double
- bool
- char
- Structs
- Vector3
- Quaternion
Reference引用类型:
- Classes
- Transform
- GameObject
值类型包含其自己的数据副本,更改它们只会影响特定变量
Vector3 currentPosition = transform.position;
currentPosition = new Vector3(0, 2, 0);
引用类型更改其中一个,另一个也会改变
Transform tran = transform;
tran.position = new Vector3(0, 2, 0);
22.类
- 构造函数的名称始终都类的名称
- 构造函数一定不会有返回类型,连void都没有
- 一个类可能又多个不同的构造函数,但对象初始化时只会调用其中一个构造函数
public class A22_Inventory : MonoBehaviour
{
// 在Inventory类中有Stuff子类,其中有3个int变量
public class Stuff {
public int projectileA;
public int projectileB;
public int projectileC;
public float fuel;
public Stuff(int prA,int prB,int prC){
projectileA = prA;
projectileB = prB;
projectileC = prC;
}
public Stuff(int prA,float fu)
{
projectileA = prA;
fuel = fu;
}
// 构造函数允许设置默认值
public Stuff(){
projectileA = 1;
projectileB = 1;
projectileC = 1;
}
}
// 创建这个类的一个实例,类名后面的括号表面使用的是构造函数,类或struct可能有多个构造函数,
public Stuff myStuff = new Stuff(50,5,5);
// 根据参数不同匹配不同的构造函数
public Stuff myOtherStuff = new Stuff(50, 1.5f);
void Start()
{
Debug.Log("myStuff.projectileA");
}
}
23.Instantiate 实例
用于克隆游戏对象,常用于克隆prefab(预配置对象),但这样实例的对象只会在原地出现
public Rigidbody projectile;
void Update()
{
if(Input.GetButtonDown("Firel")){
Instantiate(projectile);
}
}
创建一个空对象为实例对象赋值,这样就可以用空对象的位置和旋转
public Rigidbody projectile;
public Transform barrelEnd;
void Update()
{
if (Input.GetButtonDown("Fire1"))
{
Instantiate(projectile, barrelEnd.position, barrelEnd.rotation);
}
}
一般而言instantiate会返回一个名为object的类型,但为了投射并给添加作用力,需要将这个类型强制转换为Rigidbody(as Rigidbody),然后将值存储在Rigidbody变量中(projectileInstance)
Rigidbody projectileInstance;
projectileInstance=Instantiate(projectile, barrelEnd.position, barrelEnd.rotation) as Rigidbody;
projectileInstance.AddForce(barrelEnd.up * 350f);
实例的对象会一直存在在场景中,可以写一个脚本定时销毁
void Start()
{
Destroy (gameObject, 1.5f);
}
24.Arrays 数组
int[] muIntArray=new int[5];
void Start()
{
muIntArray[0] = 12;
muIntArray[1] = 43;
muIntArray[2] = 56;
muIntArray[3] = 47;
muIntArray[4] = 32;
}
没有明确声明长度,长度由大括号中的元素数量决定
int[] myIntArray = { 12, 43, 56, 47, 32 };
如果设置为public则可以在Inspector中看到这个数组并为其赋值
public GameObject[] players;
我们希望players数组储存场景中的所有玩家,FindGameObjectsWithTag返回场景中带有指定标记的
players = GameObject.FindGameObjectsWithTag("Player");
使用循环可以很轻松遍历数组中的值
for(int i=0;i<players.Length;i++){
Debug.Log("PlayerPrefs Number " + i + " is named " + players[i].name);
}
25.Invoke 延时
将函数调用延迟,函数名称、延时时长
只有不包含参数,且返回类型为void的方法才能被Invoke调用
public GameObject target;
void Start()
{
Invoke("SpawObject", 2);
}
void SpawObject(){
Instantiate(target, new Vector3(0, 2, 0), Quaternion.identity);
}
如果想反复调用可以使用InvokeRepeating,第三个参数为调用时间间隔
public class A25_InvokeRepeating : MonoBehaviour
{
public GameObject target;
void Start()
{
InvokeRepeating("SpawObject", 2,1);
}
void SpawObject()
{
// 随机x、z的坐标
float x = Random.Range(-2.0f, 2.0f);
float z = Random.Range(-2.0f, 2.0f);
Instantiate(target, new Vector3(x, 2, z), Quaternion.identity);
}
}
停止调用
CancelInvoke("SpawObject");
26.枚举
枚举可以在类内外创建,也可以创建只包含次枚举的C#脚本,不讲它声明为类,而是将它声明为枚举,然后可以在其他脚本的类中使用这个枚举
enum Direction { North,East,South,West}
North下标为0,East为1,同时可以赋值
enum Direction { North=10,East=11,South=12,West=13}
还可以更改枚举中常量的类型,可以改为任意整数类型
enum Direction : short{ North,East,South,West}
public class A26_CardinalDirection : MonoBehaviour
{
enum Direction { North,East,South,West}
void Start()
{
Direction myDirection;
myDirection = Direction.North;
}
Direction ReverseDirection(Direction dir)
{
if (dir == Direction.North)
dir = Direction.South;
else if (dir == Direction.South)
dir = Direction.North;
else if (dir == Direction.East)
dir = Direction.West;
else if (dir == Direction.West)
dir = Direction.East;
return dir;
}
}
27.Switch
将单一变量与一系列常量进行对比
public class A27_ConversationScript : MonoBehaviour
{
public int intelligence = 5;
void Greet()
{
switch (intelligence)
{
case 5:
print("Why hello there good sir! Let me teach you about Trigonometry!");
break;
case 4:
print("Hello and good day!");
break;
case 3:
print("Whadya want?");
break;
case 2:
print("Grog SMASH!");
break;
case 1:
print("Ulg, glib, Pblblblblb");
break;
default:
print("Incorrect intelligence level.");
break;
}
}
}







Comments | NOTHING