Unity超基础学习笔记(二)

1. 基本数据类型的扩展

之前在K12中学习了一些基本的数据类型,实际上C#支持更多的数据类型。如下:

注意无符号整型数和有符号整型数的表示范围,例如:
int 能表示 -231~231-1;
uint 能表示 0~2^32-1;

2. 类型转换

同种数据(例如都是整型)的不同数据类型可以互相转换。有两种方法。
1) 隐式类型转换 。这些转换是 C# 默认的以安全方式进行的转换, 不会导致数据丢失,也就是从表示范围小转换到表示范围大的数据。例如,从小的整数类型转换为大的整数类型,从派生类转换为基类。

int a=1;
long b=a;//可以成立

2) 显式类型转换 ,即强制类型转换。显式转换需要强制转换运算符,而且强制转换会造成数据丢失,也就是从表示范围大转换到表示范围小的数据。所以在转换之前要确认不会丢失的数据,或丢失的数据是可以丢失的。

long b=1;
int a=(int)b;

不同种数据之前也可以互相转换:
1) 本身一些类会提供数据转换的方法:

int a = 1;
a.ToString();//转换成字符串

2) Convert类中提供了一些数据转换的方法:

string str = “123”;
int a = Convert.ToInt32(str);//将str转换成int类型,失败报错。

3) 在基本类型中提供了Parse和TryParse方法来数据转换:

string str = “123”;
int a = int.Parse(str); //将str转换成int类型,失败报错。

4) Convert和Parse的转换区别:
(1) 这两个方法的最大不同是它们对 null 值的处理方法: Convert.ToInt32(null) 会返回 0 而不会产生任何异常,但 int.Parse(null) 则会产生异常。

(2) 对数据进行四舍五入时候的区别 :
a. Convert.ToInt32(double value) 如果 value 为两个整数中间的数字,则返回二者中的偶数;即 3.5 转换为 4,4.5 转换为 4,而 5.5 转换为 6。不过 4.6 可以转换为 5,4.4 转换为 4 。
b. int.Parse(“4.5”) 直接报错:“输入字符串的格式不正确”。
c. int(4.6) = 4 Int 转化其他数值类型为 Int 时没有四舍五入,强制转换。

(3) 对被转换类型的区别 int.Parse 是转换 String 为 int, Convert.ToInt32 是转换继承自 Object 的对象为 int 的(可以有很多其它类型的数据)。你得到一个 object 对象, 你想把它转换为 int, 用 int.Parse 就不可以, 要用 Convert.ToInt32。

  1. (is关键字)检查表达式是否为指定类型:
a is int ;//变量 is 数据类型或者类名;//判断变量是否为该数据类型或者类,是的话为返回True,否的话返回False

4. 变量的作用域:

在C#中声明的变量是有使用范围的。例如在unity脚本中:

public class NewBehaviourScript : MonoBehaviour
{int a;//可以在start方法和Update方法中使用// Start is called before the first frame updatevoid Start(){int b;//只可以在start方法中使用}// Update is called once per framevoid Update(){}
}

5. 常量:

对于一些不会改变的数据,可以将其声明为一个常量。例如圆周率可以声明为PI。

const float PI=3.14f;//声明常量
//PI=11;//常量不可修改该句会编译错误。

6. Switch语句

例如有个需求是当level=1时,执行FuncA();当level=2时,执行FuncB();当level=3时,执行FuncC()。而level只有这个三种情况下,用if条件句表示如下:

int level = 1;if (level == 1)FuncA();else if (level == 2)FuncB();else if (level == 3)FuncC();

显然这样有点难以阅读,这时就可以引入switch语句。

switch(level)
{case 1:FuncA();break;
case 2:FuncB();break;
case 3:FuncC();break;
default://可选Debug.Log(“ERROR”);break;
}

这样程序会根据level的值来选择要执行的语句。显然为了一一对应,level这里的变量,必须是整型或者枚举型,或者可以转换成整型或者枚举型的变量。而case 后接的数必须是个常量,不能是变量。原则上每个case语句后头都要跟一个break;,但如果该case语句为空时,可以不接break;,程序将直接执行下一条case的语句直至遇到break;例如:

