Unity超基础学习笔记(二)

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 update
    void Start()
    {
        int b;//只可以在start方法中使用
    }

    // Update is called once per frame
    void 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,//指定为0
    Unknown = 1, //指定为1
    ConnectionLost = 100, //指定为2
    OutlierReading = 200
}

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

enum A
{
	a=2,
	b,//b=3
	c//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下进行。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
学习Unity3D时,以下是一些重要的笔记: 1. Unity3D基础知识: - 游戏对象(Game Objects)和组件(Components):了解游戏对象的层次结构和组件的作用。 - 场景(Scenes)和摄像机(Cameras):学会如何创建场景并设置摄像机视角。 - 材质(Materials)和纹理(Textures):掌握如何创建和应用材质和纹理。 - 动画(Animations):学习如何创建和控制游戏对象的动画。 2. 脚本编程: - C#语言基础:了解C#语言的基本语法和面向对象编程概念。 - Unity脚本编写:学习如何编写脚本来控制游戏对象的行为和交互。 - 常见组件和功能:掌握常见的Unity组件和功能,如碰撞器(Colliders)、刚体(Rigidbodies)、触发器(Triggers)等。 3. 游戏开发流程: - 设计游戏关卡:了解如何设计游戏场景和关卡,包括布局、道具、敌人等。 - 游戏逻辑实现:将游戏规则和玩家交互转化为代码实现。 - UI界面设计:学习如何设计游戏中的用户界面,包括菜单、计分板等。 - 游戏优化和调试:优化游戏性能,解决常见的错误和问题。 4. 学习资源: - Unity官方文档和教程:官方提供了大量的文档和教程,逐步引导你学习Unity3D。 - 在线教程和视频教程:网上有很多免费和付费的Unity教程和视频教程,可根据自己的需求选择学习。 - 社区论坛和博客:加入Unity开发者社区,与其他开发者交流并获取帮助。 通过系统地学习这些内容,你将能够掌握Unity3D的基础知识并开始开发自己的游戏项目。记得不断实践和尝试,不断提升自己的技能!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值