-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.json
1 lines (1 loc) · 41.8 KB
/
index.json
1
[{"categories":["Algorithm"],"content":"10种排序算法详解。 ","date":"2022-03-29","objectID":"/sorting_algorithm/:0:0","tags":["algorithm","sort"],"title":"📝排序算法","uri":"/sorting_algorithm/"},{"categories":["Algorithm"],"content":"算法分类 ","date":"2022-03-29","objectID":"/sorting_algorithm/:1:0","tags":["algorithm","sort"],"title":"📝排序算法","uri":"/sorting_algorithm/"},{"categories":["Algorithm"],"content":"算法复杂度 ","date":"2022-03-29","objectID":"/sorting_algorithm/:2:0","tags":["algorithm","sort"],"title":"📝排序算法","uri":"/sorting_algorithm/"},{"categories":["Algorithm"],"content":"1. 冒泡排序 算法思想:从前往后交换两个相邻的数,第一次循环得到最后一个最大值,第二次循环得到倒数第二个最大值…..以此类推 算法分析:冒泡排序的最坏和平均时间复杂度都是O(n^ 2),因为有两个for循环。最好的时间复杂度为O(n),即当已经排好序时,但代码需稍作修改,使内循环只循环一次(例子如下)。 不需要额外的空间,所以空间复杂度为O(l)。稳定性好,因为相同值的相对位置不变。 void bubbleSort(int *arr){ int len=20; for(int i=0;i\u003clen-1;i++){ for(int j=0;j\u003clen-1-i;j++){ if(arr[j]\u003earr[j+1]){ swap(arr[j], arr[j+1]); } } } for(int i=0;i\u003clen;i++){ cout\u003c\u003carr[i]\u003c\u003c\" \"; } cout\u003c\u003cendl; } int main(int argc, const char * argv[]) { int arr[20]={6,7,17,18,19,8,9,14,15,1,2,3,10,11,12,13,4,5,16,20}; bubbleSort(arr); return 0; } 复杂度又可能为O(n)的例子: void bubbleSort(int arr[]) { boolean didSwap; for(int i = 0, len = arr.length; i \u003c len - 1; i++) { didSwap = false; for(int j = 0; j \u003c len - i - 1; j++) { if(arr[j + 1] \u003c arr[j]) { swap(arr, j, j + 1); didSwap = true; } } if(didSwap == false) return; } } ","date":"2022-03-29","objectID":"/sorting_algorithm/:3:0","tags":["algorithm","sort"],"title":"📝排序算法","uri":"/sorting_algorithm/"},{"categories":["Algorithm"],"content":"2. 选择排序 算法思想:第一次遍历从所有元素中找到最小的,放在第一个;第二次遍历从第二个元素开始到最后一个找到最小的放在第二个,以此类推。 算法分析:最坏、平均和最好的时间复杂度都为O(n^ 2),不许要额外空间,空间复杂度为O(l),但是选择排序不稳定,因为比如数组A[3,4,3,2],第一次找到最小的2和3换了一下,那第一个3和第二个3位置顺序就变化了所以不稳定了! void selectionSort(int* arr,int len){ int min_index; for(int i=0;i\u003clen;i++){ min_index=i; for(int j=i;j\u003clen;j++){ if(arr[j]\u003carr[min_index]){ min_index=j; } } swap(arr[i], arr[min_index]); } for(int i=0;i\u003clen;i++){ cout\u003c\u003carr[i]\u003c\u003c\" \"; } cout\u003c\u003cendl; } int main(int argc, const char * argv[]) { int arr[20]={6,7,17,18,19,8,9,14,15,1,2,3,10,11,12,13,4,5,16,20}; int len=20; selectionSort(arr,20); return 0; } ","date":"2022-03-29","objectID":"/sorting_algorithm/:4:0","tags":["algorithm","sort"],"title":"📝排序算法","uri":"/sorting_algorithm/"},{"categories":["Algorithm"],"content":"3. 插入排序 算法思想:默认前面部分已经排好序,只是判断后一个数插入前面序列的哪个下标 算法分析:最好时间复杂度为O(n),平均和最坏时间复杂度是O(n^2),空间复杂度为O(1),稳定性好。 void insertionSort(int *arr,int len){ int current;//当前需要插入的值 int preorder;//前一个数组下标 for(int i=1;i\u003clen;i++){ current=arr[i]; preorder=i-1; while(preorder\u003e=0\u0026\u0026current\u003carr[preorder]){ arr[preorder+1]=arr[preorder]; preorder--; } arr[preorder+1]=current; } for(int i=0;i\u003clen;i++){ cout\u003c\u003carr[i]\u003c\u003c\" \"; } cout\u003c\u003cendl; } int main(int argc, const char * argv[]) { int arr[20]={6,7,17,18,19,8,9,14,15,1,2,3,10,11,12,13,4,5,16,20}; int len=20; insertionSort(arr,len); return 0; } ","date":"2022-03-29","objectID":"/sorting_algorithm/:5:0","tags":["algorithm","sort"],"title":"📝排序算法","uri":"/sorting_algorithm/"},{"categories":["Algorithm"],"content":"4. 希尔排序 算法思想:分组交替排序 算法分析:平均时间复杂度是O(n^1.3), 最坏时间复杂度O(n^2),最好时间复杂度是O(n),空间复杂度O(1). void shellSort(int* arr,int len){ for(int gap=len/2;gap\u003e0;gap=gap/2){ for(int i=gap;i\u003clen;i++){ int index=i; int current=arr[index]; while(arr[index-gap]\u003ecurrent\u0026\u0026index-gap\u003e=0){ arr[index]=arr[index-gap];//前部分中最大的数复制给下标index的值 index=index-gap; } arr[index]=current;//将原来index的值给最前面的值 } } for(int i=0;i\u003clen;i++){ cout\u003c\u003carr[i]\u003c\u003c\" \"; } cout\u003c\u003cendl; } int main(int argc, const char * argv[]) { int arr[20]={6,7,17,18,19,8,9,14,15,1,2,3,10,11,12,13,4,5,16,20}; int len=20; shellSort(arr,len); return 0; } ","date":"2022-03-29","objectID":"/sorting_algorithm/:6:0","tags":["algorithm","sort"],"title":"📝排序算法","uri":"/sorting_algorithm/"},{"categories":["Algorithm"],"content":"5. 归并排序 算法思想:分成n/2个组,先给左边组排序再给右边组排序然后再合并总的组,再排序,递归思想。 算法思想:用了额外的空间,空间复杂度O(n)。 如果假设一个序列有n个数的排序时间为T(n),T(n)是一个关于n的函数,随着n的变化而变化。 那么我们将n个数的序列,分为两个(n/2)的序列。 那么T(n)=2*T(n/2)+合并时间 由于合并时,两个子序列已经组内排好序了,那我们将两个排好序的序列组合成一个大的有序序列,只需要一个if循环即可。if循环中有n个数需要比较,所以时间复杂度为n。 那么T(n)=2*T(n/2)+n 我们再将两个n/2个序列再分成4个(n/4)的序列。 一个(n/2)序列排序时间=两个(n/4)的序列排序时间+两个(n/4)的序列的合并为一个(n/2)的序列时间 T(n/2)=2*T(n/4)+n/2 将T(n/2)带入到T(n)中,T(n)=2*(2*T(n/4)+n/2)+n, 通过化简T(n)=4*T(n/4)+2n 我们再将4个n/4个序列再分成8个(n/8)的序列。 T(n/4)=2*T(n/8)+n/4 将T(n/4)带入到黄色公式中,T(n)=4*(2*T(n/8)+n/4)+2n 通过化简T(n)=8*T(n/8)+3n ······ 这样分下去,前面我们已经说过了,分为n个序列,每个序列里只有一个数为止。 前面我们假设的一个序列有n个数的排序时间为T(n),现在每个序列只有一个数,所以不需要进行组内排序,时间复杂度为0。T(1)=0 大家有没有找到规律,右边式子中n前面的系数随着层数的增加而增加,第一层公式中没有n,则系数为0,第二层n的系数为1,第三层为2,第四层为3。那么规律就出来了,n前面的系数为层数减1。 那这个图有没有很熟悉,就像一个二叉树一样,通过二叉树的知识点我们可以知道,一个n个结点的二叉树层数为(log2n)+1。 那么n前面的系数为层数减1。 (log2n)+1-1=log2n 那log2n就是最底层n的系数。 那么我们最后一层是不是可以这样表示 T(n)=n*T(1)+(log2n)*n T(1)=0,那么T(n)=(log2n)*n 所以归并排序的时间复杂度为nlog2n 稳定性好 void merge(int* arr,int left,int right,int mid){ int lIndex=left; int rIndex=mid+1; int help[right-left+1]; int i=0; while(lIndex\u003c=mid\u0026\u0026rIndex\u003c=right){ help[i++]=arr[lIndex]\u003carr[rIndex]?arr[lIndex++]:arr[rIndex++]; } while(lIndex\u003c=mid){ help[i++]=arr[lIndex++]; } while(rIndex\u003c=right){ help[i++]=arr[rIndex++]; } for(int i=0;i\u003cright-left+1;i++) { arr[left+i]=help[i]; } } void mergesort(int *arr,int left,int right){ if(left==right){ return; } int mid=(left+right)/2; //左部分归并排序 mergesort(arr, left, mid); //右部分归并排序 mergesort(arr, mid+1, right); merge(arr,left,right,mid); } int main(int argc, const char * argv[]) { int arr[20]={6,7,17,18,19,8,9,14,15,1,2,3,10,11,12,13,4,5,16,20}; int len=20; //归并排序 mergesort(arr,0,len-1); for(int i = 0; i \u003c len; i++){ cout \u003c\u003c arr[i] \u003c\u003c \" \"; } cout \u003c\u003c endl; return 0; } ","date":"2022-03-29","objectID":"/sorting_algorithm/:7:0","tags":["algorithm","sort"],"title":"📝排序算法","uri":"/sorting_algorithm/"},{"categories":["Algorithm"],"content":"6. 快速排序 算法思想:挖坑填数,从右往左找到比key大的填到i位置,i++;从左往右找到比key小的填到j位置,j–;循环找到i=j的位置,将key填到i的位置。再通过递归,分左边和右边,条件是l\u003cr; 算法分析: 快速排序在最坏情况下的时间复杂度和冒泡排序一样,是 O(n2),实际上每次比较都需要交换,但是这种情况并不常见。我们可以思考一下如果每次比较都需要交换,那么数列的平均时间复杂度是 O(nlogn),事实上在大多数时候,排序的速度要快于这个平均时间复杂度。这种算法实际上是一种分治法思想,也就是分而治之,把问题分为一个个的小部分来分别解决,再把结果组合起来。 快速排序只是使用数组原本的空间进行排序,所以所占用的空间应该是常量级的,但是由于每次划分之后是递归调用,所以递归调用在运行的过程中会消耗一定的空间,在一般情况下的空间复杂度为 O(nlogn), int GetIndex(int *arr,int left,int right){ int i=left; int key=arr[i]; int j=right; while(i\u003cj){ while(i\u003cj\u0026\u0026arr[j]\u003ekey){ j--; } if(i\u003cj){ arr[i]=arr[j]; i++; } while(i\u003cj\u0026\u0026arr[i]\u003c=key){ i++; } if(i\u003cj){ arr[j]=arr[i]; j--; } } //此时i=j; arr[i]=key; return i; } void QuickSort(int *arr,int left,int right){ if(left\u003cright){ //找到key放在哪个下标位置 index int index=GetIndex(arr,left,right); QuickSort(arr, left, index); QuickSort(arr, index+1, right); } } int main(int argc, const char * argv[]) { int arr[20]={6,7,17,18,19,8,9,14,15,1,2,3,10,11,12,13,4,5,16,20}; int len=20; //快速排序 QuickSort(arr,0,len-1); for(int i = 0; i \u003c len; i++){ cout \u003c\u003c arr[i] \u003c\u003c \" \"; } cout \u003c\u003c endl; return 0; } ","date":"2022-03-29","objectID":"/sorting_algorithm/:8:0","tags":["algorithm","sort"],"title":"📝排序算法","uri":"/sorting_algorithm/"},{"categories":["Algorithm"],"content":"7. 堆排序 算法思想:堆的结构类似于完全二叉树,且每个节点的值都大于节点的左右子节点。将排好序的大顶堆的结点值与堆末尾交换,堆的长度减一。 算法分析:堆排序是一种选择排序,整体主要由构建初始堆+交换堆顶元素和末尾元素并重建堆两部分组成。其中构建初始堆经推导复杂度为O(n),在交换并重建堆的过程中,需交换n-1次,而重建堆的过程中,根据完全二叉树的性质,[log2(n-1),log2(n-2)…1]逐步递减,近似为nlogn。所以堆排序时间复杂度一般认为就是O(nlogn)级。最好、最坏和平均都为O(nlogn)。算法不稳定 void CreateHeap(vector\u003cint\u003e \u0026arr,int len,int index){ int node=index; int left=2*index+1;//左子节点 int right=2*index+2;//右子节点 if(left\u003clen\u0026\u0026arr[left]\u003earr[index]) index=left; if(right\u003clen\u0026\u0026arr[right]\u003earr[index])index=right; if(index!=node){ swap(arr[index],arr[node]); //再往下遍历是否有子树可以重新排序 CreateHeap(arr, len, index); } } void HeapSort(vector\u003cint\u003e \u0026arr,int len){ //创建大根堆,从最后一个非叶子结点开始 for(int i=len/2-1;i\u003e=0;i--){ CreateHeap(arr,len,i); } //将堆顶最大数放在数组末尾 for(int i=len-1;i\u003e=1;i--){ swap(arr[i],arr[0]); //重新把堆排序 CreateHeap(arr, i, 0); } } int main(int argc, const char * argv[]) { vector\u003cint\u003e v={3,1,9,7,5}; int size=v.size(); for(int i=0;i\u003csize;i++) { cout\u003c\u003cv[i]\u003c\u003c\" \"; } cout \u003c\u003c endl; return 0; } ","date":"2022-03-29","objectID":"/sorting_algorithm/:9:0","tags":["algorithm","sort"],"title":"📝排序算法","uri":"/sorting_algorithm/"},{"categories":["Algorithm"],"content":"8.计数排序 核心思想在于将输入的数值转化为键值转存在开辟的另一个数组空间中。 算法分析: 计数排序是一个稳定的排序算法。当输入的元素是 n 个 0到 k 之间的整数时,时间复杂度是O(n+k),空间复杂度也是O(n+k),其排序速度快于任何比较排序算法。当k不是很大并且序列比较集中时,计数排序是一个很有效的排序算法。 关于空间复杂度可能为O(k)也可能是O(n+k),关键在于你最后排好序的数组重新赋值原数组还是新创建的数组。ps:我用的方法的空间复杂度为O(k)。 void CountingSort(vector\u003cint\u003e\u0026 v,int maxId,int len){ vector\u003cint\u003e temp; temp.reserve(maxId+1);//预先使数组保留maxid的空间 temp.assign(maxId+1, 0);//使数组值初始化为0 for(int i=0;i\u003clen;i++){ temp[v[i]]++; } //将temp中的数搬到原数组中 int j=0; for(int i=0;i\u003cmaxId+1;i++){ while(temp[i]){ v[j]=i; j++; temp[i]--; } } } int main(int argc, const char * argv[]) { vector\u003cint\u003e v={3,1,9,7,5,0,3}; int size=v.size(); int maxNum=9; CountingSort(v,maxNum,size); for(int i=0;i\u003csize;i++) { cout\u003c\u003cv[i]\u003c\u003c\" \"; } cout \u003c\u003c endl; return 0; ","date":"2022-03-29","objectID":"/sorting_algorithm/:10:0","tags":["algorithm","sort"],"title":"📝排序算法","uri":"/sorting_algorithm/"},{"categories":["Algorithm"],"content":"9.桶排序 算法思想:计数排序的升级,把数据分组映射到每个桶中,再将桶中的数据按照其它排序方式比如插入、查找等排序方式 桶排序最好情况下使用线性时间O(n),桶排序的时间复杂度,取决与对各个桶之间数据进行排序的时间复杂度,因为其它部分的时间复杂度都为O(n)。很显然,桶划分的越小,各个桶之间的数据越少,排序所用的时间也会越少。但相应的空间消耗就会增大。 void bucketSort(vector\u003cint\u003e\u0026 v){ //找出数组v中的最大和最小值 int minNum=v[0]; for(int i=1;i\u003cv.size();i++){ minNum=minNum\u003cv[i]?minNum:v[i]; } int maxNum=v[0]; for(int i=1;i\u003cv.size();i++){ maxNum=maxNum\u003ev[i]?maxNum:v[i]; } //创建桶数组,并设置桶数组的长度 map\u003cint,vector\u003cint\u003e\u003ebucket; //设置为4组,每组中最多装3个数 int bucketNum=3; //将v中数据分别存在4组中 for(int i=0;i\u003cv.size();i++){ int group=(v[i]-minNum)/bucketNum;//分配到第几组 bucket[group].push_back(v[i]); } //分别排序每个桶中的数 int m=0; for(int i=0;i\u003cbucket.size();i++){ //每个桶中的数用堆排序 HeapSort(bucket[i],bucket[i].size()); for(int j=0;j\u003cbucket[i].size();j++){ v[m++]=bucket[i][j]; } } } int main(int argc, const char * argv[]) { vector\u003cint\u003e v={3,1,9,7,5,0,3}; // int size=v.size(); bucketSort(v); for(int i=0;i\u003cv.size();i++) { cout\u003c\u003cv[i]\u003c\u003c\" \"; } cout \u003c\u003c endl; return 0; } ","date":"2022-03-29","objectID":"/sorting_algorithm/:11:0","tags":["algorithm","sort"],"title":"📝排序算法","uri":"/sorting_algorithm/"},{"categories":["Algorithm"],"content":"10. 基数排序 算法思想:有编号为0-9的十个桶,先将要排的数的个位按数分别放桶里,然后依次拿出来,再按十位的数字分别放桶里,按百位….,最后拿出来的就是已经排好序的啦。 稳定的算法,但基数排序的性能比桶排序要略差,每一次关键字的桶分配都需要O(n)的时间复杂度,而且分配之后得到新的关键字序列又需要O(n)的时间复杂度。假如待排数据可以分为d个关键字,则基数排序的时间复杂度将是O(d*2n) ,当然d要远远小于n,因此基本上还是线性级别的。即时间复杂度O(n). 基数排序的空间复杂度为O(n+k),其中k为桶的数量。 void radixSort(vector\u003cint\u003e\u0026 v){ int index=3;//代表最高位是个位/十位/百位...(3代表 最高位是百位) //创建桶数组,共0-9编号十个桶 map\u003cint,vector\u003cint\u003e\u003ebucket; int mod=10; int dev=1; int count=0; //从个位起装进桶,装到第index位 for(int x=0;x\u003cindex;x++){ for(int i=0;i\u003cv.size();i++){ bucket[v[i]%mod/dev].push_back(v[i]); } //将按位排好序的拿出来放在v[]数组里 for(int i=0;i\u003c10;i++){ for(int j=0;j\u003cbucket[i].size();j++){ // cout\u003c\u003c\"bucket:\"\u003c\u003cbucket[i][j]\u003c\u003c\" \"; v[count++]=bucket[i][j]; // cout\u003c\u003c\"v:\"\u003c\u003cv[count-1]\u003c\u003c\" \"; } } //变化dev和mod以改变下一次的按位排序 dev*= 10; mod*= 10; //v数组下标初始化 count=0; //bucke数组清空 bucket.clear(); } } int main(int argc, const char * argv[]) { vector\u003cint\u003e v={73, 22, 93, 43, 55, 14, 28, 65, 39, 181}; // int size=v.size(); radixSort(v); for(int i=0;i\u003cv.size();i++) { cout\u003c\u003cv[i]\u003c\u003c\" \"; } cout \u003c\u003c endl; return 0; } ","date":"2022-03-29","objectID":"/sorting_algorithm/:12:0","tags":["algorithm","sort"],"title":"📝排序算法","uri":"/sorting_algorithm/"},{"categories":["Algorithm"],"content":"参考链接 https://www.cnblogs.com/onepixel/articles/7674659.html ","date":"2022-03-29","objectID":"/sorting_algorithm/:13:0","tags":["algorithm","sort"],"title":"📝排序算法","uri":"/sorting_algorithm/"},{"categories":["Notes"],"content":"简介,一句话描述 正文 图片插入用shortcode: xxxx\" xxxx ","date":"2022-03-18","objectID":"/some_interviews/:0:0","tags":[""],"title":"Some_interviews","uri":"/some_interviews/"},{"categories":["Games"],"content":"「持续按键子弹间隔时间发射」「随机血条」「开始和结束界面」「在最后一个场景中得到前面场景对象的函数/值」「陨石坠落」 ","date":"2022-02-22","objectID":"/planefire_4/:0:0","tags":["C# ","Unity","PlaneFire"],"title":"PlaneFire_4","uri":"/planefire_4/"},{"categories":["Games"],"content":"一、实现效果 开始界面: 游戏界面: 结束界面 ","date":"2022-02-22","objectID":"/planefire_4/:1:0","tags":["C# ","Unity","PlaneFire"],"title":"PlaneFire_4","uri":"/planefire_4/"},{"categories":["Games"],"content":"二、关键技术实现 ","date":"2022-02-22","objectID":"/planefire_4/:2:0","tags":["C# ","Unity","PlaneFire"],"title":"PlaneFire_4","uri":"/planefire_4/"},{"categories":["Games"],"content":"1、 持续按键,但子弹不连续发射,而是间隔时间发射 主要思路是在按下按键时,设置一个定时器 public class MyJet : MonoBehaviour { public float deltaT = 0.5f;//设置一个你理想状态的子弹发射间隔时间 private float InvokeTime; // Start is called before the first frame update void Start() { InvokeTime = deltaT;//初始化 } // Update is called once per frame void Update() { if (Input.GetKey(KeyCode.Space)) { InvokeTime += Time.deltaTime;//检查到空格键一直按下的时候会不断累加 if(InvokeTime\u003edeltaT)//当累加到大于deltaT时便执行子弹发射 { Fire();//发射子弹的函数,可替换 InvokeTime = 0; } } if(Input.GetKeyUp(KeyCode.Space)) { InvokeTime = deltaT;//当空格键抬起时,将invokTime恢复原来值 } } } ","date":"2022-02-22","objectID":"/planefire_4/:2:1","tags":["C# ","Unity","PlaneFire"],"title":"PlaneFire_4","uri":"/planefire_4/"},{"categories":["Games"],"content":"2、设置一个随机血条,不同血条代表的分值不同,且当子弹击中怪物时,血条会逐减。 ![在这里插入图片描述](https://img-blog.csdnimg.cn/24c7374decf843de840dac10babc11bc.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAWnpf56iL5bqP5aqb,size_14,color_FFFFFF,t_70,g_se,x_16#pic_center =200x250) 关键点1、找到/制作两个可重合的血条(白色背景,红色前景) 关键点2、将两个血条的中心轴定位在血条的==左边缘==(pivot=center Left) 关键点3、将两个血条与原本怪物形成父子关系,并整体制作为预制体 代码实现: public class Monster : MonoBehaviour { public float speed = 1.0f; public int value; // 价值 (1-5), 击杀此怪可以获得的加分 public int hp; // 血量, 初始值为value // hp/value节点 private Transform hpNode; private Transform game; // Start is called before the first frame update void Start() { // 节点名称 this.name = \"Monster\"; game = GameObject.Find(\"主控脚本\").transform; // 怪物的价值:1-5之间,随机指定 value = Random.Range(0, 5) + 1; hp = value; // 血条显示 hpNode = transform.Find(\"hp/value\"); SetHealth(hp); } // Update is called once per frame void Update() { float dy = speed * Time.deltaTime; transform.Translate(0, -dy, 0, Space.Self); Vector3 sp = Camera.main.WorldToScreenPoint(transform.position); if (sp.y\u003c 0) { Destroy(this.gameObject); } } // 更新HP显示 public void SetHealth(int hpValue) { hpNode.localScale = new Vector3(hpValue / 5f, 1, 1);//x轴上的比例缩小,与中心轴点位置有关,所以一定要设置好图片的中心点轴位置靠左,这样比例缩小时才会是那种效果 } private void OnTriggerEnter2D(Collider2D collision) { if (collision.name.Equals(\"Bullet\")) { // 被子弹击中,HP-1 hp -= 1; Destroy(collision.gameObject); // 更新HP显示 SetHealth(hp); // HP降为0时,自动销毁此怪 if (hp \u003c= 0) { Destroy(this.gameObject); // 玩有得分 + this.value,这是传给在另外一个类的方法value的值。 game.SendMessage(\"AddScore\", this.value); } } } } ","date":"2022-02-22","objectID":"/planefire_4/:2:2","tags":["C# ","Unity","PlaneFire"],"title":"PlaneFire_4","uri":"/planefire_4/"},{"categories":["Games"],"content":"3、设置开始和结束界面 开始和结束的场景需要创建 设置场景顺序(Fire-\u003eBuildSetting-\u003e'+') 分别在开始和结束的场景中加入canvas ==跳转函数==: UnityEngine.SceneManagement.SceneManager.LoadScene(0); //0代表场景顺序 ","date":"2022-02-22","objectID":"/planefire_4/:2:3","tags":["C# ","Unity","PlaneFire"],"title":"PlaneFire_4","uri":"/planefire_4/"},{"categories":["Games"],"content":"4、如何在最后一个场景中得到前面场景对象的函数/值 我们知道,如果场景切换到下一个场景后,之前的场景对象会被销毁 但是,若是我们想要得到前一个场景的值,需要保留上一个对象 ==永久保留对象的函数==: void Start() { DontDestroyOnLoad(this.gameObject); } ","date":"2022-02-22","objectID":"/planefire_4/:2:4","tags":["C# ","Unity","PlaneFire"],"title":"PlaneFire_4","uri":"/planefire_4/"},{"categories":["Games"],"content":"5、实现陨石坠落 方法和前面的方法雷同,使陨石成为一个刚体、碰撞体、预制体,且挂一个脚本控制它的飞行方向、飞行速度和销毁事件,若飞机与陨石相撞、游戏结束。 ","date":"2022-02-22","objectID":"/planefire_4/:2:5","tags":["C# ","Unity","PlaneFire"],"title":"PlaneFire_4","uri":"/planefire_4/"},{"categories":["Games"],"content":"学习第三章 ","date":"2022-02-22","objectID":"/planefire_3/:0:0","tags":[" C# ","Unity","PlaneFire"],"title":"PlaneFire_3","uri":"/planefire_3/"},{"categories":["Games"],"content":"一、实现效果 ","date":"2022-02-22","objectID":"/planefire_3/:1:0","tags":[" C# ","Unity","PlaneFire"],"title":"PlaneFire_3","uri":"/planefire_3/"},{"categories":["Games"],"content":"功能实现 背景图片连续不断向下平移 有背景音乐,并能通过点击星星✨来控制暂停/播放 子弹发射时会有音效 添加UI组件(image和text)实现击中目标时,得分加1 ","date":"2022-02-22","objectID":"/planefire_3/:1:1","tags":[" C# ","Unity","PlaneFire"],"title":"PlaneFire_3","uri":"/planefire_3/"},{"categories":["Games"],"content":"二、知识点学习 ","date":"2022-02-22","objectID":"/planefire_3/:2:0","tags":[" C# ","Unity","PlaneFire"],"title":"PlaneFire_3","uri":"/planefire_3/"},{"categories":["Games"],"content":"1、将图片设置为固定宽度(同比缩放) Sprite sprite = this.images[index]; float imageWidth = sprite.rect.width;//获取图像的实际宽度 float scale = 100 / imageWidth;//设置为100像素,缩放比例 monster.transform.localScale = new Vector3(scale, scale, scale); ","date":"2022-02-22","objectID":"/planefire_3/:2:1","tags":[" C# ","Unity","PlaneFire"],"title":"PlaneFire_3","uri":"/planefire_3/"},{"categories":["Games"],"content":"2、音频播放 AudioSource:声音源 AudioListener:接收者 添加音频:在一个组件上添加Audio Source组件 音频的代码播放(常用API): clip(选哪首)/mute(静音)/loop(循环)/volume(音量)/isPlaying Play() Stop() Pause() PlayOneShot(clip)//新开一个播放 延时调用API Invoke(“方法名称”,delay);//延时调用方法 CancelInvoke(“name”)//取消延时调用 isInvoking(“name”)//判断是否正在调用 InvokeRepeating(“name”, delay,interval)//延时重复调用 ","date":"2022-02-22","objectID":"/planefire_3/:2:2","tags":[" C# ","Unity","PlaneFire"],"title":"PlaneFire_3","uri":"/planefire_3/"},{"categories":["Games"],"content":"3、 消息调用(传给其他类的方法的值) GameObject main=GameObject.Find(“游戏主控”); main.SendMessage(“AddScore”,1); ","date":"2022-02-22","objectID":"/planefire_3/:2:3","tags":[" C# ","Unity","PlaneFire"],"title":"PlaneFire_3","uri":"/planefire_3/"},{"categories":["Games"],"content":"4、UI事件 我所理解的UI是浮于屏幕表面的按键,即与游戏画面不在同一个平面上 添加控件:添加UI-\u003eCanvas,右键点击canvas可添加text/image等不同属性UI控件 ","date":"2022-02-22","objectID":"/planefire_3/:2:4","tags":[" C# ","Unity","PlaneFire"],"title":"PlaneFire_3","uri":"/planefire_3/"},{"categories":["Games"],"content":"三、代码实现 背景图片动态移动: public class BackgroundCtrl : MonoBehaviour { Transform bg1; Transform bg2; public float speed = 1.0f; // Start is called before the first frame update void Start() { bg1 = GameObject.Find(\"背景/bg1\").transform; bg2 = GameObject.Find(\"背景/bg2\").transform; bg1.position = new Vector3(0, 0, 0); bg2.position = new Vector3(0, 10, 0); } // Update is called once per frame void Update() { float dy = speed * Time.deltaTime; bg1.Translate(0, -dy, 0); bg2.Translate(0, -dy, 0); if(bg1.position.y\u003c-10) { bg1.position = new Vector3(0, 10, 0); } if (bg2.position.y \u003c -10) { bg2.position = new Vector3(0, 10, 0); } } } 背景音乐: void Update() { if(Input.GetMouseButtonDown(0)) { Vector3 mousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition);//获取鼠标坐标 mousePos.z = 0; float distance = (mousePos - transform.position).magnitude;//计算星星坐标与鼠标的距离 if (distance \u003c 0.5) { AudioSource audio = GetComponent\u003cAudioSource\u003e();//获取音乐组件 if (audio.isPlaying) { audio.Pause(); } else { audio.Play(); } } } } 子弹音效 AudioSource audio = GetComponent\u003cAudioSource\u003e();//获取音乐组件 audio.PlayOneShot(audio.clip); UI和得分 public void AddScore(int value) { score += value; scoreText.text = \"得分:\" + score; Debug.Log(\"当前得分:\" + score); } GameObject Monster = GameObject.Find(\"主控脚本\"); Monster.SendMessage(\"AddScore\", 1); ","date":"2022-02-22","objectID":"/planefire_3/:3:0","tags":[" C# ","Unity","PlaneFire"],"title":"PlaneFire_3","uri":"/planefire_3/"},{"categories":["Games"],"content":"四、心得 都学到这了,最有体会的一点就是unity实在是太强大太方便了哈哈哈,除了刚上手时有一些组件就会记不住,但是用顺手后还是非常方便的,还有学这个一定得做好文件管理,不要乱糟糟的,不然之后找文件的时候会头昏眼花。 ","date":"2022-02-22","objectID":"/planefire_3/:4:0","tags":[" C# ","Unity","PlaneFire"],"title":"PlaneFire_3","uri":"/planefire_3/"},{"categories":["Games"],"content":"学习第二篇 ","date":"2022-02-22","objectID":"/planefire_2/:0:0","tags":["C#","Unity","PlaneFire"],"title":"PlaneFire_2","uri":"/planefire_2/"},{"categories":["Games"],"content":"一、实现效果 ","date":"2022-02-22","objectID":"/planefire_2/:1:0","tags":["C#","Unity","PlaneFire"],"title":"PlaneFire_2","uri":"/planefire_2/"},{"categories":["Games"],"content":"效果图 ","date":"2022-02-22","objectID":"/planefire_2/:1:1","tags":["C#","Unity","PlaneFire"],"title":"PlaneFire_2","uri":"/planefire_2/"},{"categories":["Games"],"content":"功能列表 飞机定时无限发射 左右键控制飞机左右移动 随机生成怪物,怪物有不同 子弹射中怪物后,子弹和怪物一起被摧毁 ","date":"2022-02-22","objectID":"/planefire_2/:1:2","tags":["C#","Unity","PlaneFire"],"title":"PlaneFire_2","uri":"/planefire_2/"},{"categories":["Games"],"content":"二、知识点 ","date":"2022-02-22","objectID":"/planefire_2/:2:0","tags":["C#","Unity","PlaneFire"],"title":"PlaneFire_2","uri":"/planefire_2/"},{"categories":["Games"],"content":"刚体 Dynamic 普通刚体 有质量 Static 静态刚体 质量无穷(地面) Kinematic 运动学刚体 质量为0(物理规律不起作用,一般用于碰撞检测) 设置刚体:Physics 2D -\u003eRigidBody 2D -\u003e Body Type -\u003e Dynamic/Static/Kinematic ","date":"2022-02-22","objectID":"/planefire_2/:2:1","tags":["C#","Unity","PlaneFire"],"title":"PlaneFire_2","uri":"/planefire_2/"},{"categories":["Games"],"content":"碰撞体 Physics 2D -\u003e Box/Circle Collider 2D -\u003e is Trigger ","date":"2022-02-22","objectID":"/planefire_2/:2:2","tags":["C#","Unity","PlaneFire"],"title":"PlaneFire_2","uri":"/planefire_2/"},{"categories":["Games"],"content":"碰撞检测 设置可碰撞:添加碰撞组件Physics 2D -\u003eBox Collider2D-\u003eis Trigger 打勾 添加碰撞检测函数(例): private void OnTriggerEnter2D(Collider2D collision){Debug.Log(\"检测到了碰撞\");} Collider2D collision表示对方的碰撞组件 collision.gameObject——对方 collision.name——对方节点名称 collision.tag —— 对方的节点的Tag collision.transform——对方的Transform组件 碰撞事件回调: 1、OnTriggerEnter2D,两碰撞体开始相遇 2、OnTriggerStay2D,两碰撞体接触中 3、OnTriggerExit2D,两碰撞体离开 ","date":"2022-02-22","objectID":"/planefire_2/:2:3","tags":["C#","Unity","PlaneFire"],"title":"PlaneFire_2","uri":"/planefire_2/"},{"categories":["Games"],"content":"随机数 value=Random.Range(min,max); eg:float x=Random.Range(-2,2); ","date":"2022-02-22","objectID":"/planefire_2/:2:4","tags":["C#","Unity","PlaneFire"],"title":"PlaneFire_2","uri":"/planefire_2/"},{"categories":["Games"],"content":"定时器 InvokeRepeating(method, delay,interval);//delay 多长时间后执行第一次,interval 几秒钟重复一次方法调用 eg:InvokeRepeating(“CreatePig”,0.1f ,2f); ","date":"2022-02-22","objectID":"/planefire_2/:2:5","tags":["C#","Unity","PlaneFire"],"title":"PlaneFire_2","uri":"/planefire_2/"},{"categories":["Games"],"content":"随机选择图片 int index = Random.Range(0, images.Length); SpriteRenderer renderer = monster.GetComponent(); renderer.sprite = this.images[index]; ","date":"2022-02-22","objectID":"/planefire_2/:2:6","tags":["C#","Unity","PlaneFire"],"title":"PlaneFire_2","uri":"/planefire_2/"},{"categories":["Games"],"content":"三、代码实现 MyGame.cs using System.Collections; using System.Collections.Generic; using UnityEngine; public class MyGame : MonoBehaviour { // Start is called before the first frame update void Start() { Application.targetFrameRate = 60; } // Update is called once per frame void Update() { } } MyBullet.cs using System.Collections; using System.Collections.Generic; using UnityEngine; public class MyBullet : MonoBehaviour { public float speed = 2.0f; // Start is called before the first frame update void Start() { } // Update is called once per frame void Update() { float dy = speed * Time.deltaTime; transform.Translate(0, dy, 0, Space.Self); Vector3 sp = Camera.main.WorldToScreenPoint(transform.position); if(sp.y\u003eScreen.height) { Destroy(this.gameObject); } } private void OnTriggerEnter2D(Collider2D collision) { if(collision.tag.Equals(\"Monster\")) { Destroy(collision.gameObject); Destroy(this.gameObject); } } } MyJet.cs using System.Collections; using System.Collections.Generic; using UnityEngine; public class MyJet : MonoBehaviour { public float speed = 2.0f; public GameObject bulletPrefab; private float count = 0; private float interval = 0.4f; // Start is called before the first frame update void Start() { } // Update is called once per frame void Update() { count += Time.deltaTime; if(count \u003einterval ) { count = 0; Fire(); } float step = speed * Time.deltaTime; if(Input.GetKey(KeyCode.LeftArrow)) { transform.Translate(-step, 0, 0); } if (Input.GetKey(KeyCode.RightArrow)) { transform.Translate(step, 0, 0); } } void Fire() { Vector3 pos = transform.position + new Vector3(0, 1, 0); GameObject bullet = Instantiate(bulletPrefab, pos, transform.rotation); } } Monster.cs using System.Collections; using System.Collections.Generic; using UnityEngine; public class Monster : MonoBehaviour { public float speed = 1.0f; // Start is called before the first frame update void Start() { } // Update is called once per frame void Update() { float dy = speed * Time.deltaTime; transform.Translate(0, -dy, 0, Space.Self); Vector3 sp = Camera.main.WorldToScreenPoint(transform.position); if (-sp.y*2 \u003e Screen.height) { Destroy(this.gameObject); } } } MonsterCtrl.cs using System.Collections; using System.Collections.Generic; using UnityEngine; public class MonsterCtrl : MonoBehaviour { public GameObject MonsterPrefab; public Sprite[] images; // Start is called before the first frame update void Start() { InvokeRepeating(\"CreateMonster\", 0.1f, 2f); } // Update is called once per frame void Update() { } 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);//6张图片 SpriteRenderer renderer = monster.GetComponent\u003cSpriteRenderer\u003e(); renderer.sprite = this.images[index]; } } ","date":"2022-02-22","objectID":"/planefire_2/:3:0","tags":["C#","Unity","PlaneFire"],"title":"PlaneFire_2","uri":"/planefire_2/"},{"categories":["Games"],"content":"四、遇到了一个非常头疼的问题 报错: The object of type ‘xxx’ has been destroyed but you are still trying to access it 但我的逻辑明明没有错误,也是照着老师的步骤来的,怎么会在摧毁上一个预制体后不能成功创建下一个预制体呢?????然后搜索了很多,怀疑是因为程序调用的时候虽然逻辑上预制体被摧毁了,但其实程序滞留,在创建预制体时访问的是上一个空的预制体,所以访问不了。woc,这啥呀这是,关键是人家老师为啥能行,我就不行,我电脑可是apple M1啊,按道理cpu速度也是行的啊😭😭😭 皆大欢喜!!!!👏👏👏😃😃😃解决了!!! 我是sabi叭,完全是因为预制体移错了,贴图警告⚠️ 不知道为什么,当路径二的怪物变蓝后我就自认为它就变成预制体了,但其实并不是,只能从Prefab的文件夹中移出,也不能这样说,总的来说是要看准到底谁是预制体。 ","date":"2022-02-22","objectID":"/planefire_2/:4:0","tags":["C#","Unity","PlaneFire"],"title":"PlaneFire_2","uri":"/planefire_2/"},{"categories":["Games"],"content":"学习第一篇 ","date":"2022-02-22","objectID":"/planefire_1/:0:0","tags":["c#","Unity","PlaneFire"],"title":"PlaneFire_1","uri":"/planefire_1/"},{"categories":["Games"],"content":"一、效果图 大概的效果图如下,实现的功能:😁通过点击鼠标飞机会射出子弹,若子弹超出屏幕外会自动销毁。 ","date":"2022-02-22","objectID":"/planefire_1/:1:0","tags":["c#","Unity","PlaneFire"],"title":"PlaneFire_1","uri":"/planefire_1/"},{"categories":["Games"],"content":"二、功能实现 ","date":"2022-02-22","objectID":"/planefire_1/:2:0","tags":["c#","Unity","PlaneFire"],"title":"PlaneFire_1","uri":"/planefire_1/"},{"categories":["Games"],"content":"1、场景对象 【Main Camera】 代表主摄像机,需要这个我们才能看到如今这个视角 【游戏主控】 上面不挂载任何图片对象,但挂了一个c#文件,主要用来全局设置。比如这个项目所涉及到的时间针率。 3.【子弹】 这个子弹可惜忽略,主要是通过它来创建子弹预制体 4.【子弹Prefab】此对象需要挂MyBullet,特别提醒需要在【飞机】脚本下挂它 5.【飞机】 挂MyJet ","date":"2022-02-22","objectID":"/planefire_1/:3:0","tags":["c#","Unity","PlaneFire"],"title":"PlaneFire_1","uri":"/planefire_1/"},{"categories":["Games"],"content":"2、代码实现 游戏主控—MyGame代码 using System.Collections; using System.Collections.Generic; using UnityEngine; public class MyGame : MonoBehaviour { // Start is called before the first frame update void Start() { //设置帧率为60 //Application:应用 //target:目标 //Frame:帧 //Rate:率 Application.targetFrameRate= 60; } // Update is called once per frame void Update() { } } 子弹Prefab—MyBullet代码 using System.Collections; using System.Collections.Generic; using UnityEngine; public class MyBullet : MonoBehaviour { //在inspector中会出现【speed】,并且可以自由设置参数 public float speed = 5.5f; // Start is called before the first frame update void Start() { } // Update is called once per frame void Update() { //设置每刷新一次y轴的坐标,y轴坐标随时间增加,即表现为向上的运动 float dy = speed * Time.deltaTime; //将空间坐标翻译赋给transform transform.Translate(0, dy, 0, Space.Self); //创建对象(而这个对象的坐标由transform提供)表现形式由主摄像机的世界坐标表示 Vector3 sp = Camera.main.WorldToScreenPoint(transform.position); //如果对象的y坐标超过屏幕的高度则被摧毁 if (sp.y \u003e Screen.height) { Destroy(this.gameObject); } } } 飞机—MyJet代码 using System.Collections; using System.Collections.Generic; using UnityEngine; public class MyJet : MonoBehaviour { public GameObject myPrefab; private float interval = 0.4f; private float count = 0; // Start is called before the first frame update void Start() { } // Update is called once per frame void Update() { //如果鼠标按下,就射出子弹 if(Input.GetMouseButtonDown(0)) { Fire(); } } private void Fire() { //创建预制体 //Instantiate:示例 GameObject bullet = Instantiate(myPrefab); //子弹的位置=飞机所在位置的y轴加1 bullet.transform.position = transform.position + new Vector3(0, 1f, 0); } } ","date":"2022-02-22","objectID":"/planefire_1/:4:0","tags":["c#","Unity","PlaneFire"],"title":"PlaneFire_1","uri":"/planefire_1/"},{"categories":["Games"],"content":"三、总结 现在我只是刚开始,给自己打气遇到挫折难题不要放弃,踏踏实实学习,一定会在毕业前修炼成大师的! 对的,这个游戏还没结束,下一章会继续写键盘控制飞机、子弹击中敌人,敌人消失。😊😊😊😊下一章见! ","date":"2022-02-22","objectID":"/planefire_1/:5:0","tags":["c#","Unity","PlaneFire"],"title":"PlaneFire_1","uri":"/planefire_1/"},{"categories":["Algorithm"],"content":"给你一个按非递减顺序排序的整数数组 nums,返回每个数字的平方组成的新数组,要求也按非递减顺序排序。 示例 1: 输入:nums = [-4,-1,0,3,10] 输出:[0,1,9,16,100] 解释:平方后,数组变为 [16,1,0,9,100] 排序后,数组变为 [0,1,9,16,100] 示例 2: 输入:nums = [-7,-3,2,3,11] 输出:[4,9,9,49,121] 提示: 1 \u003c= nums.length \u003c= 104 -104 \u003c= nums[i] \u003c= 104 nums 已按 非递减顺序 排序 进阶: 请你设计时间复杂度为 O(n) 的算法解决本问题 来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/squares-of-a-sorted-array ","date":"2022-01-26","objectID":"/algorithm_day1/:0:0","tags":["Cpp","sort"],"title":"【LeetCode】有序数组的平方","uri":"/algorithm_day1/"},{"categories":["Algorithm"],"content":"方法一:暴力破解法 (idea:找到一个数与其后面的数比较,若最小则对调) (缺点:时间复杂度O(n^2),过于复杂,且没有利用题目原本数组的升序条件,没能通过) class Solution { public: vector\u003cint\u003e sortedSquares(vector\u003cint\u003e\u0026 nums) { for(int i=0;i\u003cnums.size();i++) { nums[i]=nums[i]*nums[i]; } for(int j=0;j\u003cnums.size();j++) { for(int i=j+1;i\u003cnums.size();i++) { int trans; if(nums[i]\u003cnums[j]) { trans=nums[i]; nums[i]=nums[j]; nums[j]=trans; } } } return nums; } }; 结果: ","date":"2022-01-26","objectID":"/algorithm_day1/:0:1","tags":["Cpp","sort"],"title":"【LeetCode】有序数组的平方","uri":"/algorithm_day1/"},{"categories":["Algorithm"],"content":"方法二:暴力破解法 (这个是官方的暴力破解,运用了相应的函数,和我上面写的思路是一样的,只不过我没有用封装函数,但这种方法能过,时间复杂度为O(nlogn)) class Solution { public: vector\u003cint\u003e sortedSquares(vector\u003cint\u003e\u0026 nums) { vector\u003cint\u003e ans; for(int num:nums) { ans.push_back(num*num); } sort(ans.begin(),ans.end()); return ans; } }; ","date":"2022-01-26","objectID":"/algorithm_day1/:0:2","tags":["Cpp","sort"],"title":"【LeetCode】有序数组的平方","uri":"/algorithm_day1/"},{"categories":["Algorithm"],"content":"方法三:双指针法 (如果我们使用两个指针分别指向位置0和位置n-1,每次比较选择更大的树逆序放于数组,就比如说位置0和位置n-1的数,因为题目条件,他们俩之间一定有一个最大的树可以放在新数组的最后面,依次类推) class Solution { public: vector\u003cint\u003e sortedSquares(vector\u003cint\u003e\u0026 nums) { int n=nums.size(); vector\u003cint\u003e ans(n); for(int left=0,right=n-1,p=n-1;left\u003c=right;) { if(nums[left]*nums[left]\u003cnums[right]*nums[right]) { ans[p]=nums[right]*nums[right]; right--; } else { ans[p]=nums[left]*nums[left]; left++; } p--; } return ans; } }; ","date":"2022-01-26","objectID":"/algorithm_day1/:0:3","tags":["Cpp","sort"],"title":"【LeetCode】有序数组的平方","uri":"/algorithm_day1/"}]