int level = 1;
switch(level)
{case 1:
case 2:Debug.Log(“2”);break;
}

这时会输出2。

7. 再论循环

1. 在多重循环中,最长的循环应放在最内层,在允许的情况下最短的循环应放在最外层以减少CPU跨切循环层的次数。

int[,] a= new int[6,2];
for(int i=0;i<2;i++)
{for(int j=0;j<6;j++){a[j,i]=i+j;}
}

2. 建议for循环中循环控制变量的取值采用半开半闭写法。

int[] a= new int[6];
for(int i=0;j<6;i++)//[0,6)区间
//不用for(int i=0;j<=5;i++) [0,5]区间
{a[i]=i;
}

8. 在unity中使用循环(关于协程)

通常有一个需求,例如我在unity的脚本里写了一个循环输出1,2,3……但我不希望他一下子输出出来。而是每隔几秒后输出,形成一个动画。如下,如果我直接将循环写在start方法里,它就会一下子输出出来,不能完成我的需求。因为根据unity的运行顺序,要等所有的start方法和update方法都运行完后才会渲染画面,之后到下一帧。

void Start(){for (int i = 0; i < 10; i++){Debug.Log(i);}}

这时候就需要用到协程。则将以上程序改下如下:

void Start(){StartCoroutine(Show());//开启协程}IEnumerator Show()//协程方法的写法,返回是迭代器{for (int i = 0; i < 10; i++){Debug.Log(i);yield return new WaitForSeconds(0.5f);//代码在这中断,直至0.5秒过后的下一帧重新回到该处运行下一条代码,也就是下一次for循环。}}


当然,中断后继续的条件有很多种,如下:
yield return null; // 下一帧再执行后续代码
yield return 6;//(任意数字) 下一帧再执行后续代码
yield break; //直接结束该协程的后续操作
yield return asyncOperation;//等异步操作结束后再执行后续代码
yield return StartCoroutine(/某个协程/);//等待某个协程执行完毕后再执行后续代码
yield return WWW();//等待WWW操作完成后再执行后续代码
yield return new WaitForEndOfFrame();//等待帧结束,等待直到所有的摄像机和GUI被渲染完成后,在该帧显示在屏幕之前执行
yield return new WaitForSeconds(0.3f);//等待0.3秒,一段指定的时间延迟之后继续执行,在所有的Update函数完成调用的那一帧之后(这里的时间会受到Time.timeScale的影响);
yield return new WaitForSecondsRealtime(0.3f);//等待0.3秒,一段指定的时间延迟之后继续执行,在所有的Update函数完成调用的那一帧之后(这里的时间不受到Time.timeScale的影响);
yield return WaitForFixedUpdate();//等待下一次FixedUpdate开始时再执行后续代码
yield return new WaitUntil()//将协同执行直到 当输入的参数(或者委托)为true的时候…如:yield return new WaitUntil(() => frame >= 10);
yield return new WaitWhile()//将协同执行直到 当输入的参数(或者委托)为false的时候… 如:yield return new WaitWhile(() => frame < 10);
来自:https://www.cnblogs.com/unknown6248/p/11013621.html

9. 再论方法的参数:

之前我们在C#基础中学习了用值类型(如int)和引用类型(如int[])作为参数的不同。这次我们讨论另外三种情况:
1) 我们就是想传一个值类型参数进去,让方法内能直接修改该参数的值。
这时我们就要用到ref关键字,把ref加在形参类型之前就行。在调用的时候,同样要把ref加在实参的前面。

class Program{static void Main(string[] args){int x = 1;Func(ref x);Console.WriteLine(x);}static void Func(ref int a){a = 2;}}
 那考虑一个问题,对于引用类型的参数,ref是不是没有意义了。那我们看下这个例子:
class Program{static void Main(string[] args){int[] x = { 1,2,3,4};Func(x);Console.WriteLine(x[0]);}static void Func(int[] a){a = new int[] { 5,6,7,8};}}
考虑输出的是几?明显还是1。因为形参只是复制了一份引用地址,当在方法内改变形参的引用地址时,外部的实参还是不受影响的。
这时给形参和实参加上ref的话:
class Program{static void Main(string[] args){int[] x = { 1,2,3,4};Func(ref x);Console.WriteLine(x[0]);}static void Func(ref int[] a){a = new int[] { 5,6,7,8};}}

这时输出的就是5了。那么ref的作用就很明显了。就是使参数按引用传递,让形参成为实参的一个别名。

更多资料参考C#官方文档https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/ref

2) 想传回更多的值:
这时可以用out关键字,类似ref,也是按引用传递。与ref关键字的区别在于ref需要在实参在传进去的时候一定要初始化过(如int x=1;//要赋个值),而out不要求传入前赋值,但要求传出时要赋值。

static void Main(string[] args)
{int x ;Func(out x);Console.WriteLine(x);
}
static void Func(out int a)
{a = 2;}

3) 不知道要传几个值进去:
例如要累加几个数,但具体数目不知道。除了用数组来传之外还可以用params这个关键字。如下:

static void Main(string[] args)
{int x=1 ;int y=2 ;int z=3 ;Console.WriteLine(Func(x,y,z));
}
static int Func(params int[] a)
{int sum = 0;for (int i = 0; i < a.Length; i++){sum += a[i];}return sum;}

可见params 是将参数复制在一个一维数组之中来传递(因此params声明后边必须跟的是一维数组)。那么思考是否能传一个一维数组进去呢?结论是可以的。那么这时是算传引用类型参数吗?

class Program
{static void Main(string[] args){int[] b = { 1,2,3 };Func1(b);Console.WriteLine(b[0]);Func2(b);Console.WriteLine(b[0]);}static void Func1(params int[] a){a[0] = 10;}static void Func2(params int[] a){a = new int[] { 4, 5, 6 };}}

大家可以照这个代码做下实验,发现两次输出的都是10,所以传数组时是按引用类型传递的。
注意:有多个参数的情况下,规定params只能修饰最后一个参数。

10. 交错数组

交错数组,即数组的数组。声明方式如下:

int[][] arr;

数据类型 维度(两个以上个框)变量名
与多维数组的区别:多维数组各个维度下元素的个数是一定的。但交错数组是数组的数组,每一维的元素个数是可以不一样的。

int[,] arr1 = new int[,] { { 1, 2, 3 }, { 1, 2 } };//这会报错
int[][] arr2 = new int[][] { new int[]{ 1, 2, 3 }, new int[]{ 1, 2 } };//这不会报错

交错数组的实例化:
在实例化时,只能指定下一维的长度,因为之后的的维度长度都不一定的。

int[][] arr1 = new int[2][];//先实例化第一维
arr1[0] = new int[3];//之后再分别实例化下一个维度
arr1[1] = new int[4];

交错数组的初始化:
如之前的例子中一样交错可以直接初始化

int[][] arr2 = new int[][] { new int[]{ 1, 2, 3 }, new int[]{ 1, 2 } };

交错数组的储存方式如图:

可见直到最有一个维度,数组里存的都是下一维度的引用地址。例如3维的情况:

int[][][] arr;
arr = new int[5][][];
arr[0] = new int[4][];
arr[0][0] = new int[3];

同时,每一维也不一定一定是一维,可以和多维数组自由组合。

arr[,][] arr1;
arr[][,] arr2;

练习用交错数组打印杨辉三角:

static void Main(string[] args)
{Func(9);//打印9层
}
static void Func(int n)//打印n层杨辉3角
{int[][] arr = new int[n][];for (int i = 0; i < n; i++){string str = "";arr[i] = new int[i+1];for (int j = 0; j < i + 1; j++){if (j == 0 || j == i){arr[i][j] = 1;}else{arr[i][j] = arr[i - 1][j - 1] + arr[i-1][j];}str += arr[i][j] + "\t";}Console.WriteLine(str);//输出这一层}
}

11. 值类型中的复合类型

1) 枚举类型
枚举类型是由基础整型数值类型的一组命名常量定义的值类型。例如一些数据是可以明确分为有限个数种类,我们可以在其中枚举出所有的可能选项时。我们就可以用枚举类型来声明他。比如:一星期里只有星期一到星期天。

enum Weekday
{Monday,Tuesday,Wednesday,Thusday,Friday,Saturday,Sunday
};

枚举类型的使用:
枚举类型的变量值是通过“类型名”加”.”,再加“枚举项”来使用的。

Weekday w1=Weekday.Monday;

枚举类型和整型的互相转换:
由于枚举类型是由基础整型数值类型的一组命名常量定义的。所以默认情况下,枚举成员的关联常数值为类型 int;它们从零开始,并按定义文本顺序递增 1。
比如:

enum Weekday
{Monday,Tuesday,Wednesday,Thusday,Friday,Saturday,Sunday
// Monday=0,Tuesday=1,Wednesday=2,Thusday=3,Friday=4,Saturday=5,Sunday=6
};

当然可以关联的值类型和枚举成员的值都可以指定。
比如:

enum ErrorCode : ushort//改为ushort类型
{None = 0,//指定为0Unknown = 1, //指定为1ConnectionLost = 100, //指定为2OutlierReading = 200
}

可以指定一部分的值,后面的值按顺序加1递增

enum A
{a=2,b,//b=3c//c=4
}

显然,整型和枚举型可以互相转换(显式转换)

Console.WriteLine((int)A.a);//输出为2
Console.WriteLine((A)3);//输出为b
Console.WriteLine((A)5);//输出为5,转换失败就输出原整型值

注意:程序中不应滥用枚举类型。事实上大多数的值都是有范围的。除了程序中常用的一些特殊概念之外,大部分还应直接使用整型,并在程序逻辑中控制范围,如“月份”范围为112,“年级”范围为16等,而不是把他们定义为枚举类型。
unity中的枚举类型:KeyCode

2) 结构体类型
结构类型是一种可封装数据和相关功能的值类型。有时候人们需要组合着使用一些基本类型。比如说三维坐标需要三个float类型数。一个一个去声明太麻烦了,这时就可以使用结构体类型。比如:

struct MyVector//定义结构体
{public float x;//表示x坐标public float y;//表示y坐标public float z;//表示z坐标
}//结构体内的成员也可以设定访问权限(public private,默认是private)

结构体的实例化:

 MyVector v;
//由于是值类型,一旦声明,就会在栈里分配一块内存。各个成员会赋予初始值
v.x=1;
v.y=2;
v.z=3;//之后对各成员赋值

在结构体内也能写方法,例如:

struct MyVector
{public float x;public float y; public float z;public float Length()//求该向量的长度{return Mathf.Sqrt(x*x + y*y + z*z);}
}

使用方法:

float l = v.Length();

在结构体里可以写有参数的构造函数,但是不能写无参数的构造函数。

struct MyVector
{public float x;//结构体中不能直接初始化,比如:public float x = 1;public float y; public float z;public MyVector(float x,float y,float z)//初始化{this.x=x;//this表示要初始化的变量,为了和形式参数分开this.y=y;this.z=z;}
//注意:在初始化过程中必须把所有成员初始化。
}

使用方法:

MyVector v = new MyVector(1,2,3);

结构体的使用场合:
1) 适用于点,矩形和颜色这样的轻量对象。
2) 由于结构体不支持继承,不适合表现抽象和多级别的对象层次。

Unity中的结构体:Vector2,Vector3,Color

12. 再谈string类型

1) string可以通过索引器读取字符串中的某个字符,但不能通过索引器修改。

string a=”unity”;
a[1]=’a’;//错误,改不了

2) 由于string是引用类型。

string str1 = “”;
string str2 = null;

是不同的,前者已分配了内存空间,用str1.Length的话,回得到0,相当于

string str1 = new string("");

而后者是null,没有分配内存空间,访问str1.Length会报错。

