见缝插针游戏的实现

游戏的背景:

前言:这个游戏是2015年出来的,刚开始当时aa(见缝插针)游戏很火,我也是玩了好大半天,深深地求虐感屡试不爽。那个时候我还是一个iOS小兵一枚,当时就感觉这游戏如此简单到炸裂,于是就想着尝试实现它,但是...(此处省略100字)。
时间过得真快,过去了两年,偶然翻看博客,发现有人也有相似的实现和想法,并且提供了很好的思路。于是乎,我又下定决心研究一翻,这才有了今天的实现。

游戏的逻辑:

每一关你都有不同数量的“针”,中心的圆盘上初始状态可能就会有“针”已经插入就绪,不过这是为增加游戏难度而设计的。你的任务就是将你所在关卡中所持有的所有“针”插入到中心转动的圆盘上去(插入点是相对固定的),前提是不能与其他“针”相互接触,否则游戏宣告失败。为了不让你很快通关,后面的关卡理所当然难度会越来越大,比如圆盘转速增加、初始就绪“针”数和持有针数增加、顺逆时针变速转动等。

实现思路:

中心的圆盘是一个CAShapeLayer,点击屏幕就去扩展CAShapeLayer的path,达到针插在圆盘上的效果。对于“针”的动态移动过程, 就可以创建一个过渡的“针”视图来等效代替。那么核心的问题就来了:我在扩展图层的路径时,怎么确定绘制的起始点和终点呢?因为在中心圆盘的旋转过程中,其上的个点也会随之一起做仿射变换。如果你在图层旋转时还是以圆盘静止时硬编码取到的起点和终点来确认绘制“针”的话,那么新绘制的“针”就会和之前“插上”的“针”重合在一起,导致的结果就是:无论你怎么“插针”,圆盘上看到的就只有一根“针”,虽然实际上它是很多“针”重叠在一起而表现出来的结果。

CAShapeLayer属于QuartzCore框架,继承自CALayer。CAShapeLayer是在坐标系内绘制贝塞尔曲线的,通过绘制贝塞尔曲线,设置shape(形状)的path(路径),从而绘制各种各样的图形以及不规则图形。因此,使用CAShapeLayer需要与UIBezierPath一起使用。
UIBezierPath类允许你在自定义的 View 中绘制和渲染由直线和曲线组成的路径。你可以在初始化的时候直接为你的UIBezierPath指定一个几何图形。
通俗点就是UIBezierPath用来指定绘制图形路径,而CAShapeLayer就是根据路径来绘图的。

仿射:无论变换矩阵用什么值,图层中平行的两条线在变换之后仍然保持平行。(平移,缩放,旋转等)

1、那么首要问题就是如何得到当前“针”的绘制起点和终点?

值得庆幸的是中心转轴是圆形的(故称之为圆盘),而且也只能是在圆形的情况下才能设计出aa这样的游戏效果。因为插针的起点和终点的计算依赖于圆形的中心到边界各点的距离相等这一性质,具体可以用如下两张图来表示坐标点的换算过程:


顺时针
逆时针

其中的基准点为在静止时“针”的插入点(对应于绘制“针”的起点),而终点可以通过相同的方法来实现换算,只不过半径r的值需要做出相应的调整。
基准点(x',y'),实际为了计算方便我们取(r,2r)

顺时针旋转诱导公式:
x = x' + r * sinθ
y = y' - r + r * cosθ

逆时针旋转诱导公式:
x = x' - r * sinθ
y = y' - r + r * cosθ

终点计算,只要把对应的r,换成实际的r即可。
根据公式,我们看出,要想求出针的起点和终点,我们必须知道图层旋转的角度。于是,第二个问题就出来了:

2、如何取得在旋转过程中图层的旋转角度?

我们都知道的是要是涉及到动画的话,图层会将动画的过程交给presentationLayer(表现层)来完成。至于动画的初值和终值之间的中间值则由系统不断计算,并通过表现层来展示。
既然我们选用的是图层,我们就需要知道它的transform属性是一个名为CATransform3D的结构体变量,而XXView的transform属性是一个名为CGAffineTransform的结构体变量。前者就自然没有a、b这样的成员变量,而是这样的一个矩阵结构:

struct CATransform3D
{
  CGFloat m11, m12, m13, m14;           
  CGFloat m21, m22, m23, m24;
  CGFloat m31, m32, m33, m34;
  CGFloat m41, m42, m43, m44;
};

通过矩阵控制坐标变换
struct CATransform3D
{
  CGFloat m11(x缩放), m12(y切变), m13(旋转), m14();
  CGFloat m21(x切变), m22(y缩放), m23(和m32一起决定x轴的旋转), m24();
  CGFloat m31(和m13一起决定y轴的旋转),m32(和m23一起决定x轴的旋转), m33(z缩放), m34(透视效果,要操作的这个对象要有旋转的角度,否则没有效果。正直/负值都有意义);
  CGFloat m41(x平移), m42(y平移), m43(z平移), m44(初始为1);
};

其中的m14、m24、m34、m44只是作为矩阵的占位符,通常会将m14、m24、m34设置为0,m44设为1。
常见的变化矩阵:


常见的变换矩阵

举例:图层绕Z轴顺时针旋转推导过程


图层绕Z轴旋转推导

矩阵变换:


矩阵变换
    x' = m11 * x + m21 * y + m31 * z + m41
    y' = m12 * x + m22 * y + m32 * z + m42
    z' = m13 * x + m23 * y + m33 * z + m43

    x1 = cosB * x0 - sinB * y0
    y1 = sinB * x0 + cosB * y0
    
    cosB   sinB    0    0
   -sinB   cosB    0    0
      0     0      1    0
      0     0      0    1

aa中我们是围绕z轴旋转的,我们就可以使用反正切变换函数参入实际参数m21、m22的值就可以实时地知道图层的当前旋转角度了:
三角函数:
tanθ = {sinθ\over cosθ}

θ = atan({sinθ\over cosθ})

θ = atan2({sinθ,cosθ})

  func transformRotationAngle() -> CGFloat {
        var degreeAngle:CGFloat = -CGFloat(atan2f(Float(self.presentation()!.transform.m21), Float(self.presentation()!.transform.m22)))
        if (degreeAngle < 0.0) {
            degreeAngle = degreeAngle + (CGFloat)(2.0 * Double.pi)
        }
        return degreeAngle;
    }

借助于数学诱导公式,坐标的变换计算公式可以统一为如下形式:

 func convertPointWhenRotatingWithBenchmarkPoint(point:CGPoint,radius:CGFloat) -> CGPoint {
        let rotationAngle:CGFloat = (self.presentation()?.transformRotationAngle())!
        return CGPoint.init(x: point.x + CGFloat(sinf(Float(rotationAngle))) * radius, y: point.y - radius + CGFloat(cosf(Float(rotationAngle))) * radius)
    }

在图层的旋转过程中,针的起点和终点都通过以上计算出来了,接着,生成一个贝塞尔曲线,然后追加到整体的贝塞尔曲线中,再赋值给CAShapeLayer(圆盘)的path,这样就达到了针的生成效果。

let strokeBezier = UIBezierPath()
strokeBezier.move(to: strokeStartPoint)
strokeBezier.addLine(to: strokeEndPoint)
strokeBezier.move(to: strokeEndPoint)
 strokeBezier.addArc(withCenter: strokeEndPoint, radius: kTopRoundRadius, startAngle: 0, endAngle: CGFloat(Double.pi * 2.0), clockwise: true)
self.arrowStrokePath.append(strokeBezier)
self.centralAxisLayer.path = self.arrowStrokePath.cgPath;

游戏效果:

游戏界面

Github:
https://github.com/SpringAndSummer/Game
参考资料:
https://blog.csdn.net/u013282174/article/details/50388546
https://blog.csdn.net/zmmzxxx/article/details/74276077

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,864评论 6 494
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,175评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,401评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,170评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,276评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,364评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,401评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,179评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,604评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,902评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,070评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,751评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,380评论 3 319
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,077评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,312评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,924评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,957评论 2 351

推荐阅读更多精彩内容