Swift游戏开发之俄罗斯方块:No.3 二维数组

来源:互联网 发布:淘宝网司法拍卖流程 编辑:程序博客网 时间:2024/04/29 04:49

数组是一个很有用的数据结构,很多程序都建立在数组之上,我很少看到哪个应用程序不用到数组的。

而我们的俄罗斯方块显然也需要数组,而且是更为特殊的  二维数组

为什么是二维数组呢?其实我们的整个游戏区域,可以看做是一个二维数组区域,就像这样:


我们的每一个block都占据在这样一个20x10的区域之内,20行,10列的一个200个block的二维数组让我们可以根据(x,y)坐标来确定block的位置,而这些blocks其实就是组成我们的L形,或者Z形 等等不同但我们都熟知的俄罗斯方块;

swift提供了我们array[index]供我们使用,但是我们还需要一个自定义的array2D[x,y]来更方便我们的使用,所以,让我们来自定义属于自己的二维数组吧!


确保你选中Swiftris文件夹,然后你可以选择⌘ + N 或者 File > New > File…或者左下角的“+”号 > File 来创建新的swift类,如下图所示:


选择Swift然后点击Next, 给你的新类起个名字吧,我们叫做Array2D,意为二维数组,然后我们点击create


在编辑区域会自动打开我们刚刚建好的Array2D 类,我们照着下图进行修改



准备好了,虽然这段代码不长,但是如果你是个swift的新手,这里面却有很多学问,基本上每行都有一个知识点需要学习!

友情提醒: 

-号的红色代码需要移除,+号的绿色代码需要手动加上,注释掉的#1,2,3,4 是便于给大家分段讲解,大家输入的时候可以不输入

subscript函数里面 array[(row * columns) + column] , 注意是row 而不是rows。如果你不小心写成了rows,那很显然,你的数组会越界。刚开始的时候我没有注意,就导致数组越界了。


#1

这里我们命名了一个叫做Array2D的class,在swift里面通常array是用struct 而不是用class,但是这里我们却需要一个class,因为在我们的程序里面,我们需要传引用(pass by reference),而不是传值(pass by value),class是传引用的,而struct是传值的

关于传值和传引用的区别如果学过C++的应该不难理解,我记得Effective c++这本书里面对这部分有很详尽的解释,google一下应该也能找到很多答案,这里我就不展开描述了。


想要查看更多关于swift的class相关知识,请点击这里


另外我们还看到,我们的class类型是<T>, 这里如果学过c++的应该也很容易理解,其实就是模板类了,T表示任意类型,可以是int,可以是string,可以是char等等等等;就是说,我们的这个array2D是一个通用的二维数组,你想在数组里面存任何都是可以的


#2

首先我们定义了一个传统的swift array,数组里面的类型和我们的二维数组类型一样 是<T>,但是我们注意到其实这里是<T?>,多了一个

上一章节我们已经介绍过了,它表示这是一个optional的变量,也就是说可以是nil,可以不包含任何数据,而在我们的面板上,如果数组里面是ni就表示这个地方不显示任何的block

接下来是定义我们自己的init函数,init一个二维数组需要两个参数,行数和列数,前两行代码很简单,把形参中的值传给实例化后的类的两个私有变量,而用来存储数据的数组,就得用到swift原生的array类来建立了。


#3

接下来是定义我们自己的init函数,init一个二维数组需要两个参数,行数和列数,前两行代码很简单,把形参中的值传给实例化后的类的两个私有变量,而用来存储数据的数组,就得用到swift原生的array类来建立了。这里重点讲解一下关于swift array的 init函数   init(cout: repeatedValue: )

先来看一下官方文档里面的描述:

init(count:repeatedValue:)    Constructs an array with a given number of elements, each initialized to the same value.    Declaration    init(count: Int, repeatedValue: T)  Discussion    The resulting array will have count elements in it, each initialized to the same value provided as the value for repeatedValue.    For example:    let numericArray = Array(count: 3, repeatedValue: 42)  // numericArray is [42, 42, 42]     let stringArray = Array(count: 2, repeatedValue: "Hello")  // stringArray is ["Hello", "Hello"]  

从举的两个例子来说,很容易理解了:count表示数组的大小,在我们的程序里面就是定义的rows * columns,在具体点就是20 * 10=200;

repeatedValue就表示初始化的值,这里表示所有的值都是一样的,当我第一次看到这个函数,因为教程里面写到的repeatedValue是nil,我以为是是否允许数组有重复的值呢。知道详细查阅了swift的官方文档后,才正确理解了这个函数的意思。

提醒大家:千万不能想当然,不能眼高手低,不清楚的事,一定要翻阅下资料弄清楚了。


关于更多关于swift的array知识,请点击这里


#4

这里我们其实是定义了二维数组的查找符号。swift的array里面 这样的定义其实就是array[index] 中的[], 同理,因为我们的二维数组也需要用到符号,来找到指定位置的内容,因为同时需要两个参数,row和column,所以,这个也需要我们自己来定义,那么我们来详细看一下这个subscript函数吧。

这里可能你有会感到疑虑,-> 又是个什么意思?  其实swift是个尽量把语言描述成我们看到它时想到的意思,比如前面的?和!,一个变量后面加上?就好像在问他,你是不是optional的啊?

!就好像在说,你不是一个optional!

这里的-> 其实就是这样的意思,->前面输入一些参数,然后->后面会返回一个什么类型的东西。那这个函数的意思就是,你给我输入两个参数,一个是column,一个是row,然后经过函数的计算以后,我返回给你一个T类型的值,当然,我也可能返回给你一个空,因为T是optional的 

还不是很明白,没关系,慢慢来,我们来看看官方文档里面,subscript函数的描述,请打开这个文档


Classes, structures, and enumerations can define subscripts, which are shortcuts for accessing the member elements of a collection, list, or sequence. You use subscripts to set and retrieve values by index without needing separate methods for setting and retrieval. For example, you access elements in an Array instance as someArray[index] and elements in a Dictionary instance as someDictionary[key].
Subscripts are not limited to a single dimension,and you can define subscripts with multiple input parameters to suit your custom type’s needs

看,我们就是定义了两个输入参数

Unlike instance methods, subscripts can be read-write or read-only. 

也就是说你可以定义subscript函数为读写或者只读模式,我们看到的同时有set和get的是读写模式,如果想要只读模式的,不写get关键字就是了:

As with read-only computed properties, you can drop the get keyword for read-only subscripts:subscript(index: Int) -> Int {    // return an appropriate subscript value here}


我们继续来看几个用到subscript函数的例子,其实定义是这样定义, 使用的时候,和普通的数组usage一样,一对“[]”就可以了

结构体

[plain] view plaincopy在CODE上查看代码片派生到我的代码片
  1. struct TimesTable {  
  2.     let multiplier: Int  
  3.     subscript(index: Int) -> Int {  
  4.         return multiplier * index  
  5.     }  
  6. }  
  7. let threeTimesTable = TimesTable(multiplier: 3)  
  8. println("six times three is \(threeTimesTable[6])")  
  9. // prints "six times three is 18"  
结构体的subscript函数就是返回输入的参数乘以结构体里的私有变量,所以也很容易理解举得例子的值了。

字典

[plain] view plaincopy在CODE上查看代码片派生到我的代码片
  1. var numberOfLegs = ["spider": 8, "ant": 6, "cat": 4]  
  2. numberOfLegs["bird"] = 2  
最后再来看一下官方文档里面给出的二维数组的源代码,是不是和教程里面的很像呢?

[plain] view plaincopy在CODE上查看代码片派生到我的代码片
  1. struct Matrix {  
  2.     let rows: Int, columns: Int  
  3.     var grid: [Double]  
  4.     init(rows: Int, columns: Int) {  
  5.         self.rows = rows  
  6.         self.columns = columns  
  7.         grid = Array(count: rows * columns, repeatedValue: 0.0)  
  8.     }  
  9.     func indexIsValidForRow(row: Int, column: Int) -> Bool {  
  10.         return row >= 0 && row < rows && column >= 0 && column < columns  
  11.     }  
  12.     subscript(row: Int, column: Int) -> Double {  
  13.         get {  
  14.             assert(indexIsValidForRow(row, column: column), "Index out of range")  
  15.             return grid[(row * columns) + column]  
  16.         }  
  17.         set {  
  18.             assert(indexIsValidForRow(row, column: column), "Index out of range")  
  19.             grid[(row * columns) + column] = newValue  
  20.         }  
  21.     }  
  22. }  

只不过这里的Matrix返回的是double类型的数,而我们的教程里面用到是模板T而已。看到这里应该不用再多解释,关于二维数组的所有内容都一目了然吧?



0 0
原创粉丝点击