3) 字符串自带的一些方法:
字符串有一堆的自带方法:详看官方文档:
https://docs.microsoft.com/zh-cn/dotnet/api/system.string?view=netcore-3.1
常用的有:
1) str.IndexOf(char c);返回字符串中第一个匹配的字符的索引值。
str.IndexOf(char c,int s);返回从第s个字符开始第一个匹配的字符的索引值。
str.IndexOf(string subStr); 返回字符串中第一个匹配的子字符串的第一个字符的索引值。
例如:string str=”abcde”; int a = str.IndexOf(“cd”);//返回2

2) str.SubString(int s);返回从第s个字符开始直至结束的子串。
str.SubString(int s,int n);返回从第s个字符开始连续n个字符的子串。

3) str.Insert(int s,string v);在第s索引字符前插入子串v。

4) str.Remove(int s);删除第s索引字符(包括)之后的字符
str.Remove(int s,int n)从第s索引字符(包括)开始删除之后的n个字符

5) str.Replace(char c1,char c2);将字符串中的所有c1字符都替换成c2字符
str.Replace(string s1,string s2); 将字符串中的所有s1字串都替换成s2子串

6) str.Split(char c1);以c1字符为界分割字符串,返回字符串数组。
str.Split(string s1); 以s1字符串为界分割字符串,返回字符串数组。

7) str.CompareTo(str2);依次比较str和str2两个字符串的每个字符。若str和str2长度和字符完全相等时输出为0。如果前几个字符一样,就比较下一个字符,直至不同的字符。
比较方式比较复杂。

8) 正则表达式+Regex类:可以通过一些特殊的公式来匹配字符串。

13. StringBuilder

string是一种特殊的引用类型。由于字符串的不可变性,每一次修改字符串时,并不是直接修改原来的字符串,而是新开辟了一块内存来保存修改后的字符串。而原字符串仍然留在内存中等待回收。那么如果频繁地修改字符串的话,会导致内存里都是垃圾来不及回收,造成性能消耗。
为了解决这个问题,可以用在System.Text下的StringBuilder类来对字符串进行动态管理。
声明和初始化一个StringBuilder :

StringBuilder sb1 = new StringBuilder();
StringBuilder sb2 = new StringBuilder(“abc”);//可以在声明时赋一个字符串。

StringBuilder VS string
1) 都有Length属性来获取字符串长度。

2) 都可以通过索引访问其中的字符,但StringBuilder可以通过索引器修改字符。

3) StringBuilder也提供了Insert,Remove,Replace方法来插入,删除,替换字符。要注意的是这些修改都没创造新的对象,返回值也是原StringBuilder的引用。

4) StringBuilder可以通过ToString方法返回一个同内容的字符串。

5) StringBuilder为连接字符操作提供了Append、AppendLine和AppendFormat方法。
Append方法用于将一个新的字符加到原数据尾端,可以是字符串或基本值类型。
AppendLine方法会在增加新的字符后加个换行符。
AppendFormat方法可以按格式来增加新的字符。

6)注意StringBuilder没有提供很多字符串搜索方法,要进行搜索操作还是要在string下进行。

