上周在图书馆借了一本Swift语言实战入门,入个门玩一玩^_^正好这本书的后面有一个2048小游戏的实例,笔者跟着实战了一把。

差不多一周的时间,到今天,游戏的基本功能已基本实现,细节我已不打算继续完善,就这么整理一下过程中一些值得记录的点吧。

用的Swift版本是2.0,原书中的Swift版本会低一些,所以实践起来有些地方语法并不一样。

一、开始页面

Swift实战之2048小游戏 Swift实战之2048小游戏

在程序的第一张页面(Main.storyboard)上,只放了一个“开始游戏”按钮,点击按钮,弹出一个提示对话框,确认后,进入游戏页面。

1  @IBAction func startGame(sender: UIButton) {
2         let alerController = UIAlertController(title: "开始", message: "游戏就要开始,你准备好了吗?", preferredStyle: UIAlertControllerStyle.Alert)
3         alerController.addAction(UIAlertAction(title: "Ready Go!", style: UIAlertActionStyle.Default, handler: {
4             action in
5             self.presentViewController(MainTabViewController(), animated: true, completion: nil)
6         }))
7         self.presentViewController(alerController, animated: true, completion: nil)
8         
9     }

二、游戏页面

用一个TabViewController来实现,控制游戏页面(MainViewController)、设置页面(SettingViewController)和主题页面(KKColorListViewController)等三个页面的切换。 

 1 import UIKit
 2 
 3 class MainTabViewController: UITabBarController,KKColorListViewControllerDelegate {
 4     var viewMain = MainViewController()
 5     var viewColor = KKColorListViewController(schemeType:KKColorsSchemeType.Crayola)
 6     override func viewDidLoad() {
 7         super.viewDidLoad()
 8 
 9         // Do any additional setup after loading the view.
10         
11         viewMain.title = "2048"
12         let user=UserModel.sharedInstance().user
13         if let red = user?.red{
14             let uicolor=UIColor(red: red, green: (user?.green)!, blue: (user?.blue)!, alpha: (user?.alpha)!)
15             viewMain.view.backgroundColor=uicolor
16         }
17         
18         let viewSetting = SettingViewController()
19         viewSetting.title = "设置"
20         
21         viewColor.title="颜色"
22         viewColor.headerTitle="选择背景色"
23         viewColor.delegate=self
24         
25         let main = UINavigationController(rootViewController: viewMain)
26         let setting = UINavigationController(rootViewController: viewSetting)
27         let color = UINavigationController(rootViewController: viewColor)
28         
29         self.viewControllers = [main, setting,color]
30         self.selectedIndex = 0
31     }
32 
33     override func didReceiveMemoryWarning() {
34         super.didReceiveMemoryWarning()
35         // Dispose of any resources that can be recreated.
36     }
37     
38     func colorListController(controller: KKColorListViewController!, didSelectColor color: KKColor!) {
39         viewMain.view.backgroundColor = color.uiColor()
40         
41         UserModel.sharedInstance().saveColor(color.uiColor())
42         self.selectedIndex=0
43     }
44     
45     func colorListPickerDidComplete(controller: KKColorListViewController!) {
46         self.selectedIndex=0
47     }
48 
49 }

(一)主题页面

Swift实战之2048小游戏

 其中,主题页面直接使用GitHub上的一个开源项目KKColorListViewController,选中颜色后,改变游戏页面的背景色。

这个项目可以从GitHub直接下载,但这个项目是用Objective-C写的,所以添加到Swift项目中后,需要新建一个Bridge头文件,这个头文件需要保存在项目文件夹的根目录下,而不是项目文件夹里面的源码文件夹(否则,可能需要自己配置头文件的目录)

Swift实战之2048小游戏

1 #ifndef Bridging_Header_h
2 #define Bridging_Header_h
3 #import "KKColorListPicker.h"
4 
5 #endif /* Bridging_Header_h */

另外,添加到项目后,编译时还会有一些文件会报错,需要修改一些细节才能正常使用。

(1)KKColorsSchemeType.h中需添加

#import <Foundation/Foundation.h>

