C#与unity学习
unity中产创建c#文件
在unity中新建project,为方便管理可以在assets文件内创建scripts文件夹,将所有的c#文件集中在此文件夹中管理。
在vsstudio中创建c#
前两种都是带窗口的c#应用程序,但在实际中感觉不出来,后两仅仅指示控制台程序。
c#的四个安全级别
public:对任何脚本开放
private:变量尽在创建它们的类中可用。没有使用访问修饰符声明的任何变量默认都是私有的。
protected:变量在所属类和派生类中可以访问。
internal:仅在当前程序集中可用。
打开wpf应用(全称windows platform formdation)
c#源文件中要当心类名和文件名不一致问题
初始类与命名空间与类库
namespace:
以树形结构的形式管理各个数据结构和类以及其他命名空间
class:
与c++中的十分相似。
在c#中所有的函数引用或者类的引用都是调用命名空间来是实现。
例如:
using System
using System.io
仅使用Sytem的命名空间时,不能在没用生命IO命名空间的情况下直接访问System内的io命名空间内的类
path() //这是不行的。
io.ptah()//这样就可以了
类库
程序想要使用类名和命名空间必须要引用相应的类库,类库是类名和命名空间能够被程序调用的基础,而类库一般是在dll文件中实现的。在程序代码中不需要显示调用类库的代码(像调用其他命名空间一样),一般vsstdio会自动识别相应的类库,且可以通过每个解决方案内的reference查看。
如何添加自己需要的类库:
1‘)DLL引用:
黑引用,无源代码,一般实在引用别人编译好了的dll文件。
在vsstdio中使用:
第一步:在solut栏内右击reference
第二部:点击browse,进入浏览文件窗口。
第三步:选择你想要引用的dll类文件
2)项目引用:
白盒子引用,源代码
在一个solution中同时船舰两个project,其中一个是类库,另一个则是需要引用该类库的项目,在需要应用类库的项目里的reference中按如下添加dll。
类与对象
对象是类的实例化
在c#中使用new操作符来为类创建实例
class forrm{
static void Main(string[] args){
forrm myform = new forrm;
}
}
c#类中的三大成员:属性、方法以及事件
事件是类或者对象痛着其他类或对象的机制,为c#独有
c#的数据类型
bool
sbyte :有符号为八位整数
byte:无符号为八位整数
ushot;两个字节
uint:四个字节
char:16位字符类型,与其他语言有所区别
float:32位浮点数
double:双精度浮点数 在申明浮点数的时候默认是双精度的,要申明单精度需要在每个常量后面加f.
枚举类型和结构体:
枚举
enum 枚举名{枚举值1,枚举值2....},在语法上等同于结构体
enum euipment = {bodyArmor,Helmet}
euipment myquip = equipment.bodyAemor;
euipment myquip1 = (equipment)1;
枚举类型和整数类型可以相互转换。
结构体:
Struct 结构体名{
属性值;
属性值;
}
与c++中不同的地方是调用构造函数的时候必须要用到new。其他情况类似,比如对类的实例话化。
类里面的独有的属性访问器SET和GET
C#中的get和set访问器可用来获取和设置类中字段(即属性)的值,通过get和set访问器提供访问接口,从而可以避免对字段的直接访问造成的不安全性。
方法1
public 数据类型 属性名
{
get
{
return 字段名;
}
set
{
字段名 = value;
}
}
get{},get访问器用于获取类中字段的值,需要在get语句中使用return返回一个和字段类型相匹配的值。若希望该属性为只写属性,则在属性的定义中省略get()访问器。
set{},set访问器用于设置类中字段的值,将程序中传给属性的值赋值给字段。若希望该属性问只读属性,则在属性的定义中省略set()访问器。方法2使用get和set访问器:
方法2:
可以再如上代码重看到方法1的用法代码大量重复,因此C#中将属性的设置也可以简化为:
public 数据类型 属性名{ get; set; }
使用方法2定义属性时无需先定义字段,响当于C#中会自动生成一个和属性名相对应的私有字段,这个私有字段对任何类成员都不开放,只能通过定义的属性进行访问,这种属性的定义方式也成为自动属性设置。
get{},get访问器在自动属性中不能省略,若需要设置为只写属性则需在get前加private进行声明。
set{},set访问器在自动属性中可以省略,省略后表示该属性为只读属性
c#特殊循环结构foreach
foreach(datatype object in arraytest/collection)
{
Console.WriteLine("{0} {1} {2}",name, sex, age);
}
有点类似于python里的for循环或者c++中的:使用
值类型和引用类型
传引用参数
public int Add(ref int a,int b)
其中的ref就是标定该函数形参时引用类型。
引用类型仅仅只在栈上占用一个指针的内存,其余都部署在托管堆上,就像类与数组一样还有基于object的一些类型,而值类型全部内存都部署在栈上。
类的成员
常量
与类关联的常量值
字段
相当于C++的成员变量
特性(properties)
就是属性访问器get和set,作用有三:
1.用于隐藏类成员或组件
2.用于对字段的约束,比如只能读或者只能写
3.用于响应字段变更之后的操作。
方法
类中可执行计算和操作
属性(attribute)
与读写类的命名属性相关的操作:用中括号括起来,可用于修饰类,函数以及以他关键子类型,相当于定义了新的修饰符,作用时相当于对该类型进行说明修饰,主要用于测试阶段,
使用时所需要三个注意事项:
- attribute的实例化不是通过new加构造函数实现,而是用中括号加构造函数
- 因为中括号空间有限, 不能先实例化在给对象字段赋值,而是统一在构造函数的小括号内赋值,可以不属于构造函数的参数,也可以不定义,会有默认值
- 构造函数的参数一定要写全
索引器
与以数组方式索引类的实例相关联的操作
事件
可由类生成的通知
运算符
类所支持的转换和表达式运算符
构造函数
析构函数
类型
类中声明的其他类型比如:
类:class(派生于System.Object);
接口:interface(接口不是一个“东西”,所以不存在派生于何处的问题Anders在《C# ProgrammingLanguage》中说,接口只是表示一种约定[contract]);
有点类似于c++中的虚基类,接口里面定义的方法必须要在派生类中得到实现,去c++虚基类不同的是,接口不能实例化,且接口捏的函数名必须透明,调用的时候是根据派生类实例化俩调用
委托:delegate(派生于System.Delegate)
是函数指针的升级版:
Action委托与 Func委托
Action action = new Action(function);
action();//间接调用function
action.invoke();//与上一行一样
Func<int,int,int> func1= new Func<int,int,int>(function);
func1();
func1.invoke();
自定义委托
public delegate double Calc(double x,double y);
Calc calc1 = new Calc(function);
委托的两种使用方式:
第一种;模板方法的使用
将一个委托作为一个模板方法的参数,则可以是的模板方法动态绑定任意函数
第二种:
回调方法调用指定的外部方法
常用与流水线模式,代码末尾,委托无返回值的情况。
object(System.Object的别名);
字符串:string(System.String的别名)
事件
委托类型字段和事件的不同之处,
通常事件的发生都会产生发送发和接收方,发送方指的是引发事件的对象,而接收方指就是获取、处理事件。成立事件时,事件与委托通常都是一起使用的,事件也称为一种特殊的委托,但他们的作用一般也不相同,用法也不同。
访问修饰符 event 委托名 事件名 ;
从上方实例化例语法可以看出,触发事件的就是发送方,而处理事件的接收方就是接收方。
【实例 1】通过事件完成在控制台上输岀“Hello Event!”的操作。
class Program
{
//定义委托
public delegate void SayDelegate();
//定义事件
public event SayDelegate SayEvent;
//定义委托中调用的方法
public void SayHello()
{
Console.WriteLine("Hello Event!");
}
//创建触发事件的方法
public void SayEventTrigger()
{
//触发事件,必须与事件是同名方法
SayEvent();
}
static void Main(string[] args)
{
//创建Program类的实例
Program program = new Program();
//实例化事件,使用委托指向处理方法
program.SayEvent = new SayDelegate(program.SayHello);
//调用触发事件的方法
program.SayEventTrigger();
}
}
//在控制台会输出HELLO,world!
unity
unity常识介绍
移动视角快捷键:
alt + 鼠标左键
调整图层:
直接在inspector栏内的order in layer 中调整。
unity脚本组件
一般使用c#脚本自定义gameoObject的逻辑行为。在unity中创建的脚本文件内的所有类都继承于Monobehavier类,这个基类自带两个方法start()以及update(),
public class NewBehaviourScript : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}
Start方法用于初始化游戏目标的属性,尽在游侠开始的时候才被调用
而Update用于更新下一帧的动作,unity 通过updtae渲染游戏画面,Update会不断地被重复执行知道游戏结束
gameobject的视角移动
实现方式:右健gameobject
- Move to view :强制将该gameobject位置 移动Scene中央位置,不是Game视图下也就是Main camera 的中央位置
- alligen viwe with selected:将Scene的视角移动到右击的gameobject
- align with view:将相机的位置变换到gameobject上,也即是GAME视角,
场景的对象的平移核导航操作可以利用类似的工具来完成:
- 查看四周,按下鼠标右键可移动相机(此相机指的是我们看scene中的视图)。
- 在使用相机的同时进行移动,按键ASDW
- 按F键看缩放视角并聚焦与所选中的gameobject。
材料Materials
每个材料都有shader(着色器)这使得材质能够控制每个gameobject在场景的渲染的方式,每个shader负责将光照核纹理数据组合起来,进而让gameobject展现材质的样子。
transform类的变量和公共方法:
position:在世界空间坐标transform的位置。
localPosition:相对于父级的变换的位置。如果该变换没有父级,那么等同于Transform.position。
eulerAngles:世界坐标系中的旋转(欧拉角)。
localEulerAngles:相对于父级的变换旋转角度。
right:世界坐标系中的右方向。(世界空间坐标变换的红色轴。也就是x轴。)
up:世界坐标系中的上方向。(在世界空间坐标变换的绿色轴。也就是y轴。)
forward:世界坐标系中的前方向。(在世界空间坐标变换的蓝色轴。也就是z轴。)
rotation:世界坐标系中的旋转(四元数)。
localRotation:相对于父级的变换旋转角度。
localScale:相对于父级的缩放比例。
parent:父对象Transform组件。
worldToLocalMatrix:矩阵变换的点从世界坐标转为自身坐标(只读)。
localToWorldMatrix:矩阵变换的点从自身坐标转为世界坐标(只读)。
root:对象层级关系中的根对象的Transform组件。
childCount:子对象数量。
lossyScale:全局缩放比例(只读)。
1)Translate,用来移动物体的函数,非常常用的一个函数。
public void Translate (translation : Vector3, relativeTo : Space = Space.Self) : void
把物体向translation方向移动,距离为translation.magnitude。 relativeTo表示这个移动的参考坐标系。
public void Translate (x : float, y : float, z : float, relativeTo : Space = Space.Self) : void
同上
public void Translate (translation : Vector3, relativeTo : Transform) : void
如果relativeTo 不是null,则以目标物体relativeTo的自身轴向作为参考坐标系。
public void Translate (x : float, y : float, z : float, relativeTo : Transform) : void
同上
脚本:
var speed=30;
//将物体以30米每秒的速度向前移动。
trasform.Translate(Vector3.forward*speed*Time.deltaTime);
其中Time.dataTime指的是每一帧的间隔时间,这样做的好处是使得在任何机器上该游戏对象的每秒移动距离相同。
2)Rotate,用来旋转物体的函数,非常常用,在知道需要旋转的角度的情况下。如果要让物体旋转到指定位置,需要搭配Quaternion来使用。
public void Rotate (eulerAngles : Vector3, relativeTo : Space = Space.Self) : void
旋转eulerAngles度(3个轴向分别旋转),以relativeTo为参考坐标系
public void Rotate (xAngle : float, yAngle : float, zAngle : float, relativeTo : Space = Space.Self) : void
同上
public void Rotate (axis : Vector3, angle : float, relativeTo : Space = Space.Self) : void
以axis为轴旋转angle度,以relativeTo为参考坐标系
3)RotateAround 让物体以某一点为轴心成圆周运动。
public void RotateAround (point : Vector3, axis : Vector3, angle : float) : void
让物体以point为中心,绕axis为轴向旋转angle度。保持原来与point的距离。
让物体以point为中心,绕axis为轴向旋转angle度。保持原来与point的距离。
4)LookAt 让物体的z轴看向目标物体
public void LookAt (target : Transform, worldUp : Vector3 = Vector3.up) : void
让物体的z轴看向target的位置,并以worldUp为y轴指向方向。
public void LookAt (worldPosition : Vector3, worldUp : Vector3 = Vector3.up) : void
让物体看向worldPosition
5)TransformDirection
public Vector3 TransformDirection (direction : Vector3) : Vector3
返回以物体自身为坐标轴的向量direction在世界坐标中的朝向向量。
public Vector3 TransformDirection (x : float, y : float, z : float) : Vector3
同上
6)InverseTransformDirection
public Vector3 InverseTransformDirectionTransformDirection (direction : Vector3) : Vector3
public Vector3 InverseTransformDirectionTransformDirection (x : float, y : float, z : float) : Vector3
与TransformDirection相反,从世界坐标转换到自身相对坐标。
7)TransformPoint
public Vector3 TransformPoint (position : Vector3) : Vector3
public Vector3 TransformPoint (x : float, y : float, z : float) : Vector3
把一个点从自身相对坐标转换到世界坐标
8)InverseTransformPoint
public Vector3 InverseTransformPoint (position : Vector3) : Vector3
public Vector3 InverseTransformPoint (x : float, y : float, z : float) : Vector3
把一个点从时间坐标转换到自身坐标的位置。
9)DetachChildren
public void DetachChildren () : void
把自身所有的子物体的父物体都设成世界,也就是跟自己的所有子物体接触父子关系。
10)Find
public Transform Find (name : string) : Transform
找到一个名字是name的物体并返回
如果没有找到则返回null。如果字符串被/隔离,函数则会像文件路径一样逐级下查。
// The magical rotating finger
function Update() {
aFinger = transform.Find("LeftShoulder/Arm/Hand/Finger");
aFinger.Rotate(Time.deltaTime*20, 0, 0);
}
11)IsChildOf
public bool IsChildOf (parent : Transform) : bool
如果物体是parent的父子层级关系下的一员,返回true;
如何让变量在inspectors中可以调整
1)在类中设置变量为public
2)在变量前设置权限类型为[SerializeField],这杨字段var仍是private,但可以被inspector感知到。
[SerializeField] float var = 0.4f;
获取键盘输入值方法
Input.GetKey(KeyCode.键盘code)
刚体,碰撞检测
组件rigidbody
需要某个游戏对象能够走自由运动,则可以在游戏对象中添加组件rigidbody,这样游戏对象会有:质量,阻力,摩擦力,引力的影响。
mass(质量)、linear drag(阻力)、angular drag(摩擦力)、gravity scale(引力)
组件collider
实现游戏对象的碰撞逻辑,可以根据游戏对象的形状,自动选择需要colider组件,例如:矩形对应Box Collider,边对应Edage collider等等。
通过程序逻辑实现碰撞事件响应
程序代码
void OnCollisionEnter2D(Collision2D other) {
Debug.Log("撞到了!");
}
需要检测刚体碰撞而又不想要游戏对象发生碰撞,也就是检测出两个游戏对象又发生碰撞但不会互相阻挡而是传过去。
1)在collisder组件里勾选 is trigger选项
2)在相应c#脚本内添加一个函数OntriggerEnter(),函数定义在类里面。
图片载入
将想要载入的图片直接拖入sprite render里的sprite
需要触发父gameobject内组件功能,使用transform.parent.GetComponent<>模板,需要什么组件则则就在<>内填写。
transform.parent.GetComponent<FloorManager>().CreateFloor();
Unity3D中的坐标系以及他们之间的相互转换
世界坐标
这个世界坐标unity中的全局坐标,每个Gameobject仅有一个全局坐标
屏幕坐标
屏幕坐标是以像素来定义的,它的范围是以左下角为(0,0),右上角为(Screen.width,Screen.height)定义的这样一个矩形。屏幕坐标是一个3D坐标,Z轴是以相机的世界单位来衡量的。屏幕坐标和相机之间满足:Screen.width=Camera.pixelWidth和Screen.height=Camera.pixelHeight这两个条件。例如我们将相机正对着场景中的<**在世界坐标中的原点**>原点(0,0,0),相机的Z轴分量为-10,按照屏幕坐标的定义,假设屏幕为800X640的大小,则此时原点转化为屏幕坐标后应该是(400,320,10)。在Unity3D中我们可以使用WorldToScreenPoint来将一个世界坐标转换为屏幕坐标。
视口坐标
视口坐标是标准化后的屏幕坐标。标准化的概念我们可以引申到向量的标准化中,比如一个向量(x,y)将过标准化后可以得到一个单位向量(x’,y’)。类似地,视口坐标是以0到1间的数字来表示的,它的范围是以左下角为(0,0),右上角为(1,1)定义的这样一个矩形。视口坐标是一个3D坐标,Z轴是以相机的世界单位来衡量的。通过对比可以发现视口坐标和屏幕坐标特别的相似,所以这里大家可以对比着来学习。同样以屏幕坐标中的例子来将这里的转换,例如我们将相机正对着场景中的原点(0,0,0),相机的Z轴分量为-10,按照屏幕坐标的定义,假设屏幕为800X640的大小,则此时原点转化为视口坐标后应该是(0.5,0.5,10)。在Unity3D中我们可以使用WorldToViewportPoint来将一个世界坐标转换为视口坐标。
GUI坐标
GUI坐标是指通过OnGUI方法绘制UI时使用的坐标。这个坐标系和屏幕坐标类似,它同样是以像素来定义的,它的范围是以左上角为(0,0),右下角为(Screen.width,Screen.height)定义的这样一个矩形,GUI坐标是一个2D坐标(绝对坐标)。
LocalPoaition和POSITION
localposition之的是本游戏对象相对于父对象的位置,以父对象的中心为原点,二position是全局坐标,当对象没有父母对象的时候,loaclposition与position相等。
二、给刚体施加力和加速度
2.1给刚体施加力
-
给刚体施加一个力以世界坐标为进行运动
Rigidbody.AddForce(Vector3,ForceMode)
-
给刚体施加一个力以自身坐标进行运动
Rigidbody.AddRelativeForce(Vector3,ForceMode
-
四种力的模式
ForceMode.Force:给物体一个持续的力
ForceMode.Acceleration:给物体一个持续的加速度,但是忽略其质量,质量被默认为1
ForceMode.Impulse:给物体添加一个瞬间的力
ForceMode.VelocityChange:给物体添加一个瞬间的加速度,忽略其质量
2.2获取刚体的速度
Rigibody r1,r2;
r1.velocity;
2.3Unity 控制物体旋转、朝向的一些方法
-
transform. Lookat( )
立即看向target的位置, 很生硬. transform.Lookat(target.position);
-
2,Quaternion.LookRotation( )
transform.rotation = Quaternion.LookRotation(target.position - self.position); 也是,立即看向target的位置。
-
Quaternion.RotateTowards( )
Quaternion q = Quaternion.LookRotation(target.position - self.position); Quaternion.RotateTowards(self.rotation, q, turnSpeed * Time.deltaTime); 匀速转向target的位置。(匀速转过去, 虽然不那么生硬了, 但是还不够自然)
-
Quaternion.Slerp( )
Quaternion q = Quaternion.LookRotation(target.position - self.position); Quaternion.Slerp(self.rotation, q, turnSpeed * Time.deltaTime); 非匀速转向target的位置。(人物转向看起来比较合理, 比较自然了) Lerp()方法是平分长度, Slerp()方法是平分弧度
-
transform.Rotate ( )
transform.rotate(Vector3.up, Space.Self); 地球自转
-
transform.RotateAround( )
transform.rotateAround(sun.position, Vector3.up, turnSpeed * Time.deltaTime); 绕太阳公转。
-
四元数*向量
Vector3 dir = Quaternion.Euler(new Vector3(0, 0, angle)) * Vector3.right; 将Vector3.right 饶Z轴 旋转angle度
以下是DOTween的方法
-
transform.DORotate(new Vector3(0, 60, 0), 0.3f); 0.3秒转到(0, 60, 0); Rotation.DOTween的, 注意他的参数是个三元数, 要是想与另一个物体的朝向一致, 就: transform.DORotate(other.transform.eulerAngles, 0.3f);
-
transform.DOLookAt()
transform.DOLookAt(other.transform, 0.3f)
还是DOTween的, 和transform.LookAt差不多, 设置个时间, 能达到lerp的效果, 还能在后面.SetEase(Ease.XXX);
加一些运动曲线
要知道正常游戏1帧=1次动画渲染,当你显卡不行,那么1秒可能只有20帧,如果追求极致,1秒可以144帧,也就是每帧不固定,这个时候就需要固定刷新时间/方法调用时间。特别是:在Update()方法中执行物理操作,会出现卡顿的情况
这样,所有和物理相关的操作,代码都要写在 FixedUpdate() 方法体内
FixedUpdate() 的固定更新的时间间隔是0.02秒,也就是1秒执行50次。
手动设置方法:Edit→Project Settings→Time面板,里面的Fixed Timestep参数即是时间间隔
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Text1: MonoBehaviour
{
//因为Start()只会在开始游戏时被执行一次,所以一般都用来初始化
private Transform myTransform; //实例化Transform对象
private Rigidbody myRigidbody;
void Start()
{
Debug.Log("Start"); //输出调试
myTransform = gameObject.GetComponent<Transform>(); //获取相应对象的引用
myRigidbody = gameObject.GetComponent<Rigidbody>();
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.W))
{
Debug.Log("刚体向前移动");
myRigidbody.MovePosition(myTransform.position + (new Vector3(0, 0, 1)));
}
if (Input.GetKeyDown(KeyCode.A))
{
Debug.Log("刚体向左移动");
myRigidbody.MovePosition(myTransform.position + (new Vector3(-1, 0, 0)));
}
if (Input.GetKeyDown(KeyCode.S))
{
Debug.Log("刚体向后移动");
myRigidbody.MovePosition(myTransform.position + (new Vector3(0, 0, -1)));
}
if (Input.GetKeyDown(KeyCode.D))
{
Debug.Log("刚体向右移动");
myRigidbody.MovePosition(myTransform.position + (new Vector3(1, 0, 0)));
}
}
void FixedUpdate()
{
if (Input.GetMouseButtonDown(1)) //按下鼠标右键
myRigidbody.AddForce(new Vector3(0, 0, -3), ForceMode.Impulse);
if (Input.GetMouseButton(0)) //压住鼠标左键
myRigidbody.AddForce(new Vector3(0, 0, -10), ForceMode.Force);
}
}
相机移动跟随
public class CameraBehavior:MonoBehaviour
{
public Vector3 canOffset = new Vector3(0,1.2,--2.6);
private Transform target;
void Start)(){
target - GameObject.Find("player").transform;
}
void lateUpdate()//在update方法之后执行,因为update方法会改变target的位置,所以在update方法后执行能够确保相机的在target最新位置相对位置。
{
this.transform.position = target.TransformPoint(camOffet);
//TransFORMpOINt方法适用于计算并返回世界空间的相对位置。
this.tranform.LookAt(target):
//确保相机跟谁目标一起旋转
}
}
awake函数
用于初始化变量和游戏状态。
使用
当一个脚本实例被载入时awake被调用,所有awake函数在每个场景加载完后就依次被调度执行,在start函数之前执行,一般尽在runtime模式执行,除非在在每个类前面假说属性attribute[[ExecuteInEditMode]],才会在在编辑模式执行,
-
用法一:可以在awake里面设置enable值(默认为true),使得该脚本的start函数和update函数无法被调用。
public class Test : MonoBehaviour { void Awake () { Debug.Log("Awake"); enabled = false; } void Start () { Debug.Log("Start"); }
而在游戏过程中会有另一段代码被执行
Test test = go.GetComponent<Test>(); test.enabled = true;
这段代码被执行之后,start方法才能被调用
-
设置被引用的object(reference)
//player.cs private Transform handAnchor = null; void Awake () { handAnchor = transform.Find("hand_anchor"); } // void Start () { handAnchor = transform.Find("hand_anchor"); } void GetWeapon ( GameObject go ) { if ( handAnchor == null ) { Debug.LogError("handAnchor is null"); return; } go.transform.parent = handAnchor; } //othrer.cs ... GameObject go = new GameObject("player"); player pl = go.AddComponent<player>(); // Awake invoke right after this! pl.GetWeapon(weaponGO); ...
以上代码中,我们在player Awake的时候去为handAnchor赋值。如果我们将这步操作放在Start里,那么在other.cs中,当执行GetWeapon的时候就会出现handAnchor是null reference.
setactive函数
控制整个gameobject的激活状态,当处于未激活状态时候,此gameobject将停用所有附加在其上面的组件功能,也就是对所有组件调用Onenable函数
标签:float,C#,void,Vector3,transform,学习,unity,坐标,public From: https://www.cnblogs.com/NCU-zjf/p/16804598.html