Unity超基础学习笔记(二)相关推荐

  1. Unity超基础学习笔记(四)

    1. 装箱和拆箱 由于在C#中所有类型(包括值类型)都继承于System.Object类.另一方面Object类本身又是引用类型.所以值类型的数据可以隐式转换为终极基类的Object类型,同时数据由值 ...

  2. Unity超基础学习笔记(三)

    1. 面向对象的编程 早先面向过程编程的问题: 1) 功能和数据分离,使用的建模概念不能直接映射到问题域中的对象,不符合人们对现实世界的认识和思维方式. 2) 自顶向下的设计方法限制了软件模块的可复用 ...

  3. Unity超基础学习笔记(一)

    1.安装Unity和认识Unity界面 通过访问unity下载来下载unityHub或者直接下载unity.(应该需要注册个unityID).本次课程用的版本是2019.4.3f1. 打开后新建一个项 ...

  4. 【台大郭彦甫】Matlab入门教程超详细学习笔记二:基本操作与矩阵运算(附PPT链接)

    Matlab入门教程超详细学习笔记二:基本操作与矩阵运算 前言 一.基本操作 1.把matlab当作计算器使用 2.变量 3.控制格式输出 二.矩阵运算 1.矩阵 2.矩阵索引 3.使用:创建向量 4 ...

  5. 【Unity NGUI】学习笔记(二)之英雄攻击和装备武器

    玩魔兽世界的时候,游戏人物可以行走,奔跑,攻击,死亡等等行为,今天继续结合上一个案例[Unity NGUI]学习笔记(一):英雄选择,皮肤更换上给英雄添加动作行为. 一.准备工作 给英雄准备奔跑 攻击 ...

  6. Java基础学习笔记(二)_Java核心技术(进阶)

    本篇文章的学习资源来自Java学习视频教程:Java核心技术(进阶)_华东师范大学_中国大学MOOC(慕课) 本篇文章的学习笔记即是对Java核心技术课程的总结,也是对自己学习的总结 文章目录 Jav ...

  7. java冰法_Java基础学习笔记二 Java基础语法

    注释 注释用来解释和说明程序的文字,注释是不会被执行的. 单行注释 //这是一条单行注释 public int i; 多行注释 /*这是 * 一段注释, * 它跨越了多个行*/ public void ...

  8. Python3零基础学习笔记二

    python3中常用的字符串方法(method)    方法的调用方法:       对象.方法名(方法传参)    说明:       方法的调用同函数调用一样属于表达式.    示例:       ...

  9. pythonsze_python学习笔记二 数据类型(基础篇)

    Python基础 对于Python,一切事物都是对象,对象基于类创建 不同类型的类可以创造出字符串,数字,列表这样的对象,比如"koka".24.['北京', '上海', '深圳' ...

最新文章

  1. Android消息机制 Looper源码阅读
  2. iOS之UI--转场动画
  3. 【Java并发编程】20、DelayQueue实现订单的定时取消
  4. Scrapy框架的学习(11.scrapy框架中的下载中间件的使用(DownloaderMiddlewares))
  5. Spring Boot解决跨域问题
  6. 传值类型_java中的“传值”与“传址”问题
  7. lucene 多索引目录搜索实现方法
  8. 【转载】MongoDB集群和实战详解
  9. 信号与系统公式笔记(8)——拉普拉斯变换
  10. linux 一键安装字体,在deepin中一键安装喜欢的字体,文档气质瞬间提升!
  11. 淘金网UCskype即时通讯软件定制
  12. 天猫精灵 python_天猫精灵控制esp8266点led灯
  13. 禁止迅雷极速版强制升级方法
  14. 模拟 nbut1225 NEW RDSP MODE I
  15. 【文献翻译】构建网络安全知识库的框架-A Framework to Construct Knowledge Base for Cyber Security
  16. Method findById should have no parameters
  17. lg kv510 java_为妇女节献礼 精美天翼手机 LG KV510评测
  18. 云和恩墨大讲堂电子期刊第四期
  19. 语义分割标签制作全过程(适合新手)
  20. 将Excel每行数据存储到单独的txt文件中

热门文章

  1. python 菜单 阻塞 其它程序_Python subprocess.call阻塞
  2. python类的命名空间_Python之关于类变量的两种赋值区别详解
  3. 去哪儿网查不到历史订单_去哪儿网 数据清洗
  4. python etree详解_使用lxml.etree解析python alexa结果
  5. docker 镜像修改的配置文件自动还原_所以到底该如何修改 docker 容器的端口映射!!!...
  6. 设计灵感|C4D卡通角色设计作品,你想要的模型集设都有
  7. 值得借鉴的新年海报设计|PSD分层模板,图层素材随心用
  8. 应该如何设计圣诞元素到项目?
  9. 字体设计灵感合集|字体决定了设计
  10. UI设计素材干货模板|移动按钮的“悬停效果”