(2)KKColorListViewController中(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath方法,将最后一句注释掉。否则每次选完颜色,程序就会关闭当前的MainTabViewController而回到开始游戏页面。

1 - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
2 {
3     KKColor *color = self.colors[indexPath.row];
4     if (self.delegate) {
5         [self.delegate colorListController:self didSelectColor:color];
6     }
7 //    [self dismissViewControllerAnimated:YES completion:nil];
8 }

(二)游戏页面

 Swift实战之2048小游戏

1、ScoreView

游戏页面,上方有两个自定义的UIView,用于动态显示游戏分数。

Swift实战之2048小游戏  Swift实战之2048小游戏

 1 import UIKit
 2 
 3 enum ScoreType{
 4     case Common
 5     case Best
 6 }
 7 
 8 protocol ScoreViewProtocol{
 9     func changeScore(value s:Int)
10 }
11 
12 class ScoreView: UIView, ScoreViewProtocol {
13 
14     var label:UILabel!
15     let defaultFrame = CGRectMake(0, 0, 100, 30)
16     var stype:String!
17     
18     var score:Int = 0 {
19         didSet{
20             label.text = "\(stype):\(score)"
21         }
22     }
23     
24     init(stype: ScoreType){
25         super.init(frame: defaultFrame)
26         self.stype = (stype == ScoreType.Common ? "分数":"最高分")
27         
28         backgroundColor = UIColor.orangeColor()
29         label = UILabel(frame: defaultFrame)
30         label.textAlignment = NSTextAlignment.Center
31         label.font = UIFont(name: "微软雅黑", size: 16)
32         label.textColor = UIColor.whiteColor()
33         
34         self.addSubview(label)
35         
36         //布局约束
37         //必须将该属性值设置为false,否则自己设置的约束和AutoresizingMask生成的约束有冲突,运行时会产生异常
38         self.translatesAutoresizingMaskIntoConstraints = false
39         //宽度约束
40         self.widthAnchor.constraintEqualToConstant(100).active=true
41         //高度约束
42         self.heightAnchor.constraintEqualToConstant(30).active=true
43     }
44 
45     required init?(coder aDecoder: NSCoder) {
46         super.init(coder: aDecoder)
47     }
48     
49     func changeScore(value s: Int) {
50         self.score = s
51     }
52 
53 }

上面的ScoreView首先由一个ScoreType来选择显示“分数”还是“最高分”,然后有一个changeScore的方法,可以改变Score属性值,改变该值得时候同时改变Label显示的数字。

值得一提的是,在该UIView中,添加了布局约束,方便我们把该UIView添加到页面时控制它的布局。在iOS9.0中,多了一个NSLayoutAnchor类,用它来完成布局约束,比原来低版本用的NSLayoutConstraint要更方便一些。

2、按钮

Swift实战之2048小游戏 Swift实战之2048小游戏

游戏页面下方是两个按钮,重置清空本次游戏的数字,生成则产生一个数字,这两个按钮主要用于调试。

按钮是在一个ViewFactory的工厂类中生产的,同样生产时,添加了一些布局约束。

 1 class func createButton(title:String,action:Selector,sender:UIViewController) -> UIButton{
 2         let button = UIButton(frame: CGRectZero)
 3         button.backgroundColor=UIColor.orangeColor()
 4         button.setTitle(title, forState: .Normal)
 5         button.titleLabel!.textColor=UIColor.whiteColor()
 6         button.titleLabel!.font=UIFont.systemFontOfSize(14)
 7         
 8         //布局约束
 9         button.translatesAutoresizingMaskIntoConstraints = false
10         button.widthAnchor.constraintEqualToConstant(100).active=true
11         button.heightAnchor.constraintEqualToConstant(30).active=true
12         
13         button.addTarget(sender, action: action, forControlEvents: UIControlEvents.TouchUpInside)
14         return button
15     }

3、游戏区域(游戏地图)

Swift实战之2048小游戏

一个5X5的矩阵,首先在所有位置上放置灰色的方块UIView。

 1 var backgrounds:Array<UIView>! //所有方块的背景
 2    func setupGameMap(){
 3         let margins = self.view.layoutMarginsGuide
 4         
 5         for row in 0..<self.dimension {
 6             for col in 0..<self.dimension {
 7                 //放置灰色的方块在对应的矩阵位置上
 8                 let background = UIView(frame: CGRectMake(0, 0, self.width, self.width))
 9                 background.backgroundColor = UIColor.darkGrayColor()
10                 background.translatesAutoresizingMaskIntoConstraints = false
11                 background.widthAnchor.constraintEqualToConstant(self.width).active = true
12                 background.heightAnchor.constraintEqualToConstant(self.width).active = true
13                 self.view.addSubview(background)
14                 self.backgrounds.append(background)
15                 
16                 //布局约束
17                 background.translatesAutoresizingMaskIntoConstraints=false
18                 background.widthAnchor.constraintEqualToConstant(self.width).active=true
19                 background.heightAnchor.constraintEqualToConstant(self.width).active=true
20                 
21                 //用代码进行布局约束
22                 var centerXConstant:CGFloat
23                 var centerYConstant:CGFloat
24                 if self.dimension%2 == 1 {
25                     centerXConstant = (self.padding+self.width) * CGFloat(col-self.dimension/2)
26                     centerYConstant = (self.padding+self.width) * CGFloat(row-self.dimension/2)
27                 }else{
28                     centerXConstant = (self.padding+self.width) * CGFloat(Double(col-self.dimension/2)+0.5)
29                     centerYConstant = (self.padding+self.width) * CGFloat(Double(row-self.dimension/2)+0.5)
30                 }
31                 
32                 background.centerXAnchor.constraintEqualToAnchor(margins.centerXAnchor, constant: centerXConstant).active=true
33                 background.centerYAnchor.constraintEqualToAnchor(margins.centerYAnchor, constant: centerYConstant).active=true
34 
35             }
36         }
37     }

然后,添加数字时,再在相应位置上放置数字方块TileView。

 1 var tiles = [NSIndexPath:TileView]() //存储当前的有数字的方块
 2 
 3 //插入一个数字方块
 4     func insertTile(pos:(Int,Int),value:Int){
 5         let (row,col)=pos
 6         
 7         let x=50 + CGFloat(col) * (self.width+self.padding)
 8         let y=150 + CGFloat(row) * (self.width+self.padding)
 9         
10         let tile=TileView(pos:CGPointMake(x,y),width:self.width,value:value)
11       
12         self.view.addSubview(tile)
13         self.view.bringSubviewToFront(tile)
14         
15         let index = NSIndexPath(forRow: row, inSection: col)
16         
17         tiles[index] = tile
18         
19         //布局约束
20         var centerXConstant:CGFloat
21         var centerYConstant:CGFloat
22         if self.dimension%2 == 1 {
23             centerXConstant = (self.padding+self.width) * CGFloat(col-self.dimension/2)
24             centerYConstant = (self.padding+self.width) * CGFloat(row-self.dimension/2)
25         }else{
26             centerXConstant = (self.padding+self.width) * CGFloat(Double(col-self.dimension/2)+0.5)
27             centerYConstant = (self.padding+self.width) * CGFloat(Double(row-self.dimension/2)+0.5)
28         }
29         let margins=self.view.layoutMarginsGuide
30         tile.centerXAnchor.constraintEqualToAnchor(margins.centerXAnchor, constant: centerXConstant).active=true
31         tile.centerYAnchor.constraintEqualToAnchor(margins.centerYAnchor, constant: centerYConstant).active=true
32     }

上面用了一个数字tiles来保存插入的TileView,以便清除数字时,可以把对应的TileView从界面中移除。

1     //移除一个数字方块
2     func clearTile(row:Int,col:Int){
3         
4         let index=NSIndexPath(forRow: row, inSection: col)
5         let tile=tiles[index]!
6         tile.removeFromSuperview()
7         tiles.removeValueForKey(index)
8         
9     }

TileView的实现代码如下:

 1 import Foundation
 2 import UIKit
 3 
 4 
 5 class TileView : UIView {
 6     let colorMap=[
 7         2:UIColor.redColor(),
 8         4:UIColor.orangeColor(),
 9         8:UIColor.lightTextColor(),
10         16:UIColor.greenColor(),
11         32:UIColor.brownColor(),
12         64:UIColor.blackColor(),
13         128:UIColor.purpleColor(),
14         256:UIColor.lightGrayColor(),
15         512:UIColor.cyanColor(),
16         1024:UIColor.magentaColor(),
17         2048:UIColor.blackColor()
18     ]
19     
20     var value:Int{
21         didSet{
22             backgroundColor=colorMap[value]
23             numberLabel.text="\(value)"
24         }
25     }
26     
27     var numberLabel:UILabel!
28     
29     init(pos:CGPoint,width:CGFloat,value:Int){
30         self.value=value
31         
32         numberLabel=UILabel(frame: CGRectMake(0, 0, width, width))
33         numberLabel.textColor=UIColor.whiteColor()
34         numberLabel.textAlignment=NSTextAlignment.Center
35         numberLabel.minimumScaleFactor=0.5
36         numberLabel.font=UIFont(name: "微软雅黑", size: 20)
37         numberLabel.text="\(value)"
38         
39         super.init(frame: CGRectMake(pos.x, pos.y,width , width))
40         addSubview(numberLabel)
41         backgroundColor=colorMap[value]
42         
43         //代码约束
44         self.translatesAutoresizingMaskIntoConstraints = false
45         self.widthAnchor.constraintEqualToConstant(width).active=true
46         self.heightAnchor.constraintEqualToConstant(width).active=true
47         
48     }
49 
50     required init?(coder aDecoder: NSCoder) {
51         self.value = 2
52         super.init(coder: aDecoder)
53     }
54     
55     
56 }
View Code

相关文章: